summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README23
-rw-r--r--src/faketime.c11
-rw-r--r--src/faketime_common.h36
-rw-r--r--src/libfaketime.c215
4 files changed, 260 insertions, 25 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/src/faketime.c b/src/faketime.c
index 163ed5c..8dfd518 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,9 @@ 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;
+ if (-1 == munmap(ft_shared, (sizeof(uint64_t)))) {
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..e0497eb
--- /dev/null
+++ b/src/faketime_common.h
@@ -0,0 +1,36 @@
+/*
+ * 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 FAKETIME_COMMON_H
+#define FAKETIME_COMMON_H
+
+#include <stdint.h>
+
+/** 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;
+};
+
+#endif
diff --git a/src/libfaketime.c b/src/libfaketime.c
index 3324cf4..0ef9af1 100644
--- a/src/libfaketime.c
+++ b/src/libfaketime.c
@@ -23,15 +23,20 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
+#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
@@ -89,12 +94,48 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp);
*
*/
-/**
- * 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;
+/** 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;
@@ -140,14 +181,14 @@ 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);
}
@@ -156,7 +197,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);
@@ -167,32 +208,123 @@ 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 void next_time(struct timespec *tp, struct timespec *ticklen)
{
- if (ticks_sem != NULL) {
+ if (shared_sem != NULL) {
struct timespec inc;
/* lock */
- if (sem_wait(ticks_sem) == -1) {
+ if (sem_wait(shared_sem) == -1) {
perror("sem_wait");
exit(1);
}
-
/* calculate and update elapsed time */
- timespecmul(ticklen, *ticks, &inc);
+ timespecmul(ticklen, ft_shared->ticks, &inc);
timespecadd(&user_faked_time_timespec, &inc, tp);
- (*ticks)++;
+ (ft_shared->ticks)++;
/* unlock */
- if (sem_post(ticks_sem) == -1) {
+ if (sem_post(shared_sem) == -1) {
perror("sem_post");
exit(1);
}
}
}
+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 = {tp->tv_sec, 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(shared_sem) == -1) {
+ perror("sem_wait");
+ exit(1);
+ }
+
+ 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(shared_sem) == -1) {
+ perror("sem_post");
+ exit(1);
+ }
+ }
+}
+
+/**
+ * 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 ]);
+ ftpl_starttime = *tp;
+ if (ft_shared->ticks == 0) {
+ ft_shared->ticks = 1;
+ }
+ 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
#ifndef NO_ATFILE
@@ -621,6 +753,42 @@ void __attribute__ ((constructor)) ftpl_init(void)
}
}
+ 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");
@@ -772,6 +940,12 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER;
} /* read fake time from file */
} /* cache had expired */
+ if (infile_set) {
+ if (load_time(tp)) {
+ return 0;
+ }
+ }
+
/* check whether the user gave us an absolute time to fake */
switch (ft_mode) {
case FT_FREEZE: /* a specified time */
@@ -799,6 +973,7 @@ static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER;
#ifdef PTHREAD_SINGLETHREADED_TIME
pthread_cleanup_pop(1);
#endif
+ save_time(tp);
return 0;
}