1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
// Command vscp provides a scp-like utility for copying files over VM
// sockets. It is meant to show example usage of package vsock, but is
// also useful in scenarios where a virtual machine does not have
// networking configured, but VM sockets are available.
package main
import (
"crypto/sha256"
"flag"
"hash"
"io"
"log"
"os"
"time"
"github.com/mdlayher/vsock"
)
var flagVerbose = flag.Bool("v", false, "enable verbose logging to stderr")
func main() {
var (
flagReceive = flag.Bool("r", false, "receive files from another instance of vscp")
flagSend = flag.Bool("s", false, "send files to another instance of vscp")
flagContextID = flag.Uint("c", 0, "send only: context ID of the remote VM socket")
flagPort = flag.Uint("p", 0, "- receive: port ID to listen on (random port by default)\n\t- send: port ID to connect to")
flagHash = flag.Bool("h", false, "display a checksum hash of the input or output data after transfer completes")
flagTimeout = flag.Duration("t", 0, "receive only: timeout for read operations (default: no timeout)")
)
flag.Parse()
log.SetOutput(os.Stderr)
// Determine if target should be stdin/stdout or a regular file.
var target string
if t := flag.Arg(0); t != "" {
target = t
}
switch {
case *flagReceive && *flagSend:
log.Fatalf(`vscp: specify only one of "-r" for receive or "-s" for send`)
case *flagReceive:
if *flagContextID != 0 {
log.Fatalf(`vscp: context ID flag "-c" not valid for receive operation`)
}
receive(target, uint32(*flagPort), *flagTimeout, *flagHash)
case *flagSend:
send(target, uint32(*flagContextID), uint32(*flagPort), *flagHash)
default:
flag.PrintDefaults()
}
}
// receive starts a server and receives data from a remote client using
// VM sockets. The data is written to target, which may be a file,
// or stdout, if no file is specified.
func receive(target string, port uint32, timeout time.Duration, checksum bool) {
// Log helper functions.
logf := func(format string, a ...interface{}) {
logf("receive: "+format, a...)
}
fatalf := func(format string, a ...interface{}) {
log.Fatalf("vscp: receive: "+format, a...)
}
// Determine if target is stdout or a file to be created.
var w io.Writer
switch target {
case "":
logf("empty target, file will be written to stdout")
w = os.Stdout
default:
logf("creating file %q for output", target)
f, err := os.Create(target)
if err != nil {
fatalf("failed to create output file: %q", err)
}
defer f.Close()
w = f
}
// Optionally compute a checksum of the data.
var h hash.Hash
if checksum {
h = sha256.New()
w = io.MultiWriter(w, h)
}
logf("opening listener: %d", port)
// TODO(mdlayher): support vsock.Local binds for testing.
l, err := vsock.Listen(port, nil)
if err != nil {
fatalf("failed to listen: %v", err)
}
defer l.Close()
// Show server's address for setting up client flags.
log.Printf("receive: listening: %s", l.Addr())
// Accept a single connection, and receive stream from that connection.
c, err := l.Accept()
if err != nil {
fatalf("failed to accept: %v", err)
}
_ = l.Close()
defer c.Close()
if timeout != 0 {
if err := c.SetDeadline(time.Now().Add(timeout)); err != nil {
fatalf("failed to set timeout: %v", err)
}
}
logf("server: %s", c.LocalAddr())
logf("client: %s", c.RemoteAddr())
logf("receiving data")
if _, err := io.Copy(w, c); err != nil {
fatalf("failed to receive data: %v", err)
}
logf("transfer complete")
if h != nil {
log.Printf("sha256 checksum: %x", h.Sum(nil))
}
}
// send dials a server and sends data to it using VM sockets. The data
// is read from target, which may be a file, or stdin if no file or "-"
// is specified.
func send(target string, cid, port uint32, checksum bool) {
// Log helper functions.
logf := func(format string, a ...interface{}) {
logf("send: "+format, a...)
}
fatalf := func(format string, a ...interface{}) {
log.Fatalf("vscp: send: "+format, a...)
}
// Determine if target is stdin or a file to be read in.
var r io.Reader
switch target {
case "", "-":
logf("empty or stdin target, file will be read from stdin")
r = os.Stdin
default:
logf("opening file %q for input", target)
f, err := os.Open(target)
if err != nil {
fatalf("failed to open input file: %q", err)
}
defer f.Close()
r = f
}
// Optionally compute a checksum of the data.
var h hash.Hash
if checksum {
h = sha256.New()
r = io.TeeReader(r, h)
}
logf("dialing: %d.%d", cid, port)
// Dial a remote server and send a stream to that server.
c, err := vsock.Dial(cid, port, nil)
if err != nil {
fatalf("failed to dial: %v", err)
}
defer c.Close()
logf("client: %s", c.LocalAddr())
logf("server: %s", c.RemoteAddr())
logf("sending data")
if _, err := io.Copy(c, r); err != nil {
fatalf("failed to send data: %v", err)
}
logf("transfer complete")
if h != nil {
log.Printf("sha256 checksum: %x", h.Sum(nil))
}
}
// logf shows verbose logging if -v is specified, or does nothing
// if it is not.
func logf(format string, a ...interface{}) {
if !*flagVerbose {
return
}
log.Printf(format, a...)
}
|