1 /* $OpenBSD: rthread_sem.c,v 1.19 2014/06/27 23:21:47 matthew 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 #include <sys/param.h> 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 36 #define SHARED_IDENT ((void *)-1) 37 38 /* SHA256_DIGEST_STRING_LENGTH includes nul */ 39 /* "/tmp/" + sha256 + ".sem" */ 40 #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) 41 42 /* long enough to be hard to guess */ 43 #define SEM_RANDOM_NAME_LEN 10 44 45 /* 46 * Size of memory to be mmap()'ed by named semaphores. 47 * Should be >= SEM_PATH_SIZE and page-aligned. 48 */ 49 #define SEM_MMAP_SIZE getpagesize() 50 51 /* 52 * Internal implementation of semaphores 53 */ 54 int 55 _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime, 56 int *delayed_cancel) 57 { 58 void *ident = (void *)&sem->waitcount; 59 int r; 60 61 if (sem->shared) 62 ident = SHARED_IDENT; 63 64 _spinlock(&sem->lock); 65 if (sem->value) { 66 sem->value--; 67 r = 0; 68 } else if (tryonly) { 69 r = EAGAIN; 70 } else { 71 sem->waitcount++; 72 do { 73 r = __thrsleep(ident, CLOCK_REALTIME | 74 _USING_TICKETS, abstime, &sem->lock.ticket, 75 delayed_cancel); 76 _spinlock(&sem->lock); 77 /* ignore interruptions other than cancelation */ 78 if (r == EINTR && (delayed_cancel == NULL || 79 *delayed_cancel == 0)) 80 r = 0; 81 } while (r == 0 && sem->value == 0); 82 sem->waitcount--; 83 if (r == 0) 84 sem->value--; 85 } 86 _spinunlock(&sem->lock); 87 return (r); 88 } 89 90 /* always increment count */ 91 int 92 _sem_post(sem_t sem) 93 { 94 void *ident = (void *)&sem->waitcount; 95 int rv = 0; 96 97 if (sem->shared) 98 ident = SHARED_IDENT; 99 100 _spinlock(&sem->lock); 101 sem->value++; 102 if (sem->waitcount) { 103 __thrwakeup(ident, 1); 104 rv = 1; 105 } 106 _spinunlock(&sem->lock); 107 return (rv); 108 } 109 110 /* 111 * exported semaphores 112 */ 113 int 114 sem_init(sem_t *semp, int pshared, unsigned int value) 115 { 116 sem_t sem; 117 118 if (value > SEM_VALUE_MAX) { 119 errno = EINVAL; 120 return (-1); 121 } 122 123 if (pshared) { 124 errno = EPERM; 125 return (-1); 126 #ifdef notyet 127 char name[SEM_RANDOM_NAME_LEN]; 128 sem_t *sempshared; 129 int i; 130 131 for (;;) { 132 for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) 133 name[i] = arc4random_uniform(255) + 1; 134 name[SEM_RANDOM_NAME_LEN - 1] = '\0'; 135 sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value); 136 if (sempshared != SEM_FAILED) 137 break; 138 if (errno == EEXIST) 139 continue; 140 if (errno != EPERM) 141 errno = ENOSPC; 142 return (-1); 143 } 144 145 /* unnamed semaphore should not be opened twice */ 146 if (sem_unlink(name) == -1) { 147 sem_close(sempshared); 148 errno = ENOSPC; 149 return (-1); 150 } 151 152 *semp = *sempshared; 153 free(sempshared); 154 return (0); 155 #endif 156 } 157 158 sem = calloc(1, sizeof(*sem)); 159 if (!sem) { 160 errno = ENOSPC; 161 return (-1); 162 } 163 sem->lock = _SPINLOCK_UNLOCKED_ASSIGN; 164 sem->value = value; 165 *semp = sem; 166 167 return (0); 168 } 169 170 int 171 sem_destroy(sem_t *semp) 172 { 173 sem_t sem; 174 175 if (!semp || !(sem = *semp)) { 176 errno = EINVAL; 177 return (-1); 178 } 179 180 if (sem->waitcount) { 181 #define MSG "sem_destroy on semaphore with waiters!\n" 182 write(2, MSG, sizeof(MSG) - 1); 183 #undef MSG 184 errno = EBUSY; 185 return (-1); 186 } 187 188 *semp = NULL; 189 if (sem->shared) 190 munmap(sem, SEM_MMAP_SIZE); 191 else 192 free(sem); 193 194 return (0); 195 } 196 197 int 198 sem_getvalue(sem_t *semp, int *sval) 199 { 200 sem_t sem; 201 202 if (!semp || !(sem = *semp)) { 203 errno = EINVAL; 204 return (-1); 205 } 206 207 _spinlock(&sem->lock); 208 *sval = sem->value; 209 _spinunlock(&sem->lock); 210 211 return (0); 212 } 213 214 int 215 sem_post(sem_t *semp) 216 { 217 sem_t sem; 218 219 if (!semp || !(sem = *semp)) { 220 errno = EINVAL; 221 return (-1); 222 } 223 224 _sem_post(sem); 225 226 return (0); 227 } 228 229 int 230 sem_wait(sem_t *semp) 231 { 232 pthread_t self = pthread_self(); 233 sem_t sem; 234 int r; 235 236 if (!semp || !(sem = *semp)) { 237 errno = EINVAL; 238 return (-1); 239 } 240 241 _enter_delayed_cancel(self); 242 r = _sem_wait(sem, 0, NULL, &self->delayed_cancel); 243 _leave_delayed_cancel(self, r); 244 245 if (r) { 246 errno = r; 247 return (-1); 248 } 249 250 return (0); 251 } 252 253 int 254 sem_timedwait(sem_t *semp, const struct timespec *abstime) 255 { 256 pthread_t self = pthread_self(); 257 sem_t sem; 258 int r; 259 260 if (!semp || !(sem = *semp)) { 261 errno = EINVAL; 262 return (-1); 263 } 264 265 _enter_delayed_cancel(self); 266 r = _sem_wait(sem, 0, abstime, &self->delayed_cancel); 267 _leave_delayed_cancel(self, r); 268 269 if (r) { 270 errno = r == EWOULDBLOCK ? ETIMEDOUT : r; 271 return (-1); 272 } 273 274 return (0); 275 } 276 277 int 278 sem_trywait(sem_t *semp) 279 { 280 sem_t sem; 281 int r; 282 283 if (!semp || !(sem = *semp)) { 284 errno = EINVAL; 285 return (-1); 286 } 287 288 r = _sem_wait(sem, 1, NULL, NULL); 289 290 if (r) { 291 errno = r; 292 return (-1); 293 } 294 295 return (0); 296 } 297 298 299 static void 300 makesempath(const char *origpath, char *sempath, size_t len) 301 { 302 char buf[SHA256_DIGEST_STRING_LENGTH]; 303 304 SHA256Data(origpath, strlen(origpath), buf); 305 snprintf(sempath, len, "/tmp/%s.sem", buf); 306 } 307 308 sem_t * 309 sem_open(const char *name, int oflag, ...) 310 { 311 char sempath[SEM_PATH_SIZE]; 312 struct stat sb; 313 sem_t sem, *semp; 314 unsigned int value = 0; 315 int created = 0, fd; 316 317 if (oflag & ~(O_CREAT | O_EXCL)) { 318 errno = EINVAL; 319 return (SEM_FAILED); 320 } 321 322 if (oflag & O_CREAT) { 323 va_list ap; 324 va_start(ap, oflag); 325 /* 3rd parameter mode is not used */ 326 va_arg(ap, mode_t); 327 value = va_arg(ap, unsigned); 328 va_end(ap); 329 330 if (value > SEM_VALUE_MAX) { 331 errno = EINVAL; 332 return (SEM_FAILED); 333 } 334 } 335 336 makesempath(name, sempath, sizeof(sempath)); 337 fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); 338 if (fd == -1) 339 return (SEM_FAILED); 340 if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { 341 close(fd); 342 errno = EINVAL; 343 return (SEM_FAILED); 344 } 345 if (sb.st_uid != getuid()) { 346 close(fd); 347 errno = EPERM; 348 return (SEM_FAILED); 349 } 350 if (sb.st_size != SEM_MMAP_SIZE) { 351 if (!(oflag & O_CREAT)) { 352 close(fd); 353 errno = EINVAL; 354 return (SEM_FAILED); 355 } 356 if (sb.st_size != 0) { 357 close(fd); 358 errno = EINVAL; 359 return (SEM_FAILED); 360 } 361 if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { 362 close(fd); 363 errno = EINVAL; 364 return (SEM_FAILED); 365 } 366 367 created = 1; 368 } 369 sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, 370 MAP_SHARED, fd, 0); 371 close(fd); 372 if (sem == MAP_FAILED) { 373 errno = EINVAL; 374 return (SEM_FAILED); 375 } 376 semp = malloc(sizeof(*semp)); 377 if (!semp) { 378 munmap(sem, SEM_MMAP_SIZE); 379 errno = ENOSPC; 380 return (SEM_FAILED); 381 } 382 if (created) { 383 sem->lock = _SPINLOCK_UNLOCKED_ASSIGN; 384 sem->value = value; 385 sem->shared = 1; 386 } 387 *semp = sem; 388 389 return (semp); 390 } 391 392 int 393 sem_close(sem_t *semp) 394 { 395 sem_t sem; 396 397 if (!semp || !(sem = *semp) || !sem->shared) { 398 errno = EINVAL; 399 return (-1); 400 } 401 402 *semp = NULL; 403 munmap(sem, SEM_MMAP_SIZE); 404 free(semp); 405 406 return (0); 407 } 408 409 int 410 sem_unlink(const char *name) 411 { 412 char sempath[SEM_PATH_SIZE]; 413 414 makesempath(name, sempath, sizeof(sempath)); 415 return (unlink(sempath)); 416 } 417