func main() { flag.Parse() logSetup() log.Debug("using minimega: %v", *f_minimega) // invoke minimega and get the doc json doc, err := exec.Command(*f_minimega, "-cli").Output() if err != nil { log.Fatalln(err) } log.Debug("got doc: %v", string(doc)) // decode the JSON for our template if err := json.Unmarshal(doc, &handlers); err != nil { log.Fatalln(err) } exclude = strings.Split(*f_exclude, ",") values = strings.Split(*f_values, ",") for { if err := fuzz(); err != nil { log.Fatal("fuzz: %v", err) } if err := cleanup(); err != nil { log.Fatal("cleanup: %v", err) } } }
func commandHandler() { for commands := range Client.commandChan { var ids []int for k, _ := range commands { ids = append(ids, k) } sort.Ints(ids) for _, id := range ids { log.Debug("ron commandHandler: %v", id) if id <= Client.CommandCounter { continue } if !Client.Matches(commands[id].Filter) { continue } log.Debug("ron commandHandler match: %v", id) processCommand(commands[id]) } } log.Info("command handler exit") }
// cliPreprocess performs expansion on a single string and returns the update // string or an error. func cliPreprocess(v string) (string, error) { if u, err := url.Parse(v); err == nil { switch u.Scheme { case "file": log.Debug("file preprocessor") return iomHelper(u.Opaque) case "http", "https": log.Debug("http/s preprocessor") // Check if we've already downloaded the file v2, err := iomHelper(u.Path) if err == nil { return v2, err } if err.Error() == "file not found" { log.Info("attempting to download %v", u) // Try to download the file, save to files dst := filepath.Join(*f_iomBase, u.Path) if err := wget(v, dst); err != nil { return "", err } return dst, nil } return "", err } } return v, nil }
// Return a slice of strings, split on whitespace, not unlike strings.Fields(), // except that quoted fields are grouped. // Example: a b "c d" // will return: ["a", "b", "c d"] func fieldsQuoteEscape(c string, input string) []string { log.Debug("fieldsQuoteEscape splitting on %v: %v", c, input) f := strings.Fields(input) var ret []string trace := false temp := "" for _, v := range f { if trace { if strings.Contains(v, c) { trace = false temp += " " + trimQuote(c, v) ret = append(ret, temp) } else { temp += " " + v } } else if strings.Contains(v, c) { temp = trimQuote(c, v) if strings.HasSuffix(v, c) { // special case, single word like 'foo' ret = append(ret, temp) } else { trace = true } } else { ret = append(ret, v) } } log.Debug("generated: %#v", ret) return ret }
// ConnectImage exports a image using the NBD protocol using the qemu-nbd. If // successful, returns the NBD device. func ConnectImage(image string) (string, error) { var nbdPath string var err error for i := 0; i < maxConnectRetries; i++ { nbdPath, err = GetDevice() if err != ErrNoDeviceAvailable { break } log.Debug("all nbds in use, sleeping before retrying") time.Sleep(time.Second * 10) } if err != nil { return "", err } // connect it to qemu-nbd cmd := exec.Command(process("qemu-nbd"), "-c", nbdPath, image) log.Debug("connecting to nbd with cmd: %v", cmd) result, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("unable to connect to nbd: %v", string(result)) } return nbdPath, nil }
func containerNukeWalker(path string, info os.FileInfo, err error) error { if err != nil { return nil } log.Debug("walking file: %v", path) switch info.Name() { case "tasks": d, err := ioutil.ReadFile(path) if err != nil { return nil } pids := strings.Fields(string(d)) for _, pid := range pids { log.Debug("found pid: %v", pid) fmt.Println("killing process:", pid) processWrapper("kill", "-9", pid) } } return nil }
func dnsClient() { rand.Seed(time.Now().UnixNano()) t := NewEventTicker(*f_mean, *f_stddev, *f_min, *f_max) m := new(dns.Msg) for { t.Tick() h, _ := randomHost() d := randomDomain() var t uint16 if *f_dnsv4 { t = dns.TypeA } else if *f_dnsv6 { t = dns.TypeAAAA } else { t = randomQuestionType() } m.SetQuestion(dns.Fqdn(d), t) log.Debug("dns client: question=%v", m.Question) in, err := dns.Exchange(m, h+addr) if err != nil { log.Debug(err.Error()) } else { log.Debug("dns client: answer=%v", in) dnsReportChan <- 1 } } }
// GetDevice returns the first available NBD. If there are no devices // available, returns ErrNoDeviceAvailable. func GetDevice() (string, error) { // Get a list of all devices devFiles, err := ioutil.ReadDir("/dev") if err != nil { return "", err } nbdPath := "" // Find the first available nbd for _, devInfo := range devFiles { dev := devInfo.Name() // we don't want to include partitions here if !strings.Contains(dev, "nbd") || strings.Contains(dev, "p") { continue } // check whether a pid exists for the current nbd _, err = os.Stat(filepath.Join("/sys/block", dev, "pid")) if err != nil { log.Debug("found available nbd: " + dev) nbdPath = filepath.Join("/dev", dev) break } else { log.Debug("nbd %v could not be used", dev) } } if nbdPath == "" { return "", ErrNoDeviceAvailable } return nbdPath, nil }
// a filename completer for goreadline that searches for the file: prefix, // attempts to find matching files, and returns an array of candidates. func iomCompleter(line string) []string { f := strings.Fields(line) if len(f) == 0 { return nil } last := f[len(f)-1] if strings.HasPrefix(last, IOM_HELPER_MATCH) { fileprefix := strings.TrimPrefix(last, IOM_HELPER_MATCH) matches := iom.Info(fileprefix + "*") log.Debug("got raw matches: %v", matches) // we need to clean up matches to collapse directories, unless // there is a directory common prefix, in which case we // collapse offset by the number of common directories. dlcp := lcp(matches) didx := strings.LastIndex(dlcp, string(filepath.Separator)) drel := "" if didx > 0 { drel = dlcp[:didx] } log.Debug("dlcp: %v, drel: %v", dlcp, drel) if len(fileprefix) < len(drel) { r := IOM_HELPER_MATCH + drel + string(filepath.Separator) return []string{r, r + "0"} // hack to prevent readline from fastforwarding beyond the directory name } var finalMatches []string for _, v := range matches { if strings.Contains(v, "*") { continue } r, err := filepath.Rel(drel, v) if err != nil { log.Errorln(err) return nil } dir := filepath.Dir(r) if dir == "." { finalMatches = append(finalMatches, IOM_HELPER_MATCH+v) continue } paths := strings.Split(dir, string(filepath.Separator)) found := false for _, d := range finalMatches { if d == paths[0]+string(filepath.Separator) { found = true break } } if !found { finalMatches = append(finalMatches, IOM_HELPER_MATCH+filepath.Join(drel, paths[0])+string(filepath.Separator)) } } return finalMatches } return nil }
// cmdTimeout runs the command c and returns a timeout if it doesn't complete // after time t. If a timeout occurs, cmdTimeout will kill the process. func cmdTimeout(c *exec.Cmd, t time.Duration) error { log.Debug("cmdTimeout: %v", c) start := time.Now() err := c.Start() if err != nil { return fmt.Errorf("cmd start: %v", err) } done := make(chan error) go func() { done <- c.Wait() }() select { case <-time.After(t): err = c.Process.Kill() if err != nil { return err } return <-done case err = <-done: log.Debug("cmd %v completed in %v", c, time.Now().Sub(start)) return err } }
func vncClear() { for k, v := range vncKBRecording { if inNamespace(v.VM) { log.Debug("stopping kb recording for %v", k) if err := v.Stop(); err != nil { log.Error("%v", err) } delete(vncKBRecording, k) } } for k, v := range vncFBRecording { if inNamespace(v.VM) { log.Debug("stopping fb recording for %v", k) if err := v.Stop(); err != nil { log.Error("%v", err) } delete(vncFBRecording, k) } } for k, v := range vncPlaying { if inNamespace(v.VM) { log.Debug("stopping kb playing for %v", k) if err := v.Stop(); err != nil { log.Error("%v", err) } delete(vncPlaying, k) } } }
// walk every arg looking for "file:" and calling iomHelper on the suffix. // Replace the arg with the local file if found. func iomPreprocessor(c *minicli.Command) (*minicli.Command, error) { for k, v := range c.StringArgs { if strings.HasPrefix(v, IOM_HELPER_MATCH) { file := strings.TrimPrefix(v, IOM_HELPER_MATCH) local, err := iomHelper(file) if err != nil { return nil, err } log.Debug("iomPreProcessor: %v -> %v", v, local) c.StringArgs[k] = local } } for k, v := range c.ListArgs { for x, y := range v { if strings.HasPrefix(y, IOM_HELPER_MATCH) { file := strings.TrimPrefix(y, IOM_HELPER_MATCH) local, err := iomHelper(file) if err != nil { return nil, err } log.Debug("iomPreProcessor: %v -> %v", y, local) c.ListArgs[k][x] = local } } } return c, nil }
func deployRun(hosts []string, user, remotePath string, sudo bool) []error { log.Debug("deployRun: %v, %v", hosts, user) var errs []error // minimega command flags := deployGetFlags() log.Debug("minimega flags: %v", flags) var minimegaCommand string if sudo { minimegaCommand = fmt.Sprintf("sudo -b nohup %v %v > /dev/null 2>&1 &", remotePath, flags) } else { minimegaCommand = fmt.Sprintf("nohup %v %v > /dev/null 2>&1 &", remotePath, flags) } for _, host := range hosts { command := []string{"ssh", "-o", "StrictHostKeyChecking=no"} if user != "" { command = append(command, fmt.Sprintf("%v@%v", user, host)) } else { command = append(command, fmt.Sprintf("%v", host)) } command = append(command, minimegaCommand) log.Debug("ssh command: %v", command) out, err := processWrapper(command...) if err != nil { errs = append(errs, fmt.Errorf("%v: %v", err, out)) } } return errs }
// Trunk reads data from remote, constructs a *Message, and sends it using fn. // Returns the first error. func Trunk(remote net.Conn, uuid string, fn func(*Message) error) { var n int var err error for err == nil { buf := make([]byte, 32*1024) n, err = remote.Read(buf) log.Debug("trunking %v bytes for %v", n, uuid) if err == nil { m := &Message{ Type: MESSAGE_TUNNEL, UUID: uuid, Tunnel: buf[:n], } err = fn(m) } if err != nil && err != io.ErrClosedPipe { log.Errorln(err) } } if err != io.ErrClosedPipe { log.Error("trunk failed for %v: %v", err) } log.Debug("trunk exit for %v", uuid) }
func (n *Node) handleMSA(m *Message) { log.Debug("handleMSA: %v", m) n.meshLock.Lock() defer n.meshLock.Unlock() if len(n.network[m.Source]) == len(m.Body.([]string)) { diff := false for i, v := range n.network[m.Source] { if m.Body.([]string)[i] != v { diff = true break } } if !diff { log.Debugln("MSA discarded, client data hasn't changed") return } } n.network[m.Source] = m.Body.([]string) if log.WillLog(log.DEBUG) { log.Debug("new network is: %v", n.network) } n.updateNetwork = true }
func (vm *ContainerVM) console(stdin, stdout, stderr *os.File) { socketPath := filepath.Join(vm.instancePath, "console") l, err := net.Listen("unix", socketPath) if err != nil { log.Error("could not start unix domain socket console on vm %v: %v", vm.ID, err) return } vm.listener = l for { conn, err := l.Accept() if err != nil { if strings.Contains(err.Error(), "use of closed network connection") { return } log.Error("console socket on vm %v: %v", vm.ID, err) continue } log.Debug("new connection!") go io.Copy(conn, stdout) go io.Copy(conn, stderr) io.Copy(stdin, conn) log.Debug("disconnected!") } }
func unmangleUUID(uuid string) string { // string must be in the form: // XXXXXXXX-XXXX-XXXX-YYYY-YYYYYYYYYYYY // the X characters are reversed at 2 byte intervals (big/little endian for a uuid?) var ret string re := regexp.MustCompile("[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}") u := re.FindString(strings.ToLower(uuid)) if uuid == "" { log.Fatal("uuid failed to match uuid format: %v", uuid) } log.Debug("found uuid: %v", u) if getOSVer() != "Windows XP" { return u } ret += u[6:8] ret += u[4:6] ret += u[2:4] ret += u[:2] ret += "-" ret += u[11:13] ret += u[9:11] ret += "-" ret += u[16:18] ret += u[14:16] ret += u[18:] log.Debug("mangled/unmangled uuid: %v %v", u, ret) return ret }
// Matches tests whether all the filters match the client. func (c *Client) Matches(f *Filter) bool { if f == nil { return true } if f.UUID != "" && f.UUID != c.UUID { log.Debug("failed match on UUID: %v != %v", f.UUID, c.UUID) return false } if f.Hostname != "" && f.Hostname != c.Hostname { log.Debug("failed match on hostname: %v != %v", f.Hostname, c.Hostname) return false } if f.Arch != "" && f.Arch != c.Arch { log.Debug("failed match on arch: %v != %v", f.Arch, c.Arch) return false } if f.OS != "" && f.OS != c.OS { log.Debug("failed match on os: %v != %v", f.OS, c.OS) return false } for k, v := range f.Tags { if c.Tags[k] != v { log.Debug("failed match on tag %v, %v != %v", k, v, c.Tags[k]) return false } } return c.matchesIP(f) && c.matchesMAC(f) }
func parseMega(ctx *present.Context, sourceFile string, sourceLine int, cmd string) (present.Elem, error) { cmd = strings.TrimSpace(cmd) log.Debug("parseMega cmd: %v", cmd) f := strings.Fields(cmd) if len(f) != 2 { return nil, fmt.Errorf("invalid .mega directive: %v", cmd) } filename := filepath.Join(*f_root, filepath.Dir(sourceFile), f[1]) log.Debug("filename: %v", filename) text, err := ctx.ReadFile(filename) if err != nil { return nil, fmt.Errorf("%s:%d: %v", sourceFile, sourceLine, err) } data := &megaTemplateData{ Body: string(text), } var buf bytes.Buffer if err := megaTemplate.Execute(&buf, data); err != nil { return nil, err } return Mega{ Text: template.HTML(buf.String()), Filename: filepath.Base(filename), }, nil }
func (m *megaconn) start() { log.Debug("got body: %v", m.body) lines := strings.Split(m.body, "\n") for _, v := range lines { resp, errs := sendCommand(v) mess := &Message{ Id: m.id, } if errs != "" { mess.Kind = "stderr" mess.Body = errs } else { mess.Kind = "stdout" mess.Body = resp } log.Debug("generated message: %v", mess) m.out <- mess } mess := &Message{ Id: m.id, Kind: "end", } time.AfterFunc(msgDelay, func() { m.out <- mess }) }
func deployCopy(hosts []string, user, remotePath string) []error { log.Debug("deployCopy: %v, %v", hosts, user) var errs []error minimegaBinary := fmt.Sprintf("/proc/%v/exe", os.Getpid()) log.Debug("minimega binary: %v", minimegaBinary) for _, host := range hosts { command := []string{"scp", "-B", "-o", "StrictHostKeyChecking=no", minimegaBinary} if user != "" { command = append(command, fmt.Sprintf("%v@%v:%v", user, host, remotePath)) } else { command = append(command, fmt.Sprintf("%v:%v", host, remotePath)) } log.Debug("scp command: %v", command) out, err := processWrapper(command...) if err != nil { errs = append(errs, fmt.Errorf("%v: %v", err, out)) } } return errs }
// cmdTimeout runs the command c and returns a timeout if it doesn't complete // after time t. If a timeout occurs, cmdTimeout will kill the process. Blocks // until the process terminates. func cmdTimeout(c *exec.Cmd, t time.Duration) error { log.Debug("cmdTimeout: %v", c) start := time.Now() if err := c.Start(); err != nil { return fmt.Errorf("cmd start: %v", err) } done := make(chan error) go func() { done <- c.Wait() close(done) }() select { case <-time.After(t): log.Debug("killing cmd %v", c) err := c.Process.Kill() // Receive from done so that we don't leave the goroutine hanging err2 := <-done // Kill error takes precedence as they should be unexpected if err != nil { return err } return err2 case err := <-done: log.Debug("cmd %v completed in %v", c, time.Now().Sub(start)) return err } }
// Dial a client serial port. The server will maintain this connection until a // client connects and then disconnects. func (s *Server) DialSerial(path string) error { log.Debug("DialSerial: %v", path) s.serialLock.Lock() defer s.serialLock.Unlock() // are we already connected to this client? if _, ok := s.serialConns[path]; ok { return fmt.Errorf("already connected to serial client %v", path) } // connect! serial, err := net.Dial("unix", path) if err != nil { return err } s.serialConns[path] = serial go func() { s.clientHandler(serial) // client disconnected s.serialLock.Lock() defer s.serialLock.Unlock() delete(s.serialConns, path) log.Debug("disconnected from serial client: %v", path) }() return nil }
// Starts a Ron server on the specified port func (s *Server) Listen(port int) error { ln, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) if err != nil { return err } go func() { for { conn, err := ln.Accept() if err != nil { log.Errorln(err) return } log.Debug("new connection from: %v", conn.RemoteAddr()) go func() { addr := conn.RemoteAddr() s.clientHandler(conn) log.Debug("disconnected from: %v", addr) }() } }() return nil }
func vncClear() error { for k, v := range vncKBRecording { log.Debug("stopping kb recording for %v", k) if err := v.Stop(); err != nil { log.Error("%v", err) } delete(vncKBRecording, k) } for k, v := range vncFBRecording { log.Debug("stopping fb recording for %v", k) if err := v.Stop(); err != nil { log.Error("%v", err) } delete(vncFBRecording, k) } for k, v := range vncKBPlaying { log.Debug("stopping kb playing for %v", k) if err := v.Stop(); err != nil { log.Error("%v", err) } delete(vncKBPlaying, k) } return nil }
// Walks the f_base directory and kills procs read from any qemu or // dnsmasq pid files func nukeWalker(path string, info os.FileInfo, err error) error { if err != nil { return nil } log.Debug("walking file: %v", path) switch info.Name() { case "qemu.pid", "dnsmasq.pid": d, err := ioutil.ReadFile(path) t := strings.TrimSpace(string(d)) log.Debug("found pid: %v", t) if err != nil { return err } args := []string{ "kill", t, } log.Infoln("killing process:", t) out, err := processWrapper(args...) if err != nil { log.Error("%v: %v", err, out) } } return nil }
func main() { flag.Parse() logSetup() log.Debug("using minimega: %v", *f_minimega) log.Debug("using doc template: %v", *f_template) // invoke minimega and get the doc json doc, err := exec.Command(*f_minimega, "-cli").Output() if err != nil { log.Fatalln(err) } log.Debug("got doc: %v", string(doc)) // decode the JSON for our template var handlers []*minicli.Handler err = json.Unmarshal(doc, &handlers) if err != nil { log.Fatalln(err) } // populate the apigen date for the template year, month, day := time.Now().Date() api := apigen{ Date: fmt.Sprintf("%v %v %v", day, month, year), } // populate the major sections for the template for _, v := range handlers { var p string if strings.HasPrefix(v.SharedPrefix, "clear") { p = strings.TrimPrefix(v.SharedPrefix, "clear ") } else { p = v.SharedPrefix } if strings.HasPrefix(p, ".") { api.Builtins = append(api.Builtins, v) } else if strings.HasPrefix(p, "mesh") { api.Mesh = append(api.Mesh, v) } else if strings.HasPrefix(p, "vm") { api.VM = append(api.VM, v) } else { api.Host = append(api.Host, v) } } // run the template and print to stdout var out bytes.Buffer t, err := template.ParseFiles(*f_template) if err != nil { log.Fatalln(err) } err = t.Execute(&out, api) if err != nil { log.Fatalln(err) } fmt.Println(out.String()) }
// find and record the next hop route for c. // Additionally, all hops along this route are also the shortest path, so // record those as well to save on effort. func (n *Node) updateRoute(c string) { if len(n.effectiveNetwork) == 0 { return } if log.WillLog(log.DEBUG) { log.Debug("updating route for %v", c) } routes := make(map[string]string) // a key node has a value of the previous hop, the key exists if it's been visited routes[n.name] = n.name // the route to ourself is pretty easy to calculate // dijkstra's algorithm is well suited in go - we can use a buffered // channel of nodes to order our search. We start by putting ourselves // in the queue (channel) and follow all nodes connected to it. If we // haven't visited that node before, it goes in the queue. q := make(chan string, len(n.effectiveNetwork)) q <- n.name for len(q) != 0 { v := <-q if log.WillLog(log.DEBUG) { log.Debug("visiting %v", v) } for _, a := range n.effectiveNetwork[v] { if _, ok := routes[a]; !ok { q <- a if log.WillLog(log.DEBUG) { log.Debug("previous hop for %v is %v", a, v) } routes[a] = v // this is the route to node a from v } } if v == c { break } } for k, v := range routes { curr := v prev := k r := k if curr == n.name { r += "<-" + routes[curr] } for curr != n.name { prev = curr curr = routes[curr] r += "<-" + routes[curr] } if log.WillLog(log.DEBUG) { log.Debug("full route for %v is %v", k, r) } n.routes[k] = prev } }
// newConnection processes a new incoming connection from another node, processes the connection // handshake, adds the connection to the client list, and starts the client message handler. func (n *Node) newConnection(conn net.Conn) { log.Debug("newConnection: %v", conn.RemoteAddr().String()) // are we soliciting connections? var solicited bool if uint(len(n.clients)) < n.degree { solicited = true } else { solicited = false } log.Debug("solicited: %v", solicited) c := &client{ conn: conn, enc: gob.NewEncoder(conn), dec: gob.NewDecoder(conn), ack: make(chan uint64, RECEIVE_BUFFER), } // the handshake involves the following: // 1. We send our name and our solicitation status // 2a. If the connection is solicited but we're all full, the remote node simply hangs up // 2b. If the connection is unsolicited or solicited and we are still soliciting connections, the remote node responds with its name // 3. The connection is valid, add it to our client list and broadcast a MSA announcing the new connection. // 4. The remote node does the same as 3. err := c.enc.Encode(n.name) if err != nil { log.Error("newConnection encode name: %v: %v", n.name, err) c.conn.Close() return } err = c.enc.Encode(solicited) if err != nil { log.Error("newConnection encode solicited: %v: %v", n.name, err) c.conn.Close() return } var resp string err = c.dec.Decode(&resp) if err != nil { if err != io.EOF { log.Error("newConnection decode name: %v: %v", n.name, err) } c.conn.Close() return } c.name = resp log.Debug("handshake from: %v", c.name) n.clientLock.Lock() n.clients[resp] = c n.clientLock.Unlock() go n.clientHandler(resp) }
// Send a message according to the parameters set in the message. // Users will generally use the Set and Broadcast functions instead of Send. // The returned error is always nil if the message type is broadcast. // If an error is encountered, Send returns immediately. func (n *Node) Send(m *Message) ([]string, error) { if log.WillLog(log.DEBUG) { log.Debug("Send: %v", m) } // force updating the network if needed on Send() n.checkUpdateNetwork() routeSlices, err := n.getRoutes(m) if err != nil { return nil, err } if log.WillLog(log.DEBUG) { log.Debug("routeSlices: %v", routeSlices) } errChan := make(chan error) for k, v := range routeSlices { go func(client string, recipients []string) { mOne := &Message{ Recipients: recipients, Source: m.Source, CurrentRoute: m.CurrentRoute, Command: m.Command, Body: m.Body, } err := n.clientSend(client, mOne) if err != nil { errChan <- err } else { errChan <- nil } }(k, v) } // wait on all of the client sends to complete var ret string for i := 0; i < len(routeSlices); i++ { r := <-errChan if r != nil { ret += r.Error() + "\n" } } // Rebuild the recipients from the routeSlices so that the caller can know // which recipients were actually valid. recipients := []string{} for _, r := range routeSlices { recipients = append(recipients, r...) } if ret == "" { return recipients, nil } return recipients, errors.New(ret) }