mirror of
https://github.com/wolfcw/libfaketime.git
synced 2026-05-19 09:46:11 +03:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8a0ad2496 | ||
|
|
0e3269efdc | ||
|
|
04e78e67bf | ||
|
|
0e798503a4 | ||
|
|
a7f7a54e1d | ||
|
|
8075c2e250 | ||
|
|
a12ca58dfa | ||
|
|
b4dea2ef9b | ||
|
|
796b30bebf | ||
|
|
70d26ec15c | ||
|
|
052239d828 | ||
|
|
fe8c0acee6 | ||
|
|
c44a332e0e | ||
|
|
a54f204209 | ||
|
|
195888434a | ||
|
|
f7de52c07b | ||
|
|
0efe7b3e3e | ||
|
|
b542e14959 | ||
|
|
78385ba8b7 | ||
|
|
b3dcef470e | ||
|
|
90f11685b5 | ||
|
|
d7ef17a0de | ||
|
|
3123ad7fe2 | ||
|
|
af2d2e5111 | ||
|
|
11fbc2ada1 | ||
|
|
f92d919fb0 | ||
|
|
22f8c3dd36 | ||
|
|
a0fe6b56b0 | ||
|
|
fdc3c81ae0 | ||
|
|
10b9818c2c | ||
|
|
826f8b7792 | ||
|
|
5d1e6325f2 | ||
|
|
8ed946cb63 | ||
|
|
108370f850 | ||
|
|
3de0d02353 | ||
|
|
150a6cb3b2 | ||
|
|
39a85e380c | ||
|
|
825043515f | ||
|
|
335617c4c7 | ||
|
|
96668a9c6d | ||
|
|
24fd806e6b | ||
|
|
39c6872f6d |
23
.github/workflows/make-test.yml
vendored
Normal file
23
.github/workflows/make-test.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Run make test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
schedule:
|
||||
- cron: '30 9 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: make
|
||||
- name: make test
|
||||
run: make test
|
||||
@@ -4,6 +4,8 @@ matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
- os: osx
|
||||
osx_image: xcode11
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
|
||||
15
NEWS
15
NEWS
@@ -1,3 +1,18 @@
|
||||
- Additional link-time LDFLAGS can be passed via the
|
||||
environment variable FAKETIME_LINK_FLAGS when
|
||||
running 'make'.
|
||||
|
||||
Since 0.9.8:
|
||||
- Compile-time CFLAG FAKE_SETTIME can be enabled to
|
||||
intercept calls to clock_settime(), settimeofday(), and
|
||||
adjtime(). (suggested and prototyped by @ojura)
|
||||
- Additional compile-time CFLAGs can be passed via the
|
||||
environment variable FAKETIME_COMPILE_CFLAGS when
|
||||
running 'make'.
|
||||
- src/Makefile CFLAG FORCE_PTHREAD_NONVER should be set on
|
||||
systems that hang on CLOCK_REALTIME, or that hang on
|
||||
CLOCK_MONOTONIC where FORCE_MONOTONIC_FIX is not sufficient.
|
||||
|
||||
Since 0.9.7:
|
||||
- Passthrough for unknown clock ids to avoid error messages
|
||||
- Fixes for multithreaded operations (mliertzer, qnox)
|
||||
|
||||
16
README
16
README
@@ -97,6 +97,12 @@ documentation whether it can be achieved by using libfaketime directly.
|
||||
src/Makefile and recompile libfaketime. Do not set FORCE_MONOTONIC_FIX on
|
||||
platforms where the test does not hang.
|
||||
|
||||
If you observe hangs on the CLOCK_REALTIME test, add the CFLAG
|
||||
-DFORCE_PTHREAD_NONVER. Also set this FORCE_PTHREAD_NONVER flag in case
|
||||
FORCE_MONOTONIC_FIX alone does not solve the hang on the MONOTONIC_CLOCK
|
||||
test.
|
||||
|
||||
|
||||
3. Installation
|
||||
---------------
|
||||
|
||||
@@ -450,6 +456,16 @@ a lot of processes are started (e.g., servers handling many containers
|
||||
or similar virtualization mechanisms).
|
||||
|
||||
|
||||
Intercepting time-setting calls
|
||||
-------------------------------
|
||||
|
||||
libfaketime can be compiled with the CFLAG "-DFAKE_SETTIME" in order
|
||||
to also intercept time-setting functions, i.e., clock_settime(),
|
||||
settimeofday(), and adjtime(). Instead of passing the timestamp a
|
||||
program sets through to the system, only the FAKETIME environment
|
||||
variable will be adjusted accordingly.
|
||||
|
||||
|
||||
4f) Faking the date and time system-wide
|
||||
----------------------------------------
|
||||
|
||||
|
||||
63
README.packagers
Normal file
63
README.packagers
Normal file
@@ -0,0 +1,63 @@
|
||||
README for packagers of libfaketime
|
||||
|
||||
First, thank you for your efforts to make libfaketime packages available
|
||||
on your platform / distribution!
|
||||
|
||||
libfaketime has tagged releases about once every 1-2 years, made available
|
||||
through github.com/wolfcw/libfaketime, but usually it is also safe (i.e.,
|
||||
stable) to use the latest HEAD of the master branch, which contains bug
|
||||
fixes since the last tagged release.
|
||||
|
||||
You may want to familiarize yourself with the options you can set into
|
||||
src/Makefile, but sane defaults for stable operations have been chosen.
|
||||
|
||||
Currently, libfaketime does not use autotools yet, so there is
|
||||
_no_ ./configure step, but "make" and "make test" will work as expected.
|
||||
|
||||
|
||||
However, one problem makes it somewhat difficult to get libfaketime
|
||||
working on different platforms:
|
||||
|
||||
libfaketime currently has the challenge that depending on the version
|
||||
of glibc and the platform (e.g., x86_64 or aarch64) certain compiler
|
||||
CFLAGS have to be set manually, as we have not yet found a way to
|
||||
safely determine behavior at run-time automatically.
|
||||
|
||||
Please proceed as follows:
|
||||
|
||||
- run "make test". If everything runs through smoothly and you do not
|
||||
encounter any hangs or test failure reports, use the binaries as
|
||||
they are.
|
||||
|
||||
- If you encounter endless hangs during the CLOCK_REALTIME test,
|
||||
add -DFORCE_PTHREAD_NONVER to the CFLAGS.
|
||||
|
||||
- If you encounter endless hangs during the CLOCK_MONOTONIC test,
|
||||
add -DFORCE_MONOTONIC_FIX to the CFLAGS. If it works with that,
|
||||
it's fine, otherwise additionally use -DFORCE_PTHREAD_NONVER.
|
||||
|
||||
CFLAGS can also be passed through the FAKETIME_COMPILE_CFLAGS environment
|
||||
variable, so for example
|
||||
|
||||
FAKETIME_COMPILE_CFLAGS="-DFORCE_PTHREAD_NONVER" make test
|
||||
|
||||
would create the libfaketime binaries and run the tests with the
|
||||
FORCE_PTHREAD_NONVER flag set in a single step. Likewise there is
|
||||
FAKETIME_LINK_FLAGS.
|
||||
|
||||
Please do not use FORCE_MONOTONIC_FIX by default, as it would result
|
||||
in incorrect operations on platforms that do not need it.
|
||||
|
||||
Our observations with a limited number of Linux distributions is that
|
||||
libfaketime may require different compile flags per platform even
|
||||
if the same distribution and glibc version is used across these
|
||||
platforms.
|
||||
|
||||
As soon as we have found a reliable way to automatically choose the
|
||||
correct compile-time flags, we will remove this burden from you as
|
||||
packager for obvious reasons. Until then, please feel free to report
|
||||
your experiences with different platforms and distribution versions
|
||||
through the issue tracker on Github.
|
||||
|
||||
Again, thanks for your time and effort to make libfaketime available
|
||||
easily for everyone else!
|
||||
@@ -30,6 +30,9 @@
|
||||
# FAKE_PTHREAD
|
||||
# - Intercept pthread_cond_timedwait
|
||||
#
|
||||
# FAKE_SETTIME
|
||||
# - Intercept clock_settime(), settimeofday(), and adjtime()
|
||||
#
|
||||
# FORCE_MONOTONIC_FIX
|
||||
# - If the test program hangs forever on
|
||||
# " pthread_cond_timedwait: CLOCK_MONOTONIC test
|
||||
@@ -76,7 +79,7 @@ endif
|
||||
|
||||
LIB_LDFLAGS += -shared
|
||||
|
||||
LDFLAGS += -lpthread
|
||||
LDFLAGS += $(FAKETIME_LINK_FLAGS) -lpthread
|
||||
ifneq ($(PLATFORM),SunOS)
|
||||
LDFLAGS += -Wl,--version-script=libfaketime.map
|
||||
endif
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
/* pthread-handling contributed by David North, TDI in version 0.7 */
|
||||
#if defined PTHREAD_SINGLETHREADED_TIME || defined FAKE_PTHREAD
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <sys/timeb.h>
|
||||
@@ -110,6 +111,13 @@ typedef int clockid_t;
|
||||
#define CLOCK_MONOTONIC_RAW (CLOCK_MONOTONIC + 1)
|
||||
#endif
|
||||
|
||||
#ifdef FAKE_FILE_TIMESTAMPS
|
||||
struct utimbuf {
|
||||
time_t actime; /* access time */
|
||||
time_t modtime; /* modification time */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Per thread variable, which we turn on inside real_* calls to avoid modifying
|
||||
* time multiple times of for the whole process to prevent faking time
|
||||
@@ -170,7 +178,7 @@ static int (*real_timer_gettime_233) (timer_t timerid,
|
||||
#ifdef FAKE_SLEEP
|
||||
static int (*real_nanosleep) (const struct timespec *req, struct timespec *rem);
|
||||
#ifndef __APPLE__
|
||||
static int (*real_clock_nanosleep) (clockid_t clock_id, int flags, const struct timespec *req, struct timespec *rem);
|
||||
static int (*real_clock_nanosleep) (clockid_t clock_id, int flags, const struct timespec *req, struct timespec *rem);
|
||||
#endif
|
||||
static int (*real_usleep) (useconds_t usec);
|
||||
static unsigned int (*real_sleep) (unsigned int seconds);
|
||||
@@ -200,11 +208,17 @@ static int apple_clock_gettime (clockid_t clk_id, struct timespec *
|
||||
static clock_serv_t clock_serv_real;
|
||||
#endif
|
||||
|
||||
#ifdef FAKE_FILE_TIMESTAMPS
|
||||
static int (*real_utimes) (const char *filename, const struct timeval times[2]);
|
||||
static int (*real_utime) (const char *filename, const struct utimbuf *times);
|
||||
#endif
|
||||
|
||||
static int initialized = 0;
|
||||
|
||||
/* prototypes */
|
||||
static int fake_gettimeofday(struct timeval *tv);
|
||||
static int fake_clock_gettime(clockid_t clk_id, struct timespec *tp);
|
||||
int read_config_file();
|
||||
|
||||
/** Semaphore protecting shared data */
|
||||
static sem_t *shared_sem = NULL;
|
||||
@@ -255,6 +269,7 @@ static int fake_monotonic_clock = 1;
|
||||
#endif
|
||||
static int cache_enabled = 1;
|
||||
static int cache_duration = 10; /* cache fake time input for 10 seconds */
|
||||
static int force_cache_expiration = 0;
|
||||
|
||||
/*
|
||||
* Static timespec to store our startup time, followed by a load-time library
|
||||
@@ -285,8 +300,6 @@ 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;
|
||||
static bool user_per_tick_inc_set_backup = false;
|
||||
|
||||
enum ft_mode_t {FT_FREEZE, FT_START_AT, FT_NOOP} ft_mode = FT_FREEZE;
|
||||
|
||||
/* Time to fake is not provided through FAKETIME env. var. */
|
||||
@@ -316,11 +329,11 @@ static void ft_shm_create(void) {
|
||||
snprintf(shm_name, 255, "/faketime_shm_%ld", (long)getpid());
|
||||
if (SEM_FAILED == (semN = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
|
||||
{ /* silently fail on platforms that do not support sem_open() */
|
||||
return;
|
||||
return;
|
||||
}
|
||||
/* create shm */
|
||||
if (-1 == (shm_fdN = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR)))
|
||||
{
|
||||
{
|
||||
perror("libfaketime: In ft_shm_create(), shm_open failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -329,7 +342,7 @@ static void ft_shm_create(void) {
|
||||
{
|
||||
perror("libfaketime: In ft_shm_create(), ftruncate failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
/* map shm */
|
||||
if (MAP_FAILED == (ft_sharedN = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, shm_fdN, 0)))
|
||||
@@ -361,7 +374,7 @@ static void ft_shm_create(void) {
|
||||
{
|
||||
perror("libfaketime: In ft_shm_create(), sem_post failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(shared_objsN, sizeof(shared_objsN), "%s %s", sem_name, shm_name);
|
||||
|
||||
@@ -414,6 +427,7 @@ static void ft_shm_init (void)
|
||||
int ticks_shm_fd;
|
||||
char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED");
|
||||
sem_t *shared_semR = NULL;
|
||||
static int nt=1;
|
||||
|
||||
/* create semaphore and shared memory locally unless it has been passed along */
|
||||
if (ft_shared_env == NULL)
|
||||
@@ -421,7 +435,7 @@ static void ft_shm_init (void)
|
||||
ft_shm_create();
|
||||
ft_shared_env = getenv("FAKETIME_SHARED");
|
||||
}
|
||||
|
||||
|
||||
/* check for stale semaphore / shared memory information */
|
||||
if (ft_shared_env != NULL)
|
||||
{
|
||||
@@ -435,7 +449,7 @@ static void ft_shm_init (void)
|
||||
ft_shm_create();
|
||||
ft_shared_env = getenv("FAKETIME_SHARED");
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
sem_close(shared_semR);
|
||||
}
|
||||
@@ -452,10 +466,28 @@ static void ft_shm_init (void)
|
||||
|
||||
if (SEM_FAILED == (shared_sem = sem_open(sem_name, 0)))
|
||||
{
|
||||
perror("libfaketime: In ft_shm_init(), sem_open failed");
|
||||
fprintf(stderr, "libfaketime: sem_name was %s, created locally: %s\n", sem_name, shmCreator ? "true":"false");
|
||||
fprintf(stderr, "libfaketime: parsed from env: %s\n", ft_shared_env);
|
||||
exit(1);
|
||||
if (shmCreator)
|
||||
{
|
||||
perror("libfaketime: In ft_shm_init(), sem_open failed");
|
||||
fprintf(stderr, "libfaketime: sem_name was %s, created locally: %s\n", sem_name, shmCreator ? "true":"false");
|
||||
fprintf(stderr, "libfaketime: parsed from env: %s\n", ft_shared_env);
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
nt++;
|
||||
if (nt > 3)
|
||||
{
|
||||
perror("libfaketime: In ft_shm_init(), sem_open failed and recreation attempts failed");
|
||||
fprintf(stderr, "libfaketime: sem_name was %s, created locally: %s\n", sem_name, shmCreator ? "true":"false");
|
||||
exit(1);
|
||||
}
|
||||
else{
|
||||
ft_shm_init();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == (ticks_shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)))
|
||||
@@ -498,6 +530,30 @@ static void ft_cleanup (void)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* =======================================================================
|
||||
* Get monotonic faketime setting === GETENV
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
static void get_fake_monotonic_setting(int* current_value)
|
||||
{
|
||||
char *tmp_env;
|
||||
if ((tmp_env = getenv("FAKETIME_DONT_FAKE_MONOTONIC")) != NULL
|
||||
|| (tmp_env = getenv("DONT_FAKE_MONOTONIC")) != NULL)
|
||||
{
|
||||
if (0 == strcmp(tmp_env, "1"))
|
||||
{
|
||||
(*current_value) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*current_value) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* =======================================================================
|
||||
* Internal time retrieval === INTTIME
|
||||
@@ -680,6 +736,7 @@ static bool load_time(struct timespec *tp)
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int fake_stat_disabled = 0;
|
||||
static bool user_per_tick_inc_set_backup = false;
|
||||
|
||||
void lock_for_stat()
|
||||
{
|
||||
@@ -1013,6 +1070,65 @@ int __lxstat64 (int ver, const char *path, struct stat64 *buf)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FAKE_FILE_TIMESTAMPS
|
||||
int utime(const char *filename, const struct utimbuf *times)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
ftpl_init();
|
||||
}
|
||||
if (NULL == real_utime)
|
||||
{ /* dlsym() failed */
|
||||
#ifdef DEBUG
|
||||
(void) fprintf(stderr, "faketime problem: original utime() not found.\n");
|
||||
#endif
|
||||
return -1; /* propagate error to caller */
|
||||
}
|
||||
|
||||
int result;
|
||||
struct utimbuf ntbuf;
|
||||
ntbuf.actime = times->actime - user_offset.tv_sec;
|
||||
ntbuf.modtime = times->modtime - user_offset.tv_sec;
|
||||
DONT_FAKE_TIME(result = real_utime(filename, &ntbuf));
|
||||
if (result == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int utimes(const char *filename, const struct timeval times[2])
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
ftpl_init();
|
||||
}
|
||||
if (NULL == real_utimes)
|
||||
{ /* dlsym() failed */
|
||||
#ifdef DEBUG
|
||||
(void) fprintf(stderr, "faketime problem: original utimes() not found.\n");
|
||||
#endif
|
||||
return -1; /* propagate error to caller */
|
||||
}
|
||||
|
||||
int result;
|
||||
struct timeval tn[2];
|
||||
struct timeval user_offset2;
|
||||
user_offset2.tv_sec = user_offset.tv_sec;
|
||||
user_offset2.tv_usec = 0;
|
||||
timersub(×[0], &user_offset2, &tn[0]);
|
||||
timersub(×[1], &user_offset2, &tn[1]);
|
||||
DONT_FAKE_TIME(result = real_utimes(filename, tn));
|
||||
if (result == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* =======================================================================
|
||||
* Faked system functions: sleep/alarm/poll/timer related === FAKE(SLEEP)
|
||||
@@ -1892,10 +2008,32 @@ int gettimeofday(struct timeval *tv, void *tz)
|
||||
int clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
{
|
||||
int result;
|
||||
static int recursion_depth = 0;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
ftpl_init();
|
||||
recursion_depth++;
|
||||
if (recursion_depth == 2)
|
||||
{
|
||||
fprintf(stderr, "libfaketime: Unexpected recursive calls to clock_gettime() without proper initialization. Trying alternative.\n");
|
||||
DONT_FAKE_TIME(ftpl_init()) ;
|
||||
}
|
||||
else if (recursion_depth == 3)
|
||||
{
|
||||
fprintf(stderr, "libfaketime: Cannot recover from unexpected recursive calls to clock_gettime().\n");
|
||||
fprintf(stderr, "libfaketime: Please check whether any other libraries are in use that clash with libfaketime.\n");
|
||||
fprintf(stderr, "libfaketime: Returning -1 on clock_gettime() to break recursion now... if that does not work, please check other libraries' error handling.\n");
|
||||
if (tp != NULL)
|
||||
{
|
||||
tp->tv_sec = 0;
|
||||
tp->tv_nsec = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
ftpl_init();
|
||||
}
|
||||
recursion_depth--;
|
||||
}
|
||||
/* sanity check */
|
||||
if (tp == NULL)
|
||||
@@ -2064,7 +2202,7 @@ parse_modifiers:
|
||||
{
|
||||
user_rate = atof(strchr(user_faked_time, 'x')+1);
|
||||
user_rate_set = true;
|
||||
if (NULL != getenv("FAKETIME_XRESET")) {
|
||||
if (NULL != getenv("FAKETIME_XRESET")) {
|
||||
if (ftpl_timecache.real.tv_nsec >= 0) {
|
||||
user_faked_time_timespec.tv_sec = ftpl_faketimecache.real.tv_sec;
|
||||
user_faked_time_timespec.tv_nsec = ftpl_faketimecache.real.tv_nsec;
|
||||
@@ -2079,7 +2217,7 @@ parse_modifiers:
|
||||
ftpl_starttime.boot.tv_nsec = ftpl_timecache.boot.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (NULL != (tmp_time_fmt = strchr(user_faked_time, 'i')))
|
||||
{
|
||||
@@ -2132,6 +2270,10 @@ static void ftpl_init(void)
|
||||
real_lstat64 = dlsym(RTLD_NEXT, "__lxstat64");
|
||||
real_time = dlsym(RTLD_NEXT, "time");
|
||||
real_ftime = dlsym(RTLD_NEXT, "ftime");
|
||||
#ifdef FAKE_FILE_TIMESTAMPS
|
||||
real_utimes = dlsym(RTLD_NEXT, "utimes");
|
||||
real_utime = dlsym(RTLD_NEXT, "utime");
|
||||
#endif
|
||||
#if defined(__alpha__) && defined(__GLIBC__)
|
||||
real_gettimeofday = dlvsym(RTLD_NEXT, "gettimeofday", "GLIBC_2.1");
|
||||
#else
|
||||
@@ -2249,14 +2391,7 @@ static void ftpl_init(void)
|
||||
cache_enabled = 0;
|
||||
}
|
||||
}
|
||||
if ((tmp_env = getenv("FAKETIME_DONT_FAKE_MONOTONIC")) != NULL
|
||||
|| (tmp_env = getenv("DONT_FAKE_MONOTONIC")) != NULL)
|
||||
{
|
||||
if (0 == strcmp(tmp_env, "1"))
|
||||
{
|
||||
fake_monotonic_clock = 0;
|
||||
}
|
||||
}
|
||||
get_fake_monotonic_setting(&fake_monotonic_clock);
|
||||
/* Check whether we actually should be faking the returned timestamp. */
|
||||
|
||||
/* We can prevent faking time for specified commands */
|
||||
@@ -2451,6 +2586,10 @@ static void ftpl_init(void)
|
||||
parse_config_file = false;
|
||||
parse_ft_string(tmp_env);
|
||||
}
|
||||
else
|
||||
{
|
||||
read_config_file();
|
||||
}
|
||||
|
||||
dont_fake = dont_fake_final;
|
||||
}
|
||||
@@ -2486,13 +2625,62 @@ static void remove_trailing_eols(char *line)
|
||||
*/
|
||||
|
||||
#ifdef PTHREAD_SINGLETHREADED_TIME
|
||||
/*
|
||||
* To avoid a deadlock if a faketime function is interrupted by a signal while
|
||||
* holding the lock, we block all signals while the mutex is locked.
|
||||
* The original_mask field is used to restore the previous set of signals
|
||||
* after the lock has been released.
|
||||
* (Prompted by issues with parallel garbage collection in D 2.090. D uses signals
|
||||
* to freeze all but one thread. The frozen threads may be in faketime operations.)
|
||||
*/
|
||||
struct LockedState {
|
||||
pthread_mutex_t *mutex;
|
||||
sigset_t original_mask;
|
||||
};
|
||||
|
||||
static void pthread_cleanup_mutex_lock(void *data)
|
||||
{
|
||||
pthread_mutex_t *mutex = data;
|
||||
pthread_mutex_unlock(mutex);
|
||||
struct LockedState *state = data;
|
||||
pthread_mutex_unlock(state->mutex);
|
||||
pthread_sigmask(SIG_SETMASK, &state->original_mask, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
int read_config_file()
|
||||
{
|
||||
static char user_faked_time[BUFFERLEN]; /* changed to static for caching in v0.6 */
|
||||
static char custom_filename[BUFSIZ];
|
||||
static char filename[BUFSIZ];
|
||||
FILE *faketimerc;
|
||||
/* 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,
|
||||
* Jacob Moorman, and Wayne Davison of SourceForge, Inc. in version 0.6 */
|
||||
(void) snprintf(custom_filename, BUFSIZ, "%s", getenv("FAKETIME_TIMESTAMP_FILE"));
|
||||
(void) snprintf(filename, BUFSIZ, "%s/.faketimerc", getenv("HOME"));
|
||||
if ((faketimerc = fopen(custom_filename, "rt")) != NULL ||
|
||||
(faketimerc = fopen(filename, "rt")) != NULL ||
|
||||
(faketimerc = fopen("/etc/faketimerc", "rt")) != NULL)
|
||||
{
|
||||
static char line[BUFFERLEN];
|
||||
while(fgets(line, BUFFERLEN, faketimerc) != NULL)
|
||||
{
|
||||
if ((strlen(line) > 1) && (line[0] != ' ') &&
|
||||
(line[0] != '#') && (line[0] != ';'))
|
||||
{
|
||||
remove_trailing_eols(line);
|
||||
strncpy(user_faked_time, line, BUFFERLEN-1);
|
||||
user_faked_time[BUFFERLEN-1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(faketimerc);
|
||||
parse_ft_string(user_faked_time);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
{
|
||||
/* variables used for caching, introduced in version 0.6 */
|
||||
@@ -2518,10 +2706,20 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
/* Sanity check by Karl Chan since v0.8 */
|
||||
if (tp == NULL) return -1;
|
||||
|
||||
// {ret = value; goto abort;} to call matching pthread_cleanup_pop and return value
|
||||
int ret = INT_MAX;
|
||||
|
||||
#ifdef PTHREAD_SINGLETHREADED_TIME
|
||||
static pthread_mutex_t time_mutex=PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// block all signals while locked. prevents deadlocks if signal interrupts in in mid-operation.
|
||||
sigset_t all_signals, original_mask;
|
||||
sigfillset(&all_signals);
|
||||
pthread_sigmask(SIG_SETMASK, &all_signals, &original_mask);
|
||||
pthread_mutex_lock(&time_mutex);
|
||||
pthread_cleanup_push(pthread_cleanup_mutex_lock, &time_mutex);
|
||||
|
||||
struct LockedState state = { .mutex = &time_mutex, .original_mask = original_mask };
|
||||
pthread_cleanup_push(pthread_cleanup_mutex_lock, &state);
|
||||
#endif
|
||||
|
||||
if ((limited_faking &&
|
||||
@@ -2566,10 +2764,14 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
{
|
||||
/* 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;
|
||||
if (((ft_start_after_secs != -1) && (tmp_ts.tv_sec < ft_start_after_secs))
|
||||
|| ((ft_stop_after_secs != -1) && (tmp_ts.tv_sec >= ft_stop_after_secs))
|
||||
|| ((ft_start_after_ncalls != -1) && (callcounter < ft_start_after_ncalls))
|
||||
|| ((ft_stop_after_ncalls != -1) && (callcounter >= ft_stop_after_ncalls)))
|
||||
{
|
||||
ret = 0;
|
||||
goto abort;
|
||||
}
|
||||
/* fprintf(stderr, "(libfaketime limits -> runtime: %lu, callcounter: %lu continues\n", (*time_tptr - ftpl_starttime), callcounter); */
|
||||
}
|
||||
|
||||
@@ -2607,6 +2809,12 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
cache_expired = 1;
|
||||
}
|
||||
|
||||
if (force_cache_expiration != 0)
|
||||
{
|
||||
cache_expired = 1;
|
||||
force_cache_expiration = 0;
|
||||
}
|
||||
|
||||
if (cache_expired == 1)
|
||||
{
|
||||
static char user_faked_time[BUFFERLEN]; /* changed to static for caching in v0.6 */
|
||||
@@ -2631,42 +2839,22 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
/* fake time supplied as environment variable? */
|
||||
if (parse_config_file)
|
||||
{
|
||||
char custom_filename[BUFSIZ];
|
||||
char filename[BUFSIZ];
|
||||
FILE *faketimerc;
|
||||
/* 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,
|
||||
* Jacob Moorman, and Wayne Davison of SourceForge, Inc. in version 0.6 */
|
||||
(void) snprintf(custom_filename, BUFSIZ, "%s", getenv("FAKETIME_TIMESTAMP_FILE"));
|
||||
(void) snprintf(filename, BUFSIZ, "%s/.faketimerc", getenv("HOME"));
|
||||
if ((faketimerc = fopen(custom_filename, "rt")) != NULL ||
|
||||
(faketimerc = fopen(filename, "rt")) != NULL ||
|
||||
(faketimerc = fopen("/etc/faketimerc", "rt")) != NULL)
|
||||
{
|
||||
char line[BUFFERLEN];
|
||||
while(fgets(line, BUFFERLEN, faketimerc) != NULL)
|
||||
{
|
||||
if ((strlen(line) > 1) && (line[0] != ' ') &&
|
||||
(line[0] != '#') && (line[0] != ';'))
|
||||
{
|
||||
remove_trailing_eols(line);
|
||||
strncpy(user_faked_time, line, BUFFERLEN-1);
|
||||
user_faked_time[BUFFERLEN-1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(faketimerc);
|
||||
}
|
||||
if (read_config_file() == 0) parse_ft_string(user_faked_time);
|
||||
} /* read fake time from file */
|
||||
parse_ft_string(user_faked_time);
|
||||
else
|
||||
{
|
||||
parse_ft_string(user_faked_time);
|
||||
}
|
||||
/* read monotonic faketime setting from envar */
|
||||
get_fake_monotonic_setting(&fake_monotonic_clock);
|
||||
} /* cache had expired */
|
||||
|
||||
if (infile_set)
|
||||
{
|
||||
if (load_time(tp))
|
||||
{
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2729,12 +2917,16 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto abort;
|
||||
} // end of switch(ft_mode)
|
||||
|
||||
abort:
|
||||
#ifdef PTHREAD_SINGLETHREADED_TIME
|
||||
pthread_cleanup_pop(1);
|
||||
#endif
|
||||
// came here via goto abort?
|
||||
if (ret != INT_MAX) return ret;
|
||||
save_time(tp);
|
||||
|
||||
/* Cache this most recent real and faked time we encountered */
|
||||
@@ -2768,7 +2960,7 @@ int fake_clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
ftpl_faketimecache.boot.tv_nsec = tp->tv_nsec;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3157,7 +3349,7 @@ int pthread_cond_timedwait_232(pthread_cond_t *cond, pthread_mutex_t *mutex, con
|
||||
}
|
||||
|
||||
__asm__(".symver pthread_cond_timedwait_225, pthread_cond_timedwait@GLIBC_2.2.5");
|
||||
#ifdef __ARM_ARCH
|
||||
#if defined __ARM_ARCH || defined FORCE_PTHREAD_NONVER
|
||||
__asm__(".symver pthread_cond_timedwait_232, pthread_cond_timedwait@@");
|
||||
__asm__(".symver pthread_cond_init_232, pthread_cond_init@@");
|
||||
__asm__(".symver pthread_cond_destroy_232, pthread_cond_destroy@@");
|
||||
@@ -3169,6 +3361,89 @@ __asm__(".symver pthread_cond_destroy_232, pthread_cond_destroy@@GLIBC_2.3.2");
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Intercept calls to time-setting functions if compiled with FAKE_SETTIME set.
|
||||
* Based on suggestion and prototype by @ojura, see https://github.com/wolfcw/libfaketime/issues/179
|
||||
*/
|
||||
#ifdef FAKE_SETTIME
|
||||
int clock_settime(clockid_t clk_id, const struct timespec *tp) {
|
||||
|
||||
/* only CLOCK_REALTIME can be set */
|
||||
if (clk_id != CLOCK_REALTIME) {
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* sanity check for the pointer */
|
||||
if (tp == NULL) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* When setting the FAKETIME environment variable to the new timestamp,
|
||||
we do not have to care about 'x' or 'i' modifiers given previously,
|
||||
as they are not erased when parsing them. */
|
||||
struct timespec current_time;
|
||||
DONT_FAKE_TIME(clock_gettime(clk_id, ¤t_time))
|
||||
;
|
||||
|
||||
time_t sec_diff = tp->tv_sec - current_time.tv_sec;
|
||||
long nsec_diff = tp->tv_nsec - current_time.tv_nsec;
|
||||
char newenv_string[256];
|
||||
double offset = (double) sec_diff;
|
||||
offset += (double) nsec_diff/SEC_TO_nSEC;
|
||||
snprintf(newenv_string, 255, "%+f", offset);
|
||||
|
||||
setenv("FAKETIME", newenv_string, 1);
|
||||
force_cache_expiration = 1; /* make sure it becomes effective immediately */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int settimeofday(const struct timeval *tv, void *tz)
|
||||
{
|
||||
/* The use of timezone *tz is obsolete and simply ignored here. */
|
||||
if (tz == NULL) tz = NULL;
|
||||
|
||||
if (tv == NULL)
|
||||
{
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timespec tp;
|
||||
tp.tv_sec = tv->tv_sec;
|
||||
tp.tv_nsec = tv->tv_usec * 1000;
|
||||
clock_settime(CLOCK_REALTIME, &tp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adjtime (const struct timeval *delta, struct timeval *olddelta)
|
||||
{
|
||||
/* Always signal true full success when olddelta is requested. */
|
||||
if (olddelta != NULL)
|
||||
{
|
||||
olddelta->tv_sec = 0;
|
||||
olddelta->tv_usec = 0;
|
||||
}
|
||||
|
||||
if (delta != NULL)
|
||||
{
|
||||
struct timespec tp;
|
||||
clock_gettime(CLOCK_REALTIME, &tp);
|
||||
tp.tv_sec += delta->tv_sec;
|
||||
tp.tv_nsec += delta->tv_usec * 1000;
|
||||
/* This actually will make the clock jump instead of gradually
|
||||
adjusting it, but we fulfill the caller's intention and an
|
||||
additional thread just for the gradual changes does not seem
|
||||
to be worth the effort presently. */
|
||||
clock_settime(CLOCK_REALTIME, &tp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Editor modelines
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
#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); \
|
||||
int64_t tmp_time; \
|
||||
tmp_time = (c) * (int64_t) ((tvp)->tv_sec * SEC_TO_##prefix##SEC + \
|
||||
(int64_t) (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; \
|
||||
@@ -89,16 +89,28 @@
|
||||
#ifndef timersub
|
||||
#define timersub(a, b, result) timersub2(a, b, result, u)
|
||||
#endif
|
||||
#ifndef timersub
|
||||
#ifndef timermul
|
||||
#define timermul(a, c, result) timermul2(a, c, result, u)
|
||||
#endif
|
||||
|
||||
/* ops for nanosecs */
|
||||
#ifndef timespecisset
|
||||
#define timespecisset(tvp) timerisset2(tvp,n)
|
||||
#endif
|
||||
#ifndef timespecclear
|
||||
#define timespecclear(tvp) timerclear2(tvp, n)
|
||||
#endif
|
||||
#ifndef timespeccmp
|
||||
#define timespeccmp(a, b, CMP) timercmp2(a, b, CMP, n)
|
||||
#endif
|
||||
#ifndef timespecadd
|
||||
#define timespecadd(a, b, result) timeradd2(a, b, result, n)
|
||||
#endif
|
||||
#ifndef timespecsub
|
||||
#define timespecsub(a, b, result) timersub2(a, b, result, n)
|
||||
#endif
|
||||
#ifndef timespecmul
|
||||
#define timespecmul(a, c, result) timermul2(a, c, result, n)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user