xref: /netbsd-src/tests/kernel/t_semtimedop.c (revision 331e26ad6cda682c58484564a09c123f4e070051)
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