v2 / vlib / os / process.c.v
351 lines · 319 sloc · 8.86 KB · 04d978575c57ba0de82eb7866a2663f4f9cbe540
Raw
1module os
2
3// The kind of the pipe file descriptor, that is used for communicating with the child process
4pub enum ChildProcessPipeKind {
5 stdin
6 stdout
7 stderr
8}
9
10// signal_kill kills the process, after that it is no longer running.
11pub fn (mut p Process) signal_kill() {
12 if p.status !in [.running, .stopped] {
13 return
14 }
15 p._signal_kill()
16 p.status = .aborted
17}
18
19// signal_term terminates the process.
20pub fn (mut p Process) signal_term() {
21 if p.status !in [.running, .stopped] {
22 return
23 }
24 p._signal_term()
25}
26
27// signal_pgkill kills the whole process group.
28pub fn (mut p Process) signal_pgkill() {
29 if p.status !in [.running, .stopped] {
30 return
31 }
32 p._signal_pgkill()
33}
34
35// signal_stop stops the process, you can resume it with p.signal_continue().
36pub fn (mut p Process) signal_stop() {
37 if p.status != .running {
38 return
39 }
40 p._signal_stop()
41 p.status = .stopped
42}
43
44// signal_continue tell a stopped process to continue/resume its work.
45pub fn (mut p Process) signal_continue() {
46 if p.status != .stopped {
47 return
48 }
49 p._signal_continue()
50 p.status = .running
51}
52
53// wait for a process to finish.
54// Note: You have to call p.wait(), otherwise a finished process
55// would get to a zombie state, and its resources will not get
56// released fully, until its parent process exits.
57// Note: This call will block the calling process until the child
58// process is finished.
59pub fn (mut p Process) wait() {
60 if p.status == .not_started {
61 p._spawn()
62 }
63 if p.status !in [.running, .stopped] {
64 return
65 }
66 p._wait()
67}
68
69// close frees the OS resources associated with the process.
70// It can be called multiple times, but will free the resources just once.
71// If the process has already finished, this sets the process state to
72// .closed, which is final.
73pub fn (mut p Process) close() {
74 if p.status in [.not_started, .closed] {
75 return
76 }
77 $if !windows {
78 for i in 0 .. 3 {
79 if p.stdio_fd[i] != -1 {
80 fd_close(p.stdio_fd[i])
81 p.stdio_fd[i] = -1
82 }
83 }
84 }
85 if p.status !in [.running, .stopped] {
86 p.status = .closed
87 }
88}
89
90@[unsafe]
91pub fn (mut p Process) free() {
92 p.close()
93 unsafe {
94 p.filename.free()
95 p.err.free()
96 p.args.free()
97 p.env.free()
98 }
99}
100
101// _spawn should not be called directly, but only by p.run()/p.wait().
102// It encapsulates the fork/execve mechanism that allows the
103// asynchronous starting of the new child process.
104fn (mut p Process) _spawn() int {
105 if !p.env_is_custom {
106 p.env = []string{}
107 current_environment := environ()
108 for k, v in current_environment {
109 p.env << '${k}=${v}'
110 }
111 }
112 mut pid := 0
113 $if windows {
114 pid = p.win_spawn_process()
115 } $else {
116 pid = p.unix_spawn_process()
117 }
118 p.pid = pid
119 p.status = .running
120 return 0
121}
122
123// is_alive query whether the process is still alive.
124pub fn (mut p Process) is_alive() bool {
125 mut res := false
126 if p.status in [.running, .stopped] {
127 res = p._is_alive()
128 }
129 $if trace_process_is_alive ? {
130 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, res: ${res}')
131 }
132 return res
133}
134
135//
136pub fn (mut p Process) set_redirect_stdio() {
137 p.use_stdio_ctl = true
138 $if trace_process_pipes ? {
139 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}')
140 }
141}
142
143// stdin_write will write the string `s`, to the stdin pipe of the child process.
144pub fn (mut p Process) stdin_write(s string) {
145 p._check_redirection_call(@METHOD)
146 $if trace_process_pipes ? {
147 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, s.len: ${s.len}, s: `${s}`')
148 }
149 p._write_to(.stdin, s)
150}
151
152// stdout_slurp will read from the stdout pipe.
153// It will block until it either reads all the data, or until the pipe is closed (end of file).
154pub fn (mut p Process) stdout_slurp() string {
155 p._check_redirection_call(@METHOD)
156 res := p._slurp_from(.stdout)
157 $if trace_process_pipes ? {
158 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, res.len: ${res.len}, res: `${res}`')
159 }
160 return res
161}
162
163// stderr_slurp will read from the stderr pipe.
164// It will block until it either reads all the data, or until the pipe is closed (end of file).
165pub fn (mut p Process) stderr_slurp() string {
166 p._check_redirection_call(@METHOD)
167 res := p._slurp_from(.stderr)
168 $if trace_process_pipes ? {
169 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, res.len: ${res.len}, res: `${res}`')
170 }
171 return res
172}
173
174// stdout_read reads a block of data from the child process stdout pipe.
175// It returns `''` immediately when there is currently no data to be read.
176pub fn (mut p Process) stdout_read() string {
177 p._check_redirection_call(@METHOD)
178 if !p._is_pending(.stdout) {
179 return ''
180 }
181 res := p._read_from(.stdout)
182 $if trace_process_pipes ? {
183 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, res.len: ${res.len}, res: `${res}`')
184 }
185 return res
186}
187
188// stderr_read reads a block of data from the child process stderr pipe.
189// It returns `''` immediately when there is currently no data to be read.
190pub fn (mut p Process) stderr_read() string {
191 p._check_redirection_call(@METHOD)
192 if !p._is_pending(.stderr) {
193 return ''
194 }
195 res := p._read_from(.stderr)
196 $if trace_process_pipes ? {
197 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, res.len: ${res.len}, res: `${res}`')
198 }
199 return res
200}
201
202// pipe_read reads a block of data, from the given pipe of the child process.
203// It will return `none`, if there is no data to be read, *without blocking*.
204pub fn (mut p Process) pipe_read(pkind ChildProcessPipeKind) ?string {
205 p._check_redirection_call(@METHOD)
206 if !p._is_pending(pkind) {
207 $if trace_process_pipes ? {
208 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, no pending data')
209 }
210 return none
211 }
212 res := p._read_from(pkind)
213 if res.len == 0 {
214 return none
215 }
216 $if trace_process_pipes ? {
217 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, res.len: ${res.len}, res: `${res}`')
218 }
219 return res
220}
221
222// is_pending returns whether there is data to be read from child process's pipe corresponding to `pkind`.
223// For example `if p.is_pending(.stdout) { dump( p.stdout_read() ) }` will not block indefinitely.
224pub fn (mut p Process) is_pending(pkind ChildProcessPipeKind) bool {
225 p._check_redirection_call(@METHOD)
226 res := p._is_pending(pkind)
227 $if trace_process_pipes ? {
228 eprintln('${@LOCATION}, pid: ${p.pid}, status: ${p.status}, pkind: ${pkind}, res: ${res}')
229 }
230 return res
231}
232
233// _read_from should be called only from .stdout_read/0, .stderr_read/0 and .pipe_read/1.
234fn (mut p Process) _read_from(pkind ChildProcessPipeKind) string {
235 $if windows {
236 s, _ := p.win_read_string(int(pkind), 4096)
237 return s
238 } $else {
239 s, _ := fd_read(p.stdio_fd[pkind], 4096)
240 return s
241 }
242}
243
244// _slurp_from should be called only from stdout_slurp() and stderr_slurp().
245fn (mut p Process) _slurp_from(pkind ChildProcessPipeKind) string {
246 $if windows {
247 return p.win_slurp(int(pkind))
248 } $else {
249 return fd_slurp(p.stdio_fd[pkind]).join('')
250 }
251}
252
253// _write_to should be called only from stdin_write().
254fn (mut p Process) _write_to(pkind ChildProcessPipeKind, s string) {
255 $if windows {
256 p.win_write_string(int(pkind), s)
257 } $else {
258 fd_write(p.stdio_fd[pkind], s)
259 }
260}
261
262// _is_pending should be called only from is_pending().
263fn (mut p Process) _is_pending(pkind ChildProcessPipeKind) bool {
264 $if windows {
265 return p.win_is_pending(int(pkind))
266 } $else {
267 return fd_is_pending(p.stdio_fd[pkind])
268 }
269 return false
270}
271
272// _check_redirection_call should be called just by stdxxx methods.
273fn (mut p Process) _check_redirection_call(fn_name string) {
274 if !p.use_stdio_ctl {
275 panic('Call p.set_redirect_stdio() before calling p.' + fn_name)
276 }
277 if p.status == .not_started {
278 panic('Call p.' + fn_name + '() after you have called p.run()')
279 }
280}
281
282// _signal_stop should not be called directly, except by p.signal_stop.
283fn (mut p Process) _signal_stop() {
284 $if windows {
285 p.win_stop_process()
286 } $else {
287 p.unix_stop_process()
288 }
289}
290
291// _signal_continue should not be called directly, just by p.signal_continue.
292fn (mut p Process) _signal_continue() {
293 $if windows {
294 p.win_resume_process()
295 } $else {
296 p.unix_resume_process()
297 }
298}
299
300// _signal_kill should not be called directly, except by p.signal_kill.
301fn (mut p Process) _signal_kill() {
302 $if windows {
303 p.win_kill_process()
304 } $else {
305 p.unix_kill_process()
306 }
307}
308
309// _signal_term should not be called directly, except by p.signal_term.
310fn (mut p Process) _signal_term() {
311 $if windows {
312 p.win_term_process()
313 } $else {
314 p.unix_term_process()
315 }
316}
317
318// _signal_pgkill should not be called directly, except by p.signal_pgkill.
319fn (mut p Process) _signal_pgkill() {
320 $if windows {
321 p.win_kill_pgroup()
322 } $else {
323 p.unix_kill_pgroup()
324 }
325}
326
327// _wait should not be called directly, except by p.wait().
328fn (mut p Process) _wait() {
329 $if windows {
330 p.win_wait()
331 } $else {
332 p.unix_wait()
333 }
334}
335
336// _is_alive should not be called directly, except by p.is_alive().
337fn (mut p Process) _is_alive() bool {
338 $if windows {
339 return p.win_is_alive()
340 } $else {
341 return p.unix_is_alive()
342 }
343}
344
345// run starts the new process.
346pub fn (mut p Process) run() {
347 if p.status != .not_started {
348 return
349 }
350 p._spawn()
351}
352