-
Notifications
You must be signed in to change notification settings - Fork 0
/
timemachine.c
191 lines (164 loc) · 4.6 KB
/
timemachine.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <error.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bits/types/clockid_t.h>
#include <bits/types/struct_timespec.h>
#include <bits/types/struct_timeval.h>
#include <bits/types/time_t.h>
#define NAME "timemachine"
#define ENV_LOG "TIME_MACHINE_LOG"
#define ENV_OFFSET "TIME_MACHINE_OFFSET"
#define FILE_GET "get"
#define FILE_SET "set"
static bool log;
static pid_t pid;
static char* dir;
static char* get;
static char* set;
static pthread_t thread;
static time_t offset = 0;
static void write_offset() {
FILE* file = fopen(get, "w+");
if (file == NULL) {
if (log) error(0, errno, "%s: error opening file '%s'", NAME, get);
return;
}
if (fprintf(file, "%ld", offset) <= 0) {
if (log) error(0, errno, "%s: error writing file '%s'", NAME, get);
}
if (fclose(file) == EOF) {
if (log) error(0, errno, "%s: error closing file '%s'", NAME, get);
}
}
static void read_offset() {
FILE* file = fopen(set, "r");
if (file == NULL) {
if (log) error(0, errno, "%s: error opening file '%s'", NAME, set);
return;
}
if (fscanf(file, "%ld", &offset) <= 0) {
if (log) error(0, errno, "%s: error reading file '%s'", NAME, set);
}
if (fclose(file) == EOF) {
if (log) error(0, errno, "%s: error closing file '%s'", NAME, set);
}
}
static void* run(__attribute__((unused)) void* arg) {
while(1) {
read_offset();
write_offset();
}
return NULL;
}
__attribute__((constructor (101)))
static void values() {
pid = getpid();
asprintf(&dir, "%s/%s-%d", P_tmpdir, NAME, pid);
asprintf(&get, "%s/%s", dir, FILE_GET);
asprintf(&set, "%s/%s", dir, FILE_SET);
}
__attribute__((constructor (102)))
static void env_log() {
char* text = getenv(ENV_LOG);
log = text != NULL;
}
__attribute__((constructor (103)))
static void env_offset() {
char* text = getenv(ENV_OFFSET);
if (text == NULL) {
return;
}
if (sscanf(text, "%ld", &offset) <= 0) {
if (log) error(0, errno, "%s: error parsing value '%s'", NAME, text);
}
}
__attribute__((constructor (104)))
static void create_dir() {
if (mkdir(dir, 0775) < 0) {
if (log) error(0, errno, "%s: error creating process directory '%s'", NAME, dir);
}
}
__attribute__((constructor (105)))
static void create_get() {
write_offset();
}
__attribute__((constructor (106)))
static void create_set() {
if (mkfifo(set, 0660) < 0) {
if (log) error(0, errno, "%s: error creating file '%s'", NAME, set);
}
}
__attribute__((constructor (110)))
static void start() {
pthread_create(&thread, NULL, run, NULL);
}
__attribute__((destructor (103)))
static void remove_set() {
if (unlink(set) < 0) {
if (log) error(0, errno, "%s: error removing file '%s'", NAME, set);
}
}
__attribute__((destructor (102)))
static void remove_get() {
if (unlink(get) < 0) {
if (log) error(0, errno, "%s: error removing file '%s'", NAME, get);
}
}
__attribute__((destructor (101)))
static void remove_dir() {
if (rmdir(dir) < 0) {
if (log) error(0, errno, "%s: error removing process directory '%s'", NAME, dir);
}
}
typedef time_t (*func_time_t)(time_t*);
time_t time(time_t *tloc) {
static func_time_t func;
if (func == NULL) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
func = (func_time_t) dlsym(RTLD_NEXT, "time");
#pragma GCC diagnostic pop
}
time_t res = func(tloc) + offset;
if (tloc != NULL) {
*tloc += offset;
}
return res;
}
typedef int (*func_clock_gettime_t)(clockid_t, struct timespec*);
int clock_gettime(clockid_t clk_id, struct timespec *res) {
static func_clock_gettime_t func;
if (func == NULL) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
func = (func_clock_gettime_t) dlsym(RTLD_NEXT, "clock_gettime");
#pragma GCC diagnostic pop
}
int err = func(clk_id, res);
if (res != NULL) {
res->tv_sec += offset;
}
return err;
}
typedef int (*func_gettimeofday_t)(struct timeval*, struct timezone*);
int gettimeofday(struct timeval *tv, struct timezone *tz) {
static func_gettimeofday_t func;
if (func == NULL) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
func = (func_gettimeofday_t) dlsym(RTLD_NEXT, "gettimeofday");
#pragma GCC diagnostic pop
}
int err = func(tv, tz);
tv->tv_sec += offset;
return err;
}