diff options
author | Ted Unangst <tedu@cvs.openbsd.org> | 2013-11-18 23:10:49 +0000 |
---|---|---|
committer | Ted Unangst <tedu@cvs.openbsd.org> | 2013-11-18 23:10:49 +0000 |
commit | 781e1f41f7ca8f5924dd4b1576bc346094ec6b85 (patch) | |
tree | 4fc572c728bbfa7d0188bfaf083f4f1fa9353790 | |
parent | 1fc9ce7dc6425eb22b303c2dde111cf854da86cf (diff) |
interprocess semaphores ala sem_open. mostly following in the pattern
of shm_open. with some additions and fixes from zhuk.
-rw-r--r-- | lib/librthread/rthread.h | 4 | ||||
-rw-r--r-- | lib/librthread/rthread_sem.c | 190 |
2 files changed, 170 insertions, 24 deletions
diff --git a/lib/librthread/rthread.h b/lib/librthread/rthread.h index 2f4a2d283bc..8ff50836884 100644 --- a/lib/librthread/rthread.h +++ b/lib/librthread/rthread.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rthread.h,v 1.45 2013/06/21 06:08:50 guenther Exp $ */ +/* $OpenBSD: rthread.h,v 1.46 2013/11/18 23:10:48 tedu Exp $ */ /* * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> * All Rights Reserved. @@ -64,7 +64,7 @@ struct __sem { struct _spinlock lock; volatile int waitcount; volatile int value; - int __pad; + struct __sem *shared; }; TAILQ_HEAD(pthread_queue, pthread); diff --git a/lib/librthread/rthread_sem.c b/lib/librthread/rthread_sem.c index b4a08140817..822fb5a96f8 100644 --- a/lib/librthread/rthread_sem.c +++ b/lib/librthread/rthread_sem.c @@ -1,6 +1,6 @@ -/* $OpenBSD: rthread_sem.c,v 1.9 2013/06/01 23:06:26 tedu Exp $ */ +/* $OpenBSD: rthread_sem.c,v 1.10 2013/11/18 23:10:48 tedu Exp $ */ /* - * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> + * Copyright (c) 2004,2005,2013 Ted Unangst <tedu@openbsd.org> * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software for any @@ -15,15 +15,39 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <sha2.h> #include <stdlib.h> +#include <stdio.h> +#include <string.h> #include <unistd.h> -#include <errno.h> #include <pthread.h> #include "rthread.h" +#define SHARED_IDENT ((void *)-1) + +/* SHA256_DIGEST_STRING_LENGTH includes nul */ +/* "/tmp/" + sha256 + ".sem" */ +#define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4) + +/* long enough to be hard to guess */ +#define SEM_RANDOM_NAME_LEN 160 + +/* + * Size of memory to be mmap()'ed by named semaphores. + * Should be >= SEM_PATH_SIZE and page-aligned. + */ +#define SEM_MMAP_SIZE PAGE_SIZE + /* * Internal implementation of semaphores */ @@ -31,8 +55,14 @@ int _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime, int *delayed_cancel) { + void *ident = (void *)&sem->waitcount; int r; + if (sem->shared) { + sem = sem->shared; + ident = SHARED_IDENT; + } + _spinlock(&sem->lock); if (sem->value) { sem->value--; @@ -42,7 +72,7 @@ _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime, } else { sem->waitcount++; do { - r = __thrsleep(&sem->waitcount, CLOCK_REALTIME | + r = __thrsleep(ident, CLOCK_REALTIME | _USING_TICKETS, abstime, &sem->lock.ticket, delayed_cancel); _spinlock(&sem->lock); @@ -63,12 +93,18 @@ _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime, int _sem_post(sem_t sem) { + void *ident = (void *)&sem->waitcount; int rv = 0; + if (sem->shared) { + sem = sem->shared; + ident = SHARED_IDENT; + } + _spinlock(&sem->lock); sem->value++; if (sem->waitcount) { - __thrwakeup(&sem->waitcount, 1); + __thrwakeup(ident, 1); rv = 1; } _spinunlock(&sem->lock); @@ -81,11 +117,39 @@ _sem_post(sem_t sem) int sem_init(sem_t *semp, int pshared, unsigned int value) { - sem_t sem; + sem_t sem, *sempshared; + int i, oerrno; + char name[SEM_RANDOM_NAME_LEN]; if (pshared) { - errno = EPERM; - return (-1); + while (1) { + for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++) + name[i] = arc4random_uniform(255) + 1; + name[SEM_RANDOM_NAME_LEN - 1] = '\0'; + sempshared = sem_open(name, O_CREAT|O_EXCL); + if (sempshared) { + break; + } + if (errno == EEXIST) + continue; + if (errno != EINVAL && errno != EPERM) + errno = ENOSPC; + return (-1); + } + + /* unnamed semaphore should not be opened twice */ + if (sem_unlink(name) == -1) { + oerrno = errno; + sem_close(sempshared); + errno = oerrno; + return (-1); + } + + sem = *sempshared; + free(sempshared); + sem->value = value; + *semp = sem; + return (0); } if (value > SEM_VALUE_MAX) { @@ -108,20 +172,30 @@ sem_init(sem_t *semp, int pshared, unsigned int value) int sem_destroy(sem_t *semp) { + sem_t sem; + if (!semp || !*semp) { errno = EINVAL; return (-1); } + sem = *semp; - if ((*semp)->waitcount) { + if (sem->waitcount) { #define MSG "sem_destroy on semaphore with waiters!\n" write(2, MSG, sizeof(MSG) - 1); #undef MSG errno = EBUSY; return (-1); } - free(*semp); + + if (sem->shared) + if (munmap(sem->shared, SEM_MMAP_SIZE) == -1) { + warn("sem_destroy: someone borked shared memory"); + abort(); + } + *semp = NULL; + free(sem); return (0); } @@ -136,6 +210,9 @@ sem_getvalue(sem_t *semp, int *sval) return (-1); } + if (sem->shared) + sem = sem->shared; + _spinlock(&sem->lock); *sval = sem->value; _spinunlock(&sem->lock); @@ -227,27 +304,96 @@ sem_trywait(sem_t *semp) return (0); } -/* ARGSUSED */ + +static void +makesempath(const char *origpath, char *sempath, size_t len) +{ + char buf[SHA256_DIGEST_STRING_LENGTH]; + + SHA256Data(origpath, strlen(origpath), buf); + snprintf(sempath, len, "/tmp/%s.sem", buf); +} + sem_t * -sem_open(const char *name __unused, int oflag __unused, ...) +sem_open(const char *name, int oflag, ...) { - errno = ENOSYS; - return (SEM_FAILED); + char sempath[SEM_PATH_SIZE]; + struct stat sb; + int fd, oerrno; + sem_t sem, shared; + sem_t *semp = SEM_FAILED; + + if (oflag & ~(O_CREAT | O_EXCL)) { + errno = EINVAL; + return semp; + } + + makesempath(name, sempath, sizeof(sempath)); + fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600); + if (fd == -1) + return semp; + if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) { + close(fd); + errno = EINVAL; + return semp; + } + if (sb.st_uid != getuid()) { + close(fd); + errno = EPERM; + return semp; + } + if (sb.st_size < SEM_MMAP_SIZE) + if (ftruncate(fd, SEM_MMAP_SIZE) == -1) { + oerrno = errno; + close(fd); + errno = oerrno; + /* XXX can set errno to EIO, ENOTDIR... */ + return semp; + } + shared = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED | MAP_HASSEMAPHORE, fd, 0); + oerrno = errno; + close(fd); + if (shared == MAP_FAILED) { + errno = oerrno; + return semp; + } + semp = malloc(sizeof(*semp)); + sem = calloc(1, sizeof(*sem)); + if (!semp || !sem) { + free(sem); + free(semp); + munmap(shared, SEM_MMAP_SIZE); + errno = ENOSPC; + return SEM_FAILED; + } + *semp = sem; + sem->shared = shared; + + return semp; } -/* ARGSUSED */ int -sem_close(sem_t *sem __unused) +sem_close(sem_t *semp) { - errno = ENOSYS; - return (-1); + sem_t sem = *semp; + + if (munmap(sem->shared, SEM_MMAP_SIZE) == -1) { + warn("sem_close: someone borked shared memory"); + abort(); + } + free(sem); + free(semp); + + return (0); } -/* ARGSUSED */ int -sem_unlink(const char *name __unused) +sem_unlink(const char *name) { - errno = ENOSYS; - return (-1); + char sempath[SEM_PATH_SIZE]; + + makesempath(name, sempath, sizeof(sempath)); + return unlink(sempath); } |