X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=backends%2Finnduct.c;h=de0a2bc19ef2ffa2af5b44e171793a85a442887a;hb=628a2b4f87056f05ff44ca093f34c18c810b1ca9;hp=b8124704b71b69de5ab752d361a97e9446c55b88;hpb=4e748bf495b4c5c0582b877aee531dec31fc2a36;p=innduct.git diff --git a/backends/innduct.c b/backends/innduct.c index b812470..de0a2bc 100644 --- a/backends/innduct.c +++ b/backends/innduct.c @@ -145,12 +145,15 @@ perl -ne 'print if m/-8\<-/..m/-\>8-/; print "\f" if m/-\^L-/' backends/innduct. * */ +/*============================== PROGRAM ==============================*/ + #define _GNU_SOURCE +#include "inn/list.h" #include "config.h" #include "storage.h" -#include "oop.h" -#include "oop-read.h" +#include "nntp.h" +#include "libinn.h" #include #include @@ -163,6 +166,13 @@ perl -ne 'print if m/-8\<-/..m/-\>8-/; print "\f" if m/-\^L-/' backends/innduct. #include #include #include +#include +#include +#include +#include + +#include +#include /*----- general definitions, probably best not changed -----*/ @@ -174,8 +184,75 @@ perl -ne 'print if m/-8\<-/..m/-\>8-/; print "\f" if m/-\^L-/' backends/innduct. #define INNDCOMMCHILD_ESTATUS_FAIL 6 #define INNDCOMMCHILD_ESTATUS_NONESUCH 7 +/*----- doubly linked lists -----*/ + +#define ISNODE(T) struct { T *succ, *pred; } node /* must be at start */ +#define DEFLIST(T) typedef struct { T *head, *tail, *tp; int count; } T##List + +#define NODE(n) (assert((void*)&(n)->node == &(n)), \ + (struct node*)&(n)->node) + +#define LIST_CHECKCANHAVENODE(l,n) \ + ((void)((n) == ((l).head))) /* just for the type check */ + +#define LIST_ADDSOMEHOW(l,n,list_addsomehow) \ + ( LIST_CHECKCANHAVENODE(l,n), \ + list_addsomehow((struct list*)&(l), NODE((n))), \ + (void)(l).count++ \ + ) + +#define LIST_REMSOMEHOW(l,list_remsomehow) \ + ( (typeof((l).head)) \ + ( (l).count \ + ? ( (l).count--, \ + list_remsomehow((struct list*)&(l)) ) \ + : 0 \ + ) \ + ) + + +#define LIST_ADDHEAD(l,n) LIST_ADDSOMEHOW((l),(n),list_addhead) +#define LIST_ADDTAIL(l,n) LIST_ADDSOMEHOW((l),(n),list_addtail) +#define LIST_REMHEAD(l) LIST_REMSOMEHOW((l),list_remhead) +#define LIST_REMTAIL(l) LIST_REMSOMEHOW((l),list_remtail) + +#define LIST_REMOVE(l,n) \ + ( LIST_CHECKCANHAVENODE(l,n), \ + list_remove(NODE((n))), \ + (void)(l).count-- \ + ) + +#define LIST_INSERT(l,n,pred) \ + ( LIST_CHECKCANHAVENODE(l,n), \ + LIST_CHECKCANHAVENODE(l,pred), \ + list_insert((struct list*)&(l), NODE((n)), NODE((pred))), \ + (void)(l).count++ \ + ) + +/*----- type predeclarations -----*/ + typedef struct Conn Conn; typedef struct Article Article; +typedef struct InputFile InputFile; +typedef enum StateMachineState StateMachineState; + +DEFLIST(Conn); +DEFLIST(Article); + +/*----- function predeclarations -----*/ + +static void conn_check_work(Conn *conn); + +static int filemon_init(void); +static void filemon_setfile(int mainfeed_fd, const char *mainfeed_path); +static void filemon_callback(void); + +static void conn_assign_one_article(ConnList *connlist, Conn **last_assigned); +static void statemc_setstate(StateMachineState newsms, int periods, + const char *forlog, const char *why); +static void check_master_queue(void); + +static void postfork_inputfile(InputFile *ipf); /*----- configuration options -----*/ @@ -199,28 +276,6 @@ static double nocheck_decay; /* computed in main from _articles */ static int nocheck, nocheck_reported; -/*----- doubly linked lists -----*/ - -#define ISNODE(T) T *next, *back; -#define LIST(T) struct { T *head, *tail, *tailpred; int count; } - -#define NODE(n) ((struct node*)&(n)->head) - -#define LIST_ADDHEAD(l,n) \ - (list_addhead((struct list*)&(l), NODE((n))), (void)(l).count++) -#define LIST_ADDTAIL(l,n) \ - (list_addtail((struct list*)&(l), NODE((n))), (void)(l).count++) - -#define LIST_REMHEAD(l) \ - ((l).count ? ((l).count--, (void*)list_remhead((struct list*)&(l))) : 0) -#define LIST_REMTAIL(l) \ - ((l).count ? ((l).count--, (void*)list_remtail((struct list*)&(l))) : 0) -#define LIST_REMOVE(l,n) \ - (list_remove(NODE((n))), (void)(l).count--) -#define LIST_INSERT(l,n,pred) \ - (list_insert((struct list*)&(l), NODE((n)), NODE((pred))), (void)(l).count++) - - /*----- statistics -----*/ typedef enum { /* in queue in conn->sent */ @@ -273,8 +328,8 @@ typedef struct { /*----- core operational data structure types -----*/ -typedef struct InputFile { - /* This is an instance of struct oop_readable */ +struct InputFile { + /* This is also an instance of struct oop_readable */ struct oop_readable readable; /* first */ oop_readable_call *readable_callback; void *readable_callback_user; @@ -288,9 +343,10 @@ typedef struct InputFile { int counts[art_MaxState][RCI_max]; char path[]; -} InputFile; +}; struct Article { + ISNODE(Article); ArtState state; int midlen; InputFile *ipf; @@ -308,10 +364,10 @@ struct Article { X(DROPPING) \ X(DROPPED) -typedef enum { +enum StateMachineState { #define SMS_DEF_ENUM(s) sm_##s, SMS_LIST(SMS_DEF_ENUM) -} StateMachineState; +}; static const char *sms_names[]= { #define SMS_DEF_NAME(s) #s , @@ -322,8 +378,8 @@ static const char *sms_names[]= { struct Conn { ISNODE(Conn); int fd, max_queue, stream; - LIST(Article) queue; /* not yet told peer, or CHECK said send it */ - LIST(Article) sent; /* offered/transmitted - in xmit or waiting reply */ + ArticleList queue; /* not yet told peer, or CHECK said send it */ + ArticleList sent; /* offered/transmitted - in xmit or waiting reply */ struct iovec xmit[CONNIOVS]; XmitDetails xmitd[CONNIOVS]; int xmitu; @@ -334,9 +390,9 @@ struct Conn { static oop_source *loop; -static int nconns; -static LIST(Conn) idle, working, full; -static LIST(Article) *queue; +static int nconns, until_connect; +static ConnList idle, working, full; +static ArticleList queue; static char *path_lock, *path_flushing, *path_defer; @@ -349,17 +405,6 @@ static InputFile *main_input_file, *flushing_input_file, *backlog_input_file; static int sm_period_counter; -/*----- function predeclarations -----*/ - -static void conn_check_work(Conn *conn); - -static int filemon_init(void); -static void filemon_setfile(int mainfeed_fd, const char *mainfeed_path); -static void filemon_callback(void); - -static void statemc_setstate(StateMachineState newsms, int periods, - const char *forlog, const char *why); - /*========== logging ==========*/ static void logcore(int sysloglevel, const char *fmt, ...) @@ -493,7 +538,7 @@ static void check_isreg(const struct stat *stab, const char *path, } static void xfstat(int fd, struct stat *stab_r, const char *what) { - int r= fstab(fd, stab_r); + int r= fstat(fd, stab_r); if (r) sysdie("could not fstat %s", what); } @@ -515,6 +560,13 @@ static void xlstat_isreg(const char *path, struct stat *stab, check_isreg(stab, path, what); } +static void setnonblock(int fd, int nonblocking) { + int r= fcntl(fd, F_GETFL); if (r<0) sysdie("setnonblocking fcntl F_GETFL"); + if (nonblocking) r |= O_NONBLOCK; + else r &= ~O_NONBLOCK; + r= fcntl(fd, F_SETFL, r); if (r<0) sysdie("setnonblocking fcntl F_SETFL"); +} + static int samefile(const struct stat *a, const struct stat *b) { assert(S_ISREG(a->st_mode)); assert(S_ISREG(b->st_mode)); @@ -522,6 +574,23 @@ static int samefile(const struct stat *a, const struct stat *b) { a->st_dev == b->st_dev); } +static char *sanitise(const char *input, int len) { + static char sanibuf[100]; /* returns pointer to this buffer! */ + + const char *p= input; + char *q= sanibuf; + *q++= '`'; + for (;;) { + if (q > sanibuf+sizeof(sanibuf)-8) { strcpy(q,"'.."); break; } + int c; + if (len<=0 || !(c= *p++)) { *q++= '\''; *q=0; break; } + if (c>=' ' && c<=126 && c!='\\') { *q++= c; continue; } + sprintf(q,"\\x%02x",c); + q += 4; + } + return sanibuf; +} + /*========== making new connections ==========*/ static int connecting_sockets[2]= {-1,-1}; @@ -529,7 +598,7 @@ static pid_t connecting_child; static void connect_attempt_discard(void) { if (connecting_sockets[0]) - cancel_fd(connecting_sockets[0]); + loop->cancel_fd(connecting_sockets[0]); perhaps_close(&connecting_sockets[0]); perhaps_close(&connecting_sockets[1]); @@ -569,11 +638,11 @@ static void *connchild_event(oop_source *lp, int fd, oop_event e, void *u) { assert(got==connecting_child); connecting_child= 0; if (WIFEXITED(status) && - (WEXITSTATUS(status) != 0 + (WEXITSTATUS(status) != 0 && WEXITSTATUS(status) != CONNCHILD_ESTATUS_STREAM && WEXITSTATUS(status) != CONNCHILD_ESTATUS_NOSTREAM)) { /* child already reported the problem */ - } else if (WIFSIGNALED(status) && WTERMSIG(status) == SIGALARM) { + } else if (WIFSIGNALED(status) && WTERMSIG(status) == SIGALRM) { warn("connect: connection attempt timed out"); } else if (!WIFEXITED(status)) { report_child_status("connect", status); @@ -583,7 +652,7 @@ static void *connchild_event(oop_source *lp, int fd, oop_event e, void *u) { /* child is still running apparently, report the socket problem */ if (rs < 0) syswarn("connect: read from child socket failed"); - else if (e == OOP_EXCEPTIONN) + else if (e == OOP_EXCEPTION) warn("connect: unexpected exception on child socket"); else warn("connect: unexpected EOF on child socket"); @@ -598,13 +667,14 @@ static void *connchild_event(oop_source *lp, int fd, oop_event e, void *u) { } CHK(level, SOL_SOCKET); CHK(type, SCM_RIGHTS); - CHK(len, CMSG_LEN(sizeof(conn-b>fd))); + CHK(len, CMSG_LEN(sizeof(conn->fd))); #undef CHK - if (CMSG_NXTHDR,&msg,h) { die("connect: child sent many cmsgs"); goto x; } + if (CMSG_NXTHDR(&msg,h)) { die("connect: child sent many cmsgs"); goto x; } memcpy(&conn->fd, CMSG_DATA(h), sizeof(conn->fd)); + int status; pid_t got= waitpid(connecting_child, &status, 0); if (got==-1) sysdie("connect: real wait for child"); assert(got == connecting_child); @@ -619,7 +689,7 @@ static void *connchild_event(oop_source *lp, int fd, oop_event e, void *u) { fatal("connect: child gave unexpected exit status %d", es); } - set nonblocking; + setnonblock(conn->fd, 1); /* Phew! */ LIST_ADDHEAD(idle, conn); @@ -643,7 +713,7 @@ static void connect_start(void) { notice("starting connection attempt"); - r= socketpair(AF_UNIX, SOCK_STREAM, 0, connecting_sockets); + int r= socketpair(AF_UNIX, SOCK_STREAM, 0, connecting_sockets); if (r) { syswarn("connect: cannot create socketpair for child"); goto x; } connecting_child= xfork("connection"); @@ -658,12 +728,8 @@ static void connect_start(void) { alarm(connection_setup_timeout); if (NNTPconnect(remote_host, port, &cn_from, &cn_to, buf) < 0) { - if (buf[0]) { - sanitise_inplace(buf); - fatal("connect: rejected: %s", buf); - } else { - sysfatal("connect: connection attempt failed"); - } + if (buf[0]) fatal("connect: rejected: %s", sanitise(buf)); + else sysfatal("connect: connection attempt failed"); } if (NNTPsendpassword(remote_host, cn_from, cn_to) < 0) sysfatal("connect: authentication failed"); @@ -680,19 +746,16 @@ static void connect_start(void) { } int l= strlen(buf); assert(l>=1); - if (buf[-1]!='\n') { - sanitise_inplace(buf); + if (buf[-1]!='\n') fatal("connect: response to MODE STREAM is too long: %.100s...", - remote_host, buf); - } - l--; if (l>0 && buf[1-]=='\r') l--; + remote_host, sanitise(buf)); + l--; if (l>0 && buf[l-1]=='\r') l--; buf[l]= 0; char *ep; int rcode= strtoul(buf,&ep,10); - if (ep != buf[3]) { - sanitise_inplace(buf); - fatal("connect: bad response to MODE STREAM: %.50s", buf); - } + if (ep != &buf[3]) + fatal("connect: bad response to MODE STREAM: %.50s", sanitise(buf)); + switch (rcode) { case 203: exitstatus= CONNCHILD_ESTATUS_STREAM; @@ -701,8 +764,8 @@ static void connect_start(void) { case 500: break; default: - sanitise_inplace(buf); - warn("connect: unexpected response to MODE STREAM: %.50s", buf); + warn("connect: unexpected response to MODE STREAM: %.50s", + sanitise(buf)); exitstatus= 2; break; } @@ -727,7 +790,7 @@ static void connect_start(void) { if (r) sysdie("connect: close child socket in parent"); on_fd_read_except(connecting_sockets[0], connchild_event); - return OOP_CONTINUE; + return; x: connect_attempt_discard(); @@ -746,9 +809,9 @@ static void check_master_queue(void) { conn_assign_one_article(&working, &last_assigned); } else if (idle.head) { conn_assign_one_article(&idle, &last_assigned); - } else if (nconns < maxconns && queue.count >= max_queue_per_conn && - !connecting_child && !connect_delay) { - connect_delay= reconnect_delay_periods; + } else if (nconns < max_connections && queue.count >= max_queue_per_conn && + !connecting_child && !until_connect) { + until_connect= reconnect_delay_periods; connect_start(); } else { break; @@ -757,8 +820,7 @@ static void check_master_queue(void) { conn_check_work(last_assigned); } -static void conn_assign_one_article(LIST(Conn) *connlist, - Conn **last_assigned) { +static void conn_assign_one_article(ConnList *connlist, Conn **last_assigned) { Conn *conn= connlist->head; LIST_REMOVE(*connlist, conn); @@ -778,7 +840,7 @@ static int conn_total_queued_articles(Conn *conn) { return conn->sent.count + conn->queue.count; } -static LIST(Conn) *conn_determine_right_list(Conn *conn) { +static ConnList *conn_determine_right_list(Conn *conn) { int inqueue= conn_total_queued_articles(conn); assert(inqueue <= max_queue); if (inqueue == 0) return &idle; @@ -841,7 +903,7 @@ static void vconnfail(Conn *conn, const char *fmt, va_list al) { close(conn->fd); free(conn); - connect_delay= reconnect_delay_periods; + until_connect= reconnect_delay_periods; check_master_queue(); } @@ -1109,22 +1171,12 @@ static void *peer_rd_ok(oop_source *lp, oop_read *oread, oop_event ev, } assert(ev == OOP_RD_OK); + char *sani= sanitise(data, recsz); + char *ep; unsigned long code= strtoul(data, &ep, 10); if (ep != data+3 || *ep != ' ' || data[0]=='0') { - char sanibuf[100]; - const char *p= data; - char *q= sanibuf; - *q++= '`'; - for (;;) { - if (q > sanibuf+sizeof(sanibuf)-8) { strcpy(q,"..."); break; } - int c= *p++; - if (!c) { *q++= '\''; break; } - if (c>=' ' && c<=126 && c!='\\') { *q++= c; continue; } - sprintf(q,"\\x%02x",c); - q += 4; - } - connfail(conn, "badly formatted response from peer: %s", sanibuf); + connfail(conn, "badly formatted response from peer: %s", sani); return OOP_CONTINUE; } @@ -2195,18 +2247,18 @@ static const char *debug_ipf_path(InputFile *ipf) { EVERY(period, {PERIOD_SECONDS,0}, { debug("PERIOD" - " sms=%s[%d] queue=%d connect_delay=%d" + " sms=%s[%d] queue=%d until_connect=%d" " input_files" DEBUGF_IPF(main) DEBUGF_IPF(old) DEBUGF_FMT(flushing) " conns idle=%d working=%d full=%d" " children connecting=%ld inndcomm_child" , - sms_names[sms], sm_period_counter, queue.count, connect_delay, + sms_names[sms], sm_period_counter, queue.count, until_connect, DEBUG_IPF(main), DEBUG_IPF(flushing), DEBUG_IPF(flushing), idle.count, working.count, full.count, (long)connecting_child, (long)inndcomm_child ); - if (connect_delay) connect_delay--; + if (until_connect) until_connect--; poll_backlog_file(); if (!backlog_input_file) close_defer(); /* want to start on a new backlog */