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