selftest: tests for vfs_fruite file-id behavior
[Samba.git] / ctdb / common / run_proc.c
blobf9fee80e9072c36db6a986afd58b56df1e0ac043
1 /*
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/>.
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/wait.h"
24 #include <talloc.h>
25 #include <tevent.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"
35 * Process abstraction
38 struct proc_context {
39 pid_t pid;
41 int fd;
42 struct tevent_fd *fde;
44 char *output;
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);
55 if (proc == NULL) {
56 return NULL;
59 proc->pid = -1;
60 proc->fd = -1;
62 return proc;
65 static void proc_read_handler(struct tevent_context *ev,
66 struct tevent_fd *fde, uint16_t flags,
67 void *private_data);
69 static int proc_start(struct proc_context *proc, struct tevent_context *ev,
70 const char *path, const char **argv)
72 int fd[2];
73 int ret;
75 ret = pipe(fd);
76 if (ret != 0) {
77 return ret;
80 proc->pid = fork();
81 if (proc->pid == -1) {
82 ret = errno;
83 close(fd[0]);
84 close(fd[1]);
85 return ret;
88 if (proc->pid == 0) {
89 close(fd[0]);
91 ret = dup2(fd[1], STDOUT_FILENO);
92 if (ret == -1) {
93 exit(64 + errno);
95 ret = dup2(fd[1], STDERR_FILENO);
96 if (ret == -1) {
97 exit(64 + errno);
100 close(fd[1]);
102 ret = setpgid(0, 0);
103 if (ret != 0) {
104 exit(64 + errno);
107 ret = execv(path, discard_const(argv));
108 if (ret != 0) {
109 exit(64 + errno);
112 exit(64 + ENOEXEC);
115 close(fd[1]);
117 proc->fd = fd[0];
118 proc->fde = tevent_add_fd(ev, proc, fd[0], TEVENT_FD_READ,
119 proc_read_handler, proc);
120 if (proc->fde == NULL) {
121 return ENOMEM;
124 tevent_fd_set_auto_close(proc->fde);
126 return 0;
129 static void proc_read_handler(struct tevent_context *ev,
130 struct tevent_fd *fde, uint16_t flags,
131 void *private_data)
133 struct proc_context *proc = talloc_get_type_abort(
134 private_data, struct proc_context);
135 size_t offset;
136 ssize_t nread;
137 int len = 0;
138 int ret;
140 ret = ioctl(proc->fd, FIONREAD, &len);
141 if (ret != 0) {
142 goto fail;
145 if (len == 0) {
146 /* pipe closed */
147 goto close;
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) {
154 goto fail;
157 nread = sys_read(proc->fd, proc->output + offset, len);
158 if (nread == -1) {
159 goto fail;
161 proc->output[offset+nread] = '\0';
162 return;
164 fail:
165 kill(-proc->pid, SIGKILL);
166 close:
167 TALLOC_FREE(proc->fde);
168 proc->fd = -1;
172 * Processes database
175 static int proc_db_init(TALLOC_CTX *mem_ctx, struct db_hash_context **result)
177 struct db_hash_context *pdb = NULL;
178 int ret;
180 ret = db_hash_init(pdb, "proc_db", 1001, DB_HASH_COMPLEX, &pdb);
181 if (ret != 0) {
182 return ret;
185 *result = pdb;
186 return 0;
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,
203 void *private_data)
205 struct proc_context **result = (struct proc_context **)private_data;
207 if (datalen != sizeof(struct proc_context *)) {
208 return EINVAL;
211 *result = *(struct proc_context **)databuf;
212 return 0;
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,
224 void *private_data)
226 struct db_hash_context *pdb = talloc_get_type_abort(
227 private_data, struct db_hash_context);
228 struct proc_context *proc;
229 pid_t pid;
231 if (keylen != sizeof(pid_t) ||
232 datalen != sizeof(struct proc_context *)) {
233 /* skip */
234 return 0;
237 pid = *(pid_t *)keybuf;
238 proc = talloc_steal(pdb, *(struct proc_context **)databuf);
240 TALLOC_FREE(proc->req);
241 TALLOC_FREE(proc->fde);
243 kill(-pid, SIGKILL);
244 return 0;
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,
266 void *private_data);
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;
274 int ret;
276 run_ctx = talloc_zero(mem_ctx, struct run_proc_context);
277 if (run_ctx == NULL) {
278 return ENOMEM;
281 run_ctx->ev = ev;
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);
286 return ENOMEM;
289 ret = proc_db_init(run_ctx, &run_ctx->pdb);
290 if (ret != 0) {
291 talloc_free(run_ctx);
292 return ret;
295 talloc_set_destructor(run_ctx, run_proc_context_destructor);
297 *result = run_ctx;
298 return 0;
301 static void run_proc_signal_handler(struct tevent_context *ev,
302 struct tevent_signal *se,
303 int signum, int count, void *siginfo,
304 void *private_data)
306 struct run_proc_context *run_ctx = talloc_get_type_abort(
307 private_data, struct run_proc_context);
308 struct proc_context *proc;
309 pid_t pid = -1;
310 int ret, status;
312 again:
313 pid = waitpid(-1, &status, WNOHANG);
314 if (pid == -1) {
315 return;
318 if (pid == 0) {
319 return;
322 ret = proc_db_fetch(run_ctx->pdb, pid, &proc);
323 if (ret != 0) {
324 /* unknown process */
325 return;
328 /* Mark the process as terminated */
329 proc->pid = -1;
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;
338 } else {
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);
351 talloc_free(proc);
353 goto again;
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);
366 return 0;
369 struct run_proc_state {
370 struct tevent_context *ev;
371 struct proc_context *proc;
373 struct run_proc_result result;
374 char *output;
375 pid_t pid;
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;
389 struct stat st;
390 int ret;
392 req = tevent_req_create(mem_ctx, &state, struct run_proc_state);
393 if (req == NULL) {
394 return NULL;
397 state->ev = ev;
398 state->pid = -1;
400 ret = stat(path, &st);
401 if (ret != 0) {
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);
419 if (ret != 0) {
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);
428 if (ret != 0) {
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);
443 return 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);
456 return 0;
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);
480 bool status;
482 state->proc->req = NULL;
484 status = tevent_wakeup_recv(subreq);
485 TALLOC_FREE(subreq);
486 if (! status) {
487 tevent_req_error(req, EIO);
488 return;
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);
506 int ret;
508 if (tevent_req_is_unix_error(req, &ret)) {
509 if (perr != NULL) {
510 *perr = ret;
512 return false;
515 if (result != NULL) {
516 *result = state->result;
519 if (pid != NULL) {
520 *pid = state->pid;
523 if (output != NULL) {
524 *output = talloc_steal(mem_ctx, state->output);
527 return true;