// 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. package os import ( "errors" "internal/testlog" "runtime" "sync" "sync/atomic" "syscall" "time" ) // ErrProcessDone indicates a [Process] has finished. var ErrProcessDone = errors.New("os: process already finished") type processMode uint8 const ( // modePID means that Process operations such use the raw PID from the // Pid field. handle is not used. // // This may be due to the host not supporting handles, or because // Process was created as a literal, leaving handle unset. // // This must be the zero value so Process literals get modePID. modePID processMode = iota // modeHandle means that Process operations use handle, which is // initialized with an OS process handle. // // Note that Release and Wait will deactivate and eventually close the // handle, so acquire may fail, indicating the reason. modeHandle ) type processStatus uint64 const ( // PID/handle OK to use. statusOK processStatus = 0 // statusDone indicates that the PID/handle should not be used because // the process is done (has been successfully Wait'd on). statusDone processStatus = 1 << 62 // statusReleased indicates that the PID/handle should not be used // because the process is released. statusReleased processStatus = 1 << 63 processStatusMask = 0x3 << 62 ) // Process stores the information about a process created by [StartProcess]. type Process struct { Pid int mode processMode // State contains the atomic process state. // // In modePID, this consists only of the processStatus fields, which // indicate if the process is done/released. // // In modeHandle, the lower bits also contain a reference count for the // handle field. // // The Process itself initially holds 1 persistent reference. Any // operation that uses the handle with a system call temporarily holds // an additional transient reference. This prevents the handle from // being closed prematurely, which could result in the OS allocating a // different handle with the same value, leading to Process' methods // operating on the wrong process. // // Release and Wait both drop the Process' persistent reference, but // other concurrent references may delay actually closing the handle // because they hold a transient reference. // // Regardless, we want new method calls to immediately treat the handle // as unavailable after Release or Wait to avoid extending this delay. // This is achieved by setting either processStatus flag when the // Process' persistent reference is dropped. The only difference in the // flags is the reason the handle is unavailable, which affects the // errors returned by concurrent calls. state atomic.Uint64 // Used only in modePID. sigMu sync.RWMutex // avoid race between wait and signal // handle is the OS handle for process actions, used only in // modeHandle. // // handle must be accessed only via the handleTransientAcquire method // (or during closeHandle), not directly! handle is immutable. // // On Windows, it is a handle from OpenProcess. // On Linux, it is a pidfd. // It is unused on other GOOSes. handle uintptr } func newPIDProcess(pid int) *Process { p := &Process{ Pid: pid, mode: modePID, } runtime.SetFinalizer(p, (*Process).Release) return p } func newHandleProcess(pid int, handle uintptr) *Process { p := &Process{ Pid: pid, mode: modeHandle, handle: handle, } p.state.Store(1) // 1 persistent reference runtime.SetFinalizer(p, (*Process).Release) return p } func newDoneProcess(pid int) *Process { p := &Process{ Pid: pid, mode: modeHandle, // N.B Since we set statusDone, handle will never actually be // used, so its value doesn't matter. } p.state.Store(uint64(statusDone)) // No persistent reference, as there is no handle. runtime.SetFinalizer(p, (*Process).Release) return p } func (p *Process) handleTransientAcquire() (uintptr, processStatus) { if p.mode != modeHandle { panic("handleTransientAcquire called in invalid mode") } for { refs := p.state.Load() if refs&processStatusMask != 0 { return 0, processStatus(refs & processStatusMask) } new := refs + 1 if !p.state.CompareAndSwap(refs, new) { continue } return p.handle, statusOK } } func (p *Process) handleTransientRelease() { if p.mode != modeHandle { panic("handleTransientRelease called in invalid mode") } for { state := p.state.Load() refs := state &^ processStatusMask status := processStatus(state & processStatusMask) if refs == 0 { // This should never happen because // handleTransientRelease is always paired with // handleTransientAcquire. panic("release of handle with refcount 0") } if refs == 1 && status == statusOK { // Process holds a persistent reference and always sets // a status when releasing that reference // (handlePersistentRelease). Thus something has gone // wrong if this is the last release but a status has // not always been set. panic("final release of handle without processStatus") } new := state - 1 if !p.state.CompareAndSwap(state, new) { continue } if new&^processStatusMask == 0 { p.closeHandle() } return } } // Drop the Process' persistent reference on the handle, deactivating future // Wait/Signal calls with the passed reason. // // Returns the status prior to this call. If this is not statusOK, then the // reference was not dropped or status changed. func (p *Process) handlePersistentRelease(reason processStatus) processStatus { if p.mode != modeHandle { panic("handlePersistentRelease called in invalid mode") } for { refs := p.state.Load() status := processStatus(refs & processStatusMask) if status != statusOK { // Both Release and successful Wait will drop the // Process' persistent reference on the handle. We // can't allow concurrent calls to drop the reference // twice, so we use the status as a guard to ensure the // reference is dropped exactly once. return status } if refs == 0 { // This should never happen because dropping the // persistent reference always sets a status. panic("release of handle with refcount 0") } new := (refs - 1) | uint64(reason) if !p.state.CompareAndSwap(refs, new) { continue } if new&^processStatusMask == 0 { p.closeHandle() } return status } } func (p *Process) pidStatus() processStatus { if p.mode != modePID { panic("pidStatus called in invalid mode") } return processStatus(p.state.Load()) } func (p *Process) pidDeactivate(reason processStatus) { if p.mode != modePID { panic("pidDeactivate called in invalid mode") } // Both Release and successful Wait will deactivate the PID. Only one // of those should win, so nothing left to do here if the compare // fails. // // N.B. This means that results can be inconsistent. e.g., with a // racing Release and Wait, Wait may successfully wait on the process, // returning the wait status, while future calls error with "process // released" rather than "process done". p.state.CompareAndSwap(0, uint64(reason)) } // ProcAttr holds the attributes that will be applied to a new process // started by StartProcess. type ProcAttr struct { // If Dir is non-empty, the child changes into the directory before // creating the process. Dir string // If Env is non-nil, it gives the environment variables for the // new process in the form returned by Environ. // If it is nil, the result of Environ will be used. Env []string // Files specifies the open files inherited by the new process. The // first three entries correspond to standard input, standard output, and // standard error. An implementation may support additional entries, // depending on the underlying operating system. A nil entry corresponds // to that file being closed when the process starts. // On Unix systems, StartProcess will change these File values // to blocking mode, which means that SetDeadline will stop working // and calling Close will not interrupt a Read or Write. Files []*File // Operating system-specific process creation attributes. // Note that setting this field means that your program // may not execute properly or even compile on some // operating systems. Sys *syscall.SysProcAttr } // A Signal represents an operating system signal. // The usual underlying implementation is operating system-dependent: // on Unix it is syscall.Signal. type Signal interface { String() string Signal() // to distinguish from other Stringers } // Getpid returns the process id of the caller. func Getpid() int { return syscall.Getpid() } // Getppid returns the process id of the caller's parent. func Getppid() int { return syscall.Getppid() } // FindProcess looks for a running process by its pid. // // The [Process] it returns can be used to obtain information // about the underlying operating system process. // // On Unix systems, FindProcess always succeeds and returns a Process // for the given pid, regardless of whether the process exists. To test whether // the process actually exists, see whether p.Signal(syscall.Signal(0)) reports // an error. func FindProcess(pid int) (*Process, error) { return findProcess(pid) } // StartProcess starts a new process with the program, arguments and attributes // specified by name, argv and attr. The argv slice will become [os.Args] in the // new process, so it normally starts with the program name. // // If the calling goroutine has locked the operating system thread // with [runtime.LockOSThread] and modified any inheritable OS-level // thread state (for example, Linux or Plan 9 name spaces), the new // process will inherit the caller's thread state. // // StartProcess is a low-level interface. The [os/exec] package provides // higher-level interfaces. // // If there is an error, it will be of type [*PathError]. func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) { testlog.Open(name) return startProcess(name, argv, attr) } // Release releases any resources associated with the [Process] p, // rendering it unusable in the future. // Release only needs to be called if [Process.Wait] is not. func (p *Process) Release() error { // Note to future authors: the Release API is cursed. // // On Unix and Plan 9, Release sets p.Pid = -1. This is the only part of the // Process API that is not thread-safe, but it can't be changed now. // // On Windows, Release does _not_ modify p.Pid. // // On Windows, Wait calls Release after successfully waiting to // proactively clean up resources. // // On Unix and Plan 9, Wait also proactively cleans up resources, but // can not call Release, as Wait does not set p.Pid = -1. // // On Unix and Plan 9, calling Release a second time has no effect. // // On Windows, calling Release a second time returns EINVAL. return p.release() } // Kill causes the [Process] to exit immediately. Kill does not wait until // the Process has actually exited. This only kills the Process itself, // not any other processes it may have started. func (p *Process) Kill() error { return p.kill() } // Wait waits for the [Process] to exit, and then returns a // ProcessState describing its status and an error, if any. // Wait releases any resources associated with the Process. // On most operating systems, the Process must be a child // of the current process or an error will be returned. func (p *Process) Wait() (*ProcessState, error) { return p.wait() } // Signal sends a signal to the [Process]. // Sending [Interrupt] on Windows is not implemented. func (p *Process) Signal(sig Signal) error { return p.signal(sig) } // UserTime returns the user CPU time of the exited process and its children. func (p *ProcessState) UserTime() time.Duration { return p.userTime() } // SystemTime returns the system CPU time of the exited process and its children. func (p *ProcessState) SystemTime() time.Duration { return p.systemTime() } // Exited reports whether the program has exited. // On Unix systems this reports true if the program exited due to calling exit, // but false if the program terminated due to a signal. func (p *ProcessState) Exited() bool { return p.exited() } // Success reports whether the program exited successfully, // such as with exit status 0 on Unix. func (p *ProcessState) Success() bool { return p.success() } // Sys returns system-dependent exit information about // the process. Convert it to the appropriate underlying // type, such as [syscall.WaitStatus] on Unix, to access its contents. func (p *ProcessState) Sys() any { return p.sys() } // SysUsage returns system-dependent resource usage information about // the exited process. Convert it to the appropriate underlying // type, such as [*syscall.Rusage] on Unix, to access its contents. // (On Unix, *syscall.Rusage matches struct rusage as defined in the // getrusage(2) manual page.) func (p *ProcessState) SysUsage() any { return p.sysUsage() }