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