]> git.ozlabs.org Git - petitboot/blob - lib/process/process.c
02b6f490dad8318744ba8b4f43b30c6362ceae09
[petitboot] / lib / process / process.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; version 2 of the License.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
14  */
15
16 #include <assert.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/wait.h>
21
22 #include <process/process.h>
23 #include <talloc/talloc.h>
24 #include <waiter/waiter.h>
25 #include <log/log.h>
26
27 struct procset {
28         struct waitset          *waitset;
29         struct list             async_list;
30         int                     sigchld_pipe[2];
31         struct waiter           *sigchld_waiter;
32         bool                    dry_run;
33 };
34
35 /* Internal data type for process handling
36  *
37  * Allocation: these structures may have multiple references:
38  *  - from the original ctx pointer
39  *  - due to inclusion in async_list
40  *  - due to a currently-registered waiter
41  *
42  */
43 struct process_info {
44 #ifdef DEBUG
45         /* prevent talloc_free(process) from working */
46         int                     __pad;
47 #endif
48         struct process          process;
49         struct list_item        async_list;
50         int                     stdout_buf_len;
51         struct waiter           *stdout_waiter;
52         int                     stdout_pipe[2];
53         void                    *orig_ctx;
54 };
55
56 static struct procset *procset;
57
58 static struct process_info *get_info(struct process *process)
59 {
60         return container_of(process, struct process_info, process);
61 }
62
63 struct process *procinfo_get_process(struct process_info *procinfo)
64 {
65         return &procinfo->process;
66 }
67
68 /* Read as much as possible into the currently-allocated stdout buffer, and
69  * possibly realloc it for the next read
70  * If the line pointer is not NULL, it is set to the start of the latest
71  * output.
72  *
73  * Returns:
74  *  > 0 on success (even though no bytes may have been read)
75  *    0 on EOF (no error, but no more reads can be performed)
76  *  < 0 on error
77  **/
78 static int process_read_stdout_once(struct process_info *procinfo, char **line)
79 {
80         struct process *process = &procinfo->process;
81         int rc, fd, max_len;
82
83         assert(process->keep_stdout);
84
85         fd = procinfo->stdout_pipe[0];
86
87         max_len =  procinfo->stdout_buf_len - process->stdout_len - 1;
88
89         rc = read(fd, process->stdout_buf + process->stdout_len, max_len);
90         if (rc == 0)
91                 return 0;
92         if (rc < 0) {
93                 if (errno == EINTR)
94                         return 1;
95                 pb_log_fn("read failed: %s\n", strerror(errno));
96                 return rc;
97         }
98
99         if (line)
100                 *line = process->stdout_buf + process->stdout_len;
101
102         process->stdout_len += rc;
103         if (process->stdout_len == procinfo->stdout_buf_len - 1) {
104                 procinfo->stdout_buf_len *= 2;
105                 process->stdout_buf = talloc_realloc(procinfo,
106                                 process->stdout_buf, char,
107                                 procinfo->stdout_buf_len);
108         }
109
110         return 1;
111 }
112
113 static int process_setup_stdout_pipe(struct process_info *procinfo)
114 {
115         int rc;
116
117         if (!procinfo->process.keep_stdout || procinfo->process.raw_stdout)
118                 return 0;
119
120         procinfo->stdout_buf_len = 4096;
121         procinfo->process.stdout_len = 0;
122         procinfo->process.stdout_buf = talloc_array(procinfo, char,
123                         procinfo->stdout_buf_len);
124
125         rc = pipe(procinfo->stdout_pipe);
126         if (rc) {
127                 pb_log("pipe failed");
128                 return rc;
129         }
130         return 0;
131 }
132
133 static void process_setup_stdout_parent(struct process_info *procinfo)
134 {
135         if (!procinfo->process.keep_stdout || procinfo->process.raw_stdout)
136                 return;
137
138         close(procinfo->stdout_pipe[1]);
139 }
140
141 static void process_setup_stdout_child(struct process_info *procinfo)
142 {
143         int log = fileno(pb_log_get_stream());
144
145         if (procinfo->process.raw_stdout)
146                 return;
147
148         if (procinfo->process.keep_stdout)
149                 dup2(procinfo->stdout_pipe[1], STDOUT_FILENO);
150         else
151                 dup2(log, STDOUT_FILENO);
152
153         if (procinfo->process.keep_stdout && procinfo->process.add_stderr)
154                 dup2(procinfo->stdout_pipe[1], STDERR_FILENO);
155         else
156                 dup2(log, STDERR_FILENO);
157 }
158
159 static void process_finish_stdout(struct process_info *procinfo)
160 {
161         close(procinfo->stdout_pipe[0]);
162         procinfo->process.stdout_buf[procinfo->process.stdout_len] = '\0';
163 }
164
165 static int process_read_stdout(struct process_info *procinfo)
166 {
167         int rc;
168
169         if (!procinfo->process.keep_stdout)
170                 return 0;
171
172         do {
173                 rc = process_read_stdout_once(procinfo, NULL);
174         } while (rc > 0);
175
176         process_finish_stdout(procinfo);
177
178         return rc < 0 ? rc : 0;
179 }
180
181 int process_process_stdout(struct process_info *procinfo, char **line)
182 {
183         int rc;
184
185         rc = process_read_stdout_once(procinfo, line);
186
187         /* if we're going to signal to the waitset that we're done (ie, non-zero
188          * return value), then the waiters will remove us, so we drop the
189          * reference */
190         if (rc < 0) {
191                 talloc_unlink(procset, procinfo);
192                 procinfo->stdout_waiter = NULL;
193                 rc = -1;
194         } else {
195                 rc = 0;
196         }
197
198         return rc;
199 }
200
201 static void sigchld_sigaction(int signo, siginfo_t *info,
202                 void *arg __attribute__((unused)))
203 {
204         pid_t pid;
205         int rc;
206
207         if (signo != SIGCHLD)
208                 return;
209
210         pid = info->si_pid;
211
212         rc = write(procset->sigchld_pipe[1], &pid, sizeof(pid));
213         if (rc != sizeof(pid))
214                 pb_log_fn("write failed: %s\n", strerror(errno));
215 }
216
217 static int sigchld_pipe_event(void *arg)
218 {
219         struct process_info *procinfo;
220         struct procset *procset = arg;
221         struct process *process;
222         int pid, rc;
223
224         rc = read(procset->sigchld_pipe[0], &pid, sizeof(pid));
225         if (rc != sizeof(pid))
226                 return 0;
227
228         process = NULL;
229         list_for_each_entry(&procset->async_list, procinfo, async_list) {
230                 if (procinfo->process.pid == pid) {
231                         process = &procinfo->process;
232                         break;
233                 }
234         }
235
236         /* We'll receive SIGCHLD for synchronous processes too; just ignore */
237         if (!process)
238                 return 0;
239
240         rc = waitpid(process->pid, &process->exit_status, WNOHANG);
241
242         /* if the process is still running, ignore the event. We leave
243          * the process in async_list so we can manage the final signal */
244         if (rc == 0)
245                 return 0;
246
247         /* ensure we have all of the child's stdout */
248         process_read_stdout(procinfo);
249
250         if (process->exit_cb)
251                 process->exit_cb(process);
252
253         list_remove(&procinfo->async_list);
254         talloc_unlink(procset, procinfo);
255
256         return 0;
257 }
258
259 static int process_fini(void *p)
260 {
261         struct procset *procset = p;
262         struct sigaction sa;
263
264         memset(&sa, 0, sizeof(sa));
265         sa.sa_handler = SIG_DFL;
266
267         sigaction(SIGCHLD, &sa, NULL);
268
269         waiter_remove(procset->sigchld_waiter);
270
271         close(procset->sigchld_pipe[0]);
272         close(procset->sigchld_pipe[1]);
273         return 0;
274 }
275
276 struct procset *process_init(void *ctx, struct waitset *set, bool dry_run)
277 {
278         struct sigaction sa;
279         int rc;
280
281         procset = talloc(ctx, struct procset);
282         procset->waitset = set;
283         procset->dry_run = dry_run;
284         list_init(&procset->async_list);
285
286         rc = pipe(procset->sigchld_pipe);
287         if (rc) {
288                 pb_log_fn("pipe() failed: %s\n", strerror(errno));
289                 goto err_free;
290         }
291
292         procset->sigchld_waiter = waiter_register_io(set,
293                                         procset->sigchld_pipe[0], WAIT_IN,
294                                         sigchld_pipe_event, procset);
295         if (!procset->sigchld_waiter)
296                 goto err_close;
297
298         memset(&sa, 0, sizeof(sa));
299         sa.sa_sigaction = sigchld_sigaction;
300         sa.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
301
302         rc = sigaction(SIGCHLD, &sa, NULL);
303         if (rc) {
304                 pb_log_fn("sigaction() failed: %s\n",
305                                 strerror(errno));
306                 goto err_remove;
307         }
308
309         talloc_set_destructor(procset, process_fini);
310
311         return procset;
312
313 err_remove:
314         waiter_remove(procset->sigchld_waiter);
315 err_close:
316         close(procset->sigchld_pipe[0]);
317         close(procset->sigchld_pipe[1]);
318 err_free:
319         talloc_free(procset);
320         return NULL;
321 }
322
323 struct process *process_create(void *ctx)
324 {
325         struct process_info *info = talloc_zero(ctx, struct process_info);
326         info->orig_ctx = ctx;
327         return &info->process;
328 }
329
330 void process_release(struct process *process)
331 {
332         struct process_info *info = get_info(process);
333         talloc_unlink(info->orig_ctx, info);
334 }
335
336 static int process_run_common(struct process_info *procinfo)
337 {
338         struct process *process = &procinfo->process;
339         const char *arg;
340         char *logmsg;
341         pid_t pid;
342         int rc, i;
343
344         logmsg = talloc_asprintf(procinfo, " exe:  %s\n argv:", process->path);
345         for (i = 0, arg = process->argv[i]; arg; i++, arg = process->argv[i])
346                 logmsg = talloc_asprintf_append(logmsg, " '%s'", arg);
347
348         pb_log("Running command:\n%s\n", logmsg);
349
350         rc = process_setup_stdout_pipe(procinfo);
351         if (rc)
352                 return rc;
353
354         pid = fork();
355         if (pid < 0) {
356                 pb_log_fn("fork failed: %s\n", strerror(errno));
357                 return pid;
358         }
359
360         if (pid == 0) {
361                 process_setup_stdout_child(procinfo);
362                 if (procset->dry_run)
363                         exit(EXIT_SUCCESS);
364                 execvp(process->path, (char * const *)process->argv);
365                 exit(EXIT_FAILURE);
366         }
367
368         process_setup_stdout_parent(procinfo);
369         process->pid = pid;
370
371         return 0;
372 }
373
374 int process_run_sync(struct process *process)
375 {
376         struct process_info *procinfo = get_info(process);
377         int rc;
378
379         rc = process_run_common(procinfo);
380         if (rc)
381                 return rc;
382
383         process_read_stdout(procinfo);
384
385         for (;;) {
386                 rc = waitpid(process->pid, &process->exit_status, 0);
387                 if (rc >= 0)
388                         break;
389                 if (errno == EINTR)
390                         continue;
391
392                 pb_log_fn("waitpid failed: %s\n", strerror(errno));
393                 return rc;
394         }
395
396         return 0;
397 }
398
399 static int process_stdout_cb(struct process_info *procinfo)
400 {
401         return process_process_stdout(procinfo, NULL);
402 }
403
404 int process_run_async(struct process *process)
405 {
406         struct process_info *procinfo = get_info(process);
407         int rc;
408
409         rc = process_run_common(procinfo);
410         if (rc)
411                 return rc;
412
413         if (process->keep_stdout) {
414                 waiter_cb stdout_cb = process->stdout_cb ?:
415                         (waiter_cb)process_stdout_cb;
416                 procinfo->stdout_waiter = waiter_register_io(procset->waitset,
417                                                 procinfo->stdout_pipe[0],
418                                                 WAIT_IN, stdout_cb, procinfo);
419                 talloc_reference(procset, procinfo);
420         }
421
422         list_add(&procset->async_list, &procinfo->async_list);
423         talloc_reference(procset, procinfo);
424
425         return 0;
426 }
427
428 void process_stop_async(struct process *process)
429 {
430         /* Avoid signalling an old pid */
431         if (process->cancelled)
432                 return;
433
434         pb_debug("process: sending SIGTERM to pid %d\n", process->pid);
435         kill(process->pid, SIGTERM);
436         process->cancelled = true;
437 }
438
439 void process_stop_async_all(void)
440 {
441         struct process_info *procinfo;
442         struct process *process = NULL;
443
444         pb_debug("process: cancelling all async jobs\n");
445
446         list_for_each_entry(&procset->async_list, procinfo, async_list) {
447                 process = &procinfo->process;
448                 /* Ignore the process completion - callbacks may use stale data */
449                 process->exit_cb = NULL;
450                 process->stdout_cb = NULL;
451                 process_stop_async(process);
452         }
453 }
454
455 int process_get_stdout_argv(void *ctx, struct process_stdout **stdout,
456         const char *argv[])
457 {
458         struct process *p;
459         int rc;
460
461         p = process_create(NULL);
462         p->path = argv[0];
463         p->argv = argv;
464
465         if (stdout) {
466                 p->keep_stdout = true;
467                 *stdout = NULL;
468         }
469
470         rc = process_run_sync(p);
471
472         if (!rc)
473                 rc = p->exit_status;
474         else {
475                 pb_debug("%s: process_run_sync failed: %s.\n", __func__,
476                         p->path);
477                 if (stdout)
478                         pb_debug("%s: stdout: %s\n\n", __func__, p->stdout_buf);
479                 goto exit;
480         }
481
482         if (!stdout)
483                 goto exit;
484
485         *stdout = talloc(ctx, struct process_stdout);
486
487         if (!*stdout) {
488                 rc = -1;
489                 goto exit;
490         }
491
492         (*stdout)->len = p->stdout_len;
493         (*stdout)->buf = talloc_memdup(*stdout, p->stdout_buf,
494                 p->stdout_len + 1);
495         (*stdout)->buf[p->stdout_len] = 0;
496
497 exit:
498         process_release(p);
499         return rc;
500 }
501
502 int process_get_stdout(void *ctx, struct process_stdout **stdout,
503         const char *path, ...)
504 {
505         int rc, i, n_argv = 1;
506         const char **argv;
507         va_list ap;
508
509         va_start(ap, path);
510         while (va_arg(ap, char *))
511                 n_argv++;
512         va_end(ap);
513
514         argv = talloc_array(ctx, const char *, n_argv + 1);
515         argv[0] = path;
516
517         va_start(ap, path);
518         for (i = 1; i < n_argv; i++)
519                 argv[i] = va_arg(ap, const char *);
520         va_end(ap);
521
522         argv[i] = NULL;
523
524         rc = process_get_stdout_argv(ctx, stdout, argv);
525
526         talloc_free(argv);
527
528         return rc;
529 }
530
531 bool process_exit_ok(struct process *process)
532 {
533         return WIFEXITED(process->exit_status) &&
534                 WEXITSTATUS(process->exit_status) == 0;
535 }