1 /* $OpenBSD: rthread_sem.c,v 1.23 2016/05/07 19:05:22 guenther Exp $ */ 2 /* 3 * Copyright (c) 2004,2005,2013 Ted Unangst <tedu@openbsd.org> 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/mman.h> 21 #include <sys/stat.h> 22 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <sha2.h> 26 #include <stdarg.h> 27 #include <stdlib.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #include <pthread.h> 33 34 #include "rthread.h" 35 #include "cancel.h" /* in libc/include */ 36 37 #define SHARED_IDENT ((void *)-1) 38 39 /* SHA256_DIGEST_STRING_LENGTH includes nul */ 40 /* "/tmp/" + sha256 + ".sem" */ 41 #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) 42 43 /* long enough to be hard to guess */ 44 #define SEM_RANDOM_NAME_LEN 10 45 46 /* 47 * Size of memory to be mmap()'ed by named semaphores. 48 * Should be >= SEM_PATH_SIZE and page-aligned. 49 */ 50 #define SEM_MMAP_SIZE _thread_pagesize 51 52 /* 53 * Internal implementation of semaphores 54 */ 55 int 56 _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime, 57 int *delayed_cancel) 58 { 59 void *ident = (void *)&sem->waitcount; 60 int r; 61 62 if (sem->shared) 63 ident = SHARED_IDENT; 64 65 _spinlock(&sem->lock); 66 if (sem->value) { 67 sem->value--; 68 r = 0; 69 } else if (tryonly) { 70 r = EAGAIN; 71 } else { 72 sem->waitcount++; 73 do { 74 r = __thrsleep(ident, CLOCK_REALTIME | 75 _USING_TICKETS, abstime, &sem->lock.ticket, 76 delayed_cancel); 77 _spinlock(&sem->lock); 78 /* ignore interruptions other than cancelation */ 79 if (r == EINTR && (delayed_cancel == NULL || 80 *delayed_cancel == 0)) 81 r = 0; 82 } while (r == 0 && sem->value == 0); 83 sem->waitcount--; 84 if (r == 0) 85 sem->value--; 86 } 87 _spinunlock(&sem->lock); 88 return (r); 89 } 90 91 /* always increment count */ 92 int 93 _sem_post(sem_t sem) 94 { 95 void *ident = (void *)&sem->waitcount; 96 int rv = 0; 97 98 if (sem->shared) 99 ident = SHARED_IDENT; 100 101 _spinlock(&sem->lock); 102 sem->value++; 103 if (sem->waitcount) { 104 __thrwakeup(ident, 1); 105 rv = 1; 106 } 107 _spinunlock(&sem->lock); 108 return (rv); 109 } 110 111 /* 112 * exported semaphores 113 */ 114 int 115 sem_init(sem_t *semp, int pshared, unsigned int value) 116 { 117 sem_t sem; 118 119 if (value > SEM_VALUE_MAX) { 120 errno = EINVAL; 121 return (-1); 122 } 123 124 if (pshared) { 125 errno = EPERM; 126 return (-1); 127 #ifdef notyet 128 char name[SEM_RANDOM_NAME_LEN]; 129 sem_t *sempshared; 130 int i; 131 132 for (;;) { 133 for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) 134 name[i] = arc4random_uniform(255) + 1; 135 name[SEM_RANDOM_NAME_LEN - 1] = '\0'; 136 sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); 137 if (sempshared != SEM_FAILED) 138 break; 139 if (errno == EEXIST) 140 continue; 141 if (errno != EPERM) 142 errno = ENOSPC; 143 return (-1); 144 } 145 146 /* unnamed semaphore should not be opened twice */ 147 if (sem_unlink(name) == -1) { 148 sem_close(sempshared); 149 errno = ENOSPC; 150 return (-1); 151 } 152 153 *semp = *sempshared; 154 free(sempshared); 155 return (0); 156 #endif 157 } 158 159 sem = calloc(1, sizeof(*sem)); 160 if (!sem) { 161 errno = ENOSPC; 162 return (-1); 163 } 164 sem->lock = _SPINLOCK_UNLOCKED_ASSIGN; 165 sem->value = value; 166 *semp = sem; 167 168 return (0); 169 } 170 171 int 172 sem_destroy(sem_t *semp) 173 { 174 sem_t sem; 175 176 if (!semp || !(sem = *semp)) { 177 errno = EINVAL; 178 return (-1); 179 } 180 181 if (sem->waitcount) { 182 #define MSG "sem_destroy on semaphore with waiters!\n" 183 write(2, MSG, sizeof(MSG) - 1); 184 #undef MSG 185 errno = EBUSY; 186 return (-1); 187 } 188 189 *semp = NULL; 190 if (sem->shared) 191 munmap(sem, SEM_MMAP_SIZE); 192 else 193 free(sem); 194 195 return (0); 196 } 197 198 int 199 sem_getvalue(sem_t *semp, int *sval) 200 { 201 sem_t sem; 202 203 if (!semp || !(sem = *semp)) { 204 errno = EINVAL; 205 return (-1); 206 } 207 208 _spinlock(&sem->lock); 209 *sval = sem->value; 210 _spinunlock(&sem->lock); 211 212 return (0); 213 } 214 215 int 216 sem_post(sem_t *semp) 217 { 218 sem_t sem; 219 220 if (!semp || !(sem = *semp)) { 221 errno = EINVAL; 222 return (-1); 223 } 224 225 _sem_post(sem); 226 227 return (0); 228 } 229 230 int 231 sem_wait(sem_t *semp) 232 { 233 struct tib *tib = TIB_GET(); 234 pthread_t self; 235 sem_t sem; 236 int r; 237 PREP_CANCEL_POINT(tib); 238 239 if (!_threads_ready) 240 _rthread_init(); 241 self = tib->tib_thread; 242 243 if (!semp || !(sem = *semp)) { 244 errno = EINVAL; 245 return (-1); 246 } 247 248 ENTER_DELAYED_CANCEL_POINT(tib, self); 249 r = _sem_wait(sem, 0, NULL, &self->delayed_cancel); 250 LEAVE_CANCEL_POINT_INNER(tib, r); 251 252 if (r) { 253 errno = r; 254 return (-1); 255 } 256 257 return (0); 258 } 259 260 int 261 sem_timedwait(sem_t *semp, const struct timespec *abstime) 262 { 263 struct tib *tib = TIB_GET(); 264 pthread_t self; 265 sem_t sem; 266 int r; 267 PREP_CANCEL_POINT(tib); 268 269 if (!_threads_ready) 270 _rthread_init(); 271 self = tib->tib_thread; 272 273 if (!semp || !(sem = *semp)) { 274 errno = EINVAL; 275 return (-1); 276 } 277 278 ENTER_DELAYED_CANCEL_POINT(tib, self); 279 r = _sem_wait(sem, 0, abstime, &self->delayed_cancel); 280 LEAVE_CANCEL_POINT_INNER(tib, r); 281 282 if (r) { 283 errno = r == EWOULDBLOCK ? ETIMEDOUT : r; 284 return (-1); 285 } 286 287 return (0); 288 } 289 290 int 291 sem_trywait(sem_t *semp) 292 { 293 sem_t sem; 294 int r; 295 296 if (!semp || !(sem = *semp)) { 297 errno = EINVAL; 298 return (-1); 299 } 300 301 r = _sem_wait(sem, 1, NULL, NULL); 302 303 if (r) { 304 errno = r; 305 return (-1); 306 } 307 308 return (0); 309 } 310 311 312 static void 313 makesempath(const char *origpath, char *sempath, size_t len) 314 { 315 char buf[SHA256_DIGEST_STRING_LENGTH]; 316 317 SHA256Data(origpath, strlen(origpath), buf); 318 snprintf(sempath, len, "/tmp/%s.sem", buf); 319 } 320 321 sem_t * 322 sem_open(const char *name, int oflag, ...) 323 { 324 char sempath[SEM_PATH_SIZE]; 325 struct stat sb; 326 sem_t sem, *semp; 327 unsigned int value = 0; 328 int created = 0, fd; 329 330 if (!_threads_ready) 331 _rthread_init(); 332 333 if (oflag & ~(O_CREAT | O_EXCL)) { 334 errno = EINVAL; 335 return (SEM_FAILED); 336 } 337 338 if (oflag & O_CREAT) { 339 va_list ap; 340 va_start(ap, oflag); 341 /* 3rd parameter mode is not used */ 342 va_arg(ap, mode_t); 343 value = va_arg(ap, unsigned); 344 va_end(ap); 345 346 if (value > SEM_VALUE_MAX) { 347 errno = EINVAL; 348 return (SEM_FAILED); 349 } 350 } 351 352 makesempath(name, sempath, sizeof(sempath)); 353 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 354 if (fd == -1) 355 return (SEM_FAILED); 356 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 357 close(fd); 358 errno = EINVAL; 359 return (SEM_FAILED); 360 } 361 if (sb.st_uid != geteuid()) { 362 close(fd); 363 errno = EPERM; 364 return (SEM_FAILED); 365 } 366 if (sb.st_size != (off_t)SEM_MMAP_SIZE) { 367 if (!(oflag & O_CREAT)) { 368 close(fd); 369 errno = EINVAL; 370 return (SEM_FAILED); 371 } 372 if (sb.st_size != 0) { 373 close(fd); 374 errno = EINVAL; 375 return (SEM_FAILED); 376 } 377 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 378 close(fd); 379 errno = EINVAL; 380 return (SEM_FAILED); 381 } 382 383 created = 1; 384 } 385 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 386 MAP_SHARED, fd, 0); 387 close(fd); 388 if (sem == MAP_FAILED) { 389 errno = EINVAL; 390 return (SEM_FAILED); 391 } 392 semp = malloc(sizeof(*semp)); 393 if (!semp) { 394 munmap(sem, SEM_MMAP_SIZE); 395 errno = ENOSPC; 396 return (SEM_FAILED); 397 } 398 if (created) { 399 sem->lock = _SPINLOCK_UNLOCKED_ASSIGN; 400 sem->value = value; 401 sem->shared = 1; 402 } 403 *semp = sem; 404 405 return (semp); 406 } 407 408 int 409 sem_close(sem_t *semp) 410 { 411 sem_t sem; 412 413 if (!semp || !(sem = *semp) || !sem->shared) { 414 errno = EINVAL; 415 return (-1); 416 } 417 418 *semp = NULL; 419 munmap(sem, SEM_MMAP_SIZE); 420 free(semp); 421 422 return (0); 423 } 424 425 int 426 sem_unlink(const char *name) 427 { 428 char sempath[SEM_PATH_SIZE]; 429 430 makesempath(name, sempath, sizeof(sempath)); 431 return (unlink(sempath)); 432 } 433