Source file src/os/exec_unix.go

     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
     6  
     7  package os
     8  
     9  import (
    10  	"errors"
    11  	"runtime"
    12  	"syscall"
    13  	"time"
    14  )
    15  
    16  const (
    17  	// Special values for Process.Pid.
    18  	pidUnset    = 0
    19  	pidReleased = -1
    20  )
    21  
    22  func (p *Process) wait() (ps *ProcessState, err error) {
    23  	// Which type of Process do we have?
    24  	switch p.mode {
    25  	case modeHandle:
    26  		// pidfd
    27  		return p.pidfdWait()
    28  	case modePID:
    29  		// Regular PID
    30  		return p.pidWait()
    31  	default:
    32  		panic("unreachable")
    33  	}
    34  }
    35  
    36  func (p *Process) pidWait() (*ProcessState, error) {
    37  	// TODO(go.dev/issue/67642): When there are concurrent Wait calls, one
    38  	// may wait on the wrong process if the PID is reused after the
    39  	// completes its wait.
    40  	//
    41  	// Checking for statusDone here would not be a complete fix, as the PID
    42  	// could still be waited on and reused prior to blockUntilWaitable.
    43  	switch p.pidStatus() {
    44  	case statusReleased:
    45  		return nil, syscall.EINVAL
    46  	}
    47  
    48  	// If we can block until Wait4 will succeed immediately, do so.
    49  	ready, err := p.blockUntilWaitable()
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	if ready {
    54  		// Mark the process done now, before the call to Wait4,
    55  		// so that Process.pidSignal will not send a signal.
    56  		p.pidDeactivate(statusDone)
    57  		// Acquire a write lock on sigMu to wait for any
    58  		// active call to the signal method to complete.
    59  		p.sigMu.Lock()
    60  		p.sigMu.Unlock()
    61  	}
    62  
    63  	var (
    64  		status syscall.WaitStatus
    65  		rusage syscall.Rusage
    66  		pid1   int
    67  		e      error
    68  	)
    69  	for {
    70  		pid1, e = syscall.Wait4(p.Pid, &status, 0, &rusage)
    71  		if e != syscall.EINTR {
    72  			break
    73  		}
    74  	}
    75  	if e != nil {
    76  		return nil, NewSyscallError("wait", e)
    77  	}
    78  	p.pidDeactivate(statusDone)
    79  	return &ProcessState{
    80  		pid:    pid1,
    81  		status: status,
    82  		rusage: &rusage,
    83  	}, nil
    84  }
    85  
    86  func (p *Process) signal(sig Signal) error {
    87  	s, ok := sig.(syscall.Signal)
    88  	if !ok {
    89  		return errors.New("os: unsupported signal type")
    90  	}
    91  
    92  	// Which type of Process do we have?
    93  	switch p.mode {
    94  	case modeHandle:
    95  		// pidfd
    96  		return p.pidfdSendSignal(s)
    97  	case modePID:
    98  		// Regular PID
    99  		return p.pidSignal(s)
   100  	default:
   101  		panic("unreachable")
   102  	}
   103  }
   104  
   105  func (p *Process) pidSignal(s syscall.Signal) error {
   106  	if p.Pid == pidReleased {
   107  		return errors.New("os: process already released")
   108  	}
   109  	if p.Pid == pidUnset {
   110  		return errors.New("os: process not initialized")
   111  	}
   112  
   113  	p.sigMu.RLock()
   114  	defer p.sigMu.RUnlock()
   115  
   116  	switch p.pidStatus() {
   117  	case statusDone:
   118  		return ErrProcessDone
   119  	case statusReleased:
   120  		return errors.New("os: process already released")
   121  	}
   122  
   123  	return convertESRCH(syscall.Kill(p.Pid, s))
   124  }
   125  
   126  func convertESRCH(err error) error {
   127  	if err == syscall.ESRCH {
   128  		return ErrProcessDone
   129  	}
   130  	return err
   131  }
   132  
   133  func (p *Process) release() error {
   134  	// We clear the Pid field only for API compatibility. On Unix, Release
   135  	// has always set Pid to -1. Internally, the implementation relies
   136  	// solely on statusReleased to determine that the Process is released.
   137  	p.Pid = pidReleased
   138  
   139  	switch p.mode {
   140  	case modeHandle:
   141  		// Drop the Process' reference and mark handle unusable for
   142  		// future calls.
   143  		//
   144  		// Ignore the return value: we don't care if this was a no-op
   145  		// racing with Wait, or a double Release.
   146  		p.handlePersistentRelease(statusReleased)
   147  	case modePID:
   148  		// Just mark the PID unusable.
   149  		p.pidDeactivate(statusReleased)
   150  	}
   151  	// no need for a finalizer anymore
   152  	runtime.SetFinalizer(p, nil)
   153  	return nil
   154  }
   155  
   156  func findProcess(pid int) (p *Process, err error) {
   157  	h, err := pidfdFind(pid)
   158  	if err == ErrProcessDone {
   159  		// We can't return an error here since users are not expecting
   160  		// it. Instead, return a process with a "done" state already
   161  		// and let a subsequent Signal or Wait call catch that.
   162  		return newDoneProcess(pid), nil
   163  	} else if err != nil {
   164  		// Ignore other errors from pidfdFind, as the callers
   165  		// do not expect them. Fall back to using the PID.
   166  		return newPIDProcess(pid), nil
   167  	}
   168  	// Use the handle.
   169  	return newHandleProcess(pid, h), nil
   170  }
   171  
   172  func (p *ProcessState) userTime() time.Duration {
   173  	return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
   174  }
   175  
   176  func (p *ProcessState) systemTime() time.Duration {
   177  	return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
   178  }
   179  

View as plain text