func grantpt(fd uintptr) error { rc := C.grantpt(C.int(fd)) if rc == 0 { return nil } else { return syscall.Errno(rc) } }
func (server *Server) Start( command *StartCommand, result *StartResult) error { // We need at least a command. if len(command.Command) == 0 { return syscall.EINVAL } // Lookup our binary name. var binary string _, err := os.Stat(command.Command[0]) if err == nil { // Absolute path is okay. binary = command.Command[0] } else { // Check our environment. for _, keyval := range command.Environment { if strings.HasPrefix(keyval, "PATH=") && len(keyval) > 5 { dirpaths := strings.Split(keyval[5:], ":") for _, dirpath := range dirpaths { testpath := path.Join(dirpath, command.Command[0]) _, err = os.Stat(testpath) if err == nil { binary = testpath break } } } } } // Did we find a binary? if binary == "" { return syscall.ENOENT } var input *os.File var output *os.File var stdin *os.File var stdout *os.File var stderr *os.File if command.Terminal { // Open a master terminal Fd. fd, err := C.posix_openpt(syscall.O_RDWR | syscall.O_NOCTTY) if fd == C.int(-1) && err != nil { // Out of FDs? result.Pid = -1 return err } // Save our master. master := os.NewFile(uintptr(fd), "ptmx") // Try to grant and unlock the PT. r, err := C.grantpt(C.int(master.Fd())) if r != C.int(0) && err != nil { master.Close() result.Pid = -1 return err } r, err = C.unlockpt(C.int(master.Fd())) if r != C.int(0) && err != nil { master.Close() result.Pid = -1 return err } // Get the terminal name. buf := make([]byte, 1024, 1024) r, err = C.ptsname_r( C.int(master.Fd()), (*C.char)(unsafe.Pointer(&buf[0])), 1024) if r != C.int(0) && err != nil { master.Close() result.Pid = -1 return err } // Open the slave terminal. n := bytes.Index(buf, []byte{0}) slave_pts := string(buf[:n]) slave, err := os.OpenFile(slave_pts, syscall.O_RDWR|syscall.O_NOCTTY, 0) if err != nil { master.Close() result.Pid = -1 return err } defer slave.Close() // Set our inputs. input = master output = master stdin = slave stdout = slave stderr = slave } else { // Allocate pipes. r1, w1, err := os.Pipe() if err != nil { result.Pid = -1 return err } r2, w2, err := os.Pipe() if err != nil { r1.Close() w1.Close() result.Pid = -1 return err } defer r1.Close() defer w2.Close() // Set our inputs. input = w1 output = r2 stdin = r1 stdout = w2 stderr = w2 } // Start the process. proc_attr := &os.ProcAttr{ Dir: command.Cwd, Env: command.Environment, Files: []*os.File{stdin, stdout, stderr}, Sys: &syscall.SysProcAttr{ Setsid: true, Setctty: command.Terminal, Ctty: 0, }, } proc, err := os.StartProcess( binary, command.Command, proc_attr) // Unable to start? if err != nil { input.Close() if input != output { output.Close() } result.Pid = -1 return err } // Create our process. process := &Process{ input: input, output: output, starttime: time.Now(), cond: sync.NewCond(&sync.Mutex{}), } // Save the pid. result.Pid = proc.Pid server.mutex.Lock() old_process := server.active[result.Pid] server.active[result.Pid] = process server.mutex.Unlock() if old_process != nil { old_process.close() } go server.wait() return nil }