| // 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" | |
| ) | |
| var ( | |
| // ErrProcessDone indicates a [Process] has finished. | |
| ErrProcessDone = errors.New("os: process already finished") | |
| // errProcessReleased indicates a [Process] has been released. | |
| errProcessReleased = errors.New("os: process already released") | |
| // ErrNoHandle indicates a [Process] does not have a handle. | |
| ErrNoHandle = errors.New("os: process handle unavailable") | |
| ) | |
| // processStatus describes the status of a [Process]. | |
| type processStatus uint32 | |
| const ( | |
| // statusOK means that the Process is ready to use. | |
| statusOK processStatus = iota | |
| // statusDone indicates that the PID/handle should not be used because | |
| // the process is done (has been successfully Wait'd on). | |
| statusDone | |
| // statusReleased indicates that the PID/handle should not be used | |
| // because the process is released. | |
| statusReleased | |
| ) | |
| // Process stores the information about a process created by [StartProcess]. | |
| type Process struct { | |
| Pid int | |
| // state contains the atomic process state. | |
| // | |
| // This consists of the processStatus fields, | |
| // which indicate if the process is done/released. | |
| state atomic.Uint32 | |
| // Used only when handle is nil | |
| sigMu sync.RWMutex // avoid race between wait and signal | |
| // handle, if not nil, is a pointer to a struct | |
| // that holds the OS-specific process handle. | |
| // This pointer is set when Process is created, | |
| // and never changed afterward. | |
| // This is a pointer to a separate memory allocation | |
| // so that we can use runtime.AddCleanup. | |
| handle *processHandle | |
| // cleanup is used to clean up the process handle. | |
| cleanup runtime.Cleanup | |
| } | |
| // processHandle holds an operating system handle to a process. | |
| // This is only used on systems that support that concept, | |
| // currently Linux and Windows. | |
| // This maintains a reference count to the handle, | |
| // and closes the handle when the reference drops to zero. | |
| type processHandle struct { | |
| // The actual handle. This field should not be used directly. | |
| // Instead, use the acquire and release methods. | |
| // | |
| // On Windows this is a handle returned by OpenProcess. | |
| // On Linux this is a pidfd. | |
| handle uintptr | |
| // Number of active references. When this drops to zero | |
| // the handle is closed. | |
| refs atomic.Int32 | |
| } | |
| // acquire adds a reference and returns the handle. | |
| // The bool result reports whether acquire succeeded; | |
| // it fails if the handle is already closed. | |
| // Every successful call to acquire should be paired with a call to release. | |
| func (ph *processHandle) acquire() (uintptr, bool) { | |
| for { | |
| refs := ph.refs.Load() | |
| if refs < 0 { | |
| panic("internal error: negative process handle reference count") | |
| } | |
| if refs == 0 { | |
| return 0, false | |
| } | |
| if ph.refs.CompareAndSwap(refs, refs+1) { | |
| return ph.handle, true | |
| } | |
| } | |
| } | |
| // release releases a reference to the handle. | |
| func (ph *processHandle) release() { | |
| for { | |
| refs := ph.refs.Load() | |
| if refs <= 0 { | |
| panic("internal error: too many releases of process handle") | |
| } | |
| if ph.refs.CompareAndSwap(refs, refs-1) { | |
| if refs == 1 { | |
| ph.closeHandle() | |
| } | |
| return | |
| } | |
| } | |
| } | |
| // newPIDProcess returns a [Process] for the given PID. | |
| func newPIDProcess(pid int) *Process { | |
| p := &Process{ | |
| Pid: pid, | |
| } | |
| return p | |
| } | |
| // newHandleProcess returns a [Process] with the given PID and handle. | |
| func newHandleProcess(pid int, handle uintptr) *Process { | |
| ph := &processHandle{ | |
| handle: handle, | |
| } | |
| // Start the reference count as 1, | |
| // meaning the reference from the returned Process. | |
| ph.refs.Store(1) | |
| p := &Process{ | |
| Pid: pid, | |
| handle: ph, | |
| } | |
| p.cleanup = runtime.AddCleanup(p, (*processHandle).release, ph) | |
| return p | |
| } | |
| // newDoneProcess returns a [Process] for the given PID | |
| // that is already marked as done. This is used on Unix systems | |
| // if the process is known to not exist. | |
| func newDoneProcess(pid int) *Process { | |
| p := &Process{ | |
| Pid: pid, | |
| } | |
| p.state.Store(uint32(statusDone)) // No persistent reference, as there is no handle. | |
| return p | |
| } | |
| // handleTransientAcquire returns the process handle or, | |
| // if the process is not ready, the current status. | |
| func (p *Process) handleTransientAcquire() (uintptr, processStatus) { | |
| if p.handle == nil { | |
| panic("handleTransientAcquire called in invalid mode") | |
| } | |
| status := processStatus(p.state.Load()) | |
| if status != statusOK { | |
| return 0, status | |
| } | |
| h, ok := p.handle.acquire() | |
| if ok { | |
| return h, statusOK | |
| } | |
| // This case means that the handle has been closed. | |
| // We always set the status to non-zero before closing the handle. | |
| // If we get here the status must have been set non-zero after | |
| // we just checked it above. | |
| status = processStatus(p.state.Load()) | |
| if status == statusOK { | |
| panic("inconsistent process status") | |
| } | |
| return 0, status | |
| } | |
| // handleTransientRelease releases a handle returned by handleTransientAcquire. | |
| func (p *Process) handleTransientRelease() { | |
| if p.handle == nil { | |
| panic("handleTransientRelease called in invalid mode") | |
| } | |
| p.handle.release() | |
| } | |
| // pidStatus returns the current process status. | |
| func (p *Process) pidStatus() processStatus { | |
| if p.handle != nil { | |
| panic("pidStatus called in invalid mode") | |
| } | |
| return processStatus(p.state.Load()) | |
| } | |
| // 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 { | |
| // Unfortunately, for historical reasons, on systems other | |
| // than Windows, Release sets the Pid field to -1. | |
| // This causes the race detector to report a problem | |
| // on concurrent calls to Release, but we can't change it now. | |
| if runtime.GOOS != "windows" { | |
| p.Pid = -1 | |
| } | |
| oldStatus := p.doRelease(statusReleased) | |
| // For backward compatibility, on Windows only, | |
| // we return EINVAL on a second call to Release. | |
| if runtime.GOOS == "windows" { | |
| if oldStatus == statusReleased { | |
| return syscall.EINVAL | |
| } | |
| } | |
| return nil | |
| } | |
| // doRelease releases a [Process], setting the status to newStatus. | |
| // If the previous status is not statusOK, this does nothing. | |
| // It returns the previous status. | |
| func (p *Process) doRelease(newStatus processStatus) processStatus { | |
| for { | |
| state := p.state.Load() | |
| oldStatus := processStatus(state) | |
| if oldStatus != statusOK { | |
| return oldStatus | |
| } | |
| if !p.state.CompareAndSwap(state, uint32(newStatus)) { | |
| continue | |
| } | |
| // We have successfully released the Process. | |
| // If it has a handle, release the reference we | |
| // created in newHandleProcess. | |
| if p.handle != nil { | |
| // No need for more cleanup. | |
| // We must stop the cleanup before calling release; | |
| // otherwise the cleanup might run concurrently | |
| // with the release, which would cause the reference | |
| // counts to be invalid, causing a panic. | |
| p.cleanup.Stop() | |
| p.handle.release() | |
| } | |
| return statusOK | |
| } | |
| } | |
| // 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) | |
| } | |
| // WithHandle calls a supplied function f with a valid process handle | |
| // as an argument. The handle is guaranteed to refer to process p | |
| // until f returns, even if p terminates. This function cannot be used | |
| // after [Process.Release] or [Process.Wait]. | |
| // | |
| // If process handles are not supported or a handle is not available, | |
| // it returns [ErrNoHandle]. Currently, process handles are supported | |
| // on Linux 5.4 or later (pidfd) and Windows. | |
| func (p *Process) WithHandle(f func(handle uintptr)) error { | |
| return p.withHandle(f) | |
| } | |
| // 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() | |
| } | |