func run() int { flag.Parse() runtime := flag.Args()[1] // e.g. runc dir := flag.Args()[2] // bundlePath for run, processPath for exec containerId := flag.Args()[3] signals := make(chan os.Signal, 100) signal.Notify(signals, syscall.SIGCHLD) fd3 := os.NewFile(3, "/proc/self/fd/3") logFile := fmt.Sprintf("/proc/%d/fd/4", os.Getpid()) logFD := os.NewFile(4, "/proc/self/fd/4") syncPipe := os.NewFile(5, "/proc/self/fd/5") pidFilePath := filepath.Join(dir, "pidfile") stdin, stdout, stderr, winsz := openPipes(dir) syncPipe.Write([]byte{0}) var runcStartCmd *exec.Cmd if *tty { ttySlave := setupTty(stdin, stdout, pidFilePath, winsz, garden.WindowSize{Rows: *rows, Columns: *cols}) runcStartCmd = exec.Command(runtime, "-debug", "-log", logFile, "exec", "-d", "-tty", "-console", ttySlave.Name(), "-p", fmt.Sprintf("/proc/%d/fd/0", os.Getpid()), "-pid-file", pidFilePath, containerId) } else { runcStartCmd = exec.Command(runtime, "-debug", "-log", logFile, "exec", "-p", fmt.Sprintf("/proc/%d/fd/0", os.Getpid()), "-d", "-pid-file", pidFilePath, containerId) runcStartCmd.Stdin = stdin runcStartCmd.Stdout = stdout runcStartCmd.Stderr = stderr } // we need to be the subreaper so we can wait on the detached container process system.SetSubreaper(os.Getpid()) if err := runcStartCmd.Start(); err != nil { fd3.Write([]byte{2}) return 2 } var status syscall.WaitStatus var rusage syscall.Rusage _, err := syscall.Wait4(runcStartCmd.Process.Pid, &status, 0, &rusage) check(err) // Start succeeded but Wait4 failed, this can only be a programmer error logFD.Close() // No more logs from runc so close fd fd3.Write([]byte{byte(status.ExitStatus())}) if status.ExitStatus() != 0 { return 3 // nothing to wait for, container didn't launch } containerPid, err := parsePid(pidFilePath) check(err) return waitForContainerToExit(dir, containerPid, signals) }
// newSignalHandler returns a signal handler for processing SIGCHLD and SIGWINCH signals // while still forwarding all other signals to the process. func newSignalHandler(tty *tty, enableSubreaper bool) *signalHandler { if enableSubreaper { // set us as the subreaper before registering the signal handler for the container if err := system.SetSubreaper(1); err != nil { logrus.Warn(err) } } // ensure that we have a large buffer size so that we do not miss any signals // incase we are not processing them fast enough. s := make(chan os.Signal, signalBufferSize) // handle all signals for the process. signal.Notify(s) return &signalHandler{ tty: tty, signals: s, } }