1#include <semaphore.h> 2#include <sys/mman.h> 3#include <limits.h> 4#include <fcntl.h> 5#include <unistd.h> 6#include <string.h> 7#include <stdarg.h> 8#include <errno.h> 9#include <time.h> 10#include <stdio.h> 11#include <sys/stat.h> 12#include <stdlib.h> 13#include <pthread.h> 14#include "libc.h" 15 16char *__shm_mapname(const char *, char *); 17 18static struct { 19 ino_t ino; 20 sem_t *sem; 21 int refcnt; 22} *semtab; 23static volatile int lock[2]; 24 25#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK) 26 27sem_t *sem_open(const char *name, int flags, ...) 28{ 29 va_list ap; 30 mode_t mode; 31 unsigned value; 32 int fd, i, e, slot, first=1, cnt, cs; 33 sem_t newsem; 34 void *map; 35 char tmp[64]; 36 struct timespec ts; 37 struct stat st; 38 char buf[NAME_MAX+10]; 39 40 if (!(name = __shm_mapname(name, buf))) 41 return SEM_FAILED; 42 43 LOCK(lock); 44 /* Allocate table if we don't have one yet */ 45 if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) { 46 UNLOCK(lock); 47 return SEM_FAILED; 48 } 49 50 /* Reserve a slot in case this semaphore is not mapped yet; 51 * this is necessary because there is no way to handle 52 * failures after creation of the file. */ 53 slot = -1; 54 for (cnt=i=0; i<SEM_NSEMS_MAX; i++) { 55 cnt += semtab[i].refcnt; 56 if (!semtab[i].sem && slot < 0) slot = i; 57 } 58 /* Avoid possibility of overflow later */ 59 if (cnt == INT_MAX || slot < 0) { 60 errno = EMFILE; 61 UNLOCK(lock); 62 return SEM_FAILED; 63 } 64 /* Dummy pointer to make a reservation */ 65 semtab[slot].sem = (sem_t *)-1; 66 UNLOCK(lock); 67 68 flags &= (O_CREAT|O_EXCL); 69 70 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 71 72 /* Early failure check for exclusive open; otherwise the case 73 * where the semaphore already exists is expensive. */ 74 if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) { 75 errno = EEXIST; 76 goto fail; 77 } 78 79 for (;;) { 80 /* If exclusive mode is not requested, try opening an 81 * existing file first and fall back to creation. */ 82 if (flags != (O_CREAT|O_EXCL)) { 83 fd = open(name, FLAGS); 84 if (fd >= 0) { 85 if (fstat(fd, &st) < 0 || 86 (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { 87 close(fd); 88 goto fail; 89 } 90 close(fd); 91 break; 92 } 93 if (errno != ENOENT) 94 goto fail; 95 } 96 if (!(flags & O_CREAT)) 97 goto fail; 98 if (first) { 99 first = 0; 100 va_start(ap, flags); 101 mode = va_arg(ap, mode_t) & 0666; 102 value = va_arg(ap, unsigned); 103 va_end(ap); 104 if (value > SEM_VALUE_MAX) { 105 errno = EINVAL; 106 goto fail; 107 } 108 sem_init(&newsem, 1, value); 109 } 110 /* Create a temp file with the new semaphore contents 111 * and attempt to atomically link it as the new name */ 112 clock_gettime(CLOCK_REALTIME, &ts); 113 snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec); 114 fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode); 115 if (fd < 0) { 116 if (errno == EEXIST) continue; 117 goto fail; 118 } 119 if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 || 120 (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { 121 close(fd); 122 unlink(tmp); 123 goto fail; 124 } 125 close(fd); 126 e = link(tmp, name) ? errno : 0; 127 unlink(tmp); 128 if (!e) break; 129 munmap(map, sizeof(sem_t)); 130 /* Failure is only fatal when doing an exclusive open; 131 * otherwise, next iteration will try to open the 132 * existing file. */ 133 if (e != EEXIST || flags == (O_CREAT|O_EXCL)) 134 goto fail; 135 } 136 137 /* See if the newly mapped semaphore is already mapped. If 138 * so, unmap the new mapping and use the existing one. Otherwise, 139 * add it to the table of mapped semaphores. */ 140 LOCK(lock); 141 for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++); 142 if (i<SEM_NSEMS_MAX) { 143 munmap(map, sizeof(sem_t)); 144 semtab[slot].sem = 0; 145 slot = i; 146 map = semtab[i].sem; 147 } 148 semtab[slot].refcnt++; 149 semtab[slot].sem = map; 150 semtab[slot].ino = st.st_ino; 151 UNLOCK(lock); 152 pthread_setcancelstate(cs, 0); 153 return map; 154 155fail: 156 pthread_setcancelstate(cs, 0); 157 LOCK(lock); 158 semtab[slot].sem = 0; 159 UNLOCK(lock); 160 return SEM_FAILED; 161} 162 163int sem_close(sem_t *sem) 164{ 165 int i; 166 LOCK(lock); 167 for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++); 168 if (!--semtab[i].refcnt) { 169 semtab[i].sem = 0; 170 semtab[i].ino = 0; 171 } 172 UNLOCK(lock); 173 munmap(sem, sizeof *sem); 174 return 0; 175} 176