package main import ( "flag" "fmt" "io" "log" "os" "strings" "time" "golang.org/x/crypto/ssh" "xinu.tv/thumbnailer/auth" "xinu.tv/thumbnailer/scp" ) var ( host = flag.String("host", "localhost:22", "host:port to scp from") user = flag.String("user", os.Getenv("USER"), "ssh user") ) func checkFatal(err error) { if err != nil { log.Fatal(err) } } func progress(w io.Writer, r io.Reader, size int64) error { const ( chunkSize = 1 << 10 width = 50 ) var err error total, bLast := int64(0), int64(0) tLast := time.Now() defer fmt.Println() for { var n int64 n, err = io.CopyN(w, r, chunkSize) total += n bLast += n if time.Since(tLast) > (300 * time.Millisecond) { kbps := float64(bLast) / time.Since(tLast).Seconds() / 1024 frac := int(total * width / size) fmt.Printf("\rDownloaded: %s>%s| %.2f Kb/s", strings.Repeat("=", frac), strings.Repeat(" ", width-frac), kbps) tLast = time.Now() bLast = 0 } if err != nil { break } } if err == io.EOF { return nil } return err } var sink, _ = os.Create(os.DevNull) func main() { flag.Parse() ams, err := auth.NewPublicKey() checkFatal(err) // An SSH client is represented with a ClientConn. // // To authenticate with the remote server you must pass at least one // implementation of ClientAuth via the Auth field in ClientConfig. config := &ssh.ClientConfig{ User: *user, Auth: ams, // TODO(wathiede); use FixedHostKey? HostKeyCallback: ssh.InsecureIgnoreHostKey(), } client, err := ssh.Dial("tcp", *host, config) checkFatal(err) for _, fn := range flag.Args() { log.Println("Fetching", fn) // Each ClientConn can support multiple interactive sessions, // represented by a Session. session, err := client.NewSession() checkFatal(err) f, err := scp.ScpFrom(session, fn) if err != nil { log.Println(err) continue } log.Println(f.Name, f.Perm, f.Size) err = progress(sink, f, f.Size) checkFatal(err) checkFatal(f.Close()) checkFatal(session.Close()) } }