diff --git a/Jmakefile b/Jmakefile index ff99ac2..e8040c1 100644 --- a/Jmakefile +++ b/Jmakefile @@ -15,6 +15,9 @@ src := \ ban2fail.c \ cfgmap.c \ cntry.c \ + es.c \ + ez_es.c \ + ez_libanl.c \ ez_libc.c \ ez_libz.c \ iptables.c \ @@ -23,11 +26,12 @@ src := \ logFile.c \ map.c \ maxoff.c \ + msgqueue.c \ ptrvec.c \ str.c \ util.c \ - libs := z crypto GeoIP + libs := anl z crypto GeoIP pthread endif ######################################## diff --git a/ban2fail.c b/ban2fail.c index 8e30076..e03fc00 100644 --- a/ban2fail.c +++ b/ban2fail.c @@ -16,23 +16,17 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define _GNU_SOURCE #include -#include -#include -#include -#include #include -#include -#include +#include #include -#include -#include -#include +#include #include "ban2fail.h" #include "cntry.h" +#include "ez_libanl.h" #include "ez_libc.h" -#include "ez_libz.h" #include "iptables.h" #include "logEntry.h" #include "logFile.h" @@ -69,10 +63,10 @@ struct initInfo { static int cntryStat_count_qsort(const void *p1, const void *p2); static int configure(CFGMAP *h_cfgmap, const char *pfix); +static const char* reverse_dns_lookup(const char *addr); static int logentry_count_qsort(const void *p1, const void *p2); static int map_byCountries(LOGENTRY *e, MAP *h_map); static int stub_init(CFGMAP *map, char *symStr); -static int whitelist_init(CFGMAP *h_cfgmap, char *symStr); /*==================================================================*/ @@ -133,6 +127,17 @@ static struct { PTRVEC toBlock_vec, toUnblock_vec; + /* Used for reverse DNS lookups */ + struct { + struct gaicb **cbPtrArr, + *cbArr; + } gai; + + /* Used to place LOGENTRY address objects into linear + * access container. + */ + LOGENTRY **lePtrArr; + } S; /*==================================================================*/ @@ -162,7 +167,7 @@ main(int argc, char **argv) MAP_constructor(&G.logType_map, 10, 10); // local - MAP_constructor(&S.addr2logEntry_map, N_ADDRESSES_HINT/10, 10); + MAP_constructor(&S.addr2logEntry_map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT); PTRVEC_constructor(&S.toBlock_vec, N_ADDRESSES_HINT); PTRVEC_constructor(&S.toUnblock_vec, N_ADDRESSES_HINT); @@ -200,10 +205,11 @@ main(int argc, char **argv) case 'a': G.flags |= GLB_LIST_ADDR_FLG; - if(optarg && *optarg == '+') { - G.flags |= GLB_DNS_LOOKUP_FLG; - } else { - ++errflg; + if(optarg) { + if(*optarg == '+') + G.flags |= GLB_DNS_LOOKUP_FLG; + else + ++errflg; } break; @@ -380,7 +386,7 @@ main(int argc, char **argv) if(G.flags & GLB_LONG_LISTING_FLG) { MAP map; - MAP_constructor(&map, N_ADDRESSES_HINT/10, 10); + MAP_constructor(&map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT); unsigned nOffFound= 0, nAddrFound; @@ -414,15 +420,115 @@ main(int argc, char **argv) unsigned nItems= MAP_numItems(&S.addr2logEntry_map); - LOGENTRY *leArr[nItems]; - MAP_fetchAllItems(&S.addr2logEntry_map, (void**)leArr); - qsort(leArr, nItems, sizeof(LOGENTRY*), logentry_count_qsort); + /* allocate this array, let it leak */ + S.lePtrArr= malloc(sizeof(void*) * nItems); + assert(S.lePtrArr); + + MAP_fetchAllItems(&S.addr2logEntry_map, (void**)S.lePtrArr); + qsort(S.lePtrArr, nItems, sizeof(LOGENTRY*), logentry_count_qsort); + + /* Special processing for DNS lookups */ + if(G.flags & GLB_DNS_LOOKUP_FLG) { + + static struct sigevent sev= {.sigev_notify= SIGEV_NONE}; + const static struct addrinfo hints= { + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST + }; + + /* Allocate array of structures */ + S.gai.cbArr= calloc(sizeof(struct gaicb), nItems); + assert(S.gai.cbArr); + + /* Allocate pointer array */ + S.gai.cbPtrArr= malloc(sizeof(struct gaicb*) * nItems); + assert(S.gai.cbPtrArr); + + /* Fill out cbPtrArr with addresses, populate structures */ + for(unsigned i= 0; i < nItems; ++i) { + + LOGENTRY *e= S.lePtrArr[i]; + struct gaicb *cb= S.gai.cbArr+i; + + /* Populate gaicb object */ + cb->ar_name= e->addr; + cb->ar_request= &hints; + + /* Place object address in cbPtrArr */ + S.gai.cbPtrArr[i]= cb; + } + + /* See if we can submit all of our requests */ +eprintf("Submitting %u addresses for lookup", nItems); + int rc= ez_getaddrinfo_a(GAI_NOWAIT, S.gai.cbPtrArr, nItems, &sev); + if(rc) + eprintf("returned %d", rc); + assert(0 == rc); + + // TODO: define max timeout on command line + static struct timespec ts; + ms2timespec(&ts, 10*1000); + + /* Pause for parallel DNS lookups */ + for(;;) { + int rc= ez_gai_suspend((const struct gaicb*const*)S.gai.cbPtrArr, nItems, &ts); + switch(rc) { + case 0: + case EAI_INTR: + continue; + + case EAI_ALLDONE: + break; + + default: + eprintf("INFO: gai_suspend() failed, rc= %d [%s]", rc, gai_strerror(rc)); + abort(); + } + break; + } + + eprintf("All done"); + unsigned nSucc= 0, + nFail= 0; + + /* Cancel any ongoing lookups */ + gai_cancel(NULL); + + /* Now check each gaicb object */ + for(unsigned i= 0; i < nItems; ++i) { + + struct gaicb *cb= S.gai.cbArr + i; + int status= gai_error(cb); + static char hostBuf[PATH_MAX]; + + switch(status) { + + case 0: { + ++nSucc; + assert(cb->ar_name && cb->ar_result); + struct addrinfo *ai= cb->ar_result; + assert(ai->ai_addr && ai->ai_addrlen); + + int rc= ez_getnameinfo(ai->ai_addr, ai->ai_addrlen, hostBuf, sizeof(hostBuf)-1, NULL, 0, NI_NAMEREQD); + eprintf("%s= %s", cb->ar_name, rc ? "unknown" : hostBuf); + } break; + + default: + ++nFail; + eprintf("INFO: status= %d [%s]", status, gai_strerror(status)); + continue; + } + + // TODO: use the result + } + eprintf("nItems= %u, nSucc= %u, nFail= %u", nItems, nSucc, nFail); + } /* End of GLB_DNS_LOOKUP_FLG */ /* Process each LOGENTRY item */ for(unsigned i= 0; i < nItems; ++i) { int flags=0; - LOGENTRY *e= leArr[i]; + LOGENTRY *e= S.lePtrArr[i]; if(IPTABLES_is_currently_blocked(e->addr)) flags |= BLOCKED_FLG; @@ -451,13 +557,22 @@ main(int argc, char **argv) /* Print out only for list option */ if(G.flags & GLB_LIST_ADDR_FLG) { + const char *dns_name= NULL; +#if 0 + if(G.flags & GLB_DNS_LOOKUP_FLG) + dns_name= reverse_dns_lookup(e->addr); +#endif - ez_fprintf(G.listing_fh, "%-15s\t%5u/%-4d offenses %s (%s)\n" + const static char *dns_fmt= "%-15s\t%5u/%-4d offenses %s (%s) [%s]\n", + *fmt= "%-15s\t%5u/%-4d offenses %s (%s)\n"; + + ez_fprintf(G.listing_fh, dns_name ? dns_fmt : fmt , e->addr , e->count , nAllowed , e->cntry[0] ? e->cntry : "--" , bits2str(flags, BlockBitTuples) + , dns_name ); } @@ -662,4 +777,33 @@ map_byCountries(LOGENTRY *e, MAP *h_map) return 0; } +static const char* +reverse_dns_lookup(const char *addr) +/************************************************************** + * Do a getaddrinfo() reverse lookup on addr + */ +{ + const char *rtn= NULL; + static char hostBuf[PATH_MAX]; + static struct addrinfo hints, + *res; + memset(&hints, 0, sizeof(hints)); + res= NULL; + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST; /* Only doing reverse lookups */ + + int rc= ez_getaddrinfo(addr, NULL, &hints, &res); + assert(0 == rc); + + assert(res && res->ai_addr && res->ai_addrlen); + + rc= ez_getnameinfo(res->ai_addr, res->ai_addrlen, hostBuf, sizeof(hostBuf)-1, NULL, 0, NI_NAMEREQD); + if(rc) return NULL; + + rtn= hostBuf; + +abort: + if(res) freeaddrinfo(res); + return rtn; +} diff --git a/ban2fail.h b/ban2fail.h index ca7fa5d..6c81503 100644 --- a/ban2fail.h +++ b/ban2fail.h @@ -37,6 +37,7 @@ /* For sizing maps and vectors, this a starting point */ #define N_ADDRESSES_HINT 10000 +#define BUCKET_DEPTH_HINT 10 /* Where to find stuff */ #define CONFIGFILE "/etc/ban2fail/ban2fail.cfg" diff --git a/es.c b/es.c new file mode 100644 index 0000000..7202f2f --- /dev/null +++ b/es.c @@ -0,0 +1,978 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "map.h" +#include "msgqueue.h" +#include "es.h" + +/* Types of registered callbacks */ +enum ES_type { + ES_FD_TYPE, + ES_SIG_TYPE, + ES_VSIG_TYPE, + ES_TIMER_TYPE +}; + +#define NUMSIGS 30 + +#define VSIG_QUEUE_MAX 100 + +/**************************************************** + * We get one of these anonymous structs per-process. + ****************************************************/ +static struct { + pthread_mutex_t mtx; /* global initialization mutex */ + + volatile int keySrc; + + volatile enum { + GLOBAL_INIT_FLG=1<<0 + } flags; + + /* Default sigaction stuff */ + struct { + sigset_t set; + struct sigaction arr[NUMSIGS]; + } dflt_sa; + + struct { /* Stuff for ES_spawn_thread_sched() */ + pthread_cond_t cond; /* Condition used for thread synchronization */ + pthread_mutex_t cond_mtx; /* condition mutex */ + pthread_mutex_t mtx; /* mutex for ES spawn operation */ + int release_parent; /* Value to test for pthread_cond_wait() */ + } spawn; + + struct { + pthread_mutex_t mtx; /* mutex for virtual signal operations */ + MAP thrd_ts_map; /* Map associating thread identifier to TS object */ + } vsig; + +} S= { + .mtx= PTHREAD_MUTEX_INITIALIZER, + + .spawn.cond= PTHREAD_COND_INITIALIZER, + .spawn.cond_mtx= PTHREAD_MUTEX_INITIALIZER, + .spawn.mtx= PTHREAD_MUTEX_INITIALIZER, + + .vsig.mtx= PTHREAD_MUTEX_INITIALIZER +}; + +/**************************************************** + * We get one of these anonymous structs per-thread. + ****************************************************/ +static _Thread_local struct _TS { + + /* Current pthread identifier. If it doesn't match + * pthread_self(), then we are not yet initialized + * in the current thread. + */ + pthread_t tid; + + /* Vectors of Cb by type, for fast processing */ + PTRVEC fd_vec, + timer_vec; + PTRVEC sig_vec_arr[NUMSIGS]; // One vector for each Unix signal + + /* Hash table to quickly find Cb's */ + MAP key_map; + + /* Simple bit field to know if a signal has been + * raised at least once. + */ + sigset_t sigsRaised; + + struct { + /* virtual signal message queue */ + MSGQUEUE mq; + MAP map; + } vsig; + +} TS; + +static void +UnixSignalHandler (int signo) +/**************************************************** + * Unix signal handler + */ +{ + /* Simply note that a signal has been raised */ + sigaddset(&TS.sigsRaised, signo); +} + +/******************************************************************/ +/** Class for callback objects ************************************/ +/******************************************************************/ +typedef struct { + + int64_t lastActivity_ms; + + /* Process-wide unique integer */ + int key; + + /* Which type of callback object */ + enum ES_type type; + + /* Registrant's supplied context pointer, passed back + * into callback function + */ + void *ctxt; + + union { /* Union to accommodate the different callback types */ + + /* Unix file descriptors */ + struct { + int fd; + short events; + int (*callback_f)(void *ctxt, int fd, short events); + } fd; + + /* Unix & virtual signals */ + struct { + int signum; + int (*callback_f)(void *ctxt, int signo); + } sig; + + /* Interval timers */ + struct { + int64_t register_ms, + pause_ms, + interval_ms, + remaining_ms, + count; + int (*callback_f)(void *ctxt); + } timer; + + } un; + +} Cb; + +static int64_t +msec2timeout(const Cb *cb, int64_t time_ms) +/********************************************************************* + * Compute the number of milliseconds remaining for an interval timer. + * May be negative if timeout should have already happened. + */ +{ + assert(ES_TIMER_TYPE == cb->type); + + int64_t when_ms= cb->un.timer.register_ms + + cb->un.timer.pause_ms + + cb->un.timer.count * cb->un.timer.interval_ms; + + return when_ms - time_ms; +} + +#define Cb_FdCreate(self, fd, events, callback_f, ctxt)\ + (Cb_FdConstructor((self)=malloc(sizeof(Cb)), fd, events, callback_f, ctxt) ? (self) : ( self ? realloc(Cb_destructor(self),0): 0)) +static Cb* +Cb_FdConstructor( + Cb *self, + int fd, + short events, + int (*callback_f)(void *ctxt, int fd, short events), + void *ctxt + ) +/********************************************************************* + * Initialize for Unix fd. + */ +{ + assert(self); + self->key= ++S.keySrc; + self->type= ES_FD_TYPE; + self->un.fd.fd= fd; + self->un.fd.callback_f= callback_f; + self->un.fd.events= events; + self->ctxt= ctxt; + return self; +} + + + +#define Cb_SignalCreate(self, signum, callback_f, ctxt)\ + (Cb_SignalConstructor((self)=malloc(sizeof(Cb)), signum, callback_f, ctxt) ? (self) : ( self ? realloc(Cb_destructor(self),0): 0)) +static Cb* +Cb_SignalConstructor( + Cb *self, + int signum, + int (*callback_f)(void *ctxt, int signum), + void *ctxt + ) +/********************************************************************* + * Initialize for Unix signal. + */ +{ + assert(self); + self->key= ++S.keySrc; + self->type= ES_SIG_TYPE; + self->un.sig.signum= signum; + self->un.sig.callback_f= callback_f; + self->ctxt= ctxt; + return self; +} + +#define Cb_VSignalCreate(self, signum, callback_f, ctxt)\ + (Cb_VSignalConstructor((self)=malloc(sizeof(Cb)), signum, callback_f, ctxt) ? (self) : ( self ? realloc(Cb_destructor(self),0): 0)) +static Cb* +Cb_VSignalConstructor( + Cb *self, + int signum, + int (*callback_f)(void *ctxt, int signum), + void *ctxt + ) +/********************************************************************* + * Initialize for Unix signal. + */ +{ + assert(self); + self->key= ++S.keySrc; + self->type= ES_VSIG_TYPE; + self->un.sig.signum= signum; + self->un.sig.callback_f= callback_f; + self->ctxt= ctxt; + return self; +} + +#define Cb_TimerCreate(self, pause_secs, interval_secs, callback_f, ctxt)\ + (Cb_TimerConstructor((self)=malloc(sizeof(Cb)), pause_secs, interval_secs, callback_f, ctxt) ? (self) : ( self ? realloc(Cb_destructor(self),0): 0)) +static Cb* +Cb_TimerConstructor( + Cb *self, + int64_t pause_ms, + int64_t interval_ms, + int (*callback_f)(void *ctxt), + void *ctxt + ) +/********************************************************************* + * Initialize for an interval timer. + */ +{ + assert(self); + self->key= ++S.keySrc; + self->type= ES_TIMER_TYPE; +#if ! (defined (_WIN32) || defined (__CYGWIN__)) + self->un.timer.register_ms= clock_gettime_ms(CLOCK_MONOTONIC_COARSE); +#else + self->un.timer.register_ms= clock_gettime_ms(CLOCK_MONOTONIC); +#endif + self->un.timer.pause_ms= pause_ms; + self->un.timer.interval_ms= interval_ms; + self->un.timer.callback_f= callback_f; + self->un.timer.count= 0; + self->ctxt= ctxt; + return self; +} + +#define Cb_destroy(s)\ + {if(Cb_destructor(s)) {free(s); (s)=NULL;}} +static void* +Cb_destructor(Cb *self) +/************************************************ + * Free resources associated with object. + */ +{ + return self; +} + +/******************************************************************/ +/***************** ES *********************************************/ +/******************************************************************/ + +static int +sigusr2_h(void *ctxt, int unused) +/********************************************************************** + * Handle any vsignals. + */ +{ + int vsigno; + Cb *cb_arr[VSIG_QUEUE_MAX]; + + while(EOF != MSGQUEUE_extractMsg(&TS.vsig.mq, &vsigno)) { + + int rc= MAP_findItems(&TS.vsig.map, (void**)cb_arr, VSIG_QUEUE_MAX, &vsigno, sizeof(int)); + assert(-1 != rc); + if(!rc) continue; + + for(int i= 0; i < rc; ++i) { + Cb *cb= cb_arr[i]; + int error= (* cb->un.sig.callback_f)(cb->ctxt, vsigno); + if(error) return error; + } + } + return 0; +} + +static void +initialize() +/********************************************************************** + * Initialization for current thread, and once for the whole process. + */ +{ + /* Get the global mutex */ + if(pthread_mutex_lock(&S.mtx)) assert (0); + + /* Processwide static data */ + if(!(S.flags & GLOBAL_INIT_FLG)) { + + S.flags |= GLOBAL_INIT_FLG; + if(-1 == sigemptyset(&S.dflt_sa.set)) assert(0); + MAP_constructor(&S.vsig.thrd_ts_map, 10, 10); + } + /* Release the global mutex */ + if(pthread_mutex_unlock(&S.mtx)) assert (0); + + /* Per-thread static data */ + PTRVEC_constructor(&TS.fd_vec, 10); + PTRVEC_constructor(&TS.timer_vec, 10); + for(int i= 0; i < NUMSIGS; ++i) { + PTRVEC_constructor(&TS.sig_vec_arr[i], 10); + } + if(-1 == sigemptyset(&TS.sigsRaised)) assert(0); + + MAP_constructor(&TS.key_map, 10, 10); + + /* Remember so we don't call ourselves more than once in the same thread */ + TS.tid= pthread_self(); + + /* Add ourself to the vsig thread to TS map */ + if(pthread_mutex_lock(&S.vsig.mtx)) assert (0); + MAP_addTypedKey(&S.vsig.thrd_ts_map, TS.tid, &TS); + if(pthread_mutex_unlock(&S.vsig.mtx)) assert (0); + + /*--- virtual signal infrastructure ---*/ + MSGQUEUE_constructor(&TS.vsig.mq, sizeof(int), VSIG_QUEUE_MAX); + MAP_constructor(&TS.vsig.map, 10, 10); + /* Register a signal handler for SIGUSR2 so we can have virtual signals. */ + if(-1 == ES_registerSignal(SIGUSR2, sigusr2_h, NULL)) assert(0); + +} + +inline static unsigned +signum2dflt_sa_ndx(int signum) +/********************************************************************** + * Convert signum to an index for S.dflt_sa.XX + */ +{ + assert(SIGKILL != signum && SIGSTOP != signum); + if(signum < SIGKILL) return signum - 1; + if(signum < SIGSTOP) return signum - 2; + return signum - 3; +} + +int +ES_registerSignal ( + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ) +/********************************************************************** + * Register a function to be called when a particular Unix signal is + * raised. + */ +{ + if(TS.tid != pthread_self()) initialize(); + + Cb *cb; + unsigned ndx= signum2dflt_sa_ndx(signum); + + /* Only install a new Unix signal handler if we do not already handle this signal */ + if(!PTRVEC_numItems(&TS.sig_vec_arr[ndx])) { + + struct sigaction act; + + act.sa_handler = UnixSignalHandler; + sigemptyset (&act.sa_mask); + act.sa_flags = SA_RESTART|SA_NODEFER; + + /* We only store the default action once per process */ + if(!sigismember(&S.dflt_sa.set, signum)) { + + sigaddset(&S.dflt_sa.set, signum); + + if (sigaction (signum, &act, &S.dflt_sa.arr[ndx])) assert (0); + + } else { + + if (sigaction (signum, &act, NULL)) assert (0); + + } + } + + if(!Cb_SignalCreate(cb, signum, callback_f, ctxt)) assert(0); + + /* All callbacks are put in the key table */ + MAP_addTypedKey(&TS.key_map, cb->key, cb); + + + /* Add to the signal vector */ + PTRVEC_addTail(&TS.sig_vec_arr[signum2dflt_sa_ndx(signum)], cb); + + return cb->key; +} + +int +ES_registerVSignal ( + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ) +/********************************************************************** + * Register a function to be called when a particular virtual signal is + * raised. Virtual signals are implemented on top of the Unix signal, SIGUSR2. + * + * signum: Any integer number which is meaningful to your application. + * callback_f: callback function for when activity is detected. + * ctxt: Pointer which will be passed as the last argument to callback_f(). + * + * RETURNS: + * If successful, a positive integer which can be used to unregister the callback. + * On failure, -1 is returned. + */ +{ + Cb *cb; + + if(!Cb_VSignalCreate(cb, signum, callback_f, ctxt)) assert(0); + + /* Place in the virtual signal map indexed on signum */ + MAP_addTypedKey(&TS.vsig.map, cb->un.sig.signum, cb); + + /* All callbacks are put in the key table */ + MAP_addTypedKey(&TS.key_map, cb->key, cb); + + return cb->key; +} + +int +ES_registerFd ( + int fd, + short events, + int (*callback_f)(void *ctxt, int fd, short events), + void *ctxt + ) +/********************************************************************** + * Register a function to be called when there is activity on the + * file descriptor (which may be a file, socket, pipe, etc. under Unix). + */ +{ + if(TS.tid != pthread_self()) initialize(); + + Cb *cb; + if(!Cb_FdCreate(cb, fd, events, callback_f, ctxt)) assert(0); + + /* Index to vector for quick processing */ + PTRVEC_addTail(&TS.fd_vec, cb); + + /* All callbacks are put in the key table */ + MAP_addTypedKey(&TS.key_map, cb->key, cb); + return cb->key; +} + +int +ES_registerTimer ( + int64_t pause_ms, + int64_t interval_ms, + int (*callback_f)(void *ctxt), + void *ctxt + ) +/********************************************************************** + * Register a function to be called when a timer times out. + */ +{ + if(TS.tid != pthread_self()) initialize(); + + Cb *cb; + if(!Cb_TimerCreate(cb, pause_ms, interval_ms, callback_f, ctxt)) assert(0); + + /* Add to the timer vector */ + PTRVEC_addTail(&TS.timer_vec, cb); + + /* All callbacks are put in the key table */ + MAP_addTypedKey(&TS.key_map, cb->key, cb); + + return cb->key; +} + +int +ES_unregister (int key) +/********************************************************************** + * Unegister a previously registered callback. + */ +{ + if(TS.tid != pthread_self()) initialize(); + +// unsigned i; + Cb *cb = MAP_findTypedItem(&TS.key_map, key); + if(!cb) return -1; + + /* Remove from key table */ + if(!MAP_removeTypedItem(&TS.key_map, key)) assert(0);; + + /* Different operations needed based on type */ + switch(cb->type) { + + case ES_FD_TYPE: + /* Remove from file descriptor vector */ + if(!PTRVEC_remove(&TS.fd_vec, cb)) assert(0); + break; + + case ES_SIG_TYPE: + { + unsigned ndx= signum2dflt_sa_ndx(cb->un.sig.signum); + + /* Remove from appropriate signals vector */ + if(!PTRVEC_remove(&TS.sig_vec_arr[ndx], cb)) assert(0); + + /* If there are no more signals in this vector */ + if(!PTRVEC_numItems(&TS.sig_vec_arr[ndx])) { + + assert(sigismember(&S.dflt_sa.set, cb->un.sig.signum)); + + /* Restore default signal handling */ + if (sigaction (cb->un.sig.signum, &S.dflt_sa.arr[ndx], NULL)) assert (0); + } + } break; + + case ES_VSIG_TYPE: + if(!MAP_removeSpecificTypedItem(&TS.vsig.map, cb->un.sig.signum, cb)) assert(0); + break; + + case ES_TIMER_TYPE: + /* Remove from timer vector */ + if(!PTRVEC_remove(&TS.timer_vec, cb)) assert(0); + break; + + default: + assert(0); + }; + + /* Free the callback object */ + Cb_destroy(cb); + + return 0; +} + +static int +cmp_remaining_ms (const void *const*p1, const void *const*p2) +/********************************************************************** + * Compare the time remaining for PTRVEC_sort(). + */ +{ + const Cb *cb1= (const Cb*)(*p1), + *cb2= (const Cb*)(*p2); + + assert(ES_TIMER_TYPE == cb1->type && + ES_TIMER_TYPE == cb2->type); + + if(cb1->un.timer.remaining_ms < cb2->un.timer.remaining_ms) return -1; + if(cb1->un.timer.remaining_ms == cb2->un.timer.remaining_ms) return 0; + return 1; +} + +static int +lastActivity_cmp(const void *const* pp1, const void *const* pp2) +{ + const Cb *cb1= *(const Cb *const*)pp1, + *cb2= *(const Cb *const*)pp2; + + /* Put oldest at the top of the vector */ + if(cb1->lastActivity_ms < cb2->lastActivity_ms) return -1; + return 1; +} + +int +ES_run (void) +/********************************************************************** + * For this thread, use poll() to monitor socket activity until one + * of the registered callback_f() returns non-zero. + * + * RETURNS: + * Whatever nonzero value one of the callbacks() returned. + */ +{ + + if(TS.tid != pthread_self()) + initialize(); + + /* Loop forever */ + for(;;) { + + int numFds= PTRVEC_numItems(&TS.fd_vec); + struct pollfd pollItemArr[numFds]; + Cb *cbArr[numFds]; + + + /* This sort provides fair queuing */ + PTRVEC_sort(&TS.fd_vec, lastActivity_cmp); + + /****** Load up the ZeroMQ pollItemArr *****/ + unsigned i; + Cb *cb; + PTRVEC_loopFwd(&TS.fd_vec, i, cb) { + + struct pollfd *item= pollItemArr+i; + + switch(cb->type) { + + case ES_FD_TYPE: + item->fd= cb->un.fd.fd; + item->events= cb->un.fd.events; + break; + + default: + assert(0); + } + + /* Clear the return event field for good measure */ + item->revents= 0; + /* Remember the Cb object */ + cbArr[i]= cb; + } + + /* There may not be any timers */ + int64_t poll_ms= -1; + + /***** If there are any timers to consider ****/ + if(PTRVEC_numItems(&TS.timer_vec)) { + +#if ! (defined (_WIN32) || defined (__CYGWIN__)) + int64_t time_ms= clock_gettime_ms(CLOCK_MONOTONIC_COARSE); +#else + int64_t time_ms= clock_gettime_ms(CLOCK_MONOTONIC); +#endif + + /* Prepare timers to be sorted */ + unsigned i; + PTRVEC_loopFwd(&TS.timer_vec, i, cb) { + assert(ES_TIMER_TYPE == cb->type); + cb->un.timer.remaining_ms= msec2timeout(cb, time_ms); + } + + /* Sort them so the most urgent timer is at the top */ + PTRVEC_sort(&TS.timer_vec, cmp_remaining_ms); + + /* Get the top item */ + cb= PTRVEC_first(&TS.timer_vec); + assert(cb); + + /* This is how long we need to wait */ + poll_ms= MAX(cb->un.timer.remaining_ms, 0); + + } + + /*******************************************************/ + /******** Wait for something to happen *****************/ + /*******************************************************/ + + int poll_rc= poll(pollItemArr, numFds, poll_ms); + + + /********* Check return code *****/ + if(-1 == poll_rc) { + switch(errno) { + + case EFAULT: + eprintf("\tpoll() failed"); + return -1; + + case EINTR: + /* Signal caused poll() to return, which is OK */ + break; + + default: + assert(0); + } + } + + /********* Respond to signals *****/ + int signum; + for(signum= 1; signum < 32; ++signum) { + /* Can't do anything with these signals */ + if(signum == SIGKILL || signum == SIGSTOP) continue; + + /* See if signum was raised */ + while(sigismember(&TS.sigsRaised, signum)) { + + /* Clear signum from the set of raised signals */ + if(-1 == sigdelset(&TS.sigsRaised, signum)) assert(0); + + unsigned ndx= signum2dflt_sa_ndx(signum); + + /* See if any of our callbacks are for signum */ + PTRVEC_loopFwd(&TS.sig_vec_arr[ndx], i, cb) { + + assert(ES_SIG_TYPE == cb->type); + + /* Call the callback function */ + int error= (* cb->un.sig.callback_f)(cb->ctxt, signum); + + if(error) return error; + } + } + } + + /********* Service timers ********/ + if(PTRVEC_numItems(&TS.timer_vec)) { + + int64_t remaining_ms, + time_ms; + +#if ! (defined (_WIN32) || defined (__CYGWIN__)) + time_ms= clock_gettime_ms(CLOCK_MONOTONIC_COARSE); +#else + time_ms= clock_gettime_ms(CLOCK_MONOTONIC); +#endif + + PTRVEC_loopFwd(&TS.timer_vec, i, cb) { + + /* See how much time remains for this callback */ + remaining_ms= msec2timeout(cb, time_ms); + + /* close enough */ + if(remaining_ms < 2) { + + /* Keep track of how many times this timer has fired */ + ++cb->un.timer.count; + + /* Call the callback function */ + int error= (* cb->un.timer.callback_f)(cb->ctxt); + + /* If this is a single-shot timer, get rid of it now */ + if(!cb->un.timer.interval_ms) { + ES_unregister(cb->key); + /* Do this so next vector entry doesn't get skipped */ + --i; + } + + /* If the callback returned non-zero, bail out */ + if(error) return error; + + } else break; /* time remaining will increase from here on out */ + } + } + + + /********** Service file descriptors *******/ + for(int i= 0; i < numFds; ++i) { + + struct pollfd *item= pollItemArr+i; + + if(!item->revents) continue; + + Cb *cb= cbArr[i]; + +#if ! (defined (_WIN32) || defined (__CYGWIN__)) + cb->lastActivity_ms= clock_gettime_ms(CLOCK_MONOTONIC_COARSE); +#else + cb->lastActivity_ms= clock_gettime_ms(CLOCK_MONOTONIC); +#endif + + int error; + switch(cb->type) { + + case ES_FD_TYPE: + /* Call the callback function */ + error= (* cb->un.fd.callback_f)(cb->ctxt, item->fd, item->revents); + break; + + default: + assert(0); + } + + /* If the callback returned non-zero, bail out */ + if(error) return error; + } + } + + /* Shouldn't ever get to here */ + assert(0); +} + +pthread_t +ES_spawn_thread_sched( + void *(*user_main) (void *), + void *arg, + int sched_policy, /* SCHED_NORMAL || SCHED_FIFO || SCHED_RR || SCHED_BATCH */ + int priority + ) +/********************************************************************** + * Spawn a thread which will begin executing user_main(arg). + * NOTE: the calling thread will be blocked until ES_release_parent() + * is called from user_main()! + * + * user_main: function pointer where thread will execute. + * arg: address passed to user_main(arg). + * sched_policy: Which pthreads scheduling policy to use. + * priority: pthreads priority to use. + * + * RETURNS: + * 0 for success, nonzero for error + */ +{ + pthread_t tid; + pthread_attr_t attr; + int rtn; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + if(sched_policy == -1) { + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + } else { + struct sched_param sp; + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + if(priority < sched_get_priority_min(sched_policy) || + priority > sched_get_priority_max(sched_policy)) { + eprintf("ERROR: priority= %d must be between %d and %d inclusive.", priority, sched_get_priority_min(sched_policy), sched_get_priority_max(sched_policy)); + return 1; + } + + if(pthread_attr_setschedpolicy(&attr, sched_policy)) assert(0); + sp.sched_priority= priority; + if(pthread_attr_setschedparam(&attr, &sp)) assert(0); + } + + /* Get the global mutex */ + if(pthread_mutex_lock(&S.spawn.mtx)) assert (0); + + /* Get the condition ready for use */ + pthread_cond_init(&S.spawn.cond, NULL); + + /* This is the flag we will test to know when the child is ready to recieve signals */ + S.spawn.release_parent= 0; + + /* Get the condition mutex */ + if(pthread_mutex_lock(&S.spawn.cond_mtx)) assert (0); + + /* Spawn the new thread */ + rtn = pthread_create (&tid, &attr, user_main, arg); + + /* Now we, the parent, wait on the child */ + while(!S.spawn.release_parent) { + if(pthread_cond_wait(&S.spawn.cond, &S.spawn.cond_mtx)) assert(0); + } + + /* Release the condition mutex */ + if(pthread_mutex_unlock(&S.spawn.cond_mtx)) assert (0); + + /* Release the global lock */ + if(pthread_mutex_unlock(&S.spawn.mtx)) assert (0); + + return tid; +} + +void +ES_release_parent(void) +/********************************************************************** + * Called by a new thread created with ES_spawn_thread_sched(), so + * that the parent can continue execution. + */ +{ + /* Condition manipulation must be protected by a mutex */ + if (pthread_mutex_lock (&S.spawn.cond_mtx)) assert (0); + + /* Note that parent may be released */ + S.spawn.release_parent= 1; + + /* Signal the parent */ + if(pthread_cond_signal(&S.spawn.cond)) assert(0); + + /* Free up the condition mutex */ + if (pthread_mutex_unlock (&S.spawn.cond_mtx)) assert (0); + +} + +void +ES_cleanup(void) +/********************************************************************** + * Called by a thread when it exits, to clean up resources. + */ +{ + assert(TS.tid == pthread_self()); + + /* Remove ourself from the vsig thread to TS map */ + if(pthread_mutex_lock(&S.vsig.mtx)) assert (0); + MAP_removeTypedItem(&S.vsig.thrd_ts_map, TS.tid); + if(pthread_mutex_unlock(&S.vsig.mtx)) assert (0); + + { /* Destroy key map */ + unsigned len= MAP_numItems(&TS.key_map); + Cb *cbArr[len]; + MAP_fetchAllItems(&TS.key_map, (void**)cbArr); + for(unsigned i= 0; i < len; ++i) { + Cb_destroy(cbArr[i]); + } + + MAP_destructor(&TS.key_map); + } + + { /* Destroy vsignal infrastructure */ + unsigned len= MAP_numItems(&TS.vsig.map); + Cb *cbArr[len]; + MAP_fetchAllItems(&TS.vsig.map, (void**)cbArr); + for(unsigned i= 0; i < len; ++i) { + Cb_destroy(cbArr[i]); + } + MAP_destructor(&TS.vsig.map); + + /* Tear down the message queue */ + MSGQUEUE_destructor(&TS.vsig.mq); + } + + PTRVEC_destructor(&TS.fd_vec); + PTRVEC_destructor(&TS.timer_vec); + + for(unsigned i= 0; i < NUMSIGS; ++i) { + PTRVEC_destructor(TS.sig_vec_arr+i); + } +} + +int +ES_VSignal (pthread_t tid, int signum) +/********************************************************************** + * Send a virtual signal to tid by placing signum in a mutex protected + * message queue, and then call pthread_kill(tid, SIGHUP) to notify the thread. + * + * tid: Target thread identifier. + * signum: Any integer number which is meaningful to your application. + * + * RETURNS: + * 0: successful + * EOF: the message queue is full + * -1: All other failures. + */ +{ + int rtn= EOF-1; + /* find the correct TS by thread identifier */ + if(pthread_mutex_lock(&S.vsig.mtx)) assert (0); + struct _TS *ts= MAP_findTypedItem(&S.vsig.thrd_ts_map, tid); + if(pthread_mutex_unlock(&S.vsig.mtx)) assert (0); + + if(!ts) { + eprintf("ERROR: tid= %s not found!", pthread_t_str(tid)); + goto abort; + } + + assert(tid == ts->tid); + + /* Place virtual signal in message queue */ + int rc= MSGQUEUE_submitMsg(&ts->vsig.mq, &signum); + if(rc) { + rtn= EOF; + goto abort; + } + + /* And finally tell the target thread to check it's message queue */ + if(pthread_kill(tid, SIGUSR2)) { + sys_eprintf("ERROR: kill(%s, SIGUSR2)", pthread_t_str(tid)); + goto abort; + } + + rtn= 0; +abort: + return rtn; +} diff --git a/es.h b/es.h new file mode 100644 index 0000000..ec8b0a6 --- /dev/null +++ b/es.h @@ -0,0 +1,186 @@ +#ifndef ES_H +#define ES_H + +/**************************************************************************************** +* ES is short for "Event Server". This is a per-thread event server +* for multiplexing sockets, regular file descriptors, Unix signals, and interval +* timers. +****************************************************************************************/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int +ES_registerFd ( + int fd, + short events, + int (*callback_f)(void *ctxt, int fd, short events), + void *ctxt + ); +/********************************************************************** + * Register a function to be called when there is activity on the + * file descriptor (which may be a file, socket, pipe, etc. under Unix). + * + * fd: the file descriptor to be registered. + * events: event bits to monitor (see: man 2 poll). + * callback_f: callback function for when activity is detected. + * ctxt: Pointer which will be passed as the last argument to callback_f(). + * + * RETURNS: + * If successful, a positive integer which can be used to unregister the callback. + * On failure, -1 is returned. + */ + +int +ES_registerSignal ( + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ); +/********************************************************************** + * Register a function to be called when a particular Unix signal is + * raised. Note: callback_f() is not called from a Unix signal handler, + * so it is safe to modify data within callback_f(). + * + * signum: Unix signal number of interest (type "trap -l" on command line to see a list of signals) + * callback_f: callback function for when activity is detected. + * ctxt: Pointer which will be passed as the last argument to callback_f(). + * + * RETURNS: + * If successful, a positive integer which can be used to unregister the callback. + * On failure, -1 is returned. + */ + +int +ES_registerVSignal ( + int signum, + int (*callback_f)(void *ctxt,int signo), + void *ctxt + ); +/********************************************************************** + * Register a function to be called when a particular virtual signal is + * raised. Virtual signals are implemented on top of the Unix signal, SIGUSR2. + * + * signum: Any integer number which is meaningful to your application. + * callback_f: callback function for when activity is detected. + * ctxt: Pointer which will be passed as the last argument to callback_f(). + * + * RETURNS: + * If successful, a positive integer which can be used to unregister the callback. + * On failure, -1 is returned. + */ + +int +ES_VSignal (pthread_t tid, int signum); +/********************************************************************** + * Send a virtual signal to tid by placing signum in a mutex protected + * message queue, and then call pthread_kill(tid, SIGHUP) to notify the thread. + * + * tid: Target thread identifier. + * signum: Any integer number which is meaningful to your application. + * + * RETURNS: + * 0: successful + * EOF: the message queue is full + * -1: All other failures. + */ + +int +ES_registerTimer ( + int64_t pause_ms, + int64_t interval_ms, + int (*callback_f)(void *ctxt), + void *ctxt + ); +/********************************************************************** + * Register a function to be called when an interval or single-shot + * timer expires. If interval_ms == 0, the timer is single shot, and + * can only be ES_unregister()'d before it expires. + * + * pause_ms: How many milliseconds to wait before initially firing. + * interval_ms: How many milliseconds to wait between successive firings. + * if this is 0, the timer is single shot. + * callback_f: callback function for when timer expires. + * ctxt: Pointer which will be passed as the last argument to callback_f(). + * + * RETURNS: + * If successful, a positive integer which can be used to unregister the callback. + * On failure, -1 is returned. + */ + + + +int +ES_unregister (int key); +/********************************************************************** + * Unegister a previously registered callback. + * + * key: value obtained from ES_registerXXX(). + * + * RETURNS: + * 0 for success. + * -1 for failure (key not found) + */ + + +int +ES_run (void); +/********************************************************************** + * For this thread, use poll() to process socket activity until one + * of the registered callback_f() returns non-zero. + * + * RETURNS: + * Whatever nonzero value callback_f() returned. + */ + +pthread_t +ES_spawn_thread_sched( + void *(*user_main) (void *), + void *arg, + int sched_policy, /* SCHED_NORMAL || SCHED_FIFO || SCHED_RR || SCHED_BATCH */ + int priority + ); +/********************************************************************** + * Spawn a thread which will begin executing user_main(arg). + * NOTE: the calling thread will be blocked until ES_release_parent() + * is called from user_main()! + * + * user_main: function pointer where thread will execute. + * arg: address passed to user_main(arg). + * sched_policy: Which pthreads scheduling policy to use. + * priority: pthreads priority to use. + * + * RETURNS: + * 0 for success, nonzero for error + */ + +#define ES_spawn_thread(user_main, arg) \ + ES_spawn_thread_sched(user_main, arg, -1, 0) +/********************************************************************** + * This is a convenience macro. + */ + +void +ES_release_parent(void); +/********************************************************************** + * Called by a new thread created with ES_spawn_thread_sched(), so + * that the parent can continue execution. + */ + +void +ES_cleanup(void); +/********************************************************************** + * Called by a thread when it exits, to clean up resources. + */ + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/ez_es.c b/ez_es.c new file mode 100644 index 0000000..cb1b3e5 --- /dev/null +++ b/ez_es.c @@ -0,0 +1,129 @@ +#include + +#include "util.h" +#include "ez_es.h" + +/***************************************************/ +int _ez_ES_registerFd ( + const char *fileName, + int lineNo, + const char *funcName, + int fd, + short events, + int (*callback_f)(void *ctxt, int fd, short events), + void *ctxt + ) +{ + int rtn= ES_registerFd(fd, events, callback_f, ctxt); + if(-1 == rtn) { + _eprintf(fileName, lineNo, funcName, "ES_registerFd() failed."); + abort(); + } + return rtn; +} + +/***************************************************/ + +int _ez_ES_registerSignal ( + const char *fileName, + int lineNo, + const char *funcName, + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ) +{ + int rtn= ES_registerSignal(signum, callback_f, ctxt); + if(-1 == rtn) { + _eprintf(fileName, lineNo, funcName, "ES_registerSignal() failed."); + abort(); + } + return rtn; +} + +/***************************************************/ + +int _ez_ES_registerVSignal ( + const char *fileName, + int lineNo, + const char *funcName, + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ) +{ + int rtn= ES_registerVSignal(signum, callback_f, ctxt); + if(-1 == rtn) { + _eprintf(fileName, lineNo, funcName, "ES_registerVSignal() failed."); + abort(); + } + return rtn; +} + +/***************************************************/ +int _ez_ES_VSignal ( + const char *fileName, + int lineNo, + const char *funcName, + pthread_t tid, + int signum + ) +{ + int rtn= ES_VSignal(tid, signum); + if(rtn) { + _eprintf(fileName, lineNo, funcName, "ES_VSignal() returned %d.", rtn); + abort(); + } + return rtn; +} + +/***************************************************/ +int _ez_ES_registerTimer ( + const char *fileName, + int lineNo, + const char *funcName, + int64_t pause_ms, + int64_t interval_ms, + int (*callback_f)(void *ctxt), + void *ctxt + ) +{ + int rtn= ES_registerTimer(pause_ms, interval_ms, callback_f, ctxt); + if(-1 == rtn) { + _eprintf(fileName, lineNo, funcName, "ES_registerTimer() failed."); + abort(); + } + return rtn; +} + +/***************************************************/ +int _ez_ES_unregister ( + const char *fileName, + int lineNo, + const char *funcName, + int key + ) +{ + int rtn= ES_unregister(key); + if(-1 == rtn) { + _eprintf(fileName, lineNo, funcName, "ES_unregister() failed."); + abort(); + } + return rtn; +} + +/***************************************************/ +int _ez_ES_run ( + const char *fileName, + int lineNo, + const char *funcName + ) +{ + int rtn= ES_run(); + if(rtn) { + _eprintf(fileName, lineNo, funcName, "ES_run() returned %d", rtn); + abort(); + } + return rtn; +} + diff --git a/ez_es.h b/ez_es.h new file mode 100644 index 0000000..413b8ea --- /dev/null +++ b/ez_es.h @@ -0,0 +1,95 @@ +/**************************************************************************************************************************** + * NOTE: look in "es.h" for function documentation. The ez_XXX() macros here only wrap the ES_XXX() functions + * for the purpose of boilerplate error handling. + */ + +#ifndef EZ_ES_H +#define EZ_ES_H + +#include "es.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ez_ES_registerFd(fd, events, callback_f, ctxt) \ + _ez_ES_registerFd(__FILE__, __LINE__, __FUNCTION__, fd, events, callback_f, ctxt) +int _ez_ES_registerFd ( + const char *fileName, + int lineNo, + const char *funcName, + int fd, + short events, + int (*callback_f)(void *ctxt, int fd, short events), + void *ctxt + ); + + + +#define ez_ES_registerSignal(signum, callback_f, ctxt) \ + _ez_ES_registerSignal(__FILE__, __LINE__, __FUNCTION__, signum, callback_f, ctxt) +int _ez_ES_registerSignal ( + const char *fileName, + int lineNo, + const char *funcName, + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ); + +#define ez_ES_registerVSignal(signum, callback_f, ctxt) \ + _ez_ES_registerVSignal(__FILE__, __LINE__, __FUNCTION__, signum, callback_f, ctxt) +int _ez_ES_registerVSignal ( + const char *fileName, + int lineNo, + const char *funcName, + int signum, + int (*callback_f)(void *ctxt, int signo), + void *ctxt + ); + +#define ez_ES_VSignal(tid, signum) \ + _ez_ES_VSignal(__FILE__, __LINE__, __FUNCTION__, tid, signum) +int _ez_ES_VSignal ( + const char *fileName, + int lineNo, + const char *funcName, + pthread_t tid, + int signum + ); + +#define ez_ES_registerTimer(pause_ms, interval_ms, callback_f, ctxt) \ + _ez_ES_registerTimer(__FILE__, __LINE__, __FUNCTION__, pause_ms, interval_ms, callback_f, ctxt) +int _ez_ES_registerTimer ( + const char *fileName, + int lineNo, + const char *funcName, + int64_t pause_ms, + int64_t interval_ms, + int (*callback_f)(void *ctxt), + void *ctxt + ); + + +#define ez_ES_unregister(key) \ + {_ez_ES_unregister(__FILE__, __LINE__, __FUNCTION__, key); (key)= 0;} +int _ez_ES_unregister ( + const char *fileName, + int lineNo, + const char *funcName, + int key + ); + +#define ez_ES_run() \ + _ez_ES_run(__FILE__, __LINE__, __FUNCTION__) +int _ez_ES_run ( + const char *fileName, + int lineNo, + const char *funcName + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ez_libanl.c b/ez_libanl.c new file mode 100644 index 0000000..3ee8089 --- /dev/null +++ b/ez_libanl.c @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2019 by John D. Robertson * + * john@rrci.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#define _GNU_SOURCE +#include + +#include "util.h" +#include "ez_libanl.h" + +/***************************************************/ +int _ez_getaddrinfo_a( + const char *fileName, + int lineNo, + const char *funcName, + int mode, + struct gaicb *list[], + int nitems, + struct sigevent *sevp + ) +{ + errno= 0; + int rtn= getaddrinfo_a (mode, list, nitems, sevp); + switch(rtn) { + case 0: + case EAI_AGAIN: + return rtn; + } + + /* _sys_eprintf() will pass errno to gai_sterror */ + errno= rtn; + _sys_eprintf(gai_strerror, fileName, lineNo, funcName, "getaddrinfo_a() failed"); + abort(); +} + +/***************************************************/ +int _ez_gai_suspend( + const char *fileName, + int lineNo, + const char *funcName, + const struct gaicb * const list[], + int nitems, + const struct timespec *timeout + ) +{ + errno= 0; + int rtn= gai_suspend (list, nitems, timeout); + switch(rtn) { + case 0: + case EAI_AGAIN: + case EAI_ALLDONE: + case EAI_INTR: + return rtn; + } + + /* _sys_eprintf() will pass errno to gai_sterror */ + errno= rtn; + _sys_eprintf(gai_strerror, fileName, lineNo, funcName, "gai_suspend() failed"); + abort(); +} diff --git a/ez_libanl.h b/ez_libanl.h new file mode 100644 index 0000000..ea8727b --- /dev/null +++ b/ez_libanl.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2019 by John D. Robertson * + * john@rrci.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef EZ_LIBANL_H +#define EZ_LIBANL_H + +/* Simplified interface to libanl functions */ +#define _GNU_SOURCE +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ez_getaddrinfo_a(mode, list, nItems, sevp) \ + _ez_getaddrinfo_a(__FILE__, __LINE__, __FUNCTION__, mode, list, nItems, sevp) +int _ez_getaddrinfo_a( + const char *fileName, + int lineNo, + const char *funcName, + int mode, + struct gaicb *list[], + int nitems, + struct sigevent *sevp + ); + +#define ez_gai_suspend(list, nItems, timeout) \ + _ez_gai_suspend(__FILE__, __LINE__, __FUNCTION__, list, nItems, timeout) +int _ez_gai_suspend( + const char *fileName, + int lineNo, + const char *funcName, + const struct gaicb * const list[], + int nitems, + const struct timespec *timeout + ); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/ez_libc.c b/ez_libc.c index b8062ad..6033d7a 100644 --- a/ez_libc.c +++ b/ez_libc.c @@ -16,6 +16,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define _GNU_SOURCE #include #include @@ -388,3 +389,71 @@ int _ez_unlink ( } return rtn; } + +/***************************************************/ +int _ez_getaddrinfo( + const char *fileName, + int lineNo, + const char *funcName, + const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo **res + ) +{ + errno= 0; + int rtn= getaddrinfo (node, service, hints, res); + switch(rtn) { + case 0: + case EAI_AGAIN: + case EAI_FAIL: +#ifdef EAI_NODATA + case EAI_NODATA: +#endif + return rtn; + + case EAI_SYSTEM: + _sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "getaddrinfo(\"%s:%s\") failed", node, service); + abort(); + } + + /* _sys_eprintf() will pass errno to gai_sterror */ + errno= rtn; + _sys_eprintf(gai_strerror, fileName, lineNo, funcName, "getaddrinfo(\"%s:%s\") failed", node, service); + abort(); +} + +/***************************************************/ +int _ez_getnameinfo( + const char *fileName, + int lineNo, + const char *funcName, + const struct sockaddr *addr, + socklen_t addrlen, + char *host, + socklen_t hostlen, + char *serv, + socklen_t servlen, + int flags + ) +{ + errno= 0; + int rtn= getnameinfo (addr, addrlen, host, hostlen, serv, servlen, flags); + switch(rtn) { + case 0: + case EAI_AGAIN: + case EAI_FAIL: + case EAI_NONAME: + return rtn; + + case EAI_SYSTEM: + _sys_eprintf((const char*(*)(int))strerror, fileName, lineNo, funcName, "getnameinfo() failed"); + abort(); + } + + /* _sys_eprintf() will pass errno to gai_sterror */ + errno= rtn; + _sys_eprintf(gai_strerror, fileName, lineNo, funcName, "getnameinfo() failed", rtn); + abort(); +} + diff --git a/ez_libc.h b/ez_libc.h index 13f2df7..42d8461 100644 --- a/ez_libc.h +++ b/ez_libc.h @@ -19,7 +19,7 @@ ***************************************************************************/ /*************************************************************************** ez_libc.h - description -libc calls with boilerplate error handling. +glibc calls with boilerplate error handling. ------------------- begin : Tue Nov 13 19:42:23 EST 2018 @@ -28,12 +28,14 @@ libc calls with boilerplate error handling. #ifndef EZ_LIBC_H #define EZ_LIBC_H +#define _GNU_SOURCE #include +#include #include #include +#include #include #include -#include #ifdef __cplusplus extern "C" { @@ -43,9 +45,9 @@ extern "C" { #define ez_fputs(s, stream) \ _ez_fputs(__FILE__, __LINE__, __FUNCTION__, s, stream) int _ez_fputs ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *s, FILE *stream ); @@ -53,9 +55,9 @@ int _ez_fputs ( #define ez_fputc(c, stream) \ _ez_fputc(__FILE__, __LINE__, __FUNCTION__, c, stream) int _ez_fputc ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, int c, FILE *stream ); @@ -63,9 +65,9 @@ int _ez_fputc ( #define ez_fprintf(stream, fmt, ...) \ _ez_fprintf(__FILE__, __LINE__, __FUNCTION__, stream, fmt, ##__VA_ARGS__) int _ez_fprintf ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, FILE *stream, const char *fmt, ... @@ -74,9 +76,9 @@ int _ez_fprintf ( #define ez_popen(command, type) \ _ez_popen(__FILE__, __LINE__, __FUNCTION__, command, type) FILE* _ez_popen ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *command, const char *type ); @@ -84,9 +86,9 @@ FILE* _ez_popen ( #define ez_fopen(pathname, mode) \ _ez_fopen(__FILE__, __LINE__, __FUNCTION__, pathname, mode) FILE* _ez_fopen ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *pathname, const char *mode ); @@ -94,18 +96,18 @@ FILE* _ez_fopen ( #define ez_fclose(stream) \ _ez_fclose(__FILE__, __LINE__, __FUNCTION__, stream) int _ez_fclose ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, FILE *stream ); #define ez_fread(ptr, size, nmemb, stream) \ _ez_fread(__FILE__, __LINE__, __FUNCTION__, ptr, size, nmemb, stream) size_t _ez_fread( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, void *ptr, size_t size, size_t nmemb, @@ -115,9 +117,9 @@ size_t _ez_fread( #define ez_fwrite(ptr, size, nmemb, stream) \ _ez_fwrite(__FILE__, __LINE__, __FUNCTION__, ptr, size, nmemb, stream) size_t _ez_fwrite( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const void *ptr, size_t size, size_t nmemb, @@ -128,18 +130,18 @@ size_t _ez_fwrite( #define ez_pclose(stream) \ _ez_pclose(__FILE__, __LINE__, __FUNCTION__, stream) int _ez_pclose ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, FILE *stream ); #define ez_fgets(s, size, stream) \ _ez_fgets(__FILE__, __LINE__, __FUNCTION__, s, size, stream) char* _ez_fgets ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, char *s, int size, FILE *stream @@ -148,18 +150,18 @@ char* _ez_fgets ( #define ez_remove(pathname) \ _ez_remove(__FILE__, __LINE__, __FUNCTION__, pathname) int _ez_remove ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *pathname ); #define ez_rename(oldpath, newpath) \ _ez_rename(__FILE__, __LINE__, __FUNCTION__, oldpath, newpath) int _ez_rename ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *oldpath, const char *newpath ); @@ -167,36 +169,36 @@ int _ez_rename ( #define ez_opendir(name) \ _ez_opendir(__FILE__, __LINE__, __FUNCTION__, name) DIR* _ez_opendir ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *name ); #define ez_closedir(dirp) \ _ez_closedir(__FILE__, __LINE__, __FUNCTION__, dirp) int _ez_closedir ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, DIR *dirp ); #define ez_readdir(dirp) \ _ez_readdir(__FILE__, __LINE__, __FUNCTION__, dirp) struct dirent* _ez_readdir ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, DIR *dirp ); #define ez_close(fd) \ _ez_close(__FILE__, __LINE__, __FUNCTION__, fd) int _ez_close ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, int fd ); @@ -217,9 +219,9 @@ ssize_t _ez_read ( #define ez_write(fd, buf, count) \ _ez_write(__FILE__, __LINE__, __FUNCTION__, fd, buf, count) ssize_t _ez_write ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, int fd, const void *buf, size_t count @@ -228,10 +230,10 @@ ssize_t _ez_write ( #define ez_stat(pathname, statbuf) \ _ez_stat(__FILE__, __LINE__, __FUNCTION__, pathname, statbuf) int _ez_stat ( - const char *fileName, - int lineNo, - const char *funcName, - const char *pathname, + const char *fileName, + int lineNo, + const char *funcName, + const char *pathname, struct stat *statbuf ); @@ -239,9 +241,9 @@ int _ez_stat ( #define ez_mkdir(pathname, mode) \ _ez_mkdir(__FILE__, __LINE__, __FUNCTION__, pathname, mode) int _ez_mkdir ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *pathname, mode_t mode ); @@ -249,21 +251,57 @@ int _ez_mkdir ( #define ez_rmdir(pathname) \ _ez_rmdir(__FILE__, __LINE__, __FUNCTION__, pathname) int _ez_rmdir ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *pathname ); #define ez_unlink(pathname) \ _ez_unlink(__FILE__, __LINE__, __FUNCTION__, pathname) int _ez_unlink ( - const char *fileName, - int lineNo, - const char *funcName, + const char *fileName, + int lineNo, + const char *funcName, const char *pathname ); +#define ez_unlink(pathname) \ + _ez_unlink(__FILE__, __LINE__, __FUNCTION__, pathname) +int _ez_unlink ( + const char *fileName, + int lineNo, + const char *funcName, + const char *pathname + ); + +#define ez_getaddrinfo(node, service, hints, res) \ + _ez_getaddrinfo(__FILE__, __LINE__, __FUNCTION__, node, service, hints, res) +int _ez_getaddrinfo( + const char *fileName, + int lineNo, + const char *funcName, + const char *node, + const char *service, + const struct addrinfo *hints, + struct addrinfo **res + ); + +#define ez_getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags) \ + _ez_getnameinfo(__FILE__, __LINE__, __FUNCTION__, addr, addrlen, host, hostlen, serv, servlen, flags) +int _ez_getnameinfo( + const char *fileName, + int lineNo, + const char *funcName, + const struct sockaddr *addr, + socklen_t addrlen, + char *host, + socklen_t hostlen, + char *serv, + socklen_t servlen, + int flags + ); + #ifdef __cplusplus } #endif diff --git a/iptables.c b/iptables.c index 081fb74..3262045 100644 --- a/iptables.c +++ b/iptables.c @@ -16,6 +16,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define _GNU_SOURCE #include #include #include @@ -44,7 +45,7 @@ initialize (void) { S.is_init= 1; - MAP_constructor(&S.addr_map, N_ADDRESSES_HINT/10, 10); + MAP_constructor(&S.addr_map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT); const static struct ipv { const char *cmd, @@ -71,7 +72,7 @@ initialize (void) if(regex_compile(&re, ipv->pattern, REG_EXTENDED)) { eprintf("ERROR: regex_compile(\"%s\") failed.", ipv->pattern); - exit(1); + exit(EXIT_FAILURE); } fh= ez_popen(ipv->cmd, "r"); diff --git a/logEntry.c b/logEntry.c index bb37712..83da3f3 100644 --- a/logEntry.c +++ b/logEntry.c @@ -16,6 +16,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define _GNU_SOURCE #include #include #include diff --git a/logFile.c b/logFile.c index f3e3183..e6cda1e 100644 --- a/logFile.c +++ b/logFile.c @@ -16,6 +16,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define _GNU_SOURCE #include #include #include @@ -47,7 +48,7 @@ common_constructor(LOGFILE *self) */ { memset(self, 0, sizeof(*self)); - MAP_constructor(&self->addr2logEntry_map, N_ADDRESSES_HINT/10, 10); + MAP_constructor(&self->addr2logEntry_map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT); } LOGFILE* diff --git a/logType.c b/logType.c index 7583209..dae9180 100644 --- a/logType.c +++ b/logType.c @@ -17,6 +17,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define _GNU_SOURCE #include #include #include @@ -199,7 +200,7 @@ LOGTYPE_proto_constructor(LOGTYPE *self, const struct logProtoType *proto) rc= snprintf(CacheFname, sizeof(CacheFname), "%s/%s", CacheDname, sumStr); if(sizeof(CacheFname) == rc) { eprintf("FATAL: File path truncated!"); - exit(1); + exit(EXIT_FAILURE); } LOGFILE *f; @@ -259,7 +260,7 @@ LOGTYPE_proto_constructor(LOGTYPE *self, const struct logProtoType *proto) rc= snprintf(CacheFname, sizeof(CacheFname), "%s/%s", CacheDname, entry->d_name); if(sizeof(CacheFname) == rc) { eprintf("FATAL: File path truncated!"); - exit(1); + exit(EXIT_FAILURE); } ez_unlink(CacheFname); } @@ -474,7 +475,7 @@ LOGTYPE_addressCount(LOGTYPE *self) { /* We'll need a map in which to collect unique addresses */ static MAP smap; - MAP_sinit(&smap, N_ADDRESSES_HINT/10, 10); + MAP_sinit(&smap, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT); /* Collect results for all LOGILE objects we own */ MAP_visitAllEntries(&self->file_map, (int(*)(void*,void*))LOGFILE_map_addr, &smap); diff --git a/maxoff.c b/maxoff.c index 544c383..4adf0f7 100644 --- a/maxoff.c +++ b/maxoff.c @@ -36,7 +36,7 @@ initialize(void) { S.is_init= 1; MAP_constructor(&S.cntry_map, 10, 10); - MAP_constructor(&S.addr_map, N_ADDRESSES_HINT/10, 10); + MAP_constructor(&S.addr_map, N_ADDRESSES_HINT/BUCKET_DEPTH_HINT, BUCKET_DEPTH_HINT); } // Compiler doesn't like that we use integers in place of item pointers */ diff --git a/msgqueue.c b/msgqueue.c new file mode 100644 index 0000000..9e8148e --- /dev/null +++ b/msgqueue.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (C) 2008 by John D. Robertson * + * john@rrci.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "msgqueue.h" +#include "util.h" + + +MSGQUEUE * +MSGQUEUE_constructor (MSGQUEUE * self, size_t msgSize, unsigned int queueLen) +{ + if (!self) + return NULL; + + pthread_mutex_init (&self->mtx, NULL); + + /* Allocate the memory in which to store messages */ + if (!(self->buff_ptr = (char *) malloc (msgSize * queueLen))) + return NULL; + + + /* Initialize the header segment */ + self->numItems = 0; + self->head = 0; + self->tail = 0; + self->msgSize = msgSize; + self->maxItems = queueLen; + + return self; +} + +void* +MSGQUEUE_destructor (MSGQUEUE * self) +{ + pthread_mutex_destroy (&self->mtx); + free (self->buff_ptr); + return self; +} + +int +MSGQUEUE_submitMsg (MSGQUEUE * self, const void *msgBuf) +/******************************************************************* + * Submit a message to the message queue. + */ +{ + int rtn = 0; + + if (pthread_mutex_lock (&self->mtx)) + assert (0); + + if (self->numItems == self->maxItems) + { +#ifdef DEBUG + eprintf("WARNING: %p queue full.", self); +#endif + rtn = EOF; + goto done; + } + + /* If this is not the first item to be added */ + if (self->numItems) + self->tail = (self->tail + 1) % self->maxItems; + + memcpy (self->buff_ptr + self->tail * self->msgSize, msgBuf, self->msgSize); + + self->numItems++; + +done: + if (pthread_mutex_unlock (&self->mtx)) + assert (0); + return rtn; +} + +int +MSGQUEUE_extractMsg (MSGQUEUE * self, void *msgBuf) +/***************************************************************************** + * Extract a message from the message queue. Returns EOF when queue is empty. + */ +{ + int rtn = 0; + + if (pthread_mutex_lock (&self->mtx)) + assert (0); + if (!self->numItems) + { + rtn = EOF; + goto done; + } + + self->numItems--; + memcpy (msgBuf, self->buff_ptr + self->head * self->msgSize, self->msgSize); + + if (self->numItems) + self->head = (self->head + 1) % self->maxItems; + +done: + if (pthread_mutex_unlock (&self->mtx)) + assert (0); + return rtn; +} + + +int +MSGQUEUE_checkQueue (MSGQUEUE * self, + int (*check) (void *data_ptr, void *arg), void *arg) +/***************************************************************************** + * Runs through the message queuue calling check() until it returns non-zero, + * or the queue is fully traversed. + * Return: 0 -> check() always returned zero, or queue was empty. + * otherwise, return value of check(). + */ +{ + int rtn = 0; + unsigned int i; + void *ptr; + + if (pthread_mutex_lock (&self->mtx)) + assert (0); + + if (!self->numItems) + goto done; + + for (i = 0, ptr = self->buff_ptr + self->head * self->msgSize; + i < self->numItems; + i++, ptr = + self->buff_ptr + ((self->head + i) % self->maxItems) * self->msgSize) + { + if ((rtn = (*check) (ptr, arg))) + break; + } + +done: + if (pthread_mutex_unlock (&self->mtx)) + assert (0); + return rtn; +} diff --git a/msgqueue.h b/msgqueue.h new file mode 100644 index 0000000..e18d3d5 --- /dev/null +++ b/msgqueue.h @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2008 by John D. Robertson * + * john@rrci.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MSGQUEUE_H +#define MSGQUEUE_H + +#include + +typedef struct +/******************************* + * Necessary info for circular message + * ring. + */ +{ + pthread_mutex_t mtx; + unsigned int numItems, head, tail; + unsigned int msgSize, maxItems; + char *buff_ptr; +} MSGQUEUE; + +#ifdef __cplusplus +extern "C" +{ +#endif + +MSGQUEUE* +MSGQUEUE_constructor ( + MSGQUEUE *self, + size_t msgSize, + unsigned int queueLen + ); +/***************************************************************************** + * Prepare the MSGQUEUE structure for service. + * + * self - Address of the MSGQUEUE structure on which to operate. + * msgSize - The size of messages this queue will handle. + * queueLen - How many messages can be stored in this queue. + * + * Returns: NULL for failure, 'self' otherwise. + */ + + +#define MSGQUEUE_create(self, msgSize, queueLen)\ + (MSGQUEUE_constructor((self)=malloc(sizeof(MSGQUEUE)), msgSize, queueLen) ? (self) : ( self ? realloc(MSGQUEUE_destructor(self),0): 0)) +/***************************************************************************** + * Allocate and prepare the MSGQUEUE structure for service. + * + * self - Pointer that will be set to the address of the MSGQUEUE structure. + * msgSize - The size of messages this queue will handle. + * queueLen - How many messages can be stored in this queue. + * + * Returns: NULL for failure, 'self' otherwise. + */ + +void* +MSGQUEUE_destructor (MSGQUEUE *self); +/***************************************************************************** + * Free resources associated with a MSGQUEUE. Note that 'self' is not free()'d. + * + * self - Address of the MSGQUEUE structure on which to operate. + * + * Returns: NULL for failure, 'self' otherwise. + */ + +#define MSGQUEUE_destroy(s) \ + {if(MSGQUEUE_destructor(s)) {free(s);}} +/***************************************************************************** + * Free resources associated with a MSGQUEUE, and free the structure. + * + * self - Address of the MSGQUEUE structure on which to operate. + */ + +int +MSGQUEUE_submitMsg ( + MSGQUEUE *self, + const void *msgBuf + ); +/******************************************************************* + * Submit a message to the message queue. + * + * self - Address of the MSGQUEUE structure on which to operate. + * msgBuf - buffer containing the message to be copied into the queue. + * + * Returns: 0 for success, non-zero otherwise. + */ + +int +MSGQUEUE_extractMsg ( + MSGQUEUE *self, + void *msgBuf + ); +/***************************************************************************** + * Extract a message from the message queue if possible. + * + * self - Address of the MSGQUEUE structure on which to operate. + * msgBuf - buffer to which the message will be copied. + * + * Returns: 0 for success, EOF if the queue is empty. + */ + +int +MSGQUEUE_checkQueue ( + MSGQUEUE *self, + int (*check) (void *pMsg, void *pData), + void *pData + ); +/***************************************************************************** + * Runs through the message queuue calling check() until it returns non-zero, + * or the queue is fully traversed. + * + * self - Address of the MSGQUEUE structure on which to operate. + * check - Function to be called for each message in the queue. + * pData - A pointer that will be passed into check() when it is called. + * + * Return: 0 -> check() always returned zero, or queue was empty. + * otherwise, return value of check(). + */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/util.c b/util.c index b6b8892..a1a9dea 100644 --- a/util.c +++ b/util.c @@ -24,6 +24,7 @@ Common utility routines needed by most c and c++ applications. begin : Fri Oct 19 10:09:38 EDT 2018 email : john@rrci.com ***************************************************************************/ +#define _GNU_SOURCE #include #include #include @@ -144,7 +145,7 @@ void _sys_eprintf( } int64_t -timespec_ms(const struct timespec *ts) +timespec2ms(const struct timespec *ts) /********************************************************************** * Convert a timespec structure to integer milliseconds. */ @@ -152,6 +153,17 @@ timespec_ms(const struct timespec *ts) return ts->tv_sec*1000 + ts->tv_nsec/1000000; } +struct timespec* +ms2timespec(struct timespec *rtnBuf, int64_t ms) +/********************************************************************** + * Load up a timespec struct given number of milliseconds. + */ +{ + rtnBuf->tv_sec= ms/1000; + rtnBuf->tv_nsec= (ms%1000)*1000000; + return rtnBuf; +} + const char* bits2str(int64_t bits, const struct bitTuple *btArr) /********************************************************************** @@ -308,7 +320,7 @@ clock_gettime_ms(clockid_t whichClock) sys_eprintf("\tclock_gettime() failed"); abort(); } - return timespec_ms(&ts); + return timespec2ms(&ts); } const char* diff --git a/util.h b/util.h index bfd039c..9b9c9c8 100644 --- a/util.h +++ b/util.h @@ -145,11 +145,17 @@ void _sys_eprintf( struct timespec; int64_t -timespec_ms(const struct timespec *ts); +timespec2ms(const struct timespec *ts); /********************************************************************** * Convert a timespec structure to integer milliseconds. */ +struct timespec* +ms2timespec(struct timespec *rtnBuf, int64_t ms); +/********************************************************************** + * Load up a timespec struct given number of milliseconds. + */ + int64_t clock_gettime_ms(clockid_t whichClock); /**********************************************************************