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 *
sem_open(const char * path,int oflag,...)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
sem_close(sem_t * sem)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
sem_unlink(const char * path)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
sem_invalid(sem_t * sem)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
sem_init(sem_t * sem,int pshared,uint_t value)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
sem_destroy(sem_t * sem)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
sem_post(sem_t * sem)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
sem_wait(sem_t * sem)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
sem_clockwait(sem_t * sem,clockid_t clock,const timespec_t * abstime)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
sem_timedwait(sem_t * sem,const timespec_t * abstime)318 sem_timedwait(sem_t *sem, const timespec_t *abstime)
319 {
320 return (sem_clockwait(sem, CLOCK_REALTIME, abstime));
321 }
322
323 int
sem_relclockwait_np(sem_t * sem,clockid_t clock,const timespec_t * reltime)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
sem_reltimedwait_np(sem_t * sem,const timespec_t * reltime)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
sem_trywait(sem_t * sem)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
sem_getvalue(sem_t * sem,int * sval)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