1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2024 Oxide Computer Company 26 */ 27 28 #include "lint.h" 29 #include "mtlib.h" 30 #include <sys/types.h> 31 #include <semaphore.h> 32 #include <synch.h> 33 #include <errno.h> 34 #include <stdarg.h> 35 #include <limits.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <sys/stat.h> 39 #include <sys/mman.h> 40 #include <unistd.h> 41 #include <thread.h> 42 #include "pos4obj.h" 43 #include "thr_uberdata.h" 44 45 typedef struct semaddr { 46 struct semaddr *sad_next; /* next in the link */ 47 char sad_name[PATH_MAX + 1]; /* name of sem object */ 48 sem_t *sad_addr; /* mmapped address of semaphore */ 49 ino64_t sad_inode; /* inode # of the mmapped file */ 50 } semaddr_t; 51 52 static long semvaluemax = 0; 53 static semaddr_t *semheadp = NULL; 54 static mutex_t semlock = DEFAULTMUTEX; 55 56 sem_t * 57 sem_open(const char *path, int oflag, /* mode_t mode, int value */ ...) 58 { 59 va_list ap; 60 mode_t crmode = 0; 61 sem_t *sem = NULL; 62 struct stat64 statbuf; 63 semaddr_t *next = NULL; 64 int fd = 0; 65 int error = 0; 66 int cr_flag = 0; 67 uint_t value = 0; 68 69 if (__pos4obj_check(path) == -1) 70 return (SEM_FAILED); 71 72 /* acquire semaphore lock to have atomic operation */ 73 if (__pos4obj_lock(path, SEM_LOCK_TYPE) < 0) 74 return (SEM_FAILED); 75 76 /* modify oflag to have RDWR and filter CREATE mode only */ 77 oflag = (oflag & (O_CREAT|O_EXCL)) | (O_RDWR); 78 if (oflag & O_CREAT) { 79 if (semvaluemax == 0 && 80 (semvaluemax = _sysconf(_SC_SEM_VALUE_MAX)) <= 0) 81 semvaluemax = -1; 82 va_start(ap, oflag); 83 crmode = va_arg(ap, mode_t); 84 value = va_arg(ap, uint_t); 85 va_end(ap); 86 /* check value < the max for a named semaphore */ 87 if (semvaluemax < 0 || 88 (ulong_t)value > (ulong_t)semvaluemax) { 89 errno = EINVAL; 90 goto out; 91 } 92 } 93 94 errno = 0; 95 96 if ((fd = __pos4obj_open(path, SEM_DATA_TYPE, 97 oflag, crmode, &cr_flag)) < 0) 98 goto out; 99 100 if (cr_flag) 101 cr_flag = DFILE_CREATE | DFILE_OPEN; 102 else 103 cr_flag = DFILE_OPEN; 104 105 /* find out inode # for the opened file */ 106 if (fstat64(fd, &statbuf) < 0) 107 goto out; 108 109 /* if created, acquire total_size in the file */ 110 if ((cr_flag & DFILE_CREATE) != 0) { 111 if (ftruncate64(fd, (off64_t)sizeof (sem_t)) < 0) 112 goto out; 113 } else { 114 /* 115 * if this semaphore has already been opened, inode 116 * will indicate then return the same semaphore address 117 */ 118 lmutex_lock(&semlock); 119 for (next = semheadp; next != NULL; next = next->sad_next) { 120 if (statbuf.st_ino == next->sad_inode && 121 strcmp(path, next->sad_name) == 0) { 122 (void) __close_nc(fd); 123 lmutex_unlock(&semlock); 124 (void) __pos4obj_unlock(path, SEM_LOCK_TYPE); 125 return (next->sad_addr); 126 } 127 } 128 lmutex_unlock(&semlock); 129 } 130 131 132 /* new sem descriptor to be allocated and new address to be mapped */ 133 if ((next = malloc(sizeof (semaddr_t))) == NULL) { 134 errno = ENOMEM; 135 goto out; 136 } 137 cr_flag |= ALLOC_MEM; 138 139 /* LINTED */ 140 sem = (sem_t *)mmap64(NULL, sizeof (sem_t), PROT_READ|PROT_WRITE, 141 MAP_SHARED, fd, (off64_t)0); 142 (void) __close_nc(fd); 143 cr_flag &= ~DFILE_OPEN; 144 if (sem == MAP_FAILED) 145 goto out; 146 cr_flag |= DFILE_MMAP; 147 148 /* if created, initialize */ 149 if (cr_flag & DFILE_CREATE) { 150 error = sema_init((sema_t *)sem, value, USYNC_PROCESS, 0); 151 if (error) { 152 errno = error; 153 goto out; 154 } 155 } 156 157 if (__pos4obj_unlock(path, SEM_LOCK_TYPE) == 0) { 158 /* add to the list pointed by semheadp */ 159 lmutex_lock(&semlock); 160 next->sad_next = semheadp; 161 semheadp = next; 162 next->sad_addr = sem; 163 next->sad_inode = statbuf.st_ino; 164 (void) strcpy(next->sad_name, path); 165 lmutex_unlock(&semlock); 166 return (sem); 167 } 168 /* fall into the error case */ 169 out: 170 error = errno; 171 if ((cr_flag & DFILE_OPEN) != 0) 172 (void) __close_nc(fd); 173 if ((cr_flag & DFILE_CREATE) != 0) 174 (void) __pos4obj_unlink(path, SEM_DATA_TYPE); 175 if ((cr_flag & ALLOC_MEM) != 0) 176 free(next); 177 if ((cr_flag & DFILE_MMAP) != 0) 178 (void) munmap((caddr_t)sem, sizeof (sem_t)); 179 (void) __pos4obj_unlock(path, SEM_LOCK_TYPE); 180 errno = error; 181 return (SEM_FAILED); 182 } 183 184 int 185 sem_close(sem_t *sem) 186 { 187 semaddr_t **next; 188 semaddr_t *freeit; 189 190 lmutex_lock(&semlock); 191 for (next = &semheadp; (freeit = *next) != NULL; 192 next = &(freeit->sad_next)) { 193 if (freeit->sad_addr == sem) { 194 *next = freeit->sad_next; 195 lmutex_unlock(&semlock); 196 free(freeit); 197 return (munmap((caddr_t)sem, sizeof (sem_t))); 198 } 199 } 200 lmutex_unlock(&semlock); 201 errno = EINVAL; 202 return (-1); 203 } 204 205 int 206 sem_unlink(const char *path) 207 { 208 int error; 209 int oerrno; 210 211 if (__pos4obj_check(path) < 0) 212 return (-1); 213 214 if (__pos4obj_lock(path, SEM_LOCK_TYPE) < 0) 215 return (-1); 216 217 error = __pos4obj_unlink(path, SEM_DATA_TYPE); 218 219 oerrno = errno; 220 221 (void) __pos4obj_unlock(path, SEM_LOCK_TYPE); 222 223 errno = oerrno; 224 225 return (error); 226 } 227 228 /* 229 * SUSV3 requires ("shall fail") an EINVAL failure for operations 230 * on invalid semaphores, including uninitialized unnamed semaphores. 231 * The best we can do is check that the magic number is correct. 232 * This is not perfect, but it allows the test suite to pass. 233 * (Standards bodies are filled with fools and idiots.) 234 */ 235 static int 236 sem_invalid(sem_t *sem) 237 { 238 if (sem->sem_magic != SEMA_MAGIC) { 239 errno = EINVAL; 240 return (-1); 241 } 242 return (0); 243 } 244 245 int 246 sem_init(sem_t *sem, int pshared, uint_t value) 247 { 248 int error; 249 250 if ((error = sema_init((sema_t *)sem, value, 251 pshared ? USYNC_PROCESS : USYNC_THREAD, NULL)) != 0) { 252 errno = error; 253 return (-1); 254 } 255 return (0); 256 } 257 258 int 259 sem_destroy(sem_t *sem) 260 { 261 int error; 262 263 if (sem_invalid(sem)) 264 return (-1); 265 if ((error = sema_destroy((sema_t *)sem)) != 0) { 266 errno = error; 267 return (-1); 268 } 269 return (0); 270 } 271 272 int 273 sem_post(sem_t *sem) 274 { 275 int error; 276 277 if (sem_invalid(sem)) 278 return (-1); 279 if ((error = sema_post((sema_t *)sem)) != 0) { 280 errno = error; 281 return (-1); 282 } 283 return (0); 284 } 285 286 int 287 sem_wait(sem_t *sem) 288 { 289 int error; 290 291 if (sem_invalid(sem)) 292 return (-1); 293 if ((error = sema_wait((sema_t *)sem)) != 0) { 294 errno = error; 295 return (-1); 296 } 297 return (0); 298 } 299 300 int 301 sem_clockwait(sem_t *sem, clockid_t clock, const timespec_t *abstime) 302 { 303 int error; 304 305 if (sem_invalid(sem)) 306 return (-1); 307 308 if ((error = sema_clockwait((sema_t *)sem, clock, abstime)) != 0) { 309 if (error == ETIME) 310 error = ETIMEDOUT; 311 errno = error; 312 return (-1); 313 } 314 return (0); 315 } 316 317 int 318 sem_timedwait(sem_t *sem, const timespec_t *abstime) 319 { 320 return (sem_clockwait(sem, CLOCK_REALTIME, abstime)); 321 } 322 323 int 324 sem_relclockwait_np(sem_t *sem, clockid_t clock, const timespec_t *reltime) 325 { 326 int error; 327 328 if (sem_invalid(sem)) 329 return (-1); 330 331 if ((error = sema_relclockwait((sema_t *)sem, clock, reltime)) != 0) { 332 if (error == ETIME) 333 error = ETIMEDOUT; 334 errno = error; 335 return (-1); 336 } 337 return (0); 338 } 339 340 int 341 sem_reltimedwait_np(sem_t *sem, const timespec_t *reltime) 342 { 343 return (sem_relclockwait_np(sem, CLOCK_REALTIME, reltime)); 344 } 345 346 int 347 sem_trywait(sem_t *sem) 348 { 349 int error; 350 351 if (sem_invalid(sem)) 352 return (-1); 353 if ((error = sema_trywait((sema_t *)sem)) != 0) { 354 if (error == EBUSY) 355 error = EAGAIN; 356 errno = error; 357 return (-1); 358 } 359 return (0); 360 } 361 362 int 363 sem_getvalue(sem_t *sem, int *sval) 364 { 365 if (sem_invalid(sem)) 366 return (-1); 367 *sval = (int)sem->sem_count; 368 return (0); 369 } 370