xref: /openbsd-src/lib/librthread/rthread_sem_compat.c (revision c554a768c1617ab8d979dfdbb7ffdf8827e11e8e)
1*c554a768Scheloha /*	$OpenBSD: rthread_sem_compat.c,v 1.2 2022/05/14 14:52:20 cheloha Exp $ */
24b11781bSpirofti /*
34b11781bSpirofti  * Copyright (c) 2004,2005,2013 Ted Unangst <tedu@openbsd.org>
44b11781bSpirofti  * All Rights Reserved.
54b11781bSpirofti  *
64b11781bSpirofti  * Permission to use, copy, modify, and distribute this software for any
74b11781bSpirofti  * purpose with or without fee is hereby granted, provided that the above
84b11781bSpirofti  * copyright notice and this permission notice appear in all copies.
94b11781bSpirofti  *
104b11781bSpirofti  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114b11781bSpirofti  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124b11781bSpirofti  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134b11781bSpirofti  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144b11781bSpirofti  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154b11781bSpirofti  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164b11781bSpirofti  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174b11781bSpirofti  */
184b11781bSpirofti 
194b11781bSpirofti #include <sys/types.h>
204b11781bSpirofti #include <sys/mman.h>
214b11781bSpirofti #include <sys/stat.h>
22*c554a768Scheloha #include <sys/time.h>
234b11781bSpirofti 
244b11781bSpirofti #include <errno.h>
254b11781bSpirofti #include <fcntl.h>
264b11781bSpirofti #include <sha2.h>
274b11781bSpirofti #include <stdarg.h>
284b11781bSpirofti #include <stdlib.h>
294b11781bSpirofti #include <stdio.h>
304b11781bSpirofti #include <string.h>
314b11781bSpirofti #include <unistd.h>
324b11781bSpirofti 
334b11781bSpirofti #include <pthread.h>
344b11781bSpirofti 
354b11781bSpirofti #include "rthread.h"
364b11781bSpirofti #include "cancel.h"		/* in libc/include */
374b11781bSpirofti 
384b11781bSpirofti #define SHARED_IDENT ((void *)-1)
394b11781bSpirofti 
404b11781bSpirofti /* SHA256_DIGEST_STRING_LENGTH includes nul */
414b11781bSpirofti /* "/tmp/" + sha256 + ".sem" */
424b11781bSpirofti #define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4)
434b11781bSpirofti 
444b11781bSpirofti /* long enough to be hard to guess */
454b11781bSpirofti #define SEM_RANDOM_NAME_LEN	10
464b11781bSpirofti 
474b11781bSpirofti /*
484b11781bSpirofti  * Size of memory to be mmap()'ed by named semaphores.
494b11781bSpirofti  * Should be >= SEM_PATH_SIZE and page-aligned.
504b11781bSpirofti  */
514b11781bSpirofti #define SEM_MMAP_SIZE	_thread_pagesize
524b11781bSpirofti 
534b11781bSpirofti /*
544b11781bSpirofti  * Internal implementation of semaphores
554b11781bSpirofti  */
564b11781bSpirofti int
_sem_wait(sem_t sem,int can_eintr,const struct timespec * abstime,int * delayed_cancel)574b11781bSpirofti _sem_wait(sem_t sem, int can_eintr, const struct timespec *abstime,
584b11781bSpirofti     int *delayed_cancel)
594b11781bSpirofti {
604b11781bSpirofti 	void *ident = (void *)&sem->waitcount;
614b11781bSpirofti 	int r;
624b11781bSpirofti 
634b11781bSpirofti 	if (sem->shared)
644b11781bSpirofti 		ident = SHARED_IDENT;
654b11781bSpirofti 
664b11781bSpirofti 	_spinlock(&sem->lock);
674b11781bSpirofti 	if (sem->value) {
684b11781bSpirofti 		sem->value--;
694b11781bSpirofti 		r = 0;
704b11781bSpirofti 	} else {
714b11781bSpirofti 		sem->waitcount++;
724b11781bSpirofti 		do {
734b11781bSpirofti 			r = __thrsleep(ident, CLOCK_REALTIME, abstime,
744b11781bSpirofti 			    &sem->lock, delayed_cancel);
754b11781bSpirofti 			_spinlock(&sem->lock);
764b11781bSpirofti 			/* ignore interruptions other than cancelation */
774b11781bSpirofti 			if ((r == ECANCELED && *delayed_cancel == 0) ||
784b11781bSpirofti 			    (r == EINTR && !can_eintr))
794b11781bSpirofti 				r = 0;
804b11781bSpirofti 		} while (r == 0 && sem->value == 0);
814b11781bSpirofti 		sem->waitcount--;
824b11781bSpirofti 		if (r == 0)
834b11781bSpirofti 			sem->value--;
844b11781bSpirofti 	}
854b11781bSpirofti 	_spinunlock(&sem->lock);
864b11781bSpirofti 	return (r);
874b11781bSpirofti }
884b11781bSpirofti 
894b11781bSpirofti /* always increment count */
904b11781bSpirofti int
_sem_post(sem_t sem)914b11781bSpirofti _sem_post(sem_t sem)
924b11781bSpirofti {
934b11781bSpirofti 	void *ident = (void *)&sem->waitcount;
944b11781bSpirofti 	int rv = 0;
954b11781bSpirofti 
964b11781bSpirofti 	if (sem->shared)
974b11781bSpirofti 		ident = SHARED_IDENT;
984b11781bSpirofti 
994b11781bSpirofti 	_spinlock(&sem->lock);
1004b11781bSpirofti 	sem->value++;
1014b11781bSpirofti 	if (sem->waitcount) {
1024b11781bSpirofti 		__thrwakeup(ident, 1);
1034b11781bSpirofti 		rv = 1;
1044b11781bSpirofti 	}
1054b11781bSpirofti 	_spinunlock(&sem->lock);
1064b11781bSpirofti 	return (rv);
1074b11781bSpirofti }
1084b11781bSpirofti 
1094b11781bSpirofti /*
1104b11781bSpirofti  * exported semaphores
1114b11781bSpirofti  */
1124b11781bSpirofti int
sem_init(sem_t * semp,int pshared,unsigned int value)1134b11781bSpirofti sem_init(sem_t *semp, int pshared, unsigned int value)
1144b11781bSpirofti {
1154b11781bSpirofti 	sem_t sem;
1164b11781bSpirofti 
1174b11781bSpirofti 	if (value > SEM_VALUE_MAX) {
1184b11781bSpirofti 		errno = EINVAL;
1194b11781bSpirofti 		return (-1);
1204b11781bSpirofti 	}
1214b11781bSpirofti 
1224b11781bSpirofti 	if (pshared) {
1234b11781bSpirofti 		errno = EPERM;
1244b11781bSpirofti 		return (-1);
1254b11781bSpirofti #ifdef notyet
1264b11781bSpirofti 		char name[SEM_RANDOM_NAME_LEN];
1274b11781bSpirofti 		sem_t *sempshared;
1284b11781bSpirofti 		int i;
1294b11781bSpirofti 
1304b11781bSpirofti 		for (;;) {
1314b11781bSpirofti 			for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++)
1324b11781bSpirofti 				name[i] = arc4random_uniform(255) + 1;
1334b11781bSpirofti 			name[SEM_RANDOM_NAME_LEN - 1] = '\0';
1344b11781bSpirofti 			sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value);
1354b11781bSpirofti 			if (sempshared != SEM_FAILED)
1364b11781bSpirofti 				break;
1374b11781bSpirofti 			if (errno == EEXIST)
1384b11781bSpirofti 				continue;
1394b11781bSpirofti 			if (errno != EPERM)
1404b11781bSpirofti 				errno = ENOSPC;
1414b11781bSpirofti 			return (-1);
1424b11781bSpirofti 		}
1434b11781bSpirofti 
1444b11781bSpirofti 		/* unnamed semaphore should not be opened twice */
1454b11781bSpirofti 		if (sem_unlink(name) == -1) {
1464b11781bSpirofti 			sem_close(sempshared);
1474b11781bSpirofti 			errno = ENOSPC;
1484b11781bSpirofti 			return (-1);
1494b11781bSpirofti 		}
1504b11781bSpirofti 
1514b11781bSpirofti 		*semp = *sempshared;
1524b11781bSpirofti 		free(sempshared);
1534b11781bSpirofti 		return (0);
1544b11781bSpirofti #endif
1554b11781bSpirofti 	}
1564b11781bSpirofti 
1574b11781bSpirofti 	sem = calloc(1, sizeof(*sem));
1584b11781bSpirofti 	if (!sem) {
1594b11781bSpirofti 		errno = ENOSPC;
1604b11781bSpirofti 		return (-1);
1614b11781bSpirofti 	}
1624b11781bSpirofti 	sem->lock = _SPINLOCK_UNLOCKED;
1634b11781bSpirofti 	sem->value = value;
1644b11781bSpirofti 	*semp = sem;
1654b11781bSpirofti 
1664b11781bSpirofti 	return (0);
1674b11781bSpirofti }
1684b11781bSpirofti 
1694b11781bSpirofti int
sem_destroy(sem_t * semp)1704b11781bSpirofti sem_destroy(sem_t *semp)
1714b11781bSpirofti {
1724b11781bSpirofti 	sem_t sem;
1734b11781bSpirofti 
1744b11781bSpirofti 	if (!_threads_ready)		 /* for SEM_MMAP_SIZE */
1754b11781bSpirofti 		_rthread_init();
1764b11781bSpirofti 
1774b11781bSpirofti 	if (!semp || !(sem = *semp)) {
1784b11781bSpirofti 		errno = EINVAL;
1794b11781bSpirofti 		return (-1);
1804b11781bSpirofti 	}
1814b11781bSpirofti 
1824b11781bSpirofti 	if (sem->waitcount) {
1834b11781bSpirofti #define MSG "sem_destroy on semaphore with waiters!\n"
1844b11781bSpirofti 		write(2, MSG, sizeof(MSG) - 1);
1854b11781bSpirofti #undef MSG
1864b11781bSpirofti 		errno = EBUSY;
1874b11781bSpirofti 		return (-1);
1884b11781bSpirofti 	}
1894b11781bSpirofti 
1904b11781bSpirofti 	*semp = NULL;
1914b11781bSpirofti 	if (sem->shared)
1924b11781bSpirofti 		munmap(sem, SEM_MMAP_SIZE);
1934b11781bSpirofti 	else
1944b11781bSpirofti 		free(sem);
1954b11781bSpirofti 
1964b11781bSpirofti 	return (0);
1974b11781bSpirofti }
1984b11781bSpirofti 
1994b11781bSpirofti int
sem_getvalue(sem_t * semp,int * sval)2004b11781bSpirofti sem_getvalue(sem_t *semp, int *sval)
2014b11781bSpirofti {
2024b11781bSpirofti 	sem_t sem;
2034b11781bSpirofti 
2044b11781bSpirofti 	if (!semp || !(sem = *semp)) {
2054b11781bSpirofti 		errno = EINVAL;
2064b11781bSpirofti 		return (-1);
2074b11781bSpirofti 	}
2084b11781bSpirofti 
2094b11781bSpirofti 	_spinlock(&sem->lock);
2104b11781bSpirofti 	*sval = sem->value;
2114b11781bSpirofti 	_spinunlock(&sem->lock);
2124b11781bSpirofti 
2134b11781bSpirofti 	return (0);
2144b11781bSpirofti }
2154b11781bSpirofti 
2164b11781bSpirofti int
sem_post(sem_t * semp)2174b11781bSpirofti sem_post(sem_t *semp)
2184b11781bSpirofti {
2194b11781bSpirofti 	sem_t sem;
2204b11781bSpirofti 
2214b11781bSpirofti 	if (!semp || !(sem = *semp)) {
2224b11781bSpirofti 		errno = EINVAL;
2234b11781bSpirofti 		return (-1);
2244b11781bSpirofti 	}
2254b11781bSpirofti 
2264b11781bSpirofti 	_sem_post(sem);
2274b11781bSpirofti 
2284b11781bSpirofti 	return (0);
2294b11781bSpirofti }
2304b11781bSpirofti 
2314b11781bSpirofti int
sem_wait(sem_t * semp)2324b11781bSpirofti sem_wait(sem_t *semp)
2334b11781bSpirofti {
2344b11781bSpirofti 	struct tib *tib = TIB_GET();
2354b11781bSpirofti 	pthread_t self;
2364b11781bSpirofti 	sem_t sem;
2374b11781bSpirofti 	int r;
2384b11781bSpirofti 	PREP_CANCEL_POINT(tib);
2394b11781bSpirofti 
2404b11781bSpirofti 	if (!_threads_ready)
2414b11781bSpirofti 		_rthread_init();
2424b11781bSpirofti 	self = tib->tib_thread;
2434b11781bSpirofti 
2444b11781bSpirofti 	if (!semp || !(sem = *semp)) {
2454b11781bSpirofti 		errno = EINVAL;
2464b11781bSpirofti 		return (-1);
2474b11781bSpirofti 	}
2484b11781bSpirofti 
2494b11781bSpirofti 	ENTER_DELAYED_CANCEL_POINT(tib, self);
2504b11781bSpirofti 	r = _sem_wait(sem, 1, NULL, &self->delayed_cancel);
2514b11781bSpirofti 	LEAVE_CANCEL_POINT_INNER(tib, r);
2524b11781bSpirofti 
2534b11781bSpirofti 	if (r) {
2544b11781bSpirofti 		errno = r;
2554b11781bSpirofti 		return (-1);
2564b11781bSpirofti 	}
2574b11781bSpirofti 
2584b11781bSpirofti 	return (0);
2594b11781bSpirofti }
2604b11781bSpirofti 
2614b11781bSpirofti int
sem_timedwait(sem_t * semp,const struct timespec * abstime)2624b11781bSpirofti sem_timedwait(sem_t *semp, const struct timespec *abstime)
2634b11781bSpirofti {
2644b11781bSpirofti 	struct tib *tib = TIB_GET();
2654b11781bSpirofti 	pthread_t self;
2664b11781bSpirofti 	sem_t sem;
2674b11781bSpirofti 	int r;
2684b11781bSpirofti 	PREP_CANCEL_POINT(tib);
2694b11781bSpirofti 
270*c554a768Scheloha 	if (!semp || !(sem = *semp) || !abstime || !timespecisvalid(abstime)) {
2714b11781bSpirofti 		errno = EINVAL;
2724b11781bSpirofti 		return (-1);
2734b11781bSpirofti 	}
2744b11781bSpirofti 
2754b11781bSpirofti 	if (!_threads_ready)
2764b11781bSpirofti 		_rthread_init();
2774b11781bSpirofti 	self = tib->tib_thread;
2784b11781bSpirofti 
2794b11781bSpirofti 	ENTER_DELAYED_CANCEL_POINT(tib, self);
2804b11781bSpirofti 	r = _sem_wait(sem, 1, abstime, &self->delayed_cancel);
2814b11781bSpirofti 	LEAVE_CANCEL_POINT_INNER(tib, r);
2824b11781bSpirofti 
2834b11781bSpirofti 	if (r) {
2844b11781bSpirofti 		errno = r == EWOULDBLOCK ? ETIMEDOUT : r;
2854b11781bSpirofti 		return (-1);
2864b11781bSpirofti 	}
2874b11781bSpirofti 
2884b11781bSpirofti 	return (0);
2894b11781bSpirofti }
2904b11781bSpirofti 
2914b11781bSpirofti int
sem_trywait(sem_t * semp)2924b11781bSpirofti sem_trywait(sem_t *semp)
2934b11781bSpirofti {
2944b11781bSpirofti 	sem_t sem;
2954b11781bSpirofti 	int r;
2964b11781bSpirofti 
2974b11781bSpirofti 	if (!semp || !(sem = *semp)) {
2984b11781bSpirofti 		errno = EINVAL;
2994b11781bSpirofti 		return (-1);
3004b11781bSpirofti 	}
3014b11781bSpirofti 
3024b11781bSpirofti 	_spinlock(&sem->lock);
3034b11781bSpirofti 	if (sem->value) {
3044b11781bSpirofti 		sem->value--;
3054b11781bSpirofti 		r = 0;
3064b11781bSpirofti 	} else
3074b11781bSpirofti 		r = EAGAIN;
3084b11781bSpirofti 	_spinunlock(&sem->lock);
3094b11781bSpirofti 
3104b11781bSpirofti 	if (r) {
3114b11781bSpirofti 		errno = r;
3124b11781bSpirofti 		return (-1);
3134b11781bSpirofti 	}
3144b11781bSpirofti 
3154b11781bSpirofti 	return (0);
3164b11781bSpirofti }
3174b11781bSpirofti 
3184b11781bSpirofti 
3194b11781bSpirofti static void
makesempath(const char * origpath,char * sempath,size_t len)3204b11781bSpirofti makesempath(const char *origpath, char *sempath, size_t len)
3214b11781bSpirofti {
3224b11781bSpirofti 	char buf[SHA256_DIGEST_STRING_LENGTH];
3234b11781bSpirofti 
3244b11781bSpirofti 	SHA256Data(origpath, strlen(origpath), buf);
3254b11781bSpirofti 	snprintf(sempath, len, "/tmp/%s.sem", buf);
3264b11781bSpirofti }
3274b11781bSpirofti 
3284b11781bSpirofti sem_t *
sem_open(const char * name,int oflag,...)3294b11781bSpirofti sem_open(const char *name, int oflag, ...)
3304b11781bSpirofti {
3314b11781bSpirofti 	char sempath[SEM_PATH_SIZE];
3324b11781bSpirofti 	struct stat sb;
3334b11781bSpirofti 	sem_t sem, *semp;
3344b11781bSpirofti 	unsigned int value = 0;
3354b11781bSpirofti 	int created = 0, fd;
3364b11781bSpirofti 
3374b11781bSpirofti 	if (!_threads_ready)
3384b11781bSpirofti 		_rthread_init();
3394b11781bSpirofti 
3404b11781bSpirofti 	if (oflag & ~(O_CREAT | O_EXCL)) {
3414b11781bSpirofti 		errno = EINVAL;
3424b11781bSpirofti 		return (SEM_FAILED);
3434b11781bSpirofti 	}
3444b11781bSpirofti 
3454b11781bSpirofti 	if (oflag & O_CREAT) {
3464b11781bSpirofti 		va_list ap;
3474b11781bSpirofti 		va_start(ap, oflag);
3484b11781bSpirofti 		/* 3rd parameter mode is not used */
3494b11781bSpirofti 		va_arg(ap, mode_t);
3504b11781bSpirofti 		value = va_arg(ap, unsigned);
3514b11781bSpirofti 		va_end(ap);
3524b11781bSpirofti 
3534b11781bSpirofti 		if (value > SEM_VALUE_MAX) {
3544b11781bSpirofti 			errno = EINVAL;
3554b11781bSpirofti 			return (SEM_FAILED);
3564b11781bSpirofti 		}
3574b11781bSpirofti 	}
3584b11781bSpirofti 
3594b11781bSpirofti 	makesempath(name, sempath, sizeof(sempath));
3604b11781bSpirofti 	fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600);
3614b11781bSpirofti 	if (fd == -1)
3624b11781bSpirofti 		return (SEM_FAILED);
3634b11781bSpirofti 	if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
3644b11781bSpirofti 		close(fd);
3654b11781bSpirofti 		errno = EINVAL;
3664b11781bSpirofti 		return (SEM_FAILED);
3674b11781bSpirofti 	}
3684b11781bSpirofti 	if (sb.st_uid != geteuid()) {
3694b11781bSpirofti 		close(fd);
3704b11781bSpirofti 		errno = EPERM;
3714b11781bSpirofti 		return (SEM_FAILED);
3724b11781bSpirofti 	}
3734b11781bSpirofti 	if (sb.st_size != (off_t)SEM_MMAP_SIZE) {
3744b11781bSpirofti 		if (!(oflag & O_CREAT)) {
3754b11781bSpirofti 			close(fd);
3764b11781bSpirofti 			errno = EINVAL;
3774b11781bSpirofti 			return (SEM_FAILED);
3784b11781bSpirofti 		}
3794b11781bSpirofti 		if (sb.st_size != 0) {
3804b11781bSpirofti 			close(fd);
3814b11781bSpirofti 			errno = EINVAL;
3824b11781bSpirofti 			return (SEM_FAILED);
3834b11781bSpirofti 		}
3844b11781bSpirofti 		if (ftruncate(fd, SEM_MMAP_SIZE) == -1) {
3854b11781bSpirofti 			close(fd);
3864b11781bSpirofti 			errno = EINVAL;
3874b11781bSpirofti 			return (SEM_FAILED);
3884b11781bSpirofti 		}
3894b11781bSpirofti 
3904b11781bSpirofti 		created = 1;
3914b11781bSpirofti 	}
3924b11781bSpirofti 	sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE,
3934b11781bSpirofti 	    MAP_SHARED, fd, 0);
3944b11781bSpirofti 	close(fd);
3954b11781bSpirofti 	if (sem == MAP_FAILED) {
3964b11781bSpirofti 		errno = EINVAL;
3974b11781bSpirofti 		return (SEM_FAILED);
3984b11781bSpirofti 	}
3994b11781bSpirofti 	semp = malloc(sizeof(*semp));
4004b11781bSpirofti 	if (!semp) {
4014b11781bSpirofti 		munmap(sem, SEM_MMAP_SIZE);
4024b11781bSpirofti 		errno = ENOSPC;
4034b11781bSpirofti 		return (SEM_FAILED);
4044b11781bSpirofti 	}
4054b11781bSpirofti 	if (created) {
4064b11781bSpirofti 		sem->lock = _SPINLOCK_UNLOCKED;
4074b11781bSpirofti 		sem->value = value;
4084b11781bSpirofti 		sem->shared = 1;
4094b11781bSpirofti 	}
4104b11781bSpirofti 	*semp = sem;
4114b11781bSpirofti 
4124b11781bSpirofti 	return (semp);
4134b11781bSpirofti }
4144b11781bSpirofti 
4154b11781bSpirofti int
sem_close(sem_t * semp)4164b11781bSpirofti sem_close(sem_t *semp)
4174b11781bSpirofti {
4184b11781bSpirofti 	sem_t sem;
4194b11781bSpirofti 
4204b11781bSpirofti 	if (!semp || !(sem = *semp) || !sem->shared) {
4214b11781bSpirofti 		errno = EINVAL;
4224b11781bSpirofti 		return (-1);
4234b11781bSpirofti 	}
4244b11781bSpirofti 
4254b11781bSpirofti 	*semp = NULL;
4264b11781bSpirofti 	munmap(sem, SEM_MMAP_SIZE);
4274b11781bSpirofti 	free(semp);
4284b11781bSpirofti 
4294b11781bSpirofti 	return (0);
4304b11781bSpirofti }
4314b11781bSpirofti 
4324b11781bSpirofti int
sem_unlink(const char * name)4334b11781bSpirofti sem_unlink(const char *name)
4344b11781bSpirofti {
4354b11781bSpirofti 	char sempath[SEM_PATH_SIZE];
4364b11781bSpirofti 
4374b11781bSpirofti 	makesempath(name, sempath, sizeof(sempath));
4384b11781bSpirofti 	return (unlink(sempath));
4394b11781bSpirofti }
440