]> git.ozlabs.org Git - petitboot/blob - lib/process/process.c
lib/process: Add process helpers
[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 };
33
34 /* Internal data type for process handling
35  *
36  * Allocation: these structures may have multiple references:
37  *  - from the original ctx pointer
38  *  - due to inclusion in async_list
39  *  - due to a currently-registered waiter
40  *
41  */
42 struct process_info {
43 #ifdef DEBUG
44         /* prevent talloc_free(process) from working */
45         int                     __pad;
46 #endif
47         struct process          process;
48         struct list_item        async_list;
49         int                     stdout_buf_len;
50         struct waiter           *stdout_waiter;
51         int                     stdout_pipe[2];
52         void                    *orig_ctx;
53 };
54
55 static struct procset *procset;
56
57 static struct process_info *get_info(struct process *process)
58 {
59         return container_of(process, struct process_info, process);
60 }
61
62 /* Read as much as possible into the currently-allocated stdout buffer, and
63  * possibly realloc it for the next read */
64 static int process_read_stdout_once(struct process_info *procinfo)
65 {
66         struct process *process = &procinfo->process;
67         int rc, fd, max_len;
68
69         assert(process->keep_stdout);
70
71         fd = procinfo->stdout_pipe[0];
72
73         max_len =  procinfo->stdout_buf_len - process->stdout_len - 1;
74
75         rc = read(fd, process->stdout_buf + process->stdout_len, max_len);
76         if (rc <= 0)
77                 return rc;
78
79         process->stdout_len += rc;
80         if (process->stdout_len == procinfo->stdout_buf_len - 1) {
81                 procinfo->stdout_buf_len *= 2;
82                 process->stdout_buf = talloc_realloc(procinfo,
83                                 process->stdout_buf, char,
84                                 procinfo->stdout_buf_len);
85         }
86
87         return rc;
88 }
89
90 static int process_setup_stdout_pipe(struct process_info *procinfo)
91 {
92         int rc;
93
94         if (!procinfo->process.keep_stdout)
95                 return 0;
96
97         procinfo->stdout_buf_len = 4096;
98         procinfo->process.stdout_len = 0;
99         procinfo->process.stdout_buf = talloc_array(procinfo, char,
100                         procinfo->stdout_buf_len);
101
102         rc = pipe(procinfo->stdout_pipe);
103         if (rc) {
104                 pb_log("pipe failed");
105                 return rc;
106         }
107         return 0;
108 }
109
110 static void process_setup_stdout_parent(struct process_info *procinfo)
111 {
112         if (!procinfo->process.keep_stdout)
113                 return;
114
115         close(procinfo->stdout_pipe[1]);
116 }
117
118 static void process_setup_stdout_child(struct process_info *procinfo)
119 {
120         int log = fileno(pb_log_get_stream());
121
122         if (procinfo->process.keep_stdout)
123                 dup2(procinfo->stdout_pipe[1], STDOUT_FILENO);
124         else
125                 dup2(log, STDOUT_FILENO);
126
127         dup2(log, STDERR_FILENO);
128 }
129
130 static void process_finish_stdout(struct process_info *procinfo)
131 {
132         close(procinfo->stdout_pipe[0]);
133         procinfo->process.stdout_buf[procinfo->process.stdout_len] = '\0';
134 }
135
136 static int process_read_stdout(struct process_info *procinfo)
137 {
138         int rc;
139
140         if (!procinfo->process.keep_stdout)
141                 return 0;
142
143         do {
144                 rc = process_read_stdout_once(procinfo);
145         } while (rc > 0);
146
147         process_finish_stdout(procinfo);
148
149         return rc < 0 ? rc : 0;
150 }
151
152 static int process_stdout_cb(void *arg)
153 {
154         struct process_info *procinfo = arg;
155         int rc;
156
157         rc = process_read_stdout_once(procinfo);
158
159         /* if we're going to signal to the waitset that we're done (ie, non-zero
160          * return value), then the waiters will remove us, so we drop the
161          * reference */
162         if (rc < 0) {
163                 talloc_unlink(procset, procinfo);
164                 procinfo->stdout_waiter = NULL;
165                 rc = -1;
166         } else {
167                 rc = 0;
168         }
169
170         return rc;
171 }
172
173 static void sigchld_sigaction(int signo, siginfo_t *info,
174                 void *arg __attribute__((unused)))
175 {
176         pid_t pid;
177         int rc;
178
179         if (signo != SIGCHLD)
180                 return;
181
182         pid = info->si_pid;
183
184         rc = write(procset->sigchld_pipe[1], &pid, sizeof(pid));
185         if (rc != sizeof(pid))
186                 pb_log("%s: write failed: %s\n", __func__, strerror(errno));
187 }
188
189 static int sigchld_pipe_event(void *arg)
190 {
191         struct process_info *procinfo;
192         struct procset *procset = arg;
193         struct process *process;
194         int pid, rc;
195
196         rc = read(procset->sigchld_pipe[0], &pid, sizeof(pid));
197         if (rc != sizeof(pid))
198                 return 0;
199
200         process = NULL;
201         list_for_each_entry(&procset->async_list, procinfo, async_list) {
202                 if (procinfo->process.pid == pid) {
203                         process = &procinfo->process;
204                         break;
205                 }
206         }
207
208         /* We'll receive SIGCHLD for synchronous processes too; just ignore */
209         if (!process)
210                 return 0;
211
212         rc = waitpid(process->pid, &process->exit_status, WNOHANG);
213
214         /* if the process is still running, ignore the event. We leave
215          * the process in async_list so we can manage the final signal */
216         if (rc == 0)
217                 return 0;
218
219         /* ensure we have all of the child's stdout */
220         process_read_stdout(procinfo);
221
222         if (process->exit_cb)
223                 process->exit_cb(process);
224
225         list_remove(&procinfo->async_list);
226         talloc_unlink(procset, procinfo);
227
228         return 0;
229 }
230
231 static int process_fini(void *p)
232 {
233         struct procset *procset = p;
234         struct sigaction sa;
235
236         memset(&sa, 0, sizeof(sa));
237         sa.sa_handler = SIG_DFL;
238
239         sigaction(SIGCHLD, &sa, NULL);
240
241         waiter_remove(procset->sigchld_waiter);
242
243         close(procset->sigchld_pipe[0]);
244         close(procset->sigchld_pipe[1]);
245         return 0;
246 }
247
248 struct procset *process_init(void *ctx, struct waitset *set)
249 {
250         struct sigaction sa;
251         int rc;
252
253         procset = talloc(ctx, struct procset);
254         procset->waitset = set;
255         list_init(&procset->async_list);
256
257         rc = pipe(procset->sigchld_pipe);
258         if (rc) {
259                 pb_log("%s: pipe() failed: %s\n", __func__, strerror(errno));
260                 goto err_free;
261         }
262
263         procset->sigchld_waiter = waiter_register_io(set,
264                                         procset->sigchld_pipe[0], WAIT_IN,
265                                         sigchld_pipe_event, procset);
266         if (!procset->sigchld_waiter)
267                 goto err_close;
268
269         memset(&sa, 0, sizeof(sa));
270         sa.sa_sigaction = sigchld_sigaction;
271         sa.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
272
273         rc = sigaction(SIGCHLD, &sa, NULL);
274         if (rc) {
275                 pb_log("%s: sigaction() failed: %s\n", __func__,
276                                 strerror(errno));
277                 goto err_remove;
278         }
279
280         talloc_set_destructor(procset, process_fini);
281
282         return procset;
283
284 err_remove:
285         waiter_remove(procset->sigchld_waiter);
286 err_close:
287         close(procset->sigchld_pipe[0]);
288         close(procset->sigchld_pipe[1]);
289 err_free:
290         talloc_free(procset);
291         return NULL;
292 }
293
294 struct process *process_create(void *ctx)
295 {
296         struct process_info *info = talloc_zero(ctx, struct process_info);
297         info->orig_ctx = ctx;
298         return &info->process;
299 }
300
301 void process_release(struct process *process)
302 {
303         struct process_info *info = get_info(process);
304         talloc_unlink(info->orig_ctx, info);
305 }
306
307 static int process_run_common(struct process_info *procinfo)
308 {
309         struct process *process = &procinfo->process;
310         const char *arg;
311         char *logmsg;
312         pid_t pid;
313         int rc, i;
314
315         logmsg = talloc_asprintf(procinfo, " exe:  %s\n argv:", process->path);
316         for (i = 0, arg = process->argv[i]; arg; i++, arg = process->argv[i])
317                 logmsg = talloc_asprintf_append(logmsg, " '%s'", arg);
318
319         pb_log("Running command:\n%s\n", logmsg);
320
321         rc = process_setup_stdout_pipe(procinfo);
322         if (rc)
323                 return rc;
324
325         pid = fork();
326         if (pid < 0) {
327                 pb_log("%s: fork failed: %s\n", __func__, strerror(errno));
328                 return pid;
329         }
330
331         if (pid == 0) {
332                 process_setup_stdout_child(procinfo);
333                 execvp(process->path, (char * const *)process->argv);
334                 exit(EXIT_FAILURE);
335         }
336
337         process_setup_stdout_parent(procinfo);
338         process->pid = pid;
339
340         return 0;
341 }
342
343 int process_run_sync(struct process *process)
344 {
345         struct process_info *procinfo = get_info(process);
346         int rc;
347
348         rc = process_run_common(procinfo);
349         if (rc)
350                 return rc;
351
352         process_read_stdout(procinfo);
353
354         rc = waitpid(process->pid, &process->exit_status, 0);
355         if (rc == -1) {
356                 pb_log("%s: waitpid failed: %s\n", __func__, strerror(errno));
357                 return rc;
358         }
359
360         return 0;
361 }
362
363 int process_run_async(struct process *process)
364 {
365         struct process_info *procinfo = get_info(process);
366         int rc;
367
368         rc = process_run_common(procinfo);
369         if (rc)
370                 return rc;
371
372         if (process->keep_stdout) {
373                 procinfo->stdout_waiter = waiter_register_io(procset->waitset,
374                                                 procinfo->stdout_pipe[0],
375                                                 WAIT_IN, process_stdout_cb,
376                                                 procinfo);
377                 talloc_reference(procset, procinfo);
378         }
379
380         list_add(&procset->async_list, &procinfo->async_list);
381         talloc_reference(procset, procinfo);
382
383         return 0;
384 }
385
386 void process_stop_async(struct process *process)
387 {
388         kill(process->pid, SIGTERM);
389 }
390
391 int process_run_simple_argv(void *ctx, const char *argv[])
392 {
393         struct process *process;
394         int rc;
395
396         process = process_create(ctx);
397
398         process->path = argv[0];
399         process->argv = argv;
400
401         rc = process_run_sync(process);
402
403         if (!rc)
404                 rc = process->exit_status;
405
406         process_release(process);
407
408         return rc;
409 }
410
411 int process_run_simple(void *ctx, const char *name, ...)
412 {
413         int rc, i, n_argv = 1;
414         const char **argv;
415         va_list ap;
416
417         va_start(ap, name);
418         while (va_arg(ap, char *))
419                 n_argv++;
420         va_end(ap);
421
422         argv = talloc_array(ctx, const char *, n_argv + 1);
423         argv[0] = name;
424
425         va_start(ap, name);
426         for (i = 1; i < n_argv; i++)
427                 argv[i] = va_arg(ap, const char *);
428         va_end(ap);
429
430         argv[i] = NULL;
431
432         rc = process_run_simple_argv(ctx, argv);
433
434         talloc_free(argv);
435
436         return rc;
437 }