tc.c revision 1.1.1.1 1 /*
2 * Automated Testing Framework (atf)
3 *
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include "atf-c/config.h"
46 #include "atf-c/env.h"
47 #include "atf-c/error.h"
48 #include "atf-c/fs.h"
49 #include "atf-c/process.h"
50 #include "atf-c/sanity.h"
51 #include "atf-c/signals.h"
52 #include "atf-c/tc.h"
53 #include "atf-c/tcr.h"
54 #include "atf-c/text.h"
55 #include "atf-c/user.h"
56
57 /* ---------------------------------------------------------------------
58 * Auxiliary types and functions.
59 * --------------------------------------------------------------------- */
60
61 /* Parent-only stuff. */
62 struct timeout_data;
63 static atf_error_t body_parent(const atf_tc_t *, const atf_fs_path_t *,
64 pid_t, atf_tcr_t *);
65 static atf_error_t cleanup_parent(const atf_tc_t *, pid_t);
66 static atf_error_t fork_body(const atf_tc_t *, const atf_fs_path_t *,
67 atf_tcr_t *);
68 static atf_error_t fork_cleanup(const atf_tc_t *, const atf_fs_path_t *);
69 static atf_error_t get_tc_result(const atf_fs_path_t *, atf_tcr_t *);
70 static atf_error_t program_timeout(pid_t, const atf_tc_t *,
71 struct timeout_data *);
72 static void unprogram_timeout(struct timeout_data *);
73 static void sigalrm_handler(int);
74
75 /* Child-only stuff. */
76 static void body_child(const atf_tc_t *, const atf_fs_path_t *)
77 ATF_DEFS_ATTRIBUTE_NORETURN;
78 static atf_error_t check_arch(const char *, void *);
79 static atf_error_t check_config(const char *, void *);
80 static atf_error_t check_machine(const char *, void *);
81 static atf_error_t check_prog(const char *, void *);
82 static atf_error_t check_prog_in_dir(const char *, void *);
83 static atf_error_t check_requirements(const atf_tc_t *);
84 static void cleanup_child(const atf_tc_t *, const atf_fs_path_t *)
85 ATF_DEFS_ATTRIBUTE_NORETURN;
86 static void fail_internal(const char *, int, const char *, const char *,
87 const char *, va_list,
88 void (*)(const char *, ...));
89 static void fatal_atf_error(const char *, atf_error_t)
90 ATF_DEFS_ATTRIBUTE_NORETURN;
91 static void fatal_libc_error(const char *, int)
92 ATF_DEFS_ATTRIBUTE_NORETURN;
93 static atf_error_t prepare_child(const atf_tc_t *, const atf_fs_path_t *);
94 static void write_tcr(const atf_tcr_t *);
95
96 /* ---------------------------------------------------------------------
97 * The "atf_tc" type.
98 * --------------------------------------------------------------------- */
99
100 /*
101 * Constructors/destructors.
102 */
103
104 atf_error_t
105 atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head,
106 atf_tc_body_t body, atf_tc_cleanup_t cleanup,
107 const atf_map_t *config)
108 {
109 atf_error_t err;
110
111 atf_object_init(&tc->m_object);
112
113 tc->m_ident = ident;
114 tc->m_head = head;
115 tc->m_body = body;
116 tc->m_cleanup = cleanup;
117 tc->m_config = config;
118
119 err = atf_map_init(&tc->m_vars);
120 if (atf_is_error(err))
121 goto err_object;
122
123 err = atf_tc_set_md_var(tc, "ident", ident);
124 if (atf_is_error(err))
125 goto err_map;
126
127 err = atf_tc_set_md_var(tc, "timeout", "300");
128 if (atf_is_error(err))
129 goto err_map;
130
131 /* XXX Should the head be able to return error codes? */
132 tc->m_head(tc);
133
134 if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0)
135 atf_tc_fail("Test case head modified the read-only 'ident' "
136 "property");
137
138 INV(!atf_is_error(err));
139 return err;
140
141 err_map:
142 atf_map_fini(&tc->m_vars);
143 err_object:
144 atf_object_fini(&tc->m_object);
145
146 return err;
147 }
148
149 atf_error_t
150 atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack,
151 const atf_map_t *config)
152 {
153 return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body,
154 pack->m_cleanup, config);
155 }
156
157 void
158 atf_tc_fini(atf_tc_t *tc)
159 {
160 atf_map_fini(&tc->m_vars);
161
162 atf_object_fini(&tc->m_object);
163 }
164
165 /*
166 * Getters.
167 */
168
169 const char *
170 atf_tc_get_ident(const atf_tc_t *tc)
171 {
172 return tc->m_ident;
173 }
174
175 const char *
176 atf_tc_get_config_var(const atf_tc_t *tc, const char *name)
177 {
178 const char *val;
179 atf_map_citer_t iter;
180
181 PRE(atf_tc_has_config_var(tc, name));
182 iter = atf_map_find_c(tc->m_config, name);
183 val = atf_map_citer_data(iter);
184 INV(val != NULL);
185
186 return val;
187 }
188
189 const char *
190 atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name,
191 const char *defval)
192 {
193 const char *val;
194
195 if (!atf_tc_has_config_var(tc, name))
196 val = defval;
197 else
198 val = atf_tc_get_config_var(tc, name);
199
200 return val;
201 }
202
203 const char *
204 atf_tc_get_md_var(const atf_tc_t *tc, const char *name)
205 {
206 const char *val;
207 atf_map_citer_t iter;
208
209 PRE(atf_tc_has_md_var(tc, name));
210 iter = atf_map_find_c(&tc->m_vars, name);
211 val = atf_map_citer_data(iter);
212 INV(val != NULL);
213
214 return val;
215 }
216
217 bool
218 atf_tc_has_config_var(const atf_tc_t *tc, const char *name)
219 {
220 bool found;
221 atf_map_citer_t end, iter;
222
223 if (tc->m_config == NULL)
224 found = false;
225 else {
226 iter = atf_map_find_c(tc->m_config, name);
227 end = atf_map_end_c(tc->m_config);
228 found = !atf_equal_map_citer_map_citer(iter, end);
229 }
230
231 return found;
232 }
233
234 bool
235 atf_tc_has_md_var(const atf_tc_t *tc, const char *name)
236 {
237 atf_map_citer_t end, iter;
238
239 iter = atf_map_find_c(&tc->m_vars, name);
240 end = atf_map_end_c(&tc->m_vars);
241 return !atf_equal_map_citer_map_citer(iter, end);
242 }
243
244 /*
245 * Modifiers.
246 */
247
248 atf_error_t
249 atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...)
250 {
251 atf_error_t err;
252 char *value;
253 va_list ap;
254
255 va_start(ap, fmt);
256 err = atf_text_format_ap(&value, fmt, ap);
257 va_end(ap);
258
259 if (!atf_is_error(err))
260 err = atf_map_insert(&tc->m_vars, name, value, true);
261 else
262 free(value);
263
264 return err;
265 }
266
267 /* ---------------------------------------------------------------------
268 * Free functions.
269 * --------------------------------------------------------------------- */
270
271 atf_error_t
272 atf_tc_run(const atf_tc_t *tc, atf_tcr_t *tcr,
273 const atf_fs_path_t *workdirbase)
274 {
275 atf_error_t err, cleanuperr;
276 atf_fs_path_t workdir;
277
278 err = atf_fs_path_copy(&workdir, workdirbase);
279 if (atf_is_error(err))
280 goto out;
281
282 err = atf_fs_path_append_fmt(&workdir, "atf.XXXXXX");
283 if (atf_is_error(err))
284 goto out_workdir;
285
286 err = atf_fs_mkdtemp(&workdir);
287 if (atf_is_error(err))
288 goto out_workdir;
289
290 err = fork_body(tc, &workdir, tcr);
291 cleanuperr = fork_cleanup(tc, &workdir);
292 if (!atf_is_error(cleanuperr))
293 (void)atf_fs_cleanup(&workdir);
294 if (!atf_is_error(err))
295 err = cleanuperr;
296 else if (atf_is_error(cleanuperr))
297 atf_error_free(cleanuperr);
298
299 out_workdir:
300 atf_fs_path_fini(&workdir);
301 out:
302 return err;
303 }
304
305 /*
306 * Parent-only stuff.
307 */
308
309 static bool sigalrm_killed = false;
310 static pid_t sigalrm_pid = -1;
311
312 static
313 void
314 sigalrm_handler(int signo)
315 {
316 INV(signo == SIGALRM);
317
318 if (sigalrm_pid != -1) {
319 killpg(sigalrm_pid, SIGTERM);
320 sigalrm_killed = true;
321 }
322 }
323
324 struct timeout_data {
325 bool m_programmed;
326 atf_signal_programmer_t m_sigalrm;
327 };
328
329 static
330 atf_error_t
331 program_timeout(pid_t pid, const atf_tc_t *tc, struct timeout_data *td)
332 {
333 atf_error_t err;
334 long timeout;
335
336 err = atf_text_to_long(atf_tc_get_md_var(tc, "timeout"), &timeout);
337 if (atf_is_error(err))
338 goto out;
339
340 if (timeout != 0) {
341 sigalrm_pid = pid;
342 sigalrm_killed = false;
343
344 err = atf_signal_programmer_init(&td->m_sigalrm, SIGALRM,
345 sigalrm_handler);
346 if (atf_is_error(err))
347 goto out;
348
349 struct itimerval itv;
350 timerclear(&itv.it_interval);
351 timerclear(&itv.it_value);
352 itv.it_value.tv_sec = timeout;
353 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
354 atf_signal_programmer_fini(&td->m_sigalrm);
355 err = atf_libc_error(errno, "Failed to program timeout "
356 "with %ld seconds", timeout);
357 }
358
359 td->m_programmed = !atf_is_error(err);
360 } else
361 td->m_programmed = false;
362
363 out:
364 return err;
365 }
366
367 static
368 void
369 unprogram_timeout(struct timeout_data *td)
370 {
371 if (td->m_programmed) {
372 atf_signal_programmer_fini(&td->m_sigalrm);
373 sigalrm_pid = -1;
374 sigalrm_killed = false;
375 }
376 }
377
378 static
379 atf_error_t
380 body_parent(const atf_tc_t *tc, const atf_fs_path_t *workdir, pid_t pid,
381 atf_tcr_t *tcr)
382 {
383 atf_error_t err;
384 int state;
385 struct timeout_data td;
386
387 err = program_timeout(pid, tc, &td);
388 if (atf_is_error(err)) {
389 char buf[4096];
390
391 atf_error_format(err, buf, sizeof(buf));
392 fprintf(stderr, "Error programming test case's timeout: %s", buf);
393 atf_error_free(err);
394 killpg(pid, SIGKILL);
395 }
396
397 if (waitpid(pid, &state, 0) == -1) {
398 if (errno == EINTR && sigalrm_killed)
399 err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
400 "Test case timed out after %s "
401 "seconds",
402 atf_tc_get_md_var(tc, "timeout"));
403 else
404 err = atf_libc_error(errno, "Error waiting for child process "
405 "%d", pid);
406 } else {
407 if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS)
408 err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
409 "Test case did not exit cleanly; "
410 "state was %d", state);
411 else
412 err = get_tc_result(workdir, tcr);
413 }
414
415 unprogram_timeout(&td);
416
417 return err;
418 }
419
420 static
421 atf_error_t
422 cleanup_parent(const atf_tc_t *tc, pid_t pid)
423 {
424 atf_error_t err;
425 int state;
426
427 if (waitpid(pid, &state, 0) == -1) {
428 err = atf_libc_error(errno, "Error waiting for child process "
429 "%d", pid);
430 goto out;
431 }
432
433 if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS)
434 /* XXX Not really a libc error. */
435 err = atf_libc_error(EINVAL, "Child process did not exit cleanly");
436 else
437 err = atf_no_error();
438
439 out:
440 return err;
441 }
442
443 static
444 atf_error_t
445 fork_body(const atf_tc_t *tc, const atf_fs_path_t *workdir, atf_tcr_t *tcr)
446 {
447 atf_error_t err;
448 pid_t pid;
449
450 err = atf_process_fork(&pid);
451 if (atf_is_error(err))
452 goto out;
453
454 if (pid == 0) {
455 body_child(tc, workdir);
456 UNREACHABLE;
457 abort();
458 } else {
459 err = body_parent(tc, workdir, pid, tcr);
460 }
461
462 out:
463 return err;
464 }
465
466 static
467 atf_error_t
468 fork_cleanup(const atf_tc_t *tc, const atf_fs_path_t *workdir)
469 {
470 atf_error_t err;
471 pid_t pid;
472
473 if (tc->m_cleanup == NULL)
474 err = atf_no_error();
475 else {
476 err = atf_process_fork(&pid);
477 if (atf_is_error(err))
478 goto out;
479
480 if (pid == 0) {
481 cleanup_child(tc, workdir);
482 UNREACHABLE;
483 abort();
484 } else {
485 err = cleanup_parent(tc, pid);
486 }
487 }
488
489 out:
490 return err;
491 }
492
493 static
494 atf_error_t
495 get_tc_result(const atf_fs_path_t *workdir, atf_tcr_t *tcr)
496 {
497 atf_error_t err;
498 int fd;
499 atf_fs_path_t tcrfile;
500
501 err = atf_fs_path_copy(&tcrfile, workdir);
502 if (atf_is_error(err))
503 goto out;
504
505 err = atf_fs_path_append_fmt(&tcrfile, "tc-result");
506 if (atf_is_error(err))
507 goto out_tcrfile;
508
509 fd = open(atf_fs_path_cstring(&tcrfile), O_RDONLY);
510 if (fd == -1) {
511 err = atf_libc_error(errno, "Cannot retrieve test case result");
512 goto out_tcrfile;
513 }
514
515 err = atf_tcr_deserialize(tcr, fd);
516
517 close(fd);
518 out_tcrfile:
519 atf_fs_path_fini(&tcrfile);
520 out:
521 return err;
522 }
523
524 /*
525 * Child-only stuff.
526 */
527
528 static const atf_tc_t *current_tc = NULL;
529 static const atf_fs_path_t *current_workdir = NULL;
530 static size_t current_tc_fail_count = 0;
531
532 static
533 atf_error_t
534 prepare_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
535 {
536 atf_error_t err;
537 int i, ret;
538
539 current_tc = tc;
540 current_workdir = workdir;
541 current_tc_fail_count = 0;
542
543 ret = setpgid(getpid(), 0);
544 INV(ret != -1);
545
546 umask(S_IWGRP | S_IWOTH);
547
548 for (i = 1; i <= atf_signals_last_signo; i++)
549 atf_signal_reset(i);
550
551 err = atf_env_set("HOME", atf_fs_path_cstring(workdir));
552 if (atf_is_error(err))
553 goto out;
554
555 err = atf_env_unset("LANG");
556 if (atf_is_error(err))
557 goto out;
558
559 err = atf_env_unset("LC_ALL");
560 if (atf_is_error(err))
561 goto out;
562
563 err = atf_env_unset("LC_COLLATE");
564 if (atf_is_error(err))
565 goto out;
566
567 err = atf_env_unset("LC_CTYPE");
568 if (atf_is_error(err))
569 goto out;
570
571 err = atf_env_unset("LC_MESSAGES");
572 if (atf_is_error(err))
573 goto out;
574
575 err = atf_env_unset("LC_MONETARY");
576 if (atf_is_error(err))
577 goto out;
578
579 err = atf_env_unset("LC_NUMERIC");
580 if (atf_is_error(err))
581 goto out;
582
583 err = atf_env_unset("LC_TIME");
584 if (atf_is_error(err))
585 goto out;
586
587 err = atf_env_unset("TZ");
588 if (atf_is_error(err))
589 goto out;
590
591 if (chdir(atf_fs_path_cstring(workdir)) == -1) {
592 err = atf_libc_error(errno, "Cannot enter work directory '%s'",
593 atf_fs_path_cstring(workdir));
594 goto out;
595 }
596
597 err = atf_no_error();
598
599 out:
600 return err;
601 }
602
603 static
604 void
605 body_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
606 {
607 atf_error_t err;
608
609 atf_disable_exit_checks();
610
611 err = prepare_child(tc, workdir);
612 if (atf_is_error(err))
613 goto print_err;
614 err = check_requirements(tc);
615 if (atf_is_error(err))
616 goto print_err;
617 tc->m_body(tc);
618
619 if (current_tc_fail_count == 0)
620 atf_tc_pass();
621 else
622 atf_tc_fail("%d checks failed; see output for more details",
623 current_tc_fail_count);
624
625 UNREACHABLE;
626 abort();
627
628 print_err:
629 INV(atf_is_error(err));
630 {
631 char buf[4096];
632
633 atf_error_format(err, buf, sizeof(buf));
634 atf_error_free(err);
635
636 atf_tc_fail("Error while preparing child process: %s", buf);
637 }
638
639 UNREACHABLE;
640 abort();
641 }
642
643 static
644 atf_error_t
645 check_arch(const char *arch, void *data)
646 {
647 bool *found = data;
648
649 if (strcmp(arch, atf_config_get("atf_arch")) == 0)
650 *found = true;
651
652 return atf_no_error();
653 }
654
655 static
656 atf_error_t
657 check_config(const char *var, void *data)
658 {
659 if (!atf_tc_has_config_var(current_tc, var))
660 atf_tc_skip("Required configuration variable %s not defined", var);
661
662 return atf_no_error();
663 }
664
665 static
666 atf_error_t
667 check_machine(const char *machine, void *data)
668 {
669 bool *found = data;
670
671 if (strcmp(machine, atf_config_get("atf_machine")) == 0)
672 *found = true;
673
674 return atf_no_error();
675 }
676
677 struct prog_found_pair {
678 const char *prog;
679 bool found;
680 };
681
682 static
683 atf_error_t
684 check_prog(const char *prog, void *data)
685 {
686 atf_error_t err;
687 atf_fs_path_t p;
688
689 err = atf_fs_path_init_fmt(&p, "%s", prog);
690 if (atf_is_error(err))
691 goto out;
692
693 if (atf_fs_path_is_absolute(&p)) {
694 if (atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x)))
695 atf_tc_skip("The required program %s could not be found", prog);
696 } else {
697 const char *path = atf_env_get("PATH");
698 struct prog_found_pair pf;
699 atf_fs_path_t bp;
700
701 err = atf_fs_path_branch_path(&p, &bp);
702 if (atf_is_error(err))
703 goto out_p;
704
705 if (strcmp(atf_fs_path_cstring(&bp), ".") != 0)
706 atf_tc_fail("Relative paths are not allowed when searching for "
707 "a program (%s)", prog);
708
709 pf.prog = prog;
710 pf.found = false;
711 err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
712 if (atf_is_error(err))
713 goto out_bp;
714
715 if (!pf.found)
716 atf_tc_skip("The required program %s could not be found in "
717 "the PATH", prog);
718
719 out_bp:
720 atf_fs_path_fini(&bp);
721 }
722
723 out_p:
724 atf_fs_path_fini(&p);
725 out:
726 return err;
727 }
728
729 static
730 atf_error_t
731 check_prog_in_dir(const char *dir, void *data)
732 {
733 struct prog_found_pair *pf = data;
734 atf_error_t err;
735
736 if (pf->found)
737 err = atf_no_error();
738 else {
739 atf_fs_path_t p;
740
741 err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
742 if (atf_is_error(err))
743 goto out_p;
744
745 if (!atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x)))
746 pf->found = true;
747
748 out_p:
749 atf_fs_path_fini(&p);
750 }
751
752 return err;
753 }
754
755 static
756 atf_error_t
757 check_requirements(const atf_tc_t *tc)
758 {
759 atf_error_t err;
760
761 err = atf_no_error();
762
763 if (atf_tc_has_md_var(tc, "require.arch")) {
764 const char *arches = atf_tc_get_md_var(tc, "require.arch");
765 bool found = false;
766
767 if (strlen(arches) == 0)
768 atf_tc_fail("Invalid value in the require.arch property");
769 else {
770 err = atf_text_for_each_word(arches, " ", check_arch, &found);
771 if (atf_is_error(err))
772 goto out;
773
774 if (!found)
775 atf_tc_skip("Requires one of the '%s' architectures",
776 arches);
777 }
778 }
779
780 if (atf_tc_has_md_var(tc, "require.config")) {
781 const char *vars = atf_tc_get_md_var(tc, "require.config");
782
783 if (strlen(vars) == 0)
784 atf_tc_fail("Invalid value in the require.config property");
785 else {
786 err = atf_text_for_each_word(vars, " ", check_config, NULL);
787 if (atf_is_error(err))
788 goto out;
789 }
790 }
791
792 if (atf_tc_has_md_var(tc, "require.machine")) {
793 const char *machines = atf_tc_get_md_var(tc, "require.machine");
794 bool found = false;
795
796 if (strlen(machines) == 0)
797 atf_tc_fail("Invalid value in the require.machine property");
798 else {
799 err = atf_text_for_each_word(machines, " ", check_machine,
800 &found);
801 if (atf_is_error(err))
802 goto out;
803
804 if (!found)
805 atf_tc_skip("Requires one of the '%s' machine types",
806 machines);
807 }
808 }
809
810 if (atf_tc_has_md_var(tc, "require.progs")) {
811 const char *progs = atf_tc_get_md_var(tc, "require.progs");
812
813 if (strlen(progs) == 0)
814 atf_tc_fail("Invalid value in the require.progs property");
815 else {
816 err = atf_text_for_each_word(progs, " ", check_prog, NULL);
817 if (atf_is_error(err))
818 goto out;
819 }
820 }
821
822 if (atf_tc_has_md_var(tc, "require.user")) {
823 const char *u = atf_tc_get_md_var(tc, "require.user");
824
825 if (strcmp(u, "root") == 0) {
826 if (!atf_user_is_root())
827 atf_tc_skip("Requires root privileges");
828 } else if (strcmp(u, "unprivileged") == 0) {
829 if (atf_user_is_root())
830 atf_tc_skip("Requires an unprivileged user");
831 } else
832 atf_tc_fail("Invalid value in the require.user property");
833 }
834
835 INV(!atf_is_error(err));
836 out:
837 return err;
838 }
839
840 static
841 void
842 cleanup_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
843 {
844 atf_error_t err;
845
846 atf_disable_exit_checks();
847
848 err = prepare_child(tc, workdir);
849 if (atf_is_error(err))
850 exit(EXIT_FAILURE);
851 else {
852 tc->m_cleanup(tc);
853 exit(EXIT_SUCCESS);
854 }
855
856 UNREACHABLE;
857 abort();
858 }
859
860 static
861 void
862 fatal_atf_error(const char *prefix, atf_error_t err)
863 {
864 char buf[1024];
865
866 INV(atf_is_error(err));
867
868 atf_error_format(err, buf, sizeof(buf));
869 atf_error_free(err);
870
871 fprintf(stderr, "%s: %s", prefix, buf);
872
873 abort();
874 }
875
876 static
877 void
878 fatal_libc_error(const char *prefix, int err)
879 {
880 fprintf(stderr, "%s: %s", prefix, strerror(err));
881
882 abort();
883 }
884
885 static
886 void
887 write_tcr(const atf_tcr_t *tcr)
888 {
889 atf_error_t err;
890 int fd;
891 atf_fs_path_t tcrfile;
892
893 err = atf_fs_path_copy(&tcrfile, current_workdir);
894 if (atf_is_error(err))
895 fatal_atf_error("Cannot write test case results", err);
896
897 err = atf_fs_path_append_fmt(&tcrfile, "tc-result");
898 if (atf_is_error(err))
899 fatal_atf_error("Cannot write test case results", err);
900
901 fd = open(atf_fs_path_cstring(&tcrfile),
902 O_WRONLY | O_CREAT | O_TRUNC, 0755);
903 if (fd == -1)
904 fatal_libc_error("Cannot write test case results", errno);
905
906 err = atf_tcr_serialize(tcr, fd);
907 if (atf_is_error(err))
908 fatal_atf_error("Cannot write test case results", err);
909
910 close(fd);
911 }
912
913 void
914 atf_tc_fail(const char *fmt, ...)
915 {
916 va_list ap;
917 atf_tcr_t tcr;
918 atf_error_t err;
919
920 PRE(current_tc != NULL);
921
922 va_start(ap, fmt);
923 err = atf_tcr_init_reason_ap(&tcr, atf_tcr_failed_state, fmt, ap);
924 va_end(ap);
925 if (atf_is_error(err))
926 abort();
927
928 write_tcr(&tcr);
929
930 atf_tcr_fini(&tcr);
931
932 exit(EXIT_SUCCESS);
933 }
934
935 void
936 atf_tc_fail_nonfatal(const char *fmt, ...)
937 {
938 va_list ap;
939
940 va_start(ap, fmt);
941 vfprintf(stderr, fmt, ap);
942 va_end(ap);
943 fprintf(stderr, "\n");
944
945 current_tc_fail_count++;
946 }
947
948 void
949 atf_tc_fail_check(const char *file, int line, const char *fmt, ...)
950 {
951 va_list ap;
952
953 va_start(ap, fmt);
954 fail_internal(file, line, "Check failed", "*** ", fmt, ap,
955 atf_tc_fail_nonfatal);
956 va_end(ap);
957 }
958
959 void
960 atf_tc_fail_requirement(const char *file, int line, const char *fmt, ...)
961 {
962 va_list ap;
963
964 va_start(ap, fmt);
965 fail_internal(file, line, "Requirement failed", "", fmt, ap,
966 atf_tc_fail);
967 va_end(ap);
968
969 UNREACHABLE;
970 abort();
971 }
972
973 static
974 void
975 fail_internal(const char *file, int line, const char *reason,
976 const char *prefix, const char *fmt, va_list ap,
977 void (*failfunc)(const char *, ...))
978 {
979 va_list ap2;
980 atf_error_t err;
981 atf_dynstr_t msg;
982
983 err = atf_dynstr_init_fmt(&msg, "%s%s:%d: %s: ", prefix, file, line,
984 reason);
985 if (atf_is_error(err))
986 goto backup;
987
988 va_copy(ap2, ap);
989 err = atf_dynstr_append_ap(&msg, fmt, ap2);
990 va_end(ap2);
991 if (atf_is_error(err)) {
992 atf_dynstr_fini(&msg);
993 goto backup;
994 }
995
996 va_copy(ap2, ap);
997 failfunc("%s", atf_dynstr_cstring(&msg));
998 atf_dynstr_fini(&msg);
999 return;
1000
1001 backup:
1002 va_copy(ap2, ap);
1003 failfunc(fmt, ap2);
1004 va_end(ap2);
1005 }
1006
1007 void
1008 atf_tc_pass(void)
1009 {
1010 atf_tcr_t tcr;
1011 atf_error_t err;
1012
1013 PRE(current_tc != NULL);
1014
1015 err = atf_tcr_init(&tcr, atf_tcr_passed_state);
1016 if (atf_is_error(err))
1017 abort();
1018
1019 write_tcr(&tcr);
1020
1021 atf_tcr_fini(&tcr);
1022
1023 exit(EXIT_SUCCESS);
1024 }
1025
1026 void
1027 atf_tc_require_prog(const char *prog)
1028 {
1029 atf_error_t err;
1030
1031 err = check_prog(prog, NULL);
1032 if (atf_is_error(err))
1033 atf_tc_fail("atf_tc_require_prog failed"); /* XXX Correct? */
1034 }
1035
1036 void
1037 atf_tc_skip(const char *fmt, ...)
1038 {
1039 va_list ap;
1040 atf_tcr_t tcr;
1041 atf_error_t err;
1042
1043 PRE(current_tc != NULL);
1044
1045 va_start(ap, fmt);
1046 err = atf_tcr_init_reason_ap(&tcr, atf_tcr_skipped_state, fmt, ap);
1047 va_end(ap);
1048 if (atf_is_error(err))
1049 abort();
1050
1051 write_tcr(&tcr);
1052
1053 atf_tcr_fini(&tcr);
1054
1055 exit(EXIT_SUCCESS);
1056 }
1057