1 /*  $Id: innd.c 10514 2021-01-15 22:35:44Z iulius $
2 **
3 **  Variable definitions, miscellany, and main().
4 */
5 
6 #include "config.h"
7 #include "clibrary.h"
8 
9 #include "inn/innconf.h"
10 #include "inn/messages.h"
11 #include "inn/newsuser.h"
12 #include "innperl.h"
13 
14 #define DEFINE_DATA
15 #include "innd.h"
16 #include "inn/ov.h"
17 
18 
19 bool		Debug = false;
20 bool		NNRPTracing = false;
21 bool		StreamingOff = false ; /* default is we can stream */
22 bool		Tracing = false;
23 bool		DoCancels = true;
24 char		LogName[] = "SERVER";
25 int		ErrorCount = IO_ERROR_COUNT;
26 OPERATINGMODE	Mode = OMrunning;
27 int		RemoteLimit = REMOTELIMIT;
28 time_t		RemoteTimer = REMOTETIMER;
29 int		RemoteTotal = REMOTETOTAL;
30 bool		ThrottledbyIOError = false;
31 
32 static char	*PID = NULL;
33 
34 /* Default values for the syntaxchecks parameter in inn.conf. */
35 bool laxmid = false;
36 
37 /* Signal handling.  If we receive a signal that should kill the server,
38    killer_signal is set to the signal number that we received.  This isn't
39    what indicates that we should terminate; that's the separate global
40    variable GotTerminate, used in CHANreadloop. */
41 static volatile sig_atomic_t killer_signal = 0;
42 
43 /* Whether our self-maintained logs (stdout and stderr) are buffered, used
44    to determine whether fflush is needed.  Should be static. */
45 bool BufferedLogs = true;
46 
47 /* FILEs for logs and error logs.  Everything should just use stdout and
48    stderr. */
49 FILE *Log = NULL;
50 FILE *Errlog = NULL;
51 
52 /* Some very old systems have a completely inadequate BUFSIZ buffer size, at
53    least for our logging purposes. */
54 #if BUFSIZ < 4096
55 # define LOG_BUFSIZ 4096
56 #else
57 # define LOG_BUFSIZ BUFSIZ
58 #endif
59 
60 /* Internal prototypes. */
61 static void             catch_terminate(int sig);
62 static void             xmalloc_abort(const char *what, size_t size,
63                                       const char *file, int line);
64 static void             Usage(void) __attribute__ ((__noreturn__));
65 
66 /* header table initialization */
67 #define ARTHEADERINIT(name, type) {name, type, sizeof(name) - 1}
68 const ARTHEADER ARTheaders[] = {
69   /*		 Name			Type */
70   ARTHEADERINIT("Approved",		HTstd),
71 /* #define HDR__APPROVED			0 */
72   ARTHEADERINIT("Control",		HTstd),
73 /* #define HDR__CONTROL				1 */
74   ARTHEADERINIT("Date",			HTreq),
75 /* #define HDR__DATE				2 */
76   ARTHEADERINIT("Distribution",		HTstd),
77 /* #define HDR__DISTRIBUTION			3 */
78   ARTHEADERINIT("Expires",		HTstd),
79 /* #define HDR__EXPIRES				4 */
80   ARTHEADERINIT("From",			HTreq),
81 /* #define HDR__FROM				5 */
82   ARTHEADERINIT("Lines",		HTobs),
83 /* #define HDR__LINES				6 */
84   ARTHEADERINIT("Message-ID",		HTreq),
85 /* #define HDR__MESSAGE_ID			7 */
86   ARTHEADERINIT("Newsgroups",		HTreq),
87 /* #define HDR__NEWSGROUPS			8 */
88   ARTHEADERINIT("Path",			HTreq),
89 /* #define HDR__PATH				9 */
90   ARTHEADERINIT("Reply-To",		HTstd),
91 /* #define HDR__REPLY_TO			10 */
92   ARTHEADERINIT("Sender",		HTstd),
93 /* #define HDR__SENDER				11 */
94   ARTHEADERINIT("Subject",		HTreq),
95 /* #define HDR__SUBJECT				12 */
96   ARTHEADERINIT("Supersedes",		HTstd),
97 /* #define HDR__SUPERSEDES			13 */
98   ARTHEADERINIT("Bytes",		HTobs),
99 /* #define HDR__BYTES				14 */
100   ARTHEADERINIT("Also-Control",		HTobs),
101 /* #define HDR__ALSOCONTROL			15 */
102   ARTHEADERINIT("References",		HTstd),
103 /* #define HDR__REFERENCES			16 */
104   ARTHEADERINIT("Xref",			HTsav),
105 /* #define HDR__XREF				17 */
106   ARTHEADERINIT("Keywords",		HTstd),
107 /* #define HDR__KEYWORDS			18 */
108   ARTHEADERINIT("X-Trace",		HTobs),
109 /* #define HDR__XTRACE				19 */
110   ARTHEADERINIT("Date-Received",	HTobs),
111 /* #define HDR__DATERECEIVED			20 */
112   ARTHEADERINIT("Posted",		HTobs),
113 /* #define HDR__POSTED				21 */
114   ARTHEADERINIT("Posting-Version",	HTobs),
115 /* #define HDR__POSTINGVERSION			22 */
116   ARTHEADERINIT("Received",		HTobs),
117 /* #define HDR__RECEIVED			23 */
118   ARTHEADERINIT("Relay-Version",	HTobs),
119 /* #define HDR__RELAYVERSION			24 */
120   ARTHEADERINIT("NNTP-Posting-Host",	HTobs),
121 /* #define HDR__NNTPPOSTINGHOST			25 */
122   ARTHEADERINIT("Followup-To",		HTstd),
123 /* #define HDR__FOLLOWUPTO			26 */
124   ARTHEADERINIT("Organization",		HTstd),
125 /* #define HDR__ORGANIZATION			27 */
126   ARTHEADERINIT("Content-Type",		HTstd),
127 /* #define HDR__CONTENTTYPE			28 */
128   ARTHEADERINIT("Content-Base",		HTstd),
129 /* #define HDR__CONTENTBASE			29 */
130   ARTHEADERINIT("Content-Disposition",	HTstd),
131 /* #define HDR__CONTENTDISPOSITION		30 */
132   ARTHEADERINIT("X-Newsreader",		HTstd),
133 /* #define HDR__XNEWSREADER			31 */
134   ARTHEADERINIT("X-Mailer",		HTstd),
135 /* #define HDR__XMAILER				32 */
136   ARTHEADERINIT("X-Newsposter",		HTstd),
137 /* #define HDR__XNEWSPOSTER			33 */
138   ARTHEADERINIT("X-Cancelled-By",	HTstd),
139 /* #define HDR__XCANCELLEDBY			34 */
140   ARTHEADERINIT("X-Canceled-By",	HTstd),
141 /* #define HDR__XCANCELEDBY			35 */
142   ARTHEADERINIT("Cancel-Key",		HTstd),
143 /* #define HDR__CANCELKEY			36 */
144   ARTHEADERINIT("User-Agent",		HTstd),
145 /* #define HDR__USER_AGENT			37 */
146   ARTHEADERINIT("X-Original-Message-ID",	HTstd),
147 /* #define HDR__X_ORIGINAL_MESSAGE_ID		38 */
148   ARTHEADERINIT("Cancel-Lock",		HTstd),
149 /* #define HDR__CANCEL_LOCK			39 */
150   ARTHEADERINIT("Content-Transfer-Encoding",	HTstd),
151 /* #define HDR__CONTENT_TRANSFER_ENCODING	40 */
152   ARTHEADERINIT("Face",			HTstd),
153 /* #define HDR__FACE				41 */
154   ARTHEADERINIT("Injection-Info",	HTstd),
155 /* #define HDR__INJECTION_INFO			42 */
156   ARTHEADERINIT("List-ID",		HTstd),
157 /* #define HDR__LIST_ID				43 */
158   ARTHEADERINIT("MIME-Version",		HTstd),
159 /* #define HDR__MIME_VERSION			44 */
160   ARTHEADERINIT("Originator",		HTstd),
161 /* #define HDR__ORIGINATOR			45 */
162   ARTHEADERINIT("X-Auth",		HTstd),
163 /* #define HDR__X_AUTH				46 */
164   ARTHEADERINIT("X-Complaints-To",	HTobs),
165 /* #define HDR__X_COMPLAINTS_TO			47 */
166   ARTHEADERINIT("X-Face",		HTstd),
167 /* #define HDR__X_FACE				48 */
168   ARTHEADERINIT("X-HTTP-UserAgent",	HTstd),
169 /* #define HDR__X_HTTP_USERAGENT		49 */
170   ARTHEADERINIT("X-HTTP-Via",		HTstd),
171 /* #define HDR__X_HTTP_VIA			50 */
172   ARTHEADERINIT("X-Modbot",		HTstd),
173 /* #define HDR__X_MODBOT			51 */
174   ARTHEADERINIT("X-Modtrace",		HTstd),
175 /* #define HDR__X_MODTRACE			52 */
176   ARTHEADERINIT("X-No-Archive",		HTobs),
177 /* #define HDR__X_NO_ARCHIVE			53 */
178   ARTHEADERINIT("X-Original-Trace",	HTstd),
179 /* #define HDR__X_ORIGINAL_TRACE		54 */
180   ARTHEADERINIT("X-Originating-IP",	HTstd),
181 /* #define HDR__X_ORIGINATING_IP		55 */
182   ARTHEADERINIT("X-PGP-Key",		HTstd),
183 /* #define HDR__X_PGP_KEY			56 */
184   ARTHEADERINIT("X-PGP-Sig",		HTstd),
185 /* #define HDR__X_PGP_SIG			57 */
186   ARTHEADERINIT("X-Poster-Trace",	HTstd),
187 /* #define HDR__X_POSTER_TRACE			58 */
188   ARTHEADERINIT("X-Postfilter",		HTstd),
189 /* #define HDR__X_POSTFILTER			59 */
190   ARTHEADERINIT("X-Proxy-User",		HTstd),
191 /* #define HDR__X_PROXY_USER			60 */
192   ARTHEADERINIT("X-Submissions-To",	HTstd),
193 /* #define HDR__X_SUBMISSIONS_TO		61 */
194   ARTHEADERINIT("X-Usenet-Provider",	HTstd),
195 /* #define HDR__X_USENET_PROVIDER		62 */
196   ARTHEADERINIT("In-Reply-To",		HTstd),
197 /* #define HDR__IN_REPLY_TO			63 */
198   ARTHEADERINIT("Injection-Date",	HTstd),
199 /* #define HDR__INJECTION_DATE			64 */
200   ARTHEADERINIT("NNTP-Posting-Date",    HTobs),
201 /* #define HDR__NNTP_POSTING_DATE               65 */
202   ARTHEADERINIT("X-User-ID",            HTstd),
203 /* #define HDR__X_USER_ID                       66 */
204   ARTHEADERINIT("X-Auth-Sender",        HTstd),
205 /* #define HDR__X_AUTH_SENDER                   67 */
206   ARTHEADERINIT("X-Original-NNTP-Posting-Host", HTstd),
207 /* #define HDR__X_ORIGINAL_NNTP_POSTING_HOST    68 */
208   ARTHEADERINIT("Original-Sender",      HTstd),
209 /* #define HDR__ORIGINAL_SENDER                 69 */
210   ARTHEADERINIT("NNTP-Posting-Path",    HTobs),
211 /* #define HDR__NNTP_POSTING_PATH               70 */
212   ARTHEADERINIT("Archive",              HTstd),
213 /* #define HDR__ARCHIVE                         71 */
214   ARTHEADERINIT("Archived-At",          HTstd),
215 /* #define HDR__ARCHIVED_AT                     72 */
216   ARTHEADERINIT("Summary",              HTstd),
217 /* #define HDR__SUMMARY                         73 */
218   ARTHEADERINIT("Comments",             HTstd),
219 /* #define HDR__COMMENTS                        74 */
220   ARTHEADERINIT("Jabber-ID",            HTstd),
221 /* #define HDR__JABBER_ID                       75 */
222 };
223 /* #define MAX_ARTHEADER                        76 */
224 
225 
226 /*
227 **  Signal handler to catch SIGTERM and similar signals and queue a clean
228 **  shutdown.
229 */
230 static void
catch_terminate(int sig)231 catch_terminate(int sig)
232 {
233     GotTerminate = true;
234     killer_signal = sig;
235 
236 #ifndef HAVE_SIGACTION
237     xsignal(sig, catch_terminate);
238 #endif
239 }
240 
241 
242 /*
243 **  Memory allocation failure handler.  Instead of the default behavior of
244 **  just exiting, call abort to generate a core dump.
245 */
246 static void
xmalloc_abort(const char * what,size_t size,const char * file,int line)247 xmalloc_abort(const char *what, size_t size, const char *file, int line)
248 {
249     fprintf(stderr, "SERVER cant %s %lu bytes at %s line %d: %s", what,
250             (unsigned long) size, file, line, strerror(errno));
251     syslog(LOG_CRIT, "SERVER cant %s %lu bytes at %s line %d: %m", what,
252            (unsigned long) size, file, line);
253     abort();
254 }
255 
256 
257 /*
258 **  The name is self-explanatory.
259 */
260 void
CleanupAndExit(int status,const char * why)261 CleanupAndExit(int status, const char *why)
262 {
263     int result;
264 
265     result = sd_notify(false, "STOPPING=1");
266     if (result < 0)
267         warn("cannot notify systemd of stopping: %s", strerror(-result));
268 
269     JustCleanup();
270     if (why)
271         syslog(LOG_WARNING, "SERVER shutdown %s", why);
272     else
273         syslog(LOG_WARNING, "SERVER shutdown received signal %lu",
274                (unsigned long) killer_signal);
275 
276     exit(status);
277 }
278 
279 
280 /*
281 **  Close down all parts of the system (e.g., before calling exit or exec).
282 */
283 void
JustCleanup(void)284 JustCleanup(void)
285 {
286     SITEflushall(false);
287     CCclose();
288     LCclose();
289     NCclose();
290     RCclose();
291     ICDclose();
292     InndHisClose();
293     ARTclose();
294     if (innconf->enableoverview)
295         OVclose();
296     NGclose();
297     SMshutdown();
298 
299 #if DO_PERL
300     PerlFilter(false);
301     PerlClose();
302 #endif
303 
304 #if DO_PYTHON
305     PYclose();
306 #endif
307 
308     CHANshutdown();
309     innconf_free(innconf);
310     innconf = NULL;
311 
312     sleep(1);
313 
314     if (unlink(PID) < 0 && errno != ENOENT)
315         syslog(LOG_ERR, "SERVER cant unlink %s: %m", PID);
316 }
317 
318 
319 /*
320 **  Flush one log file, re-establishing buffering if necessary.  stdout is
321 **  block-buffered, stderr is line-buffered.
322 */
323 void
ReopenLog(FILE * F)324 ReopenLog(FILE *F)
325 {
326     char *path, *oldpath;
327     int mask;
328 
329     if (Debug)
330 	return;
331 
332     path = concatpath(innconf->pathlog,
333                       (F == stdout) ? INN_PATH_LOGFILE : INN_PATH_ERRLOG);
334     oldpath = concat(path, ".old", (char *) 0);
335     if (rename(path, oldpath) < 0)
336         syswarn("SERVER cant rename %s to %s", path, oldpath);
337     free(oldpath);
338     mask = umask(033);
339     if (freopen(path, "a", F) != F)
340         sysdie("SERVER cant freopen %s", path);
341     free(path);
342     umask(mask);
343     if (BufferedLogs)
344         setvbuf(F, NULL, (F == stdout) ? _IOFBF : _IOLBF, LOG_BUFSIZ);
345 }
346 
347 
348 /*
349 **  Print a usage message and exit.
350 */
351 static void
Usage(void)352 Usage(void)
353 {
354     fprintf(stderr, "Usage error.\n");
355     exit(1);
356 }
357 
358 
359 int
main(int ac,char * av[])360 main(int ac, char *av[])
361 {
362     const char *name, *p;
363     char *path;
364     bool flag;
365     static char		WHEN[] = "PID file";
366     int			i;
367     size_t              j;
368     char		buff[SMBUF];
369     FILE		*F;
370     bool		ShouldFork;
371     bool		ShouldRenumber;
372     bool		ShouldSyntaxCheck;
373     pid_t		pid;
374     int                 status;
375 #if defined(_DEBUG_MALLOC_INC)
376     union malloptarg	m;
377 #endif	/* defined(_DEBUG_MALLOC_INC) */
378 #if DO_PERL
379     char                *path1, *path2;
380 #endif
381 #if defined(DO_PERL) || defined(DO_PYTHON)
382     bool                filter = true;
383 #endif
384 
385     /* Set up the pathname, first thing, and teach our error handlers about
386        the name of the program. */
387     name = av[0];
388     if (name == NULL || *name == '\0')
389 	name = "innd";
390     else {
391         p = strrchr(name, '/');
392         if (p != NULL)
393             name = p + 1;
394     }
395     message_program_name = name;
396     openlog(name, LOG_CONS | LOG_NDELAY, LOG_INN_SERVER);
397     message_handlers_die(2, message_log_stderr, message_log_syslog_crit);
398     message_handlers_warn(2, message_log_stderr, message_log_syslog_err);
399     message_handlers_notice(1, message_log_syslog_notice);
400 
401     /* Make sure innd is running as the correct user.  If it is started as
402        root, switch to running as the news user. */
403     ensure_news_user_grp(true, true);
404 
405     xsignal_enable_masking();
406 
407     /* Handle malloc debugging. */
408 #if	defined(_DEBUG_MALLOC_INC)
409     m.i = M_HANDLE_ABORT;
410     dbmallopt(MALLOC_WARN, &m);
411     dbmallopt(MALLOC_FATAL, &m);
412     m.i = 3;
413     dbmallopt(MALLOC_FILLAREA, &m);
414     m.i = 0;
415     dbmallopt(MALLOC_CKCHAIN, &m);
416     dbmallopt(MALLOC_CKDATA, &m);
417 #endif	/* defined(_DEBUG_MALLOC_INC) */
418 
419     /* Set defaults. */
420     TimeOut.tv_sec = DEFAULT_TIMEOUT;
421     TimeOut.tv_usec = 0;
422     ShouldFork = true;
423     ShouldRenumber = false;
424     ShouldSyntaxCheck = false;
425 
426     /* Set some options from inn.conf that can be overridden with
427        command-line options if they exist, so read inn.conf first. */
428     if (!innconf_read(NULL))
429         exit(1);
430 
431     /* Parse JCL. */
432     CCcopyargv(av);
433     while ((i = getopt(ac, av, "4:6:ac:CdfH:i:l:m:n:No:P:rsSt:T:uX:")) != EOF)
434 	switch (i) {
435 	default:
436 	    Usage();
437 	    /* NOTREACHED */
438         case '4':
439             if (innconf->bindaddress)
440                 free(innconf->bindaddress);
441             innconf->bindaddress = xstrdup(optarg);
442             break;
443         case '6':
444             if (innconf->bindaddress6)
445                 free(innconf->bindaddress6);
446             innconf->bindaddress6 = xstrdup(optarg);
447             break;
448 	case 'a':
449 	    AnyIncoming = true;
450 	    break;
451 	case 'c':
452 	    innconf->artcutoff = strtoul(optarg, NULL, 10);
453 	    break;
454  	case 'C':
455  	    DoCancels = false;
456   	    break;
457 	case 'd':
458 	    Debug = true;
459 	    break;
460 	case 'f':
461 	    ShouldFork = false;
462 	    break;
463 	case 'H':
464 	    RemoteLimit = atoi(optarg);
465 	    break;
466 	case 'i':
467 	    innconf->maxconnections = strtoul(optarg, NULL, 10);
468 	    break;
469 	case 'l':
470 	    innconf->maxartsize = strtoul(optarg, NULL, 10);
471 	    break;
472 	case 'm':
473 	    if (ModeReason)
474 		free(ModeReason);
475 	    switch (*optarg) {
476 	    default:
477 		Usage();
478 		/* NOTREACHED */
479 	    case 'g':	Mode = OMrunning;	break;
480 	    case 'p':	Mode = OMpaused;	break;
481 	    case 't':	Mode = OMthrottled;	break;
482 	    }
483 	    if (Mode != OMrunning)
484                 ModeReason = concat(OMpaused ? "Paus" : "Throttl",
485                                     "ed from the command line", (char *) 0);
486 	    break;
487 	case 'N':
488 #if defined(DO_PERL) || defined(DO_PYTHON)
489 	    filter = false;
490 #endif
491 	    break;
492 	case 'n':
493 	    switch (*optarg) {
494 	    default:
495 		Usage();
496 		/* NOTREACHED */
497 	    case 'n':	innconf->readerswhenstopped = false;	break;
498 	    case 'y':	innconf->readerswhenstopped = true;	break;
499 	    }
500 	    break;
501 	case 'o':
502 	    MaxOutgoing = atoi(optarg);
503 	    break;
504 	case 'P':
505 	    innconf->port = strtoul(optarg, NULL, 10);
506 	    break;
507 	case 'r':
508 	    ShouldRenumber = true;
509 	    break;
510 	case 's':
511 	    ShouldSyntaxCheck = true;
512 	    break;
513         case 'S':
514             RCreadlist();
515             exit(0);
516             break;
517 	case 't':
518 	    TimeOut.tv_sec = atol(optarg);
519 	    break;
520 	case 'T':
521 	    RemoteTotal = atoi(optarg);
522 	    break;
523 	case 'u':
524 	    BufferedLogs = false;
525 	    break;
526 	case 'X':
527 	    RemoteTimer = atoi(optarg);
528 	    break;
529         case 'Z':
530             StreamingOff = true;
531             break;
532 	}
533     ac -= optind;
534     if (ac != 0)
535 	Usage();
536     if (ModeReason != NULL && !innconf->readerswhenstopped)
537 	NNRPReason = xstrdup(ModeReason);
538 
539     if (ShouldSyntaxCheck) {
540 	if ((p = CCcheckfile((char **)NULL)) == NULL)
541 	    exit(0);
542 	fprintf(stderr, "%s\n", p + 2);
543 	exit(1);
544     }
545 
546     /* Initialize the checks to perform or not on article syntax. */
547     if ((innconf->syntaxchecks != NULL) && (innconf->syntaxchecks->count > 0)) {
548         for (j = 0; j < innconf->syntaxchecks->count; j++) {
549             if (innconf->syntaxchecks->strings[j] != NULL) {
550                 if (strcmp(innconf->syntaxchecks->strings[j], "laxmid") == 0) {
551                     laxmid = true;
552                 } else if (strcmp(innconf->syntaxchecks->strings[j],
553                                   "no-laxmid") == 0) {
554                     laxmid = false;
555                 } else {
556                     syslog(L_NOTICE, "Unknown \"%s\" value in syntaxchecks "
557                            "parameter in inn.conf",
558                            innconf->syntaxchecks->strings[j]);
559                 }
560             }
561         }
562     }
563 
564     /* Get the Path entry. */
565     if (innconf->pathhost == NULL) {
566 	syslog(L_FATAL, "%s No pathhost set", LogName);
567 	exit(1);
568     }
569     Path.used = strlen(innconf->pathhost) + 1;
570     Path.size = Path.used + 1;
571     Path.data = xmalloc(Path.size);
572     snprintf(Path.data, Path.size, "%s!", innconf->pathhost);
573     if (innconf->pathalias == NULL) {
574 	Pathalias.used = 0;
575 	Pathalias.data = NULL;
576     } else {
577 	Pathalias.used = strlen(innconf->pathalias) + 1;
578 	Pathalias.size = Pathalias.used + 1;
579 	Pathalias.data = xmalloc(Pathalias.size);
580 	snprintf(Pathalias.data, Pathalias.size, "%s!", innconf->pathalias);
581     }
582     if (innconf->pathcluster == NULL) {
583         Pathcluster.used = 0;
584         Pathcluster.data = NULL;
585     } else {
586         Pathcluster.used = strlen(innconf->pathcluster) + 1;
587         Pathcluster.size = Pathcluster.used + 1;
588         Pathcluster.data = xmalloc(Pathcluster.size);
589         snprintf(Pathcluster.data, Pathcluster.size, "%s!", innconf->pathcluster);
590     }
591     /* Trace history ? */
592     if (innconf->stathist != NULL) {
593         syslog(L_NOTICE, "logging hist stats to %s", innconf->stathist);
594         HISlogto(innconf->stathist);
595     }
596 
597     i = dbzneedfilecount();
598     if (!fdreserve(3 + i)) { /* TEMPORARYOPEN, history stats, INND_HISTORY and i */
599 	syslog(L_FATAL, "%s cant reserve file descriptors %m", LogName);
600 	exit(1);
601     }
602 
603     /* Set up our permissions. */
604     umask(NEWSUMASK);
605 
606     /* Become a daemon and initialize our log files. */
607     if (Debug) {
608 	xsignal(SIGINT, catch_terminate);
609         if (chdir(innconf->patharticles) < 0)
610             sysdie("SERVER cant chdir to %s", innconf->patharticles);
611     } else {
612 	if (ShouldFork)
613             daemonize(innconf->patharticles);
614 
615 	/* Open the logs.  stdout is used to log information about incoming
616            articles and stderr is used to log serious error conditions (as
617            well as to capture stderr from embedded filters).  Both are
618            normally fully buffered. */
619         path = concatpath(innconf->pathlog, INN_PATH_LOGFILE);
620         if (freopen(path, "a", stdout) == NULL)
621             sysdie("SERVER cant freopen stdout to %s", path);
622         setvbuf(stdout, NULL, _IOFBF, LOG_BUFSIZ);
623         free(path);
624         path = concatpath(innconf->pathlog, INN_PATH_ERRLOG);
625         if (freopen(path, "a", stderr) == NULL)
626             sysdie("SERVER cant freopen stderr to %s", path);
627         setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
628         free(path);
629     }
630     Log = stdout;
631     Errlog = stderr;
632 
633     /* Initialize overview if necessary. */
634     if (innconf->enableoverview && !OVopen(OV_WRITE))
635         die("SERVER cant open overview method");
636 
637     /* Attempt to increase the number of open file descriptors. */
638     if (innconf->rlimitnofile > 0) {
639         if (setfdlimit(innconf->rlimitnofile) < 0)
640             syswarn("SERVER cant set file descriptor limit");
641     }
642 
643     /* Get number of open channels. */
644     i = getfdlimit();
645     if (i < 0)
646         sysdie("SERVER cant get file descriptor limit");
647 
648 #ifdef FD_SETSIZE
649     if (FD_SETSIZE > 0 && (unsigned) i >= FD_SETSIZE) {
650         /* Only log a warning if rlimitnofile has been set
651          * to a value different than the default setting of letting
652          * the system set the number of file descriptors. */
653         if (innconf->rlimitnofile > 0) {
654             syslog(LOG_WARNING, "%s number of descriptors (%d) exceeding or equaling FD_SETSIZE (%d)",
655                    LogName, i, FD_SETSIZE);
656         }
657         i = FD_SETSIZE-1;
658     }
659 #endif
660 
661     /* There is no file descriptor limit on some hosts; for those, cap at
662        MaxOutgoing plus maxconnections plus 20, or 5000, whichever is larger.
663        Otherwise, we use insane amounts of memory for the channel table.
664        FIXME:  Get rid of this hard-coded constant.
665        (TODO:  Consider implementing libevent.) */
666     if (i > 5000) {
667         unsigned long max;
668 
669         max = innconf->maxconnections + MaxOutgoing + 20;
670         if (max < 5000)
671             max = 5000;
672         i = max;
673     }
674     syslog(L_NOTICE, "%s descriptors %d", LogName, i);
675     if (MaxOutgoing == 0) {
676 	/* getfdlimit() - (stdio + dbz + cc + lc + rc + art + fudge) */
677 	MaxOutgoing = i - (  3   +  3  +  2 +  1 +  1 +  1  +  2  );
678 	syslog(L_NOTICE, "%s outgoing %d", LogName, MaxOutgoing);
679     }
680 
681     /* See if another instance is alive. */
682     if (PID == NULL)
683 	PID = concatpath(innconf->pathrun, INN_PATH_SERVERPID);
684     if ((F = fopen(PID, "r")) != NULL) {
685 	if (fgets(buff, sizeof buff, F) != NULL
686 	 && ((pid = (pid_t) atol(buff)) > 0)
687 	 && (kill(pid, 0) > 0 || errno != ESRCH)) {
688 	    syslog(L_FATAL, "%s already_running pid %ld", LogName,
689 	    (long) pid);
690 	    exit(1);
691 	}
692 	fclose(F);
693     }
694 
695     if (gettimeofday(&Now, NULL) < 0)
696 	syslog(L_ERROR, "%s cant gettimeofday %m", LogName);
697 
698     /* Set up signal and error handlers. */
699     xmalloc_error_handler = xmalloc_abort;
700     xsignal(SIGHUP, catch_terminate);
701     xsignal(SIGTERM, catch_terminate);
702 
703     /* Set up the various parts of the system.  Channel feeds start processes
704        so call PROCsetup before ICDsetup.  NNTP needs to know if it's a slave,
705        so call RCsetup before NCsetup.  RCsetup calls innbind and waits for
706        it, so call PROCsetup after RCsetup to not interpose a signal
707        handler. */
708     CHANsetup(i);
709     if (Mode == OMrunning)
710         InndHisOpen();
711     CCsetup();
712     LCsetup();
713     RCsetup();
714     PROCsetup(10);
715     WIPsetup();
716     NCsetup();
717     ARTsetup();
718     ICDsetup(true);
719     if (innconf->timer != 0)
720         TMRinit(TMR_MAX);
721 
722     /* Initialize the storage subsystem. */
723     flag = true;
724     if (!SMsetup(SM_RDWR, &flag) || !SMsetup(SM_PREOPEN, &flag))
725         die("SERVER cant set up storage manager");
726     if (!SMinit())
727         die("SERVER cant initialize storage manager: %s", SMerrorstr);
728 
729 #if	defined(_DEBUG_MALLOC_INC)
730     m.i = 1;
731     dbmallopt(MALLOC_CKCHAIN, &m);
732     dbmallopt(MALLOC_CKDATA, &m);
733 #endif	/* defined(_DEBUG_MALLOC_INC) */
734 
735     /* Record our PID. */
736     pid = getpid();
737     if ((F = fopen(PID, "w")) == NULL) {
738 	i = errno;
739 	syslog(L_ERROR, "%s cant fopen %s %m", LogName, PID);
740 	IOError(WHEN, i);
741     }
742     else {
743 	if (fprintf(F, "%ld\n", (long)pid) == EOF || ferror(F)) {
744 	    i = errno;
745 	    syslog(L_ERROR, "%s cant fprintf %s %m", LogName, PID);
746 	    IOError(WHEN, i);
747 	}
748 	if (fclose(F) == EOF) {
749 	    i = errno;
750 	    syslog(L_ERROR, "%s cant fclose %s %m", LogName, PID);
751 	    IOError(WHEN, i);
752 	}
753 	if (chmod(PID, 0664) < 0) {
754 	    i = errno;
755 	    syslog(L_ERROR, "%s cant chmod %s %m", LogName, PID);
756 	    IOError(WHEN, i);
757 	}
758     }
759 
760 #if DO_PERL
761     /* Load the Perl code */
762     path1 = concatpath(innconf->pathfilter, INN_PATH_PERL_STARTUP_INND);
763     path2 = concatpath(innconf->pathfilter, INN_PATH_PERL_FILTER_INND);
764     PERLsetup(path1, path2, "filter_art");
765     free(path1);
766     free(path2);
767     PLxsinit();
768     if (filter)
769 	PerlFilter(true);
770 #endif /* DO_PERL */
771 
772 #if DO_PYTHON
773     PYsetup();
774     if (!filter)
775 	PYfilter(false);
776 #endif /* DO_PYTHON */
777 
778     /* And away we go... */
779     if (ShouldRenumber) {
780         syslog(LOG_NOTICE, "SERVER renumbering");
781         if (!ICDrenumberactive())
782             die("SERVER cant renumber");
783     }
784 
785     syslog(LOG_NOTICE, "SERVER starting");
786     status = sd_notify(false, "READY=1");
787     if (status < 0)
788         warn("cannot notify systemd of startup: %s", strerror(-status));
789 
790     CHANreadloop();
791 
792     /* CHANreadloop should never return. */
793     CleanupAndExit(1, "CHANreadloop returned");
794     return 1;
795 }
796