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