overhaul internally-public declarations using wrapper headers
[musl] / src / thread / sem_open.c
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
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
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                                 close(fd);
89                                 break;
90                         }
91                         if (errno != ENOENT)
92                                 goto fail;
93                 }
94                 if (!(flags & O_CREAT))
95                         goto fail;
96                 if (first) {
97                         first = 0;
98                         va_start(ap, flags);
99                         mode = va_arg(ap, mode_t) & 0666;
100                         value = va_arg(ap, unsigned);
101                         va_end(ap);
102                         if (value > SEM_VALUE_MAX) {
103                                 errno = EINVAL;
104                                 goto fail;
105                         }
106                         sem_init(&newsem, 1, value);
107                 }
108                 /* Create a temp file with the new semaphore contents
109                  * and attempt to atomically link it as the new name */
110                 clock_gettime(CLOCK_REALTIME, &ts);
111                 snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
112                 fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
113                 if (fd < 0) {
114                         if (errno == EEXIST) continue;
115                         goto fail;
116                 }
117                 if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
118                     (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
119                         close(fd);
120                         unlink(tmp);
121                         goto fail;
122                 }
123                 close(fd);
124                 e = link(tmp, name) ? errno : 0;
125                 unlink(tmp);
126                 if (!e) break;
127                 munmap(map, sizeof(sem_t));
128                 /* Failure is only fatal when doing an exclusive open;
129                  * otherwise, next iteration will try to open the
130                  * existing file. */
131                 if (e != EEXIST || flags == (O_CREAT|O_EXCL))
132                         goto fail;
133         }
134
135         /* See if the newly mapped semaphore is already mapped. If
136          * so, unmap the new mapping and use the existing one. Otherwise,
137          * add it to the table of mapped semaphores. */
138         LOCK(lock);
139         for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
140         if (i<SEM_NSEMS_MAX) {
141                 munmap(map, sizeof(sem_t));
142                 semtab[slot].sem = 0;
143                 slot = i;
144                 map = semtab[i].sem;
145         }
146         semtab[slot].refcnt++;
147         semtab[slot].sem = map;
148         semtab[slot].ino = st.st_ino;
149         UNLOCK(lock);
150         pthread_setcancelstate(cs, 0);
151         return map;
152
153 fail:
154         pthread_setcancelstate(cs, 0);
155         LOCK(lock);
156         semtab[slot].sem = 0;
157         UNLOCK(lock);
158         return SEM_FAILED;
159 }
160
161 int sem_close(sem_t *sem)
162 {
163         int i;
164         LOCK(lock);
165         for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
166         if (!--semtab[i].refcnt) {
167                 semtab[i].sem = 0;
168                 semtab[i].ino = 0;
169         }
170         UNLOCK(lock);
171         munmap(sem, sizeof *sem);
172         return 0;
173 }