1 /*-
2 * Copyright (c) 2008 Yahoo!, Inc.
3 * All rights reserved.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the names of any co-contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #include <sys/mman.h>
33 #include <sys/queue.h>
34 #include <sys/sysctl.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37 #include <sys/user.h>
38 #include <sys/wait.h>
39
40 #include <errno.h>
41 #include <kvm.h>
42 #include <limits.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <time.h>
46
47 #include <common.h>
48
49 /*
50 * Use a timer to post a specific semaphore after a timeout. A timer
51 * is scheduled via schedule_post(). check_alarm() must be called
52 * afterwards to clean up and check for errors.
53 */
54 sem_t *alarm_id = SEM_FAILED;
55 int alarm_errno;
56 int alarm_handler_installed;
57
58 int
checkvalue(sem_t * id,int expected)59 checkvalue(sem_t *id, int expected)
60 {
61 int val;
62
63 if (sem_getvalue(id, &val) < 0) {
64 perror("sem_getvalue");
65 return (-1);
66 }
67 if (val != expected) {
68 fprintf(stderr, "sem value should be %d instead of %d",
69 expected, val);
70 return (-1);
71 }
72 return (0);
73 }
74
75 sem_t *
construct_shared_unnamed_sem(unsigned int count)76 construct_shared_unnamed_sem(unsigned int count)
77 {
78 sem_t *id = mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE,
79 MAP_SHARED|MAP_ANON, -1, 0);
80 if (id == MAP_FAILED) {
81 perror("mmap");
82 return SEM_FAILED;
83 }
84
85 if (sem_init(id, 1, count) < 0) {
86 perror("sem_init");
87 munmap(id, sizeof(sem_t));
88 return SEM_FAILED;
89 }
90
91 return id;
92 }
93
94 void
destruct_shared_unnamed_sem(sem_t * id)95 destruct_shared_unnamed_sem(sem_t *id)
96 {
97 if (sem_destroy(id) < 0)
98 perror("sem_destroy");
99
100 if (munmap(id, sizeof(sem_t)) < 0)
101 perror("munmap");
102 }
103
104 int
testwait(sem_t * id,u_int * delta)105 testwait(sem_t *id, u_int *delta)
106 {
107 struct timespec start, end;
108
109 if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
110 perror("clock_gettime(CLOCK_REALTIME)");
111 return (-1);
112 }
113 if (sem_wait(id) < 0) {
114 perror("sem_wait");
115 return (-1);
116 }
117 if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
118 perror("clock_gettime(CLOCK_REALTIME)");
119 return (-1);
120 }
121 timespecsub(&end, &start, &end);
122 *delta = end.tv_nsec / 1000000;
123 *delta += end.tv_sec * 1000;
124 return (0);
125 }
126
127 static void
alarm_handler(int signo)128 alarm_handler(int signo)
129 {
130
131 if (sem_post(alarm_id) < 0)
132 alarm_errno = errno;
133 }
134
135 int
schedule_post(sem_t * id,u_int msec)136 schedule_post(sem_t *id, u_int msec)
137 {
138 struct itimerval it;
139
140 if (!alarm_handler_installed) {
141 if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
142 perror("signal(SIGALRM)");
143 return (-1);
144 }
145 alarm_handler_installed = 1;
146 }
147 if (alarm_id != SEM_FAILED) {
148 fprintf(stderr, "sem_post() already scheduled");
149 return (-1);
150 }
151 alarm_id = id;
152 bzero(&it, sizeof(it));
153 it.it_value.tv_sec = msec / 1000;
154 it.it_value.tv_usec = (msec % 1000) * 1000;
155 if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
156 perror("setitimer");
157 return (-1);
158 }
159 return (0);
160 }
161
162 int
check_alarm(int just_clear)163 check_alarm(int just_clear)
164 {
165 struct itimerval it;
166
167 bzero(&it, sizeof(it));
168 if (just_clear) {
169 setitimer(ITIMER_REAL, &it, NULL);
170 alarm_errno = 0;
171 alarm_id = SEM_FAILED;
172 return (0);
173 }
174 if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
175 perror("setitimer");
176 return (-1);
177 }
178 if (alarm_errno != 0 && !just_clear) {
179 errno = alarm_errno;
180 perror("sem_post() (via timeout)");
181 alarm_errno = 0;
182 return (-1);
183 }
184 alarm_id = SEM_FAILED;
185
186 return (0);
187 }
188
189 /*
190 * Helper routine for tests that use a child process. This routine
191 * creates a pipe and forks a child process. The child process runs
192 * the 'func' routine which returns a status integer. The status
193 * integer gets written over the pipe to the parent and returned in
194 * '*stat'. If there is an error in pipe(), fork(), or wait() this
195 * returns -1 and fails the test.
196 */
197 int
child_worker(int (* func)(void * arg),void * arg,int * stat)198 child_worker(int (*func)(void *arg), void *arg, int *stat)
199 {
200 pid_t pid;
201 int pfd[2], cstat;
202
203 if (pipe(pfd) < 0) {
204 perror("pipe");
205 return (-1);
206 }
207
208 pid = fork();
209 switch (pid) {
210 case -1:
211 /* Error. */
212 perror("fork");
213 close(pfd[0]);
214 close(pfd[1]);
215 return (-1);
216 case 0:
217 /* Child. */
218 cstat = func(arg);
219 write(pfd[1], &cstat, sizeof(cstat));
220 exit(0);
221 }
222
223 if (read(pfd[0], stat, sizeof(*stat)) < 0) {
224 perror("read(pipe)");
225 close(pfd[0]);
226 close(pfd[1]);
227 return (-1);
228 }
229 if (waitpid(pid, NULL, 0) < 0) {
230 perror("wait");
231 close(pfd[0]);
232 close(pfd[1]);
233 return (-1);
234 }
235 close(pfd[0]);
236 close(pfd[1]);
237 return (0);
238 }
239
240 /*
241 * Fork off a child process. The child will open the semaphore via
242 * the same name. The child will then block on the semaphore waiting
243 * for the parent to post it.
244 */
245 int
wait_twoproc_child(void * arg)246 wait_twoproc_child(void *arg)
247 {
248 sem_t *id;
249
250 id = sem_open(TEST_PATH, 0, 0, 0);
251 if (id == SEM_FAILED)
252 return (CSTAT(1, errno));
253 if (sem_wait(id) < 0)
254 return (CSTAT(2, errno));
255 if (sem_close(id) < 0)
256 return (CSTAT(3, errno));
257 return (CSTAT(0, 0));
258 }
259
260 int
timedwait(sem_t * id,u_int msec,u_int * delta,int error)261 timedwait(sem_t *id, u_int msec, u_int *delta, int error)
262 {
263 struct timespec start, end;
264
265 if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
266 perror("clock_gettime(CLOCK_REALTIME)");
267 return (-1);
268 }
269 end.tv_sec = msec / 1000;
270 end.tv_nsec = msec % 1000 * 1000000;
271 timespecadd(&end, &start, &end);
272 if (sem_timedwait(id, &end) < 0) {
273 if (errno != error) {
274 perror("sem_timedwait");
275 return (-1);
276 }
277 } else if (error != 0) {
278 return (-1);
279 }
280 if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
281 perror("clock_gettime(CLOCK_REALTIME)");
282 return (-1);
283 }
284 timespecsub(&end, &start, &end);
285 *delta = end.tv_nsec / 1000000;
286 *delta += end.tv_sec * 1000;
287 return (0);
288 }
289
290 /*
291 * Attempt a sem_open() that should fail with an expected error of
292 * 'error'.
293 */
294 int
sem_open_should_fail(const char * path,int flags,mode_t mode,unsigned int value,int error)295 sem_open_should_fail(const char *path, int flags, mode_t mode,
296 unsigned int value, int error)
297 {
298 int retval = 0;
299 sem_t *id;
300
301 id = sem_open(path, flags, mode, value);
302 if (id != SEM_FAILED) {
303 sem_close(id);
304 retval = 1;
305 }
306 if (errno != error) {
307 fprintf(stderr, "sem_open: %s\n", strerror(errno));
308 retval = 1;
309 }
310 return retval;
311 }
312
313 /*
314 * Attempt a sem_init() that should fail with an expected error of
315 * 'error'.
316 */
317 int
sem_init_should_fail(unsigned int value,int error)318 sem_init_should_fail(unsigned int value, int error)
319 {
320 sem_t id;
321
322 if (sem_init(&id, 0, value) >= 0) {
323 sem_destroy(&id);
324 return 1;
325 }
326 if (errno != error) {
327 perror("sem_init");
328 return 1;
329 }
330
331 return 0;
332 }
333
334 /*
335 * Attempt a sem_unlink() that should fail with an expected error of
336 * 'error'.
337 */
338 int
sem_unlink_should_fail(const char * path,int error)339 sem_unlink_should_fail(const char *path, int error)
340 {
341
342 if (sem_unlink(path) >= 0) {
343 return 1;
344 }
345 if (errno != error) {
346 perror("sem_unlink");
347 return 1;
348 }
349 return 0;
350 }
351
352 /*
353 * Attempt a sem_destroy() that should fail with an expected error of
354 * 'error'.
355 */
356 int
sem_destroy_should_fail(sem_t * id,int error)357 sem_destroy_should_fail(sem_t *id, int error)
358 {
359 if (sem_destroy(id) >= 0) {
360 return 1;
361 }
362 if (errno != error) {
363 perror("sem_destroy");
364 return 1;
365 }
366 return 0;
367 }
368
369 /*
370 * Attempt a sem_close() that should fail with an expected error of
371 * 'error'.
372 */
373 int
sem_close_should_fail(sem_t * id,int error)374 sem_close_should_fail(sem_t *id, int error)
375 {
376
377 if (sem_close(id) >= 0) {
378 return 1;
379 }
380 if (errno != error) {
381 perror("sem_close");
382 return 1;
383 }
384 return 0;
385 }
386