diff --git a/src/libfaketime.c b/src/libfaketime.c index 94bd74f..accab96 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -164,6 +165,20 @@ struct utimbuf { #endif #endif +/* semaphore backend options */ +#define FT_POSIX 1 +#define FT_SYSV 2 +/* set default backend */ +#ifndef FT_SEMAPHORE_BACKEND +#define FT_SEMAPHORE_BACKEND FT_POSIX +#endif +/* validate selected backend */ +#if FT_SEMAPHORE_BACKEND == FT_POSIX +#elif FT_SEMAPHORE_BACKEND == FT_SYSV +#else +#error "Unknown FT_SEMAPHORE_BACKEND; select between FT_POSIX and FT_SYSV" +#endif + #ifdef FAKE_RANDOM #include #endif @@ -334,8 +349,19 @@ int read_config_file(); bool str_array_contains(const char *haystack, const char *needle); void *ft_dlvsym(void *handle, const char *symbol, const char *version, const char *full_name, char *ignore_list, bool should_debug_dlsym); + +typedef struct { + char *name; +#if FT_SEMAPHORE_BACKEND == FT_POSIX + sem_t *sem; +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + int semid; +#endif +} ft_sem_t; + /** Semaphore protecting shared data */ -static sem_t *shared_sem = NULL; +static ft_sem_t shared_sem; +static bool shared_sem_initialized = false; /** Data shared among faketime-spawned processes */ static struct ft_shared_s *ft_shared = NULL; @@ -422,6 +448,135 @@ static bool parse_config_file = true; static void ft_cleanup (void) __attribute__ ((destructor)); static void ftpl_init (void) __attribute__ ((constructor)); +/* + * ======================================================================= + * Semaphore related functions === SEM + * ======================================================================= + */ + +#if FT_SEMAPHORE_BACKEND == FT_SYSV +int ft_sem_name2key(char *name) +{ + key_t key; + char fullname[256]; + snprintf(fullname, sizeof(fullname), "/tmp%s", name); + fullname[sizeof(fullname) - 1] = '\0'; + int fd = open(fullname, O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) + { + perror("libfaketime: open"); + return -1; + } + close(fd); + if (-1 == (key = ftok(fullname, 'F'))) + { + perror("libfaketime: ftok"); + return -1; + } + return key; +} +#endif + +int ft_sem_create(char *name, ft_sem_t *ft_sem) +{ +#if FT_SEMAPHORE_BACKEND == FT_POSIX + if (SEM_FAILED == (ft_sem->sem = sem_open(name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) + { + return -1; + } +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + key_t key = ft_sem_name2key(name); + int nsems = 1; + + ft_sem->semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | S_IWUSR | S_IRUSR); + if (ft_sem->semid >= 0) { /* we got here first */ + struct sembuf sb = { + .sem_num = 0, + .sem_op = 1, /* number of resources the semaphore has (when decremented down to 0 the semaphore will block) */ + .sem_flg = 0, + }; + /* the number of semaphore operation structures (struct sembuf) passed to semop */ + int nsops = 1; + + /* do a semop() to "unlock" the semaphore */ + if (-1 == semop(ft_sem->semid, &sb, nsops)) { + return -1; + } + } else if (errno == EEXIST) { /* someone else got here before us */ + return -1; + } else { + return -1; + } +#endif + ft_sem->name = name; + return 0; +} + +int ft_sem_open(char *name, ft_sem_t *ft_sem) +{ +#if FT_SEMAPHORE_BACKEND == FT_POSIX + if (SEM_FAILED == (ft_sem->sem = sem_open(name, 0))) + { + return -1; + } +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + key_t key = ft_sem_name2key(name); + ft_sem->semid = semget(key, 1, S_IWUSR | S_IRUSR); + if (ft_sem->semid < 0) { + return -1; + } +#endif + ft_sem->name = name; + return 0; +} + +int ft_sem_lock(ft_sem_t *ft_sem) +{ +#if FT_SEMAPHORE_BACKEND == FT_POSIX + return sem_wait(ft_sem->sem); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + struct sembuf sb = { + .sem_num = 0, + .sem_op = -1, /* allocate resource (lock) */ + .sem_flg = 0, + }; + return semop(ft_sem->semid, &sb, 1); +#endif +} + +int ft_sem_unlock(ft_sem_t *ft_sem) +{ +#if FT_SEMAPHORE_BACKEND == FT_POSIX + return sem_post(ft_sem->sem); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + struct sembuf sb = { + .sem_num = 0, + .sem_op = 1, /* free resource (unlock) */ + .sem_flg = 0, + }; + return semop(ft_sem->semid, &sb, 1); +#endif +} + +int ft_sem_close(ft_sem_t *ft_sem) +{ +#if FT_SEMAPHORE_BACKEND == FT_POSIX + return sem_close(ft_sem->sem); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + /* NOP -- there's no "close" equivalent */ + (void)ft_sem; + return 0; +#endif +} + +int ft_sem_unlink(ft_sem_t *ft_sem) +{ +#if FT_SEMAPHORE_BACKEND == FT_POSIX + return sem_unlink(ft_sem->name); +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + return semctl(ft_sem->semid, 0, IPC_RMID); +#endif +} /* * ======================================================================= @@ -434,10 +589,10 @@ static bool shmCreator = false; static void ft_shm_create(void) { char sem_name[256], shm_name[256], sem_nameT[256], shm_nameT[256]; int shm_fdN; - sem_t *semN; + ft_sem_t semN; struct ft_shared_s *ft_sharedN; char shared_objsN[513]; - sem_t *shared_semT = NULL; + ft_sem_t shared_semT; pid_t pid; #ifdef FAKE_PID @@ -447,8 +602,8 @@ static void ft_shm_create(void) { #endif snprintf(sem_name, 255, "/faketime_sem_%ld", (long)pid); snprintf(shm_name, 255, "/faketime_shm_%ld", (long)pid); - if (SEM_FAILED == (semN = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) - { /* silently fail on platforms that do not support sem_open() */ + if (-1 == ft_sem_create(sem_name, &semN)) + { /* silently fail on platforms that do not support semaphores */ return; } /* create shm */ @@ -470,9 +625,9 @@ static void ft_shm_create(void) { perror("libfaketime: In ft_shm_create(), mmap failed"); exit(EXIT_FAILURE); } - if (sem_wait(semN) == -1) + if (ft_sem_lock(&semN) == -1) { - perror("libfaketime: In ft_shm_create(), sem_wait failed"); + perror("libfaketime: In ft_shm_create(), ft_sem_lock failed"); exit(EXIT_FAILURE); } /* init elapsed time ticks to zero */ @@ -490,25 +645,25 @@ static void ft_shm_create(void) { perror("libfaketime: In ft_shm_create(), munmap failed"); exit(EXIT_FAILURE); } - if (sem_post(semN) == -1) + if (ft_sem_unlock(&semN) == -1) { - perror("libfaketime: In ft_shm_create(), sem_post failed"); + perror("libfaketime: In ft_shm_create(), ft_sem_unlock failed"); exit(EXIT_FAILURE); } snprintf(shared_objsN, sizeof(shared_objsN), "%s %s", sem_name, shm_name); int semSafetyCheckPassed = 0; - sem_close(semN); + ft_sem_close(&semN); sscanf(shared_objsN, "%255s %255s", sem_nameT, shm_nameT); - if (SEM_FAILED == (shared_semT = sem_open(sem_nameT, 0))) + if (-1 == ft_sem_open(sem_nameT, &shared_semT)) { - fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal sem_open issue with %s", sem_nameT); + fprintf(stderr, "libfaketime: In ft_shm_create(), non-fatal ft_sem_open issue with %s\n", sem_nameT); } else { semSafetyCheckPassed = 1; - sem_close(shared_semT); + ft_sem_close(&shared_semT); } if (semSafetyCheckPassed == 1) { @@ -520,6 +675,7 @@ static void ft_shm_create(void) { static void ft_shm_destroy(void) { char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); + ft_sem_t ft_sem; if (ft_shared_env != NULL) { @@ -536,7 +692,10 @@ static void ft_shm_destroy(void) Since there is no easy solution for this problem (see issue #56), ft_shm_init() below at least tries to handle this carefully. */ - sem_unlink(sem_name); + if (0 == ft_sem_open(sem_name, &ft_sem)) + { + ft_sem_unlink(&ft_sem); + } shm_unlink(shm_name); unsetenv("FAKETIME_SHARED"); } @@ -554,7 +713,7 @@ static void ft_shm_really_init (void) { int ticks_shm_fd; char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); - sem_t *shared_semR = NULL; + ft_sem_t shared_semR; static int nt=1; /* create semaphore and shared memory locally unless it has been passed along */ @@ -572,14 +731,14 @@ static void ft_shm_really_init (void) printf("libfaketime: In ft_shm_init(), error parsing semaphore name and shared memory id from string: %s", ft_shared_env); exit(1); } - if (SEM_FAILED == (shared_semR = sem_open(sem_name, 0))) /* gone stale? */ + if (-1 == ft_sem_open(sem_name, &shared_semR)) /* gone stale? */ { ft_shm_create(); ft_shared_env = getenv("FAKETIME_SHARED"); } else { - sem_close(shared_semR); + ft_sem_close(&shared_semR); } } @@ -592,7 +751,7 @@ static void ft_shm_really_init (void) exit(1); } - if (SEM_FAILED == (shared_sem = sem_open(sem_name, 0))) + if (-1 == ft_sem_open(sem_name, &shared_sem)) { if (shmCreator) { @@ -617,6 +776,7 @@ static void ft_shm_really_init (void) } } + shared_sem_initialized = true; if (-1 == (ticks_shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR))) { @@ -648,10 +808,10 @@ static void ft_cleanup (void) { munmap(stss, infile_size); } - if (shared_sem != NULL) + if (shared_sem_initialized) { - sem_close(shared_sem); - shared_sem = NULL; + ft_sem_close(&shared_sem); + shared_sem_initialized = false; } #ifdef FAKE_PTHREAD if (pthread_rwlock_destroy(&monotonic_conds_lock) != 0) { @@ -727,11 +887,11 @@ static void system_time_from_system (struct system_time_s * systime) static void next_time(struct timespec *tp, struct timespec *ticklen) { - if (shared_sem != NULL) + if (shared_sem_initialized) { struct timespec inc; /* lock */ - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -740,7 +900,7 @@ static void next_time(struct timespec *tp, struct timespec *ticklen) } else { - perror("libfaketime: In next_time(), sem_wait failed"); + perror("libfaketime: In next_time(), ft_sem_lock failed"); exit(1); } } @@ -749,9 +909,9 @@ static void next_time(struct timespec *tp, struct timespec *ticklen) timespecadd(&user_faked_time_timespec, &inc, tp); (ft_shared->ticks)++; /* unlock */ - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In next_time(), sem_post failed"); + perror("libfaketime: In next_time(), ft_sem_unlock failed"); exit(1); } } @@ -760,17 +920,17 @@ static void next_time(struct timespec *tp, struct timespec *ticklen) static void reset_time() { system_time_from_system(&ftpl_starttime); - if (shared_sem != NULL) + if (shared_sem_initialized) { - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { - perror("libfaketime: In reset_time(), sem_wait failed"); + perror("libfaketime: In reset_time(), ft_sem_lock failed"); exit(1); } ft_shared->start_time = ftpl_starttime; - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In reset_time(), sem_post failed"); + perror("libfaketime: In reset_time(), ft_sem_unlock failed"); exit(1); } } @@ -785,7 +945,7 @@ static void reset_time() static void save_time(struct timespec *tp) { - if ((shared_sem != NULL) && (outfile != -1)) + if (shared_sem_initialized && (outfile != -1)) { struct saved_timestamp time_write; ssize_t written; @@ -795,7 +955,7 @@ static void save_time(struct timespec *tp) time_write.nsec = htobe64(tp->tv_nsec); /* lock */ - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -804,7 +964,7 @@ static void save_time(struct timespec *tp) } else { - perror("libfaketime: In save_time(), sem_wait failed"); + perror("libfaketime: In save_time(), ft_sem_lock failed"); exit(1); } } @@ -823,9 +983,9 @@ static void save_time(struct timespec *tp) } /* unlock */ - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In save_time(), sem_post failed"); + perror("libfaketime: In save_time(), ft_sem_unlcok failed"); exit(1); } } @@ -838,10 +998,10 @@ static void save_time(struct timespec *tp) static bool load_time(struct timespec *tp) { bool ret = false; - if ((shared_sem != NULL) && (infile_set)) + if (shared_sem_initialized && (infile_set)) { /* lock */ - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -849,7 +1009,7 @@ static bool load_time(struct timespec *tp) } else { - perror("libfaketime: In load_time(), sem_wait failed"); + perror("libfaketime: In load_time(), ft_sem_lock failed"); exit(1); } } @@ -883,9 +1043,9 @@ static bool load_time(struct timespec *tp) } /* unlock */ - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In load_time(), sem_post failed"); + perror("libfaketime: In load_time(), ft_sem_unlock failed"); exit(1); } } @@ -919,9 +1079,9 @@ static bool user_per_tick_inc_set_backup = false; void lock_for_stat() { - if (shared_sem != NULL) + if (shared_sem_initialized) { - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { if (errno == EINTR) { @@ -930,7 +1090,7 @@ void lock_for_stat() } else { - perror("libfaketime: In lock_for_stat(), sem_wait failed"); + perror("libfaketime: In lock_for_stat(), ft_sem_lock failed"); exit(1); } } @@ -944,11 +1104,11 @@ void unlock_for_stat() { user_per_tick_inc_set = user_per_tick_inc_set_backup; - if (shared_sem != NULL) + if (shared_sem_initialized) { - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In unlock_for_stat(), sem_post failed"); + perror("libfaketime: In unlock_for_stat(), ft_sem_unlock failed"); exit(1); } } @@ -3032,11 +3192,11 @@ static void ftpl_really_init(void) user_faked_time_fmt[BUFSIZ - 1] = 0; } - if (shared_sem != 0) + if (shared_sem_initialized) { - if (sem_wait(shared_sem) == -1) + if (ft_sem_lock(&shared_sem) == -1) { - perror("libfaketime: In ftpl_init(), sem_wait failed"); + perror("libfaketime: In ftpl_init(), ft_sem_lock failed"); exit(1); } if (ft_shared->start_time.real.tv_nsec == -1) @@ -3050,9 +3210,9 @@ static void ftpl_really_init(void) /** get preset start time */ ftpl_starttime = ft_shared->start_time; } - if (sem_post(shared_sem) == -1) + if (ft_sem_unlock(&shared_sem) == -1) { - perror("libfaketime: In ftpl_init(), sem_post failed"); + perror("libfaketime: In ftpl_init(), ft_sem_unlock failed"); exit(1); } }