コード例 #1
0
func newExecShell(command []string, tty bool) (engines.Shell, error) {
	if len(command) == 0 {
		command = []string{defaultShell}
	}
	s := &execShell{
		cmd: exec.Command(command[0], command[1:]...),
	}

	// Start is wrapped in pty, if shell is supposed to emulate a TTY
	var err error
	if tty && pty.Supported {
		s.pty, err = pty.Start(s.cmd)
		if err != nil {
			// if there was a start error we set empty streams
			s.stdin = ioext.WriteNopCloser(ioutil.Discard)
			s.stdout = ioutil.NopCloser(bytes.NewReader(nil))
			s.stderr = ioutil.NopCloser(bytes.NewReader(nil))
		} else {
			s.stdin = s.pty
			s.stdout = s.pty
			s.stderr = ioutil.NopCloser(bytes.NewReader(nil))
		}
	} else {
		s.cmd.Stdin, s.stdin = io.Pipe()
		s.stdout, s.cmd.Stdout = io.Pipe()
		s.stderr, s.cmd.Stderr = io.Pipe()

		err = s.cmd.Start()
	}

	// if there was an error starting, then we just resolve as is... Hence, it'll
	// be empty stdio and false result.
	if err != nil {
		s.resolve.Do(func() {
			s.stdin.Close()
			s.stdout.Close()
			s.stderr.Close()

			s.result = false
			s.abortErr = engines.ErrShellTerminated
		})
	} else {
		// otherwise wait for the result, and resolve when shell terminates
		go s.waitForResult()
	}

	return s, nil
}
コード例 #2
0
// StartProcess starts a new process with given arguments, environment variables,
// and current working folder, running as given user.
//
// Returns an human readable error explaining why the sub-process couldn't start
// if not successful.
func StartProcess(options ProcessOptions) (*Process, error) {
	// Default arguments to system shell
	if len(options.Arguments) == 0 {
		options.Arguments = []string{defaultShell}
	}

	// If WorkingFolder isn't set find home folder of options.Owner (if set)
	// or current user
	if options.WorkingFolder == "" {
		if options.Owner != nil {
			options.WorkingFolder = options.Owner.homeFolder
		} else {
			u, err := user.Current()
			if err != nil {
				panic(fmt.Sprintf("Failed to lookup current user, error: %s", err))
			}
			options.WorkingFolder = u.HomeDir
		}
	}

	// Default stdout to os.DevNul
	if options.Stdout == nil {
		options.Stdout = ioext.WriteNopCloser(ioutil.Discard)
	}

	// Default stderr to stdout
	if options.Stderr == nil {
		options.Stderr = options.Stdout
	}

	// Create process and command
	p := &Process{}
	p.cmd = exec.Command(options.Arguments[0], options.Arguments[1:]...)
	p.cmd.Env = formatEnv(options.Environment)
	p.cmd.Dir = options.WorkingFolder

	// Set owner for the process
	if options.Owner != nil {
		p.cmd.SysProcAttr = &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: options.Owner.uid,
				Gid: options.Owner.gid,
			},
		}
	}

	// Start the process
	var err error
	if !options.TTY {
		p.cmd.Stdin = options.Stdin
		p.cmd.Stdout = options.Stdout
		p.cmd.Stderr = options.Stderr
		p.stdin = options.Stdin
		p.stdout = options.Stdout
		p.stderr = options.Stderr
		err = p.cmd.Start()
	} else {
		p.pty, err = pty.Start(p.cmd)
		if options.Stdin != nil {
			p.sockets.Add(1)
			go func() {
				io.Copy(p.pty, options.Stdin)
				p.sockets.Done()
				// Kill process when stdin ends (if running as TTY)
				p.Kill()
			}()
		}
		p.sockets.Add(1)
		go func() {
			ioext.CopyAndClose(options.Stdout, p.pty)
			p.sockets.Done()
		}()
	}

	if err != nil {
		return nil, fmt.Errorf("Unable to execute binary, error: %s", err)
	}

	// Go wait for result
	go p.waitForResult()

	return p, nil
}