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