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 } 386