diff options
-rw-r--r-- | README | 23 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | src/Makefile | 10 | ||||
-rw-r--r-- | src/Makefile.MacOS | 11 | ||||
-rw-r--r-- | src/faketime.c | 18 | ||||
-rw-r--r-- | src/faketime_common.h | 49 | ||||
-rw-r--r-- | src/libfaketime.c | 1172 | ||||
-rw-r--r-- | src/time_ops.h | 99 | ||||
-rwxr-xr-x | test/test.sh | 4 |
9 files changed, 898 insertions, 490 deletions
@@ -19,6 +19,7 @@ Content of this file: g) Using the "faketime" wrapper script h) "Limiting" libfaketime i) Spawning an external process + j) Saving timestamps to file, loading them from file 5. License 6. Contact @@ -424,11 +425,31 @@ This will run the "echo" command with the given parameter during the first time-related system function call that "myprogram" performs after running for 5 seconds. +4j) Saving timestamps to file, loading them from file +-------------------------------- + +Faketime can save faked timestamps to a file specified by FAKETIME_SAVE_FILE +environment variable. It can also use the file specified by FAKETIME_LOAD_FILE +to replay timestamps from it. After consuming the whole file faketime returns +to using the rule set in FAKETIME variable, but the timestamp processes will +start counting from will be the last timestamp in the file. + +The file stores each timestamp in a stream of saved_timestamp structs +without any metadata or padding: + +/** Storage format for timestamps written to file. Big endian.*/ +struct saved_timestamp { + int64_t sec; + uint64_t nsec; +}; + + +Faketime needs to be run using the faketime wrapper to use the files. 5. License ---------- -FTPL has been released under the GNU Public License, GPL. Please see the +FTPL has been released under the GNU Public License, GPL. Please see xthe included COPYING file. @@ -4,3 +4,5 @@ Open issues / next steps for libfaketime development - use the new testing framework to also implement unit tests - make the new "limiting" and "spawning" features more flexible to use and available through the wrapper shell script +- fake timer_create and friends +- handle CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE diff --git a/src/Makefile b/src/Makefile index 51b5100..70d405e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -30,14 +30,6 @@ # frequently. Disabling the cache may negatively influence the # performance. # -# LIMITEDFAKING -# - Support environment variables that limit time faking to certain -# time intervals or number of function calls. -# -# SPAWNSUPPORT -# - Enable support for spawning an external process at a given -# timestamp. -# # * Compilation addition: second libMT target added for building the pthread- # enabled library as a separate library # @@ -52,7 +44,7 @@ INSTALL ?= install PREFIX ?= /usr/local -CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -DFAKE_INTERNAL_CALLS -fPIC -DPOSIX_REALTIME -DLIMITEDFAKING -DSPAWNSUPPORT -DPREFIX='"'$(PREFIX)'"' +CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' LIB_LDFLAGS += -shared LDFLAGS += -lrt LDADD += -ldl -lm -lpthread diff --git a/src/Makefile.MacOS b/src/Makefile.MacOS index 20bf0a3..6ceff9e 100644 --- a/src/Makefile.MacOS +++ b/src/Makefile.MacOS @@ -30,15 +30,6 @@ # frequently. Disabling the cache may negatively influence the # performance. # -# LIMITEDFAKING -# - Support environment variables that limit time faking to certain -# time intervals or number of function calls. -# -# SPAWNSUPPORT -# - Enable support for spawning an external process at a given -# timestamp. -# -# # * Compilation addition: second libMT target added for building the pthread- # enabled library as a separate library # @@ -60,7 +51,7 @@ PREFIX ?= /usr/local # 10.5 #CFLAGS = -dynamiclib -DFAKE_INTERNAL_CALLS -arch i386 -arch ppc # 10.6 -CFLAGS = -dynamiclib -DFAKE_INTERNAL_CALLS -arch i386 -arch x86_64 -DLIMITEDFAKING -DSPAWNSUPPORT -DPREFIX='"'$(PREFIX)'"' +CFLAGS = -dynamiclib -DFAKE_INTERNAL_CALLS -arch i386 -arch x86_64 -DPREFIX='"'$(PREFIX)'"' LIB_SRC = libfaketime.c SONAME = 1 diff --git a/src/faketime.c b/src/faketime.c index 163ed5c..af25edf 100644 --- a/src/faketime.c +++ b/src/faketime.c @@ -32,6 +32,8 @@ #include <sys/mman.h> #include <semaphore.h> +#include "faketime_common.h" + const char version[] = "0.8"; #ifdef __APPLE__ @@ -161,7 +163,7 @@ int main (int argc, char **argv) /* create semaphores and shared memory */ int shm_fd; sem_t *sem; - uint64_t *ticks; + struct ft_shared_s *ft_shared; char shared_objs[PATH_BUFSIZE]; snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%d", getpid()); @@ -189,7 +191,7 @@ int main (int argc, char **argv) } /* map shm */ - if (MAP_FAILED == (ticks = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, + if (MAP_FAILED == (ft_shared = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0))) { perror("mmap"); cleanup_shobjs(); @@ -203,8 +205,16 @@ int main (int argc, char **argv) } /* init elapsed time ticks to zero */ - *ticks = 0; - if (-1 == munmap(ticks, (sizeof(uint64_t)))) { + 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; + + if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s)))) { perror("munmap"); cleanup_shobjs(); exit(EXIT_FAILURE); diff --git a/src/faketime_common.h b/src/faketime_common.h new file mode 100644 index 0000000..3284c70 --- /dev/null +++ b/src/faketime_common.h @@ -0,0 +1,49 @@ +/* + * Faketime's common definitions + * + * Copyright 2013 Balint Reczey <balint@balintreczey.hu> + * + * This file is part of the FakeTime Preload Library. + * + * The FakeTime Preload Library 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. + * + * The FakeTime Preload Library 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 FakeTime Preload Library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef FAKETIME_COMMON_H +#define FAKETIME_COMMON_H + +#include <stdint.h> + +struct system_time_s { + /** System time according to CLOCK_REALTIME */ + struct timespec real; + /** System time according to CLOCK_MONOTONIC */ + struct timespec mon; + /** System time according to CLOCK_MONOTONIC_RAW */ + struct timespec mon_raw; +}; + + +/** Data shared among faketime-spawned processes */ +struct ft_shared_s { + /** + * When advancing time linearly with each time(), etc. call, the calls are + * counted here */ + uint64_t ticks; + /** Index of timstamp to be loaded from file */ + uint64_t file_idx; + /** System time Faketime started at */ + struct system_time_s start_time; +}; + +#endif diff --git a/src/libfaketime.c b/src/libfaketime.c index 8bab585..376ed95 100644 --- a/src/libfaketime.c +++ b/src/libfaketime.c @@ -26,34 +26,24 @@ #include <unistd.h> #include <fcntl.h> #include <time.h> +#include <math.h> +#include <errno.h> #include <string.h> #include <semaphore.h> #include <sys/mman.h> #include <sys/stat.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "time_ops.h" +#include "faketime_common.h" /* pthread-handling contributed by David North, TDI in version 0.7 */ #ifdef PTHREAD #include <pthread.h> -static pthread_mutex_t once_mutex=PTHREAD_MUTEX_INITIALIZER; - -#define SINGLE_IF_MTX(ifcondition,mtxaddr) \ - if (ifcondition) { \ - pthread_mutex_lock(mtxaddr); \ - pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, (void *)mtxaddr); \ - if (ifcondition) { -#define SINGLE_IF(ifcondition) SINGLE_IF_MTX(ifcondition,&once_mutex) -#define END_SINGLE_IF \ - } \ - pthread_cleanup_pop(1); \ - } - #else -#define SINGLE_IF_MTX(ifcondition,mtxaddr) if (ifcondition) { -#define SINGLE_IF(ifcondition) if (ifcondition) { -#define END_SINGLE_IF } - #endif #include <sys/timeb.h> @@ -61,13 +51,88 @@ static pthread_mutex_t once_mutex=PTHREAD_MUTEX_INITIALIZER; #define BUFFERLEN 256 +/* We fix endiannes on Apple to little endian */ +#ifdef __APPLE__ + +/* We fix endianness on Apple to little endian */ +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +/* clock_gettime() and related clock definitions are missing on __APPLE__ */ +#ifndef CLOCK_REALTIME +/* from GNU C Library time.h */ +/* Identifier for system-wide realtime clock. ( == 1) */ +#define CLOCK_REALTIME CALENDAR_CLOCK +/* Monotonic system-wide clock. (== 0) */ +#define CLOCK_MONOTONIC SYSTEM_CLOCK +/* High-resolution timer from the CPU. */ +#define CLOCK_PROCESS_CPUTIME_ID 2 +/* Thread-specific CPU-time clock. */ +#define CLOCK_THREAD_CPUTIME_ID 3 +/* Monotonic system-wide clock, not adjusted for frequency scaling. */ +#define CLOCK_MONOTONIC_RAW 4 + +typedef int clockid_t; + +#include <mach/clock.h> +#include <mach/mach.h> + +#endif + +#endif + +/** + * Per thread variable which we turn on inside real_* calls to avoid modifying + * time multiple times + */ +__thread bool dont_fake = false; + +/** Wrapper for function calls which we want to return system time */ +#define DONT_FAKE_TIME(call) \ + { \ + bool dont_fake_orig = dont_fake; \ + if (!dont_fake) { \ + dont_fake = true; \ + } \ + call; \ + dont_fake = dont_fake_orig; \ + } while (0) + +/* real pointer to faked functions */ +static int (*real_stat) (int, const char *, struct stat *); +static int (*real_fstat) (int, int, struct stat *); +static int (*real_fstatat) (int, int, const char *, struct stat *, int); +static int (*real_lstat)(int, const char *, struct stat *); +static int (*real_stat64) (int, const char *, struct stat64 *); +static int (*real_fstat64)(int, int , struct stat64 *); +static int (*real_fstatat64)(int, int , const char *, struct stat64 *, int); +static int (*real_lstat64) (int, const char *, struct stat64 *); +static time_t (*real_time)(time_t *); +static int (*real_ftime)(struct timeb *); +static int (*real_gettimeofday)(struct timeval *, void *); +static int (*real_clock_gettime)(clockid_t clk_id, struct timespec *tp); +static int (*real_nanosleep)(const struct timespec *req, struct timespec *rem); +static int (*real_usleep)(useconds_t usec); +static unsigned int (*real_sleep)(unsigned int seconds); +static unsigned int (*real_alarm)(unsigned int seconds); +#ifdef __APPLE__ +static int (*real_clock_get_time)(clock_serv_t clock_serv, mach_timespec_t *cur_timeclockid_t); +static clock_serv_t clock_serv_real; +#endif /* prototypes */ time_t fake_time(time_t *time_tptr); int fake_ftime(struct timeb *tp); int fake_gettimeofday(struct timeval *tv, void *tz); -#ifdef POSIX_REALTIME int fake_clock_gettime(clockid_t clk_id, struct timespec *tp); -#endif /* * Intercepted system calls: @@ -91,27 +156,102 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp); * */ +/** Semaphore protecting shared data */ +static sem_t *shared_sem = NULL; + +/** Data shared among faketime-spawned processes */ +static struct ft_shared_s *ft_shared = NULL; + +/** Storage format for timestamps written to file. Big endian.*/ +struct saved_timestamp { + int64_t sec; + uint64_t nsec; +}; + +static inline void timespec_from_saved (struct timespec *tp, + struct saved_timestamp *saved) +{ + /* read as big endian */ +#if __BYTE_ORDER == __BIG_ENDIAN + tp->tv_sec = saved->sec; + tp->tv_nsec = saved->nsec; +#else + if (saved->sec < 0) { + uint64_t abs_sec = 0 - saved->sec; + ((uint32_t*)&(tp->tv_sec))[0] = ntohl(((uint32_t*)&abs_sec)[1]); + ((uint32_t*)&(tp->tv_sec))[1] = ntohl(((uint32_t*)&abs_sec)[0]); + tp->tv_sec = 0 - tp->tv_sec; + } else { + ((uint32_t*)&(tp->tv_sec))[0] = ntohl(((uint32_t*)&(saved->sec))[1]); + ((uint32_t*)&(tp->tv_sec))[1] = ntohl(((uint32_t*)&(saved->sec))[0]); + } + ((uint32_t*)&(tp->tv_nsec))[0] = ntohl(((uint32_t*)&(saved->nsec))[1]); + ((uint32_t*)&(tp->tv_nsec))[1] = ntohl(((uint32_t*)&(saved->nsec))[0]); + +#endif +} + +/** Saved timestamps */ +static struct saved_timestamp *stss = NULL; +static size_t infile_size; +static bool infile_set = false; + +/** File fd to save timestamps to */ +static int outfile = -1; + +static bool limited_faking = false; +static long callcounter = 0; +static long ft_start_after_secs = -1; +static long ft_stop_after_secs = -1; +static long ft_start_after_ncalls = -1; +static long ft_stop_after_ncalls = -1; + + +static bool spawnsupport = false; +static int spawned = 0; +static char ft_spawn_target[1024]; +static long ft_spawn_secs = -1; +static long ft_spawn_ncalls = -1; + + /** - * When advancing time linearly with each time(), etc. call, the calls are - * counted in shared memory pointed at by ticks and protected by ticks_sem - * semaphore */ -static sem_t *ticks_sem = NULL; -static uint64_t *ticks = NULL; + * Static timespec to store our startup time, followed by a load-time library + * initialization declaration. + */ +static struct system_time_s ftpl_starttime = {{0, -1}, {0, -1}, {0, -1}}; + +static char user_faked_time_fmt[BUFSIZ] = {0}; + +/** User supplied base time to fake */ +static struct timespec user_faked_time_timespec = {0, -1}; +/** User supplied base time is set */ +static bool user_faked_time_set = false; +/** Fractional user offset provided through FAKETIME env. var.*/ +static struct timespec user_offset = {0, -1}; +/** Speed up or slow down clock */ +static double user_rate = 1.0; +static bool user_rate_set = false; +static struct timespec user_per_tick_inc = {0, -1}; +static bool user_per_tick_inc_set = false; +enum ft_mode_t {FT_FREEZE, FT_START_AT} ft_mode = FT_FREEZE; + +/** Time to fake is not provided through FAKETIME env. var. */ +static bool parse_config_file = true; void ft_cleanup (void) __attribute__ ((destructor)); static void ft_shm_init (void) { int ticks_shm_fd; - char sem_name[256], shm_name[256], *ft_shared = getenv("FAKETIME_SHARED"); - if (ft_shared != NULL) { - if (sscanf(ft_shared, "%255s %255s", sem_name, shm_name) < 2 ) { - printf("Error parsing semaphor name and shared memory id from string: %s", ft_shared); + char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); + if (ft_shared_env != NULL) { + if (sscanf(ft_shared_env, "%255s %255s", sem_name, shm_name) < 2 ) { + printf("Error parsing semaphor name and shared memory id from string: %s", ft_shared_env); exit(1); } - if (SEM_FAILED == (ticks_sem = sem_open(sem_name, 0))) { + if (SEM_FAILED == (shared_sem = sem_open(sem_name, 0))) { perror("sem_open"); exit(1); } @@ -120,7 +260,7 @@ static void ft_shm_init (void) perror("shm_open"); exit(1); } - if (MAP_FAILED == (ticks = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, + if (MAP_FAILED == (ft_shared = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE, MAP_SHARED, ticks_shm_fd, 0))) { perror("mmap"); exit(1); @@ -131,31 +271,153 @@ static void ft_shm_init (void) void ft_cleanup (void) { /* detach from shared memory */ - munmap(ticks, sizeof(uint64_t)); - sem_close(ticks_sem); + if (ft_shared != NULL) { + munmap(ft_shared, sizeof(uint64_t)); + } + if (stss != NULL) { + munmap(stss, infile_size); + } + if (shared_sem != NULL) { + sem_close(shared_sem); + } } -static time_t next_time(double ticklen) +/** Get system time from system for all clocks */ +static void system_time_from_system (struct system_time_s * systime) { +#ifdef __APPLE__ + /* from http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x */ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clock_serv_real); + (*real_clock_get_time)(clock_serv_real, &mts); + systime->real.tv_sec = mts.tv_sec; + systime->real.tv_nsec = mts.tv_nsec; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + (*real_clock_get_time)(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + systime->mon.tv_sec = mts.tv_sec; + systime->mon.tv_nsec = mts.tv_nsec; + systime->mon_raw.tv_sec = mts.tv_sec; + systime->mon_raw.tv_nsec = mts.tv_nsec; +#else + DONT_FAKE_TIME((*real_clock_gettime)(CLOCK_REALTIME, &systime->real)); + DONT_FAKE_TIME((*real_clock_gettime)(CLOCK_MONOTONIC, &systime->mon)); + DONT_FAKE_TIME((*real_clock_gettime)(CLOCK_MONOTONIC_RAW, &systime->mon_raw)); +#endif +} + + +static void next_time(struct timespec *tp, struct timespec *ticklen) { - time_t ret = 0; + if (shared_sem != NULL) { + struct timespec inc; + /* lock */ + if (sem_wait(shared_sem) == -1) { + perror("sem_wait"); + exit(1); + } + /* calculate and update elapsed time */ + timespecmul(ticklen, ft_shared->ticks, &inc); + timespecadd(&user_faked_time_timespec, &inc, tp); + (ft_shared->ticks)++; + /* unlock */ + if (sem_post(shared_sem) == -1) { + perror("sem_post"); + exit(1); + } + } +} - if (ticks_sem != NULL) { +static void save_time(struct timespec *tp) +{ + if ((shared_sem != NULL) && (outfile != -1)) { + struct saved_timestamp time_write; + ssize_t n = 0; + + // write as big endian +#if __BYTE_ORDER == __BIG_ENDIAN + time_write.sec = tp->tv_sec; + time_write.nsec = tp->tv_nsec; +#else + if (tp->tv_sec < 0) { + uint64_t abs_sec = 0 - tp->tv_sec; + ((uint32_t*)&(time_write.sec))[0] = htonl(((uint32_t*)&abs_sec)[1]); + ((uint32_t*)&(time_write.sec))[1] = htonl(((uint32_t*)&abs_sec)[0]); + tp->tv_sec = 0 - tp->tv_sec; + } else { + ((uint32_t*)&(time_write.sec))[0] = htonl(((uint32_t*)&(tp->tv_sec))[1]); + ((uint32_t*)&(time_write.sec))[1] = htonl(((uint32_t*)&(tp->tv_sec))[0]); + } + ((uint32_t*)&(time_write.nsec))[0] = htonl(((uint32_t*)&(tp->tv_nsec))[1]); + ((uint32_t*)&(time_write.nsec))[1] = htonl(((uint32_t*)&(tp->tv_nsec))[0]); +#endif /* lock */ - if (sem_wait(ticks_sem) == -1) { + if (sem_wait(shared_sem) == -1) { perror("sem_wait"); exit(1); } - /* calculate and update elapsed time */ - ret = ticklen * (*ticks)++; + lseek(outfile, 0, SEEK_END); + while ((sizeof(time_write) < (n += write(outfile, &(((char*)&time_write)[n]), + sizeof(time_write) - n))) && + (errno == EINTR)); + + if ((n == -1) || (n < sizeof(time_write))) { + perror("Saving timestamp to file failed"); + } + /* unlock */ - if (sem_post(ticks_sem) == -1) { + if (sem_post(shared_sem) == -1) { perror("sem_post"); exit(1); } } - return ret; +} + +/** + * Provide faked time from file. + * @return time is set from filen + */ +static bool load_time(struct timespec *tp) +{ + bool ret = false; + if ((shared_sem != NULL) && (infile_set)) { + + /* lock */ + if (sem_wait(shared_sem) == -1) { + perror("sem_wait"); + exit(1); + } + if ((sizeof(stss[0]) * (ft_shared->file_idx + 1)) > infile_size) { + /* we are out of timstamps to replay, return to faking time by rules + * using last timestamp from file as the user provided timestamp */ + timespec_from_saved(&user_faked_time_timespec, &stss[(infile_size / sizeof(stss[0])) - 1 ]); + + if (ft_shared->ticks == 0) { + /* we set shared memory to stop using infile */ + ft_shared->ticks = 1; + system_time_from_system(&ftpl_starttime); + ft_shared->start_time = ftpl_starttime; + } else { + ftpl_starttime = ft_shared->start_time; + } + + munmap(stss, infile_size); + infile_set = false; + } else { + timespec_from_saved(tp, &stss[ft_shared->file_idx]); + ft_shared->file_idx++; + ret = true; + } + + /* unlock */ + if (sem_post(shared_sem) == -1) { + perror("sem_post"); + exit(1); + } + } + return ret; } #ifdef FAKE_STAT @@ -173,17 +435,7 @@ static int fake_stat_disabled = 0; /* Contributed by Philipp Hachtmann in version 0.6 */ int __xstat (int ver, const char *path, struct stat *buf) { - static int (*real_stat) (int, const char *, struct stat *); - static int has_real_stat=0; - - SINGLE_IF(has_real_stat==0) - real_stat = NULL; - real_stat = dlsym(RTLD_NEXT, "__xstat"); - if (dlerror() == NULL) { - has_real_stat = 1; - } - END_SINGLE_IF - if (!has_real_stat) { /* dlsym() failed */ + if (NULL == real_stat) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original stat() not found.\n"); #endif @@ -209,17 +461,7 @@ int __xstat (int ver, const char *path, struct stat *buf) { /* Contributed by Philipp Hachtmann in version 0.6 */ int __fxstat (int ver, int fildes, struct stat *buf) { - static int (*real_fstat) (int, int, struct stat *); - static int has_real_fstat=0; - - SINGLE_IF(has_real_fstat==0) - real_fstat = NULL; - real_fstat = dlsym(RTLD_NEXT, "__fxstat"); - if (dlerror() == NULL) { - has_real_fstat = 1; - } - END_SINGLE_IF - if (!has_real_fstat) { /* dlsym() failed */ + if (NULL == real_fstat) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original fstat() not found.\n"); #endif @@ -245,17 +487,8 @@ int __fxstat (int ver, int fildes, struct stat *buf) { /* Added in v0.8 as suggested by Daniel Kahn Gillmor */ #ifndef NO_ATFILE int __fxstatat(int ver, int fildes, const char *filename, struct stat *buf, int flag) { - static int (*real_fstatat) (int, int, const char *, struct stat *, int); - static int has_real_fstatat=0; - SINGLE_IF(has_real_fstatat==0) - real_fstatat = NULL; - real_fstatat = dlsym(RTLD_NEXT, "__fxstatat"); - if (dlerror() == NULL) { - has_real_fstatat = 1; - } - END_SINGLE_IF - if (!has_real_fstatat) { /* dlsym() failed */ + if (NULL == real_fstatat) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original fstatat() not found.\n"); #endif @@ -281,17 +514,7 @@ int __fxstatat(int ver, int fildes, const char *filename, struct stat *buf, int /* Contributed by Philipp Hachtmann in version 0.6 */ int __lxstat (int ver, const char *path, struct stat *buf) { - static int (*real_lstat)(int, const char *, struct stat *); - static int has_real_lstat=0; - - SINGLE_IF(has_real_lstat==0) - real_lstat = NULL; - real_lstat = dlsym(RTLD_NEXT, "__lxstat"); - if (dlerror() == NULL) { - has_real_lstat = 1; - } - END_SINGLE_IF - if (!has_real_lstat) { /* dlsym() failed */ + if (NULL == real_lstat) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original lstat() not found.\n"); #endif @@ -316,24 +539,15 @@ int __lxstat (int ver, const char *path, struct stat *buf) { /* Contributed by Philipp Hachtmann in version 0.6 */ int __xstat64 (int ver, const char *path, struct stat64 *buf) { - static int (*real_stat) (int, const char *, struct stat64 *); - static int has_real_stat = 0; - - SINGLE_IF(has_real_stat==0) - real_stat = NULL; - real_stat = dlsym(RTLD_NEXT,"__xstat64"); - if (dlerror() == NULL) { - has_real_stat = 1; - } - END_SINGLE_IF - if (!has_real_stat) { /* dlsym() failed */ + if (NULL == real_stat64) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original stat() not found.\n"); #endif return -1; /* propagate error to caller */ } - int result=real_stat(ver, path, buf); + int result; + DONT_FAKE_TIME(result = real_stat64(ver, path, buf)); if (result == -1) { return -1; } @@ -350,24 +564,15 @@ int __xstat64 (int ver, const char *path, struct stat64 *buf) { /* Contributed by Philipp Hachtmann in version 0.6 */ int __fxstat64 (int ver, int fildes, struct stat64 *buf) { - static int (*real_fstat)(int, int , struct stat64 *); - static int has_real_fstat=0; - - SINGLE_IF(has_real_fstat==0) - real_fstat = NULL; - real_fstat = dlsym(RTLD_NEXT, "__fxstat64"); - if (dlerror() == NULL) { - has_real_fstat = 1; - } - END_SINGLE_IF - if (!has_real_fstat) { /* dlsym() failed */ + if (NULL == real_fstat64) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original fstat() not found.\n"); #endif return -1; /* propagate error to caller */ } - int result = real_fstat(ver, fildes, buf); + int result; + DONT_FAKE_TIME(result = real_fstat64(ver, fildes, buf)); if (result == -1){ return -1; } @@ -385,17 +590,7 @@ int __fxstat64 (int ver, int fildes, struct stat64 *buf) { /* Added in v0.8 as suggested by Daniel Kahn Gillmor */ #ifndef NO_ATFILE int __fxstatat64 (int ver, int fildes, const char *filename, struct stat64 *buf, int flag) { - static int (*real_fstatat64)(int, int , const char *, struct stat64 *, int); - static int has_real_fstatat64=0; - - SINGLE_IF(has_real_fstatat64==0) - real_fstatat64 = NULL; - real_fstatat64 = dlsym(RTLD_NEXT, "__fxstatat64"); - if (dlerror() == NULL) { - has_real_fstatat64 = 1; - } - END_SINGLE_IF - if (!has_real_fstatat64) { /* dlsym() failed */ + if (NULL == real_fstatat64) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original fstatat64() not found.\n"); #endif @@ -421,24 +616,15 @@ int __fxstatat64 (int ver, int fildes, const char *filename, struct stat64 *buf, /* Contributed by Philipp Hachtmann in version 0.6 */ int __lxstat64 (int ver, const char *path, struct stat64 *buf){ - static int (*real_lstat) (int, const char *, struct stat64 *); - static int has_real_lstat = 0; - - SINGLE_IF(has_real_lstat==0) - real_lstat = NULL; - real_lstat = dlsym(RTLD_NEXT, "__lxstat64"); - if (dlerror() == NULL) { - has_real_lstat = 1; - } - END_SINGLE_IF - if (!has_real_lstat) { /* dlsym() failed */ + if (NULL == real_lstat64) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original lstat() not found.\n"); #endif return -1; /* propagate error to caller */ } - int result = real_lstat(ver, path, buf); + int result; + DONT_FAKE_TIME(result = real_lstat64(ver, path, buf)); if (result == -1){ return -1; } @@ -454,90 +640,86 @@ int __lxstat64 (int ver, const char *path, struct stat64 *buf){ } #endif -/* - * On MacOS, time() internally uses gettimeofday. If we don't - * break the cycle by just calling it directly, we double-apply - * relative changes. - */ - -#ifdef __APPLE__ -static int (*real_gettimeofday)(struct timeval *, void *); -static int has_real_gettimeofday = 0; -#endif -/* - * Our version of time() allows us to return fake values, so the calling - * program thinks it's retrieving the current date and time, while it is - * not - * Note that this routine is split into two parts so that the initialization - * piece can call the 'real' time function to establish a base time. +/** + * Faked nanosleep() */ -static time_t _ftpl_time(time_t *time_tptr) { -#ifdef __APPLE__ - struct timeval tvm, *tv = &tvm; -#else - static time_t (*real_time)(time_t *); - static int has_real_time = 0; -#endif +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + int result; + struct timespec real_req; - time_t result; + if (real_nanosleep == NULL) { + return -1; + } + if (req != NULL) { + if (user_rate_set && !dont_fake) { + timespecmul(req, 1.0 / user_rate, &real_req); + } else { + real_req = *req; + } + } else { + return -1; + } - time_t null_dummy; + DONT_FAKE_TIME(result = (*real_nanosleep)(&real_req, rem)); + if (result == -1) { + return result; + } - /* Handle null pointers correctly, fix as suggested by Andres Ojamaa */ - if (time_tptr == NULL) { - time_tptr = &null_dummy; - /* (void) fprintf(stderr, "NULL pointer caught in time().\n"); */ + /* fake returned parts */ + if ((rem != NULL) && ((rem->tv_sec != 0) || (rem->tv_nsec != 0))) { + if (user_rate_set && !dont_fake) { + timespecmul(rem, user_rate, rem); } + } + /* return the result to the caller */ + return result; +} -#ifdef __APPLE__ - /* Check whether we've got a pointer to the real ftime() function yet */ - SINGLE_IF(has_real_gettimeofday==0) - real_gettimeofday = NULL; - real_gettimeofday = dlsym(RTLD_NEXT, "gettimeofday"); +/** + * Faked usleep() + */ +int usleep(useconds_t usec) +{ - /* check whether dlsym() worked */ - if (dlerror() == NULL) { - has_real_gettimeofday = 1; - } - END_SINGLE_IF - if (!has_real_gettimeofday) { /* dlsym() failed */ -#ifdef DEBUG - (void) fprintf(stderr, "faketime problem: original gettimeofday() not found.\n"); -#endif - return -1; /* propagate error to caller */ - } + int result; + useconds_t usec_real = (user_rate_set && !dont_fake)?((1.0 / user_rate) * usec):usec ; + if (real_usleep == NULL) { + return -1; + } - /* initialize our result with the real current time */ - result = (*real_gettimeofday)(tv, NULL); - if (result == -1) return result; /* original function failed */ - if (time_tptr != NULL) - *time_tptr = tv->tv_sec; - result = tv->tv_sec; -#else - /* Check whether we've got a pointer to the real time function yet */ - SINGLE_IF(has_real_time==0) - real_time = NULL; - real_time = dlsym(RTLD_NEXT, "time"); - - /* check whether dlsym() worked */ - if (dlerror() == NULL) { - has_real_time = 1; - } - END_SINGLE_IF - if (!has_real_time) { /* dlsym() failed */ -#ifdef DEBUG - (void) fprintf(stderr, "faketime problem: original time() not found.\n"); -#endif - if (time_tptr != NULL) - *time_tptr = -1; - return -1; /* propagate error to caller */ - } + DONT_FAKE_TIME(result = (*real_usleep)(usec_real)); + return result; +} - /* initialize our result with the real current time */ - result = (*real_time)(time_tptr); -#endif +/** + * Faked sleep() + */ +unsigned int sleep(unsigned int seconds) +{ + unsigned int ret; + unsigned int seconds_real = (user_rate_set && !dont_fake)?((1.0 / user_rate) * seconds):seconds; + if (real_sleep == NULL) { + return 0; + } - return result; + DONT_FAKE_TIME(ret = (*real_sleep)(seconds_real)); + return (user_rate_set && !dont_fake)?(user_rate * ret):ret; +} + +/** + * Faked alarm() + */ +unsigned int alarm(unsigned int seconds) +{ + unsigned int ret; + unsigned int seconds_real = (user_rate_set && !dont_fake)?((1.0 / user_rate) * seconds):seconds; + if (real_alarm == NULL) { + return -1; + } + + DONT_FAKE_TIME(ret = (*real_alarm)(seconds_real)); + return (user_rate_set && !dont_fake)?(user_rate * ret):ret; } time_t time(time_t *time_tptr) { @@ -547,7 +729,7 @@ time_t time(time_t *time_tptr) { time_tptr = &null_dummy; /* (void) fprintf(stderr, "NULL pointer caught in time().\n"); */ } - result = _ftpl_time(time_tptr); + DONT_FAKE_TIME(result = (*real_time)(time_tptr)); if (result == ((time_t) -1)) return result; /* pass the real current time to our faking version, overwriting it */ @@ -559,8 +741,6 @@ time_t time(time_t *time_tptr) { int ftime(struct timeb *tp) { - static int (*real_ftime)(struct timeb *); - static int has_real_ftime = 0; int result; /* sanity check */ @@ -568,16 +748,7 @@ int ftime(struct timeb *tp) { return 0; /* ftime() always returns 0, see manpage */ /* Check whether we've got a pointer to the real ftime() function yet */ - SINGLE_IF(has_real_ftime==0) - real_ftime = NULL; - real_ftime = dlsym(RTLD_NEXT, "ftime"); - - /* check whether dlsym() worked */ - if (dlerror() == NULL) { - has_real_ftime = 1; - } - END_SINGLE_IF - if (!has_real_ftime) { /* dlsym() failed */ + if (NULL == real_ftime) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original ftime() not found.\n"); #endif @@ -596,10 +767,6 @@ int ftime(struct timeb *tp) { } int gettimeofday(struct timeval *tv, void *tz) { -#ifndef __APPLE__ - static int (*real_gettimeofday)(struct timeval *, void *); - static int has_real_gettimeofday = 0; -#endif int result; /* sanity check */ @@ -608,16 +775,7 @@ int gettimeofday(struct timeval *tv, void *tz) { } /* Check whether we've got a pointer to the real ftime() function yet */ - SINGLE_IF(has_real_gettimeofday==0) - real_gettimeofday = NULL; - real_gettimeofday = dlsym(RTLD_NEXT, "gettimeofday"); - - /* check whether dlsym() worked */ - if (dlerror() == NULL) { - has_real_gettimeofday = 1; - } - END_SINGLE_IF - if (!has_real_gettimeofday) { /* dlsym() failed */ + if (NULL == real_gettimeofday) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original gettimeofday() not found.\n"); #endif @@ -635,10 +793,7 @@ int gettimeofday(struct timeval *tv, void *tz) { return result; } -#ifdef POSIX_REALTIME int clock_gettime(clockid_t clk_id, struct timespec *tp) { - static int (*real_clock_gettime)(clockid_t clk_id, struct timespec *tp); - static int has_real_clock_gettime = 0; int result; /* sanity check */ @@ -646,17 +801,7 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) { return -1; } - /* Check whether we've got a pointer to the real clock_gettime() function yet */ - SINGLE_IF(has_real_clock_gettime==0) - real_clock_gettime = NULL; - real_clock_gettime = dlsym(RTLD_NEXT, "__clock_gettime"); - - /* check whether dlsym() worked */ - if (dlerror() == NULL && real_clock_gettime) { - has_real_clock_gettime = 1; - } - END_SINGLE_IF - if (!has_real_clock_gettime) { /* dlsym() failed */ + if (NULL == real_clock_gettime) { /* dlsym() failed */ #ifdef DEBUG (void) fprintf(stderr, "faketime problem: original clock_gettime() not found.\n"); #endif @@ -673,17 +818,93 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) { /* return the result to the caller */ return result; } -#endif -/* - * Static time_t to store our startup time, followed by a load-time library - * initialization declaration. - */ -static time_t ftpl_starttime = 0; +static void parse_ft_string(const char *user_faked_time) +{ + struct tm user_faked_time_tm; + char * tmp_time_fmt; + /* check whether the user gave us an absolute time to fake */ + switch (user_faked_time[0]) { + + default: /* Try and interpret this as a specified time */ + ft_mode = FT_FREEZE; + user_faked_time_tm.tm_isdst = -1; + if (NULL != strptime(user_faked_time, user_faked_time_fmt, + &user_faked_time_tm)) { + user_faked_time_timespec.tv_sec = mktime(&user_faked_time_tm); + user_faked_time_timespec.tv_nsec = 0; + user_faked_time_set = true; + } + break; + + case '+': + case '-': /* User-specified offset */ + ft_mode = FT_START_AT; + /* fractional time offsets contributed by Karl Chen in v0.8 */ + double frac_offset = atof(user_faked_time); + + /* offset is in seconds by default, but the string may contain + * multipliers... + */ + if (strchr(user_faked_time, 'm') != NULL) frac_offset *= 60; + else if (strchr(user_faked_time, 'h') != NULL) frac_offset *= 60 * 60; + else if (strchr(user_faked_time, 'd') != NULL) frac_offset *= 60 * 60 * 24; + else if (strchr(user_faked_time, 'y') != NULL) frac_offset *= 60 * 60 * 24 * 365; + + user_offset.tv_sec = floor(frac_offset); + user_offset.tv_nsec = (frac_offset - user_offset.tv_sec) * SEC_TO_nSEC; + timespecadd(&ftpl_starttime.real, &user_offset, &user_faked_time_timespec); + goto parse_modifiers; + break; + + /* Contributed by David North, TDI in version 0.7 */ + case '@': /* Specific time, but clock along relative to that starttime */ + ft_mode = FT_START_AT; + user_faked_time_tm.tm_isdst = -1; + (void) strptime(&user_faked_time[1], user_faked_time_fmt, &user_faked_time_tm); + + user_faked_time_timespec.tv_sec = mktime(&user_faked_time_tm); + user_faked_time_timespec.tv_nsec = 0; + parse_modifiers: + /* Speed-up / slow-down contributed by Karl Chen in v0.8 */ + if (strchr(user_faked_time, 'x') != NULL) { + user_rate = atof(strchr(user_faked_time, 'x')+1); + user_rate_set = true; + } else if (NULL != (tmp_time_fmt = strchr(user_faked_time, 'i'))) { + double tick_inc = atof(tmp_time_fmt + 1); + /* increment time with every time() call*/ + user_per_tick_inc.tv_sec = floor(tick_inc); + user_per_tick_inc.tv_nsec = (tick_inc - user_per_tick_inc.tv_sec) * SEC_TO_nSEC ; + user_per_tick_inc_set = true; + } + break; + } +} void __attribute__ ((constructor)) ftpl_init(void) { - time_t temp_tt; + char *tmp_env; + + /* Look up all real_* functions. NULL will mark missing ones. */ + real_stat = dlsym(RTLD_NEXT, "__xstat"); + real_fstat = dlsym(RTLD_NEXT, "__fxstat"); + real_fstatat = dlsym(RTLD_NEXT, "__fxstatat"); + real_lstat = dlsym(RTLD_NEXT, "__lxstat"); + real_stat64 = dlsym(RTLD_NEXT,"__xstat64"); + real_fstat64 = dlsym(RTLD_NEXT, "__fxstat64"); + real_fstatat64 = dlsym(RTLD_NEXT, "__fxstatat64"); + real_lstat64 = dlsym(RTLD_NEXT, "__lxstat64"); + real_time = dlsym(RTLD_NEXT, "time"); + real_ftime = dlsym(RTLD_NEXT, "ftime"); + real_gettimeofday = dlsym(RTLD_NEXT, "gettimeofday"); + real_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime"); + real_nanosleep = dlsym(RTLD_NEXT, "nanosleep"); + real_usleep = dlsym(RTLD_NEXT, "usleep"); + real_sleep = dlsym(RTLD_NEXT, "sleep"); + real_alarm = dlsym(RTLD_NEXT, "alarm"); +#ifdef __APPLE__ + real_clock_get_time = dlsym(RTLD_NEXT, "clock_get_time"); +#endif ft_shm_init(); #ifdef FAKE_STAT @@ -692,7 +913,108 @@ void __attribute__ ((constructor)) ftpl_init(void) } #endif - ftpl_starttime = _ftpl_time(&temp_tt); + /* Check whether we actually should be faking the returned timestamp. */ + + if ((tmp_env = getenv("FAKETIME_START_AFTER_SECONDS")) != NULL) { + ft_start_after_secs = atol(tmp_env); + limited_faking = true; + } + if ((tmp_env = getenv("FAKETIME_STOP_AFTER_SECONDS")) != NULL) { + ft_stop_after_secs = atol(tmp_env); + limited_faking = true; + } + if ((tmp_env = getenv("FAKETIME_START_AFTER_NUMCALLS")) != NULL) { + ft_start_after_ncalls = atol(tmp_env); + limited_faking = true; + } + if ((tmp_env = getenv("FAKETIME_STOP_AFTER_NUMCALLS")) != NULL) { + ft_stop_after_ncalls = atol(tmp_env); + limited_faking = true; + } + + /* check whether we should spawn an external command */ + if ((tmp_env = getenv("FAKETIME_SPAWN_TARGET")) != NULL) { + spawnsupport = true; + (void) strncpy(ft_spawn_target, getenv("FAKETIME_SPAWN_TARGET"), 1024); + + if ((tmp_env = getenv("FAKETIME_SPAWN_SECONDS")) != NULL) { + ft_spawn_secs = atol(tmp_env); + } + if ((tmp_env = getenv("FAKETIME_SPAWN_NUMCALLS")) != NULL) { + ft_spawn_ncalls = atol(tmp_env); + } + } + + if ((tmp_env = getenv("FAKETIME_SAVE_FILE")) != NULL) { + if (-1 == (outfile = open(tmp_env, O_RDWR | O_APPEND | O_CLOEXEC | O_CREAT, + S_IWUSR | S_IRUSR))) { + perror("Opening file for saving timestamps failed"); + exit(EXIT_FAILURE); + } + } + + /* load file only if reading timstamps from it is not finished yet */ + if ((tmp_env = getenv("FAKETIME_LOAD_FILE")) != NULL) { + int infile = -1; + struct stat sb; + if (-1 == (infile = open(tmp_env, O_RDONLY|O_CLOEXEC))) { + perror("Opening file for loading timestamps failed"); + exit(EXIT_FAILURE); + } + + fstat(infile, &sb); + if (sizeof(stss[0]) > (infile_size = sb.st_size)) { + printf("There are no timstamps in the provided file to load timesamps from"); + exit(EXIT_FAILURE); + } + + if ((infile_size % sizeof(stss[0])) != 0) { + printf("File size is not multiple of timstamp size. It is probably damaged."); + exit(EXIT_FAILURE); + } + + stss = mmap(NULL, infile_size, PROT_READ, MAP_SHARED, infile, 0); + if (stss == MAP_FAILED) { + perror("Mapping file for loading timestamps failed"); + exit(EXIT_FAILURE); + }; + infile_set = true; + } + + tmp_env = getenv("FAKETIME_FMT"); + if (tmp_env == NULL) { + strcpy(user_faked_time_fmt, "%Y-%m-%d %T"); + } else { + strncpy(user_faked_time_fmt, tmp_env, BUFSIZ); + } + + if (shared_sem != 0) { + if (sem_wait(shared_sem) == -1) { + perror("sem_wait"); + exit(1); + } + if (ft_shared->start_time.real.tv_nsec == -1) { + /* set up global start time */ + system_time_from_system(&ftpl_starttime); + ft_shared->start_time = ftpl_starttime; + } else { + /** get preset start time */ + ftpl_starttime = ft_shared->start_time; + } + if (sem_post(shared_sem) == -1) { + perror("sem_post"); + exit(1); + } + + } else { + system_time_from_system(&ftpl_starttime); + } + /* fake time supplied as environment variable? */ + if (NULL != (tmp_env = getenv("FAKETIME"))) { + parse_config_file = false; + parse_ft_string(tmp_env); + } + } static void remove_trailing_eols(char *line) @@ -710,48 +1032,25 @@ static void remove_trailing_eols(char *line) } -time_t fake_time(time_t *time_tptr) { - static char user_faked_time[BUFFERLEN]; /* changed to static for caching in v0.6 */ - struct tm user_faked_time_tm; - time_t user_faked_time_time_t; - long user_offset; - double frac_user_offset; - char filename[BUFSIZ], line[BUFFERLEN]; - FILE *faketimerc; - static const char *user_faked_time_fmt = NULL; - char * tmp_time_fmt; +int fake_clock_gettime(clockid_t clk_id, struct timespec *tp) { + /* variables used for caching, introduced in version 0.6 */ static time_t last_data_fetch = 0; /* not fetched previously at first call */ static int cache_expired = 1; /* considered expired at first call */ static int cache_duration = 10; /* cache fake time input for 10 seconds */ -#ifdef LIMITEDFAKING - static long callcounter = 0; - static int limited_initialized = 0; - char envvarbuf[32]; - static long FAKETIME_START_AFTER_SECONDS = -1; - static long FAKETIME_STOP_AFTER_SECONDS = -1; - static long FAKETIME_START_AFTER_NUMCALLS = -1; - static long FAKETIME_STOP_AFTER_NUMCALLS = -1; -#endif - -#ifdef SPAWNSUPPORT - static int spawned = 0; - static long spawn_callcounter = 0; - static int spawn_initialized = 0; - char spawn_envvarbuf[32]; - static char FAKETIME_SPAWN_TARGET[1024]; - static long FAKETIME_SPAWN_SECONDS = -1; - static long FAKETIME_SPAWN_NUMCALLS = -1; -#endif + if (dont_fake) return 0; + /* Per process timers are only sped up or slowed down */ + if ((clk_id == CLOCK_PROCESS_CPUTIME_ID ) || (clk_id == CLOCK_THREAD_CPUTIME_ID)) { + if (user_rate_set) { + timespecmul(tp, user_rate, tp); + } + return 0; + } -/* - * This no longer appears to be necessary in Mac OS X 10.7 Lion - */ -//#ifdef __APPLE__ -// static int malloc_arena = 0; -//#endif + /* Sanity check by Karl Chan since v0.8 */ + if (tp == NULL) return -1; #ifdef PTHREAD_SINGLETHREADED_TIME static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER; @@ -759,82 +1058,54 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER; pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, (void *)&time_mutex); #endif - /* Sanity check by Karl Chan since v0.8 */ - if (time_tptr == NULL) return -1; - -#ifdef LIMITEDFAKING - /* Check whether we actually should be faking the returned timestamp. */ - - if (ftpl_starttime > 0) { - if (limited_initialized == 0) { - if (getenv("FAKETIME_START_AFTER_SECONDS") != NULL) { - (void) strncpy(envvarbuf, getenv("FAKETIME_START_AFTER_SECONDS"), 30); - FAKETIME_START_AFTER_SECONDS = atol(envvarbuf); - } - if (getenv("FAKETIME_STOP_AFTER_SECONDS") != NULL) { - (void) strncpy(envvarbuf, getenv("FAKETIME_STOP_AFTER_SECONDS"), 30); - FAKETIME_STOP_AFTER_SECONDS = atol(envvarbuf); - } - if (getenv("FAKETIME_START_AFTER_NUMCALLS") != NULL) { - (void) strncpy(envvarbuf, getenv("FAKETIME_START_AFTER_NUMCALLS"), 30); - FAKETIME_START_AFTER_NUMCALLS = atol(envvarbuf); - } - if (getenv("FAKETIME_STOP_AFTER_NUMCALLS") != NULL) { - (void) strncpy(envvarbuf, getenv("FAKETIME_STOP_AFTER_NUMCALLS"), 30); - FAKETIME_STOP_AFTER_NUMCALLS = atol(envvarbuf); - } - limited_initialized = 1; - } - if ((callcounter + 1) >= callcounter) callcounter++; - - /* For debugging, output #seconds and #calls */ - /* fprintf(stderr, "(libfaketime limits -> runtime: %lu, callcounter: %lu\n", (*time_tptr - ftpl_starttime), callcounter); */ - if ((FAKETIME_START_AFTER_SECONDS != -1) && ((*time_tptr - ftpl_starttime) < FAKETIME_START_AFTER_SECONDS)) return *time_tptr; - if ((FAKETIME_STOP_AFTER_SECONDS != -1) && ((*time_tptr - ftpl_starttime) >= FAKETIME_STOP_AFTER_SECONDS)) return *time_tptr; - if ((FAKETIME_START_AFTER_NUMCALLS != -1) && (callcounter < FAKETIME_START_AFTER_NUMCALLS)) return *time_tptr; - if ((FAKETIME_STOP_AFTER_NUMCALLS != -1) && (callcounter >= FAKETIME_STOP_AFTER_NUMCALLS)) return *time_tptr; - /* fprintf(stderr, "(libfaketime limits -> runtime: %lu, callcounter: %lu continues\n", (*time_tptr - ftpl_starttime), callcounter); */ + if ((limited_faking && + ((ft_start_after_ncalls != -1) || (ft_stop_after_ncalls != -1))) || + (spawnsupport && ft_spawn_ncalls)) { + if ((callcounter + 1) >= callcounter) callcounter++; } -#endif - -#ifdef SPAWNSUPPORT - /* check whether we should spawn an external command */ - - if (ftpl_starttime > 0) { - - if(spawn_initialized == 0) { - if (getenv("FAKETIME_SPAWN_TARGET") != NULL) { - (void) strncpy(FAKETIME_SPAWN_TARGET, getenv("FAKETIME_SPAWN_TARGET"), 1024); - - if (getenv("FAKETIME_SPAWN_SECONDS") != NULL) { - (void) strncpy(spawn_envvarbuf, getenv("FAKETIME_SPAWN_SECONDS"), 30); - FAKETIME_SPAWN_SECONDS = atol(spawn_envvarbuf); - } - - if (getenv("FAKETIME_SPAWN_NUMCALLS") != NULL) { - (void) strncpy(spawn_envvarbuf, getenv("FAKETIME_SPAWN_NUMCALLS"), 30); - FAKETIME_SPAWN_NUMCALLS = atol(spawn_envvarbuf); - } - } - spawn_initialized = 1; - } - - if (spawned == 0) { /* exec external command once only */ - if ((spawn_callcounter + 1) >= spawn_callcounter) spawn_callcounter++; - if ((((*time_tptr - ftpl_starttime) == FAKETIME_SPAWN_SECONDS) || (spawn_callcounter == FAKETIME_SPAWN_NUMCALLS)) && (spawned == 0)) { - spawned = 1; - system(FAKETIME_SPAWN_TARGET); - } - - } + if (limited_faking || spawnsupport) { + struct timespec tmp_ts; + /* For debugging, output #seconds and #calls */ + switch (clk_id) { + case CLOCK_REALTIME: + timespecsub(tp, &ftpl_starttime.real, &tmp_ts); + break; + case CLOCK_MONOTONIC: + timespecsub(tp, &ftpl_starttime.mon, &tmp_ts); + break; + case CLOCK_MONOTONIC_RAW: + timespecsub(tp, &ftpl_starttime.mon_raw, &tmp_ts); + break; + default: + printf("Invalid clock_id for clock_gettime: %d", clk_id); + exit(EXIT_FAILURE); + } + if (limited_faking) { + /* Check whether we actually should be faking the returned timestamp. */ + /* fprintf(stderr, "(libfaketime limits -> runtime: %lu, callcounter: %lu\n", (*time_tptr - ftpl_starttime), callcounter); */ + if ((ft_start_after_secs != -1) && (tmp_ts.tv_sec < ft_start_after_secs)) return 0; + if ((ft_stop_after_secs != -1) && (tmp_ts.tv_sec >= ft_stop_after_secs)) return 0; + if ((ft_start_after_ncalls != -1) && (callcounter < ft_start_after_ncalls)) return 0; + if ((ft_stop_after_ncalls != -1) && (callcounter >= ft_stop_after_ncalls)) return 0; + /* fprintf(stderr, "(libfaketime limits -> runtime: %lu, callcounter: %lu continues\n", (*time_tptr - ftpl_starttime), callcounter); */ + + } + + if (spawnsupport) { + /* check whether we should spawn an external command */ + + if (spawned == 0) { /* exec external command once only */ + if (((tmp_ts.tv_sec == ft_spawn_secs) || (callcounter == ft_spawn_ncalls)) && (spawned == 0)) { + spawned = 1; + system(ft_spawn_target); + } + } + } } -#endif - - if (last_data_fetch > 0) { - if ((*time_tptr - last_data_fetch) > cache_duration) { + if ((tp->tv_sec - last_data_fetch) > cache_duration) { cache_expired = 1; } else { @@ -847,8 +1118,11 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER; #endif if (cache_expired == 1) { + static char user_faked_time[BUFFERLEN]; /* changed to static for caching in v0.6 */ + char filename[BUFSIZ], line[BUFFERLEN]; + FILE *faketimerc; - last_data_fetch = *time_tptr; + last_data_fetch = tp->tv_sec; /* Can be enabled for testing ... fprintf(stderr, "***************++ Cache expired ++**************\n"); @@ -858,11 +1132,7 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER; snprintf(user_faked_time, BUFFERLEN, "+0"); /* fake time supplied as environment variable? */ - if (getenv("FAKETIME") != NULL) { - (void) strncpy(user_faked_time, getenv("FAKETIME"), BUFFERLEN-2); - user_faked_time[BUFFERLEN-1] = 0; - } - else { + if (parse_config_file) { /* check whether there's a .faketimerc in the user's home directory, or * a system-wide /etc/faketimerc present. * The /etc/faketimerc handling has been contributed by David Burley, @@ -881,128 +1151,100 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER; } fclose(faketimerc); } + parse_ft_string(user_faked_time); } /* read fake time from file */ - - - user_faked_time_fmt = getenv("FAKETIME_FMT"); - if (user_faked_time_fmt == NULL) - user_faked_time_fmt = "%Y-%m-%d %T"; - } /* cache had expired */ -/* - * This no longer appears to be necessary in Mac OS X 10.7 Lion - */ -//#ifdef __APPLE__ -// SINGLE_IF(malloc_arena==0) -// malloc_arena = 1; -// return *time_tptr; -// END_SINGLE_IF -//#endif + if (infile_set) { + if (load_time(tp)) { + return 0; + } + } /* check whether the user gave us an absolute time to fake */ - switch (user_faked_time[0]) { - - default: /* Try and interpret this as a specified time */ - user_faked_time_tm.tm_isdst = -1; - (void) strptime(user_faked_time, user_faked_time_fmt, &user_faked_time_tm); - - user_faked_time_time_t = mktime(&user_faked_time_tm); - if (user_faked_time_time_t != -1) { - if (time_tptr != NULL) /* sanity check */ - *time_tptr = user_faked_time_time_t; - } - break; - - case '+': - case '-': /* User-specified offset */ - /* fractional time offsets contributed by Karl Chen in v0.8 */ - frac_user_offset = atof(user_faked_time); - - /* offset is in seconds by default, but the string may contain - * multipliers... - */ - if (strchr(user_faked_time, 'm') != NULL) frac_user_offset *= 60; - else if (strchr(user_faked_time, 'h') != NULL) frac_user_offset *= 60 * 60; - else if (strchr(user_faked_time, 'd') != NULL) frac_user_offset *= 60 * 60 * 24; - else if (strchr(user_faked_time, 'y') != NULL) frac_user_offset *= 60 * 60 * 24 * 365; - - /* Speed-up / slow-down contributed by Karl Chen in v0.8 */ - if (strchr(user_faked_time, 'x') != NULL) { - const double rate = atof(strchr(user_faked_time, 'x')+1); - const long tdiff = (long long) *time_tptr - (long long)ftpl_starttime; - const double timeadj = tdiff * (rate - 1.0); - *time_tptr += (long) timeadj; - } else if (NULL != (tmp_time_fmt = strchr(user_faked_time, 'i'))) { - /* increment time with every time() call*/ - *time_tptr += next_time(atof(tmp_time_fmt + 1)); - } - - *time_tptr += (long) frac_user_offset; - - break; - - /* Contributed by David North, TDI in version 0.7 */ - case '@': /* Specific time, but clock along relative to that starttime */ - user_faked_time_tm.tm_isdst = -1; - (void) strptime(&user_faked_time[1], user_faked_time_fmt, &user_faked_time_tm); - - user_faked_time_time_t = mktime(&user_faked_time_tm); - if (user_faked_time_time_t != -1) { - user_offset = - ( (long long int)ftpl_starttime - (long long int)user_faked_time_time_t ); - - /* Speed-up / slow-down contributed by Karl Chen in v0.8 */ - if (strchr(user_faked_time, 'x') != NULL) { - const double rate = atof(strchr(user_faked_time, 'x')+1); - const long tdiff = (long long) *time_tptr - (long long)ftpl_starttime; - const double timeadj = tdiff * (rate - 1.0); - *time_tptr += (long) timeadj; - } else if (NULL != (tmp_time_fmt = strchr(user_faked_time, 'i'))) { - /* increment time with every time() call*/ - *time_tptr += next_time(atof(tmp_time_fmt + 1)); - } - - *time_tptr += user_offset; - } - break; + switch (ft_mode) { + case FT_FREEZE: /* a specified time */ + if (user_faked_time_set) { + *tp = user_faked_time_timespec; + } + break; + + case FT_START_AT: /* User-specified offset */ + if (user_per_tick_inc_set) { + /* increment time with every time() call*/ + next_time(tp, &user_per_tick_inc); + } else { + /* Speed-up / slow-down contributed by Karl Chen in v0.8 */ + struct timespec tdiff, timeadj; + switch (clk_id) { + case CLOCK_REALTIME: + timespecsub(tp, &ftpl_starttime.real, &tdiff); + break; + case CLOCK_MONOTONIC: + timespecsub(tp, &ftpl_starttime.mon, &tdiff); + break; + case CLOCK_MONOTONIC_RAW: + timespecsub(tp, &ftpl_starttime.mon_raw, &tdiff); + break; + default: + printf("Invalid clock_id for clock_gettime: %d", clk_id); + exit(EXIT_FAILURE); + } + if (user_rate_set) { + timespecmul(&tdiff, user_rate, &timeadj); + } else { + timeadj = tdiff; + } + timespecadd(&user_faked_time_timespec, &timeadj, tp); + } + break; + default: + return -1; } #ifdef PTHREAD_SINGLETHREADED_TIME pthread_cleanup_pop(1); #endif - - /* pass the possibly modified time back to caller */ - return *time_tptr; + save_time(tp); + return 0; } -int fake_ftime(struct timeb *tp) { - time_t temp_tt = tp->time; - tp->time = fake_time(&temp_tt); +time_t fake_time(time_t *time_tptr) { + struct timespec tp; - return 0; /* always returns 0, see manpage */ + tp.tv_sec = *time_tptr; + tp.tv_nsec = 0; + (void)fake_clock_gettime(CLOCK_REALTIME, &tp); + *time_tptr = tp.tv_sec; + return *time_tptr; } -int fake_gettimeofday(struct timeval *tv, void *tz) { - time_t temp_tt = tv->tv_sec; +int fake_ftime(struct timeb *tp) { + struct timespec ts; + int ret; + ts.tv_sec = tp->time; + ts.tv_nsec =tp->millitm * 1000000; - tv->tv_sec = fake_time(&temp_tt); + ret = fake_clock_gettime(CLOCK_REALTIME, &ts); + tp->time = ts.tv_sec; + tp->millitm =ts.tv_nsec / 1000000; - return 0; + return ret; } -#ifdef POSIX_REALTIME -int fake_clock_gettime(clockid_t clk_id, struct timespec *tp) { - time_t temp_tt = tp->tv_sec; +int fake_gettimeofday(struct timeval *tv, void *tz) { + struct timespec ts; + int ret; + ts.tv_sec = tv->tv_sec; + ts.tv_nsec =tv->tv_usec * 1000; - /* Fake only if the call is realtime clock related */ - if (clk_id == CLOCK_REALTIME) { - tp->tv_sec = fake_time(&temp_tt); - } + ret = fake_clock_gettime(CLOCK_REALTIME, &ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec =ts.tv_nsec / 1000; - return 0; + return ret; } -#endif #ifdef __APPLE__ @@ -1047,11 +1289,9 @@ int __gettimeofday(struct timeval *tv, void *tz) { return gettimeofday(tv, tz); } -#ifdef POSIX_REALTIME int __clock_gettime(clockid_t clk_id, struct timespec *tp) { return clock_gettime(clk_id, tp); } -#endif int __ftime(struct timeb *tp) { return ftime(tp); diff --git a/src/time_ops.h b/src/time_ops.h new file mode 100644 index 0000000..a12ead0 --- /dev/null +++ b/src/time_ops.h @@ -0,0 +1,99 @@ +/* + * Time operation macros based on sys/time.h + * Copyright 2013 Balint Reczey <balint@balintreczey.hu> + * + * This file is part of the FakeTime Preload Library. + * + * The FakeTime Preload Library 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. + * + * The FakeTime Preload Library 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 FakeTime Preload Library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TIME_OPS_H +#define TIME_OPS_H +#include <time.h> + +#define SEC_TO_uSEC 1000000 +#define SEC_TO_nSEC 1000000000 + +/* Convenience macros for operations on timevals. + NOTE: `timercmp' does not work for >= or <=. */ +#define timerisset2(tvp, prefix) ((tvp)->tv_sec || (tvp)->tv_##prefix##sec) +#define timerclear2(tvp, prefix) ((tvp)->tv_sec = (tvp)->tv_##prefix##sec = 0) +#define timercmp2(a, b, CMP, prefix) \ + (((a)->tv_sec == (b)->tv_sec) ? \ + ((a)->tv_##prefix##sec CMP (b)->tv_##prefix##sec) : \ + ((a)->tv_sec CMP (b)->tv_sec)) +#define timeradd2(a, b, result, prefix) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_##prefix##sec = (a)->tv_##prefix##sec + \ + (b)->tv_##prefix##sec; \ + if ((result)->tv_##prefix##sec >= SEC_TO_##prefix##SEC) \ + { \ + ++(result)->tv_sec; \ + (result)->tv_##prefix##sec -= SEC_TO_##prefix##SEC; \ + } \ + } while (0) +#define timersub2(a, b, result, prefix) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_##prefix##sec = (a)->tv_##prefix##sec - \ + (b)->tv_##prefix##sec; \ + if ((result)->tv_##prefix##sec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_##prefix##sec += SEC_TO_##prefix##SEC; \ + } \ + } while (0) +#define timermul2(tvp, c, result, prefix) \ + do { \ + long long tmp_time; \ + tmp_time = (c) * ((tvp)->tv_sec * SEC_TO_##prefix##SEC + \ + (tvp)->tv_##prefix##sec); \ + (result)->tv_##prefix##sec = tmp_time % SEC_TO_##prefix##SEC; \ + (result)->tv_sec = (tmp_time - (result)->tv_##prefix##sec) / \ + SEC_TO_##prefix##SEC; \ + if ((result)->tv_##prefix##sec < 0) { \ + (result)->tv_##prefix##sec += SEC_TO_##prefix##SEC; \ + (result)->tv_sec -= 1; \ + } \ + } while (0) + +/* ops for microsecs */ +#ifndef timerisset +#define timerisset(tvp) timerisset2(tvp,u) +#endif +#ifndef timerclear +#define timerclear(tvp) timerclear2(tvp, u) +#endif +#ifndef timercmp +#define timercmp(a, b, CMP) timercmp2(a, b, CMP, u) +#endif +#ifndef timeradd +#define timeradd(a, b, result) timeradd2(a, b, result, u) +#endif +#ifndef timersub +#define timersub(a, b, result) timersub2(a, b, result, u) +#endif +#ifndef timersub +#define timermul(a, c, result) timermul2(a, c, result, u) +#endif + +/* ops for nanosecs */ +#define timespecisset(tvp) timerisset2(tvp,n) +#define timespecclear(tvp) timerclear2(tvp, n) +#define timespeccmp(a, b, CMP) timercmp2(a, b, CMP, n) +#define timespecadd(a, b, result) timeradd2(a, b, result, n) +#define timespecsub(a, b, result) timersub2(a, b, result, n) +#define timespecmul(a, c, result) timermul2(a, c, result, n) + +#endif diff --git a/test/test.sh b/test/test.sh index a377b3f..4014b99 100755 --- a/test/test.sh +++ b/test/test.sh @@ -30,6 +30,10 @@ echo echo "Running the test program with 10 days negative offset specified, and FAKE_STAT disabled" echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"-10d\" NO_FAKE_STAT=1 ./timetest" LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="-10d" NO_FAKE_STAT=1 ./timetest + +echo "Running the test program with 10 days postive offset specified, and sped up 2 times" +echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"+10d x2\" ./timetest" +LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="+10d x2" NO_FAKE_STAT=1 ./timetest echo echo "Running the 'date' command with 15 days negative offset specified" |