func logSetup() { level, err := log.LevelInt(*f_loglevel) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } color := true if runtime.GOOS == "windows" { color = false } if *f_log { log.AddLogger("stdio", os.Stderr, level, color) } if *f_logfile != "" { err := os.MkdirAll(filepath.Dir(*f_logfile), 0755) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } logfile, err := os.OpenFile(*f_logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } log.AddLogger("file", logfile, level, false) } }
func cliLogStderr(c *minicli.Command) *minicli.Response { resp := &minicli.Response{Host: hostname} if c.BoolArgs["false"] { // Turn off logging to stderr log.DelLogger("stdio") } else if len(c.BoolArgs) == 0 { // Print true or false depending on whether stderr is enabled _, err := log.GetLevel("stdio") resp.Response = strconv.FormatBool(err == nil) } else if c.BoolArgs["true"] { // Enable stderr logging or adjust the level if already enabled level, _ := log.LevelInt(*f_loglevel) _, err := log.GetLevel("stdio") if err != nil { log.AddLogger("stdio", os.Stderr, level, true) } else { // TODO: Why do this? cliLogLevel updates stdio level whenever // f_loglevel is changed. log.SetLevel("stdio", level) } } return resp }
func cliLogFile(c *minicli.Command) *minicli.Response { resp := &minicli.Response{Host: hostname} if len(c.StringArgs) == 0 { // Print true or false depending on whether file is enabled if logFile != nil { resp.Response = logFile.Name() } } else { var err error // Enable logging to file if it's not already enabled level, _ := log.LevelInt(*f_loglevel) if logFile != nil { if err = stopFileLogger(); err != nil { resp.Error = err.Error() return resp } } flags := os.O_WRONLY | os.O_APPEND | os.O_CREATE logFile, err = os.OpenFile(c.StringArgs["file"], flags, 0660) if err != nil { resp.Error = err.Error() } else { log.AddLogger("file", logFile, level, false) } } return resp }
func cliLogFile(c *minicli.Command, resp *minicli.Response) error { if len(c.StringArgs) == 0 { // Print true or false depending on whether file is enabled if logFile != nil { resp.Response = logFile.Name() } return nil } // Enable logging to file if it's not already enabled level, _ := log.LevelInt(*f_loglevel) if logFile != nil { if err := stopFileLogger(); err != nil { return err } } err := os.MkdirAll(filepath.Dir(c.StringArgs["file"]), 0755) if err != nil { return err } flags := os.O_WRONLY | os.O_APPEND | os.O_CREATE logFile, err = os.OpenFile(c.StringArgs["file"], flags, 0660) if err != nil { return err } log.AddLogger("file", logFile, level, false) return nil }
func logSetup() { level, err := log.LevelInt(*f_loglevel) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } color := true if runtime.GOOS == "windows" { color = false } if *f_log { log.AddLogger("stdio", os.Stderr, level, color) } if *f_logfile != "" { err := os.MkdirAll(filepath.Dir(*f_logfile), 0755) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } logfile, err := os.OpenFile(*f_logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } log.AddLogger("file", logfile, level, false) } // a special logger for pushing logs up to minimega if *f_miniccc != "" && *f_u == "" { var f tagLogger log.AddLogger("taglogger", &f, level, false) } }
// golang can't easily support the typical clone+exec method of firing off a // child process. We need a child process to do ample setup for containers // *post* clone. We have two options - duplicate the forkAndExec shim in // src/syscall to do a clone and get into a new minimega that can finish the // container setup, or start a new minimega with an 'nsinit' C shim *before* // the runtime starts. Docker and others use the latter, though it's not // entirely clear why they don't just borrow the forkAndExec method. We'll use // the forkAndExec method here. // // This function is a shim to finalize container setup from a running minimega // parent. It expects to be called inside namespace isolations (mount, pid, // etc...) with args: minimega CONTAINER hostname fsPath... // // A number of fds get passed to the child on specific fd numbers: // // 3: logging port, closed just before exec into init // 4: closed by the child before exec to elect to be frozen // 5: closed by the parent when the child returns to allow calling exec // 6: stdin // 7: stdout // 8: stderr // // A number of arguments are passed on os.Args to configure the container: // 0 : minimega binary // 1 : CONTAINER // 2 : instance path // 3 : vm id // 4 : hostname ("CONTAINER_NONE" if none) // 5 : filesystem path // 6 : memory in megabytes // 7 : uuid // 8 : number of fifos // 9 : init program (relative to filesystem path) // 10: init args func containerShim() { if len(os.Args) < 10 { // 10 because init args can be nil os.Exit(1) } // we log to fd(3), and close it before we move on to exec ourselves logFile := os.NewFile(uintptr(3), "") log.AddLogger("file", logFile, log.DEBUG, false) log.Debug("containerShim: %v", os.Args) // dup2 stdio err := syscall.Dup2(6, syscall.Stdin) if err != nil { log.Fatalln(err) } err = syscall.Dup2(7, syscall.Stdout) if err != nil { log.Fatalln(err) } err = syscall.Dup2(8, syscall.Stderr) if err != nil { log.Fatalln(err) } // get args vmInstancePath := os.Args[2] vmID, err := strconv.Atoi(os.Args[3]) if err != nil { log.Fatalln(err) } vmHostname := os.Args[4] if vmHostname == CONTAINER_NONE { vmHostname = "" } vmFSPath := os.Args[5] vmMemory, err := strconv.Atoi(os.Args[6]) if err != nil { log.Fatalln(err) } vmUUID := os.Args[7] vmFifos, err := strconv.Atoi(os.Args[8]) if err != nil { log.Fatalln(err) } vmInit := os.Args[9:] // set hostname log.Debug("vm %v hostname", vmID) if vmHostname != "" { _, err := processWrapper("hostname", vmHostname) if err != nil { log.Fatal("set hostname: %v", err) } } // setup the root fs log.Debug("vm %v containerSetupRoot", vmID) err = containerSetupRoot(vmFSPath) if err != nil { log.Fatal("containerSetupRoot: %v", err) } // mount defaults log.Debug("vm %v containerMountDefaults", vmID) err = containerMountDefaults(vmFSPath) if err != nil { log.Fatal("containerMountDefaults: %v", err) } // mknod log.Debug("vm %v containerMknodDevices", vmID) err = containerMknodDevices(vmFSPath) if err != nil { log.Fatal("containerMknodDevices: %v", err) } // pseudoterminals log.Debug("vm %v containerPtmx", vmID) err = containerPtmx(vmFSPath) if err != nil { log.Fatal("containerPtmx: %v", err) } // symlinks log.Debug("vm %v containerSymlinks", vmID) err = containerSymlinks(vmFSPath) if err != nil { log.Fatal("containerSymlinks: %v", err) } // remount key paths as read-only log.Debug("vm %v containerRemountReadOnly", vmID) err = containerRemountReadOnly(vmFSPath) if err != nil { log.Fatal("containerRemountReadOnly: %v", err) } // mask uuid path log.Debug("uuid bind mount: %v -> %v", vmUUID, containerUUIDLink) err = syscall.Mount(vmUUID, filepath.Join(vmFSPath, containerUUIDLink), "", syscall.MS_BIND, "") if err != nil { log.Fatal("containerUUIDLink: %v", err) } // bind mount fifos log.Debug("vm %v containerFifos", vmID) err = containerFifos(vmFSPath, vmInstancePath, vmFifos) if err != nil { log.Fatal("containerFifos: %v", err) } // mask paths log.Debug("vm %v containerMaskPaths", vmID) err = containerMaskPaths(vmFSPath) if err != nil { log.Fatal("containerMaskPaths: %v", err) } // setup cgroups for this vm log.Debug("vm %v containerPopulateCgroups", vmID) err = containerPopulateCgroups(vmID, vmMemory) if err != nil { log.Fatal("containerPopulateCgroups: %v", err) } // chdir log.Debug("vm %v chdir", vmID) err = syscall.Chdir(vmFSPath) if err != nil { log.Fatal("chdir: %v", err) } // attempt to chroot log.Debug("vm %v containerChroot", vmID) err = containerChroot(vmFSPath) if err != nil { log.Fatal("containerChroot: %v", err) } // set capabilities log.Debug("vm %v containerSetCapabilities", vmID) err = containerSetCapabilities() if err != nil { log.Fatal("containerSetCapabilities: %v", err) } // in order to synchronize freezing the container before we call init, // we close fd(4) to signal the parent that we're ready to freeze. We // then read fd(5) in order to block for the parent. The parent will // freeze the child and close the other end of fd(5). Upon unfreezing, // the read will fail and we can move on to exec. log.Debug("sync for freezing") sync1 := os.NewFile(uintptr(4), "") sync2 := os.NewFile(uintptr(5), "") sync1.Close() var buf = make([]byte, 1) sync2.Read(buf) log.Debug("return from freezing") // close fds we don't want in init logFile.Close() // GO! log.Debug("vm %v exec: %v %v", vmID, vmInit) err = syscall.Exec(vmInit[0], vmInit, nil) if err != nil { log.Fatal("Exec: %v", err) } // the new child process will exit and the parent will catch it log.Fatalln("how did I get here?") }
func init() { log.AddLogger("stdio", os.Stderr, log.DEBUG, true) }