2011-03-04 00:45:59 -05:00
|
|
|
#include <semaphore.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/stat.h>
|
2011-03-10 21:34:19 -05:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <pthread.h>
|
2012-09-30 19:35:40 -04:00
|
|
|
#include "libc.h"
|
|
|
|
|
|
|
|
char *__shm_mapname(const char *, char *);
|
2011-03-04 00:45:59 -05:00
|
|
|
|
2011-03-10 21:34:19 -05:00
|
|
|
static struct {
|
|
|
|
ino_t ino;
|
|
|
|
sem_t *sem;
|
|
|
|
int refcnt;
|
|
|
|
} *semtab;
|
2018-01-03 14:17:12 +01:00
|
|
|
static volatile int lock[1];
|
2011-03-10 21:34:19 -05:00
|
|
|
|
2012-09-30 19:35:40 -04:00
|
|
|
#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
|
2011-03-04 00:45:59 -05:00
|
|
|
|
|
|
|
sem_t *sem_open(const char *name, int flags, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
mode_t mode;
|
|
|
|
unsigned value;
|
2012-09-30 19:44:45 -04:00
|
|
|
int fd, i, e, slot, first=1, cnt, cs;
|
2011-03-04 00:45:59 -05:00
|
|
|
sem_t newsem;
|
|
|
|
void *map;
|
|
|
|
char tmp[64];
|
|
|
|
struct timespec ts;
|
2011-03-10 21:34:19 -05:00
|
|
|
struct stat st;
|
2012-09-30 19:35:40 -04:00
|
|
|
char buf[NAME_MAX+10];
|
2011-03-04 00:45:59 -05:00
|
|
|
|
2012-09-30 19:35:40 -04:00
|
|
|
if (!(name = __shm_mapname(name, buf)))
|
2011-03-04 00:45:59 -05:00
|
|
|
return SEM_FAILED;
|
|
|
|
|
2012-09-30 19:35:40 -04:00
|
|
|
LOCK(lock);
|
|
|
|
/* Allocate table if we don't have one yet */
|
|
|
|
if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
|
|
|
|
UNLOCK(lock);
|
2011-03-10 21:34:19 -05:00
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
|
2012-09-30 19:35:40 -04:00
|
|
|
/* Reserve a slot in case this semaphore is not mapped yet;
|
|
|
|
* this is necessary because there is no way to handle
|
|
|
|
* failures after creation of the file. */
|
|
|
|
slot = -1;
|
|
|
|
for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
|
|
|
|
cnt += semtab[i].refcnt;
|
|
|
|
if (!semtab[i].sem && slot < 0) slot = i;
|
2011-03-04 00:45:59 -05:00
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
/* Avoid possibility of overflow later */
|
|
|
|
if (cnt == INT_MAX || slot < 0) {
|
|
|
|
errno = EMFILE;
|
|
|
|
UNLOCK(lock);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
/* Dummy pointer to make a reservation */
|
|
|
|
semtab[slot].sem = (sem_t *)-1;
|
|
|
|
UNLOCK(lock);
|
2011-03-04 00:45:59 -05:00
|
|
|
|
2012-09-30 19:35:40 -04:00
|
|
|
flags &= (O_CREAT|O_EXCL);
|
2011-03-04 00:45:59 -05:00
|
|
|
|
2013-06-26 21:39:15 -04:00
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
|
|
|
2012-09-30 19:35:40 -04:00
|
|
|
/* Early failure check for exclusive open; otherwise the case
|
|
|
|
* where the semaphore already exists is expensive. */
|
|
|
|
if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
|
|
|
|
errno = EEXIST;
|
2013-06-26 21:39:15 -04:00
|
|
|
goto fail;
|
2012-09-30 19:35:40 -04:00
|
|
|
}
|
2011-03-10 21:34:19 -05:00
|
|
|
|
2011-03-04 00:45:59 -05:00
|
|
|
for (;;) {
|
2012-09-30 19:35:40 -04:00
|
|
|
/* If exclusive mode is not requested, try opening an
|
|
|
|
* existing file first and fall back to creation. */
|
|
|
|
if (flags != (O_CREAT|O_EXCL)) {
|
|
|
|
fd = open(name, FLAGS);
|
|
|
|
if (fd >= 0) {
|
2013-06-26 21:35:56 -04:00
|
|
|
if (fstat(fd, &st) < 0 ||
|
|
|
|
(map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
2011-03-10 21:34:19 -05:00
|
|
|
close(fd);
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2011-03-10 21:34:19 -05:00
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
close(fd);
|
2011-03-04 00:45:59 -05:00
|
|
|
break;
|
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
if (errno != ENOENT)
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2011-03-04 00:45:59 -05:00
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
if (!(flags & O_CREAT))
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2012-09-30 19:35:40 -04:00
|
|
|
if (first) {
|
|
|
|
first = 0;
|
|
|
|
va_start(ap, flags);
|
|
|
|
mode = va_arg(ap, mode_t) & 0666;
|
|
|
|
value = va_arg(ap, unsigned);
|
|
|
|
va_end(ap);
|
|
|
|
if (value > SEM_VALUE_MAX) {
|
|
|
|
errno = EINVAL;
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2012-09-30 19:35:40 -04:00
|
|
|
}
|
|
|
|
sem_init(&newsem, 1, value);
|
2011-03-10 22:05:16 -05:00
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
/* Create a temp file with the new semaphore contents
|
|
|
|
* and attempt to atomically link it as the new name */
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
|
|
|
|
fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
|
|
|
|
if (fd < 0) {
|
|
|
|
if (errno == EEXIST) continue;
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2011-03-04 00:45:59 -05:00
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
|
|
|
|
(map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
|
|
|
close(fd);
|
2011-03-04 00:45:59 -05:00
|
|
|
unlink(tmp);
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2011-03-04 00:45:59 -05:00
|
|
|
}
|
2011-03-10 21:34:19 -05:00
|
|
|
close(fd);
|
2013-06-26 21:41:51 -04:00
|
|
|
e = link(tmp, name) ? errno : 0;
|
2012-09-30 19:35:40 -04:00
|
|
|
unlink(tmp);
|
2013-06-26 21:41:51 -04:00
|
|
|
if (!e) break;
|
2015-04-21 13:24:46 -04:00
|
|
|
munmap(map, sizeof(sem_t));
|
2012-09-30 19:35:40 -04:00
|
|
|
/* Failure is only fatal when doing an exclusive open;
|
|
|
|
* otherwise, next iteration will try to open the
|
|
|
|
* existing file. */
|
|
|
|
if (e != EEXIST || flags == (O_CREAT|O_EXCL))
|
2012-09-30 19:44:45 -04:00
|
|
|
goto fail;
|
2011-03-10 21:34:19 -05:00
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
|
|
|
|
/* See if the newly mapped semaphore is already mapped. If
|
|
|
|
* so, unmap the new mapping and use the existing one. Otherwise,
|
|
|
|
* add it to the table of mapped semaphores. */
|
|
|
|
LOCK(lock);
|
|
|
|
for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
|
|
|
|
if (i<SEM_NSEMS_MAX) {
|
|
|
|
munmap(map, sizeof(sem_t));
|
2012-09-30 19:44:45 -04:00
|
|
|
semtab[slot].sem = 0;
|
|
|
|
slot = i;
|
|
|
|
map = semtab[i].sem;
|
2011-03-10 21:34:19 -05:00
|
|
|
}
|
2012-09-30 19:44:45 -04:00
|
|
|
semtab[slot].refcnt++;
|
2012-09-30 19:35:40 -04:00
|
|
|
semtab[slot].sem = map;
|
|
|
|
semtab[slot].ino = st.st_ino;
|
|
|
|
UNLOCK(lock);
|
2012-09-30 19:44:45 -04:00
|
|
|
pthread_setcancelstate(cs, 0);
|
2011-03-04 00:45:59 -05:00
|
|
|
return map;
|
2012-09-30 19:44:45 -04:00
|
|
|
|
|
|
|
fail:
|
|
|
|
pthread_setcancelstate(cs, 0);
|
2013-06-26 21:39:15 -04:00
|
|
|
LOCK(lock);
|
|
|
|
semtab[slot].sem = 0;
|
|
|
|
UNLOCK(lock);
|
2012-09-30 19:44:45 -04:00
|
|
|
return SEM_FAILED;
|
2011-03-04 00:45:59 -05:00
|
|
|
}
|
2011-03-10 21:34:19 -05:00
|
|
|
|
|
|
|
int sem_close(sem_t *sem)
|
|
|
|
{
|
|
|
|
int i;
|
2012-09-30 19:35:40 -04:00
|
|
|
LOCK(lock);
|
2011-03-10 21:34:19 -05:00
|
|
|
for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
|
|
|
|
if (!--semtab[i].refcnt) {
|
|
|
|
semtab[i].sem = 0;
|
|
|
|
semtab[i].ino = 0;
|
|
|
|
}
|
2012-09-30 19:35:40 -04:00
|
|
|
UNLOCK(lock);
|
|
|
|
munmap(sem, sizeof *sem);
|
|
|
|
return 0;
|
2011-03-10 21:34:19 -05:00
|
|
|
}
|