Added event server sources

This commit is contained in:
john 2019-11-29 17:23:16 -05:00
parent 0ef57ad814
commit 2e4c215de2
20 changed files with 2193 additions and 99 deletions

View File

@ -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
########################################

View File

@ -16,23 +16,17 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <string.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#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;
}

View File

@ -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"

978
es.c Normal file
View File

@ -0,0 +1,978 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <poll.h>
#include <time.h>
#include <assert.h>
#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;
}

186
es.h Normal file
View File

@ -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 <pthread.h>
#include <stdint.h>
#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

129
ez_es.c Normal file
View File

@ -0,0 +1,129 @@
#include <stdlib.h>
#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;
}

95
ez_es.h Normal file
View File

@ -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

74
ez_libanl.c Normal file
View File

@ -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 <stdlib.h>
#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();
}

59
ez_libanl.h Normal file
View File

@ -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 <netdb.h>
#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

View File

@ -16,6 +16,7 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
@ -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();
}

170
ez_libc.h
View File

@ -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 <dirent.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
#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

View File

@ -16,6 +16,7 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -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");

View File

@ -16,6 +16,7 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <stddef.h>
#include <string.h>

View File

@ -16,6 +16,7 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@ -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*

View File

@ -17,6 +17,7 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <limits.h>
@ -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);

View File

@ -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 */

157
msgqueue.c Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#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;
}

138
msgqueue.h Normal file
View File

@ -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 <pthread.h>
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

16
util.c
View File

@ -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 <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
@ -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*

8
util.h
View File

@ -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);
/**********************************************************************