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