xref: /dragonfly/test/testcases/posixipc/common/common.c (revision 944cd60c7b4392d637be82be7baafe9ac12a3061)
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