+ postfork_inputfile(flushing_input_file);
+
+ Conn *conn;
+ for (conn=LIST_HEAD(conns); conn; conn=LIST_NEXT(conn))
+ conn_closefd(conn,"(in child) ");
+
+ postfork_stdio(defer, "defer file ", path_defer);
+}
+
+typedef struct Every Every;
+struct Every {
+ struct timeval interval;
+ int fixed_rate;
+ void (*f)(void);
+};
+
+static void every_schedule(Every *e, struct timeval base);
+
+static void *every_happens(oop_source *lp, struct timeval base, void *e_v) {
+ Every *e= e_v;
+ e->f();
+ if (!e->fixed_rate) xgettimeofday(&base);
+ every_schedule(e, base);
+ return OOP_CONTINUE;
+}
+
+static void every_schedule(Every *e, struct timeval base) {
+ struct timeval when;
+ timeradd(&base, &e->interval, &when);
+ loop->on_time(loop, when, every_happens, e);
+}
+
+static void every(int interval, int fixed_rate, void (*f)(void)) {
+ Every *e= xmalloc(sizeof(*e));
+ e->interval.tv_sec= interval;
+ e->interval.tv_usec= 0;
+ e->fixed_rate= fixed_rate;
+ e->f= f;
+ struct timeval now;
+ xgettimeofday(&now);
+ every_schedule(e, now);
+}
+
+static void filepoll(void) {
+ filemon_callback(main_input_file);
+ filemon_callback(flushing_input_file);
+}
+
+static char *debug_report_ipf(InputFile *ipf) {
+ if (!ipf) return xasprintf("none");
+
+ const char *slash= strrchr(ipf->path,'/');
+ const char *path= slash ? slash+1 : ipf->path;
+
+ return xasprintf("%p/%s:ip=%ld,off=%ld,fd=%d%s",
+ ipf, path,
+ ipf->inprogress, (long)ipf->offset,
+ ipf->fd, ipf->rd ? "+" : "");
+}
+
+static void period(void) {
+ char *dipf_main= debug_report_ipf(main_input_file);
+ char *dipf_flushing= debug_report_ipf(flushing_input_file);
+ char *dipf_backlog= debug_report_ipf(backlog_input_file);
+
+ debug("PERIOD"
+ " sms=%s[%d] conns=%d queue=%d until_connect=%d"
+ " input_files main:%s old:%s flushing:%s"
+ " children connecting=%ld inndcomm=%ld"
+ ,
+ sms_names[sms], sm_period_counter,
+ conns.count, queue.count, until_connect,
+ dipf_main, dipf_flushing, dipf_backlog,
+ (long)connecting_child, (long)inndcomm_child
+ );
+
+ free(dipf_main);
+ free(dipf_flushing);
+ free(dipf_backlog);
+
+ if (until_connect) until_connect--;
+
+ poll_backlog_file();
+ if (!backlog_input_file) close_defer(); /* want to start on a new backlog */
+ statemc_period_poll();
+ check_assign_articles();
+ check_idle_conns();
+}
+
+
+/*========== option parsing ==========*/
+
+static void vbadusage(const char *fmt, va_list al) NORET_PRINTF(1,0);
+static void vbadusage(const char *fmt, va_list al) {
+ char *m= xvasprintf(fmt,al);
+ fprintf(stderr, "bad usage: %s\n"
+ "say --help for help, or read the manpage\n",
+ m);
+ if (become_daemon)
+ syslog(LOG_CRIT,"innduct: invoked with bad usage: %s",m);
+ exit(8);
+}
+
+/*---------- generic option parser ----------*/
+
+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,
+};
+
+typedef struct Option Option;
+typedef void OptionParser(const Option*, const char *val);
+
+struct Option {
+ int shrt;
+ const char *lng, *formarg;
+ void *store;
+ OptionParser *fn;
+ int intval;
+};
+
+static void parse_options(const Option *options, char ***argvp) {
+ /* on return *argvp is first non-option arg; argc is not updated */
+
+ for (;;) {
+ const char *arg= *++(*argvp);
+ if (!arg) break;
+ if (*arg != '-') break;
+ if (!strcmp(arg,"--")) { arg= *++(*argvp); break; }
+ int a;
+ while ((a= *++arg)) {
+ const Option *o;
+ if (a=='-') {
+ arg++;
+ char *equals= strchr(arg,'=');
+ int len= equals ? (equals - arg) : strlen(arg);
+ for (o=options; o->shrt || 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->formarg) {
+ if (equals) badusage("option --%s does not take a value",o->lng);
+ arg= 0;
+ } else if (equals) {
+ arg= equals+1;
+ } else {
+ arg= *++(*argvp);
+ if (!arg) badusage("option --%s needs a value for %s",
+ o->lng, o->formarg);
+ }
+ o->fn(o, arg);
+ break; /* eaten the whole argument now */
+ }
+ for (o=options; o->shrt || o->lng; o++)
+ if (a == o->shrt)
+ goto found_short;
+ badusage("unknown short option -%c",a);
+ found_short:
+ if (!o->formarg) {
+ o->fn(o,0);
+ } else {
+ if (!*++arg) {
+ arg= *++(*argvp);
+ if (!arg) badusage("option -%c needs a value for %s",
+ o->shrt, o->formarg);
+ }
+ o->fn(o,arg);
+ break; /* eaten the whole argument now */
+ }
+ }
+ }
+}
+
+#define DELIMPERHAPS(delim,str) (str) ? (delim) : "", (str) ? (str) : ""
+
+static void print_options(const Option *options, FILE *f) {
+ const Option *o;
+ for (o=options; o->shrt || o->lng; o++) {
+ char shrt[2] = { o->shrt, 0 };
+ char *optspec= xasprintf("%s%s%s%s%s",
+ o->shrt ? "-" : "", shrt,
+ o->shrt && o->lng ? "|" : "",
+ DELIMPERHAPS("--", o->lng));
+ fprintf(f, " %s%s%s\n", optspec, DELIMPERHAPS(" ", o->formarg));
+ free(optspec);
+ }
+}
+
+/*---------- 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->lng);
+ int *store= o->store;
+ *store= ul;
+}
+
+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->lng);
+}
+
+static void op_string(const Option *o, const char *val) {
+ const char **store= o->store;
+ *store= 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->lng);
+
+ if (!*ep || !strcmp(ep,"s") || !strcmp(ep,"sec")) unit= 1;
+ else if (!strcmp(ep,"m") || !strcmp(ep,"min")) unit= 60;
+ else if (!strcmp(ep,"h") || !strcmp(ep,"hour")) unit= 3600;
+ else if (!strcmp(ep,"d") || !strcmp(ep,"day")) unit= 86400;
+ else if (!strcmp(ep,"das")) unit= 10;
+ else if (!strcmp(ep,"hs")) unit= 100;
+ else if (!strcmp(ep,"ks")) unit= 1000;
+ else if (!strcmp(ep,"Ms")) unit= 1000000;
+ 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->lng);
+ *store= v;
+}
+
+static void op_setint(const Option *o, const char *val) {
+ int *store= o->store;
+ *store= o->intval;
+}
+
+/*---------- specific options ----------*/
+
+static void help(const Option *o, const char *val);
+
+static const Option innduct_options[]= {
+{'f',"feedfile", "F", &feedfile, op_string },
+{'q',"quiet-multiple", 0, &quiet_multiple, op_setint, 1 },
+{0,"no-daemon", 0, &become_daemon, op_setint, 0 },
+{0,"no-streaming", 0, &try_stream, op_setint, 0 },
+{'C',"inndconf", "F", &inndconffile, op_string },
+{'P',"port", "PORT", &port, op_integer },
+{0,"help", 0, 0, help },
+
+{0,"max-connections", "N", &max_connections, op_integer },
+{0,"max-queue-per-conn", "N", &max_queue_per_conn, op_integer },
+{0,"feedfile-flush-size","BYTES", &target_max_feedfile_size, op_integer },
+{0,"period-interval", "TIME", &period_seconds, op_seconds },
+
+{0,"connection-timeout", "TIME", &connection_setup_timeout, op_seconds },
+{0,"stuck-flush-timeout","TIME", &inndcomm_flush_timeout, op_seconds },
+
+{0,"no-check-proportion", "PERCENT", &nocheck_thresh, op_double },
+{0,"no-check-response-time","ARTICLES", &nocheck_decay, op_double },
+
+{0,"reconnect-interval", "PERIOD", &reconnect_delay_periods, op_seconds },
+{0,"flush-retry-interval", "PERIOD", &flushfail_retry_periods, op_seconds },
+{0,"earliest-deferred-retry","PERIOD", &backlog_retry_minperiods, op_seconds },
+{0,"backlog-rescan-interval","PERIOD",&backlog_spontrescan_periods,op_seconds},
+{0,"max-flush-interval", "PERIOD", &spontaneous_flush_periods,op_seconds },
+{0,"idle-timeout", "PERIOD", &need_activity_periods, op_seconds },
+
+{0,"max-bad-input-data-ratio","PERCENT", &max_bad_data_ratio, op_double },
+{0,"max-bad-input-data-init", "PERCENT", &max_bad_data_initial, op_integer },
+
+{0,0}