diff --git a/src/Makefile b/src/Makefile index d40c90b..f42de7e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -168,17 +168,20 @@ all: ${LIBS} ${BINS} libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME -${LIBS_OBJ}: libfaketime.c +ft_sem.o: ft_sem.c ft_sem.h + ${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} $< + +${LIBS_OBJ}: libfaketime.c ft_sem.h ${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< -%.so.${SONAME}: %.o libfaketime.map - ${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD} +%.so.${SONAME}: %.o ft_sem.o libfaketime.map + ${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ft_sem.o ${LDADD} -${BINS}: faketime.c - ${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS} +${BINS}: faketime.c ft_sem.o ft_sem.h + ${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ft_sem.o ${LDFLAGS} ${BIN_LDFLAGS} clean: - @rm -f ${LIBS_OBJ} ${LIBS} ${BINS} + @rm -f ${LIBS_OBJ} ${LIBS} ${BINS} ft_sem.o distclean: clean @echo diff --git a/src/faketime.c b/src/faketime.c index cabbc49..3cf4e0f 100644 --- a/src/faketime.c +++ b/src/faketime.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include "ft_sem.h" #include "faketime_common.h" @@ -60,6 +60,7 @@ static const char *date_cmd = "date"; /* semaphore and shared memory names */ char sem_name[PATH_BUFSIZE] = {0}, shm_name[PATH_BUFSIZE] = {0}; +static ft_sem_t wrapper_sem; void usage(const char *name) { @@ -98,9 +99,9 @@ void usage(const char *name) /** Clean up shared objects */ static void cleanup_shobjs() { - if (-1 == sem_unlink(sem_name)) + if (-1 == ft_sem_unlink(&wrapper_sem)) { - perror("faketime: sem_unlink"); + perror("faketime: ft_sem_unlink"); } if (-1 == shm_unlink(shm_name)) { @@ -255,9 +256,8 @@ int main (int argc, char **argv) curr_opt++; { - /* create semaphores and shared memory */ + /* create lock and shared memory */ int shm_fd; - sem_t *sem; struct ft_shared_s *ft_shared; char shared_objs[PATH_BUFSIZE * 2 + 1]; @@ -271,10 +271,10 @@ int main (int argc, char **argv) snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%ld", (long)getpid()); snprintf(shm_name, PATH_BUFSIZE -1 ,"/faketime_shm_%ld", (long)getpid()); - if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1))) + if (-1 == ft_sem_create(sem_name, &wrapper_sem)) { - perror("faketime: sem_open"); - fprintf(stderr, "The faketime wrapper only works on platforms that support the sem_open()\nsystem call. However, you may LD_PRELOAD libfaketime without using this wrapper.\n"); + perror("faketime: ft_sem_create"); + fprintf(stderr, "The faketime wrapper failed to create its lock.\nHowever, you may LD_PRELOAD libfaketime without using this wrapper.\n"); exit(EXIT_FAILURE); } @@ -282,15 +282,12 @@ int main (int argc, char **argv) if (-1 == (shm_fd = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR))) { perror("faketime: shm_open"); - if (-1 == sem_unlink(argv[2])) - { - perror("faketime: sem_unlink"); - } + ft_sem_unlink(&wrapper_sem); exit(EXIT_FAILURE); } /* set shm size */ - if (-1 == ftruncate(shm_fd, sizeof(uint64_t))) + if (-1 == ftruncate(shm_fd, sizeof(struct ft_shared_s))) { perror("faketime: ftruncate"); cleanup_shobjs(); @@ -306,9 +303,9 @@ int main (int argc, char **argv) exit(EXIT_FAILURE); } - if (sem_wait(sem) == -1) + if (ft_sem_lock(&wrapper_sem) == -1) { - perror("faketime: sem_wait"); + perror("faketime: ft_sem_lock"); cleanup_shobjs(); exit(EXIT_FAILURE); } @@ -316,12 +313,16 @@ int main (int argc, char **argv) /* init elapsed time ticks to zero */ ft_shared->ticks = 0; ft_shared->file_idx = 0; - ft_shared->start_time.real.tv_sec = 0; - ft_shared->start_time.real.tv_nsec = -1; - ft_shared->start_time.mon.tv_sec = 0; - ft_shared->start_time.mon.tv_nsec = -1; - ft_shared->start_time.mon_raw.tv_sec = 0; - ft_shared->start_time.mon_raw.tv_nsec = -1; + ft_shared->start_time_real.sec = 0; + ft_shared->start_time_real.nsec = -1; + ft_shared->start_time_mon.sec = 0; + ft_shared->start_time_mon.nsec = -1; + ft_shared->start_time_mon_raw.sec = 0; + ft_shared->start_time_mon_raw.nsec = -1; +#ifdef CLOCK_BOOTTIME + ft_shared->start_time_boot.sec = 0; + ft_shared->start_time_boot.nsec = -1; +#endif if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s)))) { @@ -330,16 +331,16 @@ int main (int argc, char **argv) exit(EXIT_FAILURE); } - if (sem_post(sem) == -1) + if (ft_sem_unlock(&wrapper_sem) == -1) { - perror("faketime: semop"); + perror("faketime: ft_sem_unlock"); cleanup_shobjs(); exit(EXIT_FAILURE); } snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name); setenv("FAKETIME_SHARED", shared_objs, true); - sem_close(sem); + ft_sem_close(&wrapper_sem); } { diff --git a/src/faketime_common.h b/src/faketime_common.h index ab47d89..3d82b2f 100644 --- a/src/faketime_common.h +++ b/src/faketime_common.h @@ -38,6 +38,13 @@ struct system_time_s #endif }; +/* Fixed-width time representation for shared memory (arch-independent) */ +struct ft_shared_time_s +{ + int64_t sec; + int64_t nsec; +}; + /* Data shared among faketime-spawned processes */ struct ft_shared_s { @@ -47,8 +54,13 @@ struct ft_shared_s uint64_t ticks; /* Index of timestamp to be loaded from file */ uint64_t file_idx; - /* System time Faketime started at */ - struct system_time_s start_time; + /* System time Faketime started at (fixed-width for cross-arch safety) */ + struct ft_shared_time_s start_time_real; + struct ft_shared_time_s start_time_mon; + struct ft_shared_time_s start_time_mon_raw; +#ifdef CLOCK_BOOTTIME + struct ft_shared_time_s start_time_boot; +#endif }; /* These are all needed in order to properly build on OSX */ diff --git a/src/ft_sem.c b/src/ft_sem.c new file mode 100644 index 0000000..f565ae6 --- /dev/null +++ b/src/ft_sem.c @@ -0,0 +1,220 @@ +/* + * This file is part of libfaketime, version 0.9.12 + * + * libfaketime is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * libfaketime is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License v2 along + * with the libfaketime; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ft_sem.h" + +#if FT_SEMAPHORE_BACKEND == FT_SYSV +#include +#endif + +#if FT_SEMAPHORE_BACKEND == FT_FLOCK +#include +#endif + +/* + * ======================================================================= + * 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 + +#if FT_SEMAPHORE_BACKEND == FT_FLOCK +static int ft_sem_name_to_path(const char *name, char *path, size_t pathlen) +{ + const char *prefix = "/faketime_sem_"; + const char *p = strstr(name, prefix); + if (p == NULL) + { + return -1; + } + const char *pid_str = p + strlen(prefix); + snprintf(path, pathlen, "/dev/shm/faketime_lock_%s", pid_str); + path[pathlen - 1] = '\0'; + return 0; +} +#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; + } +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + char path[256]; + if (ft_sem_name_to_path(name, path, sizeof(path)) < 0) + { + return -1; + } + ft_sem->fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR); + if (ft_sem->fd < 0) + { + return -1; + } +#endif + strncpy(ft_sem->name, name, sizeof ft_sem->name - 1); + ft_sem->name[sizeof ft_sem->name - 1] = '\0'; + 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; + } +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + char path[256]; + if (ft_sem_name_to_path(name, path, sizeof(path)) < 0) + { + return -1; + } + ft_sem->fd = open(path, O_RDWR); + if (ft_sem->fd < 0) + { + return -1; + } +#endif + strncpy(ft_sem->name, name, sizeof ft_sem->name - 1); + ft_sem->name[sizeof ft_sem->name - 1] = '\0'; + 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); +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + return flock(ft_sem->fd, LOCK_EX); +#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); +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + return flock(ft_sem->fd, LOCK_UN); +#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; +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + int ret = close(ft_sem->fd); + ft_sem->fd = -1; + return ret; +#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); +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + char path[256]; + if (ft_sem_name_to_path(ft_sem->name, path, sizeof(path)) < 0) + { + return -1; + } + return unlink(path); +#endif +} diff --git a/src/ft_sem.h b/src/ft_sem.h new file mode 100644 index 0000000..9307a9c --- /dev/null +++ b/src/ft_sem.h @@ -0,0 +1,62 @@ +/* + * This file is part of libfaketime, version 0.9.12 + * + * libfaketime is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * libfaketime is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License v2 along + * with the libfaketime; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef FT_SEM_H +#define FT_SEM_H + +/* semaphore backend options */ +#define FT_POSIX 1 +#define FT_SYSV 2 +#define FT_FLOCK 3 + +/* set default backend */ +#ifndef FT_SEMAPHORE_BACKEND +#define FT_SEMAPHORE_BACKEND FT_FLOCK +#endif + +/* validate selected backend */ +#if FT_SEMAPHORE_BACKEND == FT_POSIX +#elif FT_SEMAPHORE_BACKEND == FT_SYSV +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK +#else +#error "Unknown FT_SEMAPHORE_BACKEND; select between FT_POSIX, FT_SYSV, and FT_FLOCK" +#endif + +#if FT_SEMAPHORE_BACKEND == FT_POSIX +#include +#endif + +typedef struct +{ + char name[256]; +#if FT_SEMAPHORE_BACKEND == FT_POSIX + sem_t *sem; +#elif FT_SEMAPHORE_BACKEND == FT_SYSV + int semid; +#elif FT_SEMAPHORE_BACKEND == FT_FLOCK + int fd; +#endif +} ft_sem_t; + +int ft_sem_create(char *name, ft_sem_t *ft_sem); +int ft_sem_open(char *name, ft_sem_t *ft_sem); +int ft_sem_lock(ft_sem_t *ft_sem); +int ft_sem_unlock(ft_sem_t *ft_sem); +int ft_sem_close(ft_sem_t *ft_sem); +int ft_sem_unlink(ft_sem_t *ft_sem); + +#endif /* FT_SEM_H */ diff --git a/src/libfaketime.c b/src/libfaketime.c index b8d3bfa..74caccb 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -75,6 +74,7 @@ #include "time_ops.h" #include "faketime_common.h" +#include "ft_sem.h" #if defined PTHREAD_SINGLETHREADED_TIME && defined FAKE_STATELESS #undef PTHREAD_SINGLETHREADED_TIME @@ -165,20 +165,6 @@ 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 @@ -352,15 +338,6 @@ 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[256]; -#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 ft_sem_t shared_sem; static bool shared_sem_initialized = false; @@ -450,144 +427,42 @@ 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 - strncpy(ft_sem->name, name, sizeof ft_sem->name - 1); - ft_sem->name[sizeof ft_sem->name - 1] = '\0'; - 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 - strncpy(ft_sem->name, name, sizeof ft_sem->name - 1); - ft_sem->name[sizeof ft_sem->name - 1] = '\0'; - 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 -} - /* * ======================================================================= * Shared memory related functions === SHM * ======================================================================= */ +static void system_time_to_shared(const struct system_time_s *src, + struct ft_shared_s *dst) +{ + dst->start_time_real.sec = (int64_t)src->real.tv_sec; + dst->start_time_real.nsec = (int64_t)src->real.tv_nsec; + dst->start_time_mon.sec = (int64_t)src->mon.tv_sec; + dst->start_time_mon.nsec = (int64_t)src->mon.tv_nsec; + dst->start_time_mon_raw.sec = (int64_t)src->mon_raw.tv_sec; + dst->start_time_mon_raw.nsec = (int64_t)src->mon_raw.tv_nsec; +#ifdef CLOCK_BOOTTIME + dst->start_time_boot.sec = (int64_t)src->boot.tv_sec; + dst->start_time_boot.nsec = (int64_t)src->boot.tv_nsec; +#endif +} + +static void shared_to_system_time(const struct ft_shared_s *src, + struct system_time_s *dst) +{ + dst->real.tv_sec = (time_t)src->start_time_real.sec; + dst->real.tv_nsec = (long)src->start_time_real.nsec; + dst->mon.tv_sec = (time_t)src->start_time_mon.sec; + dst->mon.tv_nsec = (long)src->start_time_mon.nsec; + dst->mon_raw.tv_sec = (time_t)src->start_time_mon_raw.sec; + dst->mon_raw.tv_nsec = (long)src->start_time_mon_raw.nsec; +#ifdef CLOCK_BOOTTIME + dst->boot.tv_sec = (time_t)src->start_time_boot.sec; + dst->boot.tv_nsec = (long)src->start_time_boot.nsec; +#endif +} + static bool shmCreator = false; static void ft_shm_create(void) { @@ -617,7 +492,7 @@ static void ft_shm_create(void) { exit(EXIT_FAILURE); } /* set shm size */ - if (-1 == ftruncate(shm_fdN, sizeof(uint64_t))) + if (-1 == ftruncate(shm_fdN, sizeof(struct ft_shared_s))) { perror("libfaketime: In ft_shm_create(), ftruncate failed"); exit(EXIT_FAILURE); @@ -637,12 +512,16 @@ static void ft_shm_create(void) { /* init elapsed time ticks to zero */ ft_sharedN->ticks = 0; ft_sharedN->file_idx = 0; - ft_sharedN->start_time.real.tv_sec = 0; - ft_sharedN->start_time.real.tv_nsec = -1; - ft_sharedN->start_time.mon.tv_sec = 0; - ft_sharedN->start_time.mon.tv_nsec = -1; - ft_sharedN->start_time.mon_raw.tv_sec = 0; - ft_sharedN->start_time.mon_raw.tv_nsec = -1; + ft_sharedN->start_time_real.sec = 0; + ft_sharedN->start_time_real.nsec = -1; + ft_sharedN->start_time_mon.sec = 0; + ft_sharedN->start_time_mon.nsec = -1; + ft_sharedN->start_time_mon_raw.sec = 0; + ft_sharedN->start_time_mon_raw.nsec = -1; +#ifdef CLOCK_BOOTTIME + ft_sharedN->start_time_boot.sec = 0; + ft_sharedN->start_time_boot.nsec = -1; +#endif if (-1 == munmap(ft_sharedN, (sizeof(struct ft_shared_s)))) { @@ -864,7 +743,7 @@ static void ft_cleanup (void) /* detach from shared memory */ if (ft_shared != NULL) { - munmap(ft_shared, sizeof(uint64_t)); + munmap(ft_shared, sizeof(struct ft_shared_s)); } if (stss != NULL) { @@ -989,7 +868,7 @@ static void reset_time() perror("libfaketime: In reset_time(), ft_sem_lock failed"); exit(1); } - ft_shared->start_time = ftpl_starttime; + system_time_to_shared(&ftpl_starttime, ft_shared); if (ft_sem_unlock(&shared_sem) == -1) { perror("libfaketime: In reset_time(), ft_sem_unlock failed"); @@ -1087,11 +966,11 @@ static bool load_time(struct timespec *tp) /* we set shared memory to stop using infile */ ft_shared->ticks = 1; system_time_from_system(&ftpl_starttime); - ft_shared->start_time = ftpl_starttime; + system_time_to_shared(&ftpl_starttime, ft_shared); } else { - ftpl_starttime = ft_shared->start_time; + shared_to_system_time(ft_shared, &ftpl_starttime); } munmap(stss, infile_size); @@ -3269,16 +3148,16 @@ static void ftpl_really_init(void) perror("libfaketime: In ftpl_init(), ft_sem_lock failed"); exit(1); } - if (ft_shared->start_time.real.tv_nsec == -1) + if (ft_shared->start_time_real.nsec == -1) { /* set up global start time */ system_time_from_system(&ftpl_starttime); - ft_shared->start_time = ftpl_starttime; + system_time_to_shared(&ftpl_starttime, ft_shared); } else { /** get preset start time */ - ftpl_starttime = ft_shared->start_time; + shared_to_system_time(ft_shared, &ftpl_starttime); } if (ft_sem_unlock(&shared_sem) == -1) { diff --git a/test/Makefile b/test/Makefile index 01cbd9f..b766188 100644 --- a/test/Makefile +++ b/test/Makefile @@ -9,7 +9,7 @@ OBJ = ${SRC:.c=.o} TEST_SNIPPETS = $(notdir $(basename $(wildcard snippets/*.c))) EXPECTATIONS = $(notdir $(basename $(wildcard snippets/*.variable))) -ALL_TESTS = timetest test +ALL_TESTS = timetest test shm_layout_test ifneq ($(filter -DINTERCEPT_SYSCALL,${CFLAGS}),) ALL_TESTS += confirm_variadic_promotion @@ -26,6 +26,9 @@ all: $(ALL_TESTS) timetest: ${OBJ} ${CC} -o $@ ${OBJ} ${LDFLAGS} +shm_layout_test: shm_layout_test.c ../src/faketime_common.h + ${CC} -o $@ ${CFLAGS} $< + test: timetest functest libmallocintercept.so @echo @./test.sh @@ -76,7 +79,7 @@ use_lib_%: _use_lib_test.c snippets/%.c lib%.so ## cleanup and metainformation clean: - @rm -f ${OBJ} timetest getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so + @rm -f ${OBJ} timetest shm_layout_test getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so distclean: clean @echo diff --git a/test/functests/test_shm_across_processes.sh b/test/functests/test_shm_across_processes.sh new file mode 100644 index 0000000..e4126ac --- /dev/null +++ b/test/functests/test_shm_across_processes.sh @@ -0,0 +1,31 @@ +# Verify that shared memory works end-to-end across processes. +# The faketime wrapper creates SHM with a known FAKETIME value, +# and the child process (via LD_PRELOAD) reads it and reports +# the faked time. + +init() +{ + typeset testsuite="$1" + PLATFORM=$(platform) + if [ -z "$PLATFORM" ]; then + echo "$testsuite: unknown platform! quitting" + return 1 + fi + echo "# PLATFORM=$PLATFORM" + return 0 +} + +run() +{ + init + + run_testcase shm_year_check +} + +shm_year_check() +{ + typeset expected="2020" + typeset actual + actual=$(fakecmd "2020-06-15 12:00:00" date -u +%Y) + asserteq "$actual" "$expected" "child process should see faked year via SHM" +} diff --git a/test/shm_layout_test.c b/test/shm_layout_test.c new file mode 100644 index 0000000..69f3ca6 --- /dev/null +++ b/test/shm_layout_test.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include "../src/faketime_common.h" + +static int failures = 0; + +static void check_offset(const char *field, size_t actual, size_t expected) +{ + if (actual != expected) + { + fprintf(stderr, "FAIL: offsetof(ft_shared_s, %s) = %zu, expected %zu\n", + field, actual, expected); + failures++; + } + else + { + printf("OK: offsetof(ft_shared_s, %s) = %zu\n", field, actual); + } +} + +static void check_size(const char *name, size_t actual, size_t expected) +{ + if (actual != expected) + { + fprintf(stderr, "FAIL: sizeof(%s) = %zu, expected %zu\n", + name, actual, expected); + failures++; + } + else + { + printf("OK: sizeof(%s) = %zu\n", name, actual); + } +} + +int main() +{ + /* ft_shared_time_s must be exactly 16 bytes: two int64_t */ + check_size("ft_shared_time_s", sizeof(struct ft_shared_time_s), 16); + + /* Field offsets in ft_shared_s */ + check_offset("ticks", offsetof(struct ft_shared_s, ticks), 0); + check_offset("file_idx", offsetof(struct ft_shared_s, file_idx), 8); + check_offset("start_time_real", offsetof(struct ft_shared_s, start_time_real), 16); + check_offset("start_time_mon", offsetof(struct ft_shared_s, start_time_mon), 32); + check_offset("start_time_mon_raw", offsetof(struct ft_shared_s, start_time_mon_raw), 48); +#ifdef CLOCK_BOOTTIME + check_offset("start_time_boot", offsetof(struct ft_shared_s, start_time_boot), 64); + check_size("ft_shared_s", sizeof(struct ft_shared_s), 80); +#else + check_size("ft_shared_s", sizeof(struct ft_shared_s), 64); +#endif + + if (failures > 0) + { + fprintf(stderr, "%d layout check(s) failed\n", failures); + return EXIT_FAILURE; + } + + printf("All layout checks passed\n"); + return EXIT_SUCCESS; +}