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