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