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, &end);
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, &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
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 }
386