1570af302Sopenharmony_ci#include <semaphore.h> 2570af302Sopenharmony_ci#include <sys/mman.h> 3570af302Sopenharmony_ci#include <limits.h> 4570af302Sopenharmony_ci#include <fcntl.h> 5570af302Sopenharmony_ci#include <unistd.h> 6570af302Sopenharmony_ci#include <string.h> 7570af302Sopenharmony_ci#include <stdarg.h> 8570af302Sopenharmony_ci#include <errno.h> 9570af302Sopenharmony_ci#include <time.h> 10570af302Sopenharmony_ci#include <stdio.h> 11570af302Sopenharmony_ci#include <sys/stat.h> 12570af302Sopenharmony_ci#include <stdlib.h> 13570af302Sopenharmony_ci#include <pthread.h> 14570af302Sopenharmony_ci#include "lock.h" 15570af302Sopenharmony_ci#include "fork_impl.h" 16570af302Sopenharmony_ci 17570af302Sopenharmony_ci#define malloc __libc_malloc 18570af302Sopenharmony_ci#define calloc __libc_calloc 19570af302Sopenharmony_ci#define realloc undef 20570af302Sopenharmony_ci#define free undef 21570af302Sopenharmony_ci 22570af302Sopenharmony_cistatic struct { 23570af302Sopenharmony_ci ino_t ino; 24570af302Sopenharmony_ci sem_t *sem; 25570af302Sopenharmony_ci int refcnt; 26570af302Sopenharmony_ci} *semtab; 27570af302Sopenharmony_cistatic volatile int lock[1]; 28570af302Sopenharmony_civolatile int *const __sem_open_lockptr = lock; 29570af302Sopenharmony_ci 30570af302Sopenharmony_ci#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK) 31570af302Sopenharmony_ci 32570af302Sopenharmony_cisem_t *sem_open(const char *name, int flags, ...) 33570af302Sopenharmony_ci{ 34570af302Sopenharmony_ci va_list ap; 35570af302Sopenharmony_ci mode_t mode; 36570af302Sopenharmony_ci unsigned value; 37570af302Sopenharmony_ci int fd, i, e, slot, first=1, cnt, cs; 38570af302Sopenharmony_ci sem_t newsem; 39570af302Sopenharmony_ci void *map; 40570af302Sopenharmony_ci char tmp[64]; 41570af302Sopenharmony_ci struct timespec ts; 42570af302Sopenharmony_ci struct stat st; 43570af302Sopenharmony_ci char buf[NAME_MAX+10]; 44570af302Sopenharmony_ci 45570af302Sopenharmony_ci if (!(name = __shm_mapname(name, buf))) 46570af302Sopenharmony_ci return SEM_FAILED; 47570af302Sopenharmony_ci 48570af302Sopenharmony_ci LOCK(lock); 49570af302Sopenharmony_ci /* Allocate table if we don't have one yet */ 50570af302Sopenharmony_ci if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) { 51570af302Sopenharmony_ci UNLOCK(lock); 52570af302Sopenharmony_ci return SEM_FAILED; 53570af302Sopenharmony_ci } 54570af302Sopenharmony_ci 55570af302Sopenharmony_ci /* Reserve a slot in case this semaphore is not mapped yet; 56570af302Sopenharmony_ci * this is necessary because there is no way to handle 57570af302Sopenharmony_ci * failures after creation of the file. */ 58570af302Sopenharmony_ci slot = -1; 59570af302Sopenharmony_ci for (cnt=i=0; i<SEM_NSEMS_MAX; i++) { 60570af302Sopenharmony_ci cnt += semtab[i].refcnt; 61570af302Sopenharmony_ci if (!semtab[i].sem && slot < 0) slot = i; 62570af302Sopenharmony_ci } 63570af302Sopenharmony_ci /* Avoid possibility of overflow later */ 64570af302Sopenharmony_ci if (cnt == INT_MAX || slot < 0) { 65570af302Sopenharmony_ci errno = EMFILE; 66570af302Sopenharmony_ci UNLOCK(lock); 67570af302Sopenharmony_ci return SEM_FAILED; 68570af302Sopenharmony_ci } 69570af302Sopenharmony_ci /* Dummy pointer to make a reservation */ 70570af302Sopenharmony_ci semtab[slot].sem = (sem_t *)-1; 71570af302Sopenharmony_ci UNLOCK(lock); 72570af302Sopenharmony_ci 73570af302Sopenharmony_ci flags &= (O_CREAT|O_EXCL); 74570af302Sopenharmony_ci 75570af302Sopenharmony_ci pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 76570af302Sopenharmony_ci 77570af302Sopenharmony_ci /* Early failure check for exclusive open; otherwise the case 78570af302Sopenharmony_ci * where the semaphore already exists is expensive. */ 79570af302Sopenharmony_ci if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) { 80570af302Sopenharmony_ci errno = EEXIST; 81570af302Sopenharmony_ci goto fail; 82570af302Sopenharmony_ci } 83570af302Sopenharmony_ci 84570af302Sopenharmony_ci for (;;) { 85570af302Sopenharmony_ci /* If exclusive mode is not requested, try opening an 86570af302Sopenharmony_ci * existing file first and fall back to creation. */ 87570af302Sopenharmony_ci if (flags != (O_CREAT|O_EXCL)) { 88570af302Sopenharmony_ci fd = open(name, FLAGS); 89570af302Sopenharmony_ci if (fd >= 0) { 90570af302Sopenharmony_ci if (fstat(fd, &st) < 0 || 91570af302Sopenharmony_ci (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { 92570af302Sopenharmony_ci close(fd); 93570af302Sopenharmony_ci goto fail; 94570af302Sopenharmony_ci } 95570af302Sopenharmony_ci#ifndef __LITEOS_A__ 96570af302Sopenharmony_ci close(fd); 97570af302Sopenharmony_ci#endif 98570af302Sopenharmony_ci break; 99570af302Sopenharmony_ci } 100570af302Sopenharmony_ci if (errno != ENOENT) 101570af302Sopenharmony_ci goto fail; 102570af302Sopenharmony_ci } 103570af302Sopenharmony_ci if (!(flags & O_CREAT)) 104570af302Sopenharmony_ci goto fail; 105570af302Sopenharmony_ci if (first) { 106570af302Sopenharmony_ci first = 0; 107570af302Sopenharmony_ci va_start(ap, flags); 108570af302Sopenharmony_ci mode = va_arg(ap, mode_t) & 0666; 109570af302Sopenharmony_ci value = va_arg(ap, unsigned); 110570af302Sopenharmony_ci va_end(ap); 111570af302Sopenharmony_ci if (value > SEM_VALUE_MAX) { 112570af302Sopenharmony_ci errno = EINVAL; 113570af302Sopenharmony_ci goto fail; 114570af302Sopenharmony_ci } 115570af302Sopenharmony_ci sem_init(&newsem, 1, value); 116570af302Sopenharmony_ci } 117570af302Sopenharmony_ci /* Create a temp file with the new semaphore contents 118570af302Sopenharmony_ci * and attempt to atomically link it as the new name */ 119570af302Sopenharmony_ci clock_gettime(CLOCK_REALTIME, &ts); 120570af302Sopenharmony_ci snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec); 121570af302Sopenharmony_ci fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode); 122570af302Sopenharmony_ci if (fd < 0) { 123570af302Sopenharmony_ci if (errno == EEXIST) continue; 124570af302Sopenharmony_ci goto fail; 125570af302Sopenharmony_ci } 126570af302Sopenharmony_ci if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 || 127570af302Sopenharmony_ci (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { 128570af302Sopenharmony_ci close(fd); 129570af302Sopenharmony_ci unlink(tmp); 130570af302Sopenharmony_ci goto fail; 131570af302Sopenharmony_ci } 132570af302Sopenharmony_ci#ifndef __LITEOS_A__ 133570af302Sopenharmony_ci close(fd); 134570af302Sopenharmony_ci#endif 135570af302Sopenharmony_ci e = link(tmp, name) ? errno : 0; 136570af302Sopenharmony_ci unlink(tmp); 137570af302Sopenharmony_ci if (!e) break; 138570af302Sopenharmony_ci munmap(map, sizeof(sem_t)); 139570af302Sopenharmony_ci /* Failure is only fatal when doing an exclusive open; 140570af302Sopenharmony_ci * otherwise, next iteration will try to open the 141570af302Sopenharmony_ci * existing file. */ 142570af302Sopenharmony_ci if (e != EEXIST || flags == (O_CREAT|O_EXCL)) 143570af302Sopenharmony_ci goto fail; 144570af302Sopenharmony_ci } 145570af302Sopenharmony_ci 146570af302Sopenharmony_ci /* See if the newly mapped semaphore is already mapped. If 147570af302Sopenharmony_ci * so, unmap the new mapping and use the existing one. Otherwise, 148570af302Sopenharmony_ci * add it to the table of mapped semaphores. */ 149570af302Sopenharmony_ci LOCK(lock); 150570af302Sopenharmony_ci for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++); 151570af302Sopenharmony_ci if (i<SEM_NSEMS_MAX) { 152570af302Sopenharmony_ci munmap(map, sizeof(sem_t)); 153570af302Sopenharmony_ci semtab[slot].sem = 0; 154570af302Sopenharmony_ci slot = i; 155570af302Sopenharmony_ci map = semtab[i].sem; 156570af302Sopenharmony_ci } 157570af302Sopenharmony_ci semtab[slot].refcnt++; 158570af302Sopenharmony_ci semtab[slot].sem = map; 159570af302Sopenharmony_ci semtab[slot].ino = st.st_ino; 160570af302Sopenharmony_ci UNLOCK(lock); 161570af302Sopenharmony_ci pthread_setcancelstate(cs, 0); 162570af302Sopenharmony_ci return map; 163570af302Sopenharmony_ci 164570af302Sopenharmony_cifail: 165570af302Sopenharmony_ci pthread_setcancelstate(cs, 0); 166570af302Sopenharmony_ci LOCK(lock); 167570af302Sopenharmony_ci semtab[slot].sem = 0; 168570af302Sopenharmony_ci UNLOCK(lock); 169570af302Sopenharmony_ci return SEM_FAILED; 170570af302Sopenharmony_ci} 171570af302Sopenharmony_ci 172570af302Sopenharmony_ciint sem_close(sem_t *sem) 173570af302Sopenharmony_ci{ 174570af302Sopenharmony_ci int i; 175570af302Sopenharmony_ci LOCK(lock); 176570af302Sopenharmony_ci for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++); 177570af302Sopenharmony_ci#ifdef __LITEOS_A__ 178570af302Sopenharmony_ci if (!--semtab[i].refcnt) { 179570af302Sopenharmony_ci#else 180570af302Sopenharmony_ci if (--semtab[i].refcnt) { 181570af302Sopenharmony_ci#endif 182570af302Sopenharmony_ci UNLOCK(lock); 183570af302Sopenharmony_ci return 0; 184570af302Sopenharmony_ci } 185570af302Sopenharmony_ci semtab[i].sem = 0; 186570af302Sopenharmony_ci semtab[i].ino = 0; 187570af302Sopenharmony_ci UNLOCK(lock); 188570af302Sopenharmony_ci munmap(sem, sizeof *sem); 189570af302Sopenharmony_ci return 0; 190570af302Sopenharmony_ci} 191