1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice(s), this list of conditions and the following disclaimer as 12 * the first lines of this file unmodified other than the possible 13 * addition of one or more copyright notices. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice(s), this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 26 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 28 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 #include "namespace.h" 35 #include <sys/types.h> 36 #include <sys/queue.h> 37 #include <sys/mman.h> 38 #include <sys/stat.h> 39 #include <errno.h> 40 #include <machine/atomic.h> 41 #include <sys/umtx.h> 42 #include <limits.h> 43 #include <fcntl.h> 44 #include <pthread.h> 45 #include <stdarg.h> 46 #include <stdbool.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <time.h> 50 #include <semaphore.h> 51 #include <unistd.h> 52 #include "un-namespace.h" 53 #include "libc_private.h" 54 55 __weak_reference(_sem_close, sem_close); 56 __weak_reference(_sem_destroy, sem_destroy); 57 __weak_reference(_sem_getvalue, sem_getvalue); 58 __weak_reference(_sem_init, sem_init); 59 __weak_reference(_sem_open, sem_open); 60 __weak_reference(_sem_post, sem_post); 61 __weak_reference(_sem_timedwait, sem_timedwait); 62 __weak_reference(_sem_clockwait_np, sem_clockwait_np); 63 __weak_reference(_sem_trywait, sem_trywait); 64 __weak_reference(_sem_unlink, sem_unlink); 65 __weak_reference(_sem_wait, sem_wait); 66 67 #define SEM_PREFIX "/tmp/SEMD" 68 #define SEM_MAGIC ((u_int32_t)0x73656d32) 69 70 _Static_assert(SEM_VALUE_MAX <= USEM_MAX_COUNT, "SEM_VALUE_MAX too large"); 71 72 struct sem_nameinfo { 73 int open_count; 74 char *name; 75 dev_t dev; 76 ino_t ino; 77 sem_t *sem; 78 LIST_ENTRY(sem_nameinfo) next; 79 }; 80 81 static pthread_once_t once = PTHREAD_ONCE_INIT; 82 static pthread_mutex_t sem_llock; 83 static LIST_HEAD(, sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 84 85 static void 86 sem_prefork(void) 87 { 88 89 _pthread_mutex_lock(&sem_llock); 90 } 91 92 static void 93 sem_postfork(void) 94 { 95 96 _pthread_mutex_unlock(&sem_llock); 97 } 98 99 static void 100 sem_child_postfork(void) 101 { 102 103 _pthread_mutex_unlock(&sem_llock); 104 } 105 106 static void 107 sem_module_init(void) 108 { 109 110 _pthread_mutex_init(&sem_llock, NULL); 111 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 112 } 113 114 static inline int 115 sem_check_validity(sem_t *sem) 116 { 117 118 if (sem->_magic == SEM_MAGIC) 119 return (0); 120 errno = EINVAL; 121 return (-1); 122 } 123 124 int 125 _sem_init(sem_t *sem, int pshared, unsigned int value) 126 { 127 128 if (value > SEM_VALUE_MAX) { 129 errno = EINVAL; 130 return (-1); 131 } 132 133 bzero(sem, sizeof(sem_t)); 134 sem->_magic = SEM_MAGIC; 135 sem->_kern._count = (u_int32_t)value; 136 sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 137 return (0); 138 } 139 140 sem_t * 141 _sem_open(const char *name, int flags, ...) 142 { 143 char path[PATH_MAX]; 144 struct stat sb; 145 va_list ap; 146 struct sem_nameinfo *ni; 147 sem_t *sem, tmp; 148 int errsave, fd, len, mode, value; 149 150 ni = NULL; 151 sem = NULL; 152 fd = -1; 153 value = 0; 154 155 if (name[0] != '/') { 156 errno = EINVAL; 157 return (SEM_FAILED); 158 } 159 name++; 160 strcpy(path, SEM_PREFIX); 161 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 162 errno = ENAMETOOLONG; 163 return (SEM_FAILED); 164 } 165 if (flags & ~(O_CREAT|O_EXCL)) { 166 errno = EINVAL; 167 return (SEM_FAILED); 168 } 169 if ((flags & O_CREAT) != 0) { 170 va_start(ap, flags); 171 mode = va_arg(ap, int); 172 value = va_arg(ap, int); 173 va_end(ap); 174 } 175 fd = -1; 176 _pthread_once(&once, sem_module_init); 177 178 _pthread_mutex_lock(&sem_llock); 179 LIST_FOREACH(ni, &sem_list, next) { 180 if (ni->name != NULL && strcmp(name, ni->name) == 0) { 181 fd = _open(path, flags | O_RDWR | O_CLOEXEC | 182 O_EXLOCK, mode); 183 if (fd == -1 || _fstat(fd, &sb) == -1) { 184 ni = NULL; 185 goto error; 186 } 187 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 188 O_EXCL) || ni->dev != sb.st_dev || 189 ni->ino != sb.st_ino) { 190 ni->name = NULL; 191 ni = NULL; 192 break; 193 } 194 ni->open_count++; 195 sem = ni->sem; 196 _pthread_mutex_unlock(&sem_llock); 197 _close(fd); 198 return (sem); 199 } 200 } 201 202 len = sizeof(*ni) + strlen(name) + 1; 203 ni = (struct sem_nameinfo *)malloc(len); 204 if (ni == NULL) { 205 errno = ENOSPC; 206 goto error; 207 } 208 209 ni->name = (char *)(ni+1); 210 strcpy(ni->name, name); 211 212 if (fd == -1) { 213 fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 214 if (fd == -1 || _fstat(fd, &sb) == -1) 215 goto error; 216 } 217 if (sb.st_size < sizeof(sem_t)) { 218 tmp._magic = SEM_MAGIC; 219 tmp._kern._count = value; 220 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 221 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 222 goto error; 223 } 224 flock(fd, LOCK_UN); 225 sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, 226 MAP_SHARED | MAP_NOSYNC, fd, 0); 227 if (sem == MAP_FAILED) { 228 sem = NULL; 229 if (errno == ENOMEM) 230 errno = ENOSPC; 231 goto error; 232 } 233 if (sem->_magic != SEM_MAGIC) { 234 errno = EINVAL; 235 goto error; 236 } 237 ni->open_count = 1; 238 ni->sem = sem; 239 ni->dev = sb.st_dev; 240 ni->ino = sb.st_ino; 241 LIST_INSERT_HEAD(&sem_list, ni, next); 242 _close(fd); 243 _pthread_mutex_unlock(&sem_llock); 244 return (sem); 245 246 error: 247 errsave = errno; 248 if (fd != -1) 249 _close(fd); 250 if (sem != NULL) 251 munmap(sem, sizeof(sem_t)); 252 free(ni); 253 _pthread_mutex_unlock(&sem_llock); 254 errno = errsave; 255 return (SEM_FAILED); 256 } 257 258 int 259 _sem_close(sem_t *sem) 260 { 261 struct sem_nameinfo *ni; 262 bool last; 263 264 if (sem_check_validity(sem) != 0) 265 return (-1); 266 267 if (!(sem->_kern._flags & SEM_NAMED)) { 268 errno = EINVAL; 269 return (-1); 270 } 271 272 _pthread_once(&once, sem_module_init); 273 274 _pthread_mutex_lock(&sem_llock); 275 LIST_FOREACH(ni, &sem_list, next) { 276 if (sem == ni->sem) { 277 last = --ni->open_count == 0; 278 if (last) 279 LIST_REMOVE(ni, next); 280 _pthread_mutex_unlock(&sem_llock); 281 if (last) { 282 munmap(sem, sizeof(*sem)); 283 free(ni); 284 } 285 return (0); 286 } 287 } 288 _pthread_mutex_unlock(&sem_llock); 289 errno = EINVAL; 290 return (-1); 291 } 292 293 int 294 _sem_unlink(const char *name) 295 { 296 char path[PATH_MAX]; 297 298 if (name[0] != '/') { 299 errno = ENOENT; 300 return -1; 301 } 302 name++; 303 strcpy(path, SEM_PREFIX); 304 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 305 errno = ENAMETOOLONG; 306 return (-1); 307 } 308 309 return (unlink(path)); 310 } 311 312 int 313 _sem_destroy(sem_t *sem) 314 { 315 316 if (sem_check_validity(sem) != 0) 317 return (-1); 318 319 if (sem->_kern._flags & SEM_NAMED) { 320 errno = EINVAL; 321 return (-1); 322 } 323 sem->_magic = 0; 324 return (0); 325 } 326 327 int 328 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 329 { 330 331 if (sem_check_validity(sem) != 0) 332 return (-1); 333 334 *sval = (int)USEM_COUNT(sem->_kern._count); 335 return (0); 336 } 337 338 static __inline int 339 usem_wake(struct _usem2 *sem) 340 { 341 342 return (_umtx_op(sem, UMTX_OP_SEM2_WAKE, 0, NULL, NULL)); 343 } 344 345 static __inline int 346 usem_wait(struct _usem2 *sem, clockid_t clock_id, int flags, 347 const struct timespec *rqtp, struct timespec *rmtp) 348 { 349 struct { 350 struct _umtx_time timeout; 351 struct timespec remain; 352 } tms; 353 void *tm_p; 354 size_t tm_size; 355 int retval; 356 357 if (rqtp == NULL) { 358 tm_p = NULL; 359 tm_size = 0; 360 } else { 361 tms.timeout._clockid = clock_id; 362 tms.timeout._flags = (flags & TIMER_ABSTIME) ? UMTX_ABSTIME : 0; 363 tms.timeout._timeout = *rqtp; 364 tm_p = &tms; 365 tm_size = sizeof(tms); 366 } 367 retval = _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0, (void *)tm_size, tm_p); 368 if (retval == -1 && errno == EINTR && (flags & TIMER_ABSTIME) == 0 && 369 rqtp != NULL && rmtp != NULL) { 370 *rmtp = tms.remain; 371 } 372 373 return (retval); 374 } 375 376 int 377 _sem_trywait(sem_t *sem) 378 { 379 int val; 380 381 if (sem_check_validity(sem) != 0) 382 return (-1); 383 384 while (USEM_COUNT(val = sem->_kern._count) > 0) { 385 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 386 return (0); 387 } 388 errno = EAGAIN; 389 return (-1); 390 } 391 392 int 393 _sem_clockwait_np(sem_t * __restrict sem, clockid_t clock_id, int flags, 394 const struct timespec *rqtp, struct timespec *rmtp) 395 { 396 int val, retval; 397 398 if (sem_check_validity(sem) != 0) 399 return (-1); 400 401 retval = 0; 402 _pthread_testcancel(); 403 for (;;) { 404 while (USEM_COUNT(val = sem->_kern._count) > 0) { 405 if (atomic_cmpset_acq_int(&sem->_kern._count, val, 406 val - 1)) 407 return (0); 408 } 409 410 if (retval) { 411 _pthread_testcancel(); 412 break; 413 } 414 415 /* 416 * The timeout argument is only supposed to 417 * be checked if the thread would have blocked. 418 */ 419 if (rqtp != NULL) { 420 if (rqtp->tv_nsec >= 1000000000 || rqtp->tv_nsec < 0) { 421 errno = EINVAL; 422 return (-1); 423 } 424 } 425 _pthread_cancel_enter(1); 426 retval = usem_wait(&sem->_kern, clock_id, flags, rqtp, rmtp); 427 _pthread_cancel_leave(0); 428 } 429 return (retval); 430 } 431 432 int 433 _sem_timedwait(sem_t * __restrict sem, 434 const struct timespec * __restrict abstime) 435 { 436 437 return (_sem_clockwait_np(sem, CLOCK_REALTIME, TIMER_ABSTIME, abstime, 438 NULL)); 439 }; 440 441 int 442 _sem_wait(sem_t *sem) 443 { 444 445 return (_sem_timedwait(sem, NULL)); 446 } 447 448 /* 449 * POSIX: 450 * The sem_post() interface is reentrant with respect to signals and may be 451 * invoked from a signal-catching function. 452 * The implementation does not use lock, so it should be safe. 453 */ 454 int 455 _sem_post(sem_t *sem) 456 { 457 unsigned int count; 458 459 if (sem_check_validity(sem) != 0) 460 return (-1); 461 462 do { 463 count = sem->_kern._count; 464 if (USEM_COUNT(count) + 1 > SEM_VALUE_MAX) { 465 errno = EOVERFLOW; 466 return (-1); 467 } 468 } while (!atomic_cmpset_rel_int(&sem->_kern._count, count, count + 1)); 469 if (count & USEM_HAS_WAITERS) 470 usem_wake(&sem->_kern); 471 return (0); 472 } 473