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