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  	)
    67  	pid1, err := ignoringEINTR2(func() (int, error) {
    68  		return syscall.Wait4(p.Pid, &status, 0, &rusage)
    69  	})
    70  	if err != nil {
    71  		return nil, NewSyscallError("wait", err)
    72  	}
    73  	p.pidDeactivate(statusDone)
    74  	return &ProcessState{
    75  		pid:    pid1,
    76  		status: status,
    77  		rusage: &rusage,
    78  	}, nil
    79  }
    80  
    81  func (p *Process) signal(sig Signal) error {
    82  	s, ok := sig.(syscall.Signal)
    83  	if !ok {
    84  		return errors.New("os: unsupported signal type")
    85  	}
    86  
    87  	// Which type of Process do we have?
    88  	switch p.mode {
    89  	case modeHandle:
    90  		// pidfd
    91  		return p.pidfdSendSignal(s)
    92  	case modePID:
    93  		// Regular PID
    94  		return p.pidSignal(s)
    95  	default:
    96  		panic("unreachable")
    97  	}
    98  }
    99  
   100  func (p *Process) pidSignal(s syscall.Signal) error {
   101  	if p.Pid == pidReleased {
   102  		return errors.New("os: process already released")
   103  	}
   104  	if p.Pid == pidUnset {
   105  		return errors.New("os: process not initialized")
   106  	}
   107  
   108  	p.sigMu.RLock()
   109  	defer p.sigMu.RUnlock()
   110  
   111  	switch p.pidStatus() {
   112  	case statusDone:
   113  		return ErrProcessDone
   114  	case statusReleased:
   115  		return errors.New("os: process already released")
   116  	}
   117  
   118  	return convertESRCH(syscall.Kill(p.Pid, s))
   119  }
   120  
   121  func convertESRCH(err error) error {
   122  	if err == syscall.ESRCH {
   123  		return ErrProcessDone
   124  	}
   125  	return err
   126  }
   127  
   128  func (p *Process) release() error {
   129  	// We clear the Pid field only for API compatibility. On Unix, Release
   130  	// has always set Pid to -1. Internally, the implementation relies
   131  	// solely on statusReleased to determine that the Process is released.
   132  	p.Pid = pidReleased
   133  
   134  	switch p.mode {
   135  	case modeHandle:
   136  		// Drop the Process' reference and mark handle unusable for
   137  		// future calls.
   138  		//
   139  		// Ignore the return value: we don't care if this was a no-op
   140  		// racing with Wait, or a double Release.
   141  		p.handlePersistentRelease(statusReleased)
   142  	case modePID:
   143  		// Just mark the PID unusable.
   144  		p.pidDeactivate(statusReleased)
   145  	}
   146  	// no need for a finalizer anymore
   147  	runtime.SetFinalizer(p, nil)
   148  	return nil
   149  }
   150  
   151  func findProcess(pid int) (p *Process, err error) {
   152  	h, err := pidfdFind(pid)
   153  	if err == ErrProcessDone {
   154  		// We can't return an error here since users are not expecting
   155  		// it. Instead, return a process with a "done" state already
   156  		// and let a subsequent Signal or Wait call catch that.
   157  		return newDoneProcess(pid), nil
   158  	} else if err != nil {
   159  		// Ignore other errors from pidfdFind, as the callers
   160  		// do not expect them. Fall back to using the PID.
   161  		return newPIDProcess(pid), nil
   162  	}
   163  	// Use the handle.
   164  	return newHandleProcess(pid, h), nil
   165  }
   166  
   167  func (p *ProcessState) userTime() time.Duration {
   168  	return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
   169  }
   170  
   171  func (p *ProcessState) systemTime() time.Duration {
   172  	return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
   173  }
   174  

View as plain text