master
  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
 22static struct {
 23	ino_t ino;
 24	sem_t *sem;
 25	int refcnt;
 26} *semtab;
 27static volatile int lock[1];
 28volatile int *const __sem_open_lockptr = lock;
 29
 30#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
 31
 32sem_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
160fail:
161	pthread_setcancelstate(cs, 0);
162	LOCK(lock);
163	semtab[slot].sem = 0;
164	UNLOCK(lock);
165	return SEM_FAILED;
166}
167
168int 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}