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