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"
31 #include "common/db_hash.h"
32 #include "common/run_proc.h"
42 struct tevent_fd
*fde
;
45 struct run_proc_result result
;
47 struct tevent_req
*req
;
50 static struct proc_context
*proc_new(TALLOC_CTX
*mem_ctx
)
52 struct proc_context
*proc
;
54 proc
= talloc_zero(mem_ctx
, struct proc_context
);
65 static void proc_read_handler(struct tevent_context
*ev
,
66 struct tevent_fd
*fde
, uint16_t flags
,
69 static int proc_start(struct proc_context
*proc
, struct tevent_context
*ev
,
70 const char *path
, const char **argv
)
81 if (proc
->pid
== -1) {
91 ret
= dup2(fd
[1], STDOUT_FILENO
);
95 ret
= dup2(fd
[1], STDERR_FILENO
);
107 ret
= execv(path
, discard_const(argv
));
118 proc
->fde
= tevent_add_fd(ev
, proc
, fd
[0], TEVENT_FD_READ
,
119 proc_read_handler
, proc
);
120 if (proc
->fde
== NULL
) {
124 tevent_fd_set_auto_close(proc
->fde
);
129 static void proc_read_handler(struct tevent_context
*ev
,
130 struct tevent_fd
*fde
, uint16_t flags
,
133 struct proc_context
*proc
= talloc_get_type_abort(
134 private_data
, struct proc_context
);
140 ret
= ioctl(proc
->fd
, FIONREAD
, &len
);
150 offset
= (proc
->output
== NULL
) ? 0 : strlen(proc
->output
);
152 proc
->output
= talloc_realloc(proc
, proc
->output
, char, offset
+len
+1);
153 if (proc
->output
== NULL
) {
157 nread
= sys_read(proc
->fd
, proc
->output
+ offset
, len
);
161 proc
->output
[offset
+nread
] = '\0';
165 kill(-proc
->pid
, SIGKILL
);
167 TALLOC_FREE(proc
->fde
);
175 static int proc_db_init(TALLOC_CTX
*mem_ctx
, struct db_hash_context
**result
)
177 struct db_hash_context
*pdb
= NULL
;
180 ret
= db_hash_init(pdb
, "proc_db", 1001, DB_HASH_COMPLEX
, &pdb
);
189 static int proc_db_add(struct db_hash_context
*pdb
, pid_t pid
,
190 struct proc_context
*proc
)
192 return db_hash_insert(pdb
, (uint8_t *)&pid
, sizeof(pid_t
),
193 (uint8_t *)&proc
, sizeof(struct proc_context
*));
196 static int proc_db_remove(struct db_hash_context
*pdb
, pid_t pid
)
198 return db_hash_delete(pdb
, (uint8_t *)&pid
, sizeof(pid_t
));
201 static int proc_db_fetch_parser(uint8_t *keybuf
, size_t keylen
,
202 uint8_t *databuf
, size_t datalen
,
205 struct proc_context
**result
= (struct proc_context
**)private_data
;
207 if (datalen
!= sizeof(struct proc_context
*)) {
211 *result
= *(struct proc_context
**)databuf
;
215 static int proc_db_fetch(struct db_hash_context
*pdb
, pid_t pid
,
216 struct proc_context
**result
)
218 return db_hash_fetch(pdb
, (uint8_t *)&pid
, sizeof(pid_t
),
219 proc_db_fetch_parser
, result
);
222 static int proc_db_killall_parser(uint8_t *keybuf
, size_t keylen
,
223 uint8_t *databuf
, size_t datalen
,
226 struct db_hash_context
*pdb
= talloc_get_type_abort(
227 private_data
, struct db_hash_context
);
228 struct proc_context
*proc
;
231 if (keylen
!= sizeof(pid_t
) ||
232 datalen
!= sizeof(struct proc_context
*)) {
237 pid
= *(pid_t
*)keybuf
;
238 proc
= talloc_steal(pdb
, *(struct proc_context
**)databuf
);
240 TALLOC_FREE(proc
->req
);
241 TALLOC_FREE(proc
->fde
);
247 static void proc_db_killall(struct db_hash_context
*pdb
)
249 (void) db_hash_traverse(pdb
, proc_db_killall_parser
, pdb
, NULL
);
254 * Run proc abstraction
257 struct run_proc_context
{
258 struct tevent_context
*ev
;
259 struct tevent_signal
*se
;
260 struct db_hash_context
*pdb
;
263 static void run_proc_signal_handler(struct tevent_context
*ev
,
264 struct tevent_signal
*se
,
265 int signum
, int count
, void *siginfo
,
267 static int run_proc_context_destructor(struct run_proc_context
*run_ctx
);
268 static void run_proc_done(struct tevent_req
*req
);
270 int run_proc_init(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
271 struct run_proc_context
**result
)
273 struct run_proc_context
*run_ctx
;
276 run_ctx
= talloc_zero(mem_ctx
, struct run_proc_context
);
277 if (run_ctx
== NULL
) {
282 run_ctx
->se
= tevent_add_signal(ev
, run_ctx
, SIGCHLD
, 0,
283 run_proc_signal_handler
, run_ctx
);
284 if (run_ctx
->se
== NULL
) {
285 talloc_free(run_ctx
);
289 ret
= proc_db_init(run_ctx
, &run_ctx
->pdb
);
291 talloc_free(run_ctx
);
295 talloc_set_destructor(run_ctx
, run_proc_context_destructor
);
301 static void run_proc_signal_handler(struct tevent_context
*ev
,
302 struct tevent_signal
*se
,
303 int signum
, int count
, void *siginfo
,
306 struct run_proc_context
*run_ctx
= talloc_get_type_abort(
307 private_data
, struct run_proc_context
);
308 struct proc_context
*proc
;
313 pid
= waitpid(-1, &status
, WNOHANG
);
322 ret
= proc_db_fetch(run_ctx
->pdb
, pid
, &proc
);
324 /* unknown process */
328 /* Mark the process as terminated */
331 /* Update process status */
332 if (WIFEXITED(status
)) {
333 int pstatus
= WEXITSTATUS(status
);
334 if (WIFSIGNALED(status
)) {
335 proc
->result
.sig
= WTERMSIG(status
);
336 } else if (pstatus
>= 64 && pstatus
< 255) {
337 proc
->result
.err
= pstatus
-64;
339 proc
->result
.status
= pstatus
;
341 } else if (WIFSIGNALED(status
)) {
342 proc
->result
.sig
= WTERMSIG(status
);
345 /* Active run_proc request */
346 if (proc
->req
!= NULL
) {
347 run_proc_done(proc
->req
);
350 proc_db_remove(run_ctx
->pdb
, pid
);
357 static int run_proc_context_destructor(struct run_proc_context
*run_ctx
)
359 /* Get rid of signal handler */
360 TALLOC_FREE(run_ctx
->se
);
362 /* Kill any pending processes */
363 proc_db_killall(run_ctx
->pdb
);
364 TALLOC_FREE(run_ctx
->pdb
);
369 struct run_proc_state
{
370 struct tevent_context
*ev
;
371 struct proc_context
*proc
;
373 struct run_proc_result result
;
378 static int run_proc_state_destructor(struct run_proc_state
*state
);
379 static void run_proc_timedout(struct tevent_req
*subreq
);
381 struct tevent_req
*run_proc_send(TALLOC_CTX
*mem_ctx
,
382 struct tevent_context
*ev
,
383 struct run_proc_context
*run_ctx
,
384 const char *path
, const char **argv
,
385 struct timeval timeout
)
387 struct tevent_req
*req
;
388 struct run_proc_state
*state
;
392 req
= tevent_req_create(mem_ctx
, &state
, struct run_proc_state
);
400 ret
= stat(path
, &st
);
402 state
->result
.err
= errno
;
403 tevent_req_done(req
);
404 return tevent_req_post(req
, ev
);
407 if (! (st
.st_mode
& S_IXUSR
)) {
408 state
->result
.err
= EACCES
;
409 tevent_req_done(req
);
410 return tevent_req_post(req
, ev
);
413 state
->proc
= proc_new(run_ctx
);
414 if (tevent_req_nomem(state
->proc
, req
)) {
415 return tevent_req_post(req
, ev
);
418 ret
= proc_start(state
->proc
, ev
, path
, argv
);
420 tevent_req_error(req
, ret
);
421 return tevent_req_post(req
, ev
);
424 state
->proc
->req
= req
;
425 talloc_set_destructor(state
, run_proc_state_destructor
);
427 ret
= proc_db_add(run_ctx
->pdb
, state
->proc
->pid
, state
->proc
);
429 tevent_req_error(req
, ret
);
430 return tevent_req_post(req
, ev
);
433 if (! tevent_timeval_is_zero(&timeout
)) {
434 struct tevent_req
*subreq
;
436 subreq
= tevent_wakeup_send(state
, ev
, timeout
);
437 if (tevent_req_nomem(subreq
, req
)) {
438 return tevent_req_post(req
, ev
);
440 tevent_req_set_callback(subreq
, run_proc_timedout
, req
);
446 static int run_proc_state_destructor(struct run_proc_state
*state
)
448 /* Do not get rid of the child process if timeout has occurred */
449 if (state
->proc
->req
!= NULL
) {
450 state
->proc
->req
= NULL
;
451 if (state
->proc
->pid
!= -1) {
452 kill(-state
->proc
->pid
, SIGTERM
);
459 static void run_proc_done(struct tevent_req
*req
)
461 struct run_proc_state
*state
= tevent_req_data(
462 req
, struct run_proc_state
);
464 state
->proc
->req
= NULL
;
466 state
->result
= state
->proc
->result
;
467 if (state
->proc
->output
!= NULL
) {
468 state
->output
= talloc_steal(state
, state
->proc
->output
);
471 tevent_req_done(req
);
474 static void run_proc_timedout(struct tevent_req
*subreq
)
476 struct tevent_req
*req
= tevent_req_callback_data(
477 subreq
, struct tevent_req
);
478 struct run_proc_state
*state
= tevent_req_data(
479 req
, struct run_proc_state
);
482 state
->proc
->req
= NULL
;
484 status
= tevent_wakeup_recv(subreq
);
487 tevent_req_error(req
, EIO
);
491 state
->result
.err
= ETIME
;
492 if (state
->proc
->output
!= NULL
) {
493 state
->output
= talloc_steal(state
, state
->proc
->output
);
495 state
->pid
= state
->proc
->pid
;
497 tevent_req_done(req
);
500 bool run_proc_recv(struct tevent_req
*req
, int *perr
,
501 struct run_proc_result
*result
, pid_t
*pid
,
502 TALLOC_CTX
*mem_ctx
, char **output
)
504 struct run_proc_state
*state
= tevent_req_data(
505 req
, struct run_proc_state
);
508 if (tevent_req_is_unix_error(req
, &ret
)) {
515 if (result
!= NULL
) {
516 *result
= state
->result
;
523 if (output
!= NULL
) {
524 *output
= talloc_steal(mem_ctx
, state
->output
);