1 /* $Id: innd.c 7858 2008-06-05 18:51:20Z iulius $
3 ** Variable definitions, miscellany, and main().
9 #include "inn/innconf.h"
10 #include "inn/messages.h"
19 bool NNRPTracing = false;
20 bool StreamingOff = false ; /* default is we can stream */
22 bool DoCancels = true;
23 char LogName[] = "SERVER";
24 int ErrorCount = IO_ERROR_COUNT;
25 OPERATINGMODE Mode = OMrunning;
26 int RemoteLimit = REMOTELIMIT;
27 time_t RemoteTimer = REMOTETIMER;
28 int RemoteTotal = REMOTETOTAL;
29 bool ThrottledbyIOError = false;
31 static char *PID = NULL;
33 /* Signal handling. If we receive a signal that should kill the server,
34 killer_signal is set to the signal number that we received. This isn't
35 what indicates that we should terminate; that's the separate global
36 variable GotTerminate, used in CHANreadloop. */
37 static volatile sig_atomic_t killer_signal = 0;
39 /* Whether our self-maintained logs (stdout and stderr) are buffered, used
40 to determine whether fflush is needed. Should be static. */
41 bool BufferedLogs = true;
43 /* FILEs for logs and error logs. Everything should just use stdout and
48 /* Some very old systems have a completely inadequate BUFSIZ buffer size, at
49 least for our logging purposes. */
51 # define LOG_BUFSIZ 4096
53 # define LOG_BUFSIZ BUFSIZ
56 /* Internal prototypes. */
57 static RETSIGTYPE catch_terminate(int sig);
58 static void xmalloc_abort(const char *what, size_t size,
59 const char *file, int line);
61 /* header table initialization */
62 #define ARTHEADERINIT(name, type) {name, type, sizeof(name) - 1}
63 const ARTHEADER ARTheaders[] = {
65 ARTHEADERINIT("Approved", HTstd),
66 /* #define HDR__APPROVED 0 */
67 ARTHEADERINIT("Control", HTstd),
68 /* #define HDR__CONTROL 1 */
69 ARTHEADERINIT("Date", HTreq),
70 /* #define HDR__DATE 2 */
71 ARTHEADERINIT("Distribution", HTstd),
72 /* #define HDR__DISTRIBUTION 3 */
73 ARTHEADERINIT("Expires", HTstd),
74 /* #define HDR__EXPIRES 4 */
75 ARTHEADERINIT("From", HTreq),
76 /* #define HDR__FROM 5 */
77 ARTHEADERINIT("Lines", HTstd),
78 /* #define HDR__LINES 6 */
79 ARTHEADERINIT("Message-ID", HTreq),
80 /* #define HDR__MESSAGE_ID 7 */
81 ARTHEADERINIT("Newsgroups", HTreq),
82 /* #define HDR__NEWSGROUPS 8 */
83 ARTHEADERINIT("Path", HTreq),
84 /* #define HDR__PATH 9 */
85 ARTHEADERINIT("Reply-To", HTstd),
86 /* #define HDR__REPLY_TO 10 */
87 ARTHEADERINIT("Sender", HTstd),
88 /* #define HDR__SENDER 11 */
89 ARTHEADERINIT("Subject", HTreq),
90 /* #define HDR__SUBJECT 12 */
91 ARTHEADERINIT("Supersedes", HTstd),
92 /* #define HDR__SUPERSEDES 13 */
93 ARTHEADERINIT("Bytes", HTstd),
94 /* #define HDR__BYTES 14 */
95 ARTHEADERINIT("Also-Control", HTobs),
96 /* #define HDR__ALSOCONTROL 15 */
97 ARTHEADERINIT("References", HTstd),
98 /* #define HDR__REFERENCES 16 */
99 ARTHEADERINIT("Xref", HTsav),
100 /* #define HDR__XREF 17 */
101 ARTHEADERINIT("Keywords", HTstd),
102 /* #define HDR__KEYWORDS 18 */
103 ARTHEADERINIT("X-Trace", HTstd),
104 /* #define HDR__XTRACE 19 */
105 ARTHEADERINIT("Date-Received", HTobs),
106 /* #define HDR__DATERECEIVED 20 */
107 ARTHEADERINIT("Posted", HTobs),
108 /* #define HDR__POSTED 21 */
109 ARTHEADERINIT("Posting-Version", HTobs),
110 /* #define HDR__POSTINGVERSION 22 */
111 ARTHEADERINIT("Received", HTobs),
112 /* #define HDR__RECEIVED 23 */
113 ARTHEADERINIT("Relay-Version", HTobs),
114 /* #define HDR__RELAYVERSION 24 */
115 ARTHEADERINIT("NNTP-Posting-Host", HTstd),
116 /* #define HDR__NNTPPOSTINGHOST 25 */
117 ARTHEADERINIT("Followup-To", HTstd),
118 /* #define HDR__FOLLOWUPTO 26 */
119 ARTHEADERINIT("Organization", HTstd),
120 /* #define HDR__ORGANIZATION 27 */
121 ARTHEADERINIT("Content-Type", HTstd),
122 /* #define HDR__CONTENTTYPE 28 */
123 ARTHEADERINIT("Content-Base", HTstd),
124 /* #define HDR__CONTENTBASE 29 */
125 ARTHEADERINIT("Content-Disposition", HTstd),
126 /* #define HDR__CONTENTDISPOSITION 30 */
127 ARTHEADERINIT("X-Newsreader", HTstd),
128 /* #define HDR__XNEWSREADER 31 */
129 ARTHEADERINIT("X-Mailer", HTstd),
130 /* #define HDR__XMAILER 32 */
131 ARTHEADERINIT("X-Newsposter", HTstd),
132 /* #define HDR__XNEWSPOSTER 33 */
133 ARTHEADERINIT("X-Cancelled-By", HTstd),
134 /* #define HDR__XCANCELLEDBY 34 */
135 ARTHEADERINIT("X-Canceled-By", HTstd),
136 /* #define HDR__XCANCELEDBY 35 */
137 ARTHEADERINIT("Cancel-Key", HTstd),
138 /* #define HDR__CANCELKEY 36 */
139 ARTHEADERINIT("User-Agent", HTstd),
140 /* #define HDR__USER_AGENT 37 */
141 ARTHEADERINIT("X-Original-Message-ID", HTstd),
142 /* #define HDR__X_ORIGINAL_MESSAGE_ID 38 */
143 ARTHEADERINIT("Cancel-Lock", HTstd),
144 /* #define HDR__CANCEL_LOCK 39 */
145 ARTHEADERINIT("Content-Transfer-Encoding", HTstd),
146 /* #define HDR__CONTENT_TRANSFER_ENCODING 40 */
147 ARTHEADERINIT("Face", HTstd),
148 /* #define HDR__FACE 41 */
149 ARTHEADERINIT("Injection-Info", HTstd),
150 /* #define HDR__INJECTION_INFO 42 */
151 ARTHEADERINIT("List-ID", HTstd),
152 /* #define HDR__LIST_ID 43 */
153 ARTHEADERINIT("MIME-Version", HTstd),
154 /* #define HDR__MIME_VERSION 44 */
155 ARTHEADERINIT("Originator", HTstd),
156 /* #define HDR__ORIGINATOR 45 */
157 ARTHEADERINIT("X-Auth", HTstd),
158 /* #define HDR__X_AUTH 46 */
159 ARTHEADERINIT("X-Complaints-To", HTstd),
160 /* #define HDR__X_COMPLAINTS_TO 47 */
161 ARTHEADERINIT("X-Face", HTstd),
162 /* #define HDR__X_FACE 48 */
163 ARTHEADERINIT("X-HTTP-UserAgent", HTstd),
164 /* #define HDR__X_HTTP_USERAGENT 49 */
165 ARTHEADERINIT("X-HTTP-Via", HTstd),
166 /* #define HDR__X_HTTP_VIA 50 */
167 ARTHEADERINIT("X-Modbot", HTstd),
168 /* #define HDR__X_MODBOT 51 */
169 ARTHEADERINIT("X-Modtrace", HTstd),
170 /* #define HDR__X_MODTRACE 52 */
171 ARTHEADERINIT("X-No-Archive", HTstd),
172 /* #define HDR__X_NO_ARCHIVE 53 */
173 ARTHEADERINIT("X-Original-Trace", HTstd),
174 /* #define HDR__X_ORIGINAL_TRACE 54 */
175 ARTHEADERINIT("X-Originating-IP", HTstd),
176 /* #define HDR__X_ORIGINATING_IP 55 */
177 ARTHEADERINIT("X-PGP-Key", HTstd),
178 /* #define HDR__X_PGP_KEY 56 */
179 ARTHEADERINIT("X-PGP-Sig", HTstd),
180 /* #define HDR__X_PGP_SIG 57 */
181 ARTHEADERINIT("X-Poster-Trace", HTstd),
182 /* #define HDR__X_POSTER_TRACE 58 */
183 ARTHEADERINIT("X-Postfilter", HTstd),
184 /* #define HDR__X_POSTFILTER 59 */
185 ARTHEADERINIT("X-Proxy-User", HTstd),
186 /* #define HDR__X_PROXY_USER 60 */
187 ARTHEADERINIT("X-Submissions-To", HTstd),
188 /* #define HDR__X_SUBMISSIONS_TO 61 */
189 ARTHEADERINIT("X-Usenet-Provider", HTstd),
190 /* #define HDR__X_USENET_PROVIDER 62 */
191 ARTHEADERINIT("In-Reply-To", HTstd),
192 /* #define HDR__IN_REPLY_TO 63 */
193 ARTHEADERINIT("Injection-Date", HTstd),
194 /* #define HDR__INJECTION_DATE 64 */
195 ARTHEADERINIT("NNTP-Posting-Date", HTstd)
196 /* #define HDR__NNTP_POSTING_DATE 65 */
198 /* #define MAX_ARTHEADER 66 */
202 ** Signal handler to catch SIGTERM and similar signals and queue a clean
206 catch_terminate(int sig)
211 #ifndef HAVE_SIGACTION
212 xsignal(sig, catch_terminate);
218 ** Memory allocation failure handler. Instead of the default behavior of
219 ** just exiting, call abort to generate a core dump.
222 xmalloc_abort(const char *what, size_t size, const char *file, int line)
224 fprintf(stderr, "SERVER cant %s %lu bytes at %s line %d: %s", what,
225 (unsigned long) size, file, line, strerror(errno));
226 syslog(LOG_CRIT, "SERVER cant %s %lu bytes at %s line %d: %m", what,
227 (unsigned long) size, file, line);
233 ** The name is self-explanatory.
236 CleanupAndExit(int status, const char *why)
240 syslog(LOG_WARNING, "SERVER shutdown %s", why);
242 syslog(LOG_WARNING, "SERVER shutdown received signal %d",
249 ** Close down all parts of the system (e.g., before calling exit or exec).
262 if (innconf->enableoverview)
281 innconf_free(innconf);
286 if (unlink(PID) < 0 && errno != ENOENT)
287 syslog(LOG_ERR, "SERVER cant unlink %s: %m", PID);
292 ** Flush one log file, re-establishing buffering if necessary. stdout is
293 ** block-buffered, stderr is line-buffered.
298 char *path, *oldpath;
304 path = concatpath(innconf->pathlog,
305 (F == stdout) ? _PATH_LOGFILE : _PATH_ERRLOG);
306 oldpath = concat(path, ".old", (char *) 0);
307 if (rename(path, oldpath) < 0)
308 syswarn("SERVER cant rename %s to %s", path, oldpath);
311 if (freopen(path, "a", F) != F)
312 sysdie("SERVER cant freopen %s", path);
316 setvbuf(F, NULL, (F == stdout) ? _IOFBF : _IOLBF, LOG_BUFSIZ);
321 ** Print a usage message and exit.
326 fprintf(stderr, "Usage error.\n");
332 main(int ac, char *av[])
334 const char *name, *p;
338 static char WHEN[] = "PID file";
339 int i, j, fd[MAX_SOCKETS + 1];
340 char buff[SMBUF], *path1, *path2;
344 bool ShouldSyntaxCheck;
347 #if defined(_DEBUG_MALLOC_INC)
349 #endif /* defined(_DEBUG_MALLOC_INC) */
351 /* Set up the pathname, first thing, and teach our error handlers about
352 the name of the program. */
354 if (name == NULL || *name == '\0')
357 p = strrchr(name, '/');
361 message_program_name = name;
362 openlog(name, LOG_CONS | LOG_NDELAY, LOG_INN_SERVER);
363 message_handlers_die(2, message_log_stderr, message_log_syslog_crit);
364 message_handlers_warn(2, message_log_stderr, message_log_syslog_err);
365 message_handlers_notice(1, message_log_syslog_notice);
367 /* Make sure innd is not running as root. innd must be either started
368 via inndstart or use a non-privileged port. */
369 if (getuid() == 0 || geteuid() == 0)
370 die("SERVER must be run as user news, not root (use inndstart)");
372 /* Handle malloc debugging. */
373 #if defined(_DEBUG_MALLOC_INC)
374 m.i = M_HANDLE_ABORT;
375 dbmallopt(MALLOC_WARN, &m);
376 dbmallopt(MALLOC_FATAL, &m);
378 dbmallopt(MALLOC_FILLAREA, &m);
380 dbmallopt(MALLOC_CKCHAIN, &m);
381 dbmallopt(MALLOC_CKDATA, &m);
382 #endif /* defined(_DEBUG_MALLOC_INC) */
385 TimeOut.tv_sec = DEFAULT_TIMEOUT;
388 ShouldRenumber = false;
389 ShouldSyntaxCheck = false;
392 /* Set some options from inn.conf that can be overridden with
393 command-line options if they exist, so read inn.conf first. */
394 if (!innconf_read(NULL))
399 while ((i = getopt(ac, av, "ac:Cdfi:l:m:o:Nn:p:P:rst:uH:T:X:")) != EOF)
408 innconf->artcutoff = atoi(optarg);
420 RemoteLimit = atoi(optarg);
423 innconf->maxconnections = atoi(optarg);
426 if (innconf->bindaddress) free(innconf->bindaddress);
427 innconf->bindaddress = xstrdup(optarg);
430 innconf->maxartsize = atol(optarg);
439 case 'g': Mode = OMrunning; break;
440 case 'p': Mode = OMpaused; break;
441 case 't': Mode = OMthrottled; break;
443 if (Mode != OMrunning)
444 ModeReason = concat(OMpaused ? "Paus" : "Throttl",
445 "ed from the command line", (char *) 0);
455 case 'n': innconf->readerswhenstopped = false; break;
456 case 'y': innconf->readerswhenstopped = true; break;
460 MaxOutgoing = atoi(optarg);
463 /* Silently ignore multiple -p flags, in case ctlinnd xexec
472 if (j == MAX_SOCKETS)
474 } while ((p = strtok(NULL, ",")) != NULL);
479 innconf->port = atoi(optarg);
482 ShouldRenumber = true;
485 ShouldSyntaxCheck = true;
488 TimeOut.tv_sec = atol(optarg);
491 RemoteTotal = atoi(optarg);
494 BufferedLogs = false;
497 RemoteTimer = atoi(optarg);
506 if (ModeReason && !innconf->readerswhenstopped)
507 NNRPReason = xstrdup(ModeReason);
509 if (ShouldSyntaxCheck) {
510 if ((p = CCcheckfile((char **)NULL)) == NULL)
512 fprintf(stderr, "%s\n", p + 2);
516 /* Get the Path entry. */
517 if (innconf->pathhost == NULL) {
518 syslog(L_FATAL, "%s No pathhost set", LogName);
521 Path.used = strlen(innconf->pathhost) + 1;
522 Path.size = Path.used + 1;
523 Path.data = xmalloc(Path.size);
524 snprintf(Path.data, Path.size, "%s!", innconf->pathhost);
525 if (innconf->pathalias == NULL) {
527 Pathalias.data = NULL;
529 Pathalias.used = strlen(innconf->pathalias) + 1;
530 Pathalias.size = Pathalias.used + 1;
531 Pathalias.data = xmalloc(Pathalias.size);
532 snprintf(Pathalias.data, Pathalias.size, "%s!", innconf->pathalias);
534 if (innconf->pathcluster == NULL) {
535 Pathcluster.used = 0;
536 Pathcluster.data = NULL;
538 Pathcluster.used = strlen(innconf->pathcluster) + 1;
539 Pathcluster.size = Pathcluster.used + 1;
540 Pathcluster.data = xmalloc(Pathcluster.size);
541 snprintf(Pathcluster.data, Pathcluster.size, "%s!", innconf->pathcluster);
543 /* Trace history ? */
544 if (innconf->stathist != NULL) {
545 syslog(L_NOTICE, "logging hist stats to %s", innconf->stathist);
546 HISlogto(innconf->stathist);
549 i = dbzneedfilecount();
550 if (!fdreserve(3 + i)) { /* TEMPORARYOPEN, history stats, INND_HISTORY and i */
551 syslog(L_FATAL, "%s cant reserve file descriptors %m", LogName);
555 /* Set up our permissions. */
558 /* Become a daemon and initialize our log files. */
560 xsignal(SIGINT, catch_terminate);
561 if (chdir(innconf->patharticles) < 0)
562 sysdie("SERVER cant chdir to %s", innconf->patharticles);
565 daemonize(innconf->patharticles);
567 /* Open the logs. stdout is used to log information about incoming
568 articles and stderr is used to log serious error conditions (as
569 well as to capture stderr from embedded filters). Both are
570 normally fully buffered. */
571 path = concatpath(innconf->pathlog, _PATH_LOGFILE);
572 if (freopen(path, "a", stdout) == NULL)
573 sysdie("SERVER cant freopen stdout to %s", path);
574 setvbuf(stdout, NULL, _IOFBF, LOG_BUFSIZ);
576 path = concatpath(innconf->pathlog, _PATH_ERRLOG);
577 if (freopen(path, "a", stderr) == NULL)
578 sysdie("SERVER cant freopen stderr to %s", path);
579 setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
585 /* Initialize overview if necessary. */
586 if (innconf->enableoverview && !OVopen(OV_WRITE))
587 die("SERVER cant open overview method");
589 /* Always attempt to increase the number of open file descriptors. If
590 we're not root, this may just fail quietly. */
591 if (innconf->rlimitnofile > 0)
592 setfdlimit(innconf->rlimitnofile);
594 /* Get number of open channels. */
597 syslog(L_FATAL, "%s cant get file descriptor limit: %m", LogName);
601 /* There is no file descriptor limit on some hosts; for those, cap at
602 MaxOutgoing plus maxconnections plus 20, or 5000, whichever is larger.
603 Otherwise, we use insane amounts of memory for the channel table.
604 FIXME: Get rid of this hard-coded constant. */
608 max = innconf->maxconnections + MaxOutgoing + 20;
613 syslog(L_NOTICE, "%s descriptors %d", LogName, i);
614 if (MaxOutgoing == 0) {
615 /* getfdlimit() - (stdio + dbz + cc + lc + rc + art + fudge) */
616 MaxOutgoing = i - ( 3 + 3 + 2 + 1 + 1 + 1 + 2 );
617 syslog(L_NOTICE, "%s outgoing %d", LogName, MaxOutgoing);
620 /* See if another instance is alive. */
622 PID = concatpath(innconf->pathrun, _PATH_SERVERPID);
623 if ((F = fopen(PID, "r")) != NULL) {
624 if (fgets(buff, sizeof buff, F) != NULL
625 && ((pid = (pid_t) atol(buff)) > 0)
626 && (kill(pid, 0) > 0 || errno != ESRCH)) {
627 syslog(L_FATAL, "%s already_running pid %ld", LogName,
634 if (GetTimeInfo(&Now) < 0)
635 syslog(L_ERROR, "%s cant gettimeinfo %m", LogName);
637 /* Set up signal and error handlers. */
638 xmalloc_error_handler = xmalloc_abort;
639 xsignal(SIGHUP, catch_terminate);
640 xsignal(SIGTERM, catch_terminate);
642 /* Set up the various parts of the system. Channel feeds start
643 processes so call PROCsetup before ICDsetup. NNTP needs to know if
644 it's a slave, so call RCsetup before NCsetup. */
647 if (Mode == OMrunning)
652 for (i = 1; fd[i] != -1; i++)
661 /* Initialize the storage subsystem. */
663 if (!SMsetup(SM_RDWR, &flag) || !SMsetup(SM_PREOPEN, &flag))
664 die("SERVER cant set up storage manager");
666 die("SERVER cant initalize storage manager: %s", SMerrorstr);
668 #if defined(_DEBUG_MALLOC_INC)
670 dbmallopt(MALLOC_CKCHAIN, &m);
671 dbmallopt(MALLOC_CKDATA, &m);
672 #endif /* defined(_DEBUG_MALLOC_INC) */
674 /* Record our PID. */
676 if ((F = fopen(PID, "w")) == NULL) {
678 syslog(L_ERROR, "%s cant fopen %s %m", LogName, PID);
682 if (fprintf(F, "%ld\n", (long)pid) == EOF || ferror(F)) {
684 syslog(L_ERROR, "%s cant fprintf %s %m", LogName, PID);
687 if (fclose(F) == EOF) {
689 syslog(L_ERROR, "%s cant fclose %s %m", LogName, PID);
692 if (chmod(PID, 0664) < 0) {
694 syslog(L_ERROR, "%s cant chmod %s %m", LogName, PID);
706 /* Load the Perl code */
707 path1 = concatpath(innconf->pathfilter, _PATH_PERL_STARTUP_INND);
708 path2 = concatpath(innconf->pathfilter, _PATH_PERL_FILTER_INND);
709 PERLsetup(path1, path2, "filter_art");
721 #endif /* DO_PYTHON */
723 /* And away we go... */
724 if (ShouldRenumber) {
725 syslog(LOG_NOTICE, "SERVER renumbering");
726 if (!ICDrenumberactive())
727 die("SERVER cant renumber");
729 syslog(LOG_NOTICE, "SERVER starting");
732 /* CHANreadloop should never return. */
733 CleanupAndExit(1, "CHANreadloop returned");