From: Ian Jackson Date: Sat, 1 May 2010 13:12:07 +0000 (+0100) Subject: dump control command X-Git-Tag: innduct-0.1~81 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=innduct.git;a=commitdiff_plain;h=b48514c1c344fc24c4560bd118898b99a30affa0 dump control command --- diff --git a/backends/innduct.c b/backends/innduct.c index b02e9d1..8e92f08 100644 --- a/backends/innduct.c +++ b/backends/innduct.c @@ -1,6 +1,11 @@ /* * todo - * - some per-conn info thing for control + * - rename defraise to raise_default + * - xmalloc + memset -> xcalloc + * - macro for conn iteration + * - skipping_long offset calculation is wrong + * - reset signals TERM and INT (and HUP) in children + * * - manpage: document control master stuff * - admin-initiated flush * @@ -208,6 +213,8 @@ perl -ne 'print if m/-8\<-/..m/-\>8-/; print "\f" if m/-\^L-/' backends/innduct. #define PRINTF(f,a) __attribute__((__format__(printf,f,a))) #define NORET_PRINTF(f,a) __attribute__((__noreturn__,__format__(printf,f,a))) +#define DUMPV(fmt,pfx,v) fprintf(f, " " #v "=" fmt, pfx v); + /*----- doubly linked lists -----*/ #define ISNODE(T) struct node list_node @@ -269,6 +276,7 @@ typedef struct InputFile InputFile; typedef struct XmitDetails XmitDetails; typedef struct Filemon_Perfile Filemon_Perfile; typedef enum StateMachineState StateMachineState; +typedef struct ControlCommand ControlCommand; DEFLIST(Conn); DEFLIST(Article); @@ -305,6 +313,7 @@ static void close_defer(void); static void search_backlog_file(void); static void preterminate(void); static void defraise(int signo); +static char *debug_report_ipf(InputFile *ipf); static void inputfile_reading_start(InputFile *ipf); static void inputfile_reading_stop(InputFile *ipf); @@ -365,6 +374,9 @@ typedef enum { /* in queue in conn->sent */ art_MaxState, } ArtState; +static const char *const artstate_names[]= + { "Unchecked", "Wanted", "Unsolicited", 0 }; + #define RESULT_COUNTS(RCS,RCN) \ RCS(sent) \ RCS(accepted) \ @@ -479,15 +491,16 @@ struct Conn { static oop_source *loop; static ConnList conns; static ArticleList queue; -static char *path_lock, *path_flushing, *path_defer, *path_control; +static char *path_lock, *path_flushing, *path_defer; +static char *path_control, *path_dump; static char *globpat_backlog; static pid_t self_pid; /* statemc_init initialises */ static StateMachineState sms; -static FILE *defer; -static InputFile *main_input_file, *flushing_input_file, *backlog_input_file; static int sm_period_counter; +static InputFile *main_input_file, *flushing_input_file, *backlog_input_file; +static FILE *defer; /* initialisation to 0 is good */ static int until_connect, until_backlog_nextscan; @@ -709,15 +722,16 @@ static int samefile(const struct stat *a, const struct stat *b) { a->st_dev == b->st_dev); } -static char *sanitise(const char *input) { +static char *sanitise(const char *input, int len) { static char sanibuf[100]; /* returns pointer to this buffer! */ const char *p= input; + const char *endp= len>=0 ? input+len : 0; char *q= sanibuf; *q++= '`'; for (;;) { if (q > sanibuf+sizeof(sanibuf)-8) { strcpy(q,"'.."); break; } - int c= *p++; + int c= (!endp || p=' ' && c<=126 && c!='\\') { *q++= c; continue; } sprintf(q,"\\x%02x",c); @@ -730,7 +744,6 @@ static int isewouldblock(int errnoval) { return errnoval==EWOULDBLOCK || errnoval==EAGAIN; } - /*========== command and control connections ==========*/ static int control_master; @@ -770,7 +783,6 @@ static void control_prompt(ControlConn *cc /* may destroy*/) { control_checkouterr(cc); } -typedef struct ControlCommand ControlCommand; struct ControlCommand { const char *cmd; void (*f)(ControlConn *cc, const ControlCommand *ccmd, @@ -796,6 +808,7 @@ CCMD(period) { period(); } CCMD(setintarg) { *(int*)c->xdata= atoi(arg); } CCMD(setint) { *(int*)c->xdata= c->xval; } CCMD(setint_period) { *(int*)c->xdata= c->xval; period(); } +CCMD(dump); CCMD(stop) { preterminate(); @@ -805,9 +818,11 @@ CCMD(stop) { } static const ControlCommand control_commands[]= { - { "h", ccmd_help }, - { "p", ccmd_period }, - { "stop", ccmd_stop }, + { "h", ccmd_help }, + { "p", ccmd_period }, + { "stop", ccmd_stop }, + { "dump q", ccmd_dump, 0,0 }, + { "dump a", ccmd_dump, 0,1 }, #define POKES(cmd,func) \ { cmd "sm", func, &sm_period_counter, 1 }, \ @@ -1315,7 +1330,7 @@ static void connect_start(void) { } else { buf[l]= 0; fatal("connect: %s: %s", stripped ? "rejected" : "failed", - sanitise(buf)); + sanitise(buf,-1)); } } if (NNTPsendpassword((char*)remote_host, cn_from, cn_to) < 0) @@ -1335,13 +1350,13 @@ static void connect_start(void) { assert(l>=1); if (buf[l-1]!='\n') fatal("connect: response to MODE STREAM is too long: %.100s...", - sanitise(buf)); + sanitise(buf,-1)); l--; if (l>0 && buf[l-1]=='\r') l--; buf[l]= 0; char *ep; int rcode= strtoul(buf,&ep,10); if (ep != &buf[3]) - fatal("connect: bad response to MODE STREAM: %.50s", sanitise(buf)); + fatal("connect: bad response to MODE STREAM: %.50s", sanitise(buf,-1)); switch (rcode) { case 203: @@ -1352,7 +1367,7 @@ static void connect_start(void) { break; default: warn("connect: unexpected response to MODE STREAM: %.50s", - sanitise(buf)); + sanitise(buf,-1)); exitstatus= 2; break; } @@ -1722,7 +1737,7 @@ static void *peer_rd_ok(oop_source *lp, oop_read *oread, oop_rd_event ev, } assert(ev == OOP_RD_OK); - char *sani= sanitise(data); + char *sani= sanitise(data,-1); char *ep; unsigned long code= strtoul(data, &ep, 10); @@ -1869,7 +1884,7 @@ static void close_input_file(InputFile *ipf) { /* does not free */ static void *feedfile_got_bad_data(InputFile *ipf, off_t offset, const char *data, const char *how) { warn("corrupted file: %s, offset %lu: %s: in %s", - ipf->path, (unsigned long)offset, how, sanitise(data)); + ipf->path, (unsigned long)offset, how, sanitise(data,-1)); ipf->readcount_err++; if (ipf->readcount_err > max_bad_data_initial + (ipf->readcount_ok+ipf->readcount_blank) / max_bad_data_ratio) @@ -1940,6 +1955,7 @@ static void *feedfile_got_article(oop_source *lp, oop_read *rd, ipf->readcount_ok++; art= xmalloc(sizeof(*art) - 1 + midlen + 1); + memset(art,0,sizeof(art)); art->state= art_Unchecked; art->midlen= midlen; art->ipf= ipf; ipf->inprogress++; @@ -2098,6 +2114,15 @@ static int filemon_method_init(void) { return 1; } +static void filemon_method_dump_info(FILE *f) { + int i; + fprintf(f,"inotify"); + DUMPV("%d",,filemon_inotify_fd); + DUMPV("%d",,filemon_inotify_wdmax); + for (i=0; ipath,'/'); const char *path= slash ? slash+1 : ipf->path; - return xasprintf("%p/%s:ip=%ld,off=%ld,fd=%d%s", + return xasprintf("%p/%s:ip=%ld,off=%ld,fd=%d%s%s", ipf, path, ipf->inprogress, (long)ipf->offset, - ipf->fd, ipf->rd ? "" : ",!rd"); + ipf->fd, + ipf->rd ? "" : ",!rd", + ipf->skippinglong ? "*skiplong" : ""); } static void period(void) { @@ -3047,6 +3075,140 @@ static void period(void) { } +/*========== dumping state ==========*/ + +static void dump_article_list(FILE *f, const ControlCommand *c, + const ArticleList *al) { + fprintf(f, " count=%d\n", al->count); + if (!c->xval) return; + + int i; Article *art; + for (i=0, art=LIST_HEAD(*al); art; i++, art=LIST_NEXT(art)) { + fprintf(f," #%05d %-11s", i, artstate_names[art->state]); + DUMPV("%p", art->,ipf); + DUMPV("%d", art->,missing); + DUMPV("%lu", (unsigned long)art->,offset); + DUMPV("%d", art->,blanklen); + DUMPV("%d", art->,midlen); + fprintf(f, " %s %s\n", TokenToText(art->token), art->messageid); + } +} + +static void dump_input_file(FILE *f, InputFile *ipf, const char *wh) { + char *dipf= debug_report_ipf(ipf); + fprintf(f,"input %s %s", wh, dipf); + free(dipf); + + if (ipf) { + DUMPV("%d", ipf->,readcount_ok); + DUMPV("%d", ipf->,readcount_blank); + DUMPV("%d", ipf->,readcount_err); + } + fprintf(f,"\n"); + if (ipf) { + ArtState state; const char *const *statename; + for (state=0, statename=artstate_names; *statename; state++,statename++) { +#define RC_DUMP_FMT(x) " " #x "=%d" +#define RC_DUMP_VAL(x) ,ipf->counts[state][RC_##x] + fprintf(f,"input %s counts %-11s" + RESULT_COUNTS(RC_DUMP_FMT,RC_DUMP_FMT) "\n", + wh, *statename + RESULT_COUNTS(RC_DUMP_VAL,RC_DUMP_VAL)); + } + } +} + +CCMD(dump) { + int i; + fprintf(cc->out, "dumping state to %s\n", path_dump); + FILE *f= fopen(path_dump, "w"); + if (!f) { fprintf(cc->out, "failed: open: %s\n", strerror(errno)); return; } + + fprintf(f,"general"); + DUMPV("%s", sms_names,[sms]); + DUMPV("%d", ,sm_period_counter); + DUMPV("%ld", (long),self_pid); + DUMPV("%p", , defer); + DUMPV("%d", , until_connect); + DUMPV("%d", , until_backlog_nextscan); + DUMPV("%d", , simulate_flush); + fprintf(f,"\nnocheck"); + DUMPV("%#.10f", , accept_proportion); + DUMPV("%d", , nocheck); + DUMPV("%d", , nocheck_reported); + fprintf(f,"\n"); + + fprintf(f,"special"); + DUMPV("%ld", (long),connecting_child); + DUMPV("%d", , connecting_fdpass_sock); + DUMPV("%d", , control_master); + fprintf(f,"\n"); + + fprintf(f,"filemon "); + filemon_method_dump_info(f); + + dump_input_file(f, main_input_file, "main" ); + dump_input_file(f, flushing_input_file, "flushing"); + dump_input_file(f, backlog_input_file, "backlog" ); + + fprintf(f,"conns count=%d\n", conns.count); + + Conn *conn; + for (conn=LIST_HEAD(conns); conn; conn=LIST_NEXT(conn)) { + + fprintf(f,"C%d",conn->fd); + DUMPV("%p",conn->,rd); DUMPV("%d",conn->,max_queue); + DUMPV("%d",conn->,stream); DUMPV("%d",conn->,quitting); + DUMPV("%d",conn->,since_activity); + fprintf(f,"\n"); + + fprintf(f,"C%d waiting", conn->fd); dump_article_list(f,c,&conn->waiting); + fprintf(f,"C%d priority",conn->fd); dump_article_list(f,c,&conn->priority); + fprintf(f,"C%d sent", conn->fd); dump_article_list(f,c,&conn->sent); + + fprintf(f,"C%d xmit xmitu=%d\n", conn->fd, conn->xmitu); + for (i=0; ixmitu; i++) { + const struct iovec *iv= &conn->xmit[i]; + const XmitDetails *xd= &conn->xmitd[i]; + char *dinfo; + long diff; + switch (xd->kind) { + case xk_Malloc: + diff= xd->info.malloc_tofree - (char*)iv->iov_base; + dinfo= xasprintf("M%5ld", diff); + break; + case xk_Const: + dinfo= xasprintf("Const"); + break; + case xk_Artdata: + dinfo= xasprintf("A%p", xd->info.sm_art); + break; + default: + abort(); + } + fprintf(f," #%03d %-11s l=%d %s\n", i, dinfo, iv->iov_len, + sanitise(iv->iov_base, iv->iov_len)); + free(dinfo); + } + } + + fprintf(f,"queue"); dump_article_list(f,c,&queue); + + fprintf(f,"paths"); + DUMPV("%s", , path_lock); + DUMPV("%s", , path_flushing); + DUMPV("%s", , path_defer); + DUMPV("%s", , path_control); + DUMPV("%s", , path_dump); + DUMPV("%s", , globpat_backlog); + fprintf(f,"\n"); + + if (!!ferror(f) + !!fclose(f)) { + fprintf(cc->out, "failed: write: %s\n", strerror(errno)); + return; + } +} + /*========== option parsing ==========*/ static void vbadusage(const char *fmt, va_list al) NORET_PRINTF(1,0); @@ -3329,6 +3491,7 @@ int main(int argc, char **argv) { path_flushing= xasprintf("%s_flushing", feedfile); path_defer= xasprintf("%s_defer", feedfile); path_control= xasprintf("%s_control", feedfile); + path_dump= xasprintf("%s_dump", feedfile); globpat_backlog= xasprintf("%s_backlog*", feedfile); oop_source_sys *sysloop= oop_sys_new(); diff --git a/doc/man/innduct.8 b/doc/man/innduct.8 index c3beb92..50e4a41 100644 --- a/doc/man/innduct.8 +++ b/doc/man/innduct.8 @@ -413,6 +413,10 @@ safely remove backlog files. \fIsomething\fR may not contain \fB#\fR you rename it to match the pattern \fIfeedfile\fR\fB_backlog\fR*, as otherwise innduct may find and process the file and read it to EOF before you have finished creating it. +.IP \fIfeedfile\fR_dump +.IX Item "debug dump file" +On request via a control connection innduct dumps a summary of its +state to this text file. This is mostly useful for debugging. .IP /etc/news/inn.conf .IX Item inn.conf Used to find