Merge pull request #504 from bjornfor/system-v-semaphores

Add optional System V semaphores
This commit is contained in:
Wolfgang Hommel
2025-06-27 19:33:14 +02:00
committed by GitHub

View File

@@ -51,6 +51,7 @@
#include <string.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <netinet/in.h>
@@ -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 <sys/random.h>
#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);
}
}