func testNewLinuxHostTaoServer(t *testing.T) (Tao, error) { lh, err := testNewRootLinuxHost() if err != nil { return nil, fmt.Errorf("Can't make root linux host: %s", err) } hostRead, childWrite, err := os.Pipe() if err != nil { return nil, fmt.Errorf("Can't make pipe: %s", err) } childRead, hostWrite, err := os.Pipe() if err != nil { childWrite.Close() hostRead.Close() return nil, fmt.Errorf("Can't make pipe: %s", err) } hostChannel := util.NewPairReadWriteCloser(hostRead, hostWrite) childChannel := util.NewPairReadWriteCloser(childRead, childWrite) child := &LinuxHostChild{ channel: hostChannel, ChildSubprin: []auth.PrinExt{auth.PrinExt{Name: "TestChild"}}, Cmd: nil, // The Cmd field is not used in this test. } go NewLinuxHostTaoServer(lh, child).Serve(hostChannel) return &RPC{protorpc.NewClient(childChannel), "Tao"}, nil }
// Start starts the the hosted process and returns a tao channel to it. func (p *HostedProcess) Start() (channel io.ReadWriteCloser, err error) { var extraFiles []*os.File var evar string switch p.Factory.channelType { case "pipe": // Get a pipe pair for communication with the child. var serverRead, clientRead, serverWrite, clientWrite *os.File serverRead, clientWrite, err = os.Pipe() if err != nil { return } defer clientWrite.Close() clientRead, serverWrite, err = os.Pipe() if err != nil { serverRead.Close() return } defer clientRead.Close() channel = util.NewPairReadWriteCloser(serverRead, serverWrite) extraFiles = []*os.File{clientRead, clientWrite} // fd 3, fd 4 // Note: ExtraFiles below ensures readfd=3, writefd=4 in child evar = HostSpecEnvVar + "=tao::RPC+tao::FDMessageChannel(3, 4)" case "unix": // Get a random name for the socket. nameBytes := make([]byte, sockNameLen) if _, err = rand.Read(nameBytes); err != nil { return } sockName := base64.URLEncoding.EncodeToString(nameBytes) sockPath := path.Join(p.Factory.socketPath, sockName) channel = util.NewUnixSingleReadWriteCloser(sockPath) if channel == nil { err = fmt.Errorf("Couldn't create a new Unix channel\n") return } evar = HostSpecEnvVar + "=" + sockPath default: err = fmt.Errorf("invalid channel type '%s'\n", p.Factory.channelType) return } defer func() { if err != nil { channel.Close() channel = nil } }() env := p.spec.Env if env == nil { env = os.Environ() } // Make sure that the child knows to use the right kind of channel. etvar := HostChannelTypeEnvVar + "=" + p.Factory.channelType replaced := false replacedType := false for i, pair := range env { if strings.HasPrefix(pair, HostSpecEnvVar+"=") { env[i] = evar replaced = true } if strings.HasPrefix(pair, HostChannelTypeEnvVar+"=") { env[i] = etvar replacedType = true } } if !replaced { env = append(env, evar) } if !replacedType { env = append(env, etvar) } if (p.spec.Uid == 0 || p.spec.Gid == 0) && !p.spec.Superuser { err = fmt.Errorf("Uid and Gid must be nonzero unless Superuser is set\n") return } wd := p.spec.Dir if wd == "" { wd = p.Tempdir } // Every hosted process is given its own process group (Setpgid=true). This // ensures that hosted processes will not be in orphaned process groups, // allowing them to receive job control signals (SIGTTIN, SIGTTOU, and // SIGTSTP). // // If this host is running in "daemon" mode, i.e. without a controlling tty // and in our own session and process group, then this host will be (a) the // parent of a process in the child's group, (b) in the same session, and // (c) not in the same group as the child, so it will serve as the anchor // that keeps the child process groups from being considered orphaned. // // If this host is running in "foreground" mode, i.e. with a controlling tty // and as part of our parent process's session but in our own process group, // then the same three conditions are satisified, so this host can still // serve as the anchor that keeps the child process groups from being // considered orphaned. (Note: We could also use Setpid=false in this case, // since the host would be part of the child process group and our parent // would then meet the requirements.) spa := &syscall.SysProcAttr{ Credential: &syscall.Credential{ Uid: uint32(p.spec.Uid), Gid: uint32(p.spec.Uid), }, // Setsid: true, // Create session. Setpgid: true, // Set process group ID to new pid (SYSV setpgrp) // Setctty: true, // Set controlling terminal to fd Ctty (only meaningful if Setsid is set) // Noctty: true, // Detach fd 0 from controlling terminal // Ctty: 0, // Controlling TTY fd (Linux only) } argv := []string{p.Argv0} argv = append(argv, p.spec.Args...) p.Cmd = exec.Cmd{ Path: p.Temppath, Dir: wd, Args: argv, Stdin: p.spec.Stdin, Stdout: p.spec.Stdout, Stderr: p.spec.Stderr, Env: env, ExtraFiles: extraFiles, SysProcAttr: spa, } if err = p.Cmd.Start(); err != nil { return } // Reap the child when the process dies. sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGCHLD) go func() { <-sc p.Cmd.Wait() signal.Stop(sc) os.RemoveAll(p.Tempdir) p.Done <- true close(p.Done) // prevent any more blocking }() // TODO(kwalsh) put channel into p, remove the struct in linux_host.go return }