xref: /openbsd-src/lib/librthread/rthread_sem.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: rthread_sem.c,v 1.19 2014/06/27 23:21:47 matthew Exp $ */
2 /*
3  * Copyright (c) 2004,2005,2013 Ted Unangst <tedu@openbsd.org>
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include <sys/param.h>
19 #include <sys/types.h>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <sha2.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include <pthread.h>
33 
34 #include "rthread.h"
35 
36 #define SHARED_IDENT ((void *)-1)
37 
38 /* SHA256_DIGEST_STRING_LENGTH includes nul */
39 /* "/tmp/" + sha256 + ".sem" */
40 #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4)
41 
42 /* long enough to be hard to guess */
43 #define SEM_RANDOM_NAME_LEN	10
44 
45 /*
46  * Size of memory to be mmap()'ed by named semaphores.
47  * Should be >= SEM_PATH_SIZE and page-aligned.
48  */
49 #define SEM_MMAP_SIZE	getpagesize()
50 
51 /*
52  * Internal implementation of semaphores
53  */
54 int
55 _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime,
56     int *delayed_cancel)
57 {
58 	void *ident = (void *)&sem->waitcount;
59 	int r;
60 
61 	if (sem->shared)
62 		ident = SHARED_IDENT;
63 
64 	_spinlock(&sem->lock);
65 	if (sem->value) {
66 		sem->value--;
67 		r = 0;
68 	} else if (tryonly) {
69 		r = EAGAIN;
70 	} else {
71 		sem->waitcount++;
72 		do {
73 			r = __thrsleep(ident, CLOCK_REALTIME |
74 			    _USING_TICKETS, abstime, &sem->lock.ticket,
75 			    delayed_cancel);
76 			_spinlock(&sem->lock);
77 			/* ignore interruptions other than cancelation */
78 			if (r == EINTR && (delayed_cancel == NULL ||
79 			    *delayed_cancel == 0))
80 				r = 0;
81 		} while (r == 0 && sem->value == 0);
82 		sem->waitcount--;
83 		if (r == 0)
84 			sem->value--;
85 	}
86 	_spinunlock(&sem->lock);
87 	return (r);
88 }
89 
90 /* always increment count */
91 int
92 _sem_post(sem_t sem)
93 {
94 	void *ident = (void *)&sem->waitcount;
95 	int rv = 0;
96 
97 	if (sem->shared)
98 		ident = SHARED_IDENT;
99 
100 	_spinlock(&sem->lock);
101 	sem->value++;
102 	if (sem->waitcount) {
103 		__thrwakeup(ident, 1);
104 		rv = 1;
105 	}
106 	_spinunlock(&sem->lock);
107 	return (rv);
108 }
109 
110 /*
111  * exported semaphores
112  */
113 int
114 sem_init(sem_t *semp, int pshared, unsigned int value)
115 {
116 	sem_t sem;
117 
118 	if (value > SEM_VALUE_MAX) {
119 		errno = EINVAL;
120 		return (-1);
121 	}
122 
123 	if (pshared) {
124 		errno = EPERM;
125 		return (-1);
126 #ifdef notyet
127 		char name[SEM_RANDOM_NAME_LEN];
128 		sem_t *sempshared;
129 		int i;
130 
131 		for (;;) {
132 			for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++)
133 				name[i] = arc4random_uniform(255) + 1;
134 			name[SEM_RANDOM_NAME_LEN - 1] = '\0';
135 			sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value);
136 			if (sempshared != SEM_FAILED)
137 				break;
138 			if (errno == EEXIST)
139 				continue;
140 			if (errno != EPERM)
141 				errno = ENOSPC;
142 			return (-1);
143 		}
144 
145 		/* unnamed semaphore should not be opened twice */
146 		if (sem_unlink(name) == -1) {
147 			sem_close(sempshared);
148 			errno = ENOSPC;
149 			return (-1);
150 		}
151 
152 		*semp = *sempshared;
153 		free(sempshared);
154 		return (0);
155 #endif
156 	}
157 
158 	sem = calloc(1, sizeof(*sem));
159 	if (!sem) {
160 		errno = ENOSPC;
161 		return (-1);
162 	}
163 	sem->lock = _SPINLOCK_UNLOCKED_ASSIGN;
164 	sem->value = value;
165 	*semp = sem;
166 
167 	return (0);
168 }
169 
170 int
171 sem_destroy(sem_t *semp)
172 {
173 	sem_t sem;
174 
175 	if (!semp || !(sem = *semp)) {
176 		errno = EINVAL;
177 		return (-1);
178 	}
179 
180 	if (sem->waitcount) {
181 #define MSG "sem_destroy on semaphore with waiters!\n"
182 		write(2, MSG, sizeof(MSG) - 1);
183 #undef MSG
184 		errno = EBUSY;
185 		return (-1);
186 	}
187 
188 	*semp = NULL;
189 	if (sem->shared)
190 		munmap(sem, SEM_MMAP_SIZE);
191 	else
192 		free(sem);
193 
194 	return (0);
195 }
196 
197 int
198 sem_getvalue(sem_t *semp, int *sval)
199 {
200 	sem_t sem;
201 
202 	if (!semp || !(sem = *semp)) {
203 		errno = EINVAL;
204 		return (-1);
205 	}
206 
207 	_spinlock(&sem->lock);
208 	*sval = sem->value;
209 	_spinunlock(&sem->lock);
210 
211 	return (0);
212 }
213 
214 int
215 sem_post(sem_t *semp)
216 {
217 	sem_t sem;
218 
219 	if (!semp || !(sem = *semp)) {
220 		errno = EINVAL;
221 		return (-1);
222 	}
223 
224 	_sem_post(sem);
225 
226 	return (0);
227 }
228 
229 int
230 sem_wait(sem_t *semp)
231 {
232 	pthread_t self = pthread_self();
233 	sem_t sem;
234 	int r;
235 
236 	if (!semp || !(sem = *semp)) {
237 		errno = EINVAL;
238 		return (-1);
239 	}
240 
241 	_enter_delayed_cancel(self);
242 	r = _sem_wait(sem, 0, NULL, &self->delayed_cancel);
243 	_leave_delayed_cancel(self, r);
244 
245 	if (r) {
246 		errno = r;
247 		return (-1);
248 	}
249 
250 	return (0);
251 }
252 
253 int
254 sem_timedwait(sem_t *semp, const struct timespec *abstime)
255 {
256 	pthread_t self = pthread_self();
257 	sem_t sem;
258 	int r;
259 
260 	if (!semp || !(sem = *semp)) {
261 		errno = EINVAL;
262 		return (-1);
263 	}
264 
265 	_enter_delayed_cancel(self);
266 	r = _sem_wait(sem, 0, abstime, &self->delayed_cancel);
267 	_leave_delayed_cancel(self, r);
268 
269 	if (r) {
270 		errno = r == EWOULDBLOCK ? ETIMEDOUT : r;
271 		return (-1);
272 	}
273 
274 	return (0);
275 }
276 
277 int
278 sem_trywait(sem_t *semp)
279 {
280 	sem_t sem;
281 	int r;
282 
283 	if (!semp || !(sem = *semp)) {
284 		errno = EINVAL;
285 		return (-1);
286 	}
287 
288 	r = _sem_wait(sem, 1, NULL, NULL);
289 
290 	if (r) {
291 		errno = r;
292 		return (-1);
293 	}
294 
295 	return (0);
296 }
297 
298 
299 static void
300 makesempath(const char *origpath, char *sempath, size_t len)
301 {
302 	char buf[SHA256_DIGEST_STRING_LENGTH];
303 
304 	SHA256Data(origpath, strlen(origpath), buf);
305 	snprintf(sempath, len, "/tmp/%s.sem", buf);
306 }
307 
308 sem_t *
309 sem_open(const char *name, int oflag, ...)
310 {
311 	char sempath[SEM_PATH_SIZE];
312 	struct stat sb;
313 	sem_t sem, *semp;
314 	unsigned int value = 0;
315 	int created = 0, fd;
316 
317 	if (oflag & ~(O_CREAT | O_EXCL)) {
318 		errno = EINVAL;
319 		return (SEM_FAILED);
320 	}
321 
322 	if (oflag & O_CREAT) {
323 		va_list ap;
324 		va_start(ap, oflag);
325 		/* 3rd parameter mode is not used */
326 		va_arg(ap, mode_t);
327 		value = va_arg(ap, unsigned);
328 		va_end(ap);
329 
330 		if (value > SEM_VALUE_MAX) {
331 			errno = EINVAL;
332 			return (SEM_FAILED);
333 		}
334 	}
335 
336 	makesempath(name, sempath, sizeof(sempath));
337 	fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600);
338 	if (fd == -1)
339 		return (SEM_FAILED);
340 	if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
341 		close(fd);
342 		errno = EINVAL;
343 		return (SEM_FAILED);
344 	}
345 	if (sb.st_uid != getuid()) {
346 		close(fd);
347 		errno = EPERM;
348 		return (SEM_FAILED);
349 	}
350 	if (sb.st_size != SEM_MMAP_SIZE) {
351 		if (!(oflag & O_CREAT)) {
352 			close(fd);
353 			errno = EINVAL;
354 			return (SEM_FAILED);
355 		}
356 		if (sb.st_size != 0) {
357 			close(fd);
358 			errno = EINVAL;
359 			return (SEM_FAILED);
360 		}
361 		if (ftruncate(fd, SEM_MMAP_SIZE) == -1) {
362 			close(fd);
363 			errno = EINVAL;
364 			return (SEM_FAILED);
365 		}
366 
367 		created = 1;
368 	}
369 	sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE,
370 	    MAP_SHARED, fd, 0);
371 	close(fd);
372 	if (sem == MAP_FAILED) {
373 		errno = EINVAL;
374 		return (SEM_FAILED);
375 	}
376 	semp = malloc(sizeof(*semp));
377 	if (!semp) {
378 		munmap(sem, SEM_MMAP_SIZE);
379 		errno = ENOSPC;
380 		return (SEM_FAILED);
381 	}
382 	if (created) {
383 		sem->lock = _SPINLOCK_UNLOCKED_ASSIGN;
384 		sem->value = value;
385 		sem->shared = 1;
386 	}
387 	*semp = sem;
388 
389 	return (semp);
390 }
391 
392 int
393 sem_close(sem_t *semp)
394 {
395 	sem_t sem;
396 
397 	if (!semp || !(sem = *semp) || !sem->shared) {
398 		errno = EINVAL;
399 		return (-1);
400 	}
401 
402 	*semp = NULL;
403 	munmap(sem, SEM_MMAP_SIZE);
404 	free(semp);
405 
406 	return (0);
407 }
408 
409 int
410 sem_unlink(const char *name)
411 {
412 	char sempath[SEM_PATH_SIZE];
413 
414 	makesempath(name, sempath, sizeof(sempath));
415 	return (unlink(sempath));
416 }
417