From: Ian Jackson Date: Sat, 1 May 2010 01:32:54 +0000 (+0100) Subject: signal handling X-Git-Tag: innduct-0.1~83 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=innduct.git;a=commitdiff_plain;h=f3a845d82cf46aed625bced7f0551ac436b50241 signal handling --- diff --git a/backends/innduct.c b/backends/innduct.c index c518fd0..b02e9d1 100644 --- a/backends/innduct.c +++ b/backends/innduct.c @@ -1,11 +1,11 @@ /* * todo - * - inotify not working ? * - some per-conn info thing for control * - manpage: document control master stuff + * - admin-initiated flush * * debugging rune: - * build-lfs/backends/innduct --no-daemon -f `pwd`/fee sit dom + * build-lfs/backends/innduct --no-daemon -C ../inn.conf -f `pwd`/fee sit localhost */ /* @@ -303,6 +303,8 @@ static void period(void); static void open_defer(void); static void close_defer(void); static void search_backlog_file(void); +static void preterminate(void); +static void defraise(int signo); static void inputfile_reading_start(InputFile *ipf); static void inputfile_reading_stop(InputFile *ipf); @@ -490,7 +492,7 @@ static int sm_period_counter; /* initialisation to 0 is good */ static int until_connect, until_backlog_nextscan; static double accept_proportion; -static int nocheck, nocheck_reported; +static int nocheck, nocheck_reported, in_child; /* for simulation, debugging, etc. */ int simulate_flush= -1; @@ -530,6 +532,7 @@ static void logv(int sysloglevel, const char *pfx, int errnoval, #define diewrap(fn, pfx, sysloglevel, err, estatus) \ static void fn(const char *fmt, ...) NORET_PRINTF(1,2); \ static void fn(const char *fmt, ...) { \ + preterminate(); \ VA; \ logv(sysloglevel, pfx, err, fmt, al); \ exit(estatus); \ @@ -654,6 +657,11 @@ static time_t xtime(void) { return now; } +static void xsigaction(int s, const struct sigaction *sa) { + int r= sigaction(s,sa,0); + if (r) sysdie("sigaction failed for \"%s\"", strsignal(s)); +} + static void xgettimeofday(struct timeval *tv_r) { int r= gettimeofday(tv_r,0); if (r) sysdie("gettimeofday(2) failed"); @@ -789,10 +797,17 @@ CCMD(setintarg) { *(int*)c->xdata= atoi(arg); } CCMD(setint) { *(int*)c->xdata= c->xval; } CCMD(setint_period) { *(int*)c->xdata= c->xval; period(); } +CCMD(stop) { + preterminate(); + notice("terminating (CTRL%d)",cc->fd); + defraise(SIGTERM); + abort(); +} + static const ControlCommand control_commands[]= { { "h", ccmd_help }, { "p", ccmd_period }, - { "pretend flush", ccmd_setintarg, &simulate_flush }, + { "stop", ccmd_stop }, #define POKES(cmd,func) \ { cmd "sm", func, &sm_period_counter, 1 }, \ @@ -801,6 +816,7 @@ static const ControlCommand control_commands[]= { POKES("prod ", ccmd_setint_period) POKES("next ", ccmd_setint) + { "pretend flush", ccmd_setintarg, &simulate_flush }, { "wedge blscan", ccmd_setint, &until_backlog_nextscan, -1 }, { 0 } }; @@ -2414,20 +2430,26 @@ static int inputfile_is_done(InputFile *ipf) { return 1; } -static void notice_processed(InputFile *ipf, const char *what, - const char *spec) { +static void notice_processed(InputFile *ipf, int completed, + const char *what, const char *spec) { + if (!ipf) return; /* allows preterminate to be lazy */ + #define RCI_NOTHING(x) /* nothing */ #define RCI_TRIPLE_FMT(x) " " #x "=" RCI_TRIPLE_FMT_BASE #define RCI_TRIPLE_VALS(x) , RCI_TRIPLE_VALS_BASE(ipf->counts, [RC_##x]) #define CNT(art,rc) (ipf->counts[art_##art][RC_##rc]) - info("processed %s%s read=%d (+bl=%d,+err=%d)" + char *inprog= completed + ? xasprintf("%s","") /* GCC produces a stupid warning for printf("") ! */ + : xasprintf(" inprogress=%ld", ipf->inprogress); + + info("%s %s%s read=%d (+bl=%d,+err=%d)%s" " offered=%d (ch=%d,nc=%d) accepted=%d (ch=%d,nc=%d)" RESULT_COUNTS(RCI_NOTHING, RCI_TRIPLE_FMT) , - what, spec, - ipf->readcount_ok, ipf->readcount_blank, ipf->readcount_err, + completed?"completed":"processed", what, spec, + ipf->readcount_ok, ipf->readcount_blank, ipf->readcount_err, inprog, CNT(Unchecked,sent) + CNT(Unsolicited,sent) , CNT(Unchecked,sent), CNT(Unsolicited,sent), CNT(Wanted,accepted) + CNT(Unsolicited,accepted) @@ -2435,6 +2457,8 @@ static void notice_processed(InputFile *ipf, const char *what, RESULT_COUNTS(RCI_NOTHING, RCI_TRIPLE_VALS) ); + free(inprog); + #undef CNT } @@ -2447,7 +2471,7 @@ static void statemc_check_backlog_done(void) { const char *under= strchr(slash, '_'); const char *rest= under ? under+1 : leaf; if (!strncmp(rest,"backlog",7)) rest += 7; - notice_processed(ipf,"backlog ",rest); + notice_processed(ipf,1,"backlog ",rest); close_input_file(ipf); if (unlink(ipf->path)) { @@ -2469,7 +2493,7 @@ static void statemc_check_flushing_done(void) { assert(sms==sm_SEPARATED || sms==sm_DROPPING); - notice_processed(ipf,"feedfile",""); + notice_processed(ipf,1,"feedfile",""); close_defer(); @@ -2670,6 +2694,7 @@ static void search_backlog_file(void) { debug("backlog scan: none"); if (sms==sm_DROPPED) { + preterminate(); notice("feed dropped and our work is complete"); int r= unlink(path_control); @@ -2716,6 +2741,74 @@ static void search_backlog_file(void) { return; } +/*---------- shutdown and signal handling ----------*/ + +static void preterminate(void) { + if (in_child) return; + notice_processed(main_input_file,0,"feedfile",""); + notice_processed(flushing_input_file,0,"flushing file",""); + if (backlog_input_file) + notice_processed(backlog_input_file,0, "backlog file ", + backlog_input_file->path); +} + +static int signal_self_pipe[2]; +static sig_atomic_t terminate_sig_flag; + +static void defraise(int signo) { + struct sigaction sa; + memset(&sa,0,sizeof(sa)); + sa.sa_handler= SIG_DFL; + xsigaction(signo,&sa); + raise(signo); +} + +static void *sigarrived_event(oop_source *lp, int fd, oop_event e, void *u) { + assert(fd=signal_self_pipe[0]); + char buf[PIPE_BUF]; + int r= read(signal_self_pipe[0], buf, sizeof(buf)); + if (r<0 && !isewouldblock(errno)) sysdie("failed to read signal self pipe"); + if (r==0) die("eof on signal self pipe"); + if (terminate_sig_flag) { + preterminate(); + notice("terminating (%s)", strsignal(terminate_sig_flag)); + defraise(terminate_sig_flag); + abort(); + } + return OOP_CONTINUE; +} + +static void sigarrived_handler(int signum) { + static char x; + switch (signum) { + case SIGINT: case SIGTERM: + if (!terminate_sig_flag) terminate_sig_flag= signum; + break; + default: + abort(); + } + write(signal_self_pipe[1],&x,1); +} + +static void init_signals(void) { + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + sysdie("could not ignore SIGPIPE"); + + if (pipe(signal_self_pipe)) sysfatal("create self-pipe for signals"); + + xsetnonblock(signal_self_pipe[0],1); + xsetnonblock(signal_self_pipe[1],1); + + struct sigaction sa; + memset(&sa,0,sizeof(sa)); + sa.sa_handler= sigarrived_handler; + sa.sa_flags= SA_RESTART; + xsigaction(SIGTERM,&sa); + xsigaction(SIGINT,&sa); + + on_fd_read_except(signal_self_pipe[0], sigarrived_event); +} + /*========== flushing the feed ==========*/ static pid_t inndcomm_child; @@ -2858,6 +2951,8 @@ static void postfork_stdio(FILE *f, const char *what, const char *what2) { } static void postfork(void) { + in_child= 1; + if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) sysdie("(in child) failed to reset SIGPIPE"); @@ -3240,9 +3335,6 @@ int main(int argc, char **argv) { 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"); - LIST_INIT(conns); LIST_INIT(queue); @@ -3275,6 +3367,8 @@ int main(int argc, char **argv) { statemc_lock(); + init_signals(); + notice("starting"); if (!become_daemon) diff --git a/doc/man/innduct.8 b/doc/man/innduct.8 index 83aeade..c3beb92 100644 --- a/doc/man/innduct.8 +++ b/doc/man/innduct.8 @@ -307,13 +307,10 @@ articles it has in hand now, and then exit. It is harmless to cause innd to flush the feed (but innduct won't notice and flushing won't start a new feedfile; you have to leave that to innduct). .LP -There are no signals that can usefully be sent to innduct to give it -complicated instructions. If you need to kill innduct, feel free to -send it a -.B SIGTERM -or -.B SIGKILL -and nothing will be broken or corrupted. +If you want to stop innduct you can send it SIGTERM or SIGINT, in +which case it will report statistics so far and quickly exit. +If innduct receives SIGKILL nothing will be broken or corrupted; you +just won't see some of the article stats. .SH EXIT STATUS .TP .B 0