xref: /freebsd/lib/libc/tests/stdlib/libc_exit_test.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
1 /*-
2  * Copyright (c) 2023 Klara, Inc.
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/wait.h>
8 
9 #include <pthread.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 
14 #include <atf-c.h>
15 
16 static void
17 func_a(void)
18 {
19 	if (write(STDOUT_FILENO, "a", 1) != 1)
20 		_Exit(1);
21 }
22 
23 static void
24 func_b(void)
25 {
26 	if (write(STDOUT_FILENO, "b", 1) != 1)
27 		_Exit(1);
28 }
29 
30 static void
31 func_c(void)
32 {
33 	if (write(STDOUT_FILENO, "c", 1) != 1)
34 		_Exit(1);
35 }
36 
37 static void
38 child(void)
39 {
40 	/* this will be received by the parent */
41 	printf("hello, ");
42 	fflush(stdout);
43 	/* this won't, because quick_exit() does not flush */
44 	printf("world");
45 	/* these will be called in reverse order, producing "abc" */
46 	if (at_quick_exit(func_c) != 0 ||
47 	    at_quick_exit(func_b) != 0 ||
48 	    at_quick_exit(func_a) != 0)
49 		_Exit(1);
50 	quick_exit(0);
51 }
52 
53 ATF_TC_WITHOUT_HEAD(quick_exit);
54 ATF_TC_BODY(quick_exit, tc)
55 {
56 	char buf[100] = "";
57 	ssize_t len;
58 	int p[2], wstatus = 0;
59 	pid_t pid;
60 
61 	ATF_REQUIRE(pipe(p) == 0);
62 	pid = fork();
63 	if (pid == 0) {
64 		if (dup2(p[1], STDOUT_FILENO) < 0)
65 			_Exit(1);
66 		(void)close(p[1]);
67 		(void)close(p[0]);
68 		child();
69 		_Exit(1);
70 	}
71 	ATF_REQUIRE_MSG(pid > 0,
72 	    "expect fork() to succeed");
73 	ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0),
74 	    "expect to collect child process");
75 	ATF_CHECK_EQ_MSG(0, wstatus,
76 	    "expect child to exit cleanly");
77 	ATF_CHECK_MSG((len = read(p[0], buf, sizeof(buf))) > 0,
78 	    "expect to receive output from child");
79 	ATF_CHECK_STREQ("hello, abc", buf);
80 }
81 
82 static void
83 myatexit1(void)
84 {
85 	exit(12);
86 }
87 
88 ATF_TC_WITHOUT_HEAD(recursive_exit1);
89 ATF_TC_BODY(recursive_exit1, tc)
90 {
91 	pid_t pid;
92 	int wstatus;
93 
94 	pid = fork();
95 	if (pid == 0) {
96 		atexit(myatexit1);
97 		exit(1);
98 	}
99 	ATF_REQUIRE_MSG(pid > 0,
100 	    "expect fork() to succeed");
101 	ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0),
102 	    "expect to collect child process");
103 	ATF_CHECK(WIFEXITED(wstatus));
104 	ATF_CHECK_EQ(WEXITSTATUS(wstatus), 12);
105 }
106 
107 static pthread_barrier_t barrier;
108 
109 static void
110 myatexit2(void)
111 {
112 	pthread_barrier_wait(&barrier);
113 	exit(12);
114 }
115 
116 static void *
117 mythreadexit(void *arg)
118 {
119 	pthread_barrier_wait(&barrier);
120 	exit(15);
121 }
122 
123 ATF_TC_WITHOUT_HEAD(recursive_exit2);
124 ATF_TC_BODY(recursive_exit2, tc)
125 {
126 	pid_t pid;
127 	int wstatus;
128 
129 	pid = fork();
130 	if (pid == 0) {
131 		pthread_t thr;
132 
133 		atexit(myatexit2);
134 
135 		pthread_barrier_init(&barrier, NULL, 2);
136 		pthread_create(&thr, NULL, mythreadexit, NULL);
137 
138 		exit(1);
139 	}
140 	ATF_REQUIRE_MSG(pid > 0,
141 	    "expect fork() to succeed");
142 	ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0),
143 	    "expect to collect child process");
144 	ATF_CHECK(WIFEXITED(wstatus));
145 	ATF_CHECK_EQ(WEXITSTATUS(wstatus), 12);
146 }
147 
148 ATF_TP_ADD_TCS(tp)
149 {
150 	ATF_TP_ADD_TC(tp, quick_exit);
151 	ATF_TP_ADD_TC(tp, recursive_exit1);
152 	ATF_TP_ADD_TC(tp, recursive_exit2);
153 	return (atf_no_error());
154 }
155