1/* $OpenBSD: rthread_sem_compat.c,v 1.2 2022/05/14 14:52:20 cheloha 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#include <sys/time.h> 23 24#include <errno.h> 25#include <fcntl.h> 26#include <sha2.h> 27#include <stdarg.h> 28#include <stdlib.h> 29#include <stdio.h> 30#include <string.h> 31#include <unistd.h> 32 33#include <pthread.h> 34 35#include "rthread.h" 36#include "cancel.h" /* in libc/include */ 37 38#define SHARED_IDENT ((void *)-1) 39 40/* SHA256_DIGEST_STRING_LENGTH includes nul */ 41/* "/tmp/" + sha256 + ".sem" */ 42#define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) 43 44/* long enough to be hard to guess */ 45#define SEM_RANDOM_NAME_LEN 10 46 47/* 48 * Size of memory to be mmap()'ed by named semaphores. 49 * Should be >= SEM_PATH_SIZE and page-aligned. 50 */ 51#define SEM_MMAP_SIZE _thread_pagesize 52 53/* 54 * Internal implementation of semaphores 55 */ 56int 57_sem_wait(sem_t sem, int can_eintr, const struct timespec *abstime, 58 int *delayed_cancel) 59{ 60 void *ident = (void *)&sem->waitcount; 61 int r; 62 63 if (sem->shared) 64 ident = SHARED_IDENT; 65 66 _spinlock(&sem->lock); 67 if (sem->value) { 68 sem->value--; 69 r = 0; 70 } else { 71 sem->waitcount++; 72 do { 73 r = __thrsleep(ident, CLOCK_REALTIME, abstime, 74 &sem->lock, delayed_cancel); 75 _spinlock(&sem->lock); 76 /* ignore interruptions other than cancelation */ 77 if ((r == ECANCELED && *delayed_cancel == 0) || 78 (r == EINTR && !can_eintr)) 79 r = 0; 80 } while (r == 0 && sem->value == 0); 81 sem->waitcount--; 82 if (r == 0) 83 sem->value--; 84 } 85 _spinunlock(&sem->lock); 86 return (r); 87} 88 89/* always increment count */ 90int 91_sem_post(sem_t sem) 92{ 93 void *ident = (void *)&sem->waitcount; 94 int rv = 0; 95 96 if (sem->shared) 97 ident = SHARED_IDENT; 98 99 _spinlock(&sem->lock); 100 sem->value++; 101 if (sem->waitcount) { 102 __thrwakeup(ident, 1); 103 rv = 1; 104 } 105 _spinunlock(&sem->lock); 106 return (rv); 107} 108 109/* 110 * exported semaphores 111 */ 112int 113sem_init(sem_t *semp, int pshared, unsigned int value) 114{ 115 sem_t sem; 116 117 if (value > SEM_VALUE_MAX) { 118 errno = EINVAL; 119 return (-1); 120 } 121 122 if (pshared) { 123 errno = EPERM; 124 return (-1); 125#ifdef notyet 126 char name[SEM_RANDOM_NAME_LEN]; 127 sem_t *sempshared; 128 int i; 129 130 for (;;) { 131 for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) 132 name[i] = arc4random_uniform(255) + 1; 133 name[SEM_RANDOM_NAME_LEN - 1] = '\0'; 134 sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); 135 if (sempshared != SEM_FAILED) 136 break; 137 if (errno == EEXIST) 138 continue; 139 if (errno != EPERM) 140 errno = ENOSPC; 141 return (-1); 142 } 143 144 /* unnamed semaphore should not be opened twice */ 145 if (sem_unlink(name) == -1) { 146 sem_close(sempshared); 147 errno = ENOSPC; 148 return (-1); 149 } 150 151 *semp = *sempshared; 152 free(sempshared); 153 return (0); 154#endif 155 } 156 157 sem = calloc(1, sizeof(*sem)); 158 if (!sem) { 159 errno = ENOSPC; 160 return (-1); 161 } 162 sem->lock = _SPINLOCK_UNLOCKED; 163 sem->value = value; 164 *semp = sem; 165 166 return (0); 167} 168 169int 170sem_destroy(sem_t *semp) 171{ 172 sem_t sem; 173 174 if (!_threads_ready) /* for SEM_MMAP_SIZE */ 175 _rthread_init(); 176 177 if (!semp || !(sem = *semp)) { 178 errno = EINVAL; 179 return (-1); 180 } 181 182 if (sem->waitcount) { 183#define MSG "sem_destroy on semaphore with waiters!\n" 184 write(2, MSG, sizeof(MSG) - 1); 185#undef MSG 186 errno = EBUSY; 187 return (-1); 188 } 189 190 *semp = NULL; 191 if (sem->shared) 192 munmap(sem, SEM_MMAP_SIZE); 193 else 194 free(sem); 195 196 return (0); 197} 198 199int 200sem_getvalue(sem_t *semp, int *sval) 201{ 202 sem_t sem; 203 204 if (!semp || !(sem = *semp)) { 205 errno = EINVAL; 206 return (-1); 207 } 208 209 _spinlock(&sem->lock); 210 *sval = sem->value; 211 _spinunlock(&sem->lock); 212 213 return (0); 214} 215 216int 217sem_post(sem_t *semp) 218{ 219 sem_t sem; 220 221 if (!semp || !(sem = *semp)) { 222 errno = EINVAL; 223 return (-1); 224 } 225 226 _sem_post(sem); 227 228 return (0); 229} 230 231int 232sem_wait(sem_t *semp) 233{ 234 struct tib *tib = TIB_GET(); 235 pthread_t self; 236 sem_t sem; 237 int r; 238 PREP_CANCEL_POINT(tib); 239 240 if (!_threads_ready) 241 _rthread_init(); 242 self = tib->tib_thread; 243 244 if (!semp || !(sem = *semp)) { 245 errno = EINVAL; 246 return (-1); 247 } 248 249 ENTER_DELAYED_CANCEL_POINT(tib, self); 250 r = _sem_wait(sem, 1, NULL, &self->delayed_cancel); 251 LEAVE_CANCEL_POINT_INNER(tib, r); 252 253 if (r) { 254 errno = r; 255 return (-1); 256 } 257 258 return (0); 259} 260 261int 262sem_timedwait(sem_t *semp, const struct timespec *abstime) 263{ 264 struct tib *tib = TIB_GET(); 265 pthread_t self; 266 sem_t sem; 267 int r; 268 PREP_CANCEL_POINT(tib); 269 270 if (!semp || !(sem = *semp) || !abstime || !timespecisvalid(abstime)) { 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 291int 292sem_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 319static void 320makesempath(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 328sem_t * 329sem_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 415int 416sem_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 432int 433sem_unlink(const char *name) 434{ 435 char sempath[SEM_PATH_SIZE]; 436 437 makesempath(name, sempath, sizeof(sempath)); 438 return (unlink(sempath)); 439} 440