55d5ef6b7808bae8e070e1d979d0b67083407375
[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
13 static void *find_map(int fd)
14 {
15         char c;
16         struct stat st;
17         FILE *f;
18         void *addr;
19         unsigned long long off, ino;
20         char buf[100];
21
22         if (fstat(fd, &st) < 0) return 0;
23         if (!(f = fopen("/proc/self/maps", "rb"))) return 0;
24         
25         while (fgets(buf, sizeof buf, f)) {
26                 sscanf(buf, "%lx-%*lx %*s %llx %*x:%*x %llu /dev/shm%c",
27                         (long *)&addr, &off, &ino, &c);
28                 while (!strchr(buf, '\n') && fgets(buf, sizeof buf, f));
29                 if (c!='/') continue;
30                 c = 0;
31                 if (!off && st.st_ino == ino) {
32                         fclose(f);
33                         return addr;
34                 }
35         }
36         fclose(f);
37         return 0;
38 }
39
40 sem_t *sem_open(const char *name, int flags, ...)
41 {
42         va_list ap;
43         mode_t mode;
44         unsigned value;
45         int fd, tfd, dir;
46         sem_t newsem;
47         void *map;
48         char tmp[64];
49         struct timespec ts;
50
51         while (*name=='/') name++;
52         if (strchr(name, '/')) {
53                 errno = EINVAL;
54                 return SEM_FAILED;
55         }
56
57         if (flags & O_CREAT) {
58                 va_start(ap, flags);
59                 mode = va_arg(ap, mode_t) & 0666;
60                 value = va_arg(ap, unsigned);
61                 va_end(ap);
62                 if (value > SEM_VALUE_MAX) {
63                         errno = EINVAL;
64                         return SEM_FAILED;
65                 }
66                 sem_init(&newsem, 0, value);
67                 clock_gettime(CLOCK_REALTIME, &ts);
68                 snprintf(tmp, sizeof(tmp), "/dev/shm/%p-%p-%d-%d",
69                         &name, name, (int)getpid(), (int)ts.tv_nsec);
70                 tfd = open(tmp, O_CREAT|O_EXCL|O_RDWR, mode);
71                 if (tfd<0) return SEM_FAILED;
72                 dir = open("/dev/shm", O_DIRECTORY|O_RDONLY);
73                 if (dir<0 || write(tfd,&newsem,sizeof newsem)!=sizeof newsem) {
74                         if (dir >= 0) close(dir);
75                         close(tfd);
76                         unlink(tmp);
77                         return SEM_FAILED;
78                 }
79         }
80
81         flags &= ~O_ACCMODE;
82         flags |= O_RDWR;
83
84         for (;;) {
85                 if (!(flags & O_EXCL)) {
86                         fd = shm_open(name, flags&~O_CREAT, mode);
87                         if (fd >= 0 || errno != ENOENT) {
88                                 if (flags & O_CREAT) {
89                                         close(dir);
90                                         close(tfd);
91                                         unlink(tmp);
92                                 }
93                                 if (fd < 0) return SEM_FAILED;
94                                 if ((map = find_map(fd)))
95                                         return map;
96                                 break;
97                         }
98                 }
99                 if (!linkat(AT_FDCWD, tmp, dir, name, 0)) {
100                         fd = tfd;
101                         close(dir);
102                         unlink(tmp);
103                         break;
104                 }
105                 if ((flags & O_EXCL) || errno != EEXIST) {
106                         close(dir);
107                         close(tfd);
108                         unlink(tmp);
109                         return SEM_FAILED;
110                 }
111         }
112         map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
113         close(fd);
114         if (map == MAP_FAILED) return SEM_FAILED;
115         return map;
116 }