1/*
2 * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: vtwrapper.c,v 1.4 2010/08/12 09:31:50 fdupont Exp $ */
18
19#define _GNU_SOURCE
20#include <sys/syscall.h>
21#include <sys/time.h>
22#include <sys/types.h>
23#include <math.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include <stdio.h>
27#ifdef SYS_select
28#include <sys/select.h>
29#endif
30#ifdef SYS_poll
31#include <poll.h>
32#endif
33#ifdef SYS_kevent
34#include <sys/event.h>
35#endif
36#ifdef SYS_epoll_wait
37#include <sys/epoll.h>
38#endif
39
40
41#ifdef SYS_gettimeofday
42#define VIRTUAL_TIME
43#ifdef VIRTUAL_TIME
44static struct timeval epoch = { 0, 0 };
45static int _init_called = 0;
46
47void
48_init(void) {
49	(void)syscall(SYS_gettimeofday, &epoch, NULL);
50	_init_called = 1;
51}
52
53static void
54absolute_inflate(struct timeval *vt, struct timeval *rt)
55{
56	double d;
57
58	rt->tv_sec = vt->tv_sec;
59	rt->tv_usec = vt->tv_usec;
60
61	if ((epoch.tv_sec > vt->tv_sec) ||
62	    ((epoch.tv_sec == vt->tv_sec) && (epoch.tv_usec > vt->tv_usec)))
63		return;
64
65	rt->tv_sec -= epoch.tv_sec;
66	rt->tv_usec -= epoch.tv_usec;
67	while (rt->tv_usec < 0) {
68		rt->tv_sec -= 1;
69		rt->tv_usec += 1000000;
70	}
71
72	if (rt->tv_sec == 0)
73		goto done;
74
75	d = (double) (rt->tv_sec - 1);
76	d += (double) rt->tv_usec / 1000000.;
77	d = exp(d);
78	rt->tv_sec = (time_t) d;
79	d -= (double) rt->tv_sec;
80	rt->tv_usec = (suseconds_t) (d * 1000000.);
81
82 done:
83	rt->tv_sec += epoch.tv_sec;
84	rt->tv_usec += epoch.tv_usec;
85	while (rt->tv_usec >= 1000000) {
86		rt->tv_sec += 1;
87		rt->tv_usec -= 1000000;
88	}
89	return;
90}
91
92static void
93absolute_deflate(struct timeval *rt, struct timeval *vt) {
94	double d;
95
96	vt->tv_sec = rt->tv_sec;
97	vt->tv_usec = rt->tv_usec;
98
99	if ((epoch.tv_sec > rt->tv_sec) ||
100	    ((epoch.tv_sec == rt->tv_sec) && (epoch.tv_usec > rt->tv_usec)))
101		return;
102
103	vt->tv_sec -= epoch.tv_sec;
104	vt->tv_usec -= epoch.tv_usec;
105	while (vt->tv_usec < 0) {
106		vt->tv_sec -= 1;
107		vt->tv_usec += 1000000;
108	}
109
110	if (vt->tv_sec == 0)
111		goto done;
112
113	d = (double) vt->tv_sec;
114	d += (double) vt->tv_usec / 1000000.;
115	d = log(d);
116	vt->tv_sec = (time_t) d;
117	d -= (double) vt->tv_sec;
118	vt->tv_sec += 1;
119	vt->tv_usec = (suseconds_t) (d * 1000000.);
120
121 done:
122	vt->tv_sec += epoch.tv_sec;
123	vt->tv_usec += epoch.tv_usec;
124	while (vt->tv_usec >= 1000000) {
125		vt->tv_sec += 1;
126		vt->tv_usec -= 1000000;
127	}
128	return;
129}
130
131static void
132interval_inflate(struct timeval *vt, struct timeval *rt) {
133	struct timeval now, tv;
134
135	(void) gettimeofday(&now, NULL);
136
137	absolute_deflate(&now, &tv);
138
139	tv.tv_sec += vt->tv_sec;
140	tv.tv_usec += vt->tv_usec;
141	while (tv.tv_usec >= 1000000) {
142		tv.tv_sec += 1;
143		tv.tv_usec -= 1000000;
144	}
145
146	absolute_inflate(&tv, rt);
147
148	rt->tv_sec -= now.tv_sec;
149	rt->tv_usec -= now.tv_usec;
150	if (rt->tv_usec < 0) {
151		rt->tv_sec -= 1;
152		rt->tv_usec += 1000000;
153	}
154	return;
155}
156
157static void
158interval_deflate(struct timeval *rt, struct timeval *vt) {
159	struct timeval now, tv;
160
161	vt->tv_sec = rt->tv_sec;
162	vt->tv_usec = rt->tv_usec;
163
164	if ((vt->tv_sec == 0) && (vt->tv_usec <= 10000))
165		return;
166
167	(void) gettimeofday(&now, NULL);
168
169	tv.tv_sec = now.tv_sec + rt->tv_sec;
170	tv.tv_usec = now.tv_usec + rt->tv_usec;
171	while (tv.tv_usec >= 1000000) {
172		tv.tv_sec += 1;
173		tv.tv_usec -= 1000000;
174	}
175
176	absolute_deflate(&now, &now);
177	absolute_deflate(&tv, vt);
178
179	vt->tv_sec -= now.tv_sec;
180	vt->tv_usec -= now.tv_usec;
181	while (vt->tv_usec < 0) {
182		vt->tv_sec -= 1;
183		vt->tv_usec += 1000000;
184	}
185
186	if ((vt->tv_sec == 0) && (vt->tv_usec < 10000))
187		vt->tv_usec = 10000;
188	return;
189}
190#endif
191
192int
193gettimeofday(struct timeval *tv, struct timezone *tz) {
194#ifdef VIRTUAL_TIME
195	struct timeval now;
196	int ret;
197
198	if (!_init_called) _init();
199
200	if (epoch.tv_sec == 0)
201		return syscall(SYS_gettimeofday, tv, tz);
202
203	ret = syscall(SYS_gettimeofday, &now, tz);
204	if (ret == 0)
205		absolute_inflate(&now, tv);
206	return ret;
207#else
208	return syscall(SYS_gettimeofday, tv, tz);
209#endif
210}
211
212#ifdef SYS_select
213int
214select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
215       struct timeval *timeout)
216{
217#ifdef VIRTUAL_TIME
218	struct timeval tv;
219
220	if (!_init_called) _init();
221
222	if (epoch.tv_sec == 0 || timeout == NULL ||
223	    (timeout->tv_sec == 0 && timeout->tv_usec == 0))
224		return syscall(SYS_select, nfds, rfds, wfds, xfds, timeout);
225
226	interval_deflate(timeout, &tv);
227	return syscall(SYS_select, nfds, rfds, wfds, xfds, &tv);
228#else
229	return syscall(SYS_select, nfds, rfds, wfds, xfds, timeout);
230#endif
231}
232#endif
233
234#ifdef SYS_poll
235int
236poll(struct pollfd fds[], nfds_t nfds, int timeout) {
237#ifdef VIRTUAL_TIME
238	struct timeval in, out;
239
240	if (!_init_called) _init();
241
242	if (timeout <= 0 || epoch.tv_sec == 0)
243		return syscall(SYS_poll, fds, nfds, timeout);
244
245	in.tv_sec = timeout / 1000;
246	in.tv_usec = (timeout % 1000) * 1000;
247	interval_deflate(&in, &out);
248	timeout = out.tv_sec * 1000 + out.tv_usec / 1000;
249	return syscall(SYS_poll, fds, nfds, timeout);
250#else
251	return syscall(SYS_poll, fds, nfds, timeout);
252#endif
253}
254#endif
255
256#ifdef SYS_kevent
257int
258kevent(int kq, struct kevent *changelist, int nchanges,
259       struct kevent *eventlist, int nevents, const struct timespec *timeout)
260{
261#ifdef VIRTUAL_TIME
262	struct timeval in, out;
263	struct timespec ts;
264
265	if (!_init_called) _init();
266
267	if (epoch.tv_sec == 0 || timeout == NULL ||
268	    (timeout->tv_sec == 0 && timeout->tv_nsec == 0))
269		return syscall(SYS_kevent, kq, changelist, nchanges,
270			       eventlist, nevents, timeout);
271
272	in.tv_sec = timeout->tv_sec;
273	in.tv_usec = timeout->tv_nsec / 1000;
274	interval_deflate(&in, &out);
275	ts.tv_sec = out.tv_sec;
276	ts.tv_nsec = out.tv_usec * 1000;
277	return syscall(SYS_kevent, kq, changelist, nchanges, eventlist,
278		       nevents, &ts);
279#else
280	return syscall(SYS_kevent, kq, changelist, nchanges, eventlist,
281		       nevents, timeout);
282#endif
283}
284#endif
285
286#ifdef SYS_epoll_wait
287int
288epoll_wait(int fd, struct epoll_event *events, int maxevents, int timeout) {
289#ifdef VIRTUAL_TIME
290	struct timeval in, out;
291
292	if (!_init_called) _init();
293
294	if (timeout == 0 || timeout == -1 || epoch.tv_sec == 0)
295		return syscall(SYS_epoll_wait, fd, events, maxevents, timeout);
296
297	in.tv_sec = timeout / 1000;
298	in.tv_usec = (timeout % 1000) * 1000;
299	interval_deflate(&in, &out);
300	timeout = out.tv_sec * 1000 + out.tv_usec / 1000;
301	return syscall(SYS_poll, fd, events, maxevents, timeout);
302#else
303	return syscall(SYS_poll, fd, events, maxevents, timeout);
304#endif
305}
306#endif
307#endif
308