process.c revision 275988
1/* Copyright (c) 2007 The NetBSD Foundation, Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25
26#include "atf-c/detail/process.h"
27
28#include <sys/types.h>
29#include <sys/wait.h>
30
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include "atf-c/defs.h"
39#include "atf-c/detail/sanity.h"
40#include "atf-c/error.h"
41
42/* This prototype is not in the header file because this is a private
43 * function; however, we need to access it during testing. */
44atf_error_t atf_process_status_init(atf_process_status_t *, int);
45
46/* ---------------------------------------------------------------------
47 * The "stream_prepare" auxiliary type.
48 * --------------------------------------------------------------------- */
49
50struct stream_prepare {
51    const atf_process_stream_t *m_sb;
52
53    bool m_pipefds_ok;
54    int m_pipefds[2];
55};
56typedef struct stream_prepare stream_prepare_t;
57
58static
59atf_error_t
60stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb)
61{
62    atf_error_t err;
63
64    const int type = atf_process_stream_type(sb);
65
66    sp->m_sb = sb;
67    sp->m_pipefds_ok = false;
68
69    if (type == atf_process_stream_type_capture) {
70        if (pipe(sp->m_pipefds) == -1)
71            err = atf_libc_error(errno, "Failed to create pipe");
72        else {
73            err = atf_no_error();
74            sp->m_pipefds_ok = true;
75        }
76    } else
77        err = atf_no_error();
78
79    return err;
80}
81
82static
83void
84stream_prepare_fini(stream_prepare_t *sp)
85{
86    if (sp->m_pipefds_ok) {
87        close(sp->m_pipefds[0]);
88        close(sp->m_pipefds[1]);
89    }
90}
91
92/* ---------------------------------------------------------------------
93 * The "atf_process_stream" type.
94 * --------------------------------------------------------------------- */
95
96const int atf_process_stream_type_capture = 1;
97const int atf_process_stream_type_connect = 2;
98const int atf_process_stream_type_inherit = 3;
99const int atf_process_stream_type_redirect_fd = 4;
100const int atf_process_stream_type_redirect_path = 5;
101
102static
103bool
104stream_is_valid(const atf_process_stream_t *sb)
105{
106    return (sb->m_type == atf_process_stream_type_capture) ||
107           (sb->m_type == atf_process_stream_type_connect) ||
108           (sb->m_type == atf_process_stream_type_inherit) ||
109           (sb->m_type == atf_process_stream_type_redirect_fd) ||
110           (sb->m_type == atf_process_stream_type_redirect_path);
111}
112
113atf_error_t
114atf_process_stream_init_capture(atf_process_stream_t *sb)
115{
116    sb->m_type = atf_process_stream_type_capture;
117
118    POST(stream_is_valid(sb));
119    return atf_no_error();
120}
121
122atf_error_t
123atf_process_stream_init_connect(atf_process_stream_t *sb,
124                                const int src_fd, const int tgt_fd)
125{
126    PRE(src_fd >= 0);
127    PRE(tgt_fd >= 0);
128    PRE(src_fd != tgt_fd);
129
130    sb->m_type = atf_process_stream_type_connect;
131    sb->m_src_fd = src_fd;
132    sb->m_tgt_fd = tgt_fd;
133
134    POST(stream_is_valid(sb));
135    return atf_no_error();
136}
137
138atf_error_t
139atf_process_stream_init_inherit(atf_process_stream_t *sb)
140{
141    sb->m_type = atf_process_stream_type_inherit;
142
143    POST(stream_is_valid(sb));
144    return atf_no_error();
145}
146
147atf_error_t
148atf_process_stream_init_redirect_fd(atf_process_stream_t *sb,
149                                    const int fd)
150{
151    sb->m_type = atf_process_stream_type_redirect_fd;
152    sb->m_fd = fd;
153
154    POST(stream_is_valid(sb));
155    return atf_no_error();
156}
157
158atf_error_t
159atf_process_stream_init_redirect_path(atf_process_stream_t *sb,
160                                      const atf_fs_path_t *path)
161{
162    sb->m_type = atf_process_stream_type_redirect_path;
163    sb->m_path = path;
164
165    POST(stream_is_valid(sb));
166    return atf_no_error();
167}
168
169void
170atf_process_stream_fini(atf_process_stream_t *sb)
171{
172    PRE(stream_is_valid(sb));
173}
174
175int
176atf_process_stream_type(const atf_process_stream_t *sb)
177{
178    PRE(stream_is_valid(sb));
179
180    return sb->m_type;
181}
182
183/* ---------------------------------------------------------------------
184 * The "atf_process_status" type.
185 * --------------------------------------------------------------------- */
186
187atf_error_t
188atf_process_status_init(atf_process_status_t *s, int status)
189{
190    s->m_status = status;
191
192    return atf_no_error();
193}
194
195void
196atf_process_status_fini(atf_process_status_t *s ATF_DEFS_ATTRIBUTE_UNUSED)
197{
198}
199
200bool
201atf_process_status_exited(const atf_process_status_t *s)
202{
203    int mutable_status = s->m_status;
204    return WIFEXITED(mutable_status);
205}
206
207int
208atf_process_status_exitstatus(const atf_process_status_t *s)
209{
210    PRE(atf_process_status_exited(s));
211    int mutable_status = s->m_status;
212    return WEXITSTATUS(mutable_status);
213}
214
215bool
216atf_process_status_signaled(const atf_process_status_t *s)
217{
218    int mutable_status = s->m_status;
219    return WIFSIGNALED(mutable_status);
220}
221
222int
223atf_process_status_termsig(const atf_process_status_t *s)
224{
225    PRE(atf_process_status_signaled(s));
226    int mutable_status = s->m_status;
227    return WTERMSIG(mutable_status);
228}
229
230bool
231atf_process_status_coredump(const atf_process_status_t *s)
232{
233    PRE(atf_process_status_signaled(s));
234#if defined(WCOREDUMP)
235    int mutable_status = s->m_status;
236    return WCOREDUMP(mutable_status);
237#else
238    return false;
239#endif
240}
241
242/* ---------------------------------------------------------------------
243 * The "atf_process_child" type.
244 * --------------------------------------------------------------------- */
245
246static
247atf_error_t
248atf_process_child_init(atf_process_child_t *c)
249{
250    c->m_pid = 0;
251    c->m_stdout = -1;
252    c->m_stderr = -1;
253
254    return atf_no_error();
255}
256
257static
258void
259atf_process_child_fini(atf_process_child_t *c)
260{
261    if (c->m_stdout != -1)
262        close(c->m_stdout);
263    if (c->m_stderr != -1)
264        close(c->m_stderr);
265}
266
267atf_error_t
268atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s)
269{
270    atf_error_t err;
271    int status;
272
273    if (waitpid(c->m_pid, &status, 0) == -1)
274        err = atf_libc_error(errno, "Failed waiting for process %d",
275                             c->m_pid);
276    else {
277        atf_process_child_fini(c);
278        err = atf_process_status_init(s, status);
279    }
280
281    return err;
282}
283
284pid_t
285atf_process_child_pid(const atf_process_child_t *c)
286{
287    return c->m_pid;
288}
289
290int
291atf_process_child_stdout(atf_process_child_t *c)
292{
293    PRE(c->m_stdout != -1);
294    return c->m_stdout;
295}
296
297int
298atf_process_child_stderr(atf_process_child_t *c)
299{
300    PRE(c->m_stderr != -1);
301    return c->m_stderr;
302}
303
304/* ---------------------------------------------------------------------
305 * Free functions.
306 * --------------------------------------------------------------------- */
307
308static
309atf_error_t
310safe_dup(const int oldfd, const int newfd)
311{
312    atf_error_t err;
313
314    if (oldfd != newfd) {
315        if (dup2(oldfd, newfd) == -1) {
316            err = atf_libc_error(errno, "Could not allocate file descriptor");
317        } else {
318            close(oldfd);
319            err = atf_no_error();
320        }
321    } else
322        err = atf_no_error();
323
324    return err;
325}
326
327static
328atf_error_t
329child_connect(const stream_prepare_t *sp, int procfd)
330{
331    atf_error_t err;
332    const int type = atf_process_stream_type(sp->m_sb);
333
334    if (type == atf_process_stream_type_capture) {
335        close(sp->m_pipefds[0]);
336        err = safe_dup(sp->m_pipefds[1], procfd);
337    } else if (type == atf_process_stream_type_connect) {
338        if (dup2(sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd) == -1)
339            err = atf_libc_error(errno, "Cannot connect descriptor %d to %d",
340                                 sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd);
341        else
342            err = atf_no_error();
343    } else if (type == atf_process_stream_type_inherit) {
344        err = atf_no_error();
345    } else if (type == atf_process_stream_type_redirect_fd) {
346        err = safe_dup(sp->m_sb->m_fd, procfd);
347    } else if (type == atf_process_stream_type_redirect_path) {
348        int aux = open(atf_fs_path_cstring(sp->m_sb->m_path),
349                       O_WRONLY | O_CREAT | O_TRUNC, 0644);
350        if (aux == -1)
351            err = atf_libc_error(errno, "Could not create %s",
352                                 atf_fs_path_cstring(sp->m_sb->m_path));
353        else {
354            err = safe_dup(aux, procfd);
355            if (atf_is_error(err))
356                close(aux);
357        }
358    } else {
359        UNREACHABLE;
360        err = atf_no_error();
361    }
362
363    return err;
364}
365
366static
367void
368parent_connect(const stream_prepare_t *sp, int *fd)
369{
370    const int type = atf_process_stream_type(sp->m_sb);
371
372    if (type == atf_process_stream_type_capture) {
373        close(sp->m_pipefds[1]);
374        *fd = sp->m_pipefds[0];
375    } else if (type == atf_process_stream_type_connect) {
376        /* Do nothing. */
377    } else if (type == atf_process_stream_type_inherit) {
378        /* Do nothing. */
379    } else if (type == atf_process_stream_type_redirect_fd) {
380        /* Do nothing. */
381    } else if (type == atf_process_stream_type_redirect_path) {
382        /* Do nothing. */
383    } else {
384        UNREACHABLE;
385    }
386}
387
388static
389atf_error_t
390do_parent(atf_process_child_t *c,
391          const pid_t pid,
392          const stream_prepare_t *outsp,
393          const stream_prepare_t *errsp)
394{
395    atf_error_t err;
396
397    err = atf_process_child_init(c);
398    if (atf_is_error(err))
399        goto out;
400
401    c->m_pid = pid;
402
403    parent_connect(outsp, &c->m_stdout);
404    parent_connect(errsp, &c->m_stderr);
405
406out:
407    return err;
408}
409
410static
411void
412do_child(void (*)(void *),
413         void *,
414         const stream_prepare_t *,
415         const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
416
417static
418void
419do_child(void (*start)(void *),
420         void *v,
421         const stream_prepare_t *outsp,
422         const stream_prepare_t *errsp)
423{
424    atf_error_t err;
425
426    err = child_connect(outsp, STDOUT_FILENO);
427    if (atf_is_error(err))
428        goto out;
429
430    err = child_connect(errsp, STDERR_FILENO);
431    if (atf_is_error(err))
432        goto out;
433
434    start(v);
435    UNREACHABLE;
436
437out:
438    if (atf_is_error(err)) {
439        char buf[1024];
440
441        atf_error_format(err, buf, sizeof(buf));
442        fprintf(stderr, "Unhandled error: %s\n", buf);
443        atf_error_free(err);
444
445        exit(EXIT_FAILURE);
446    } else
447        exit(EXIT_SUCCESS);
448}
449
450static
451atf_error_t
452fork_with_streams(atf_process_child_t *c,
453                  void (*start)(void *),
454                  const atf_process_stream_t *outsb,
455                  const atf_process_stream_t *errsb,
456                  void *v)
457{
458    atf_error_t err;
459    stream_prepare_t outsp;
460    stream_prepare_t errsp;
461    pid_t pid;
462
463    err = stream_prepare_init(&outsp, outsb);
464    if (atf_is_error(err))
465        goto out;
466
467    err = stream_prepare_init(&errsp, errsb);
468    if (atf_is_error(err))
469        goto err_outpipe;
470
471    pid = fork();
472    if (pid == -1) {
473        err = atf_libc_error(errno, "Failed to fork");
474        goto err_errpipe;
475    }
476
477    if (pid == 0) {
478        do_child(start, v, &outsp, &errsp);
479        UNREACHABLE;
480        abort();
481        err = atf_no_error();
482    } else {
483        err = do_parent(c, pid, &outsp, &errsp);
484        if (atf_is_error(err))
485            goto err_errpipe;
486    }
487
488    goto out;
489
490err_errpipe:
491    stream_prepare_fini(&errsp);
492err_outpipe:
493    stream_prepare_fini(&outsp);
494
495out:
496    return err;
497}
498
499static
500atf_error_t
501init_stream_w_default(const atf_process_stream_t *usersb,
502                      atf_process_stream_t *inheritsb,
503                      const atf_process_stream_t **realsb)
504{
505    atf_error_t err;
506
507    if (usersb == NULL) {
508        err = atf_process_stream_init_inherit(inheritsb);
509        if (!atf_is_error(err))
510            *realsb = inheritsb;
511    } else {
512        err = atf_no_error();
513        *realsb = usersb;
514    }
515
516    return err;
517}
518
519atf_error_t
520atf_process_fork(atf_process_child_t *c,
521                 void (*start)(void *),
522                 const atf_process_stream_t *outsb,
523                 const atf_process_stream_t *errsb,
524                 void *v)
525{
526    atf_error_t err;
527    atf_process_stream_t inherit_outsb, inherit_errsb;
528    const atf_process_stream_t *real_outsb, *real_errsb;
529
530    real_outsb = NULL;  /* Shut up GCC warning. */
531    err = init_stream_w_default(outsb, &inherit_outsb, &real_outsb);
532    if (atf_is_error(err))
533        goto out;
534
535    real_errsb = NULL;  /* Shut up GCC warning. */
536    err = init_stream_w_default(errsb, &inherit_errsb, &real_errsb);
537    if (atf_is_error(err))
538        goto out_out;
539
540    err = fork_with_streams(c, start, real_outsb, real_errsb, v);
541
542    if (errsb == NULL)
543        atf_process_stream_fini(&inherit_errsb);
544out_out:
545    if (outsb == NULL)
546        atf_process_stream_fini(&inherit_outsb);
547out:
548    return err;
549}
550
551static
552int
553const_execvp(const char *file, const char *const *argv)
554{
555#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
556    return execvp(file, UNCONST(argv));
557#undef UNCONST
558}
559
560static
561atf_error_t
562list_to_array(const atf_list_t *l, const char ***ap)
563{
564    atf_error_t err;
565    const char **a;
566
567    a = (const char **)malloc((atf_list_size(l) + 1) * sizeof(const char *));
568    if (a == NULL)
569        err = atf_no_memory_error();
570    else {
571        const char **aiter;
572        atf_list_citer_t liter;
573
574        aiter = a;
575        atf_list_for_each_c(liter, l) {
576            *aiter = (const char *)atf_list_citer_data(liter);
577            aiter++;
578        }
579        *aiter = NULL;
580
581        err = atf_no_error();
582        *ap = a;
583    }
584
585    return err;
586}
587
588struct exec_args {
589    const atf_fs_path_t *m_prog;
590    const char *const *m_argv;
591    void (*m_prehook)(void);
592};
593
594static
595void
596do_exec(void *v)
597{
598    struct exec_args *ea = v;
599
600    if (ea->m_prehook != NULL)
601        ea->m_prehook();
602
603    const int ret = const_execvp(atf_fs_path_cstring(ea->m_prog), ea->m_argv);
604    const int errnocopy = errno;
605    INV(ret == -1);
606    fprintf(stderr, "exec(%s) failed: %s\n",
607            atf_fs_path_cstring(ea->m_prog), strerror(errnocopy));
608    exit(EXIT_FAILURE);
609}
610
611atf_error_t
612atf_process_exec_array(atf_process_status_t *s,
613                       const atf_fs_path_t *prog,
614                       const char *const *argv,
615                       const atf_process_stream_t *outsb,
616                       const atf_process_stream_t *errsb,
617                       void (*prehook)(void))
618{
619    atf_error_t err;
620    atf_process_child_t c;
621    struct exec_args ea = { prog, argv, prehook };
622
623    PRE(outsb == NULL ||
624        atf_process_stream_type(outsb) != atf_process_stream_type_capture);
625    PRE(errsb == NULL ||
626        atf_process_stream_type(errsb) != atf_process_stream_type_capture);
627
628    err = atf_process_fork(&c, do_exec, outsb, errsb, &ea);
629    if (atf_is_error(err))
630        goto out;
631
632again:
633    err = atf_process_child_wait(&c, s);
634    if (atf_is_error(err)) {
635        INV(atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR);
636        atf_error_free(err);
637        goto again;
638    }
639
640out:
641    return err;
642}
643
644atf_error_t
645atf_process_exec_list(atf_process_status_t *s,
646                      const atf_fs_path_t *prog,
647                      const atf_list_t *argv,
648                      const atf_process_stream_t *outsb,
649                      const atf_process_stream_t *errsb,
650                      void (*prehook)(void))
651{
652    atf_error_t err;
653    const char **argv2;
654
655    PRE(outsb == NULL ||
656        atf_process_stream_type(outsb) != atf_process_stream_type_capture);
657    PRE(errsb == NULL ||
658        atf_process_stream_type(errsb) != atf_process_stream_type_capture);
659
660    argv2 = NULL; /* Silence GCC warning. */
661    err = list_to_array(argv, &argv2);
662    if (atf_is_error(err))
663        goto out;
664
665    err = atf_process_exec_array(s, prog, argv2, outsb, errsb, prehook);
666
667    free(argv2);
668out:
669    return err;
670}
671