sem_open.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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 "libc.h"
  15. char *__shm_mapname(const char *, char *);
  16. static struct {
  17. ino_t ino;
  18. sem_t *sem;
  19. int refcnt;
  20. } *semtab;
  21. static int lock[2];
  22. #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
  23. sem_t *sem_open(const char *name, int flags, ...)
  24. {
  25. va_list ap;
  26. mode_t mode;
  27. unsigned value;
  28. int fd, i, e, slot, first=1, cnt;
  29. sem_t newsem;
  30. void *map;
  31. char tmp[64];
  32. struct timespec ts;
  33. struct stat st;
  34. char buf[NAME_MAX+10];
  35. if (!(name = __shm_mapname(name, buf)))
  36. return SEM_FAILED;
  37. LOCK(lock);
  38. /* Allocate table if we don't have one yet */
  39. if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
  40. UNLOCK(lock);
  41. return SEM_FAILED;
  42. }
  43. /* Reserve a slot in case this semaphore is not mapped yet;
  44. * this is necessary because there is no way to handle
  45. * failures after creation of the file. */
  46. slot = -1;
  47. for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
  48. cnt += semtab[i].refcnt;
  49. if (!semtab[i].sem && slot < 0) slot = i;
  50. }
  51. /* Avoid possibility of overflow later */
  52. if (cnt == INT_MAX || slot < 0) {
  53. errno = EMFILE;
  54. UNLOCK(lock);
  55. return SEM_FAILED;
  56. }
  57. /* Dummy pointer to make a reservation */
  58. semtab[slot].sem = (sem_t *)-1;
  59. UNLOCK(lock);
  60. flags &= (O_CREAT|O_EXCL);
  61. /* Early failure check for exclusive open; otherwise the case
  62. * where the semaphore already exists is expensive. */
  63. if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
  64. errno = EEXIST;
  65. return SEM_FAILED;
  66. }
  67. for (;;) {
  68. /* If exclusive mode is not requested, try opening an
  69. * existing file first and fall back to creation. */
  70. if (flags != (O_CREAT|O_EXCL)) {
  71. fd = open(name, FLAGS);
  72. if (fd >= 0) {
  73. if ((map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED ||
  74. fstat(fd, &st) < 0) {
  75. close(fd);
  76. return SEM_FAILED;
  77. }
  78. close(fd);
  79. break;
  80. }
  81. if (errno != ENOENT)
  82. return SEM_FAILED;
  83. }
  84. if (!(flags & O_CREAT))
  85. return SEM_FAILED;
  86. if (first) {
  87. first = 0;
  88. va_start(ap, flags);
  89. mode = va_arg(ap, mode_t) & 0666;
  90. value = va_arg(ap, unsigned);
  91. va_end(ap);
  92. if (value > SEM_VALUE_MAX) {
  93. errno = EINVAL;
  94. return SEM_FAILED;
  95. }
  96. sem_init(&newsem, 1, value);
  97. }
  98. /* Create a temp file with the new semaphore contents
  99. * and attempt to atomically link it as the new name */
  100. clock_gettime(CLOCK_REALTIME, &ts);
  101. snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
  102. fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
  103. if (fd < 0) {
  104. if (errno == EEXIST) continue;
  105. return SEM_FAILED;
  106. }
  107. if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
  108. (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
  109. close(fd);
  110. unlink(tmp);
  111. return SEM_FAILED;
  112. }
  113. close(fd);
  114. if (link(tmp, name) == 0) break;
  115. e = errno;
  116. unlink(tmp);
  117. /* Failure is only fatal when doing an exclusive open;
  118. * otherwise, next iteration will try to open the
  119. * existing file. */
  120. if (e != EEXIST || flags == (O_CREAT|O_EXCL))
  121. return SEM_FAILED;
  122. }
  123. /* See if the newly mapped semaphore is already mapped. If
  124. * so, unmap the new mapping and use the existing one. Otherwise,
  125. * add it to the table of mapped semaphores. */
  126. LOCK(lock);
  127. for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
  128. if (i<SEM_NSEMS_MAX) {
  129. munmap(map, sizeof(sem_t));
  130. semtab[i].refcnt++;
  131. UNLOCK(lock);
  132. return semtab[i].sem;
  133. }
  134. semtab[slot].refcnt = 1;
  135. semtab[slot].sem = map;
  136. semtab[slot].ino = st.st_ino;
  137. UNLOCK(lock);
  138. return map;
  139. }
  140. int sem_close(sem_t *sem)
  141. {
  142. int i;
  143. LOCK(lock);
  144. for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
  145. if (!--semtab[i].refcnt) {
  146. semtab[i].sem = 0;
  147. semtab[i].ino = 0;
  148. }
  149. UNLOCK(lock);
  150. munmap(sem, sizeof *sem);
  151. return 0;
  152. }