| // 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() | |
| } |