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