sem_open.c raw

   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  }
 183