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
5package os
6
7import (
8	"internal/itoa"
9	"runtime"
10	"syscall"
11	"time"
12)
13
14// The only signal values guaranteed to be present in the os package
15// on all systems are Interrupt (send the process an interrupt) and
16// Kill (force the process to exit). Interrupt is not implemented on
17// Windows; using it with [os.Process.Signal] will return an error.
18var (
19	Interrupt Signal = syscall.Note("interrupt")
20	Kill      Signal = syscall.Note("kill")
21)
22
23func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
24	sysattr := &syscall.ProcAttr{
25		Dir: attr.Dir,
26		Env: attr.Env,
27		Sys: attr.Sys,
28	}
29
30	sysattr.Files = make([]uintptr, 0, len(attr.Files))
31	for _, f := range attr.Files {
32		sysattr.Files = append(sysattr.Files, f.Fd())
33	}
34
35	pid, _, e := syscall.StartProcess(name, argv, sysattr)
36	if e != nil {
37		return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
38	}
39
40	return newPIDProcess(pid), nil
41}
42
43func (p *Process) writeProcFile(file string, data string) error {
44	f, e := OpenFile("/proc/"+itoa.Itoa(p.Pid)+"/"+file, O_WRONLY, 0)
45	if e != nil {
46		return e
47	}
48	defer f.Close()
49	_, e = f.Write([]byte(data))
50	return e
51}
52
53func (p *Process) signal(sig Signal) error {
54	switch p.pidStatus() {
55	case statusDone:
56		return ErrProcessDone
57	case statusReleased:
58		return syscall.ENOENT
59	}
60
61	if e := p.writeProcFile("note", sig.String()); e != nil {
62		return NewSyscallError("signal", e)
63	}
64	return nil
65}
66
67func (p *Process) kill() error {
68	return p.signal(Kill)
69}
70
71func (p *Process) wait() (ps *ProcessState, err error) {
72	var waitmsg syscall.Waitmsg
73
74	switch p.pidStatus() {
75	case statusReleased:
76		return nil, ErrInvalid
77	}
78
79	err = syscall.WaitProcess(p.Pid, &waitmsg)
80	if err != nil {
81		return nil, NewSyscallError("wait", err)
82	}
83
84	p.pidDeactivate(statusDone)
85	ps = &ProcessState{
86		pid:    waitmsg.Pid,
87		status: &waitmsg,
88	}
89	return ps, nil
90}
91
92func (p *Process) release() error {
93	p.Pid = -1
94
95	// Just mark the PID unusable.
96	p.pidDeactivate(statusReleased)
97
98	// no need for a finalizer anymore
99	runtime.SetFinalizer(p, nil)
100	return nil
101}
102
103func findProcess(pid int) (p *Process, err error) {
104	// NOOP for Plan 9.
105	return newPIDProcess(pid), nil
106}
107
108// ProcessState stores information about a process, as reported by Wait.
109type ProcessState struct {
110	pid    int              // The process's id.
111	status *syscall.Waitmsg // System-dependent status info.
112}
113
114// Pid returns the process id of the exited process.
115func (p *ProcessState) Pid() int {
116	return p.pid
117}
118
119func (p *ProcessState) exited() bool {
120	return p.status.Exited()
121}
122
123func (p *ProcessState) success() bool {
124	return p.status.ExitStatus() == 0
125}
126
127func (p *ProcessState) sys() any {
128	return p.status
129}
130
131func (p *ProcessState) sysUsage() any {
132	return p.status
133}
134
135func (p *ProcessState) userTime() time.Duration {
136	return time.Duration(p.status.Time[0]) * time.Millisecond
137}
138
139func (p *ProcessState) systemTime() time.Duration {
140	return time.Duration(p.status.Time[1]) * time.Millisecond
141}
142
143func (p *ProcessState) String() string {
144	if p == nil {
145		return "<nil>"
146	}
147	return "exit status: " + p.status.Msg
148}
149
150// ExitCode returns the exit code of the exited process, or -1
151// if the process hasn't exited or was terminated by a signal.
152func (p *ProcessState) ExitCode() int {
153	// return -1 if the process hasn't started.
154	if p == nil {
155		return -1
156	}
157	return p.status.ExitStatus()
158}
159