func main() { defer func() { if r := recover(); r != nil { log.Errorf("run time panic: %s : %s", r, debug.Stack()) } reboot() }() src, err := extraconfig.GuestInfoSourceWithPrefix("init") if err != nil { log.Error(err) return } extraconfig.Decode(src, &config) debugLevel = config.Diagnostics.DebugLevel if debugLevel > 2 { enableShell() } setLogLevels() logFile, err := os.OpenFile("/dev/ttyS1", os.O_WRONLY|os.O_SYNC, 0644) if err != nil { log.Errorf("Could not pipe stderr to serial for debugging info. Some debug info may be lost! Error reported was %s", err) } err = syscall.Dup3(int(logFile.Fd()), int(os.Stderr.Fd()), 0) if err != nil { log.Errorf("Could not pipe logfile to standard error due to error %s", err) } _, err = os.Stderr.WriteString("all stderr redirected to debug log") if err != nil { log.Errorf("Could not write to Stderr due to error %s", err) } sink, err := extraconfig.GuestInfoSinkWithPrefix("init") if err != nil { log.Error(err) return } // create the tether tthr = tether.New(src, sink, &operations{}) // register the toolbox extension and configure for appliance toolbox := configureToolbox(tether.NewToolbox()) toolbox.PrimaryIP = externalIP tthr.Register("Toolbox", toolbox) err = tthr.Start() if err != nil { log.Error(err) return } log.Info("Clean exit from init") }
/* * This is called by lxd when called as "lxd forkstart <container>" * 'forkstart' is used instead of just 'start' in the hopes that people * do not accidentally type 'lxd start' instead of 'lxc start' * * We expect to read the lxcconfig over fd 3. */ func startContainer(args []string) error { if len(args) != 4 { return fmt.Errorf("Bad arguments: %q", args) } name := args[1] lxcpath := args[2] configPath := args[3] c, err := lxc.NewContainer(name, lxcpath) if err != nil { return fmt.Errorf("Error initializing container for start: %q", err) } err = c.LoadConfigFile(configPath) if err != nil { return fmt.Errorf("Error opening startup config file: %q", err) } /* due to https://github.com/golang/go/issues/13155 and the * CollectOutput call we make for the forkstart process, we need to * close our stdin/stdout/stderr here. Collecting some of the logs is * better than collecting no logs, though. */ os.Stdin.Close() os.Stderr.Close() os.Stdout.Close() // Redirect stdout and stderr to a log file logPath := shared.LogPath(name, "forkstart.log") if shared.PathExists(logPath) { os.Remove(logPath) } logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) if err == nil { syscall.Dup3(int(logFile.Fd()), 1, 0) syscall.Dup3(int(logFile.Fd()), 2, 0) } // Move the config so we can inspect it on failure shared.FileMove(configPath, shared.LogPath(name, "lxc.conf")) return c.Start() }
func setupLogging(config *Config) error { if config.LogFile == "" { return nil } if err := createDirs(path.Dir(config.LogFile)); err != nil { return err } output, err := os.Create(config.LogFile) if err != nil { return err } syscall.Dup3(int(output.Fd()), int(os.Stdout.Fd()), 0) syscall.Dup3(int(output.Fd()), int(os.Stderr.Fd()), 0) return nil }
func setupLogging(config *Config) error { if config.LogFile == "" { return nil } if err := createDirs(path.Dir(config.LogFile)); err != nil { return err } output, err := os.OpenFile(config.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return err } syscall.Dup3(int(output.Fd()), int(os.Stdout.Fd()), 0) syscall.Dup3(int(output.Fd()), int(os.Stderr.Fd()), 0) return nil }
// dupStdio opens the slavePath for the console and dups the fds to the current // processes stdio, fd 0,1,2. func (c *linuxConsole) dupStdio() error { slave, err := c.open(syscall.O_RDWR) if err != nil { return err } fd := int(slave.Fd()) for _, i := range []int{0, 1, 2} { if err := syscall.Dup3(fd, i, 0); err != nil { return err } } return nil }
// If stdin, stdout, and/or stderr are pointing to `/dev/null` in the parent's rootfs // this method will make them point to `/dev/null` in this container's rootfs. This // needs to be called after we chroot/pivot into the container's rootfs so that any // symlinks are resolved locally. func reOpenDevNull(rootfs string) error { var stat, devNullStat syscall.Stat_t file, err := os.Open("/dev/null") if err != nil { return fmt.Errorf("Failed to open /dev/null - %s", err) } defer file.Close() if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil { return err } for fd := 0; fd < 3; fd++ { if err := syscall.Fstat(fd, &stat); err != nil { return err } if stat.Rdev == devNullStat.Rdev { // Close and re-open the fd. if err := syscall.Dup3(int(file.Fd()), fd, 0); err != nil { return err } } } return nil }
func main() { defer func() { if r := recover(); r != nil { log.Errorf("run time panic: %s : %s", r, debug.Stack()) } halt() }() logFile, err := os.OpenFile("/dev/ttyS1", os.O_WRONLY|os.O_SYNC, 0644) if err != nil { log.Errorf("Could not open serial port for debugging info. Some debug info may be lost! Error reported was %s", err) } if err = syscall.Dup3(int(logFile.Fd()), int(os.Stderr.Fd()), 0); err != nil { log.Errorf("Could not pipe logfile to standard error due to error %s", err) } if _, err = os.Stderr.WriteString("all stderr redirected to debug log"); err != nil { log.Errorf("Could not write to Stderr due to error %s", err) } // where to look for the various devices and files related to tether pathPrefix = "/.tether" if strings.HasSuffix(os.Args[0], "-debug") { extraconfig.DecodeLogLevel = log.DebugLevel extraconfig.EncodeLogLevel = log.DebugLevel } // use the same logger for trace and other logging trace.Logger = log.StandardLogger() log.SetLevel(log.DebugLevel) // Initiliaze logger with default TextFormatter log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true}) // TODO: hard code executor initialization status reporting via guestinfo here err = createDevices() if err != nil { log.Error(err) // return gives us good behaviour in the case of "-debug" binary return } sshserver := NewAttachServerSSH() src, err := extraconfig.GuestInfoSource() if err != nil { log.Error(err) return } sink, err := extraconfig.GuestInfoSink() if err != nil { log.Error(err) return } // create the tether tthr = tether.New(src, sink, &operations{}) // register the attach extension tthr.Register("Attach", sshserver) // register the toolbox extension tthr.Register("Toolbox", tether.NewToolbox().InContainer()) err = tthr.Start() if err != nil { log.Error(err) return } log.Info("Clean exit from tether") }
/* * This is called by lxd when called as "lxd forkexec <container>" */ func execContainer(args []string) (int, error) { if len(args) < 6 { return -1, fmt.Errorf("Bad arguments: %q", args) } wait := true if args[1] == "nowait" { wait = false } name := args[2] lxcpath := args[3] configPath := args[4] c, err := lxc.NewContainer(name, lxcpath) if err != nil { return -1, fmt.Errorf("Error initializing container for start: %q", err) } err = c.LoadConfigFile(configPath) if err != nil { return -1, fmt.Errorf("Error opening startup config file: %q", err) } syscall.Dup3(int(os.Stdin.Fd()), 200, 0) syscall.Dup3(int(os.Stdout.Fd()), 201, 0) syscall.Dup3(int(os.Stderr.Fd()), 202, 0) syscall.Close(int(os.Stdin.Fd())) syscall.Close(int(os.Stdout.Fd())) syscall.Close(int(os.Stderr.Fd())) opts := lxc.DefaultAttachOptions opts.ClearEnv = true opts.StdinFd = 200 opts.StdoutFd = 201 opts.StderrFd = 202 logPath := shared.LogPath(name, "forkexec.log") if shared.PathExists(logPath) { os.Remove(logPath) } logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) if err == nil { syscall.Dup3(int(logFile.Fd()), 1, 0) syscall.Dup3(int(logFile.Fd()), 2, 0) } env := []string{} cmd := []string{} section := "" for _, arg := range args[5:len(args)] { // The "cmd" section must come last as it may contain a -- if arg == "--" && section != "cmd" { section = "" continue } if section == "" { section = arg continue } if section == "env" { fields := strings.SplitN(arg, "=", 2) if len(fields) == 2 && fields[0] == "HOME" { opts.Cwd = fields[1] } env = append(env, arg) } else if section == "cmd" { cmd = append(cmd, arg) } else { return -1, fmt.Errorf("Invalid exec section: %s", section) } } opts.Env = env var status int if wait { status, err = c.RunCommandStatus(cmd, opts) if err != nil { return -1, fmt.Errorf("Failed running command and waiting for it to exit: %q", err) } } else { status, err = c.RunCommandNoWait(cmd, opts) if err != nil { return -1, fmt.Errorf("Failed running command: %q", err) } // Send the PID of the executing process. w := os.NewFile(uintptr(3), "attachedPid") defer w.Close() err = json.NewEncoder(w).Encode(status) if err != nil { return -1, fmt.Errorf("Failed sending PID of executing command: %q", err) } proc, err := os.FindProcess(status) if err != nil { return -1, fmt.Errorf("Failed finding process: %q", err) } procState, err := proc.Wait() if err != nil { return -1, fmt.Errorf("Failed waiting on process %d: %q", status, err) } if procState.Success() { return 0, nil } status, ok := procState.Sys().(syscall.WaitStatus) if ok { if status.Exited() { return status.ExitStatus(), nil } // Backwards compatible behavior. Report success when we exited // due to a signal. Otherwise this may break Jenkins, e.g. when // lxc exec foo reboot receives SIGTERM and status.Exitstats() // would report -1. if status.Signaled() { return 0, nil } } return -1, fmt.Errorf("Command failed") } return status >> 8, nil }
func dup2(oldfd, newfd int) error { return syscall.Dup3(oldfd, newfd, 0) }
// Always use dup3 on Linux because dup2 is not available on arm64. func dup2(sourceFD, targetFD int) error { return syscall.Dup3(sourceFD, targetFD, 0) }
/* * This is called by lxd when called as "lxd forkexec <container>" */ func execContainer(args []string) (int, error) { if len(args) < 6 { return -1, fmt.Errorf("Bad arguments: %q", args) } name := args[1] lxcpath := args[2] configPath := args[3] c, err := lxc.NewContainer(name, lxcpath) if err != nil { return -1, fmt.Errorf("Error initializing container for start: %q", err) } err = c.LoadConfigFile(configPath) if err != nil { return -1, fmt.Errorf("Error opening startup config file: %q", err) } syscall.Dup3(int(os.Stdin.Fd()), 200, 0) syscall.Dup3(int(os.Stdout.Fd()), 201, 0) syscall.Dup3(int(os.Stderr.Fd()), 202, 0) syscall.Close(int(os.Stdin.Fd())) syscall.Close(int(os.Stdout.Fd())) syscall.Close(int(os.Stderr.Fd())) opts := lxc.DefaultAttachOptions opts.ClearEnv = true opts.StdinFd = 200 opts.StdoutFd = 201 opts.StderrFd = 202 logPath := shared.LogPath(name, "forkexec.log") if shared.PathExists(logPath) { os.Remove(logPath) } logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) if err == nil { syscall.Dup3(int(logFile.Fd()), 1, 0) syscall.Dup3(int(logFile.Fd()), 2, 0) } env := []string{} cmd := []string{} section := "" for _, arg := range args[5:len(args)] { // The "cmd" section must come last as it may contain a -- if arg == "--" && section != "cmd" { section = "" continue } if section == "" { section = arg continue } if section == "env" { fields := strings.SplitN(arg, "=", 2) if len(fields) == 2 && fields[0] == "HOME" { opts.Cwd = fields[1] } env = append(env, arg) } else if section == "cmd" { cmd = append(cmd, arg) } else { return -1, fmt.Errorf("Invalid exec section: %s", section) } } opts.Env = env status, err := c.RunCommandStatus(cmd, opts) if err != nil { return -1, fmt.Errorf("Failed running command: %q", err) } return status >> 8, nil }
// linux_arm64 doesn't have syscall.Dup2 which ginkgo uses, so // use the nearly identical syscall.Dup3 instead func syscallDup(oldfd int, newfd int) (err error) { return syscall.Dup3(oldfd, newfd, 0) }