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