summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2013-11-18 23:10:49 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2013-11-18 23:10:49 +0000
commit781e1f41f7ca8f5924dd4b1576bc346094ec6b85 (patch)
tree4fc572c728bbfa7d0188bfaf083f4f1fa9353790
parent1fc9ce7dc6425eb22b303c2dde111cf854da86cf (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.h4
-rw-r--r--lib/librthread/rthread_sem.c190
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);
}