2 Run a child process and collect the output
4 Copyright (C) Amitay Isaacs 2016
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "system/filesys.h"
22 #include "system/wait.h"
27 #include "lib/util/tevent_unix.h"
28 #include "lib/util/sys_rw.h"
29 #include "lib/util/blocking.h"
30 #include "lib/util/dlinklist.h"
32 #include "common/run_proc.h"
38 struct run_proc_context
;
41 struct proc_context
*prev
, *next
;
46 struct tevent_fd
*fde
;
49 struct run_proc_result result
;
51 struct tevent_req
*req
;
54 static int proc_destructor(struct proc_context
*proc
);
56 static struct proc_context
*proc_new(TALLOC_CTX
*mem_ctx
,
57 struct run_proc_context
*run_ctx
)
59 struct proc_context
*proc
;
61 proc
= talloc_zero(mem_ctx
, struct proc_context
);
69 talloc_set_destructor(proc
, proc_destructor
);
74 static void run_proc_kill(struct tevent_req
*req
);
76 static int proc_destructor(struct proc_context
*proc
)
78 if (proc
->req
!= NULL
) {
79 run_proc_kill(proc
->req
);
82 talloc_free(proc
->fde
);
83 if (proc
->pid
!= -1) {
84 kill(-proc
->pid
, SIGKILL
);
90 static void proc_read_handler(struct tevent_context
*ev
,
91 struct tevent_fd
*fde
, uint16_t flags
,
94 static int proc_start(struct proc_context
*proc
, struct tevent_context
*ev
,
95 const char *path
, const char **argv
, int stdin_fd
)
106 if (proc
->pid
== -1) {
113 if (proc
->pid
== 0) {
116 ret
= dup2(fd
[1], STDOUT_FILENO
);
120 ret
= dup2(fd
[1], STDERR_FILENO
);
127 if (stdin_fd
!= -1) {
128 ret
= dup2(stdin_fd
, STDIN_FILENO
);
139 ret
= execv(path
, discard_const(argv
));
150 proc
->fde
= tevent_add_fd(ev
, proc
, fd
[0], TEVENT_FD_READ
,
151 proc_read_handler
, proc
);
152 if (proc
->fde
== NULL
) {
157 tevent_fd_set_auto_close(proc
->fde
);
162 static void proc_read_handler(struct tevent_context
*ev
,
163 struct tevent_fd
*fde
, uint16_t flags
,
166 struct proc_context
*proc
= talloc_get_type_abort(
167 private_data
, struct proc_context
);
173 ret
= ioctl(proc
->fd
, FIONREAD
, &len
);
183 offset
= (proc
->output
== NULL
) ? 0 : strlen(proc
->output
);
185 proc
->output
= talloc_realloc(proc
, proc
->output
, char, offset
+len
+1);
186 if (proc
->output
== NULL
) {
190 nread
= sys_read(proc
->fd
, proc
->output
+ offset
, len
);
194 proc
->output
[offset
+nread
] = '\0';
198 if (proc
->pid
!= -1) {
199 kill(-proc
->pid
, SIGKILL
);
203 TALLOC_FREE(proc
->fde
);
209 * Run proc abstraction
212 struct run_proc_context
{
213 struct tevent_context
*ev
;
214 struct tevent_signal
*se
;
215 struct proc_context
*plist
;
218 static void run_proc_signal_handler(struct tevent_context
*ev
,
219 struct tevent_signal
*se
,
220 int signum
, int count
, void *siginfo
,
222 static int run_proc_context_destructor(struct run_proc_context
*run_ctx
);
223 static void run_proc_done(struct tevent_req
*req
);
225 int run_proc_init(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
226 struct run_proc_context
**result
)
228 struct run_proc_context
*run_ctx
;
230 run_ctx
= talloc_zero(mem_ctx
, struct run_proc_context
);
231 if (run_ctx
== NULL
) {
236 run_ctx
->se
= tevent_add_signal(ev
, run_ctx
, SIGCHLD
, 0,
237 run_proc_signal_handler
, run_ctx
);
238 if (run_ctx
->se
== NULL
) {
239 talloc_free(run_ctx
);
243 talloc_set_destructor(run_ctx
, run_proc_context_destructor
);
249 static void run_proc_signal_handler(struct tevent_context
*ev
,
250 struct tevent_signal
*se
,
251 int signum
, int count
, void *siginfo
,
254 struct run_proc_context
*run_ctx
= talloc_get_type_abort(
255 private_data
, struct run_proc_context
);
256 struct proc_context
*proc
;
261 pid
= waitpid(-1, &status
, WNOHANG
);
270 for (proc
= run_ctx
->plist
; proc
!= NULL
; proc
= proc
->next
) {
271 if (proc
->pid
== pid
) {
277 /* unknown process */
281 /* Mark the process as terminated */
284 /* Update process status */
285 if (WIFEXITED(status
)) {
286 int pstatus
= WEXITSTATUS(status
);
287 if (WIFSIGNALED(status
)) {
288 proc
->result
.sig
= WTERMSIG(status
);
289 } else if (pstatus
>= 64 && pstatus
< 255) {
290 proc
->result
.err
= pstatus
-64;
292 proc
->result
.status
= pstatus
;
294 } else if (WIFSIGNALED(status
)) {
295 proc
->result
.sig
= WTERMSIG(status
);
298 /* Confirm that all data has been read from the pipe */
299 if (proc
->fd
!= -1) {
300 proc_read_handler(ev
, proc
->fde
, 0, proc
);
301 TALLOC_FREE(proc
->fde
);
305 DLIST_REMOVE(run_ctx
->plist
, proc
);
307 /* Active run_proc request */
308 if (proc
->req
!= NULL
) {
309 run_proc_done(proc
->req
);
317 static int run_proc_context_destructor(struct run_proc_context
*run_ctx
)
319 struct proc_context
*proc
;
321 /* Get rid of signal handler */
322 TALLOC_FREE(run_ctx
->se
);
324 /* Kill any pending processes */
325 while ((proc
= run_ctx
->plist
) != NULL
) {
326 DLIST_REMOVE(run_ctx
->plist
, proc
);
333 struct run_proc_state
{
334 struct tevent_context
*ev
;
335 struct run_proc_context
*run_ctx
;
336 struct proc_context
*proc
;
338 struct run_proc_result result
;
343 static int run_proc_state_destructor(struct run_proc_state
*state
);
344 static void run_proc_timedout(struct tevent_req
*subreq
);
346 struct tevent_req
*run_proc_send(TALLOC_CTX
*mem_ctx
,
347 struct tevent_context
*ev
,
348 struct run_proc_context
*run_ctx
,
349 const char *path
, const char **argv
,
350 int stdin_fd
, struct timeval timeout
)
352 struct tevent_req
*req
;
353 struct run_proc_state
*state
;
357 req
= tevent_req_create(mem_ctx
, &state
, struct run_proc_state
);
363 state
->run_ctx
= run_ctx
;
366 ret
= stat(path
, &st
);
368 state
->result
.err
= errno
;
369 tevent_req_done(req
);
370 return tevent_req_post(req
, ev
);
373 if (! (st
.st_mode
& S_IXUSR
)) {
374 state
->result
.err
= EACCES
;
375 tevent_req_done(req
);
376 return tevent_req_post(req
, ev
);
379 state
->proc
= proc_new(run_ctx
, run_ctx
);
380 if (tevent_req_nomem(state
->proc
, req
)) {
381 return tevent_req_post(req
, ev
);
384 state
->proc
->req
= req
;
385 DLIST_ADD(run_ctx
->plist
, state
->proc
);
387 ret
= proc_start(state
->proc
, ev
, path
, argv
, stdin_fd
);
389 tevent_req_error(req
, ret
);
390 return tevent_req_post(req
, ev
);
393 talloc_set_destructor(state
, run_proc_state_destructor
);
395 if (! tevent_timeval_is_zero(&timeout
)) {
396 struct tevent_req
*subreq
;
398 subreq
= tevent_wakeup_send(state
, ev
, timeout
);
399 if (tevent_req_nomem(subreq
, req
)) {
400 return tevent_req_post(req
, ev
);
402 tevent_req_set_callback(subreq
, run_proc_timedout
, req
);
408 static int run_proc_state_destructor(struct run_proc_state
*state
)
410 /* Do not get rid of the child process if timeout has occurred */
411 if (state
->proc
->req
!= NULL
) {
412 state
->proc
->req
= NULL
;
413 DLIST_REMOVE(state
->run_ctx
->plist
, state
->proc
);
414 talloc_free(state
->proc
);
420 static void run_proc_done(struct tevent_req
*req
)
422 struct run_proc_state
*state
= tevent_req_data(
423 req
, struct run_proc_state
);
425 state
->proc
->req
= NULL
;
427 state
->result
= state
->proc
->result
;
428 if (state
->proc
->output
!= NULL
) {
429 state
->output
= talloc_steal(state
, state
->proc
->output
);
431 talloc_steal(state
, state
->proc
);
433 tevent_req_done(req
);
436 static void run_proc_kill(struct tevent_req
*req
)
438 struct run_proc_state
*state
= tevent_req_data(
439 req
, struct run_proc_state
);
441 state
->proc
->req
= NULL
;
443 state
->result
.sig
= SIGKILL
;
445 tevent_req_done(req
);
448 static void run_proc_timedout(struct tevent_req
*subreq
)
450 struct tevent_req
*req
= tevent_req_callback_data(
451 subreq
, struct tevent_req
);
452 struct run_proc_state
*state
= tevent_req_data(
453 req
, struct run_proc_state
);
456 state
->proc
->req
= NULL
;
458 status
= tevent_wakeup_recv(subreq
);
461 tevent_req_error(req
, EIO
);
465 state
->result
.err
= ETIMEDOUT
;
466 if (state
->proc
->output
!= NULL
) {
467 state
->output
= talloc_steal(state
, state
->proc
->output
);
469 state
->pid
= state
->proc
->pid
;
471 tevent_req_done(req
);
474 bool run_proc_recv(struct tevent_req
*req
, int *perr
,
475 struct run_proc_result
*result
, pid_t
*pid
,
476 TALLOC_CTX
*mem_ctx
, char **output
)
478 struct run_proc_state
*state
= tevent_req_data(
479 req
, struct run_proc_state
);
482 if (tevent_req_is_unix_error(req
, &ret
)) {
489 if (result
!= NULL
) {
490 *result
= state
->result
;
497 if (output
!= NULL
) {
498 *output
= talloc_steal(mem_ctx
, state
->output
);