func handleSingle(path string) error { // Open a socket. ln, err := net.Listen("unix", path) if err != nil { return err } defer ln.Close() // We only accept a single connection, since we can only really have // one reader for os.Stdin. Plus this is all a PoC. conn, err := ln.Accept() if err != nil { return err } defer conn.Close() // Close ln, to allow for other instances to take over. ln.Close() // Get the fd of the connection. unixconn, ok := conn.(*net.UnixConn) if !ok { return fmt.Errorf("failed to cast to unixconn") } socket, err := unixconn.File() if err != nil { return err } defer socket.Close() // Get the master file descriptor from runC. master, err := utils.RecvFd(socket) if err != nil { return err } // Print the file descriptor tag. ti, err := libcontainer.GetTerminalInfo(master.Name()) if err != nil { return err } fmt.Printf("[recvtty] received masterfd: container '%s'\n", ti.ContainerID) // Copy from our stdio to the master fd. quitChan := make(chan struct{}) go func() { io.Copy(os.Stdout, master) quitChan <- struct{}{} }() go func() { io.Copy(master, os.Stdin) quitChan <- struct{}{} }() // Only close the master fd once we've stopped copying. <-quitChan master.Close() return nil }
func handleNull(path string) error { // Open a socket. ln, err := net.Listen("unix", path) if err != nil { return err } defer ln.Close() // As opposed to handleSingle we accept as many connections as we get, but // we don't interact with Stdin at all (and we copy stdout to /dev/null). for { conn, err := ln.Accept() if err != nil { return err } go func(conn net.Conn) { // Don't leave references lying around. defer conn.Close() // Get the fd of the connection. unixconn, ok := conn.(*net.UnixConn) if !ok { return } socket, err := unixconn.File() if err != nil { return } defer socket.Close() // Get the master file descriptor from runC. master, err := utils.RecvFd(socket) if err != nil { return } // Print the file descriptor tag. ti, err := libcontainer.GetTerminalInfo(master.Name()) if err != nil { bail(err) } fmt.Printf("[recvtty] received masterfd: container '%s'\n", ti.ContainerID) // Just do a dumb copy to /dev/null. devnull, err := os.OpenFile("/dev/null", os.O_RDWR, 0) if err != nil { // TODO: Handle this nicely. return } io.Copy(devnull, master) devnull.Close() }(conn) } }
func (p *setnsProcess) start() (err error) { defer p.parentPipe.Close() err = p.cmd.Start() p.childPipe.Close() p.rootDir.Close() if err != nil { return newSystemErrorWithCause(err, "starting setns process") } if p.bootstrapData != nil { if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { return newSystemErrorWithCause(err, "copying bootstrap data to pipe") } } if err = p.execSetns(); err != nil { return newSystemErrorWithCause(err, "executing setns process") } if len(p.cgroupPaths) > 0 { if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil { return newSystemErrorWithCausef(err, "adding pid %d to cgroups", p.pid()) } } // set oom_score_adj if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting oom score") } // set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting rlimits for process") } if err := utils.WriteJSON(p.parentPipe, p.config); err != nil { return newSystemErrorWithCause(err, "writing config to pipe") } ierr := parseSync(p.parentPipe, func(sync *syncT) error { switch sync.Type { case procConsole: if err := writeSync(p.parentPipe, procConsoleReq); err != nil { return newSystemErrorWithCause(err, "writing syncT 'request fd'") } masterFile, err := utils.RecvFd(p.parentPipe) if err != nil { return newSystemErrorWithCause(err, "getting master pty from child pipe") } if p.process.consoleChan == nil { // TODO: Don't panic here, do something more sane. panic("consoleChan is nil") } p.process.consoleChan <- masterFile if err := writeSync(p.parentPipe, procConsoleAck); err != nil { return newSystemErrorWithCause(err, "writing syncT 'ack fd'") } case procReady: // This shouldn't happen. panic("unexpected procReady in setns") case procHooks: // This shouldn't happen. panic("unexpected procHooks in setns") default: return newSystemError(fmt.Errorf("invalid JSON payload from child")) } return nil }) if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { return newSystemErrorWithCause(err, "calling shutdown on init pipe") } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { p.wait() return ierr } return nil }
func (p *initProcess) start() error { defer p.parentPipe.Close() err := p.cmd.Start() p.process.ops = p p.childPipe.Close() p.rootDir.Close() if err != nil { p.process.ops = nil return newSystemErrorWithCause(err, "starting init process command") } if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { return err } if err := p.execSetns(); err != nil { return newSystemErrorWithCause(err, "running exec setns process for init") } // Save the standard descriptor names before the container process // can potentially move them (e.g., via dup2()). If we don't do this now, // we won't know at checkpoint time which file descriptor to look up. fds, err := getPipeFds(p.pid()) if err != nil { return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", p.pid()) } p.setExternalDescriptors(fds) // Do this before syncing with child so that no children // can escape the cgroup if err := p.manager.Apply(p.pid()); err != nil { return newSystemErrorWithCause(err, "applying cgroup configuration for process") } defer func() { if err != nil { // TODO: should not be the responsibility to call here p.manager.Destroy() } }() if err := p.createNetworkInterfaces(); err != nil { return newSystemErrorWithCause(err, "creating network interfaces") } if err := p.sendConfig(); err != nil { return newSystemErrorWithCause(err, "sending config to init process") } var ( sentRun bool sentResume bool ) ierr := parseSync(p.parentPipe, func(sync *syncT) error { switch sync.Type { case procConsole: if err := writeSync(p.parentPipe, procConsoleReq); err != nil { return newSystemErrorWithCause(err, "writing syncT 'request fd'") } masterFile, err := utils.RecvFd(p.parentPipe) if err != nil { return newSystemErrorWithCause(err, "getting master pty from child pipe") } if p.process.consoleChan == nil { // TODO: Don't panic here, do something more sane. panic("consoleChan is nil") } p.process.consoleChan <- masterFile if err := writeSync(p.parentPipe, procConsoleAck); err != nil { return newSystemErrorWithCause(err, "writing syncT 'ack fd'") } case procReady: if err := p.manager.Set(p.config.Config); err != nil { return newSystemErrorWithCause(err, "setting cgroup config for ready process") } // set oom_score_adj if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting oom score for ready process") } // set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting rlimits for ready process") } // call prestart hooks if !p.config.Config.Namespaces.Contains(configs.NEWNS) { if p.config.Config.Hooks != nil { s := configs.HookState{ Version: p.container.config.Version, ID: p.container.id, Pid: p.pid(), Root: p.config.Config.Rootfs, } for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } } // Sync with child. if err := writeSync(p.parentPipe, procRun); err != nil { return newSystemErrorWithCause(err, "writing syncT 'run'") } sentRun = true case procHooks: if p.config.Config.Hooks != nil { s := configs.HookState{ Version: p.container.config.Version, ID: p.container.id, Pid: p.pid(), Root: p.config.Config.Rootfs, BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"), } for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } // Sync with child. if err := writeSync(p.parentPipe, procResume); err != nil { return newSystemErrorWithCause(err, "writing syncT 'resume'") } sentResume = true default: return newSystemError(fmt.Errorf("invalid JSON payload from child")) } return nil }) if !sentRun { return newSystemErrorWithCause(ierr, "container init") } if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) } if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { return newSystemErrorWithCause(err, "shutting down init pipe") } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { p.wait() return ierr } return nil }