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