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
7package os
8
9import (
10	"errors"
11	"runtime"
12	"syscall"
13	"time"
14)
15
16const (
17	// Special values for Process.Pid.
18	pidUnset    = 0
19	pidReleased = -1
20)
21
22func (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
36func (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
86func (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
105func (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
126func convertESRCH(err error) error {
127	if err == syscall.ESRCH {
128		return ErrProcessDone
129	}
130	return err
131}
132
133func (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
156func 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
172func (p *ProcessState) userTime() time.Duration {
173	return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
174}
175
176func (p *ProcessState) systemTime() time.Duration {
177	return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
178}
179