summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README23
-rw-r--r--TODO2
-rw-r--r--src/Makefile10
-rw-r--r--src/Makefile.MacOS11
-rw-r--r--src/faketime.c18
-rw-r--r--src/faketime_common.h49
-rw-r--r--src/libfaketime.c1172
-rw-r--r--src/time_ops.h99
-rwxr-xr-xtest/test.sh4
9 files changed, 898 insertions, 490 deletions
diff --git a/README b/README
index 05fdafa..c726c35 100644
--- a/README
+++ b/README
@@ -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.
diff --git a/TODO b/TODO
index 193f8fd..fc4d043 100644
--- a/TODO
+++ b/TODO
@@ -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"