1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build unix || (js && wasm) || wasip1 || windows 6 7package os 8 9import ( 10 "internal/itoa" 11 "internal/syscall/execenv" 12 "runtime" 13 "syscall" 14) 15 16// The only signal values guaranteed to be present in the os package on all 17// systems are os.Interrupt (send the process an interrupt) and os.Kill (force 18// the process to exit). On Windows, sending os.Interrupt to a process with 19// os.Process.Signal is not implemented; it will return an error instead of 20// sending a signal. 21var ( 22 Interrupt Signal = syscall.SIGINT 23 Kill Signal = syscall.SIGKILL 24) 25 26func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) { 27 // If there is no SysProcAttr (ie. no Chroot or changed 28 // UID/GID), double-check existence of the directory we want 29 // to chdir into. We can make the error clearer this way. 30 if attr != nil && attr.Sys == nil && attr.Dir != "" { 31 if _, err := Stat(attr.Dir); err != nil { 32 pe := err.(*PathError) 33 pe.Op = "chdir" 34 return nil, pe 35 } 36 } 37 38 attrSys, shouldDupPidfd := ensurePidfd(attr.Sys) 39 sysattr := &syscall.ProcAttr{ 40 Dir: attr.Dir, 41 Env: attr.Env, 42 Sys: attrSys, 43 } 44 if sysattr.Env == nil { 45 sysattr.Env, err = execenv.Default(sysattr.Sys) 46 if err != nil { 47 return nil, err 48 } 49 } 50 sysattr.Files = make([]uintptr, 0, len(attr.Files)) 51 for _, f := range attr.Files { 52 sysattr.Files = append(sysattr.Files, f.Fd()) 53 } 54 55 pid, h, e := syscall.StartProcess(name, argv, sysattr) 56 57 // Make sure we don't run the finalizers of attr.Files. 58 runtime.KeepAlive(attr) 59 60 if e != nil { 61 return nil, &PathError{Op: "fork/exec", Path: name, Err: e} 62 } 63 64 // For Windows, syscall.StartProcess above already returned a process handle. 65 if runtime.GOOS != "windows" { 66 var ok bool 67 h, ok = getPidfd(sysattr.Sys, shouldDupPidfd) 68 if !ok { 69 return newPIDProcess(pid), nil 70 } 71 } 72 73 return newHandleProcess(pid, h), nil 74} 75 76func (p *Process) kill() error { 77 return p.Signal(Kill) 78} 79 80// ProcessState stores information about a process, as reported by Wait. 81type ProcessState struct { 82 pid int // The process's id. 83 status syscall.WaitStatus // System-dependent status info. 84 rusage *syscall.Rusage 85} 86 87// Pid returns the process id of the exited process. 88func (p *ProcessState) Pid() int { 89 return p.pid 90} 91 92func (p *ProcessState) exited() bool { 93 return p.status.Exited() 94} 95 96func (p *ProcessState) success() bool { 97 return p.status.ExitStatus() == 0 98} 99 100func (p *ProcessState) sys() any { 101 return p.status 102} 103 104func (p *ProcessState) sysUsage() any { 105 return p.rusage 106} 107 108func (p *ProcessState) String() string { 109 if p == nil { 110 return "<nil>" 111 } 112 status := p.Sys().(syscall.WaitStatus) 113 res := "" 114 switch { 115 case status.Exited(): 116 code := status.ExitStatus() 117 if runtime.GOOS == "windows" && uint(code) >= 1<<16 { // windows uses large hex numbers 118 res = "exit status " + itoa.Uitox(uint(code)) 119 } else { // unix systems use small decimal integers 120 res = "exit status " + itoa.Itoa(code) // unix 121 } 122 case status.Signaled(): 123 res = "signal: " + status.Signal().String() 124 case status.Stopped(): 125 res = "stop signal: " + status.StopSignal().String() 126 if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 { 127 res += " (trap " + itoa.Itoa(status.TrapCause()) + ")" 128 } 129 case status.Continued(): 130 res = "continued" 131 } 132 if status.CoreDump() { 133 res += " (core dumped)" 134 } 135 return res 136} 137 138// ExitCode returns the exit code of the exited process, or -1 139// if the process hasn't exited or was terminated by a signal. 140func (p *ProcessState) ExitCode() int { 141 // return -1 if the process hasn't started. 142 if p == nil { 143 return -1 144 } 145 return p.status.ExitStatus() 146} 147