1 /* $NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2024 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 CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 #include <sys/cdefs.h> 29 __RCSID("$NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $"); 30 31 #include <sys/types.h> 32 #include <sys/ipc.h> 33 #include <sys/sem.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <time.h> 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <unistd.h> 40 #include <atf-c.h> 41 #include <sys/wait.h> 42 43 union semun { 44 int val; /* value for SETVAL */ 45 struct semid_ds *buf; /* buffer for IPC_{STAT,SET} */ 46 u_short *array; /* array for GETALL & SETALL */ 47 }; 48 49 ATF_TC(semtimedop_basic); 50 ATF_TC_HEAD(semtimedop_basic, tc) 51 { 52 atf_tc_set_md_var(tc, "descr", "Basic semtimedop functionality"); 53 } 54 55 ATF_TC_BODY(semtimedop_basic, tc) 56 { 57 key_t key = IPC_PRIVATE; 58 int semid; 59 struct sembuf sops; 60 union semun sun; 61 struct timespec timeout; 62 63 /* Create semaphore set with 1 semaphore */ 64 semid = semget(key, 1, IPC_CREAT | 0600); 65 ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno)); 66 67 /* Initialize semaphore to 0 */ 68 sun.val = 0; 69 if (semctl(semid, 0, SETVAL, sun) == -1) { 70 ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", 71 strerror(errno)); 72 } 73 74 /* Define semtimedop operation: increment semaphore */ 75 sops.sem_num = 0; 76 sops.sem_op = 1; 77 sops.sem_flg = 0; 78 79 /* Define timeout */ 80 timeout.tv_sec = 1; 81 timeout.tv_nsec = 0; 82 83 /* Perform semtimedop */ 84 if (semtimedop(semid, &sops, 1, &timeout) == -1) { 85 ATF_REQUIRE_MSG(0, "semtimedop failed: %s", strerror(errno)); 86 } 87 88 /* Check semaphore value */ 89 int val = semctl(semid, 0, GETVAL); 90 ATF_REQUIRE_MSG(val == 1, 91 "Semaphore value incorrect: got %d, expected 1", val); 92 93 /* Clean up */ 94 ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1, 95 "semctl IPC_RMID failed: %s", strerror(errno)); 96 } 97 98 /* semtimedop blocks until timeout expires */ 99 ATF_TC(semtimedop_timeout); 100 ATF_TC_HEAD(semtimedop_timeout, tc) 101 { 102 atf_tc_set_md_var(tc, "descr", 103 "semtimedop blocks until timeout expires"); 104 } 105 106 ATF_TC_BODY(semtimedop_timeout, tc) 107 { 108 key_t key = IPC_PRIVATE; 109 int semid; 110 struct sembuf sops; 111 union semun sun; 112 struct timespec timeout; 113 pid_t pid; 114 int status; 115 116 /* Create semaphore set with 1 semaphore */ 117 semid = semget(key, 1, IPC_CREAT | 0600); 118 ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno)); 119 120 /* Initialize semaphore to 0 */ 121 sun.val = 0; 122 if (semctl(semid, 0, SETVAL, sun) == -1) { 123 ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno)); 124 } 125 126 pid = fork(); 127 ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno)); 128 129 if (pid == 0) { 130 /* 131 * Child: perform semtimedop with negative sem_op, should 132 * block until timeout 133 */ 134 sops.sem_num = 0; 135 sops.sem_op = -1; 136 sops.sem_flg = 0; 137 138 timeout.tv_sec = 2; 139 timeout.tv_nsec = 0; 140 141 if (semtimedop(semid, &sops, 1, &timeout) == -1) { 142 if (errno == EAGAIN || errno == EINTR) { 143 exit(0); /* Expected */ 144 } 145 } 146 exit(1); /* Unexpected failure/success */ 147 } 148 149 /* Parent: wait for child to finish */ 150 waitpid(pid, &status, 0); 151 ATF_REQUIRE(WIFEXITED(status)); 152 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); 153 154 /* Clean up */ 155 ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1, 156 "semctl IPC_RMID failed: %s", strerror(errno)); 157 } 158 159 /* semtimedop with SEM_UNDO adjusts semaphore on exit */ 160 ATF_TC(semtimedop_semundo); 161 ATF_TC_HEAD(semtimedop_semundo, tc) 162 { 163 atf_tc_set_md_var(tc, "descr", 164 "semtimedop with SEM_UNDO adjusts semaphore on exit"); 165 } 166 167 ATF_TC_BODY(semtimedop_semundo, tc) 168 { 169 key_t key = IPC_PRIVATE; 170 int semid; 171 struct sembuf sops; 172 union semun sun; 173 struct timespec timeout; 174 pid_t pid; 175 int val; 176 177 /* Create semaphore set with 1 semaphore */ 178 semid = semget(key, 1, IPC_CREAT | 0600); 179 ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno)); 180 181 /* Initialize semaphore to 0 */ 182 sun.val = 0; 183 if (semctl(semid, 0, SETVAL, sun) == -1) { 184 ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno)); 185 } 186 187 pid = fork(); 188 ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno)); 189 190 if (pid == 0) { 191 /* Child: perform semtimedop with SEM_UNDO */ 192 sops.sem_num = 0; 193 sops.sem_op = 1; 194 sops.sem_flg = SEM_UNDO; 195 196 timeout.tv_sec = 1; 197 timeout.tv_nsec = 0; 198 199 if (semtimedop(semid, &sops, 1, &timeout) == -1) { 200 exit(1); /* Unexpected failure */ 201 } 202 203 exit(0); /* Exit normally, SEM_UNDO should be triggered */ 204 } 205 206 /* Parent: wait for child to exit */ 207 waitpid(pid, NULL, 0); 208 209 /* Check semaphore value; should be 0 after SEM_UNDO */ 210 val = semctl(semid, 0, GETVAL); 211 ATF_REQUIRE_MSG(val == 0, 212 "Semaphore value incorrect after SEM_UNDO: got %d, " 213 "expected 0", val); 214 215 /* Clean up */ 216 ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1, 217 "semctl IPC_RMID failed: %s", strerror(errno)); 218 } 219 220 /* semtimedop handles invalid parameters correctly */ 221 ATF_TC(semtimedop_invalid); 222 ATF_TC_HEAD(semtimedop_invalid, tc) 223 { 224 atf_tc_set_md_var(tc, "descr", 225 "semtimedop handles invalid parameters correctly"); 226 } 227 228 ATF_TC_BODY(semtimedop_invalid, tc) 229 { 230 struct sembuf sops; 231 union semun sun; 232 struct timespec timeout; 233 234 /* Invalid semaphore id */ 235 sops.sem_num = 0; 236 sops.sem_op = -1; 237 sops.sem_flg = 0; 238 239 timeout.tv_sec = 1; 240 timeout.tv_nsec = 0; 241 242 /* Attempt to perform semtimedop on invalid semid */ 243 ATF_REQUIRE_MSG(semtimedop(-1, &sops, 1, &timeout) == -1 244 && errno == EINVAL, "semtimedop did not fail on invalid semid"); 245 246 /* Create semaphore set */ 247 key_t key = IPC_PRIVATE; 248 int semid = semget(key, 1, IPC_CREAT | 0600); 249 ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno)); 250 251 /* Initialize semaphore to 0 */ 252 sun.val = 0; 253 if (semctl(semid, 0, SETVAL, sun) == -1) { 254 ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno)); 255 } 256 257 /* Set an invalid semaphore number */ 258 sops.sem_num = 1; /* Only 1 semaphore in set, index 0 */ 259 sops.sem_op = 1; 260 sops.sem_flg = 0; 261 262 /* Attempt semtimedop with invalid sem_num */ 263 ATF_REQUIRE_MSG(semtimedop(semid, &sops, 1, &timeout) == -1 264 && errno == EFBIG, "semtimedop did not fail on invalid sem_num"); 265 266 /* Clean up */ 267 ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1, 268 "semctl IPC_RMID failed: %s", strerror(errno)); 269 } 270 271 ATF_TP_ADD_TCS(tp) 272 { 273 ATF_TP_ADD_TC(tp, semtimedop_basic); 274 ATF_TP_ADD_TC(tp, semtimedop_timeout); 275 ATF_TP_ADD_TC(tp, semtimedop_semundo); 276 ATF_TP_ADD_TC(tp, semtimedop_invalid); 277 278 return atf_no_error(); 279 } 280