1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
module os_js
#const $child_process = require('child_process')
// ProcessState.not_started - the process has not yet started
// ProcessState.running - the process is currently running
// ProcessState.stopped - the process was running, but was stopped temporarily
// ProcessState.exited - the process has finished/exited
// ProcessState.aborted - the process was terminated by a signal
// ProcessState.closed - the process resources like opened file descriptors were freed/discarded, final state.
pub enum ProcessState {
not_started
running
stopped
exited
aborted
closed
}
// todo(playX): fix reference member access in JS backend
[heap]
pub struct Process {
pub:
filename string
pub mut:
pid voidptr
code int = -1
status ProcessState = .not_started
// the current status of the process
err string // if the process fails, contains the reason why
args []string // the arguments that the command takes
env_is_custom bool // true, when the environment was customized with .set_environment
env []string // the environment with which the process was started (list of 'var=val')
use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp()
use_pgroup bool // when true, the process will create a new process group, enabling .signal_pgkill()
stdio_fd [3]int // the stdio file descriptors for the child process, used only by the nix implementation
}
// new_process - create a new process descriptor
// NB: new does NOT start the new process.
// That is done because you may want to customize it first,
// by calling different set_ methods on it.
// In order to start it, call p.run() or p.wait()
pub fn new_process(filename string) &Process {
return &Process{
filename: filename
stdio_fd: [-1, -1, -1]!
}
}
// set_args - set the arguments for the new process
pub fn (mut p Process) set_args(pargs []string) {
if p.status != .not_started {
return
}
p.args = pargs
return
}
// set_environment - set a custom environment variable mapping for the new process
pub fn (mut p Process) set_environment(envs map[string]string) {
if p.status != .not_started {
return
}
p.env_is_custom = true
p.env = []string{}
for k, v in envs {
p.env << '$k=$v'
}
return
}
fn (mut p Process) spawn_internal() {
#p.val.pid = $child_process.spawn(
#p.val.filename+'',
#p.val.args.arr.map((x) => x.valueOf() + ''),
#{
#env: (p.val.env_is_custom ? p.val.env : $process.env),
#})
#p.val.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') })
p.status = .running
// todo(playX): stderr,stdin
if p.use_stdio_ctl {
#p.val.pid.stdout.pipe(process.stdout)
#p.val.pid.stdin.pipe(process.stdin)
#p.val.pid.stderr.pipe(process.stderr)
}
}
pub fn (mut p Process) run() {
if p.status != .not_started {
return
}
p.spawn_internal()
return
}
pub fn (mut p Process) signal_kill() {
if p.status !in [.running, .stopped] {
return
}
#p.val.pid.kill('SIGKILL');
p.status = .aborted
}
pub fn (mut p Process) signal_stop() {
if p.status !in [.running, .stopped] {
return
}
#p.val.pid.kill('SIGSTOP');
p.status = .aborted
}
pub fn (mut p Process) signal_continue() {
if p.status != .stopped {
return
}
#p.val.pid.kill('SIGCONT');
p.status = .running
return
}
pub fn (mut p Process) wait() {
if p.status == .not_started {
p.spawn_internal()
}
if p.status !in [.running, .stopped] {
return
}
p.wait_internal()
return
}
fn (mut p Process) wait_internal() {
#p.val.pid.on('exit', function (code) { console.log(code) })
}
pub fn (mut p Process) set_redirect_stdio() {
p.use_stdio_ctl = true
return
}
pub fn (mut p Process) stdin_write(s string) {
p.check_redirection_call('stdin_write')
#p.val.pid.stdin.write(s)
}
// todo(playX): probably does not work
// will read from stdout pipe, will only return when EOF (end of file) or data
// means this will block unless there is data
pub fn (mut p Process) stdout_slurp() string {
p.check_redirection_call('stdout_slurp')
mut res := ''
#p.val.pid.stdout.on('data', function (data) { res = new builtin.string(data) })
return res
}
// _check_redirection_call - should be called just by stdxxx methods
fn (mut p Process) check_redirection_call(fn_name string) {
if !p.use_stdio_ctl {
panic('Call p.set_redirect_stdio() before calling p.$fn_name')
}
if p.status == .not_started {
panic('Call p.${fn_name}() after you have called p.run()')
}
}
|