• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 				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 
sem_close(sem_t * sem)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 }
174