diff --git a/docs/changes.txt b/docs/changes.txt index 1dde656..6422fd2 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -280,3 +280,7 @@ v0.9.4.3 0.9.4.7 * nfqws2: do not fail tls_mod if dupsid,rndsni,padencap fail + +0.9.5 + +* nfqws2: timers diff --git a/lua/zapret-tests.lua b/lua/zapret-tests.lua index a2ea6bf..0941f72 100644 --- a/lua/zapret-tests.lua +++ b/lua/zapret-tests.lua @@ -15,7 +15,7 @@ end function test_all(...) test_run({ test_crypto, test_bin, test_time, test_gzip, test_ipstr, test_dissect, test_csum, test_resolve, - test_get_source_ip, test_ifaddrs, test_rawsend},...) + test_get_source_ip, test_ifaddrs, test_rawsend, test_timer},...) end @@ -907,6 +907,23 @@ function test_ifaddrs(opts) end end +function timer1(name, data) + print("timer "..name.." fired. data="..tostring(data)) +end +function timer2(name, data) + data.n = data.n+1 + print("timer "..name.." fired. data.n="..tostring(data.n)) + if data.n>=3 then + timer_del(name) + end +end +function test_timer(opts) + timer_set("t1","timer1",500,true,"sample_data"); + tbl = {n=0} + timer_set("t2","timer2",700,false,tbl); +end + + function test_rawsend(opts) print("* rawsend") diff --git a/nfq2/darkmagic.c b/nfq2/darkmagic.c index 9ba21ba..5478571 100644 --- a/nfq2/darkmagic.c +++ b/nfq2/darkmagic.c @@ -1627,14 +1627,33 @@ static bool windivert_recv_exit(void) } return false; } -static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count) +static DWORD win_timer_check(t_timer_callback timer_callback, uint64_t *bt_prev) +{ + uint64_t bt,dbt; + + bt = boottime_ms(); + dbt = bt-*bt_prev; + if (dbt>=params.timer_res) + { + timer_callback(bt); + + *bt_prev = bt; + return params.timer_res; + } + else + return params.timer_res-(int)dbt; +} + +static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev) { UINT recv_len; - DWORD rd; + DWORD rd,twait,tmax; unsigned int wac; if (windivert_recv_exit()) return false; + tmax = win_timer_check(timer_callback, bt_prev); + wac = *wa_count * sizeof(WINDIVERT_ADDRESS); if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, &wac, &ovl)) { @@ -1647,10 +1666,16 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, switch(w_win32_error) { case ERROR_IO_PENDING: - // make signals working - while (WaitForSingleObject(ovl.hEvent,50)==WAIT_TIMEOUT) + // need to check for signals periodically + for(;;) { - if (windivert_recv_exit()) return false; + twait = tmax>50 ? 50 : tmax; + tmax -= twait; + // make signals working + if (WaitForSingleObject(ovl.hEvent,twait)!=WAIT_TIMEOUT) break; + if (windivert_recv_exit()) + return false; + if (!tmax) tmax = win_timer_check(timer_callback, bt_prev); } if (!GetOverlappedResult(hFilter,&ovl,&rd,FALSE)) { @@ -1675,9 +1700,9 @@ cancel: GetOverlappedResult(hFilter, &ovl, &rd, TRUE); return false; } -bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count) +bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev) { - return windivert_recv_filter(w_filter,packet,len,wa,wa_count); + return windivert_recv_filter(w_filter,packet,len,wa,wa_count,timer_callback,bt_prev); } static bool windivert_send_filter(HANDLE hFilter, const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa) diff --git a/nfq2/darkmagic.h b/nfq2/darkmagic.h index 5136410..eb233af 100644 --- a/nfq2/darkmagic.h +++ b/nfq2/darkmagic.h @@ -135,7 +135,8 @@ bool logical_net_filter_present(void); bool logical_net_filter_match(void); bool nlm_list(bool bAll); bool windivert_init(const char *filter); -bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count); +typedef void (*t_timer_callback)(uint64_t bt); +bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev); bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa); #else #define ensure_dir_access(dir) ensure_file_access(dir) diff --git a/nfq2/helpers.c b/nfq2/helpers.c index 4c96df6..a7e5a59 100644 --- a/nfq2/helpers.c +++ b/nfq2/helpers.c @@ -739,6 +739,14 @@ time_t boottime(void) struct timespec ts; return clock_gettime(CLOCK_BOOT_OR_UPTIME, &ts) ? 0 : ts.tv_sec; } +uint64_t boottime_ms(void) +{ + struct timespec ts; + if (clock_gettime(CLOCK_BOOT_OR_UPTIME, &ts)) + return 0; + else + return ts.tv_sec*1000ULL + ts.tv_nsec/1000000; +} #ifdef __CYGWIN__ diff --git a/nfq2/helpers.h b/nfq2/helpers.h index 680c3a4..05d9cc8 100644 --- a/nfq2/helpers.h +++ b/nfq2/helpers.h @@ -122,6 +122,7 @@ bool parse_int16(const char *p, int16_t *v); #endif time_t boottime(void); +uint64_t boottime_ms(void); #ifdef __CYGWIN__ uint32_t mask_from_bitcount(uint32_t zct); diff --git a/nfq2/lua.c b/nfq2/lua.c index 818b648..63fc5ba 100644 --- a/nfq2/lua.c +++ b/nfq2/lua.c @@ -3787,6 +3787,60 @@ static int luacall_timegm(lua_State *L) return 1; } +static int luacall_timer_set(lua_State *L) +{ + // timer_set(name, func, period, oneshot, data) + lua_check_argc_range(L,"timer_set",3,5); + + LUA_STACK_GUARD_ENTER(L) + + int argc=lua_gettop(L); + const char *name = luaL_checkstring(L,1); + const char *func = luaL_checkstring(L,2); + lua_Integer period = luaL_checkinteger(L,3); + if (period<10) luaL_error(L,"invalid timer period. must be >=10 ms"); + bool oneshot = argc>=4 && lua_toboolean(L,4); + + lua_getglobal(L, func); + bool is_f = lua_isfunction(L, -1); + lua_pop(L, 1); + if (!is_f) luaL_error(L, "timer function '%s' does not exist", func); + + timer_pool *timer = TimerPoolSearch(params.timers, name); + if (timer) luaL_error(L,"timer '%s' already present", name); + + timer = TimerPoolAdd(¶ms.timers, name, func, period, oneshot); + if (!timer) luaL_error(L,"could not create timer"); + + if (argc>=5) + { + lua_pushvalue(L, 5); + timer->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + DLOG("timer: '%s' created. function '%s' period %llu oneshot %u\n", timer->str, timer->func, timer->period, timer->oneshot); + + LUA_STACK_GUARD_RETURN(L,0) +} +static int luacall_timer_del(lua_State *L) +{ + // timer_del(name) + lua_check_argc(L,"timer_del",1); + + LUA_STACK_GUARD_ENTER(L) + + const char *name = luaL_checkstring(L,1); + timer_pool *timer = TimerPoolSearch(params.timers, name); + if (timer) + { + DLOG("timer: '%s' deleted\n", timer->str); + TimerPoolDel(¶ms.timers, timer); + } + else + DLOG("timer: '%s' not found\n", timer->str); + lua_pushboolean(L, !!timer); + LUA_STACK_GUARD_RETURN(L,1) +} + // ---------------------------------------- void lua_cleanup(lua_State *L) @@ -3794,6 +3848,8 @@ void lua_cleanup(lua_State *L) lua_desync_ctx_destroy(L); // conntrack holds lua state. must clear it before lua shoudown ConntrackPoolDestroy(¶ms.conntrack); + // timer can hold custom lua object + TimerPoolDestroy(¶ms.timers); } void lua_shutdown() @@ -4438,7 +4494,11 @@ static void lua_init_functions(void) {"localtime",luacall_localtime}, {"gmtime",luacall_gmtime}, {"timelocal",luacall_timelocal}, - {"timegm",luacall_timegm} + {"timegm",luacall_timegm}, + + // timers + {"timer_set",luacall_timer_set}, + {"timer_del",luacall_timer_del} }; for(int i=0;i<(sizeof(lfunc)/sizeof(*lfunc));i++) lua_register(params.L,lfunc[i].name,lfunc[i].f); diff --git a/nfq2/lua.h b/nfq2/lua.h index c0e9c52..d13c0c3 100644 --- a/nfq2/lua.h +++ b/nfq2/lua.h @@ -4,7 +4,7 @@ #include #ifdef LUAJIT -#include "luajit.h" +#include #else #include #endif diff --git a/nfq2/nfqws.c b/nfq2/nfqws.c index 8c7137a..71200a5 100644 --- a/nfq2/nfqws.c +++ b/nfq2/nfqws.c @@ -11,6 +11,7 @@ #include "ipset.h" #include "gzip.h" #include "pools.h" +#include "timer.h" #include "lua.h" #include "crypto/aes.h" @@ -239,6 +240,28 @@ static int write_pidfile(FILE **Fpid) return true; } +void NoInterceptLoop(void) +{ + useconds_t usec = params.timer_res * 1000; + + if (params.timers) + { + DLOG("processing timers\n"); + + while(params.timers) + { + if (bQuit) goto quit; + usleep(usec); + if (bQuit) goto quit; + ReloadCheck(); + lua_do_gc(); + TimerPoolRun(¶ms.timers, 0); + } + } +quit: + DLOG_CONDUP("quit requested\n"); +} + #ifdef __linux__ @@ -417,6 +440,9 @@ static int nfq_main(void) FILE *Fpid = NULL; uint8_t *buf=NULL, *mod=NULL; struct nfq_cb_data cbdata = { .sock = -1, .mod = NULL }; + fd_set fdset; + struct timeval tv = {.tv_sec = params.timer_res/1000, .tv_usec = params.timer_res%1000*1000}; + unsigned int bt,bt_prev,dbt; if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w"))) { @@ -454,6 +480,7 @@ static int nfq_main(void) if (!params.intercept) { + NoInterceptLoop(); DLOG_CONDUP("no intercept quit\n"); goto exok; } @@ -491,26 +518,58 @@ static int nfq_main(void) notify_ready(); fd = nfq_fd(h); + bt_prev=0; do { if (bQuit) goto quit; - while ((rd = recv(fd, buf, NFQ_MAX_RECV_SIZE, 0)) >= 0) + for(;;) { - if (!rd) + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + res = select(fd+1, &fdset, NULL, NULL, &tv); + if (bQuit) goto quit; + if (res == -1) { - DLOG_ERR("recv from nfq returned 0 !\n"); + if (errno == EINTR) continue; + DLOG_PERROR("select"); goto err; } - ReloadCheck(); lua_do_gc(); + ReloadCheck(); + if (res) + { + rd = recv(fd, buf, NFQ_MAX_RECV_SIZE, 0); + if (rd<0) break; + if (!rd) + { + DLOG_ERR("recv from nfq returned 0 !\n"); + goto err; + } #ifdef HAS_FILTER_SSID - if (params.filter_ssid_present) - if (!wlan_info_get_rate_limited()) - DLOG_ERR("cannot get wlan info\n"); + if (params.filter_ssid_present) + if (!wlan_info_get_rate_limited()) + DLOG_ERR("cannot get wlan info\n"); #endif - int r = nfq_handle_packet(h, (char *)buf, (int)rd); - if (r<0) DLOG_ERR("nfq_handle_packet result %d, errno %d : %s\n", r, errno, strerror(errno)); - if (bQuit) goto quit; + res = nfq_handle_packet(h, (char *)buf, (int)rd); + if (res<0) DLOG_ERR("nfq_handle_packet result %d, errno %d : %s\n", res, errno, strerror(errno)); + } + + bt = boottime_ms(); + dbt = bt-bt_prev; + if (dbt>=params.timer_res) + { + TimerPoolRun(¶ms.timers, bt); + + bt_prev = bt; + tv.tv_sec = params.timer_res/1000; + tv.tv_usec = params.timer_res%1000*1000; + } + else + { + dbt = params.timer_res-dbt; + tv.tv_sec = (time_t)(dbt/1000); + tv.tv_usec = (suseconds_t)(dbt%1000*1000); + } } if (errno==EINTR) continue; @@ -553,11 +612,13 @@ static int dvt_main(void) unsigned int id = 0; socklen_t socklen; ssize_t rd, wr; - fd_set fdset; FILE *Fpid = NULL; struct sockaddr_in bp4; struct sockaddr_in6 bp6; uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned)); + fd_set fdset; + struct timeval tv = {.tv_sec = params.timer_res/1000, .tv_usec = params.timer_res%1000*1000}; + unsigned int bt,bt_prev,dbt; if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w"))) { @@ -633,6 +694,7 @@ static int dvt_main(void) if (!params.intercept) { + NoInterceptLoop(); DLOG("no intercept quit\n"); goto exitok; } @@ -640,7 +702,7 @@ static int dvt_main(void) if (params.daemon) daemonize(); if (!write_pidfile(&Fpid)) goto exiterr; - for (;;) + for (bt_prev=0;;) { if (bQuit) { @@ -649,7 +711,7 @@ static int dvt_main(void) } FD_ZERO(&fdset); for (i = 0; i < fdct; i++) FD_SET(fd[i], &fdset); - r = select(fdmax, &fdset, NULL, NULL, NULL); + r = select(fdmax, &fdset, NULL, NULL, &tv); if (bQuit) { DLOG_CONDUP("quit requested\n"); @@ -661,6 +723,8 @@ static int dvt_main(void) DLOG_PERROR("select"); goto exiterr; } + ReloadCheck(); + lua_do_gc(); for (i = 0; i < fdct; i++) { if (FD_ISSET(fd[i], &fdset)) @@ -680,9 +744,6 @@ static int dvt_main(void) size_t modlen, len = rd; const char *ifin, *ifout; - ReloadCheck(); - lua_do_gc(); - // in any BSD addr of incoming packet is set to the first addr of the interface. addr of outgoing packet is set to zero bool bIncoming = sa_has_addr((struct sockaddr*)&sa_from); ifin = bIncoming ? "unknown" : ""; @@ -734,6 +795,22 @@ static int dvt_main(void) } } } + bt = boottime_ms(); + dbt = bt-bt_prev; + if (dbt>=params.timer_res) + { + TimerPoolRun(¶ms.timers, bt); + + bt_prev = bt; + tv.tv_sec = params.timer_res/1000; + tv.tv_usec = params.timer_res%1000*1000; + } + else + { + dbt = params.timer_res-dbt; + tv.tv_sec = (time_t)(dbt/1000); + tv.tv_usec = (suseconds_t)(dbt%1000*1000); + } } exitok: @@ -754,7 +831,12 @@ exiterr: // do not make it less than 65536 - loopback packets can be up to 64K #define WINDIVERT_PACKET_BUF_SIZE 196608 // 3*64K, 128*1500=192000 -static int win_main() +static void win_timer_callback(uint64_t bt) +{ + TimerPoolRun(¶ms.timers, bt); +} + +static int win_main(void) { size_t len, packet_len, left, modlen; unsigned int id; @@ -766,6 +848,7 @@ static int win_main() WINDIVERT_ADDRESS wa[WINDIVERT_BULK_MAX]; uint8_t *packets = NULL, *packet, *mod=NULL; unsigned int n,wa_count; + uint64_t bt_prev = 0; // windows emulated fork logic does not cover objects outside of cygwin world. have to daemonize before inits if (params.daemon) daemonize(); @@ -830,6 +913,7 @@ static int win_main() if (!params.intercept) { + NoInterceptLoop(); DLOG("no intercept quit\n"); goto ex; } @@ -838,7 +922,7 @@ static int win_main() { len = WINDIVERT_PACKET_BUF_SIZE; wa_count = WINDIVERT_BULK_MAX; - if (!windivert_recv(packets, &len, wa, &wa_count)) + if (!windivert_recv(packets, &len, wa, &wa_count, win_timer_callback, &bt_prev)) { if (errno == ENOBUFS) { @@ -1743,6 +1827,7 @@ static void exithelp(void) " --ipcache-lifetime=\t\t\t\t; time in seconds to keep cached hop count and domain name (default %u). 0 = no expiration\n" " --ipcache-hostname=[0|1]\t\t\t\t; 1 or no argument enables ip->hostname caching\n" " --reasm-disable=[type[,type]]\t\t\t\t; disable reasm for these L7 payloads : tls_client_hello quic_initial . if no argument - disable all reasm.\n" + " --timer-res=msec\t\t\t\t\t; Lua timer resolution. default %d ms\n" #ifdef __CYGWIN__ "\nWINDIVERT FILTER:\n" " --wf-iface=[.]\t\t\t\t; numeric network interface and subinterface indexes\n" @@ -1818,6 +1903,7 @@ static void exithelp(void) #endif CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, CTRACK_T_UDP, IPCACHE_LIFETIME, + TIMER_RES_DEFAULT, LUA_GC_INTERVAL, all_protos, HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, @@ -1900,6 +1986,7 @@ enum opt_indices { IDX_IPCACHE_LIFETIME, IDX_IPCACHE_HOSTNAME, IDX_REASM_DISABLE, + IDX_TIMER_RES, #ifdef __linux__ IDX_FWMARK, #elif defined(SO_USER_COOKIE) @@ -2005,6 +2092,7 @@ static const struct option long_options[] = { [IDX_IPCACHE_LIFETIME] = {"ipcache-lifetime", required_argument, 0, 0}, [IDX_IPCACHE_HOSTNAME] = {"ipcache-hostname", optional_argument, 0, 0}, [IDX_REASM_DISABLE] = {"reasm-disable", optional_argument, 0, 0}, + [IDX_TIMER_RES] = {"timer-res", required_argument, 0, 0}, #ifdef __linux__ [IDX_FWMARK] = {"fwmark", required_argument, 0, 0}, #elif defined(SO_USER_COOKIE) @@ -2407,6 +2495,14 @@ int main(int argc, char **argv) else params.reasm_payload_disable = L7P_ALL; break; + case IDX_TIMER_RES: + params.timer_res = atoi(optarg); + if (params.timer_res<10) + { + DLOG_ERR("Invalid timer resolution. must be >=10 ms\n"); + exit_clean(1); + } + break; #if defined(__linux__) case IDX_FWMARK: #elif defined(SO_USER_COOKIE) diff --git a/nfq2/params.c b/nfq2/params.c index dbc0bda..f10cd56 100644 --- a/nfq2/params.c +++ b/nfq2/params.c @@ -559,6 +559,7 @@ void cleanup_params(struct params_s *params) ipcacheDestroy(¶ms->ipcache); blob_collection_destroy(¶ms->blobs); strlist_destroy(¶ms->lua_init_scripts); + TimerPoolDestroy(¶ms->timers); #ifdef __CYGWIN__ strlist_destroy(¶ms->ssid_filter); @@ -587,6 +588,7 @@ void init_params(struct params_s *params) params->ctrack_t_fin = CTRACK_T_FIN; params->ctrack_t_udp = CTRACK_T_UDP; params->ipcache_lifetime = IPCACHE_LIFETIME; + params->timer_res = TIMER_RES_DEFAULT; params->lua_gc = LUA_GC_INTERVAL; LIST_INIT(¶ms->hostlists); diff --git a/nfq2/params.h b/nfq2/params.h index a04772f..ef65b85 100644 --- a/nfq2/params.h +++ b/nfq2/params.h @@ -7,6 +7,7 @@ #include "protocol.h" #include "helpers.h" #include "sec.h" +#include "timer.h" #include #include @@ -36,6 +37,8 @@ #define IPCACHE_LIFETIME 7200 +#define TIMER_RES_DEFAULT 50 + #define MAX_GIDS 64 #define MAX_BLOB_SIZE (16*1024) @@ -192,6 +195,9 @@ struct params_s bool writeable_dir_enable; char writeable_dir[PATH_MAX]; + int timer_res; // timer resolution in msec + timer_pool *timers; + int lua_gc; int ref_desync_ctx; // desync ctx userdata registry ref lua_State *L; diff --git a/nfq2/pools.c b/nfq2/pools.c index 202012d..25ab761 100644 --- a/nfq2/pools.c +++ b/nfq2/pools.c @@ -5,33 +5,6 @@ #include #include -#define DESTROY_STR_POOL(etype, ppool) \ - etype *elem, *tmp; \ - HASH_ITER(hh, *ppool, elem, tmp) { \ - free(elem->str); \ - HASH_DEL(*ppool, elem); \ - free(elem); \ - } - -#define ADD_STR_POOL(etype, ppool, keystr, keystr_len) \ - etype *elem; \ - if (!(elem = (etype*)malloc(sizeof(etype)))) \ - return false; \ - if (!(elem->str = malloc(keystr_len + 1))) \ - { \ - free(elem); \ - return false; \ - } \ - memcpy(elem->str, keystr, keystr_len); \ - elem->str[keystr_len] = 0; \ - oom = false; \ - HASH_ADD_KEYPTR(hh, *ppool, elem->str, keystr_len, elem); \ - if (oom) \ - { \ - free(elem->str); \ - free(elem); \ - return false; \ - } #define ADD_HOSTLIST_POOL(etype, ppool, keystr, keystr_len, flg) \ etype *elem_find; \ HASH_FIND(hh, *ppool, keystr, keystr_len, elem_find); \ diff --git a/nfq2/pools.h b/nfq2/pools.h index d87b3e2..01798c3 100644 --- a/nfq2/pools.h +++ b/nfq2/pools.h @@ -27,6 +27,34 @@ else \ LIST_INSERT_HEAD(head, elm, field); } +#define DESTROY_STR_POOL(etype, ppool) \ + etype *elem, *tmp; \ + HASH_ITER(hh, *ppool, elem, tmp) { \ + free(elem->str); \ + HASH_DEL(*ppool, elem); \ + free(elem); \ + } + +#define ADD_STR_POOL(etype, ppool, keystr, keystr_len) \ + etype *elem; \ + if (!(elem = (etype*)malloc(sizeof(etype)))) \ + return false; \ + if (!(elem->str = malloc(keystr_len + 1))) \ + { \ + free(elem); \ + return NULL; \ + } \ + memcpy(elem->str, keystr, keystr_len); \ + elem->str[keystr_len] = 0; \ + oom = false; \ + HASH_ADD_KEYPTR(hh, *ppool, elem->str, keystr_len, elem); \ + if (oom) \ + { \ + free(elem->str); \ + free(elem); \ + return NULL; \ + } + typedef struct hostlist_pool { char *str; /* key */ diff --git a/nfq2/timer.c b/nfq2/timer.c new file mode 100644 index 0000000..f9b8407 --- /dev/null +++ b/nfq2/timer.c @@ -0,0 +1,117 @@ +#include "timer.h" +#include "params.h" +#include "helpers.h" + +#include "lua.h" + +#undef uthash_nonfatal_oom +#define uthash_nonfatal_oom(elt) ut_oom_recover(elt) + +static bool oom = false; +static void ut_oom_recover(void *elem) +{ + oom = true; +} + +static unsigned int timer_n=0; + + +static void TimerPoolDestroyItem(timer_pool *elem) +{ + free(elem->str); + free(elem->func); + luaL_unref(params.L, LUA_REGISTRYINDEX, elem->lua_ref); +} +void TimerPoolDel(timer_pool **pp, timer_pool *p) +{ + TimerPoolDestroyItem(p); + HASH_DEL(*pp,p); + free(p); +} +void TimerPoolDestroy(timer_pool **pp) +{ + timer_pool *elem, *tmp; + HASH_ITER(hh, *pp, elem, tmp) TimerPoolDel(pp,elem); +} +struct timer_pool *TimerPoolSearch(timer_pool *p, const char *str) +{ + timer_pool *elem_find; + HASH_FIND_STR(p, str, elem_find); + return elem_find; +} +struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, const char *func, uint64_t period, bool oneshot) +{ + ADD_STR_POOL(timer_pool, pp, str, strlen(str)) + if (!(elem->func = strdup(func))) + { + TimerPoolDel(pp,elem); + return NULL; + } + elem->period = period; + elem->oneshot = oneshot; + elem->lua_ref = LUA_NOREF; + elem->bt_prev = boottime_ms(); + elem->n = ++timer_n; + return elem; +} + +static bool TimerPoolRunTimer(timer_pool *p) +{ + lua_getglobal(params.L, p->func); + if (!lua_isfunction(params.L, -1)) + { + lua_pop(params.L, 1); + DLOG_ERR("timer: '%s' function '%s' does not exist\n",p->str,p->func); + return false; + } + lua_pushstring(params.L, p->str); + lua_rawgeti(params.L, LUA_REGISTRYINDEX, p->lua_ref); + DLOG("\ntimer: '%s' function '%s' period %llu oneshot %u\n",p->str,p->func,p->period,p->oneshot); + int status = lua_pcall(params.L, 2, 0, 0); + if (status) + { + lua_dlog_error(); + return false; + } + return true; +} +void TimerPoolRun(timer_pool **pp, uint64_t bt) +{ + if (!pp) return; // no timers + + if (!bt) bt = boottime_ms(); + + timer_pool *elem, *tmp, *p; + char *name; + const char *del; + unsigned int n; + HASH_ITER(hh, *pp, elem, tmp) + { + if (bt >= (elem->bt_prev + elem->period)) + { + if (!(name = strdup(elem->str))) + return; + n = elem->n; + + del = NULL; + if (!TimerPoolRunTimer(elem)) + del = "timer: '%s' deleted because of error\n"; + else if (elem->oneshot) + del = "timer: '%s' deleted because of oneshot\n"; + if (del) + { + // timer function could delete the timer itself or recreate with the same name + p = TimerPoolSearch(*pp, name); + if (p==elem && p->n==n) + { + DLOG(del,name); + TimerPoolDel(pp, elem); + } + } + else + elem->bt_prev = bt; + + free(name); + } + } +} diff --git a/nfq2/timer.h b/nfq2/timer.h new file mode 100644 index 0000000..4df727d --- /dev/null +++ b/nfq2/timer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include "pools.h" + +typedef struct timer_pool { + char *str; /* key */ + char *func; + uint64_t period; + bool oneshot; + int lua_ref; + uint64_t bt_prev; + unsigned int n; + UT_hash_handle hh; /* makes this structure hashable */ +} timer_pool; + +void TimerPoolDestroy(timer_pool **pp); +struct timer_pool *TimerPoolSearch(timer_pool *p, const char *str); +struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, const char *func, uint64_t period, bool oneshot); +void TimerPoolDel(timer_pool **pp, timer_pool *p); +void TimerPoolRun(timer_pool **pp, uint64_t bt);