xref: /illumos-gate/usr/src/lib/libc/port/rt/sem.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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