X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=backends%2Finnduct.c;h=58d2870de5b8bed162d49e161635e384584f8834;hb=0556ef460c00ebe81f3c23c8bcfcfeb1f937c95d;hp=0258ad022de0ce6b17341679f0449c7fb43745be;hpb=c2c88becbf08aa2b088ee42e6ed3331f733cbaaf;p=innduct.git diff --git a/backends/innduct.c b/backends/innduct.c index 0258ad0..58d2870 100644 --- a/backends/innduct.c +++ b/backends/innduct.c @@ -3,6 +3,9 @@ * - close idle connections * - cope better with garbage in feed file * - cope better with NULs in feed file + * - check all structs initialised + * - check all fd watches properly undone + * - check all init functions called * - -k kill mode ? */ @@ -157,11 +160,14 @@ perl -ne 'print if m/-8\<-/..m/-\>8-/; print "\f" if m/-\^L-/' backends/innduct. #define _GNU_SOURCE -#include "inn/list.h" #include "config.h" #include "storage.h" #include "nntp.h" #include "libinn.h" +#include "inndcomm.h" + +#include "inn/list.h" +#include "inn/innconf.h" #include #include @@ -178,6 +184,9 @@ perl -ne 'print if m/-8\<-/..m/-\>8-/; print "\f" if m/-\^L-/' backends/innduct. #include #include #include +#include +#include +#include #include #include @@ -291,7 +300,7 @@ static void filemon_callback(InputFile *ipf); /*----- configuration options -----*/ -static char *sitename, *feedfile; +static const char *sitename, *feedfile, *pathoutgoing; static const char *remote_host; static int quiet_multiple=0, become_daemon=1; @@ -307,14 +316,13 @@ static int connection_setup_timeout=200, port=119, try_stream=1; static int inndcomm_flush_timeout=100; static int reconnect_delay_periods, flushfail_retry_periods, open_wait_periods; static int backlog_retry_minperiods, backlog_spontaneous_rescan_periods; +static int spontaneous_flush_periods; static const char *inndconffile; -static double accept_proportion; static double nocheck_thresh_pct= 95.0; static double nocheck_thresh; /* computed in main from _pct */ static double nocheck_decay_articles= 100; /* converted to _decay */ static double nocheck_decay; /* computed in main from _articles */ -static int nocheck, nocheck_reported; /*----- statistics -----*/ @@ -446,16 +454,20 @@ static char *path_lock, *path_flushing, *path_defer, *globpat_backlog; static StateMachineState sms; static FILE *defer; static InputFile *main_input_file, *flushing_input_file, *backlog_input_file; -static int sm_period_counter; +static int sm_period_counter, until_backlog_nextscan; +static double accept_proportion; +static int nocheck, nocheck_reported; /*========== logging ==========*/ -static void logcore(int sysloglevel, const char *fmt, ...) - __attribute__((__format__(printf,2,3))); +#define VA va_list al; va_start(al,fmt) +#define PRINTF(f,a) __attribute__((__format__(printf,f,a))) +#define NORET_PRINTF(f,a) __attribute__((__noreturn__,__format__(printf,f,a))) + +static void logcore(int sysloglevel, const char *fmt, ...) PRINTF(2,3); static void logcore(int sysloglevel, const char *fmt, ...) { - va_list al; - va_start(al,fmt); + VA; if (become_daemon) { vsyslog(sysloglevel,fmt,al); } else { @@ -466,10 +478,9 @@ static void logcore(int sysloglevel, const char *fmt, ...) { } static void logv(int sysloglevel, const char *pfx, int errnoval, - int exitstatus, const char *fmt, va_list al) - __attribute__((__format__(printf,5,0))); + const char *fmt, va_list al) PRINTF(5,0); static void logv(int sysloglevel, const char *pfx, int errnoval, - int exitstatus, const char *fmt, va_list al) { + const char *fmt, va_list al) { char msgbuf[256]; /* NB do not call xvasprintf here or you'll recurse */ vsnprintf(msgbuf,sizeof(msgbuf), fmt,al); msgbuf[sizeof(msgbuf)-1]= 0; @@ -483,44 +494,48 @@ static void logv(int sysloglevel, const char *pfx, int errnoval, errnoval>=0 ? strerror(errnoval) : ""); } -#define logwrap(fn, pfx, sysloglevel, err, estatus) \ - static void fn(const char *fmt, ...) \ - __attribute__((__format__(printf,1,2))); \ +#define diewrap(fn, pfx, sysloglevel, err, estatus) \ + static void fn(const char *fmt, ...) NORET_PRINTF(1,2); \ + static void fn(const char *fmt, ...) { \ + VA; \ + logv(sysloglevel, pfx, err, fmt, al); \ + exit(estatus); \ + } + +#define logwrap(fn, pfx, sysloglevel, err) \ + static void fn(const char *fmt, ...) PRINTF(1,2); \ static void fn(const char *fmt, ...) { \ - va_list al; \ - va_start(al,fmt); \ - logv(sysloglevel, pfx, err, estatus, fmt, al); \ + VA; \ + logv(sysloglevel, pfx, err, fmt, al); \ + va_end(al); \ } -logwrap(sysdie, " critical", LOG_CRIT, errno, 16); -logwrap(die, " critical", LOG_CRIT, -1, 16); +diewrap(sysdie, " critical", LOG_CRIT, errno, 16); +diewrap(die, " critical", LOG_CRIT, -1, 16); -logwrap(sysfatal, " fatal", LOG_ERR, errno, 12); -logwrap(fatal, " fatal", LOG_ERR, -1, 12); +diewrap(sysfatal, " fatal", LOG_ERR, errno, 12); +diewrap(fatal, " fatal", LOG_ERR, -1, 12); -logwrap(syswarn, " warning", LOG_WARNING, errno, 0); -logwrap(warn, " warning", LOG_WARNING, -1, 0); +logwrap(syswarn, " warning", LOG_WARNING, errno); +logwrap(warn, " warning", LOG_WARNING, -1); -logwrap(notice, "", LOG_NOTICE, -1, 0); -logwrap(info, " info", LOG_INFO, -1, 0); -logwrap(debug, " debug", LOG_DEBUG, -1, 0); +logwrap(notice, "", LOG_NOTICE, -1); +logwrap(info, " info", LOG_INFO, -1); +logwrap(debug, " debug", LOG_DEBUG, -1); /*========== utility functions etc. ==========*/ -static char *xvasprintf(const char *fmt, va_list al) - __attribute__((__format__(printf,1,0))); +static char *xvasprintf(const char *fmt, va_list al) PRINTF(1,0); static char *xvasprintf(const char *fmt, va_list al) { char *str; int rc= vasprintf(&str,fmt,al); if (rc<0) sysdie("vasprintf(\"%s\",...) failed", fmt); return str; } -static char *xasprintf(const char *fmt, ...) - __attribute__((__format__(printf,1,2))); +static char *xasprintf(const char *fmt, ...) PRINTF(1,2); static char *xasprintf(const char *fmt, ...) { - va_list al; - va_start(al,fmt); + VA; char *str= xvasprintf(fmt,al); va_end(al); return str; @@ -585,6 +600,11 @@ static void xunlink(const char *path, const char *what) { if (r) sysdie("can't unlink %s %s", path, what); } +static time_t xtime(void) { + time_t now= time(0); + if (now==-1) sysdie("time(2) failed"); +} + static void check_isreg(const struct stat *stab, const char *path, const char *what) { if (!S_ISREG(stab->st_mode)) @@ -932,9 +952,7 @@ static void conn_maybe_write(Conn *conn) { } } -static void vconnfail(Conn *conn, const char *fmt, va_list al) - __attribute__((__format__(printf,2,0))); - +static void vconnfail(Conn *conn, const char *fmt, va_list al) PRINTF(2,0); static void vconnfail(Conn *conn, const char *fmt, va_list al) { int requeue[art_MaxState]; @@ -962,8 +980,7 @@ static void vconnfail(Conn *conn, const char *fmt, va_list al) { check_master_queue(); } -static void connfail(Conn *conn, const char *fmt, ...) - __attribute__((__format__(printf,2,3))); +static void connfail(Conn *conn, const char *fmt, ...) PRINTF(2,3); static void connfail(Conn *conn, const char *fmt, ...) { va_list al; va_start(al,fmt); @@ -2078,13 +2095,13 @@ static void close_defer(void) { if (!defer) return; - xfstat(fileno(defer), &stab, path_defer, "defer file"); + struct stat stab; + xfstat_isreg(fileno(defer), &stab, path_defer, "defer file"); if (fclose(defer)) sysfatal("could not close defer file %s", path_defer); defer= 0; - time_t now= time(0); - if (now==-1) sysdie("time(2) failed"); + time_t now= xtime(); char *backlog= xasprintf("%s_backlog_%lu.%lu", feedfile, (unsigned long)now, @@ -2113,12 +2130,12 @@ static void search_backlog_file(void) { /* returns non-0 iff there are any backlog files */ glob_t gl; - int r; + int r, i; struct stat stab; const char *oldest_path=0; time_t oldest_mtime, now; - if (backlog_input_file) return 3; + if (backlog_input_file) return; try_again: @@ -2169,10 +2186,10 @@ static void search_backlog_file(void) { exit(0); } until_backlog_nextscan= backlog_spontaneous_rescan_periods; - return 0; + return; } - now= time(); if (now==-1) sysdie("time(2) failed"); + now= xtime(); double age= difftime(now, oldest_mtime); long age_deficiency= (backlog_retry_minperiods * PERIOD_SECONDS) - age; @@ -2187,7 +2204,7 @@ static void search_backlog_file(void) { } inputfile_tailing_start(backlog_input_file); until_backlog_nextscan= -1; - return 1; + return; } until_backlog_nextscan= age_deficiency / PERIOD_SECONDS; @@ -2198,7 +2215,7 @@ static void search_backlog_file(void) { debug("backlog scan: young age=%f deficiency=%ld nextscan=%d oldest=%s", age, age_deficiency, until_backlog_nextscan, oldest_path); - return 2; + return; } /*========== flushing the feed ==========*/ @@ -2208,7 +2225,7 @@ static pid_t inndcomm_child; static void *inndcomm_event(oop_source *lp, int fd, oop_event e, void *u) { assert(inndcomm_child); int status= xwaitpid(&inndcomm_child, "inndcomm"); - loop->cancel_fd(fd); + cancel_fd_read_except(fd); close(fd); assert(!flushing_input_file); @@ -2286,11 +2303,12 @@ void spawn_inndcomm_flush(const char *why) { /* Moved => Flushing */ if (pipe(pipefds)) sysdie("create pipe for inndcomm child sentinel"); - inndcomm_child= xfork(); + inndcomm_child= xfork("inndcomm child"); if (!inndcomm_child) { - static char flushargv[2]= { sitename, 0 }; + const char *flushargv[2]= { sitename, 0 }; char *reply; + int r; close(pipefds[0]); @@ -2339,8 +2357,9 @@ static void postfork(const char *what) { postfork_stdio(defer); } -#define EVERY(what, interval, body) \ - static const struct timeval what##_timeout = { 5, 0 }; \ +#define EVERY(what, interval_sec, interval_usec, body) \ + static const struct timeval what##_timeout = \ + { interval_sec, interval_usec }; \ static void what##_schedule(void); \ static void *what##_timedout(oop_source *lp, struct timeval tv, void *u) { \ { body } \ @@ -2350,13 +2369,13 @@ static void postfork(const char *what) { loop->on_time(loop, what##_timeout, what##_timedout, 0); \ } -EVERY(filepoll, {5,0}, { +EVERY(filepoll, 5,0, { if (main_input_file && main_input_file->readable_callback) filemon_callback(main_input_file); }); -#define DEBUGF_IPF(wh) " " #wh "=%p/%s:ip=%ld,off=%ld,fd=%d%s" \ -#define DEBUG_IPF(sh) \ +#define DEBUGF_IPF(wh) " " #wh "=%p/%s:ip=%ld,off=%ld,fd=%d%s" +#define DEBUG_IPF(wh) \ wh##_input_file, debug_ipf_path(wh##_input_file), \ wh##_input_file->inprogress, (long)wh##_input_file->offset, \ wh##_input_file->fd, wh##_input_file->rd ? "+" : "" @@ -2365,10 +2384,10 @@ static const char *debug_ipf_path(InputFile *ipf) { return slash ? slash+1 : ipf->path; } -EVERY(period, {PERIOD_SECONDS,0}, { +EVERY(period, PERIOD_SECONDS,0, { debug("PERIOD" " sms=%s[%d] conns=%d queue=%d until_connect=%d" - " input_files" DEBUGF_IPF(main) DEBUGF_IPF(old) DEBUGF_FMT(flushing) + " input_files" DEBUGF_IPF(main) DEBUGF_IPF(old) DEBUGF_IPF(flushing) " children connecting=%ld inndcomm_child" , sms_names[sms], sm_period_counter, @@ -2388,103 +2407,115 @@ EVERY(period, {PERIOD_SECONDS,0}, { /*========== option parsing ==========*/ +/*---------- generic option parser and logging ----------*/ + +static void vbadusage(const char *fmt, va_list al) NORET_PRINTF(1,0); +static void vbadusage(const char *fmt, va_list al) { + abort(); +} +static void badusage(const char *fmt, ...) NORET_PRINTF(1,2); +static void badusage(const char *fmt, ...) { + va_list al; + va_start(al,fmt); + vbadusage(fmt,al); +} + enum OptFlags { - of_seconds= 001000u; - of_boolean= 002000u; + of_seconds= 001000u, + of_boolean= 002000u, }; typedef struct Option Option; typedef void OptionParser(const Option*, const char *val); struct Option { - int short; - const char *long; + int shrt; + const char *lng; void *store; OptionParser *fn; - int noarg; + int noarg, intval; }; -void op_integer(const Option *o, const char *val) { +/*---------- specific option types ----------*/ + +static void op_integer(const Option *o, const char *val) { char *ep; errno= 0; unsigned long ul= strtoul(val,&ep,10); if (*ep || ep==val || errno || ul>INT_MAX) - badusage("bad integer value for %s",o->long); + badusage("bad integer value for %s",o->lng); int *store= o->store; *store= ul; } -void op_double(const Option *o, const char *val) { +static void op_double(const Option *o, const char *val) { int *store= o->store; char *ep; errno= 0; *store= strtod(val, &ep); if (*ep || ep==val || errno) - badusage("bad floating point value for %s",o->long); + badusage("bad floating point value for %s",o->lng); } -void op_string(const Option *o, const char *val) { - char **store= o->store; - free(*store); +static void op_string(const Option *o, const char *val) { + const char **store= o->store; *store= val; } -void op_seconds(const Option *o, const char *val) { +static void op_seconds(const Option *o, const char *val) { int *store= o->store; char *ep; + int unit; double v= strtod(val,&ep); - if (ep==val) badusage("bad time/duration value for %s",o->long); + if (ep==val) badusage("bad time/duration value for %s",o->lng); if (!*ep || !strcmp(ep,"s")) unit= 1; else if (!strcmp(ep,"m")) unit= 60; else if (!strcmp(ep,"h")) unit= 3600; else if (!strcmp(ep,"d")) unit= 86400; - else badusage("bad units %s for time/duration value for %s",ep,o->long); + else badusage("bad units %s for time/duration value for %s",ep,o->lng); v *= unit; v= ceil(v); - if (v > INT_MAX) badusage("time/duration value for %s out of range",o->long); + if (v > INT_MAX) badusage("time/duration value for %s out of range",o->lng); *store= v; } -void op_periods_rndup(const Option *o, const char *val) { +static void op_periods_rndup(const Option *o, const char *val) { int *store= o->store; op_seconds(o,val); *store += PERIOD_SECONDS-1; *store /= PERIOD_SECONDS; } -void op_periods_booltrue(const Option *o, const char *val) { - int *store= o->store; - *store= 1; -} -void op_periods_boolfalse(const Option *o, const char *val) { +static void op_setint(const Option *o, const char *val) { int *store= o->store; - *store= 0; + *store= o->intval; } +/*---------- specific options ----------*/ + static const Option options[]= { {'f',"feedfile", &feedfile, op_string }, -{'q',"quiet-multiple", &quiet_multiple, op_booltrue, 1 }, +{'q',"quiet-multiple", &quiet_multiple, op_setint, 1,1 }, -{ 0, "max-connections", &max_connections op_integer }, -{ 0, "max-queue-per-conn", &max_queue_per_conn op_integer }, +{ 0, "max-connections", &max_connections, op_integer }, +{ 0, "max-queue-per-conn", &max_queue_per_conn, op_integer }, -{ 0, "streaming", &try_stream, op_booltrue, 1 }, -{ 0, "no-streaming", &try_stream, op_boolfalse, 1 }, -{'P',"port", &port op_integer }, +{ 0, "streaming", &try_stream, op_setint, 1,1 }, +{ 0, "no-streaming", &try_stream, op_setint, 1,0 }, +{'P',"port", &port, op_integer }, { 0, "inndconf", &inndconffile, op_string }, -{'d',"daemon", &become_daemon, op_booltrue, 1 }, -{ 0, "no-daemon", &become_daemon, op_boolfalse, 1 }, +{'d',"daemon", &become_daemon, op_setint, 1,1 }, +{ 0, "no-daemon", &become_daemon, op_setint, 1,0 }, { 0, "no-check-proportion", &nocheck_thresh_pct, op_double }, { 0, "no-check-filter", &nocheck_decay_articles, op_double }, { 0, "reconnect-interval", &reconnect_delay_periods, op_periods_rndup }, { 0, "flush-retry-interval", &flushfail_retry_periods, op_periods_rndup }, -{ 0, "connection-timeout", &connection_timeout, op_seconds }, { 0, "inndcomm-timeout", &inndcomm_flush_timeout, op_seconds }, }; @@ -2503,25 +2534,25 @@ int main(int argc, char **argv) { arg++; char *equals= strchr(arg,'='); int len= equals ? (equals - arg) : strlen(arg); - for (o=options; o->long; o++) - if (strlen(o->long) == len && !memcmp(o->long,arg,len)) + for (o=options; o->lng; o++) + if (strlen(o->lng) == len && !memcmp(o->lng,arg,len)) goto found_long; badusage("unknown long option --%s",arg); found_long: if (o->noarg) { - if (equals) badusage("option --%s does not take a value",o->long); + if (equals) badusage("option --%s does not take a value",o->lng); arg= 0; } else if (equals) { arg= equals+1; } else { arg= *++argv; - if (!arg) badusage("option --%s needs a value",o->long); + if (!arg) badusage("option --%s needs a value",o->lng); } o->fn(o, arg); break; /* eaten the whole argument now */ } - for (o=options; o->long; o++) - if (a == o->short) + for (o=options; o->lng; o++) + if (a == o->shrt) goto found_short; badusage("unknown short option -%c",a); found_short: @@ -2530,7 +2561,7 @@ int main(int argc, char **argv) { } else { if (!*++arg) { arg= *++argv; - if (!arg) badusage("option -%c needs a value",o->short); + if (!arg) badusage("option -%c needs a value",o->shrt); } o->fn(o,arg); break; /* eaten the whole argument now */ @@ -2571,13 +2602,15 @@ int main(int argc, char **argv) { if (strchr(feedfile, c)) badusage("feed filename may not contain metacharacter %c",c); - loop= oop_sys_new(); - if (!loop) sysdie("could not create liboop event loop"); + oop_source_sys *sysloop= oop_sys_new(); + if (!sysloop) sysdie("could not create liboop event loop"); + loop= (oop_source*)sysloop; if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) sysdie("could not ignore SIGPIPE"); if (become_daemon) { + int i; for (i=3; i<255; i++) /* do this now before we open syslog, etc. */ close(i); @@ -2602,7 +2635,7 @@ int main(int argc, char **argv) { notice("starting"); - if (!filemon_init()) { + if (!filemon_method_init()) { warn("no file monitoring available, polling"); filepoll_schedule(); } @@ -2611,5 +2644,7 @@ int main(int argc, char **argv) { statemc_init(); - loop->execute. + void *r= oop_sys_run(sysloop); + assert(r == OOP_ERROR); + sysdie("event loop failed"); }