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>
|
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;
|
|
|
|
|
|
|
|
static int semcnt;
|
|
|
|
static pthread_spinlock_t lock;
|
|
|
|
static pthread_once_t once;
|
|
|
|
|
|
|
|
static void init()
|
2011-03-04 00:45:59 -05:00
|
|
|
{
|
2011-03-10 21:34:19 -05:00
|
|
|
semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX);
|
|
|
|
}
|
2011-03-04 00:45:59 -05:00
|
|
|
|
2011-03-10 21:34:19 -05:00
|
|
|
static sem_t *find_map(ino_t ino)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != ino; i++);
|
|
|
|
if (i==SEM_NSEMS_MAX) return 0;
|
|
|
|
if (semtab[i].refcnt == INT_MAX) return (sem_t *)-1;
|
|
|
|
semtab[i].refcnt++;
|
|
|
|
return semtab[i].sem;
|
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;
|
|
|
|
int fd, tfd, dir;
|
|
|
|
sem_t newsem;
|
|
|
|
void *map;
|
|
|
|
char tmp[64];
|
|
|
|
struct timespec ts;
|
2011-03-10 21:34:19 -05:00
|
|
|
struct stat st;
|
|
|
|
int i;
|
2011-03-04 00:45:59 -05:00
|
|
|
|
|
|
|
while (*name=='/') name++;
|
|
|
|
if (strchr(name, '/')) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
|
2011-03-10 21:34:19 -05:00
|
|
|
pthread_once(&once, init);
|
|
|
|
if (!semtab) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
|
2011-03-04 00:45:59 -05:00
|
|
|
if (flags & O_CREAT) {
|
|
|
|
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;
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
2012-09-29 16:49:32 -04:00
|
|
|
sem_init(&newsem, 1, value);
|
2011-03-04 00:45:59 -05:00
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
snprintf(tmp, sizeof(tmp), "/dev/shm/%p-%p-%d-%d",
|
|
|
|
&name, name, (int)getpid(), (int)ts.tv_nsec);
|
2012-09-29 16:48:52 -04:00
|
|
|
tfd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC, mode);
|
2011-03-04 00:45:59 -05:00
|
|
|
if (tfd<0) return SEM_FAILED;
|
2012-09-29 16:48:52 -04:00
|
|
|
dir = open("/dev/shm", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
|
2011-03-04 00:45:59 -05:00
|
|
|
if (dir<0 || write(tfd,&newsem,sizeof newsem)!=sizeof newsem) {
|
|
|
|
if (dir >= 0) close(dir);
|
|
|
|
close(tfd);
|
|
|
|
unlink(tmp);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
flags &= ~O_ACCMODE;
|
|
|
|
flags |= O_RDWR;
|
|
|
|
|
2011-03-10 21:34:19 -05:00
|
|
|
pthread_spin_lock(&lock);
|
|
|
|
|
2011-03-04 00:45:59 -05:00
|
|
|
for (;;) {
|
|
|
|
if (!(flags & O_EXCL)) {
|
2011-06-26 16:34:05 -04:00
|
|
|
fd = shm_open(name, flags&~O_CREAT, 0);
|
2011-03-04 00:45:59 -05:00
|
|
|
if (fd >= 0 || errno != ENOENT) {
|
|
|
|
if (flags & O_CREAT) {
|
|
|
|
close(dir);
|
|
|
|
close(tfd);
|
|
|
|
unlink(tmp);
|
|
|
|
}
|
2011-03-10 22:05:16 -05:00
|
|
|
if (fd >= 0 && fstat(fd, &st) < 0) {
|
2011-03-10 21:34:19 -05:00
|
|
|
close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
if (fd < 0) {
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
if ((map = find_map(st.st_ino))) {
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
close(fd);
|
|
|
|
if (map == (sem_t *)-1)
|
|
|
|
return SEM_FAILED;
|
2011-03-04 00:45:59 -05:00
|
|
|
return map;
|
2011-03-10 21:34:19 -05:00
|
|
|
}
|
2011-03-04 00:45:59 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-03-10 22:05:16 -05:00
|
|
|
if (!(flags & O_CREAT)) {
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
2011-03-04 00:45:59 -05:00
|
|
|
if (!linkat(AT_FDCWD, tmp, dir, name, 0)) {
|
|
|
|
fd = tfd;
|
|
|
|
close(dir);
|
|
|
|
unlink(tmp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((flags & O_EXCL) || errno != EEXIST) {
|
|
|
|
close(dir);
|
|
|
|
close(tfd);
|
|
|
|
unlink(tmp);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
}
|
2011-03-10 21:34:19 -05:00
|
|
|
if (fstat(fd, &st) < 0) {
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
close(fd);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
if (semcnt == SEM_NSEMS_MAX) {
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
close(fd);
|
|
|
|
errno = EMFILE;
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem; i++);
|
2011-03-04 00:45:59 -05:00
|
|
|
map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
|
|
|
close(fd);
|
2011-03-10 21:34:19 -05:00
|
|
|
if (map == MAP_FAILED) {
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
return SEM_FAILED;
|
|
|
|
}
|
|
|
|
semtab[i].ino = st.st_ino;
|
|
|
|
semtab[i].sem = map;
|
|
|
|
semtab[i].refcnt = 1;
|
|
|
|
pthread_spin_unlock(&lock);
|
2011-03-04 00:45:59 -05:00
|
|
|
return map;
|
|
|
|
}
|
2011-03-10 21:34:19 -05:00
|
|
|
|
|
|
|
int sem_close(sem_t *sem)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
pthread_spin_lock(&lock);
|
|
|
|
for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
|
|
|
|
if (!--semtab[i].refcnt) {
|
|
|
|
semtab[i].sem = 0;
|
|
|
|
semtab[i].ino = 0;
|
|
|
|
}
|
|
|
|
pthread_spin_unlock(&lock);
|
|
|
|
return munmap(sem, sizeof *sem);
|
|
|
|
}
|