1 /* $OpenBSD: rthread_sem_compat.c,v 1.1 2018/06/08 13:53:01 pirofti 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 can_eintr, 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 { 70 sem->waitcount++; 71 do { 72 r = __thrsleep(ident, CLOCK_REALTIME, abstime, 73 &sem->lock, delayed_cancel); 74 _spinlock(&sem->lock); 75 /* ignore interruptions other than cancelation */ 76 if ((r == ECANCELED && *delayed_cancel == 0) || 77 (r == EINTR && !can_eintr)) 78 r = 0; 79 } while (r == 0 && sem->value == 0); 80 sem->waitcount--; 81 if (r == 0) 82 sem->value--; 83 } 84 _spinunlock(&sem->lock); 85 return (r); 86 } 87 88 /* always increment count */ 89 int 90 _sem_post(sem_t sem) 91 { 92 void *ident = (void *)&sem->waitcount; 93 int rv = 0; 94 95 if (sem->shared) 96 ident = SHARED_IDENT; 97 98 _spinlock(&sem->lock); 99 sem->value++; 100 if (sem->waitcount) { 101 __thrwakeup(ident, 1); 102 rv = 1; 103 } 104 _spinunlock(&sem->lock); 105 return (rv); 106 } 107 108 /* 109 * exported semaphores 110 */ 111 int 112 sem_init(sem_t *semp, int pshared, unsigned int value) 113 { 114 sem_t sem; 115 116 if (value > SEM_VALUE_MAX) { 117 errno = EINVAL; 118 return (-1); 119 } 120 121 if (pshared) { 122 errno = EPERM; 123 return (-1); 124 #ifdef notyet 125 char name[SEM_RANDOM_NAME_LEN]; 126 sem_t *sempshared; 127 int i; 128 129 for (;;) { 130 for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) 131 name[i] = arc4random_uniform(255) + 1; 132 name[SEM_RANDOM_NAME_LEN - 1] = '\0'; 133 sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); 134 if (sempshared != SEM_FAILED) 135 break; 136 if (errno == EEXIST) 137 continue; 138 if (errno != EPERM) 139 errno = ENOSPC; 140 return (-1); 141 } 142 143 /* unnamed semaphore should not be opened twice */ 144 if (sem_unlink(name) == -1) { 145 sem_close(sempshared); 146 errno = ENOSPC; 147 return (-1); 148 } 149 150 *semp = *sempshared; 151 free(sempshared); 152 return (0); 153 #endif 154 } 155 156 sem = calloc(1, sizeof(*sem)); 157 if (!sem) { 158 errno = ENOSPC; 159 return (-1); 160 } 161 sem->lock = _SPINLOCK_UNLOCKED; 162 sem->value = value; 163 *semp = sem; 164 165 return (0); 166 } 167 168 int 169 sem_destroy(sem_t *semp) 170 { 171 sem_t sem; 172 173 if (!_threads_ready) /* for SEM_MMAP_SIZE */ 174 _rthread_init(); 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, 1, 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 (!semp || !(sem = *semp) || abstime == NULL || 270 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { 271 errno = EINVAL; 272 return (-1); 273 } 274 275 if (!_threads_ready) 276 _rthread_init(); 277 self = tib->tib_thread; 278 279 ENTER_DELAYED_CANCEL_POINT(tib, self); 280 r = _sem_wait(sem, 1, abstime, &self->delayed_cancel); 281 LEAVE_CANCEL_POINT_INNER(tib, r); 282 283 if (r) { 284 errno = r == EWOULDBLOCK ? ETIMEDOUT : r; 285 return (-1); 286 } 287 288 return (0); 289 } 290 291 int 292 sem_trywait(sem_t *semp) 293 { 294 sem_t sem; 295 int r; 296 297 if (!semp || !(sem = *semp)) { 298 errno = EINVAL; 299 return (-1); 300 } 301 302 _spinlock(&sem->lock); 303 if (sem->value) { 304 sem->value--; 305 r = 0; 306 } else 307 r = EAGAIN; 308 _spinunlock(&sem->lock); 309 310 if (r) { 311 errno = r; 312 return (-1); 313 } 314 315 return (0); 316 } 317 318 319 static void 320 makesempath(const char *origpath, char *sempath, size_t len) 321 { 322 char buf[SHA256_DIGEST_STRING_LENGTH]; 323 324 SHA256Data(origpath, strlen(origpath), buf); 325 snprintf(sempath, len, "/tmp/%s.sem", buf); 326 } 327 328 sem_t * 329 sem_open(const char *name, int oflag, ...) 330 { 331 char sempath[SEM_PATH_SIZE]; 332 struct stat sb; 333 sem_t sem, *semp; 334 unsigned int value = 0; 335 int created = 0, fd; 336 337 if (!_threads_ready) 338 _rthread_init(); 339 340 if (oflag & ~(O_CREAT | O_EXCL)) { 341 errno = EINVAL; 342 return (SEM_FAILED); 343 } 344 345 if (oflag & O_CREAT) { 346 va_list ap; 347 va_start(ap, oflag); 348 /* 3rd parameter mode is not used */ 349 va_arg(ap, mode_t); 350 value = va_arg(ap, unsigned); 351 va_end(ap); 352 353 if (value > SEM_VALUE_MAX) { 354 errno = EINVAL; 355 return (SEM_FAILED); 356 } 357 } 358 359 makesempath(name, sempath, sizeof(sempath)); 360 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 361 if (fd == -1) 362 return (SEM_FAILED); 363 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 364 close(fd); 365 errno = EINVAL; 366 return (SEM_FAILED); 367 } 368 if (sb.st_uid != geteuid()) { 369 close(fd); 370 errno = EPERM; 371 return (SEM_FAILED); 372 } 373 if (sb.st_size != (off_t)SEM_MMAP_SIZE) { 374 if (!(oflag & O_CREAT)) { 375 close(fd); 376 errno = EINVAL; 377 return (SEM_FAILED); 378 } 379 if (sb.st_size != 0) { 380 close(fd); 381 errno = EINVAL; 382 return (SEM_FAILED); 383 } 384 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 385 close(fd); 386 errno = EINVAL; 387 return (SEM_FAILED); 388 } 389 390 created = 1; 391 } 392 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 393 MAP_SHARED, fd, 0); 394 close(fd); 395 if (sem == MAP_FAILED) { 396 errno = EINVAL; 397 return (SEM_FAILED); 398 } 399 semp = malloc(sizeof(*semp)); 400 if (!semp) { 401 munmap(sem, SEM_MMAP_SIZE); 402 errno = ENOSPC; 403 return (SEM_FAILED); 404 } 405 if (created) { 406 sem->lock = _SPINLOCK_UNLOCKED; 407 sem->value = value; 408 sem->shared = 1; 409 } 410 *semp = sem; 411 412 return (semp); 413 } 414 415 int 416 sem_close(sem_t *semp) 417 { 418 sem_t sem; 419 420 if (!semp || !(sem = *semp) || !sem->shared) { 421 errno = EINVAL; 422 return (-1); 423 } 424 425 *semp = NULL; 426 munmap(sem, SEM_MMAP_SIZE); 427 free(semp); 428 429 return (0); 430 } 431 432 int 433 sem_unlink(const char *name) 434 { 435 char sempath[SEM_PATH_SIZE]; 436 437 makesempath(name, sempath, sizeof(sempath)); 438 return (unlink(sempath)); 439 } 440