mirror of
https://github.com/wolfcw/libfaketime.git
synced 2026-05-18 09:16:10 +03:00
Compare commits
290 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba77bd61b | ||
|
|
e36ec5726e | ||
|
|
37c9430117 | ||
|
|
4aa0077bfc | ||
|
|
097ce79771 | ||
|
|
994b7c75ca | ||
|
|
483a7a703c | ||
|
|
e8a1c1fd3b | ||
|
|
75d91ea726 | ||
|
|
6c7aa3966c | ||
|
|
3062fb2004 | ||
|
|
b687b165b2 | ||
|
|
e21bf3017a | ||
|
|
13d47210d5 | ||
|
|
712733e5f0 | ||
|
|
1e931cb4cd | ||
|
|
026a2627af | ||
|
|
7295f20288 | ||
|
|
92bf909d95 | ||
|
|
ebe76e26b0 | ||
|
|
c0aa6189f7 | ||
|
|
7ba95f4cb0 | ||
|
|
aabe141783 | ||
|
|
dbe865dfdb | ||
|
|
1231a002e0 | ||
|
|
3109728f45 | ||
|
|
949b36e6a2 | ||
|
|
0c76f27777 | ||
|
|
7bfe6566b3 | ||
|
|
c2a9bc1878 | ||
|
|
4d0f0b7426 | ||
|
|
9d63a80062 | ||
|
|
cccce3bf23 | ||
|
|
f33dda8022 | ||
|
|
4fc06b90df | ||
|
|
75e130c4f1 | ||
|
|
066f38baac | ||
|
|
4de86c2145 | ||
|
|
ffdb51bc30 | ||
|
|
3aa2028174 | ||
|
|
6566162e7e | ||
|
|
53ba71e547 | ||
|
|
6404d81f63 | ||
|
|
44c578c6d6 | ||
|
|
2649cdb156 | ||
|
|
71b31e908d | ||
|
|
523584abd4 | ||
|
|
a2e406c669 | ||
|
|
3ccdd344aa | ||
|
|
f63569e422 | ||
|
|
d276658b74 | ||
|
|
cb48e454be | ||
|
|
77ae25f529 | ||
|
|
30d7defcf5 | ||
|
|
9f7b304dbe | ||
|
|
0277016bb5 | ||
|
|
0e2dbe4ae1 | ||
|
|
45d29c8256 | ||
|
|
264e8efad7 | ||
|
|
3a3d1deebc | ||
|
|
6714b98794 | ||
|
|
3e56ada3ff | ||
|
|
2dca058f5c | ||
|
|
2e2d3eefb5 | ||
|
|
fa731ed50f | ||
|
|
3f6467d421 | ||
|
|
2dac72caba | ||
|
|
21af5175f5 | ||
|
|
b5a48c870b | ||
|
|
52fe3cc442 | ||
|
|
63aef51102 | ||
|
|
50e2c56914 | ||
|
|
b6e87c6f26 | ||
|
|
d9ba684b18 | ||
|
|
2503b0fffc | ||
|
|
97721e5491 | ||
|
|
fdb5ba3f7a | ||
|
|
f289bf702f | ||
|
|
536889d797 | ||
|
|
19b2476534 | ||
|
|
92c322507c | ||
|
|
0516055224 | ||
|
|
ba9ed5b289 | ||
|
|
7e9d69b98f | ||
|
|
a04750217b | ||
|
|
a3e91605ad | ||
|
|
b716122cbe | ||
|
|
23bec3882d | ||
|
|
2a2af0fcdc | ||
|
|
39fdbde365 | ||
|
|
c745ab783b | ||
|
|
f32986867a | ||
|
|
b2fe742aa7 | ||
|
|
265651969b | ||
|
|
6a0f35dcbd | ||
|
|
0af80dd593 | ||
|
|
8908752a25 | ||
|
|
27b9c83a27 | ||
|
|
942b30e940 | ||
|
|
7154a3f42c | ||
|
|
0c2e3d41be | ||
|
|
f262b5fba7 | ||
|
|
d17bb114c6 | ||
|
|
7df1bf7122 | ||
|
|
6d072025c0 | ||
|
|
8ef74e33b6 | ||
|
|
6fc4ae74f4 | ||
|
|
1997652d8e | ||
|
|
de37190d40 | ||
|
|
e0e6b79568 | ||
|
|
df8a045597 | ||
|
|
2adb56b07f | ||
|
|
1c80b19fe5 | ||
|
|
32eedc2b42 | ||
|
|
ccc9992840 | ||
|
|
a059f1294f | ||
|
|
f4bf28356c | ||
|
|
5c63238544 | ||
|
|
be4e373e63 | ||
|
|
431f09eb19 | ||
|
|
326c20ebb5 | ||
|
|
b61fade280 | ||
|
|
859751e2cb | ||
|
|
f706373bc2 | ||
|
|
e7ca8378ca | ||
|
|
3b3f80a42f | ||
|
|
2bfbe19f71 | ||
|
|
75cbe8e507 | ||
|
|
e8838709ea | ||
|
|
bf3a08b04d | ||
|
|
141d1a7a87 | ||
|
|
0f79f21e11 | ||
|
|
450d5d4549 | ||
|
|
fff49b23fc | ||
|
|
6e0f978079 | ||
|
|
f836ea3eb3 | ||
|
|
642b6ee870 | ||
|
|
03cb104691 | ||
|
|
7e86fb5419 | ||
|
|
e9c74131fc | ||
|
|
8fa0530d83 | ||
|
|
98e3d3f36f | ||
|
|
0ca35dd8c4 | ||
|
|
2d941a894f | ||
|
|
f50664f0bd | ||
|
|
d475b92594 | ||
|
|
40edcc7ca0 | ||
|
|
da348ae2dd | ||
|
|
68f01e7101 | ||
|
|
089a78add5 | ||
|
|
a8d6a76906 | ||
|
|
36090e8ceb | ||
|
|
0e61d3d191 | ||
|
|
985d666d73 | ||
|
|
9dcaf53fd7 | ||
|
|
02bc1fccae | ||
|
|
2c02fc08ef | ||
|
|
2d5126411b | ||
|
|
ee4f57d8a5 | ||
|
|
f09c98a89f | ||
|
|
527da8441b | ||
|
|
79defa361e | ||
|
|
6d7c42e2df | ||
|
|
a7e536bcca | ||
|
|
5466cd8a5f | ||
|
|
61490dc09a | ||
|
|
db47664840 | ||
|
|
bc24e278ff | ||
|
|
b0b9432ea4 | ||
|
|
84fc285923 | ||
|
|
806c05f49d | ||
|
|
fe6eeae423 | ||
|
|
f26242b655 | ||
|
|
e0ca33132d | ||
|
|
cbf1d729ed | ||
|
|
543f6b5040 | ||
|
|
aa9eb1006d | ||
|
|
14cf8d7ba8 | ||
|
|
f4ae29fb91 | ||
|
|
a9142e0e9a | ||
|
|
b7fff74716 | ||
|
|
e26859e5ca | ||
|
|
3155e0ee38 | ||
|
|
078a4e4060 | ||
|
|
9043941fa9 | ||
|
|
973111d78a | ||
|
|
4bab3179ce | ||
|
|
2090f5e548 | ||
|
|
f88c8d4221 | ||
|
|
9a73db074b | ||
|
|
932c138112 | ||
|
|
9e27b2ed8b | ||
|
|
c9f292ee39 | ||
|
|
d37421dbe7 | ||
|
|
89161a0cdf | ||
|
|
f87c2f8915 | ||
|
|
ce1d39c98f | ||
|
|
262d1d574f | ||
|
|
06d49adc12 | ||
|
|
1686664c97 | ||
|
|
5217bcd13d | ||
|
|
8ae4c9bc0e | ||
|
|
6733dc3a8d | ||
|
|
008d33fdf2 | ||
|
|
5a0071f952 | ||
|
|
51f1248593 | ||
|
|
e70b143733 | ||
|
|
e1073c8733 | ||
|
|
01b0b4bb56 | ||
|
|
5f5756ccd9 | ||
|
|
bca9f1bf90 | ||
|
|
d3f3ee38c6 | ||
|
|
a3f9410e51 | ||
|
|
a92d6ffe7c | ||
|
|
253774c8d8 | ||
|
|
986e6e1cdc | ||
|
|
0bfb72b627 | ||
|
|
5a1bd98979 | ||
|
|
7e62881c8f | ||
|
|
0e6b1b2460 | ||
|
|
1297568caf | ||
|
|
940502b3de | ||
|
|
26b4b395e9 | ||
|
|
0b0cc29d2e | ||
|
|
a5885f1479 | ||
|
|
0872c6c0c0 | ||
|
|
a51a38d0ae | ||
|
|
f47223ff12 | ||
|
|
7b1d0958b5 | ||
|
|
17522c5ba1 | ||
|
|
5e62eafcc2 | ||
|
|
01f6bc76c9 | ||
|
|
f329eee8c5 | ||
|
|
c89582fc1f | ||
|
|
9a38e5d775 | ||
|
|
7f4e5c378a | ||
|
|
9337bccfcb | ||
|
|
3668fd9b0f | ||
|
|
3a81c6becd | ||
|
|
3db9d20828 | ||
|
|
20e74b1b02 | ||
|
|
00d6edf90c | ||
|
|
811283e683 | ||
|
|
a8283c646d | ||
|
|
2ca0b719e3 | ||
|
|
f6ddc32695 | ||
|
|
8de66f799f | ||
|
|
63fe6f0be5 | ||
|
|
062abac575 | ||
|
|
004222585e | ||
|
|
9c59e24d33 | ||
|
|
46dc625642 | ||
|
|
8f2c856d8e | ||
|
|
b4a822cd6a | ||
|
|
54994ceb0d | ||
|
|
3c0b101a84 | ||
|
|
44a6d1f0fa | ||
|
|
772d9523a7 | ||
|
|
8b5519d496 | ||
|
|
3ba66842aa | ||
|
|
4359458c7c | ||
|
|
726c4657fc | ||
|
|
8853afb509 | ||
|
|
48f280ac86 | ||
|
|
47e6f5f33d | ||
|
|
e4e5ea6211 | ||
|
|
206ae9ea80 | ||
|
|
cce377b371 | ||
|
|
5e6ed4cd2c | ||
|
|
55e634a6ca | ||
|
|
532816864e | ||
|
|
4564afb924 | ||
|
|
5b8673df54 | ||
|
|
e00ba47ca9 | ||
|
|
ca2f3fefa1 | ||
|
|
dacc5866a7 | ||
|
|
b35e7c8ca6 | ||
|
|
25a60d0292 | ||
|
|
d90c8c26d3 | ||
|
|
c683c81417 | ||
|
|
f19d68ea32 | ||
|
|
c36674c27f | ||
|
|
112809f986 | ||
|
|
9498b2cacc | ||
|
|
c9a3b1bace | ||
|
|
834953480e | ||
|
|
c1d10321a7 | ||
|
|
58ccfb6c27 | ||
|
|
1e25e1f042 | ||
|
|
690ed3f158 |
12
.github/workflows/make-test.yml
vendored
12
.github/workflows/make-test.yml
vendored
@@ -2,22 +2,22 @@ name: Run make test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
schedule:
|
||||
- cron: '30 9 * * *'
|
||||
|
||||
- cron: '30 9 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
os: [ubuntu-latest, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: make
|
||||
run: FAKETIME_COMPILE_CFLAGS="-DFORCE_MONOTONIC_FIX" make
|
||||
- name: make test
|
||||
run: make test
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,9 +1,16 @@
|
||||
*.o
|
||||
*.so.1
|
||||
timetest
|
||||
test/getrandom_test
|
||||
test/lib*.so
|
||||
test/use_lib_*
|
||||
test/run_*
|
||||
test/repeat_random
|
||||
test/getentropy_test
|
||||
test/syscall_test
|
||||
test/variadic_promotion
|
||||
|
||||
src/libfaketime.dylib.1
|
||||
src/libfaketime.1.dylib
|
||||
src/core
|
||||
src/faketime
|
||||
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -3,11 +3,19 @@ language: c
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
arch: amd64
|
||||
compiler: gcc
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
compiler: gcc
|
||||
- os: osx
|
||||
osx_image: xcode11
|
||||
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- FAKETIME_COMPILE_CFLAGS="-DFORCE_MONOTONIC_FIX" make
|
||||
- if [ "$TRAVIS_ARCH" = ppc64le ]; then
|
||||
FAKETIME_COMPILE_CFLAGS="-DFORCE_MONOTONIC_FIX -DFORCE_PTHREAD_NONVER" make;
|
||||
else
|
||||
FAKETIME_COMPILE_CFLAGS="-DFORCE_MONOTONIC_FIX" make;
|
||||
fi
|
||||
- make test
|
||||
|
||||
1
Makefile
1
Makefile
@@ -2,6 +2,7 @@ INSTALL ?= install
|
||||
|
||||
UNAME=$(shell uname)
|
||||
SELECTOR:=$(shell if test "${UNAME}" = "Darwin" ; then echo "-f Makefile.OSX" ; fi)
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
all:
|
||||
$(MAKE) $(SELECTOR) -C src all
|
||||
|
||||
46
NEWS
46
NEWS
@@ -1,8 +1,50 @@
|
||||
List of changes for v0.9.12
|
||||
===========================
|
||||
|
||||
Since 0.9.11:
|
||||
- Improved macOS compatibility (@usertam)
|
||||
|
||||
Since 0.9.10:
|
||||
- Fixed various cross-platform compile-time issues
|
||||
- Honor nanosecond parameters/fields in relevant system calls
|
||||
- Limited improvements to enhance compatibility with other
|
||||
LD_PRELOAD libraries
|
||||
- Added selected more intercepted system calls
|
||||
- Unset FAKETIME_SHARED automatically for child processes
|
||||
when enabling FAKETIME_FLSHM=1
|
||||
- Disable shared memory for child processes through
|
||||
FAKETIME_DISABLE_SHM=1
|
||||
|
||||
Since 0.9.9:
|
||||
- automatically try to decide about FORCE_MONOTONIC_FIX
|
||||
at run-time when not set as a compile-time flag
|
||||
- improved macOS Monterey support through dyld interposing
|
||||
- changed interception hooks for stat() and similar functions,
|
||||
refactored to use a common handler (@sirainen)
|
||||
- added support for timespec_get, timerfd_{get,set} (@sliquister)
|
||||
- generic syscall() interception for selected syscalls (@dkg)
|
||||
- improved testing system (@dkg)
|
||||
|
||||
Since 0.9.8:
|
||||
- When compiled with the CFLAG FAKE_RANDOM set,
|
||||
libfaketime will intercept calls to getrandom()
|
||||
and return pseudorandom numbers for determinism.
|
||||
The mechanism needs to be activated by setting
|
||||
the environment variable FAKERANDOM_SEED to a
|
||||
64-bit seed value, e.g., "0x12345678DEADBEEF".
|
||||
Please note that this completely breaks the
|
||||
security of random numbers for cryptographic
|
||||
purposes and should only be used for deterministic
|
||||
tests. Never use this in production!
|
||||
- When the environment variable FAKETIME_TIMESTAMP_FILE is
|
||||
set, points to a writeable (creatable) custom config file
|
||||
and the environment variable FAKETIME_UPDATE_TIMESTAMP_FILE
|
||||
is "1", then the file also is updated on each call. By
|
||||
this, a common "virtual time" can be shared by several
|
||||
processes, where each can adjust the time for all.
|
||||
- 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)
|
||||
|
||||
156
README
156
README
@@ -1,5 +1,5 @@
|
||||
libfaketime, version 0.9.8 (August 2019)
|
||||
========================================
|
||||
libfaketime, version 0.9.12 (June 2025)
|
||||
=======================================
|
||||
|
||||
|
||||
Content of this file:
|
||||
@@ -20,6 +20,7 @@ Content of this file:
|
||||
i) "Limiting" libfaketime per process
|
||||
j) Spawning an external process
|
||||
k) Saving timestamps to file, loading them from file
|
||||
l) Replacing random numbers with deterministic number sequences
|
||||
5. License
|
||||
6. Contact
|
||||
|
||||
@@ -102,6 +103,28 @@ documentation whether it can be achieved by using libfaketime directly.
|
||||
FORCE_MONOTONIC_FIX alone does not solve the hang on the MONOTONIC_CLOCK
|
||||
test.
|
||||
|
||||
If FORCE_MONOTONIC_FIX was not set as a compile-time flag, you can also
|
||||
set an environment variable FAKETIME_FORCE_MONOTONIC_FIX=1 if you want
|
||||
to enable the fix at run-time, or to 0 if you explicitly want to disable
|
||||
it. The fix is automatically enabled if libfaketime was compiled on a
|
||||
system with glibc as the underlying libc implementation, and a glibc
|
||||
version is detected at run-time that is assumed to need this workaround.
|
||||
Please use Github issues at https://github.com/wolfcw/libfaketime/issues
|
||||
to report any observed hangs during CLOCK_MONOTONIC tests and report
|
||||
your CPU architecture, libc implementation (e.g., glibc 2.30) and any
|
||||
other details that might help (e.g., Linux distribution, use within, e.g.,
|
||||
Docker containers etc.).
|
||||
|
||||
Please try to avoid compiling with FORCE_MONOTONIC_FIX on platforms that
|
||||
do not need it. While it won't make a difference in most cases, depending
|
||||
on the specific FAKETIME settings in use, it would cause certain
|
||||
intercepted functions such as pthread_cond_timedwait() return with a
|
||||
time-out too early or too late, which could break some applications.
|
||||
Try compiling without FORCE_MONOTONIC_FIX first and check whether the
|
||||
tests appear to hang. If they do, you can either set the
|
||||
FAKETIME_FORCE_MONOTONIC_FIX environment variable to 1, or re-compile
|
||||
with FORCE_MONOTONIC_FIX set.
|
||||
|
||||
|
||||
3. Installation
|
||||
---------------
|
||||
@@ -187,19 +210,21 @@ linker configuration file, e.g., /etc/ld.so.conf.d/local.conf, and then run
|
||||
the "ldconfig" command. Afterwards, using LD_PRELOAD=libfaketime.so.1 suffices.
|
||||
|
||||
However, also the faked time should be specified; otherwise, libfaketime will
|
||||
be loaded, but just report the real system time. There are three ways to
|
||||
be loaded, but just report the real system time. There are multiple ways to
|
||||
specify the faked time:
|
||||
|
||||
a) By setting the environment variable FAKETIME.
|
||||
b) By using the file given in the environment variable FAKETIME_TIMESTAMP_FILE
|
||||
c) By using the file .faketimerc in your home directory.
|
||||
d) By using the file /etc/faketimerc for a system-wide default.
|
||||
e) By using FAKETIME_UPDATE_TIMESTAMP_FILE and date -s "<time>" or alike.
|
||||
|
||||
If you want to use b) c) or d), $HOME/.faketimerc or /etc/faketimerc consist of
|
||||
only one line of text with exactly the same content as the FAKETIME environment
|
||||
variable, which is described below. Note that /etc/faketimerc will only be used
|
||||
if there is no $HOME/.faketimerc and no FAKETIME_TIMESTAMP_FILE file exists.
|
||||
Also, the FAKETIME environment variable _always_ has priority over the files.
|
||||
For FAKETIME_UPDATE_TIMESTAMP_FILE please see below.
|
||||
|
||||
|
||||
4b) Using absolute dates
|
||||
@@ -231,10 +256,10 @@ the difference:
|
||||
|
||||
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
|
||||
FAKETIME_DONT_RESET=1 \
|
||||
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
|
||||
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
|
||||
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
|
||||
|
||||
In the second example, the "date" command will always print the same time,
|
||||
while in the first example, with FAKETIME_DONT_RESET set, time will increment
|
||||
@@ -324,12 +349,12 @@ the same global clock without restarting it at the start of each process.
|
||||
Please note that using "x" or "i" in FAKETIME still requires giving an offset
|
||||
(see section 4d). This means that "+1y x2" will work, but "x2" only will not.
|
||||
If you do not want to fake the time, but just modify clock speed, use something
|
||||
like "+0 x2", i.e., use an explizit zero offset as a prefix in your FAKETIME.
|
||||
like "+0 x2", i.e., use an explicit zero offset as a prefix in your FAKETIME.
|
||||
|
||||
For testing, your should run a command like
|
||||
|
||||
LD_PRELOAD=./libfaketime.so.1 FAKETIME="+1,5y x10,0" \
|
||||
/bin/bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
|
||||
For each second that the endless loop sleeps, the executed bash shell will
|
||||
think that 10 seconds have passed ($SECONDS is a bash-internal variable
|
||||
@@ -368,7 +393,8 @@ may take up to 10 seconds before the new fake time is applied. If this is a
|
||||
problem in your scenario, you can change number of seconds before the file is read
|
||||
again with environment variable FAKETIME_CACHE_DURATION, or disable caching at all
|
||||
with FAKETIME_NO_CACHE=1. Remember that disabling the cache may negatively
|
||||
influence the performance.
|
||||
influence the performance (especially when not using FAKETIME environment
|
||||
but configuration files, such as FAKETIME_TIMESTAMP_FILE).
|
||||
|
||||
|
||||
Setting FAKETIME by means of a file timestamp
|
||||
@@ -394,6 +420,15 @@ LD_PRELOAD=/path/to/libfaketime.so.1 \
|
||||
# (in a different terminal window or whatever)
|
||||
touch -t 2002290123.45 /tmp/my-demo-file.tmp
|
||||
|
||||
Setting the environment variable FAKETIME_FOLLOW_ABSOLUTE=1 enables a submode
|
||||
of FAKETIME_FOLLOW_FILE behavior where fake time ONLY advances when the follow
|
||||
file's timestamp advances. In this mode an application that is not subject to
|
||||
libfaketime LD_PRELOAD intercept can absolutely control time for applications
|
||||
that are hooked by libfaketime. For example, a host application can control the
|
||||
timestamp of a follow file mapped into a container to implement (relatively)
|
||||
clean pause/resume behavior for fake time applications running within the
|
||||
container.
|
||||
|
||||
|
||||
Changing the 'x' modifier during run-time
|
||||
-----------------------------------------
|
||||
@@ -434,9 +469,14 @@ Cleaning up shared memory
|
||||
-------------------------
|
||||
|
||||
libfaketime uses semaphores and shared memory on platforms that support it in
|
||||
order to sync faketime settings across parent-child processes. It will clean
|
||||
up when it exits properly. However, when processes are terminated (e.g., by
|
||||
Ctrl-C on command line), shared memory cannot be cleaned up properly. In such
|
||||
order to sync faketime settings across parent-child processes.
|
||||
|
||||
Please note that this does not share the time set by settimeofday (for that
|
||||
see FAKETIME_UPDATE_TIMESTAMP_FILE below).
|
||||
|
||||
libfaketime will clean up when it exits properly.
|
||||
However, when processes are terminated (e.g., by Ctrl-C on command line),
|
||||
shared memory cannot be cleaned up properly. In such
|
||||
cases, you should occasionally delete
|
||||
|
||||
/dev/shm/faketime_shm_* and
|
||||
@@ -455,15 +495,63 @@ for long-running systems (servers with high uptime) and systems on which
|
||||
a lot of processes are started (e.g., servers handling many containers
|
||||
or similar virtualization mechanisms).
|
||||
|
||||
Use of shared memory can be disabled by setting the FAKETIME_DISABLE_SHM
|
||||
environment variable, or equivalently, passing --disable-shm to faketime.
|
||||
|
||||
|
||||
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.
|
||||
settimeofday(), and adjtime(). The FAKETIME environment
|
||||
variable will be adjusted on each call.
|
||||
|
||||
When the environment variable FAKETIME_TIMESTAMP_FILE is set, points to a
|
||||
writeable (creatable) custom config file and the environment variable
|
||||
FAKETIME_UPDATE_TIMESTAMP_FILE is "1", then the file also is updated on each
|
||||
call. By this, a common "virtual time" can be shared by several
|
||||
processes, where each can adjust the time for all.
|
||||
|
||||
|
||||
Sharing "virtual settable time" between independent processes
|
||||
-------------------------------------------------------------
|
||||
|
||||
When libfaketime was compiled with FAKETIME_COMPILE_CFLAGS="-DFAKE_SETTIME",
|
||||
it can be configured to support a common time offset for multiple processes.
|
||||
This for example allows to use "ntpdate" as normal user without affecting
|
||||
system clock, interactively testing software with different dates or testing
|
||||
complex software with multiple independent processes that themself use
|
||||
settime internally.
|
||||
|
||||
Examples:
|
||||
|
||||
$ export LD_PRELOAD=libfaketime.so.1
|
||||
$ export FAKETIME_TIMESTAMP_FILE="/tmp/my-faketime.rc"
|
||||
$ export FAKETIME_UPDATE_TIMESTAMP_FILE=1
|
||||
$ export FAKETIME_CACHE_DURATION=1 # in seconds
|
||||
# or: export FAKETIME_NO_CACHE=1
|
||||
|
||||
$ date -s "1999-12-24 16:00:00"
|
||||
Fri Dec 24 16:00:00 CET 1999
|
||||
$ LD_PRELOAD="" date
|
||||
Thu Apr 9 15:19:38 CEST 2020
|
||||
$ date
|
||||
Fri Dec 24 16:00:02 CET 1999
|
||||
$ /usr/sbin/ntpdate -u clock.isc.org
|
||||
9 Apr 15:18:37 ntpdate[718]: step time server xx offset 640390517.057257 sec
|
||||
$ date
|
||||
Thu Apr 9 15:18:40 CEST 2020
|
||||
|
||||
In another terminal, script or environmment the same variables could be set
|
||||
and the same time would be printed.
|
||||
This also avoid the need to directly update the rc config file to use
|
||||
different times, but of course only supports time offsets.
|
||||
|
||||
Please note that this feature is not compatible with several other features,
|
||||
such as FAKETIME_FOLLOW_FILE, FAKETIME_XRESET and maybe others. After first
|
||||
settime, offsets will be used in FAKETIME_TIMESTAMP_FILE, even if it
|
||||
initially used advanced time specification options.
|
||||
|
||||
|
||||
4f) Faking the date and time system-wide
|
||||
@@ -639,6 +727,11 @@ seconds.
|
||||
4k) Saving timestamps to file, loading them from file
|
||||
-----------------------------------------------------
|
||||
|
||||
To store and load timestamp _offsets_ using _one and the same_ file allowing
|
||||
to share a common "virtual time" between independent processes, please see
|
||||
FAKETIME_UPDATE_TIMESTAMP_FILE above. The FAKETIME_SAVE_FILE feature is
|
||||
different.
|
||||
|
||||
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, libfaketime
|
||||
@@ -658,6 +751,41 @@ faketime needs to be run using the faketime wrapper to use these files. This
|
||||
functionality has been added by Balint Reczey in v0.9.5.
|
||||
|
||||
|
||||
4l) Replacing random numbers with deterministic number sequences
|
||||
----------------------------------------------------------------
|
||||
|
||||
libfaketime can be compiled with the CFLAG FAKE_RANDOM set (see src/Makefile).
|
||||
|
||||
When compiled this way, libfaketime additionally intercepts calls to the
|
||||
function getrandom(), which currently is Linux-specific.
|
||||
|
||||
This functionality is intended to feed a sequence of deterministic, repeatable
|
||||
numbers to applications, which use getrandom(), instead of the random numbers
|
||||
provided by /dev/[u]random.
|
||||
|
||||
For creating the deterministic number sequence, libfaketime internally
|
||||
uses Bernard Widynski's Middle Square Weyl Sequence Random Number Generator,
|
||||
see https://mswsrng.wixsite.com/rand.
|
||||
|
||||
It requires a 64-bit seed value, which has to be passed via the environment
|
||||
variable FAKERANDOM_SEED, as in, for example
|
||||
|
||||
LD_PRELOAD=src/libfaketime.so.1 \
|
||||
FAKERANDOM_SEED="0x12345678DEADBEEF" \
|
||||
test/getrandom_test
|
||||
|
||||
Whenever the same seed value is used, the same sequence of "random-looking"
|
||||
numbers is generated.
|
||||
|
||||
Please be aware that this definitely breaks any security properties that
|
||||
may be attributed to random numbers delivered by getrandom(), e.g., in the
|
||||
context of cryptographic operations. Use it for deterministic testing
|
||||
purposes only. Never use it in production.
|
||||
|
||||
For a discussion on why this apparently not date-/time-related function
|
||||
has been added to libfaketime and how it may evolve, see Github issue #275.
|
||||
|
||||
|
||||
5. License
|
||||
----------
|
||||
|
||||
|
||||
63
README.OSX
63
README.OSX
@@ -1,10 +1,17 @@
|
||||
README file for libfaketime on macOS
|
||||
====================================
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
! If you compiled libfaketime successfully but even the simple examples !
|
||||
! with the "date" command do not seem to work, please see the notes about !
|
||||
! SIP (system integrity protection) in this document! !
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
|
||||
Support for macOS has meanwhile matured and many command line and
|
||||
GUI applications will run stable.
|
||||
|
||||
Developments and tests are done on Mojave currently.
|
||||
Developments and tests are done on Catalina currently.
|
||||
|
||||
Version 0.9.5 and higher no longer work with OSX <= 10.6 due to
|
||||
changes in the underlying system libraries. If you need libfaketime
|
||||
@@ -138,6 +145,13 @@ However, there are two important aspects:
|
||||
application to a non-SIP-protected path, and if libfaketime still does not
|
||||
work, feel free to report it.
|
||||
|
||||
Please note that this also applies to simple programs such as /bin/date,
|
||||
which is used as an example in the libfaketime documentation and help texts.
|
||||
|
||||
Again, either disable SIP on your system (which might not be the best idea),
|
||||
or copy the applications / programs you want to use with libfaketime to
|
||||
a different path, which is not SIP-protected, e.g., within your home directory.
|
||||
|
||||
- We cannot and will not help with using libfaketime for proprietary or
|
||||
commercial software unless you are its developer trying to integrate
|
||||
libfaketime. Please contact the developers or the vendor directly if
|
||||
@@ -152,3 +166,50 @@ The environment variable FAKETIME can be changed at application run-time
|
||||
and always takes precedence over other user-controlled settings. It can
|
||||
be re-set to 0 (zero) to work around potential incompatibilities or if
|
||||
you do not want libfaketime applied to your software.
|
||||
|
||||
5) Working with the new arm64e system binaries in Apple Silicon
|
||||
---------------------------------------------------------------
|
||||
|
||||
Since Apple Silicon, Apple started shipping system binaries compiled against
|
||||
the `arm64e` ABI. This new ABI enforces Pointer Authentication Codes (PACs),
|
||||
and enforces assembly instructions to sign and check pointer signatures to
|
||||
prevent malicious control flow altering.
|
||||
|
||||
$ file /bin/date
|
||||
/bin/date: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
|
||||
/bin/date (for architecture x86_64): Mach-O 64-bit executable x86_64
|
||||
/bin/date (for architecture arm64e): Mach-O 64-bit executable arm64e
|
||||
|
||||
Most importantly, the new `arm64e` ABI is incompatible with the normal `arm64`
|
||||
ABI we are used to; this is done so that everything `arm64e` is PAC-enforced.
|
||||
|
||||
As a result, this will happen when we try to hook naive `arm64` libfaketime on
|
||||
system binaries (and vice versa with `arm64e` libfaketime on `arm64` binaries):
|
||||
|
||||
$ DYLD_INSERT_LIBRARIES=libfaketime.1.dylib /bin/date
|
||||
dyld[5788]: terminating because inserted dylib 'libfaketime.1.dylib' could not be loaded:
|
||||
tried: 'libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e'))
|
||||
|
||||
Since PR #497, we now compile libfaketime with a fat library/binary setup, so
|
||||
that we support both ABIs at the same time:
|
||||
|
||||
$ file libfaketime.1.dylib
|
||||
libfaketime.1.dylib: Mach-O universal binary with 2 architectures: [arm64:Mach-O 64-bit dynamically linked shared library arm64] [arm64e:Mach-O 64-bit dynamically linked shared library arm64e]
|
||||
libfaketime.1.dylib (for architecture arm64): Mach-O 64-bit dynamically linked shared library arm64
|
||||
libfaketime.1.dylib (for architecture arm64e): Mach-O 64-bit dynamically linked shared library arm64e
|
||||
|
||||
Unfortunately, Apple does not support running third-party `arm64e` code yet,
|
||||
since the ABI is still unstable. This means that you cannot use libfaketime
|
||||
on system `arm64e` binaries out of the box, at the time of writing.
|
||||
|
||||
If you really need to, you may disable SIP in the recovery terminal:
|
||||
|
||||
(in recovery) # csrutil disable
|
||||
|
||||
And enable the experimental ABI after boot:
|
||||
|
||||
(in regular boot) $ sudo nvram boot-args=-arm64e_preview_abi
|
||||
|
||||
Then `arm64e` should work as-is. This use case is rather uncommon since most
|
||||
userspace binaries will remain `arm64` for the time being, until Apple really
|
||||
doubles down on `arm64e`. Regardless, we should be prepared for that.
|
||||
|
||||
@@ -14,6 +14,10 @@ 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.
|
||||
|
||||
For "make test", an optional environment variable FAKETIME_TESTLIB can
|
||||
be set, pointing to the path and filename of the libfaketime library
|
||||
to be used for tests; the default is "../src/libfaketime.so.1".
|
||||
|
||||
|
||||
However, one problem makes it somewhat difficult to get libfaketime
|
||||
working on different platforms:
|
||||
|
||||
@@ -6,7 +6,7 @@ all:
|
||||
|
||||
install:
|
||||
$(INSTALL) -Dm0644 faketime.1 "${DESTDIR}${PREFIX}/share/man/man1/faketime.1"
|
||||
gzip -f "${DESTDIR}${PREFIX}/share/man/man1/faketime.1"
|
||||
gzip -nf "${DESTDIR}${PREFIX}/share/man/man1/faketime.1"
|
||||
|
||||
uninstall:
|
||||
rm -f "${DESTDIR}${PREFIX}/share/man/man1/faketime.1.gz"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH FAKETIME "1" "August 2019" "faketime 0.9.8" wolfcw
|
||||
.TH FAKETIME "1" "June 2025" "faketime 0.9.12" wolfcw
|
||||
.SH NAME
|
||||
faketime \- manipulate the system time for a given command
|
||||
.SH SYNOPSIS
|
||||
@@ -7,11 +7,11 @@ faketime \- manipulate the system time for a given command
|
||||
.SH DESCRIPTION
|
||||
.\" \fIfaketime\fR will trick the given program into seeing the specified timestamp as its starting date and time.
|
||||
.PP
|
||||
The given command will be tricked into believing that the current system time is the one specified in the timestamp. Filesystem timestamps will also be
|
||||
reported relative to this timestamp. The wall clock will continue to run from this date and time unless specified otherwise (see advanced options).
|
||||
Actually, faketime is a simple wrapper for libfaketime, which uses the LD_PRELOAD mechanism to load a small library which intercepts system calls to
|
||||
functions such as \fItime(2)\fR and \fIfstat(2)\fR. This wrapper exposes only a subset of libfaketime's functionality; please refer to the README file
|
||||
that came with faketime for more details and advanced options, or have a look at http://github.com/wolfcw/libfaketime
|
||||
The given command will be tricked into believing that the current system time is the one specified in the timestamp.
|
||||
Filesystem timestamps will also be reported relative to this timestamp.
|
||||
The wall clock will continue to run from this date and time unless specified otherwise (see advanced options).
|
||||
Actually, faketime is a simple wrapper for libfaketime, which uses the LD_PRELOAD mechanism to load a small library which intercepts system calls to functions such as \fItime(2)\fR and \fIfstat(2)\fR.
|
||||
This wrapper exposes only a subset of libfaketime's functionality; please refer to the README file that came with faketime for more details and advanced options, or have a look at https://github.com/wolfcw/libfaketime
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-\-help\fR
|
||||
@@ -23,59 +23,80 @@ show version information and quit.
|
||||
\fB\-m\fR
|
||||
use the multi-threading variant of libfaketime.
|
||||
.TP
|
||||
\fB\-p <PID>\fR
|
||||
pretend that the program's process ID is PID. (only available if built with FAKE_PID)
|
||||
.TP
|
||||
\fB\-f\fR
|
||||
use the advanced timestamp specification format.
|
||||
.TP
|
||||
\fB\--exclude-monotonic\fR
|
||||
Do not fake time when the program makes a call to clock_gettime with a CLOCK_MONOTONIC clock.
|
||||
.TP
|
||||
\fB\--disable-shm\fR
|
||||
Disable use of shared memory by libfaketime.
|
||||
.TP
|
||||
\fB\--date-prog <PATH>\fR
|
||||
Use a specific GNU-date compatible implementation of the helper used to transform "timestamp format" strings into programmatically usable dates, instead of a compile-time default guess for the generic target platform.
|
||||
|
||||
.SH EXAMPLES
|
||||
.nf
|
||||
faketime 'last Friday 5 pm' /bin/date
|
||||
faketime '2008-12-24 08:15:42' /bin/date
|
||||
faketime -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y i2,0' /bin/bash -c 'while true; do date ; sleep 1 ; done'
|
||||
faketime 'last Friday 5 pm' date
|
||||
faketime '2008-12-24 08:15:42' date
|
||||
faketime -f '+2,5y x10,0' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y x0,50' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
|
||||
faketime -f '+2,5y i2,0' bash -c 'while true; do date ; sleep 1 ; done'
|
||||
In this single case all spawned processes will use the same global clock without restarting it at the start of each process.
|
||||
|
||||
(Please note that it depends on your locale settings whether . or , has to be used for fractional offsets)
|
||||
.fi
|
||||
.SH ADVANCED TIMESTAMP FORMAT
|
||||
The simple timestamp format used by default applies the \fB/bin/date -d\fR command to parse user-friendly specifications such as 'last friday'. When using
|
||||
the faketime option \fB\-f\fR, the timestamp specified on the command line is directly passed to libfaketime, which enables a couple of additional features
|
||||
such as speeding the clock up or slowing it down for the target program. It is strongly recommended that you have a look at the libfaketime documentation. Summary:
|
||||
The simple timestamp format used by default applies the \fB/bin/date -d\fR command to parse user-friendly specifications such as 'last friday'.
|
||||
When using the faketime option \fB\-f\fR, the timestamp specified on the command line is directly passed to libfaketime, which enables a couple of additional features such as speeding the clock up or slowing it down for the target program.
|
||||
It is strongly recommended that you have a look at the libfaketime documentation.
|
||||
Summary:
|
||||
.TP
|
||||
Freeze clock at absolute timestamp: \fB"YYYY-MM-DD hh:mm:ss"\fR
|
||||
If you want to specify an absolute point in time, exactly this format must be used. Please note that freezing the clock is usually not what you want and may break the application. Only use if you know what you're doing!
|
||||
If you want to specify an absolute point in time, exactly this format must be used.
|
||||
Please note that freezing the clock is usually not what you want and may break the application.
|
||||
Only use if you know what you're doing!
|
||||
.TP
|
||||
Relative time offset: \fB"[+/-]123[m/h/d/y]\fR, e.g. "+60m", "+2y"
|
||||
This is the most often used format and specifies the faked time relatively to the current real time. The first character of the format string \fBmust\fR be a + or a -. The numeric value by default represents seconds, but the modifiers m, h, d, and y can be used to specify minutes, hours, days, or years, respectively. For example, "-2y" means "two years ago". Fractional time offsets can be used, e.g. "+2,5y", which means "two and a half years in the future". Please note that the fraction delimiter depends on your locale settings, so if "+2,5y" does not work, you might want to try "+2.5y".
|
||||
Relative time offset: \fB"[+/-]123[m/h/d/y]"\fR, e.g., "+60m", "+2y"
|
||||
This is the most often used format and specifies the faked time relatively to the current real time.
|
||||
The first character of the format string \fBmust\fR be a + or a -.
|
||||
The numeric value by default represents seconds, but the modifiers m, h, d, and y can be used to specify minutes, hours, days, or years, respectively.
|
||||
For example, "-2y" means "two years ago". Fractional time offsets can be used, e.g., "+2,5y", which means "two and a half years in the future".
|
||||
Please note that the fraction delimiter depends on your locale settings, so if "+2,5y" does not work, you might want to try "+2.5y".
|
||||
.TP
|
||||
Start-at timestamps: \fB"@YYYY-MM-DD hh:mm:ss"\fR
|
||||
The wall clock will start counting at the given timestamp for the program. This can be used for specifying absolute timestamps without freezing the clock.
|
||||
The wall clock will start counting at the given timestamp for the program.
|
||||
This can be used for specifying absolute timestamps without freezing the clock.
|
||||
.SH ADVANCED USAGE
|
||||
When using relative time offsets or start-at timestamps (see ADVANCED TIMESTAMP FORMAT above and option \fB\-f\fR), the clock speed can be adjusted, i.e. time may run faster or slower for the executed program. For example, \fB"+5y x10"\fR will set the faked time 5 years into the future and make the time pass 10 times as fast (one real second equals 10 seconds measured by the program). Similarly, the flow of time can be slowed, e.g. using \fB"-7d x0,2"\fR, which will set the faked time 7 days in the past and set the clock speed to 20 percent, i.e. it takes five real world seconds for one second measured by the program. Again, depending on your locale, either "x2.0" or "x2,0" may be required regarding the delimiter. You can also make faketime to advance the reported time by a preset interval upon each time() call independently from the system's time using \fB"-7d i2,0"\fR, where
|
||||
\fB"i"\fR is followed by the increase interval in seconds.
|
||||
When using relative time offsets or start-at timestamps (see ADVANCED TIMESTAMP FORMAT above and option \fB\-f\fR), the clock speed can be adjusted, i.e., time may run faster or slower for the executed program.
|
||||
For example, \fB"+5y x10"\fR will set the faked time 5 years into the future and make the time pass 10 times as fast (one real second equals 10 seconds measured by the program).
|
||||
Similarly, the flow of time can be slowed, e.g., using \fB"-7d x0,2"\fR, which will set the faked time 7 days in the past and set the clock speed to 20 percent, i.e., it takes five real world seconds for one second measured by the program.
|
||||
Again, depending on your locale, either "x2.0" or "x2,0" may be required regarding the delimiter.
|
||||
You can also make faketime to advance the reported time by a preset interval upon each time() call independently from the system's time using \fB"-7d i2,0"\fR, where \fB"i"\fR is followed by the increase interval in seconds.
|
||||
.PP
|
||||
Faking times for multiple programs or even system-wide can be simplified by using ~/.faketimerc files and /etc/faketimerc. Please refer to the README that came with faketime for warnings and details.
|
||||
Faking times for multiple programs or even system-wide can be simplified by using ~/.faketimerc files and /etc/faketimerc.
|
||||
Please refer to the README that came with faketime for warnings and details.
|
||||
.PP
|
||||
Faking of filesystem timestamps may be disabled by setting the NO_FAKE_STAT environment variable to a non-empty value.
|
||||
.SH AUTHOR
|
||||
Please see the README and NEWS files for contributors.
|
||||
.SH BUGS
|
||||
Due to limitations of the LD_PRELOAD mechanism, faketime will not work with suidroot and statically linked programs.
|
||||
While timestamps and time offsets will work for child processes, speeding the clock up or slowing it down might not
|
||||
work for child processes spawned by the executed program as expected; a new instance of libfaketime is used for each
|
||||
child process, which means that the libfaketime start time, which is used in speed adjustments, will also be
|
||||
re-initialized. Some programs may dynamically load system libraries, such as librt, at run-time and therefore bypass libfaketime. You may report programs that do not work with libfaketime, but only if they are available as open source.
|
||||
While timestamps and time offsets will work for child processes, speeding the clock up or slowing it down might not work for child processes spawned by the executed program as expected;
|
||||
a new instance of libfaketime is used for each child process, which means that the libfaketime start time, which is used in speed adjustments, will also be re-initialized.
|
||||
Some programs may dynamically load system libraries, such as librt, at run-time and therefore bypass libfaketime.
|
||||
You may report programs that do not work with libfaketime, but only if they are available as open source.
|
||||
.SH "REPORTING BUGS"
|
||||
Please use https://github.com/wolfcw/libfaketime/issues
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2003-2013 by the libfaketime authors.
|
||||
Copyright \(co 2003-2021 by the libfaketime authors.
|
||||
.PP
|
||||
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You may redistribute copies of faketime under the
|
||||
terms of the GNU General Public License.
|
||||
There is NO warranty;
|
||||
not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
You may redistribute copies of faketime under the terms of the GNU General Public License.
|
||||
.br
|
||||
For more information about these matters, see the file named COPYING.
|
||||
.SH "SEE ALSO"
|
||||
|
||||
156
src/Makefile
156
src/Makefile
@@ -1,25 +1,10 @@
|
||||
#
|
||||
# Notes:
|
||||
#
|
||||
# * Compilation Defines:
|
||||
# * Compilation Defines that are set by default:
|
||||
#
|
||||
# FAKE_STAT
|
||||
# - Enables time faking also for files' timestamps.
|
||||
#
|
||||
# NO_ATFILE
|
||||
# - Disables support for the fstatat() group of functions
|
||||
#
|
||||
# PTHREAD_SINGLETHREADED_TIME
|
||||
# - Define this if you want to single-thread time() ... there ARE
|
||||
# possible caching side-effects in a multithreaded environment
|
||||
# without this, but the performance impact may require you to
|
||||
# try it unsynchronized.
|
||||
#
|
||||
# FAKE_INTERNAL_CALLS
|
||||
# - Also intercept libc internal __functions, e.g. not just time(),
|
||||
# but also __time(). Enhances compatibility with applications
|
||||
# that make use of low-level system calls, such as Java Virtual
|
||||
# Machines.
|
||||
# - Enables time faking when reading files' timestamps.
|
||||
#
|
||||
# FAKE_SLEEP
|
||||
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
|
||||
@@ -30,9 +15,69 @@
|
||||
# FAKE_PTHREAD
|
||||
# - Intercept pthread_cond_timedwait
|
||||
#
|
||||
# FAKE_INTERNAL_CALLS
|
||||
# - Also intercept libc internal __functions, e.g. not just time(),
|
||||
# but also __time(). Enhances compatibility with applications
|
||||
# that make use of low-level system calls, such as Java Virtual
|
||||
# Machines.
|
||||
#
|
||||
# PTHREAD_SINGLETHREADED_TIME (only set in libfaketimeMT.so)
|
||||
# - Define this if you want to single-thread time() ... there ARE
|
||||
# possible caching side-effects in a multithreaded environment
|
||||
# without this, but the performance impact may require you to
|
||||
# try it unsynchronized.
|
||||
#
|
||||
# * Compilation Defines that are unset by default:
|
||||
#
|
||||
# FAKE_FILE_TIMESTAMPS, FAKE_UTIME
|
||||
# - Enables time faking for the utime* functions. If enabled via
|
||||
# FAKE_FILE_TIMESTAMPS, the faking is opt-in at runtime using
|
||||
# with the FAKE_UTIME environment variable. If enabled via
|
||||
# FAKE_UTIME, the faking is opt-out at runtime.
|
||||
#
|
||||
# NO_ATFILE
|
||||
# - Disables support for the fstatat() group of functions
|
||||
#
|
||||
# FAKE_SETTIME
|
||||
# - Intercept clock_settime(), settimeofday(), and adjtime()
|
||||
#
|
||||
# FAKE_RANDOM
|
||||
# - Intercept getrandom()
|
||||
#
|
||||
# FAKE_PID
|
||||
# - Intercept getpid()
|
||||
#
|
||||
# INTERCEPT_SYSCALL
|
||||
# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls.
|
||||
# If enabled, this currently only works a few types of syscalls,
|
||||
# including FUTEX (see below), clock_gettime, etc.
|
||||
#
|
||||
# - note that on unusual architectures, if INTERCEPT_SYSCALL is set, you may
|
||||
# need to explicitly define variadic_promotion_t (e.g. by putting
|
||||
# -Dvariadic_promotion_t=int into CFLAGS). See src/faketime_common.h for
|
||||
# more info.
|
||||
#
|
||||
# INTERCEPT_FUTEX
|
||||
# - (On GNU/Linux only) intercept glibc's syscall() for relevant FUTEX syscalls.
|
||||
# If enabled, FUTEX syscalls will be intercepted and translated correspondingly.
|
||||
# - when FUTEX_WAIT_BITSET (with absolute deadline) is set, the deadline will
|
||||
# be adjusted based on the faketime.
|
||||
# - when FUTEX_WAKE (with relative deadline) is set, the deadline will be
|
||||
# adjusted based on the time rate alone.
|
||||
# - for other FUTEX operations, no adjustment is made for now.
|
||||
#
|
||||
#
|
||||
# FAKE_STATELESS
|
||||
# - Remove support for any functionality that requires sharing state across
|
||||
# threads of a process, or different processes. This decreases the risk of
|
||||
# interference with a program's normal execution, at the cost of supporting
|
||||
# fewer ways of specifying the time.
|
||||
# Concretely, this currently:
|
||||
# - disables PTHREAD_SINGLETHREADED_TIME, which can cause deadlocks in
|
||||
# multithreaded programs that fork due to making clock_gettime not
|
||||
# async-signal-safe
|
||||
# - disables all shared-memory across processes
|
||||
#
|
||||
# FORCE_MONOTONIC_FIX
|
||||
# - If the test program hangs forever on
|
||||
# " pthread_cond_timedwait: CLOCK_MONOTONIC test
|
||||
@@ -41,11 +86,20 @@
|
||||
# (This is a platform-specific issue we cannot handle at run-time.)
|
||||
#
|
||||
# MULTI_ARCH
|
||||
# - If MULTI_ARCH is set, the faketime wrapper program will put a literal
|
||||
# - If MULTI_ARCH is set, the faketime wrapper program will put a literal
|
||||
# $LIB into the LD_PRELOAD environment variable it creates, which makes
|
||||
# ld automatically choose the correct library version to use for the
|
||||
# target binary. Use for Linux platforms with Multi-Arch support only!
|
||||
#
|
||||
# SILENT
|
||||
# - avoid that the faketime wrapper complains when running within a
|
||||
# libfaketime environment
|
||||
#
|
||||
# FAIL_PRE_INIT_CALLS
|
||||
# - If the time is queried before the library was initialised, let the
|
||||
# call fail instead of trying to initialise on-the-fly. This fixes /
|
||||
# works around hangs that were seen with address sanitizer.
|
||||
#
|
||||
# * Compilation addition: second libMT target added for building the pthread-
|
||||
# enabled library as a separate library
|
||||
#
|
||||
@@ -72,20 +126,55 @@ PREFIX ?= /usr/local
|
||||
LIBDIRNAME ?= /lib/faketime
|
||||
PLATFORM ?=$(shell uname)
|
||||
|
||||
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"' $(FAKETIME_COMPILE_CFLAGS)
|
||||
ifneq ($(filter android%,$(subst -, ,$(CC))),)
|
||||
IS_ANDROID := true
|
||||
endif
|
||||
|
||||
ifeq ($(shell $(CC) -dM -E - < /dev/null | grep -c "__ANDROID__"),1)
|
||||
IS_ANDROID := true
|
||||
endif
|
||||
|
||||
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)
|
||||
COMPILER := clang
|
||||
else
|
||||
COMPILER := gcc
|
||||
endif
|
||||
export COMPILER
|
||||
|
||||
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_UTIME -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"' $(FAKETIME_COMPILE_CFLAGS)
|
||||
|
||||
ifeq ($(COMPILER),clang)
|
||||
CFLAGS += -Wno-tautological-pointer-compare
|
||||
endif
|
||||
|
||||
ifeq ($(COMPILER),gcc)
|
||||
CFLAGS += -Wno-nonnull-compare
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),SunOS)
|
||||
CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
|
||||
endif
|
||||
|
||||
LIB_LDFLAGS += -shared
|
||||
|
||||
LDFLAGS += $(FAKETIME_LINK_FLAGS) -lpthread
|
||||
ifneq ($(PLATFORM),SunOS)
|
||||
LDFLAGS += -Wl,--version-script=libfaketime.map
|
||||
ifdef IS_ANDROID
|
||||
CFLAGS += -D__ANDROID__
|
||||
endif
|
||||
|
||||
LDADD += -ldl -lm -lrt
|
||||
BIN_LDFLAGS += -lrt
|
||||
LIB_LDFLAGS += -shared
|
||||
|
||||
LDFLAGS += $(FAKETIME_LINK_FLAGS)
|
||||
ifneq ($(PLATFORM),SunOS)
|
||||
ifndef IS_ANDROID
|
||||
LDFLAGS += -Wl,--version-script=libfaketime.map
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef IS_ANDROID
|
||||
LDADD += -ldl -lm
|
||||
BIN_LDFLAGS +=
|
||||
else
|
||||
LDADD += -ldl -lm -lrt -lpthread
|
||||
BIN_LDFLAGS += -lrt -lpthread
|
||||
endif
|
||||
|
||||
SRC = libfaketime.c
|
||||
LIBS_OBJ = libfaketime.o libfaketimeMT.o
|
||||
@@ -98,17 +187,20 @@ all: ${LIBS} ${BINS}
|
||||
|
||||
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
|
||||
|
||||
${LIBS_OBJ}: libfaketime.c
|
||||
ft_sem.o: ft_sem.c ft_sem.h
|
||||
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} $<
|
||||
|
||||
${LIBS_OBJ}: libfaketime.c ft_sem.h
|
||||
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $<
|
||||
|
||||
%.so.${SONAME}: %.o libfaketime.map
|
||||
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD}
|
||||
%.so.${SONAME}: %.o ft_sem.o libfaketime.map
|
||||
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ft_sem.o ${LDADD}
|
||||
|
||||
${BINS}: faketime.c
|
||||
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS}
|
||||
${BINS}: faketime.c ft_sem.o ft_sem.h
|
||||
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ft_sem.o ${LDFLAGS} ${BIN_LDFLAGS}
|
||||
|
||||
clean:
|
||||
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS}
|
||||
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS} ft_sem.o
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
@@ -3,11 +3,31 @@
|
||||
#
|
||||
# * Compilation Defines:
|
||||
#
|
||||
# MACOS_DYLD_INTERPOSE
|
||||
# - Use dlyd interposing instead of name-based function interception
|
||||
# (required since macOS Monterey)
|
||||
#
|
||||
# FAKE_SLEEP
|
||||
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
|
||||
#
|
||||
# FAKE_SETTIME
|
||||
# - Intercept clock_settime(), settimeofday(), and adjtime()
|
||||
#
|
||||
# FAKE_PID
|
||||
# - Enable faked values for getpid() calls through FAKETIME_FAKEPID
|
||||
#
|
||||
# FAKE_RANDOM
|
||||
# - Intercept getentropy(). Dangerous for production use.
|
||||
# See README about FAKE_RANDOM.
|
||||
#
|
||||
# FAKE_STAT
|
||||
# - Enables time faking also for files' timestamps.
|
||||
#
|
||||
# NO_ATFILE
|
||||
# - Disables support for the fstatat() group of functions
|
||||
# FAKE_FILE_TIMESTAMPS, FAKE_UTIME
|
||||
# - Enables time faking for the utime* functions. If enabled via
|
||||
# FAKE_FILE_TIMESTAMPS, the faking is opt-in at runtime using
|
||||
# with the FAKE_UTIME environment variable. If enabled via
|
||||
# FAKE_UTIME, the faking is opt-out at runtime. Requires FAKE_STAT.
|
||||
#
|
||||
# PTHREAD
|
||||
# - Define this to enable multithreading support.
|
||||
@@ -18,9 +38,6 @@
|
||||
# without this, but the performance impact may require you to
|
||||
# try it unsynchronized.
|
||||
#
|
||||
# FAKE_SLEEP
|
||||
# - Also intercept sleep(), nanosleep(), usleep(), alarm(), [p]poll()
|
||||
#
|
||||
# * Compilation addition: second libMT target added for building the pthread-
|
||||
# enabled library as a separate library
|
||||
#
|
||||
@@ -38,8 +55,21 @@ INSTALL ?= install
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
CFLAGS += -DFAKE_SLEEP -DFAKE_INTERNAL_CALLS -DPREFIX='"'${PREFIX}'"' $(FAKETIME_COMPILE_CFLAGS)
|
||||
LIB_LDFLAGS += -dynamiclib -current_version 0.9.8 -compatibility_version 0.7
|
||||
CFLAGS += -DFAKE_SLEEP -DFAKE_INTERNAL_CALLS -DPREFIX='"'${PREFIX}'"' $(FAKETIME_COMPILE_CFLAGS) -DMACOS_DYLD_INTERPOSE -DFAKE_SETTIME
|
||||
LIB_LDFLAGS += -dynamiclib -current_version 0.9.12 -compatibility_version 0.7
|
||||
|
||||
# From macOS 13 onwards, system binaries are compiled against the new arm64e ABI on Apple Silicon.
|
||||
# These arm64e binaries enforce Pointer Authentication Code (PAC), and will refuse to run with
|
||||
# "unprotected" arm64 libraries. Meanwhile, older platforms might not recognize the new arm64e ABI.
|
||||
|
||||
# Therefore, we now compile for two ABIs at the same time, producing a fat library of arm64e and arm64,
|
||||
# so in the end the OS gets to pick which architecture it wants at runtime.
|
||||
|
||||
# In addition, we need to enable signing and authentication of indirect calls (-fptrauth-calls);
|
||||
# otherwise in ftpl_init, pthread_once will indirectly call ftpl_really_init, which then fail PAC.
|
||||
# Ideally this should be a compiler default for the arm64e ABI, but apparently not.
|
||||
|
||||
ARCH := $(shell uname -m)
|
||||
|
||||
SONAME = 1
|
||||
LIBS = libfaketime.${SONAME}.dylib
|
||||
@@ -47,14 +77,26 @@ BINS = faketime
|
||||
|
||||
all: ${LIBS} ${BINS}
|
||||
|
||||
ifeq ($(ARCH),arm64)
|
||||
libfaketime.${SONAME}.dylib: libfaketime.c
|
||||
${CC} -o libfaketime.arm64e.dylib ${CFLAGS} -arch arm64e -fptrauth-calls -fptrauth-returns ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
|
||||
${CC} -o libfaketime.arm64.dylib ${CFLAGS} -arch arm64 ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
|
||||
lipo -create -output $@ libfaketime.arm64e.dylib libfaketime.arm64.dylib
|
||||
rm libfaketime.arm64e.dylib libfaketime.arm64.dylib
|
||||
|
||||
faketime: faketime.c
|
||||
${CC} -o $@ ${CFLAGS} -arch arm64e -arch arm64 ${LDFLAGS} $<
|
||||
|
||||
else
|
||||
libfaketime.${SONAME}.dylib: libfaketime.c
|
||||
${CC} -o $@ ${CFLAGS} ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
|
||||
|
||||
faketime: faketime.c
|
||||
${CC} -o $@ ${CFLAGS} ${LDFLAGS} $<
|
||||
endif
|
||||
|
||||
clean:
|
||||
@rm -f ${OBJ} ${LIBS} ${BINS}
|
||||
@rm -f ${OBJ} ${LIBS} ${BINS} libfaketime.arm64e.dylib libfaketime.arm64.dylib
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
51
src/android_compat.h
Normal file
51
src/android_compat.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef ANDROID_COMPAT_H
|
||||
#define ANDROID_COMPAT_H
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static inline const char *__android_shm_tmpdir(void)
|
||||
{
|
||||
const char *dir = getenv("FAKETIME_SHM_DIR");
|
||||
if (dir == NULL)
|
||||
dir = getenv("TMPDIR");
|
||||
if (dir == NULL)
|
||||
dir = "/data/local/tmp";
|
||||
return dir;
|
||||
}
|
||||
|
||||
static inline int __android_shm_open(const char *name, int oflag, mode_t mode)
|
||||
{
|
||||
char path[512];
|
||||
const char *dir = __android_shm_tmpdir();
|
||||
while (*name == '/') name++;
|
||||
snprintf(path, sizeof(path), "%s/faketime_shm_%s", dir, name);
|
||||
int fd = open(path, oflag, mode);
|
||||
if (fd == -1 && (oflag & O_CREAT))
|
||||
{
|
||||
mkdir(dir, 0777);
|
||||
fd = open(path, oflag, mode);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static inline int __android_shm_unlink(const char *name)
|
||||
{
|
||||
char path[512];
|
||||
const char *dir = __android_shm_tmpdir();
|
||||
while (*name == '/') name++;
|
||||
snprintf(path, sizeof(path), "%s/faketime_shm_%s", dir, name);
|
||||
return unlink(path);
|
||||
}
|
||||
|
||||
#define shm_open __android_shm_open
|
||||
#define shm_unlink __android_shm_unlink
|
||||
|
||||
#endif /* __ANDROID__ */
|
||||
#endif /* ANDROID_COMPAT_H */
|
||||
133
src/faketime.c
133
src/faketime.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* libfaketime wrapper command
|
||||
*
|
||||
* This file is part of libfaketime, version 0.9.8
|
||||
* This file is part of libfaketime, version 0.9.12
|
||||
*
|
||||
* libfaketime is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
@@ -44,13 +44,14 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <semaphore.h>
|
||||
#include "ft_sem.h"
|
||||
|
||||
#include "android_compat.h"
|
||||
#include "faketime_common.h"
|
||||
|
||||
const char version[] = "0.9.7";
|
||||
const char version[] = "0.9.12";
|
||||
|
||||
#ifdef __APPLE__
|
||||
#if (defined __APPLE__) || (defined __sun)
|
||||
static const char *date_cmd = "gdate";
|
||||
#else
|
||||
static const char *date_cmd = "date";
|
||||
@@ -60,6 +61,7 @@ static const char *date_cmd = "date";
|
||||
|
||||
/* semaphore and shared memory names */
|
||||
char sem_name[PATH_BUFSIZE] = {0}, shm_name[PATH_BUFSIZE] = {0};
|
||||
static ft_sem_t wrapper_sem;
|
||||
|
||||
void usage(const char *name)
|
||||
{
|
||||
@@ -75,13 +77,20 @@ void usage(const char *name)
|
||||
" -m : Use the multi-threaded version of libfaketime\n"
|
||||
" -f : Use the advanced timestamp specification format (see manpage)\n"
|
||||
" --exclude-monotonic : Prevent monotonic clock from drifting (not the raw monotonic one)\n"
|
||||
#ifdef FAKE_PID
|
||||
" -p PID : Pretend that the program's process ID is PID\n"
|
||||
#endif
|
||||
#ifndef FAKE_STATELESS
|
||||
" --disable-shm : Disable use of shared memory by libfaketime.\n"
|
||||
#endif
|
||||
" --date-prog PROG : Use specified GNU-compatible implementation of 'date' program\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
"%s 'last friday 5 pm' /bin/date\n"
|
||||
"%s '2008-12-24 08:15:42' /bin/date\n"
|
||||
"%s -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y i2,0' /bin/bash -c 'date; while true; do date; sleep 1 ; done'\n"
|
||||
"%s 'last friday 5 pm' date\n"
|
||||
"%s '2008-12-24 08:15:42' date\n"
|
||||
"%s -f '+2,5y x10,0' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y x0,50' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
|
||||
"%s -f '+2,5y i2,0' bash -c 'date; while true; do date; sleep 1 ; done'\n"
|
||||
"In this single case all spawned processes will use the same global clock\n"
|
||||
"without restarting it at the start of each process.\n\n"
|
||||
"(Please note that it depends on your locale settings whether . or , has to be used for fractions)\n"
|
||||
@@ -91,13 +100,13 @@ void usage(const char *name)
|
||||
/** Clean up shared objects */
|
||||
static void cleanup_shobjs()
|
||||
{
|
||||
if (-1 == sem_unlink(sem_name))
|
||||
if (-1 == ft_sem_unlink(&wrapper_sem))
|
||||
{
|
||||
perror("sem_unlink");
|
||||
perror("faketime: ft_sem_unlink");
|
||||
}
|
||||
if (-1 == shm_unlink(shm_name))
|
||||
{
|
||||
perror("shm_unlink");
|
||||
perror("faketime: shm_unlink");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +116,16 @@ int main (int argc, char **argv)
|
||||
int curr_opt = 1;
|
||||
bool use_mt = false, use_direct = false;
|
||||
long offset;
|
||||
bool fake_pid = false;
|
||||
const char *pid_val;
|
||||
|
||||
while(curr_opt < argc)
|
||||
#ifndef SILENT
|
||||
if (getenv("FAKETIME") || getenv("FAKETIME_SHARED") || getenv("FAKETIME_FAKEPID") || getenv("FAKERANDOM_SEED")) {
|
||||
fprintf(stderr, "faketime: You appear to be running faketime within a libfaketime environment. Proceeding, but check for unexpected results...\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
while (curr_opt < argc)
|
||||
{
|
||||
if (0 == strcmp(argv[curr_opt], "-m"))
|
||||
{
|
||||
@@ -116,6 +133,16 @@ int main (int argc, char **argv)
|
||||
curr_opt++;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp(argv[curr_opt], "-p"))
|
||||
{
|
||||
fake_pid = true;
|
||||
pid_val = argv[curr_opt + 1];
|
||||
curr_opt += 2;
|
||||
#ifndef FAKE_PID
|
||||
fprintf(stderr, "faketime: -p argument probably won't work (try rebuilding with -DFAKE_PID)\n");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
else if (0 == strcmp(argv[curr_opt], "-f"))
|
||||
{
|
||||
use_direct = true;
|
||||
@@ -128,6 +155,28 @@ int main (int argc, char **argv)
|
||||
curr_opt++;
|
||||
continue;
|
||||
}
|
||||
#ifndef FAKE_STATELESS
|
||||
else if (0 == strcmp(argv[curr_opt], "--disable-shm"))
|
||||
{
|
||||
setenv("FAKETIME_DISABLE_SHM", "1", true);
|
||||
curr_opt++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
else if (0 == strcmp(argv[curr_opt], "--date-prog"))
|
||||
{
|
||||
curr_opt++;
|
||||
if (curr_opt > argc) {
|
||||
// At best this avoids a segfault reading beyond the argv[]
|
||||
// Realistically there would be other args (e.g. program to call)
|
||||
fprintf(stderr, "faketime: --date-prog requires a further argument\n");
|
||||
} else {
|
||||
date_cmd = argv[curr_opt];
|
||||
curr_opt++;
|
||||
//fprintf(stderr, "faketime: --date-prog assigned: %s\n", date_cmd);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if ((0 == strcmp(argv[curr_opt], "-v")) ||
|
||||
(0 == strcmp(argv[curr_opt], "--version")))
|
||||
{
|
||||
@@ -169,9 +218,10 @@ int main (int argc, char **argv)
|
||||
close(1); /* close normal stdout */
|
||||
(void) (dup(pfds[1]) + 1); /* make stdout same as pfds[1] */
|
||||
close(pfds[0]); /* we don't need this */
|
||||
// fprintf(stderr, "faketime: using --date-prog: %s\n", date_cmd);
|
||||
if (EXIT_SUCCESS != execlp(date_cmd, date_cmd, "-d", argv[curr_opt], "+%s",(char *) NULL))
|
||||
{
|
||||
perror("Running (g)date failed");
|
||||
perror("faketime: Running (g)date failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@@ -198,6 +248,8 @@ int main (int argc, char **argv)
|
||||
/* simply pass format string along */
|
||||
setenv("FAKETIME", argv[curr_opt], true);
|
||||
}
|
||||
if (fake_pid)
|
||||
setenv("FAKETIME_FAKEPID", pid_val, true);
|
||||
int keepalive_fds[2];
|
||||
(void) (pipe(keepalive_fds) + 1);
|
||||
|
||||
@@ -205,9 +257,8 @@ int main (int argc, char **argv)
|
||||
curr_opt++;
|
||||
|
||||
{
|
||||
/* create semaphores and shared memory */
|
||||
/* create lock and shared memory */
|
||||
int shm_fd;
|
||||
sem_t *sem;
|
||||
struct ft_shared_s *ft_shared;
|
||||
char shared_objs[PATH_BUFSIZE * 2 + 1];
|
||||
|
||||
@@ -221,28 +272,25 @@ int main (int argc, char **argv)
|
||||
snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%ld", (long)getpid());
|
||||
snprintf(shm_name, PATH_BUFSIZE -1 ,"/faketime_shm_%ld", (long)getpid());
|
||||
|
||||
if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
|
||||
if (-1 == ft_sem_create(sem_name, &wrapper_sem))
|
||||
{
|
||||
perror("sem_open");
|
||||
fprintf(stderr, "The faketime wrapper only works on platforms that support the sem_open()\nsystem call. However, you may LD_PRELOAD libfaketime without using this wrapper.\n");
|
||||
perror("faketime: ft_sem_create");
|
||||
fprintf(stderr, "The faketime wrapper failed to create its lock.\nHowever, you may LD_PRELOAD libfaketime without using this wrapper.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* create shm */
|
||||
if (-1 == (shm_fd = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR)))
|
||||
{
|
||||
perror("shm_open");
|
||||
if (-1 == sem_unlink(argv[2]))
|
||||
{
|
||||
perror("sem_unlink");
|
||||
}
|
||||
perror("faketime: shm_open");
|
||||
ft_sem_unlink(&wrapper_sem);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* set shm size */
|
||||
if (-1 == ftruncate(shm_fd, sizeof(uint64_t)))
|
||||
if (-1 == ftruncate(shm_fd, sizeof(struct ft_shared_s)))
|
||||
{
|
||||
perror("ftruncate");
|
||||
perror("faketime: ftruncate");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -251,14 +299,14 @@ int main (int argc, char **argv)
|
||||
if (MAP_FAILED == (ft_shared = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, shm_fd, 0)))
|
||||
{
|
||||
perror("mmap");
|
||||
perror("faketime: mmap");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sem_wait(sem) == -1)
|
||||
if (ft_sem_lock(&wrapper_sem) == -1)
|
||||
{
|
||||
perror("sem_wait");
|
||||
perror("faketime: ft_sem_lock");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -266,30 +314,34 @@ int main (int argc, char **argv)
|
||||
/* init elapsed time ticks to zero */
|
||||
ft_shared->ticks = 0;
|
||||
ft_shared->file_idx = 0;
|
||||
ft_shared->start_time.real.tv_sec = 0;
|
||||
ft_shared->start_time.real.tv_nsec = -1;
|
||||
ft_shared->start_time.mon.tv_sec = 0;
|
||||
ft_shared->start_time.mon.tv_nsec = -1;
|
||||
ft_shared->start_time.mon_raw.tv_sec = 0;
|
||||
ft_shared->start_time.mon_raw.tv_nsec = -1;
|
||||
ft_shared->start_time_real.sec = 0;
|
||||
ft_shared->start_time_real.nsec = -1;
|
||||
ft_shared->start_time_mon.sec = 0;
|
||||
ft_shared->start_time_mon.nsec = -1;
|
||||
ft_shared->start_time_mon_raw.sec = 0;
|
||||
ft_shared->start_time_mon_raw.nsec = -1;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
ft_shared->start_time_boot.sec = 0;
|
||||
ft_shared->start_time_boot.nsec = -1;
|
||||
#endif
|
||||
|
||||
if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s))))
|
||||
{
|
||||
perror("munmap");
|
||||
perror("faketime: munmap");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (sem_post(sem) == -1)
|
||||
if (ft_sem_unlock(&wrapper_sem) == -1)
|
||||
{
|
||||
perror("semop");
|
||||
perror("faketime: ft_sem_unlock");
|
||||
cleanup_shobjs();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name);
|
||||
setenv("FAKETIME_SHARED", shared_objs, true);
|
||||
sem_close(sem);
|
||||
ft_sem_close(&wrapper_sem);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -345,9 +397,10 @@ int main (int argc, char **argv)
|
||||
if (0 == (child_pid = fork()))
|
||||
{
|
||||
close(keepalive_fds[0]); /* only parent needs to read this */
|
||||
// fprintf(stderr, "faketime: Executing: %s\n", argv[curr_opt]);
|
||||
if (EXIT_SUCCESS != execvp(argv[curr_opt], &argv[curr_opt]))
|
||||
{
|
||||
perror("Running specified command failed");
|
||||
perror("faketime: Running specified command failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ struct system_time_s
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Fixed-width time representation for shared memory (arch-independent) */
|
||||
struct ft_shared_time_s
|
||||
{
|
||||
int64_t sec;
|
||||
int64_t nsec;
|
||||
};
|
||||
|
||||
/* Data shared among faketime-spawned processes */
|
||||
struct ft_shared_s
|
||||
{
|
||||
@@ -45,10 +52,15 @@ 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 */
|
||||
/* Index of timestamp to be loaded from file */
|
||||
uint64_t file_idx;
|
||||
/* System time Faketime started at */
|
||||
struct system_time_s start_time;
|
||||
/* System time Faketime started at (fixed-width for cross-arch safety) */
|
||||
struct ft_shared_time_s start_time_real;
|
||||
struct ft_shared_time_s start_time_mon;
|
||||
struct ft_shared_time_s start_time_mon_raw;
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
struct ft_shared_time_s start_time_boot;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* These are all needed in order to properly build on OSX */
|
||||
@@ -58,4 +70,43 @@ struct ft_shared_s
|
||||
#include <mach/mach_port.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
Variadic Argument Re-packing
|
||||
|
||||
Functions with variadic arguments typically have most arguments
|
||||
passed on the stack, but it varies across ABIs.
|
||||
|
||||
C specifies that variadic arguments that are smaller than some
|
||||
standard promotion size are promoted to "int or larger". If your
|
||||
platform's ABI only promotes to "int" and not "long" (and "int" and
|
||||
"long" differ on your platform), you should probably add
|
||||
-Dvariadic_promotion_t=int to CFLAGS.
|
||||
|
||||
Note that some ABIs do not put all the variadic arguments on the
|
||||
stack. For example, x86-64 puts float and double variadic
|
||||
arguments into floating point registers, according to
|
||||
https://www.uclibc.org/docs/psABI-x86_64.pdf
|
||||
|
||||
The only variadic function faketime cares about intercepting is
|
||||
syscall. But we don't believe that any syscalls expect float or
|
||||
double arguments, so we hope all the rest will be on the stack.
|
||||
tests/variadic/ attempts to confirm this if you are compiling
|
||||
with -DINTERCEPT_SYSCALL.
|
||||
|
||||
If libc were capable of exposing a variadic form of syscall, we
|
||||
could depend on that and drop this approach, which would be
|
||||
preferable: https://sourceware.org/bugzilla/show_bug.cgi?id=27508
|
||||
*/
|
||||
#ifndef variadic_promotion_t
|
||||
#define variadic_promotion_t long
|
||||
#endif
|
||||
|
||||
/*
|
||||
The Linux kernel appears to have baked-in 6 as the maximum number
|
||||
of arguments for a syscall beyond the syscall number itself.
|
||||
*/
|
||||
#ifndef syscall_max_args
|
||||
#define syscall_max_args 6
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
227
src/ft_sem.c
Normal file
227
src/ft_sem.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* This file is part of libfaketime, version 0.9.12
|
||||
*
|
||||
* libfaketime is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License v2 along
|
||||
* with the libfaketime; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "ft_sem.h"
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
#include <sys/sem.h>
|
||||
#endif
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
#include <sys/file.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* =======================================================================
|
||||
* Semaphore related functions === SEM
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
int ft_sem_name2key(char *name)
|
||||
{
|
||||
key_t key;
|
||||
char fullname[256];
|
||||
snprintf(fullname, sizeof(fullname), "/tmp%s", name);
|
||||
fullname[sizeof(fullname) - 1] = '\0';
|
||||
int fd = open(fullname, O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror("libfaketime: open");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
if (-1 == (key = ftok(fullname, 'F')))
|
||||
{
|
||||
perror("libfaketime: ftok");
|
||||
return -1;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
static int ft_sem_name_to_path(const char *name, char *path, size_t pathlen)
|
||||
{
|
||||
const char *prefix = "/faketime_sem_";
|
||||
const char *p = strstr(name, prefix);
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
const char *pid_str = p + strlen(prefix);
|
||||
const char *tmpdir;
|
||||
#ifdef __ANDROID__
|
||||
tmpdir = getenv("TMPDIR");
|
||||
if (tmpdir == NULL) tmpdir = "/data/local/tmp";
|
||||
#else
|
||||
tmpdir = "/dev/shm";
|
||||
#endif
|
||||
snprintf(path, pathlen, "%s/faketime_lock_%s", tmpdir, pid_str);
|
||||
path[pathlen - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ft_sem_create(char *name, ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
if (SEM_FAILED == (ft_sem->sem = sem_open(name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
key_t key = ft_sem_name2key(name);
|
||||
int nsems = 1;
|
||||
|
||||
ft_sem->semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | S_IWUSR | S_IRUSR);
|
||||
if (ft_sem->semid >= 0) { /* we got here first */
|
||||
struct sembuf sb = {
|
||||
.sem_num = 0,
|
||||
.sem_op = 1, /* number of resources the semaphore has (when decremented down to 0 the semaphore will block) */
|
||||
.sem_flg = 0,
|
||||
};
|
||||
/* the number of semaphore operation structures (struct sembuf) passed to semop */
|
||||
int nsops = 1;
|
||||
|
||||
/* do a semop() to "unlock" the semaphore */
|
||||
if (-1 == semop(ft_sem->semid, &sb, nsops)) {
|
||||
return -1;
|
||||
}
|
||||
} else if (errno == EEXIST) { /* someone else got here before us */
|
||||
return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
char path[256];
|
||||
if (ft_sem_name_to_path(name, path, sizeof(path)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ft_sem->fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR);
|
||||
if (ft_sem->fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
strncpy(ft_sem->name, name, sizeof ft_sem->name - 1);
|
||||
ft_sem->name[sizeof ft_sem->name - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_sem_open(char *name, ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
if (SEM_FAILED == (ft_sem->sem = sem_open(name, 0)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
key_t key = ft_sem_name2key(name);
|
||||
ft_sem->semid = semget(key, 1, S_IWUSR | S_IRUSR);
|
||||
if (ft_sem->semid < 0) {
|
||||
return -1;
|
||||
}
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
char path[256];
|
||||
if (ft_sem_name_to_path(name, path, sizeof(path)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ft_sem->fd = open(path, O_RDWR);
|
||||
if (ft_sem->fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
strncpy(ft_sem->name, name, sizeof ft_sem->name - 1);
|
||||
ft_sem->name[sizeof ft_sem->name - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_sem_lock(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_wait(ft_sem->sem);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
struct sembuf sb = {
|
||||
.sem_num = 0,
|
||||
.sem_op = -1, /* allocate resource (lock) */
|
||||
.sem_flg = 0,
|
||||
};
|
||||
return semop(ft_sem->semid, &sb, 1);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
return flock(ft_sem->fd, LOCK_EX);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ft_sem_unlock(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_post(ft_sem->sem);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
struct sembuf sb = {
|
||||
.sem_num = 0,
|
||||
.sem_op = 1, /* free resource (unlock) */
|
||||
.sem_flg = 0,
|
||||
};
|
||||
return semop(ft_sem->semid, &sb, 1);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
return flock(ft_sem->fd, LOCK_UN);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ft_sem_close(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_close(ft_sem->sem);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
/* NOP -- there's no "close" equivalent */
|
||||
(void)ft_sem;
|
||||
return 0;
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
int ret = close(ft_sem->fd);
|
||||
ft_sem->fd = -1;
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ft_sem_unlink(ft_sem_t *ft_sem)
|
||||
{
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
return sem_unlink(ft_sem->name);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
return semctl(ft_sem->semid, 0, IPC_RMID);
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
char path[256];
|
||||
if (ft_sem_name_to_path(ft_sem->name, path, sizeof(path)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return unlink(path);
|
||||
#endif
|
||||
}
|
||||
62
src/ft_sem.h
Normal file
62
src/ft_sem.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of libfaketime, version 0.9.12
|
||||
*
|
||||
* libfaketime is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License v2 along
|
||||
* with the libfaketime; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef FT_SEM_H
|
||||
#define FT_SEM_H
|
||||
|
||||
/* semaphore backend options */
|
||||
#define FT_POSIX 1
|
||||
#define FT_SYSV 2
|
||||
#define FT_FLOCK 3
|
||||
|
||||
/* set default backend */
|
||||
#ifndef FT_SEMAPHORE_BACKEND
|
||||
#define FT_SEMAPHORE_BACKEND FT_FLOCK
|
||||
#endif
|
||||
|
||||
/* validate selected backend */
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
#else
|
||||
#error "Unknown FT_SEMAPHORE_BACKEND; select between FT_POSIX, FT_SYSV, and FT_FLOCK"
|
||||
#endif
|
||||
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[256];
|
||||
#if FT_SEMAPHORE_BACKEND == FT_POSIX
|
||||
sem_t *sem;
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
|
||||
int semid;
|
||||
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
|
||||
int fd;
|
||||
#endif
|
||||
} ft_sem_t;
|
||||
|
||||
int ft_sem_create(char *name, ft_sem_t *ft_sem);
|
||||
int ft_sem_open(char *name, ft_sem_t *ft_sem);
|
||||
int ft_sem_lock(ft_sem_t *ft_sem);
|
||||
int ft_sem_unlock(ft_sem_t *ft_sem);
|
||||
int ft_sem_close(ft_sem_t *ft_sem);
|
||||
int ft_sem_unlink(ft_sem_t *ft_sem);
|
||||
|
||||
#endif /* FT_SEM_H */
|
||||
2585
src/libfaketime.c
2585
src/libfaketime.c
File diff suppressed because it is too large
Load Diff
@@ -9,4 +9,4 @@
|
||||
#define htole64(x) LE_64(x)
|
||||
#define le64toh(x) LE_64(x)
|
||||
|
||||
#endif /* SUN_OS_ENDIAN_H */
|
||||
#endif /* SUN_OS_ENDIAN_H */
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
do \
|
||||
{ \
|
||||
int64_t tmp_time; \
|
||||
tmp_time = (c) * (int64_t) ((tvp)->tv_sec * SEC_TO_##prefix##SEC + \
|
||||
tmp_time = (c) * (int64_t) ((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) / \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
## Copyright (c) 2013, adrelanos at riseup dot net
|
||||
## All rights reserved.
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
CC = gcc
|
||||
|
||||
CFLAGS = -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS)
|
||||
LDFLAGS = -lrt -lpthread
|
||||
CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS) -U_FILE_OFFSET_BITS -U_TIME_BITS
|
||||
LDFLAGS += -lrt -lpthread
|
||||
|
||||
SRC = timetest.c
|
||||
OBJ = ${SRC:.c=.o}
|
||||
|
||||
all: timetest test
|
||||
TEST_SNIPPETS = $(notdir $(basename $(wildcard snippets/*.c)))
|
||||
EXPECTATIONS = $(notdir $(basename $(wildcard snippets/*.variable)))
|
||||
|
||||
ALL_TESTS = timetest test shm_layout_test
|
||||
|
||||
ifneq ($(filter -DINTERCEPT_SYSCALL,${CFLAGS}),)
|
||||
ALL_TESTS += confirm_variadic_promotion
|
||||
else
|
||||
TEST_SNIPPETS := $(filter-out syscall%,${TEST_SNIPPETS})
|
||||
EXPECTATIONS := $(filter-out syscall%,${EXPECTATIONS})
|
||||
endif
|
||||
|
||||
all: $(ALL_TESTS)
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $<
|
||||
@@ -14,7 +26,10 @@ all: timetest test
|
||||
timetest: ${OBJ}
|
||||
${CC} -o $@ ${OBJ} ${LDFLAGS}
|
||||
|
||||
test: timetest functest
|
||||
shm_layout_test: shm_layout_test.c ../src/faketime_common.h
|
||||
${CC} -o $@ ${CFLAGS} $<
|
||||
|
||||
test: timetest functest libmallocintercept.so
|
||||
@echo
|
||||
@./test.sh
|
||||
|
||||
@@ -22,10 +37,51 @@ test: timetest functest
|
||||
functest:
|
||||
./testframe.sh functests
|
||||
|
||||
%_test: %_test.c
|
||||
${CC} -o $@ ${CFLAGS} $<
|
||||
|
||||
randomtest: repeat_random
|
||||
./randomtest.sh
|
||||
|
||||
libmallocintercept.so: libmallocintercept.c
|
||||
${CC} -shared -o $@ -fpic ${CFLAGS} $<
|
||||
|
||||
# ensure our variadic argument unpacking/repacking works as expected
|
||||
confirm_variadic_promotion: variadic_promotion
|
||||
./variadic_promotion
|
||||
variadic_promotion: variadic/main.o variadic/outer.o variadic/inner.o
|
||||
${CC} -o $@ ${CFLAGS} $^
|
||||
variadic/%.o: variadic/%.c
|
||||
${CC} -c -o $@ ${CFLAGS} $<
|
||||
|
||||
# run snippet tests
|
||||
snippets: test_variable_data test_library_constructors
|
||||
|
||||
## test snippet behavior across env var setting over time:
|
||||
test_variable_data: test_variable_data.sh $(foreach f,${EXPECTATIONS},run_${f})
|
||||
./test_variable_data.sh ${EXPECTATIONS}
|
||||
|
||||
run_%: _run_test.c snippets/%.c
|
||||
sed s/SNIPPET_NAME/$*/g < _run_test.c | ${CC} -o $@ ${CFLAGS} -x c -
|
||||
|
||||
## test snippets in other library constructors:
|
||||
test_library_constructors: $(foreach f,${TEST_SNIPPETS},test_lib_${f})
|
||||
test_lib_%: test_constructors.sh use_lib_% lib%.so
|
||||
./test_constructors.sh $*
|
||||
|
||||
lib%.so: _libtest.c snippets/%.c
|
||||
sed s/SNIPPET_NAME/$*/g < _libtest.c | ${CC} -shared -o $@ -fpic ${CFLAGS} -x c -
|
||||
|
||||
use_lib_%: _use_lib_test.c snippets/%.c lib%.so
|
||||
sed s/SNIPPET_NAME/$*/g < _use_lib_test.c | ${CC} -L. -o $@ ${CFLAGS} -x c - -l$*
|
||||
|
||||
|
||||
## cleanup and metainformation
|
||||
|
||||
clean:
|
||||
@rm -f ${OBJ} timetest
|
||||
@rm -f ${OBJ} timetest shm_layout_test getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so
|
||||
|
||||
distclean: clean
|
||||
@echo
|
||||
|
||||
.PHONY: all test clean distclean
|
||||
.PHONY: all test clean distclean randomtest snippets test_variable_data test_library_constructors
|
||||
|
||||
8
test/_libtest.c
Normal file
8
test/_libtest.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "snippets/include_headers.h"
|
||||
#define where "libSNIPPET_NAME"
|
||||
void SNIPPET_NAME_as_needed() {
|
||||
printf(" called SNIPPET_NAME_as_needed() \n");
|
||||
}
|
||||
static __attribute__((constructor)) void init_SNIPPET_NAME() {
|
||||
#include "snippets/SNIPPET_NAME.c"
|
||||
}
|
||||
5
test/_run_test.c
Normal file
5
test/_run_test.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "snippets/include_headers.h"
|
||||
#define where "run_SNIPPET_NAME"
|
||||
int main() {
|
||||
#include "snippets/SNIPPET_NAME.c"
|
||||
}
|
||||
7
test/_use_lib_test.c
Normal file
7
test/_use_lib_test.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "snippets/include_headers.h"
|
||||
extern void SNIPPET_NAME_as_needed();
|
||||
#define where "use_lib_SNIPPET_NAME"
|
||||
int main() {
|
||||
SNIPPET_NAME_as_needed();
|
||||
#include "snippets/SNIPPET_NAME.c"
|
||||
}
|
||||
@@ -39,7 +39,8 @@ sunos_fakecmd()
|
||||
linuxlike_fakecmd()
|
||||
{
|
||||
typeset timestring="$1"; shift
|
||||
typeset fakelib=../src/libfaketime.so.1
|
||||
FTPL="${FAKETIME_TESTLIB:-../src/libfaketime.so.1}"
|
||||
typeset fakelib="$FTPL"
|
||||
export LD_PRELOAD=$fakelib
|
||||
FAKETIME="$timestring" \
|
||||
"$@"
|
||||
|
||||
@@ -66,7 +66,7 @@ get_monotonic_time()
|
||||
clock_id=$1; shift;
|
||||
FAKETIME_DONT_FAKE_MONOTONIC=${dont_fake_mono} \
|
||||
fakecmd "2014-07-21 09:00:00" \
|
||||
/bin/bash -c "for i in 1 2; do \
|
||||
bash -c "for i in 1 2; do \
|
||||
perl -w -MTime::HiRes=clock_gettime,${clock_id} -E \
|
||||
'say clock_gettime(${clock_id})'; \
|
||||
sleep 1; \
|
||||
|
||||
79
test/functests/test_follow_absolute.sh
Normal file
79
test/functests/test_follow_absolute.sh
Normal file
@@ -0,0 +1,79 @@
|
||||
# Tests for FAKETIME_FOLLOW_ABSOLUTE feature.
|
||||
#
|
||||
# When FAKETIME_FOLLOW_ABSOLUTE=1 is set alongside FAKETIME="%" and
|
||||
# FAKETIME_FOLLOW_FILE, time freezes at the follow file's mtime
|
||||
# and only advances when the file's mtime changes.
|
||||
|
||||
FOLLOW_FILE=".follow_absolute_test_file"
|
||||
|
||||
init()
|
||||
{
|
||||
typeset testsuite="$1"
|
||||
PLATFORM=$(platform)
|
||||
if [ -z "$PLATFORM" ]; then
|
||||
echo "$testsuite: unknown platform! quitting"
|
||||
return 1
|
||||
fi
|
||||
echo "# PLATFORM=$PLATFORM"
|
||||
return 0
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
init
|
||||
|
||||
run_testcase follow_absolute_basic
|
||||
run_testcase follow_absolute_freeze
|
||||
run_testcase follow_absolute_tracks_mtime
|
||||
|
||||
rm -f "$FOLLOW_FILE"
|
||||
}
|
||||
|
||||
# Helper to run a command with follow-absolute configuration
|
||||
follow_absolute_cmd()
|
||||
{
|
||||
FAKETIME_FOLLOW_FILE="$FOLLOW_FILE" \
|
||||
FAKETIME_FOLLOW_ABSOLUTE=1 \
|
||||
fakecmd "%" "$@"
|
||||
}
|
||||
|
||||
# Test that time matches the follow file's mtime
|
||||
follow_absolute_basic()
|
||||
{
|
||||
touch -d "2020-03-15 10:30:00 UTC" "$FOLLOW_FILE"
|
||||
typeset actual
|
||||
actual=$(follow_absolute_cmd date -u +"%Y-%m-%d %H:%M:%S")
|
||||
asserteq "$actual" "2020-03-15 10:30:00" \
|
||||
"time should match follow file mtime"
|
||||
}
|
||||
|
||||
# Test that time stays frozen (does not advance with real time)
|
||||
follow_absolute_freeze()
|
||||
{
|
||||
touch -d "2020-03-15 10:30:00 UTC" "$FOLLOW_FILE"
|
||||
typeset timestamps
|
||||
timestamps=$(follow_absolute_cmd \
|
||||
perl -e 'print time(), "\n"; sleep(2); print time(), "\n"')
|
||||
typeset first second
|
||||
first=$(echo "$timestamps" | head -1)
|
||||
second=$(echo "$timestamps" | tail -1)
|
||||
asserteq "$first" "$second" \
|
||||
"time should stay frozen within a single process"
|
||||
}
|
||||
|
||||
# Test that time tracks file mtime changes at millisecond precision
|
||||
follow_absolute_tracks_mtime()
|
||||
{
|
||||
touch -d "2020-03-15 10:30:00.000 UTC" "$FOLLOW_FILE"
|
||||
typeset first
|
||||
first=$(follow_absolute_cmd \
|
||||
perl -MTime::HiRes=time -e 'printf "%.3f\n", time()')
|
||||
|
||||
touch -d "2020-03-15 10:30:00.005 UTC" "$FOLLOW_FILE"
|
||||
typeset second
|
||||
second=$(follow_absolute_cmd \
|
||||
perl -MTime::HiRes=time -e 'printf "%.3f\n", time()')
|
||||
|
||||
assertneq "$first" "$second" \
|
||||
"time should advance with file mtime (ms precision)"
|
||||
}
|
||||
31
test/functests/test_shm_across_processes.sh
Normal file
31
test/functests/test_shm_across_processes.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
# Verify that shared memory works end-to-end across processes.
|
||||
# The faketime wrapper creates SHM with a known FAKETIME value,
|
||||
# and the child process (via LD_PRELOAD) reads it and reports
|
||||
# the faked time.
|
||||
|
||||
init()
|
||||
{
|
||||
typeset testsuite="$1"
|
||||
PLATFORM=$(platform)
|
||||
if [ -z "$PLATFORM" ]; then
|
||||
echo "$testsuite: unknown platform! quitting"
|
||||
return 1
|
||||
fi
|
||||
echo "# PLATFORM=$PLATFORM"
|
||||
return 0
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
init
|
||||
|
||||
run_testcase shm_year_check
|
||||
}
|
||||
|
||||
shm_year_check()
|
||||
{
|
||||
typeset expected="2020"
|
||||
typeset actual
|
||||
actual=$(fakecmd "2020-06-15 12:00:00" date -u +%Y)
|
||||
asserteq "$actual" "$expected" "child process should see faked year via SHM"
|
||||
}
|
||||
85
test/libmallocintercept.c
Normal file
85
test/libmallocintercept.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2022 be.storaged GmbH
|
||||
*
|
||||
* This file is part of libfaketime
|
||||
*
|
||||
* libfaketime is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License v2 as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License v2 along
|
||||
* with the libfaketime; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void print_msg(const char *msg) {
|
||||
size_t out;
|
||||
out = write(0, msg, strlen(msg));
|
||||
(void) out; /* unused */
|
||||
}
|
||||
|
||||
static void* actual_malloc(size_t size) {
|
||||
/* We would like to use "the real malloc", but cannot. Thus, this
|
||||
* implements a trivial, allocate-only bump allocator to make things
|
||||
* work.
|
||||
*/
|
||||
static char memory_arena[16 << 20];
|
||||
static size_t allocated_index = 0;
|
||||
|
||||
void *result = &memory_arena[allocated_index];
|
||||
|
||||
allocated_index += size;
|
||||
/* align to a multiple of 8 bytes */
|
||||
allocated_index = (allocated_index + 7) / 8 * 8;
|
||||
|
||||
if (allocated_index >= sizeof(memory_arena)) {
|
||||
print_msg("libmallocintercept is out of memory!");
|
||||
abort();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void poke_faketime(void) {
|
||||
#ifdef FAIL_PRE_INIT_CALLS
|
||||
/* To complicate things for libfaketime, this calls clock_gettime()
|
||||
* while holding a lock. This should simulate problems that occurred
|
||||
* with address sanitizer.
|
||||
*/
|
||||
static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
struct timespec timespec;
|
||||
|
||||
pthread_mutex_lock(&time_mutex);
|
||||
clock_gettime(CLOCK_REALTIME, ×pec);
|
||||
pthread_mutex_unlock(&time_mutex);
|
||||
#else
|
||||
print_msg("FAIL_PRE_INIT_CALLS not defined, skipping poke_faketime() ");
|
||||
#endif
|
||||
}
|
||||
|
||||
void *malloc(size_t size) {
|
||||
print_msg("Called malloc() from libmallocintercept...");
|
||||
poke_faketime();
|
||||
print_msg("successfully\n");
|
||||
return actual_malloc(size);
|
||||
}
|
||||
|
||||
void free(void *ptr) {
|
||||
(void) ptr; /* unused */
|
||||
print_msg("Called free() on from libmallocintercept...");
|
||||
poke_faketime();
|
||||
print_msg("successfully\n");
|
||||
|
||||
/* We cannot actually free memory */
|
||||
}
|
||||
21
test/randomtest.sh
Executable file
21
test/randomtest.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
FTPL="${FAKETIME_TESTLIB:-../src/libfaketime.so.1}"
|
||||
|
||||
set -e
|
||||
|
||||
error=0
|
||||
|
||||
repeat3x5="$(FAKERANDOM_SEED=0xDEADBEEFDEADBEEF LD_PRELOAD="$FTPL" ./repeat_random 3 5)"
|
||||
repeat5x3="$(FAKERANDOM_SEED=0xDEADBEEFDEADBEEF LD_PRELOAD="$FTPL" ./repeat_random 5 3)"
|
||||
|
||||
if [ "$repeat3x5" != "$repeat5x3" ]; then
|
||||
error=1
|
||||
printf >&2 '5 calls of getrandom(3) got %s\n3 calls of getrandom(5) got %s\n' "$repeat3x5" "$repeat5x3"
|
||||
fi
|
||||
|
||||
if [ 0 = $error ]; then
|
||||
printf 'getrandom interception test successful.\n'
|
||||
fi
|
||||
|
||||
exit $error
|
||||
40
test/repeat_random.c
Normal file
40
test/repeat_random.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/random.h>
|
||||
|
||||
void usage(const char* name) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s REPS SIZE\n\n"
|
||||
"Gather and print REPS blocks of SIZE bytes from getrandom()\n",
|
||||
name);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int reps, size;
|
||||
unsigned char *buf;
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
reps = atoi(argv[1]);
|
||||
size = atoi(argv[2]);
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "failure to allocate buffer of size %d\n", size);
|
||||
return 1;
|
||||
}
|
||||
for (int i = 0; i < reps; i++) {
|
||||
ssize_t resp = getrandom(buf, size, 0);
|
||||
if (resp != size) {
|
||||
fprintf(stderr, "tried to get %d bytes, got %zd\n", size, resp);
|
||||
free(buf);
|
||||
return 2;
|
||||
}
|
||||
for (int j = 0; j < size; j++) {
|
||||
printf("%02x", buf[j]);
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
printf("\n");
|
||||
return 0;
|
||||
};
|
||||
65
test/shm_layout_test.c
Normal file
65
test/shm_layout_test.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../src/faketime_common.h"
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check_offset(const char *field, size_t actual, size_t expected)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
fprintf(stderr, "FAIL: offsetof(ft_shared_s, %s) = %zu, expected %zu\n",
|
||||
field, actual, expected);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OK: offsetof(ft_shared_s, %s) = %zu\n", field, actual);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_size(const char *name, size_t actual, size_t expected)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
fprintf(stderr, "FAIL: sizeof(%s) = %zu, expected %zu\n",
|
||||
name, actual, expected);
|
||||
failures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OK: sizeof(%s) = %zu\n", name, actual);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
/* ft_shared_time_s must be exactly 16 bytes: two int64_t */
|
||||
check_size("ft_shared_time_s", sizeof(struct ft_shared_time_s), 16);
|
||||
|
||||
/* Field offsets in ft_shared_s */
|
||||
check_offset("ticks", offsetof(struct ft_shared_s, ticks), 0);
|
||||
check_offset("file_idx", offsetof(struct ft_shared_s, file_idx), 8);
|
||||
check_offset("start_time_real", offsetof(struct ft_shared_s, start_time_real), 16);
|
||||
check_offset("start_time_mon", offsetof(struct ft_shared_s, start_time_mon), 32);
|
||||
check_offset("start_time_mon_raw", offsetof(struct ft_shared_s, start_time_mon_raw), 48);
|
||||
#ifdef CLOCK_BOOTTIME
|
||||
check_offset("start_time_boot", offsetof(struct ft_shared_s, start_time_boot), 64);
|
||||
check_size("ft_shared_s", sizeof(struct ft_shared_s), 80);
|
||||
#else
|
||||
check_size("ft_shared_s", sizeof(struct ft_shared_s), 64);
|
||||
#endif
|
||||
|
||||
if (failures > 0)
|
||||
{
|
||||
fprintf(stderr, "%d layout check(s) failed\n", failures);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("All layout checks passed\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
72
test/snippets/README
Normal file
72
test/snippets/README
Normal file
@@ -0,0 +1,72 @@
|
||||
Testing Interception with Snippets
|
||||
==================================
|
||||
|
||||
Faketime intercepts some C library functions and system calls. We
|
||||
want to make it easier to apply certain generic tests across many
|
||||
different functions. We do that with snippets of C, which each can be
|
||||
applied in multiple testing frameworks.
|
||||
|
||||
Most snippets are just a minimalist invocation of a single function or
|
||||
syscall that is likely to be intercepted by libfaketime, though it's
|
||||
possible to test more complex snippets too.
|
||||
|
||||
Including a New Snippet
|
||||
-----------------------
|
||||
|
||||
To cover a new bit of intercepted functionality, supply a C snippet in
|
||||
this directory named `FOO.c` (the name FOO should conform to C
|
||||
function names -- letters, numbers, and underscores; if you're testing
|
||||
interception of a single function, the simplest thing is to name
|
||||
snippet file after the intercepted function name).
|
||||
|
||||
This file should contain a small bit of code that exercises the
|
||||
functionality in question. It should be self-contained, and it should
|
||||
produce some kind of description of what happened to stdout. The data
|
||||
sent to stdout should include the const char* "where" value (which is
|
||||
an indication of which test framework is using the snippet). Take a
|
||||
look at getpid.c for a simple example.
|
||||
|
||||
If the snippet needs additional #include headers, please add them
|
||||
in `include_headers.h`. These #includes will be used by every snippet,
|
||||
so try to keep it minimal.
|
||||
|
||||
Snippet Testing Frameworks
|
||||
--------------------------
|
||||
|
||||
We have the following frameworks that use the snippets:
|
||||
|
||||
### Variable Data
|
||||
|
||||
Most functionality intercepted by libfaketime will normally produce
|
||||
variant output when invoked multiple times across the span of a few
|
||||
seconds by different processes. But if `FAKETIME` (or an analogous
|
||||
variable like `FAKERANDOM_SEED`) is set, the output data should remain
|
||||
constant.
|
||||
|
||||
If this describes the functionality in a new snippet `FOO.c`, please
|
||||
also drop a single-line file `FOO.variable` in this directory, where
|
||||
the first word of the line is the variable that should hold the output
|
||||
constant, and the rest of the line is the value of that variable.
|
||||
|
||||
See the `test_variable_data` target in `test/Makefile` for how this is
|
||||
implemented.
|
||||
|
||||
### Library Constructors
|
||||
|
||||
Library constructor routines are run by ld.so before the main process
|
||||
is launched. If the LD_PRELOADed libfaketime is masking a symbol from
|
||||
libc, and another library has a constructor routine that invokes that
|
||||
symbol, it might get called before libfaketime has had a chance to
|
||||
initialize its followup pointers to the actual libc functionality.
|
||||
|
||||
This framework is applied automatically to all snippets.
|
||||
|
||||
See the `test_library_constructors` target in `test/Makefile` for how
|
||||
this is implemented.
|
||||
|
||||
Adding a New Framework
|
||||
----------------------
|
||||
|
||||
If you want to add a new framework that tests across all snippets,
|
||||
implement it in a few lines of `test/Makefile` and document it in the
|
||||
section above.
|
||||
8
test/snippets/clock_gettime.c
Normal file
8
test/snippets/clock_gettime.c
Normal file
@@ -0,0 +1,8 @@
|
||||
struct timespec ts;
|
||||
clockid_t ckid = CLOCK_REALTIME;
|
||||
int ret = clock_gettime(ckid, &ts);
|
||||
if (ret == 0) {
|
||||
printf("[%s] clock_gettime(CLOCK_REALTIME[%d], &ts) -> {%lld, %ld}\n", where, ckid, (long long)ts.tv_sec, ts.tv_nsec);
|
||||
} else {
|
||||
printf("[%s] clock_gettime(CLOCK_REALTIME[%d], &ts) returned non-zero (%d), errno = %d (%s)\n", where, ckid, ret, errno, strerror(errno));
|
||||
}
|
||||
1
test/snippets/clock_gettime.variable
Normal file
1
test/snippets/clock_gettime.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-02-02 02:02:02+00:00
|
||||
8
test/snippets/clock_gettime_heap.c
Normal file
8
test/snippets/clock_gettime_heap.c
Normal file
@@ -0,0 +1,8 @@
|
||||
struct timespec *ts = malloc(sizeof(struct timespec));
|
||||
clockid_t ckid = CLOCK_REALTIME;
|
||||
int ret = clock_gettime(ckid, ts);
|
||||
if (ret == 0) {
|
||||
printf("[%s] clock_gettime_heap(CLOCK_REALTIME[%d], ts) -> {%lld, %ld}\n", where, ckid, (long long)ts->tv_sec, ts->tv_nsec);
|
||||
} else {
|
||||
printf("[%s] clock_gettime_heap(CLOCK_REALTIME[%d], ts) returned non-zero (%d), errno = %d (%s)\n", where, ckid, ret, errno, strerror(errno));
|
||||
}
|
||||
1
test/snippets/clock_gettime_heap.variable
Normal file
1
test/snippets/clock_gettime_heap.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-02-02 02:02:02+00:00
|
||||
6
test/snippets/getentropy.c
Normal file
6
test/snippets/getentropy.c
Normal file
@@ -0,0 +1,6 @@
|
||||
unsigned int targ;
|
||||
if (getentropy(&targ, sizeof(targ)) == 0) {
|
||||
printf("[%s] getentropy() yielded 0x%08x\n", where, targ);
|
||||
} else {
|
||||
printf("[%s] getentropy() failed with %d (%s)\n", where, errno, strerror(errno));
|
||||
}
|
||||
1
test/snippets/getentropy.variable
Normal file
1
test/snippets/getentropy.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKERANDOM_SEED 0x0123456789ABCDEF
|
||||
2
test/snippets/getpid.c
Normal file
2
test/snippets/getpid.c
Normal file
@@ -0,0 +1,2 @@
|
||||
pid_t pid = getpid();
|
||||
printf("[%s] getpid() yielded %d\n", where, pid);
|
||||
1
test/snippets/getpid.variable
Normal file
1
test/snippets/getpid.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME_FAKEPID 1
|
||||
7
test/snippets/getrandom.c
Normal file
7
test/snippets/getrandom.c
Normal file
@@ -0,0 +1,7 @@
|
||||
unsigned int targ;
|
||||
ssize_t ret = getrandom(&targ, sizeof(targ), 0);
|
||||
if (ret == sizeof(targ)) {
|
||||
printf("[%s] getrandom() yielded 0x%08x\n", where, targ);
|
||||
} else {
|
||||
printf("[%s] getrandom() failed with only %zd\n", where, ret);
|
||||
}
|
||||
1
test/snippets/getrandom.variable
Normal file
1
test/snippets/getrandom.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKERANDOM_SEED 0xDEADBEEFDEADBEEF
|
||||
9
test/snippets/include_headers.h
Normal file
9
test/snippets/include_headers.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
2
test/snippets/syscall.c
Normal file
2
test/snippets/syscall.c
Normal file
@@ -0,0 +1,2 @@
|
||||
long uid = syscall(__NR_getuid);
|
||||
printf("[%s] syscall(__NR_getuid) -> %ld\n", where, uid);
|
||||
8
test/snippets/syscall_clock_gettime.c
Normal file
8
test/snippets/syscall_clock_gettime.c
Normal file
@@ -0,0 +1,8 @@
|
||||
struct timespec ts;
|
||||
clockid_t ckid = CLOCK_REALTIME;
|
||||
long ret = syscall(__NR_clock_gettime, ckid, &ts);
|
||||
if (ret == 0)
|
||||
printf("[%s] syscall(__NR_gettime, CLOCK_REALTIME[%d], &ts) -> {%lld, %ld}\n", where, ckid, (long long)ts.tv_sec, ts.tv_nsec);
|
||||
else
|
||||
printf("[%s] syscall(__NR_gettime, CLOCK_REALTIME[%d], &ts) returned non-zero (%ld)\n", where, ckid, ret);
|
||||
|
||||
1
test/snippets/syscall_clock_gettime.variable
Normal file
1
test/snippets/syscall_clock_gettime.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-01-01 00:00:00
|
||||
8
test/snippets/syscall_clock_gettime_heap.c
Normal file
8
test/snippets/syscall_clock_gettime_heap.c
Normal file
@@ -0,0 +1,8 @@
|
||||
struct timespec *ts = malloc(sizeof(struct timespec));
|
||||
clockid_t ckid = CLOCK_REALTIME;
|
||||
long ret = syscall(__NR_clock_gettime, ckid, ts);
|
||||
if (ret == 0)
|
||||
printf("[%s] syscall(__NR_gettime, CLOCK_REALTIME[%d], ts) -> {%lld, %ld}\n", where, ckid, (long long)ts->tv_sec, ts->tv_nsec);
|
||||
else
|
||||
printf("[%s] syscall(__NR_gettime, CLOCK_REALTIME[%d], ts) returned non-zero (%ld)\n", where, ckid, ret);
|
||||
|
||||
1
test/snippets/syscall_clock_gettime_heap.variable
Normal file
1
test/snippets/syscall_clock_gettime_heap.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-02-02 02:02:02+00:00
|
||||
32
test/snippets/syscall_clock_nanosleep.c
Normal file
32
test/snippets/syscall_clock_nanosleep.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Test raw syscall(__NR_clock_nanosleep): relative and TIMER_ABSTIME */
|
||||
#ifdef __NR_clock_nanosleep
|
||||
struct timespec start_fake, end_fake_rel, end_fake_abs;
|
||||
struct timespec req_rel = {0, 200 * 1000 * 1000}; /* 200ms fake sleep */
|
||||
struct timespec req_abs;
|
||||
long ret;
|
||||
|
||||
/* Capture starting time (fake view) */
|
||||
clock_gettime(CLOCK_REALTIME, &start_fake);
|
||||
|
||||
/* Relative sleep via syscall */
|
||||
ret = syscall(__NR_clock_nanosleep, CLOCK_REALTIME, 0, &req_rel, NULL);
|
||||
clock_gettime(CLOCK_REALTIME, &end_fake_rel);
|
||||
|
||||
/* Absolute sleep target: 300ms after start_fake *./test_variable_data.sh */
|
||||
req_abs.tv_sec = start_fake.tv_sec;
|
||||
req_abs.tv_nsec = start_fake.tv_nsec + 300 * 1000 * 1000;
|
||||
if (req_abs.tv_nsec >= 1000000000) { req_abs.tv_sec += 1; req_abs.tv_nsec -= 1000000000; }
|
||||
|
||||
/* Absolute sleep via syscall */
|
||||
ret = syscall(__NR_clock_nanosleep, CLOCK_REALTIME, TIMER_ABSTIME, &req_abs, NULL);
|
||||
clock_gettime(CLOCK_REALTIME, &end_fake_abs);
|
||||
|
||||
/* Report durations (fake view). */
|
||||
long rel_ns = (end_fake_rel.tv_sec - start_fake.tv_sec) * 1000000000L + (end_fake_rel.tv_nsec - start_fake.tv_nsec);
|
||||
long abs_ns = (end_fake_abs.tv_sec - start_fake.tv_sec) * 1000000000L + (end_fake_abs.tv_nsec - start_fake.tv_nsec);
|
||||
printf("[%s] syscall(clock_nanosleep) relative 200ms -> ~%ld ns, absolute +300ms -> ~%ld ns\n", where, rel_ns, abs_ns);
|
||||
return ret;
|
||||
#else
|
||||
printf("[%s] __NR_clock_nanosleep not defined on this platform\n", where);
|
||||
#endif
|
||||
|
||||
1
test/snippets/syscall_clock_nanosleep.variable
Normal file
1
test/snippets/syscall_clock_nanosleep.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-01-01 00:00:00
|
||||
2
test/snippets/time.c
Normal file
2
test/snippets/time.c
Normal file
@@ -0,0 +1,2 @@
|
||||
time_t t = time(NULL);
|
||||
printf("[%s] time() yielded %lu\n", where, (unsigned long)t);
|
||||
1
test/snippets/time.variable
Normal file
1
test/snippets/time.variable
Normal file
@@ -0,0 +1 @@
|
||||
FAKETIME 2020-02-02 02:02:02+00:00
|
||||
53
test/test.sh
53
test/test.sh
@@ -1,14 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
FTPL="${FAKETIME_TESTLIB:-../src/libfaketime.so.1}"
|
||||
MALLOC_INTERCEPT=./libmallocintercept.so
|
||||
|
||||
if [ -f /etc/faketimerc ] ; then
|
||||
echo "Running the test program with your system-wide default in /etc/faketimerc"
|
||||
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 ./timetest"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 ./timetest
|
||||
echo "\$ LD_PRELOAD=$FTPL ./timetest"
|
||||
LD_PRELOAD="$FTPL" ./timetest
|
||||
echo
|
||||
else
|
||||
echo "Running the test program with no faked time specified"
|
||||
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 ./timetest"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 ./timetest
|
||||
echo "\$ LD_PRELOAD=$FTPL ./timetest"
|
||||
LD_PRELOAD="$FTPL" ./timetest
|
||||
echo
|
||||
fi
|
||||
|
||||
@@ -16,48 +19,66 @@ echo "==========================================================================
|
||||
echo
|
||||
|
||||
echo "Running the test program with absolute date 2003-01-01 10:00:05 specified"
|
||||
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"2003-01-01 10:00:05\" ./timetest"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="2003-01-01 10:00:05" ./timetest
|
||||
echo "\$ LD_PRELOAD=$FTPL FAKETIME=\"2003-01-01 10:00:05\" ./timetest"
|
||||
LD_PRELOAD="$FTPL" FAKETIME="2003-01-01 10:00:05" ./timetest
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
echo
|
||||
|
||||
echo "Running the test program with START date @2005-03-29 14:14:14 specified"
|
||||
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"@2005-03-29 14:14:14\" ./timetest"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="@2005-03-29 14:14:14" ./timetest
|
||||
echo "\$ LD_PRELOAD=$FTPL FAKETIME=\"@2005-03-29 14:14:14\" ./timetest"
|
||||
LD_PRELOAD="$FTPL" FAKETIME="@2005-03-29 14:14:14" ./timetest
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
echo
|
||||
|
||||
echo "Running the test program with 10 days negative offset specified"
|
||||
echo "LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"-10d\" ./timetest"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="-10d" ./timetest
|
||||
echo "LD_PRELOAD=$FTPL FAKETIME=\"-10d\" ./timetest"
|
||||
LD_PRELOAD="$FTPL" FAKETIME="-10d" ./timetest
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
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 "\$ LD_PRELOAD=$FTPL FAKETIME=\"-10d\" NO_FAKE_STAT=1 ./timetest"
|
||||
LD_PRELOAD="$FTPL" FAKETIME="-10d" NO_FAKE_STAT=1 ./timetest
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
echo
|
||||
|
||||
echo "Running the test program with 10 days positive offset specified, and speed-up factor"
|
||||
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"+10d x1\" ./timetest"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="+10d x1" NO_FAKE_STAT=1 ./timetest
|
||||
echo "\$ LD_PRELOAD=$FTPL FAKETIME=\"+10d x1\" ./timetest"
|
||||
LD_PRELOAD="$FTPL" FAKETIME="+10d x1" NO_FAKE_STAT=1 ./timetest
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
echo
|
||||
|
||||
echo "Running the 'date' command with 15 days negative offset specified"
|
||||
echo "\$ LD_PRELOAD=../src/libfaketime.so.1 FAKETIME=\"-15d\" date"
|
||||
LD_PRELOAD=../src/libfaketime.so.1 FAKETIME="-15d" date
|
||||
echo "\$ LD_PRELOAD=$FTPL FAKETIME=\"-15d\" date"
|
||||
LD_PRELOAD="$FTPL" FAKETIME="-15d" date
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
echo
|
||||
|
||||
echo "Running the test program with malloc interception"
|
||||
echo "\$ LD_PRELOAD=./libmallocintercept.so:$FTPL ./timetest"
|
||||
LD_PRELOAD="./libmallocintercept.so:$FTPL" ./timetest
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
echo
|
||||
|
||||
echo "@2005-03-29 14:14:14" > .faketimerc-for-test
|
||||
echo "Running the test program with malloc interception and file faketimerc"
|
||||
echo "\$ FAKETIME_NO_CACHE=1 FAKETIME_TIMESTAMP_FILE=.faketimerc-for-test LD_PRELOAD=./libmallocintercept.so:$FTPL ./timetest"
|
||||
FAKETIME_NO_CACHE=1 FAKETIME_TIMESTAMP_FILE=.faketimerc-for-test LD_PRELOAD="./libmallocintercept.so:$FTPL" ./timetest
|
||||
rm .faketimerc-for-test
|
||||
echo
|
||||
|
||||
echo "============================================================================="
|
||||
|
||||
10
test/test_constructors.sh
Executable file
10
test/test_constructors.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
FTPL="${FAKETIME_TESTLIB:-../src/libfaketime.so.1}"
|
||||
|
||||
function="$1"
|
||||
|
||||
printf 'Testing library init for %s (no LD_PRELOAD)\n' "$function"
|
||||
LD_LIBRARY_PATH=. "./use_lib_$function"
|
||||
printf 'Testing library init for %s (LD_PRELOAD)\n' "$function"
|
||||
LD_LIBRARY_PATH=. LD_PRELOAD="$FTPL" "./use_lib_$function"
|
||||
54
test/test_variable_data.sh
Executable file
54
test/test_variable_data.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
FTPL="${FAKETIME_TESTLIB:-../src/libfaketime.so.1}"
|
||||
DELAY="${DELAY:-2}"
|
||||
|
||||
declare -A firstunset first second delayed delayedunset
|
||||
|
||||
err=0
|
||||
for func in "$@"; do
|
||||
if ! [ -x "./run_$func" ]; then
|
||||
printf >&2 '%s does not exist, failing\n' "./run_$func"
|
||||
exit 1
|
||||
fi
|
||||
read varname value < "snippets/$func.variable"
|
||||
unset $varname
|
||||
firstunset[$func]="$(env LD_PRELOAD="$FTPL" "./run_$func")"
|
||||
first[$func]="$(env LD_PRELOAD="$FTPL" "$varname=$value" "./run_$func")"
|
||||
second[$func]="$(env LD_PRELOAD="$FTPL" "$varname=$value" "./run_$func")"
|
||||
if [ "${first[$func]}" != "${second[$func]}" ]; then
|
||||
printf >&2 '[%s] Set %s="%s", but got two different outputs:\n - %s\n - %s\n' "$func" "$varname" "$value" "${first[$func]}" "${second[$func]}"
|
||||
err=$(( $err + 1 ))
|
||||
fi
|
||||
if [ "${first[$func]}" == "${firstunset[$func]}" ]; then
|
||||
printf >&2 '[%s] Same answer when %s="%s" and when unset:\n - set: %s\n - unset: %s\n' "$func" "$varname" "$value" "${first[$func]}" "${firstunset[$func]}"
|
||||
err=$(( $err + 1 ))
|
||||
fi
|
||||
done
|
||||
|
||||
printf "Sleeping %d seconds..." "$DELAY"
|
||||
sleep "$DELAY"
|
||||
printf 'done\n'
|
||||
|
||||
for func in "$@"; do
|
||||
read varname value < "snippets/$func.variable"
|
||||
unset $varname
|
||||
delayed[$func]="$(env LD_PRELOAD="$FTPL" "$varname=$value" "./run_$func")"
|
||||
delayedunset[$func]="$(LD_PRELOAD="$FTPL" "./run_$func")"
|
||||
if [ "${first[$func]}" != "${delayed[$func]}" ]; then
|
||||
printf >&2 '[%s] Vary across delay of %d seconds (%s="%s"):\n - before: %s\n - after: %s\n' "$func" "$DELAY" "$varname" "$value" "${first[$func]}" "${delayed[$func]}"
|
||||
err=$(( $err + 1 ))
|
||||
fi
|
||||
if [ "${firstunset[$func]}" == "${delayedunset[$func]}" ]; then
|
||||
printf >&2 '[%s] Same answer when unset across delay of %d seconds:\n - before: %s\n - after: %s\n' "$func" "$DELAY" "${firstunset[$func]}" "${delayedunset[$func]}"
|
||||
err=$(( $err + 1 ))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$err" -gt 0 ]; then
|
||||
printf >&2 'Got %d errors, failing\n' "$err"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
@@ -1,4 +1,4 @@
|
||||
#! /bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# testframe.sh DIR
|
||||
# bare-bones testing framework.
|
||||
# run the test suites in the given DIR;
|
||||
|
||||
217
test/timetest.c
217
test/timetest.c
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
@@ -39,11 +40,131 @@
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <utime.h>
|
||||
|
||||
#define MONO_FIX_TIMEOUT_SECONDS 1
|
||||
#define MONO_FIX_TOLERANCE_SECONDS 0.25 // Increased tolerance slightly for CI environments
|
||||
#define MONO_FIX_LOWER_BOUND (MONO_FIX_TIMEOUT_SECONDS - MONO_FIX_TOLERANCE_SECONDS)
|
||||
#define MONO_FIX_UPPER_BOUND (MONO_FIX_TIMEOUT_SECONDS + MONO_FIX_TOLERANCE_SECONDS)
|
||||
|
||||
#define VERBOSE 0
|
||||
|
||||
#define SIG SIGUSR1
|
||||
|
||||
#ifdef __ARM_ARCH
|
||||
static int fake_monotonic_clock = 0;
|
||||
#else
|
||||
static int fake_monotonic_clock = 1;
|
||||
#endif
|
||||
|
||||
static void test_utime_now(void)
|
||||
{
|
||||
char path[] = "/tmp/libfaketime-utime-XXXXXX";
|
||||
int fd;
|
||||
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror("mkstemp");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (utime(path, NULL) == -1)
|
||||
{
|
||||
perror("utime(NULL)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (utimes(path, NULL) == -1)
|
||||
{
|
||||
perror("utimes(NULL)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (close(fd) == -1)
|
||||
{
|
||||
perror("close");
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unlink(path) == -1)
|
||||
{
|
||||
perror("unlink");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("utime()/utimes(): NOW handling passed\n");
|
||||
}
|
||||
|
||||
static void test_utimens_now(void)
|
||||
{
|
||||
char path[] = "/tmp/libfaketime-utimensat-XXXXXX";
|
||||
struct timespec now_times[2];
|
||||
int fd;
|
||||
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror("mkstemp");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (utimensat(AT_FDCWD, path, NULL, 0) == -1)
|
||||
{
|
||||
perror("utimensat(NULL)");
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (futimens(fd, NULL) == -1)
|
||||
{
|
||||
perror("futimens(NULL)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
now_times[0].tv_sec = now_times[1].tv_sec = 0;
|
||||
now_times[0].tv_nsec = now_times[1].tv_nsec = UTIME_NOW;
|
||||
|
||||
if (utimensat(AT_FDCWD, path, now_times, 0) == -1)
|
||||
{
|
||||
perror("utimensat(UTIME_NOW)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (futimens(fd, now_times) == -1)
|
||||
{
|
||||
perror("futimens(UTIME_NOW)");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (close(fd) == -1)
|
||||
{
|
||||
perror("close");
|
||||
unlink(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unlink(path) == -1)
|
||||
{
|
||||
perror("unlink");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("utimensat()/futimens(): NOW handling passed\n");
|
||||
}
|
||||
|
||||
static void
|
||||
handler(int sig, siginfo_t *si, void *uc)
|
||||
{
|
||||
@@ -57,35 +178,53 @@ handler(int sig, siginfo_t *si, void *uc)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* pthread_test(void* args)
|
||||
{
|
||||
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t fake_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t fake_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
pthread_cond_t monotonic_cond;
|
||||
pthread_condattr_t attr;
|
||||
|
||||
struct timespec timeToWait, now;
|
||||
struct timespec time_to_wait, now;
|
||||
int rt;
|
||||
|
||||
args = args; // silence compiler warning about unused argument
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
timeToWait.tv_sec = now.tv_sec+1;
|
||||
timeToWait.tv_nsec = now.tv_nsec;
|
||||
time_to_wait.tv_sec = now.tv_sec+1;
|
||||
time_to_wait.tv_nsec = now.tv_nsec;
|
||||
|
||||
printf("pthread_cond_timedwait: CLOCK_REALTIME test\n");
|
||||
printf("(Intentionally sleeping 1 second...)\n");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_lock(&fakeMutex);
|
||||
rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
|
||||
pthread_mutex_lock(&fake_mtx);
|
||||
rt = pthread_cond_timedwait(&fake_cond, &fake_mtx, &time_to_wait);
|
||||
if (rt != ETIMEDOUT)
|
||||
{
|
||||
printf("pthread_cond_timedwait failed\n");
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
|
||||
|
||||
pthread_condattr_init(&attr);
|
||||
@@ -93,22 +232,63 @@ void* pthread_test(void* args)
|
||||
pthread_cond_init(&monotonic_cond, &attr);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
timeToWait.tv_sec = now.tv_sec+1;
|
||||
timeToWait.tv_nsec = now.tv_nsec;
|
||||
time_to_wait.tv_sec = now.tv_sec+1;
|
||||
time_to_wait.tv_nsec = now.tv_nsec;
|
||||
|
||||
printf("pthread_cond_timedwait: CLOCK_MONOTONIC test\n");
|
||||
printf("(Intentionally sleeping 1 second..., see docs about CLOCK_MONOTONIC test)\n");
|
||||
printf("(Intentionally sleeping 1 second...)\n");
|
||||
printf("(If this test hangs for more than a few seconds, please report\n your glibc version and system details as FORCE_MONOTONIC_FIX\n issue at https://github.com/wolfcw/libfaketime)\n");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_mutex_lock(&fakeMutex);
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
|
||||
pthread_mutex_lock(&fake_mtx);
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
|
||||
if (rt != ETIMEDOUT)
|
||||
{
|
||||
printf("pthread_cond_timedwait failed\n");
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pthread_mutex_unlock(&fakeMutex);
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
|
||||
get_fake_monotonic_setting(&fake_monotonic_clock);
|
||||
if (!fake_monotonic_clock)
|
||||
{
|
||||
printf("pthread_cond_timedwait: using real CLOCK_MONOTONIC test\n");
|
||||
struct timespec mono_after_wait;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
time_to_wait.tv_sec = now.tv_sec + MONO_FIX_TIMEOUT_SECONDS;
|
||||
time_to_wait.tv_nsec = now.tv_nsec;
|
||||
|
||||
struct timespec current_real_time;
|
||||
clock_gettime(CLOCK_REALTIME, ¤t_real_time);
|
||||
|
||||
struct timespec new_real_time = current_real_time;
|
||||
new_real_time.tv_sec += 3600; // Advance by one hour
|
||||
|
||||
clock_settime(CLOCK_REALTIME, &new_real_time);
|
||||
|
||||
pthread_mutex_lock(&fake_mtx);
|
||||
|
||||
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
|
||||
clock_gettime(CLOCK_MONOTONIC, &mono_after_wait);
|
||||
|
||||
pthread_mutex_unlock(&fake_mtx);
|
||||
|
||||
double elapsed_wall_time = (mono_after_wait.tv_sec - now.tv_sec) +
|
||||
(mono_after_wait.tv_nsec - now.tv_nsec) / 1000000000.0;
|
||||
|
||||
if (rt == ETIMEDOUT && (elapsed_wall_time >= MONO_FIX_LOWER_BOUND && elapsed_wall_time <= MONO_FIX_UPPER_BOUND))
|
||||
{
|
||||
printf("pthread_cond_timedwait with real CLOCK_MONOTONIC passed: elapsed time = %.2f seconds\n", elapsed_wall_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("pthread_cond_timedwait with real CLOCK_MONOTONIC FAILED: elapsed time = %.2f seconds, return code = %d\n", elapsed_wall_time, rt);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
pthread_cond_destroy(&monotonic_cond);
|
||||
|
||||
return NULL;
|
||||
@@ -216,7 +396,10 @@ printf("%s", 0 == 1 ? argv[0] : "");
|
||||
printf("time() : Current date and time: %s", ctime(&now));
|
||||
printf("time(NULL) : Seconds since Epoch : %u\n", (unsigned int)time(NULL));
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
ftime(&tb);
|
||||
#pragma GCC diagnostic pop
|
||||
printf("ftime() : Current date and time: %s", ctime(&tb.time));
|
||||
|
||||
printf("(Intentionally sleeping 2 seconds...)\n");
|
||||
@@ -231,6 +414,8 @@ printf("%s", 0 == 1 ? argv[0] : "");
|
||||
printf("gettimeofday() : Current date and time: %s", ctime(&tv.tv_sec));
|
||||
|
||||
#ifndef __APPLE__
|
||||
test_utime_now();
|
||||
test_utimens_now();
|
||||
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
|
||||
{
|
||||
perror("sigprocmask");
|
||||
@@ -243,7 +428,11 @@ printf("%s", 0 == 1 ? argv[0] : "");
|
||||
int timer_getoverrun_timerid1 = timer_getoverrun(timerid1);
|
||||
if (timer_getoverrun_timerid1 != 3)
|
||||
{
|
||||
#ifdef __GNU__
|
||||
printf("(Timer overruns are assumed to be fine on Hurd)\n");
|
||||
#else
|
||||
printf("timer_getoverrun(timerid1) FAILED, must be 3 but got: %d\n", timer_getoverrun_timerid1);
|
||||
#endif
|
||||
}
|
||||
|
||||
timer_gettime(timerid1, &its);
|
||||
|
||||
46
test/variadic/inner.c
Normal file
46
test/variadic/inner.c
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* round 0: c, s, wc, i, wi */
|
||||
long inner0(char *out, ...) {
|
||||
char c = 0;
|
||||
short s = 0;
|
||||
wchar_t wc = 0;
|
||||
int i = 0;
|
||||
wint_t wi = 0;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, out);
|
||||
c = va_arg(ap, int);
|
||||
s = va_arg(ap, int);
|
||||
wc = va_arg(ap, typeof(wc));
|
||||
i = va_arg(ap, typeof(i));
|
||||
wi = va_arg(ap, typeof(wi));
|
||||
va_end(ap);
|
||||
|
||||
int ret = sprintf(out, "c: 0x%x s: 0x%x wc: 0x%lx i: 0x%x wi: 0x%x\n", c, s, (long)wc, i, wi);
|
||||
return ret;
|
||||
}
|
||||
/* round 1: l, ll, ptr, pd, sz */
|
||||
long inner1(char *out, ...) {
|
||||
long l = 0;
|
||||
long long ll = 0;
|
||||
void *ptr = NULL;
|
||||
ptrdiff_t pd = 0;
|
||||
size_t sz = 0;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, out);
|
||||
l = va_arg(ap, typeof(l));
|
||||
ll = va_arg(ap, typeof(ll));
|
||||
ptr = va_arg(ap, typeof(ptr));
|
||||
pd = va_arg(ap, typeof(pd));
|
||||
sz = va_arg(ap, typeof(sz));
|
||||
va_end(ap);
|
||||
|
||||
int ret = sprintf(out, "l: 0x%lx ll: 0x%llx ptr: %p pd: 0x%tx sz: 0x%zx\n", l, ll, ptr, pd, sz);
|
||||
return ret;
|
||||
}
|
||||
70
test/variadic/main.c
Normal file
70
test/variadic/main.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <wchar.h>
|
||||
|
||||
extern long outer(long num, char *out, ...);
|
||||
extern long inner0(char *out, ...);
|
||||
extern long inner1(char *out, ...);
|
||||
|
||||
#define bufsize 2048
|
||||
|
||||
static int compare_buffers(int round,
|
||||
long ret_outer, long ret_inner,
|
||||
const char* outer, const char* inner) {
|
||||
int ret = 0;
|
||||
if (ret_outer != ret_inner) {
|
||||
printf("Round %d: return values differ (outer: %ld inner: %ld)\n", round, ret_outer, ret_inner);
|
||||
ret++;
|
||||
}
|
||||
if (memcmp(outer, inner, bufsize)) {
|
||||
printf("Round %d strings differ:\n - outer: %s\n - inner: %s\n", round, outer, inner);
|
||||
ret++;
|
||||
}
|
||||
if (ret == 0)
|
||||
printf("Round %d success: %s\n", round, outer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main() {
|
||||
/* sizes of intrinsic types as reported by echo | cpp -dM | grep
|
||||
SIZEOF, pruned to avoid floating point types. Should work with
|
||||
both clang and gcc, not sure about other C preprocessors.
|
||||
|
||||
Note that we set bits in every high octet and every low octet to
|
||||
see that they end up in the right spot.
|
||||
*/
|
||||
char c = 0x03L;
|
||||
short s = (0x04L << ((__SIZEOF_SHORT__ - 1) * 8)) + 0xff;
|
||||
wchar_t wc = (0x05L << ((__SIZEOF_WCHAR_T__ - 1) * 8)) + 0xfe;
|
||||
int i = (0x06L << ((__SIZEOF_INT__ - 1) * 8)) + 0xfd;
|
||||
wint_t wi = (0x07L << ((__SIZEOF_WINT_T__ - 1) * 8)) + 0xfc;
|
||||
long l = (0x08L << ((__SIZEOF_LONG__ - 1) * 8) ) + 0xfb;
|
||||
long long ll = (0x09LL << ((__SIZEOF_LONG_LONG__ - 1) * 8)) + 0xfa;
|
||||
void *ptr = (void*)((0x0aL << ((__SIZEOF_POINTER__ - 1) * 8)) + 0xf9);
|
||||
ptrdiff_t pd = (0x0bL << ((__SIZEOF_PTRDIFF_T__ -1) * 8)) + 0xf9;
|
||||
size_t sz = (0x0cL << ((__SIZEOF_SIZE_T__ - 1) * 8)) + 0xf8;
|
||||
|
||||
char *buf[2];
|
||||
for (int j = 0; j < 2; j++)
|
||||
buf[j] = malloc(bufsize);
|
||||
|
||||
int ret[2];
|
||||
int errors = 0;
|
||||
|
||||
#define reset_buffers(n) for (int j = 0; j < 2; j++) memset(buf[j], n, bufsize)
|
||||
#define check_buffers(n) errors += compare_buffers(n, ret[0], ret[1], buf[0], buf[1])
|
||||
|
||||
reset_buffers(0);
|
||||
ret[0] = outer(0, buf[0], c, s, wc, i, wi);
|
||||
ret[1] = inner0(buf[1], c, s, wc, i, wi);
|
||||
check_buffers(0);
|
||||
|
||||
reset_buffers(1);
|
||||
ret[0] = outer(1, buf[0], l, ll, ptr, pd, sz);
|
||||
ret[1] = inner1(buf[1], l, ll, ptr, pd, sz);
|
||||
check_buffers(1);
|
||||
|
||||
return (int)errors;
|
||||
}
|
||||
21
test/variadic/outer.c
Normal file
21
test/variadic/outer.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include "../../src/faketime_common.h"
|
||||
|
||||
extern long inner0(char *out, ...);
|
||||
extern long inner1(char *out, ...);
|
||||
|
||||
long outer(long num, char *out, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, out);
|
||||
variadic_promotion_t a[syscall_max_args];
|
||||
for (int i = 0; i < syscall_max_args; i++)
|
||||
a[i] = va_arg(ap, variadic_promotion_t);
|
||||
va_end(ap);
|
||||
|
||||
if (num == 0)
|
||||
return inner0(out, a[0], a[1], a[2], a[3], a[4], a[5]);
|
||||
if (num == 1)
|
||||
return inner1(out, a[0], a[1], a[2], a[3], a[4], a[5]);
|
||||
else return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user