|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows | 
|  |  | 
|  | package os | 
|  |  | 
|  | import ( | 
|  | "syscall" | 
|  | ) | 
|  |  | 
|  | // The only signal values guaranteed to be present on all systems | 
|  | // are Interrupt (send the process an interrupt) and Kill (force | 
|  | // the process to exit). | 
|  | var ( | 
|  | Interrupt Signal = syscall.SIGINT | 
|  | Kill      Signal = syscall.SIGKILL | 
|  | ) | 
|  |  | 
|  | func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) { | 
|  | // If there is no SysProcAttr (ie. no Chroot or changed | 
|  | // UID/GID), double-check existence of the directory we want | 
|  | // to chdir into.  We can make the error clearer this way. | 
|  | if attr != nil && attr.Sys == nil && attr.Dir != "" { | 
|  | if _, err := Stat(attr.Dir); err != nil { | 
|  | pe := err.(*PathError) | 
|  | pe.Op = "chdir" | 
|  | return nil, pe | 
|  | } | 
|  | } | 
|  |  | 
|  | sysattr := &syscall.ProcAttr{ | 
|  | Dir: attr.Dir, | 
|  | Env: attr.Env, | 
|  | Sys: attr.Sys, | 
|  | } | 
|  | if sysattr.Env == nil { | 
|  | sysattr.Env = Environ() | 
|  | } | 
|  | for _, f := range attr.Files { | 
|  | sysattr.Files = append(sysattr.Files, f.Fd()) | 
|  | } | 
|  |  | 
|  | pid, h, e := syscall.StartProcess(name, argv, sysattr) | 
|  | if e != nil { | 
|  | return nil, &PathError{"fork/exec", name, e} | 
|  | } | 
|  | return newProcess(pid, h), nil | 
|  | } | 
|  |  | 
|  | func (p *Process) kill() error { | 
|  | return p.Signal(Kill) | 
|  | } | 
|  |  | 
|  | // ProcessState stores information about a process, as reported by Wait. | 
|  | type ProcessState struct { | 
|  | pid    int                // The process's id. | 
|  | status syscall.WaitStatus // System-dependent status info. | 
|  | rusage *syscall.Rusage | 
|  | } | 
|  |  | 
|  | // Pid returns the process id of the exited process. | 
|  | func (p *ProcessState) Pid() int { | 
|  | return p.pid | 
|  | } | 
|  |  | 
|  | func (p *ProcessState) exited() bool { | 
|  | return p.status.Exited() | 
|  | } | 
|  |  | 
|  | func (p *ProcessState) success() bool { | 
|  | return p.status.ExitStatus() == 0 | 
|  | } | 
|  |  | 
|  | func (p *ProcessState) sys() interface{} { | 
|  | return p.status | 
|  | } | 
|  |  | 
|  | func (p *ProcessState) sysUsage() interface{} { | 
|  | return p.rusage | 
|  | } | 
|  |  | 
|  | // Convert i to decimal string. | 
|  | func itod(i int) string { | 
|  | if i == 0 { | 
|  | return "0" | 
|  | } | 
|  |  | 
|  | u := uint64(i) | 
|  | if i < 0 { | 
|  | u = -u | 
|  | } | 
|  |  | 
|  | // Assemble decimal in reverse order. | 
|  | var b [32]byte | 
|  | bp := len(b) | 
|  | for ; u > 0; u /= 10 { | 
|  | bp-- | 
|  | b[bp] = byte(u%10) + '0' | 
|  | } | 
|  |  | 
|  | if i < 0 { | 
|  | bp-- | 
|  | b[bp] = '-' | 
|  | } | 
|  |  | 
|  | return string(b[bp:]) | 
|  | } | 
|  |  | 
|  | func (p *ProcessState) String() string { | 
|  | if p == nil { | 
|  | return "<nil>" | 
|  | } | 
|  | status := p.Sys().(syscall.WaitStatus) | 
|  | res := "" | 
|  | switch { | 
|  | case status.Exited(): | 
|  | res = "exit status " + itod(status.ExitStatus()) | 
|  | case status.Signaled(): | 
|  | res = "signal: " + status.Signal().String() | 
|  | case status.Stopped(): | 
|  | res = "stop signal: " + status.StopSignal().String() | 
|  | if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 { | 
|  | res += " (trap " + itod(status.TrapCause()) + ")" | 
|  | } | 
|  | case status.Continued(): | 
|  | res = "continued" | 
|  | } | 
|  | if status.CoreDump() { | 
|  | res += " (core dumped)" | 
|  | } | 
|  | return res | 
|  | } |