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