func commandSocketHandle(c net.Conn) { defer c.Close() enc := json.NewEncoder(c) dec := json.NewDecoder(c) var err error for err == nil { var cmd *minicli.Command cmd, err = readLocalCommand(dec) if err != nil { break } if cmd == nil { err = sendLocalResp(enc, nil, false) continue } // HAX: Don't record the read command if hasCommand(cmd, "read") { cmd.SetRecord(false) } // HAX: Work around so that we can add the more boolean. var prev minicli.Responses // Keep sending until we hit the first error, then just consume the // channel to ensure that we release any locks acquired by cmd. for resp := range RunCommands(cmd) { if prev != nil && err == nil { err = sendLocalResp(enc, prev, true) } else if err != nil && len(resp) > 0 { log.Info("dropping resp from %v", resp[0].Host) } prev = resp } if err == nil { err = sendLocalResp(enc, prev, false) } } // finally, log the error, if there was one if err == nil || err == io.EOF { log.Infoln("command client disconnected") } else if err != nil && strings.Contains(err.Error(), "write: broken pipe") { log.Infoln("command client disconnected without waiting for responses") } else if err != nil { log.Errorln(err) } }
func dnsmasqKill(id int) error { pid := dnsmasqPID(id) log.Debug("dnsmasq id %v has pid %v", id, pid) if pid == -1 { return fmt.Errorf("invalid id") } var sOut bytes.Buffer var sErr bytes.Buffer p := process("kill") cmd := &exec.Cmd{ Path: p, Args: []string{ p, fmt.Sprintf("%v", pid), }, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } log.Infoln("killing dnsmasq server:", pid) err := cmd.Run() if err != nil { return err } 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 nukeBridges() { bNames := nukeBridgeNames(false) for _, b := range bNames { var sOut bytes.Buffer var sErr bytes.Buffer p := process("ovs") cmd := &exec.Cmd{ Path: p, Args: []string{ p, "del-br", b, }, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } log.Infoln("removing bridge:", b) //err := cmd.Run() err := cmdTimeout(cmd, OVS_TIMEOUT) if err != nil { log.Error("%v: %v", err, sErr.String()) } } }
func (vms VMs) flush() { for i, vm := range vms { if vm.State()&(VM_QUIT|VM_ERROR) != 0 { log.Infoln("deleting VM: ", i) delete(vms, i) } } }
func (vms VMs) flush() { stateMask := VM_QUIT | VM_ERROR for i, vm := range vms { if vm.State&stateMask != 0 { log.Infoln("deleting VM: ", i) delete(vms, i) } } }
func (vms VMs) flush() { vmLock.Lock() defer vmLock.Unlock() for i, vm := range vms { if vm.GetState()&(VM_QUIT|VM_ERROR) != 0 { log.Infoln("deleting VM: ", i) delete(vms, i) } } }
func sendLocalResp(enc *json.Encoder, resp minicli.Responses, more bool) error { log.Infoln("sending resp:", resp) r := localResponse{ More: more, } if resp != nil { r.Resp = resp r.Rendered = resp.String() } return enc.Encode(&r) }
func commandSocketHandle(c net.Conn) { var err error enc := json.NewEncoder(c) dec := json.NewDecoder(c) outer: for err == nil { var cmd *minicli.Command cmd, err = readLocalCommand(dec) if err != nil { if err != io.EOF { // Must be incompatible versions of minimega... F*** log.Errorln(err) } break } err = nil var prevResp minicli.Responses if cmd != nil { // HAX: Don't record the read command if hasCommand(cmd, "read") { cmd.Record = false } // HAX: Work around so that we can add the more boolean for resp := range runCommand(cmd) { if prevResp != nil { err = sendLocalResp(enc, prevResp, true) if err != nil { break outer } } prevResp = resp } } if err == nil { err = sendLocalResp(enc, prevResp, false) } } if err != nil { if err == io.EOF { log.Infoln("command client disconnected") } else { log.Errorln(err) } } }
// runCommand runs a command through a JSON pipe. func runCommand(cmd Command) chan *localResponse { conn, err := net.Dial("unix", path.Join(*f_minimega, "minimega")) if err != nil { log.Errorln(err) return nil } enc := json.NewEncoder(conn) dec := json.NewDecoder(conn) log.Debug("encoding command: %v", cmd) err = enc.Encode(cmd) if err != nil { log.Errorln("local command json encode: %v", err) return nil } log.Debugln("encoded command:", cmd) respChan := make(chan *localResponse) go func() { defer close(respChan) for { var r localResponse err = dec.Decode(&r) if err != nil { if err == io.EOF { log.Infoln("server disconnected") return } log.Errorln("local command json decode: %v", err) return } respChan <- &r if !r.More { log.Debugln("got last message") break } else { log.Debugln("expecting more data") } } }() return respChan }
// Overlays copies any overlay directories indicated in c into the build // directory build_path. Overlays are copied in depth-first order, so that // the oldest parent overlay data is copied in first. This allows a child // to overwrite any overlay data created by a parent. func Overlays(buildPath string, c vmconfig.Config) error { // copy the overlays in order for i, o := range c.Overlays { log.Infoln("copying overlay:", o) var sourcePath string // check if overlay exists as absolute path or relative to cwd if _, err := os.Stat(o); os.IsNotExist(err) { // it doesn't, so we'll check relative to config file log.Debugln("overlay directory '%v' does not exist as an absolute path or relative to the current working directory.", o) var path string base := filepath.Base(o) // get base path of overlay directory if i == len(c.Overlays)-1 { // if this is the last overlay, we'll check relative to c.Path log.Debugln("non-parent overlay") path = filepath.Join(filepath.Dir(c.Path), base) } else { // if not, it's a parent overlay and we'll check relative to c.Parents[i] log.Debugln("parent overlay") path = filepath.Join(filepath.Dir(c.Parents[i]), base) } log.Debugln("checking path relative to config location: '%v'", path) if _, err := os.Stat(path); os.IsNotExist(err) { // check if we can find overlay relative to config file return err // nope } else { // yep sourcePath = path } } else { sourcePath = o } p := process("cp") cmd := exec.Command(p, "-r", "-v", sourcePath+"/.", buildPath) stdout, err := cmd.StdoutPipe() if err != nil { return err } stderr, err := cmd.StderrPipe() if err != nil { return err } log.LogAll(stdout, log.INFO, "cp") log.LogAll(stderr, log.ERROR, "cp") err = cmd.Run() if err != nil { return err } } return nil }
func dnsmasqKill(id int) error { pid := dnsmasqPID(id) log.Debug("dnsmasq id %v has pid %v", id, pid) if pid == -1 { return fmt.Errorf("invalid id") } log.Infoln("killing dnsmasq server:", pid) _, err := processWrapper("kill", fmt.Sprintf("%v", pid)) if err != nil { return err } return nil }
func commandSocketStart() { l, err := net.Listen("unix", filepath.Join(*f_path, "minirouter")) if err != nil { log.Fatalln("commandSocketStart: %v", err) } for { conn, err := l.Accept() if err != nil { log.Error("commandSocketStart: accept: %v", err) } log.Infoln("client connected") go commandSocketHandle(conn) } }
func commandSocketStart() { l, err := net.Listen("unix", filepath.Join(*f_base, "minimega")) if err != nil { log.Error("commandSocketStart: %v", err) teardown() } for { conn, err := l.Accept() if err != nil { log.Error("commandSocketStart: accept: %v", err) } log.Infoln("client connected") go commandSocketHandle(conn) } }
// runCommand runs a command through a JSON pipe. func (mm *MinimegaConn) runCommand(cmd *minicli.Command) chan *localResponse { err := mm.enc.Encode(*cmd) if err != nil { log.Errorln("local command gob encode: %v", err) return nil } log.Debugln("encoded command:", cmd) respChan := make(chan *localResponse) go func() { defer close(respChan) for { var r localResponse err = mm.dec.Decode(&r) if err != nil { if err == io.EOF { log.Infoln("server disconnected") return } log.Errorln("local command gob decode: %v", err) return } respChan <- &r if !r.More { log.Debugln("got last message") break } else { log.Debugln("expecting more data") } } }() return respChan }
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 } var sOut bytes.Buffer var sErr bytes.Buffer p := process("kill") cmd := &exec.Cmd{ Path: p, Args: []string{ p, t, }, Env: nil, Dir: "", Stdout: &sOut, Stderr: &sErr, } log.Infoln("killing process:", t) err = cmd.Run() if err != nil { log.Error("%v: %v", err, sErr.String()) } } return nil }
func localCommand() { a := flag.Args() log.Debugln("got args:", a) command := strings.Join(a, " ") // TODO: Need to escape? cmd, err := minicli.CompileCommand(command) if err != nil { log.Fatal(err.Error()) } if cmd == nil { log.Debugln("cmd is nil") return } log.Infoln("got command:", cmd) mm, err := DialMinimega() if err != nil { log.Fatalln(err) } for resp := range mm.runCommand(cmd) { if resp.Rendered != "" { fmt.Println(resp.Rendered) } errs := resp.Resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } }
// aggressively cleanup container cruff, called by the nuke api func containerNuke() { // walk /sys/fs/cgroup/minimega for tasks, killing each one err := filepath.Walk(CGROUP_PATH, func(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) pids := strings.Fields(string(d)) for _, pid := range pids { log.Debug("found pid: %v", pid) log.Infoln("killing process:", pid) out, err := processWrapper("kill", "-9", pid) if err != nil { log.Error("%v: %v", err, out) } } // remove the directory for this vm dir := filepath.Dir(path) err = os.Remove(dir) if err != nil { log.Errorln(err) } } return nil }) // remove cgroup structure err = os.Remove(CGROUP_PATH) if err != nil { log.Errorln(err) } // umount megamount_* d, err := ioutil.ReadFile("/proc/mounts") mounts := strings.Fields(string(d)) for _, m := range mounts { if strings.Contains(m, "megamount") { err := syscall.Unmount(m, 0) if err != nil { log.Error("overlay unmount: %v", err) } } } // remove meganet_* from /var/run/netns netns, err := ioutil.ReadDir("/var/run/netns") if err != nil { log.Errorln(err) } else { for _, n := range netns { if strings.Contains(n.Name(), "meganet") { err := os.Remove(filepath.Join("/var/run/netns", n.Name())) if err != nil { log.Errorln(err) } } } } }
func ksmSave() { log.Infoln("saving ksm values") ksmRun = ksmGetIntFromFile(ksmPathRun) ksmPagesToScan = ksmGetIntFromFile(ksmPathPagesToScan) ksmSleepMillisecs = ksmGetIntFromFile(ksmPathSleepMillisecs) }
func dnsmasqPath() (string, error) { path, err := ioutil.TempDir(*f_base, "dnsmasq_") log.Infoln("created dnsmasq server path: ", path) return path, err }
func main() { var err error flag.Usage = usage flag.Parse() if !strings.HasSuffix(*f_base, "/") { *f_base += "/" } if *f_cli { doc, err := minicli.Doc() if err != nil { log.Fatal("failed to generate docs: %v", err) } fmt.Println(doc) os.Exit(0) } // rebase f_iomBase if f_base changed but iomBase did not if *f_base != BASE_PATH && *f_iomBase == IOM_PATH { *f_iomBase = *f_base + "files" } if !strings.HasSuffix(*f_iomBase, "/") { *f_iomBase += "/" } if *f_version { fmt.Println("minimega", version.Revision, version.Date) fmt.Println(version.Copyright) os.Exit(0) } logSetup() hostname, err = os.Hostname() if err != nil { log.Fatalln(err) } if isReserved(hostname) { log.Warn("hostname `%s` is a reserved word -- abandon all hope, ye who enter here", hostname) } vms = make(map[int]VM) // special case, catch -e and execute a command on an already running // minimega instance if *f_e || *f_attach { // try to connect to the local minimega mm, err := miniclient.Dial(*f_base) if err != nil { log.Fatalln(err) } if *f_e { a := flag.Args() log.Debugln("got args:", a) // TODO: Need to escape? cmd := minicli.MustCompile(strings.Join(a, " ")) log.Infoln("got command:", cmd) mm.RunAndPrint(cmd, false) } else { mm.Attach() } return } // warn if we're not root user, err := user.Current() if err != nil { log.Fatalln(err) } if user.Uid != "0" { log.Warnln("not running as root") } // check for a running instance of minimega _, err = os.Stat(*f_base + "minimega") if err == nil { if !*f_force { log.Fatalln("minimega appears to already be running, override with -force") } log.Warn("minimega may already be running, proceed with caution") err = os.Remove(*f_base + "minimega") if err != nil { log.Fatalln(err) } } // set up signal handling sig := make(chan os.Signal, 1024) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) go func() { first := true for { <-sig if *f_panic { panic("teardown") } if first { log.Info("caught signal, tearing down, ctrl-c again will force quit") go teardown() first = false } else { os.Exit(1) } } }() err = checkExternal() if err != nil { log.Warnln(err.Error()) } // attempt to set up the base path err = os.MkdirAll(*f_base, os.FileMode(0770)) if err != nil { log.Fatal("mkdir base path: %v", err) } pid := os.Getpid() err = ioutil.WriteFile(*f_base+"minimega.pid", []byte(fmt.Sprintf("%v", pid)), 0664) if err != nil { log.Error("write minimega pid: %v", err) teardown() } go commandSocketStart() // create a node for meshage host, err := os.Hostname() if err != nil { log.Fatalln(err) } meshageInit(host, *f_namespace, uint(*f_degree), *f_port) fmt.Println(banner) // fan out to the number of cpus on the system if GOMAXPROCS env variable is // not set. if os.Getenv("GOMAXPROCS") == "" { cpus := runtime.NumCPU() runtime.GOMAXPROCS(cpus) } if !*f_nostdin { cliLocal() } else { <-sig if *f_panic { panic("teardown") } } teardown() }
// reentrant read routine. Will be called recursively if a 'parents' key exists in the config file func read(path string, c *Config) error { f, err := os.Open(path) if err != nil { if strings.Contains(err.Error(), "no such file") { // file doesn't exist, let's try some path magic if path == c.Path { return err } newpath := filepath.Join(filepath.Dir(c.Path), filepath.Base(path)) f, err = os.Open(newpath) if err != nil { return err } log.Warn("could not find %v, but found a similar one at %v, using that instead", path, newpath) } else { return err } } defer f.Close() var s scanner.Scanner s.Init(f) tok := s.Scan() for tok != scanner.EOF { pos := s.Pos() if tok != scanner.Ident { err = fmt.Errorf("%s:%s malformed config: %s, expected identifier, got %s", path, pos, s.TokenText(), scanner.TokenString(tok)) return err } k := s.TokenText() tok = s.Scan() if tok != '=' { err = fmt.Errorf("%s:%s malformed config: %s, expected '=', got %s", path, pos, s.TokenText(), scanner.TokenString(tok)) return err } tok = s.Scan() if tok != scanner.String { err = fmt.Errorf("%s:%s malformed config %s, expected string, got %s", path, pos, s.TokenText(), scanner.TokenString(tok)) return err } v := strings.Trim(s.TokenText(), "\"`") d := strings.Fields(v) switch k { case "parents": for _, i := range d { log.Infoln("reading config:", i) err = read(i, c) c.Parents = append(c.Parents, i) if err != nil { return err } } case "packages": c.Packages = append(c.Packages, d...) case "overlay": // trim any trailing "/" for i, j := range d { d[i] = strings.TrimRight(j, "/") } c.Overlays = append(c.Overlays, d...) case "postbuild": c.Postbuilds = append(c.Postbuilds, v) default: err = fmt.Errorf("invalid key %s", k, d) return err } tok = s.Scan() } return nil }
func main() { var err error flag.Usage = usage flag.Parse() logSetup() // see containerShim() if flag.NArg() > 1 && flag.Arg(0) == CONTAINER_MAGIC { containerShim() } cliSetup() if *f_cli { if err := minicli.Validate(); err != nil { log.Fatalln(err) } doc, err := minicli.Doc() if err != nil { log.Fatal("failed to generate docs: %v", err) } fmt.Println(doc) os.Exit(0) } // rebase f_iomBase if f_base changed but iomBase did not if *f_base != BASE_PATH && *f_iomBase == IOM_PATH { *f_iomBase = filepath.Join(*f_base, "files") } if *f_version { fmt.Println("minimega", version.Revision, version.Date) fmt.Println(version.Copyright) os.Exit(0) } hostname, err = os.Hostname() if err != nil { log.Fatalln(err) } if isReserved(hostname) { log.Warn("hostname `%s` is a reserved word -- abandon all hope, ye who enter here", hostname) } // special case, catch -e and execute a command on an already running // minimega instance if *f_e || *f_attach { // try to connect to the local minimega mm, err := miniclient.Dial(*f_base) if err != nil { log.Fatalln(err) } mm.Pager = minipager.DefaultPager if *f_e { a := flag.Args() log.Debugln("got args:", a) // TODO: Need to escape? cmd := minicli.MustCompile(strings.Join(a, " ")) log.Infoln("got command:", cmd) mm.RunAndPrint(cmd, false) } else { mm.Attach() } return } // warn if we're not root user, err := user.Current() if err != nil { log.Fatalln(err) } if user.Uid != "0" { log.Warnln("not running as root") } // check for a running instance of minimega _, err = os.Stat(filepath.Join(*f_base, "minimega")) if err == nil { if !*f_force { log.Fatalln("minimega appears to already be running, override with -force") } log.Warn("minimega may already be running, proceed with caution") err = os.Remove(filepath.Join(*f_base, "minimega")) if err != nil { log.Fatalln(err) } } // set up signal handling sig := make(chan os.Signal, 1024) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) go func() { first := true for s := range sig { if s == os.Interrupt && first { // do nothing continue } if *f_panic { panic("teardown") } if first { log.Info("caught signal, tearing down, ctrl-c again will force quit") go teardown() first = false } else { os.Exit(1) } } }() err = checkExternal() if err != nil { log.Warnln(err.Error()) } // attempt to set up the base path err = os.MkdirAll(*f_base, os.FileMode(0770)) if err != nil { log.Fatal("mkdir base path: %v", err) } pid := os.Getpid() writeOrDie(filepath.Join(*f_base, "minimega.pid"), strconv.Itoa(pid)) go commandSocketStart() // create a node for meshage host, err := os.Hostname() if err != nil { log.Fatalln(err) } meshageInit(host, *f_context, *f_degree, *f_msaTimeout, *f_port) // start the cc service ccStart() // start tap reaper go periodicReapTaps() fmt.Println(banner) // fan out to the number of cpus on the system if GOMAXPROCS env variable is // not set. if os.Getenv("GOMAXPROCS") == "" { cpus := runtime.NumCPU() runtime.GOMAXPROCS(cpus) } if !*f_nostdin { cliLocal() } else { <-sig if *f_panic { panic("teardown") } } teardown() }
func main() { flag.Usage = usage flag.Parse() logSetup() if flag.NArg() != 1 { usage() os.Exit(1) } externalCheck() // stage 1 and stage 2 flags are mutually exclusive if *f_stage1 && *f_stage2 != "" { log.Fatalln("-1 cannot be used with -2") } // find any other dependent configs and get an ordered list of those configfile := flag.Arg(0) log.Debugln("using config:", configfile) config, err := vmconfig.ReadConfig(configfile) if err != nil { log.Fatalln(err) } else { log.Debugln("read config:", config) } // If we're doing a LiveCD, we need to add the live-boot package if *f_iso { config.Packages = append(config.Packages, "live-boot") } var buildPath string // stage 1 if *f_stage2 == "" { // create a build path buildPath, err = ioutil.TempDir("", "vmbetter_build_") if err != nil { log.Fatalln("cannot create temporary directory:", err) } log.Debugln("using build path:", buildPath) // invoke debootstrap fmt.Println("invoking debootstrap (this may take a while)...") err = Debootstrap(buildPath, config) if err != nil { log.Fatalln(err) } // copy any overlay into place in reverse order of opened dependencies fmt.Println("copying overlays") err = Overlays(buildPath, config) if err != nil { log.Fatalln(err) } // // stage 1 complete // if *f_stage1 { stage1Target := strings.Split(filepath.Base(config.Path), ".")[0] + "_stage1" log.Infoln("writing stage 1 target", stage1Target) err = os.Mkdir(stage1Target, 0666) if err != nil { log.Fatalln(err) } p := process("cp") cmd := exec.Command(p, "-r", "-v", buildPath+"/.", stage1Target) stdout, err := cmd.StdoutPipe() if err != nil { log.Fatalln(err) } stderr, err := cmd.StderrPipe() if err != nil { log.Fatalln(err) } log.LogAll(stdout, log.INFO, "cp") log.LogAll(stderr, log.ERROR, "cp") err = cmd.Run() if err != nil { log.Fatalln(err) } } } else { buildPath = *f_stage2 } // stage 2 if *f_stage2 != "" || !*f_stage1 { // call post build chroot commands in reverse order as well fmt.Println("executing post-build commands") err = PostBuildCommands(buildPath, config) if err != nil { log.Fatalln(err) } // build the image file fmt.Println("building target files") if *f_qcow { err = Buildqcow2(buildPath, config) } else if *f_iso { err = BuildISO(buildPath, config) } else { err = BuildTargets(buildPath, config) } if err != nil { log.Fatalln(err) } } // cleanup? if !*f_noclean && *f_stage2 == "" { fmt.Println("cleaning up") err = os.RemoveAll(buildPath) if err != nil { log.Errorln(err) } } fmt.Println("done") }