t_semtimedop.c revision 1.2 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