fc97e6bf6a24eaeee331890efdb49f1fcb479ef8
[dragonfly.git] / test / testcases / posixipc / common / common.c
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
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 *
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
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
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);
122         *delta = end.tv_nsec / 1000000;
123         *delta += end.tv_sec * 1000;
124         return (0);
125 }
126
127 static void
128 alarm_handler(int signo)
129 {
130
131         if (sem_post(alarm_id) < 0)
132                 alarm_errno = errno;
133 }
134
135 int
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
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
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
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
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);
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);
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
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
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
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
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
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 }