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 */ 26 27 #include "lint.h" 28 #include "mtlib.h" 29 #include <sys/types.h> 30 #include <semaphore.h> 31 #include <synch.h> 32 #include <errno.h> 33 #include <stdarg.h> 34 #include <limits.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sys/stat.h> 38 #include <sys/mman.h> 39 #include <unistd.h> 40 #include <thread.h> 41 #include "pos4obj.h" 42 43 typedef struct semaddr { 44 struct semaddr *sad_next; /* next in the link */ 45 char sad_name[PATH_MAX + 1]; /* name of sem object */ 46 sem_t *sad_addr; /* mmapped address of semaphore */ 47 ino64_t sad_inode; /* inode # of the mmapped file */ 48 } semaddr_t; 49 50 static long semvaluemax = 0; 51 static semaddr_t *semheadp = NULL; 52 static mutex_t semlock = DEFAULTMUTEX; 53 54 sem_t * 55 sem_open(const char *path, int oflag, /* mode_t mode, int value */ ...) 56 { 57 va_list ap; 58 mode_t crmode = 0; 59 sem_t *sem = NULL; 60 struct stat64 statbuf; 61 semaddr_t *next = NULL; 62 int fd = 0; 63 int error = 0; 64 int cr_flag = 0; 65 uint_t value = 0; 66 67 if (__pos4obj_check(path) == -1) 68 return (SEM_FAILED); 69 70 /* acquire semaphore lock to have atomic operation */ 71 if (__pos4obj_lock(path, SEM_LOCK_TYPE) < 0) 72 return (SEM_FAILED); 73 74 /* modify oflag to have RDWR and filter CREATE mode only */ 75 oflag = (oflag & (O_CREAT|O_EXCL)) | (O_RDWR); 76 if (oflag & O_CREAT) { 77 if (semvaluemax == 0 && 78 (semvaluemax = _sysconf(_SC_SEM_VALUE_MAX)) <= 0) 79 semvaluemax = -1; 80 va_start(ap, oflag); 81 crmode = va_arg(ap, mode_t); 82 value = va_arg(ap, uint_t); 83 va_end(ap); 84 /* check value < the max for a named semaphore */ 85 if (semvaluemax < 0 || 86 (ulong_t)value > (ulong_t)semvaluemax) { 87 errno = EINVAL; 88 goto out; 89 } 90 } 91 92 errno = 0; 93 94 if ((fd = __pos4obj_open(path, SEM_DATA_TYPE, 95 oflag, crmode, &cr_flag)) < 0) 96 goto out; 97 98 if (cr_flag) 99 cr_flag = DFILE_CREATE | DFILE_OPEN; 100 else 101 cr_flag = DFILE_OPEN; 102 103 /* find out inode # for the opened file */ 104 if (fstat64(fd, &statbuf) < 0) 105 goto out; 106 107 /* if created, acquire total_size in the file */ 108 if ((cr_flag & DFILE_CREATE) != 0) { 109 if (ftruncate64(fd, (off64_t)sizeof (sem_t)) < 0) 110 goto out; 111 } else { 112 /* 113 * if this semaphore has already been opened, inode 114 * will indicate then return the same semaphore address 115 */ 116 lmutex_lock(&semlock); 117 for (next = semheadp; next != NULL; next = next->sad_next) { 118 if (statbuf.st_ino == next->sad_inode && 119 strcmp(path, next->sad_name) == 0) { 120 (void) __close_nc(fd); 121 lmutex_unlock(&semlock); 122 (void) __pos4obj_unlock(path, SEM_LOCK_TYPE); 123 return (next->sad_addr); 124 } 125 } 126 lmutex_unlock(&semlock); 127 } 128 129 130 /* new sem descriptor to be allocated and new address to be mapped */ 131 if ((next = malloc(sizeof (semaddr_t))) == NULL) { 132 errno = ENOMEM; 133 goto out; 134 } 135 cr_flag |= ALLOC_MEM; 136 137 /* LINTED */ 138 sem = (sem_t *)mmap64(NULL, sizeof (sem_t), PROT_READ|PROT_WRITE, 139 MAP_SHARED, fd, (off64_t)0); 140 (void) __close_nc(fd); 141 cr_flag &= ~DFILE_OPEN; 142 if (sem == MAP_FAILED) 143 goto out; 144 cr_flag |= DFILE_MMAP; 145 146 /* if created, initialize */ 147 if (cr_flag & DFILE_CREATE) { 148 error = sema_init((sema_t *)sem, value, USYNC_PROCESS, 0); 149 if (error) { 150 errno = error; 151 goto out; 152 } 153 } 154 155 if (__pos4obj_unlock(path, SEM_LOCK_TYPE) == 0) { 156 /* add to the list pointed by semheadp */ 157 lmutex_lock(&semlock); 158 next->sad_next = semheadp; 159 semheadp = next; 160 next->sad_addr = sem; 161 next->sad_inode = statbuf.st_ino; 162 (void) strcpy(next->sad_name, path); 163 lmutex_unlock(&semlock); 164 return (sem); 165 } 166 /* fall into the error case */ 167 out: 168 error = errno; 169 if ((cr_flag & DFILE_OPEN) != 0) 170 (void) __close_nc(fd); 171 if ((cr_flag & DFILE_CREATE) != 0) 172 (void) __pos4obj_unlink(path, SEM_DATA_TYPE); 173 if ((cr_flag & ALLOC_MEM) != 0) 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_timedwait(sem_t *sem, const timespec_t *abstime) 300 { 301 int error; 302 303 if (sem_invalid(sem)) 304 return (-1); 305 if ((error = sema_timedwait((sema_t *)sem, abstime)) != 0) { 306 if (error == ETIME) 307 error = ETIMEDOUT; 308 errno = error; 309 return (-1); 310 } 311 return (0); 312 } 313 314 int 315 sem_reltimedwait_np(sem_t *sem, const timespec_t *reltime) 316 { 317 int error; 318 319 if (sem_invalid(sem)) 320 return (-1); 321 if ((error = sema_reltimedwait((sema_t *)sem, reltime)) != 0) { 322 if (error == ETIME) 323 error = ETIMEDOUT; 324 errno = error; 325 return (-1); 326 } 327 return (0); 328 } 329 330 int 331 sem_trywait(sem_t *sem) 332 { 333 int error; 334 335 if (sem_invalid(sem)) 336 return (-1); 337 if ((error = sema_trywait((sema_t *)sem)) != 0) { 338 if (error == EBUSY) 339 error = EAGAIN; 340 errno = error; 341 return (-1); 342 } 343 return (0); 344 } 345 346 int 347 sem_getvalue(sem_t *sem, int *sval) 348 { 349 if (sem_invalid(sem)) 350 return (-1); 351 *sval = (int)sem->sem_count; 352 return (0); 353 } 354