xref: /freebsd/lib/libc/tests/stdlib/system_test.c (revision 7a1ade5109ac57d1f59eaa75b5d0f13fabecf6ba)
1 /*-
2  * Copyright (c) 2026 Klara, Inc.
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/wait.h>
8 
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <pthread.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 
17 #include <atf-c.h>
18 
19 ATF_TC(system_true);
20 ATF_TC_HEAD(system_true, tc)
21 {
22 	atf_tc_set_md_var(tc, "descr", "system(\"true\")");
23 }
24 ATF_TC_BODY(system_true, tc)
25 {
26 	ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("true"));
27 }
28 
29 ATF_TC(system_false);
30 ATF_TC_HEAD(system_false, tc)
31 {
32 	atf_tc_set_md_var(tc, "descr", "system(\"false\")");
33 }
34 ATF_TC_BODY(system_false, tc)
35 {
36 	ATF_REQUIRE_EQ(W_EXITCODE(1, 0), system("false"));
37 }
38 
39 ATF_TC(system_touch);
40 ATF_TC_HEAD(system_touch, tc)
41 {
42 	atf_tc_set_md_var(tc, "descr", "system(\"touch file\")");
43 }
44 ATF_TC_BODY(system_touch, tc)
45 {
46 	/* The file does not exist */
47 	ATF_CHECK_ERRNO(ENOENT, unlink("file"));
48 
49 	/* Run a command that creates it */
50 	ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("touch file"));
51 
52 	/* Now the file exists */
53 	ATF_CHECK_EQ(0, unlink("file"));
54 }
55 
56 ATF_TC(system_null);
57 ATF_TC_HEAD(system_null, tc)
58 {
59 	atf_tc_set_md_var(tc, "descr", "system(NULL)");
60 }
61 ATF_TC_BODY(system_null, tc)
62 {
63 	ATF_REQUIRE_EQ(1, system(NULL));
64 }
65 
66 /*
67  * Define PROCMASK_IS_THREADMASK if sigprocmask() gets / sets the thread
68  * mask in multithreaded programs, which makes it impossible to verify
69  * that system(3) correctly blocks and unblocks SIGCHLD.
70  */
71 #ifdef __FreeBSD__
72 #define PROCMASK_IS_THREADMASK 1
73 #endif
74 
75 static void *
76 system_thread(void *arg)
77 {
78 	char cmd[64];
79 	int i = (int)(intptr_t)arg;
80 
81 	snprintf(cmd, sizeof(cmd), "rm flag%d ; lockf -ns lock%d true", i, i);
82 	return ((void *)(intptr_t)system(cmd));
83 }
84 
85 ATF_TC(system_concurrent);
86 ATF_TC_HEAD(system_concurrent, tc)
87 {
88 	atf_tc_set_md_var(tc, "descr", "Concurrent calls");
89 }
90 ATF_TC_BODY(system_concurrent, tc)
91 {
92 #define N 3
93 	sigset_t normset, sigset;
94 	pthread_t thr[N];
95 	char fn[8];
96 	int fd[N];
97 	void *arg, *ret;
98 
99 	/* Create and lock the locks */
100 	for (int i = 0; i < N; i++) {
101 		snprintf(fn, sizeof(fn), "lock%d", i);
102 		fd[i] = open(fn, O_CREAT|O_EXCL|O_EXLOCK|O_CLOEXEC, 0644);
103 		ATF_REQUIRE_MSG(fd[i] >= 0, "%s: %m", fn);
104 	}
105 
106 	/* Create the flags */
107 	for (int i = 0; i < N; i++) {
108 		snprintf(fn, sizeof(fn), "flag%d", i);
109 		ATF_REQUIRE_EQ(0, symlink(fn, fn));
110 	}
111 
112 	/* Get the current and expected signal mask */
113 	sigprocmask(0, NULL, &normset);
114 
115 	/* Spawn threads which block on these files */
116 	for (int i = 0; i < N; i++) {
117 		arg = (void *)(intptr_t)i;
118 		ATF_REQUIRE_INTEQ(0,
119 		    pthread_create(&thr[i], NULL, system_thread, arg));
120 	}
121 
122 	/* Wait until the flags are gone */
123 	for (int i = 0; i < N; i++) {
124 		snprintf(fn, sizeof(fn), "flag%d", i);
125 		while (readlink(fn, fn, sizeof(fn)) > 0)
126 			usleep(10000);
127 		ATF_REQUIRE_EQ(ENOENT, errno);
128 	}
129 
130 	/* Release the locks */
131 	for (int i = 0; i < N; i++) {
132 		/* Check the signal dispositions */
133 		ATF_CHECK_EQ(SIG_IGN, signal(SIGINT, SIG_IGN));
134 		ATF_CHECK_EQ(SIG_IGN, signal(SIGQUIT, SIG_IGN));
135 #ifndef PROCMASK_IS_THREADMASK
136 		sigprocmask(0, NULL, &sigset);
137 		ATF_CHECK(sigismember(&sigset, SIGCHLD));
138 #endif
139 
140 		/* Close the file, releasing the lock */
141 		ATF_REQUIRE_INTEQ(0, close(fd[i]));
142 
143 		/* Join the thread and check the return value */
144 		ATF_CHECK_INTEQ(0, pthread_join(thr[i], &ret));
145 		ATF_CHECK_INTEQ(W_EXITCODE(0, 0), (int)(intptr_t)ret);
146 	}
147 
148 	/* Check the signal dispositions */
149 	ATF_CHECK_EQ(SIG_DFL, signal(SIGINT, SIG_DFL));
150 	ATF_CHECK_EQ(SIG_DFL, signal(SIGQUIT, SIG_DFL));
151 	sigprocmask(0, NULL, &sigset);
152 	ATF_CHECK_EQ(0, memcmp(&sigset, &normset, sizeof(sigset_t)));
153 #undef N
154 }
155 
156 ATF_TP_ADD_TCS(tp)
157 {
158 	ATF_TP_ADD_TC(tp, system_true);
159 	ATF_TP_ADD_TC(tp, system_false);
160 	ATF_TP_ADD_TC(tp, system_touch);
161 	ATF_TP_ADD_TC(tp, system_null);
162 	ATF_TP_ADD_TC(tp, system_concurrent);
163 	return (atf_no_error());
164 }
165