1 /* $Id: main.c 6956 2004-06-29 22:41:35Z rra $
3 ** Main routines for the innfeed program.
5 ** Written by James Brister <brister@vix.com>
11 #include "portable/socket.h"
12 #include "portable/time.h"
24 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
28 #include "inn/innconf.h"
29 #include "inn/messages.h"
35 #include "configfile.h"
36 #include "connection.h"
39 #include "innlistener.h"
48 extern int debugWrites ;
49 bool sigFlag = false ;
50 const char *InputFile ;
51 char *configFile = NULL ;
52 bool RollInputFile = false ;
53 char *pidFile = NULL ;
54 bool useMMap = false ;
55 void (*gPrintInfo) (void) ;
57 /* these are used by imapfeed */
58 char *deliver_username = NULL;
59 char *deliver_authname = NULL;
60 char *deliver_password = NULL;
61 char *deliver_realm = NULL;
62 const char *deliver_rcpt_to = "+%s";
63 char *deliver_to_header = NULL;
66 extern char *versionInfo ;
68 extern char *optarg ; /* needed for Solaris */
73 extern void openInputFile (void);
76 static char *logFile ;
77 static char *newsspool ;
79 static void sigemt (int sig) ;
80 static void sigalrm (int sig) ;
81 static void sigchld (int sig) ;
82 static void sigint (int sig) ;
83 static void sigquit (int sig) ;
84 static void sighup (int sig) ;
85 static void sigterm (int sig) ;
86 static void sigusr (int sig) ;
87 static void usage (int) ;
88 static void gprintinfo (void) ;
89 static void openLogFile (void) ;
90 static void writePidFile (void) ;
91 static int mainOptionsProcess (void *data) ;
92 static int mainConfigLoadCbk (void *data) ;
93 static void mainCleanup (void) ;
95 static char *bopt = NULL ;
96 static char *aopt = NULL ;
97 static char *popt = NULL ;
98 static bool Mopt = false ;
99 static bool Zopt = false ;
100 static bool Dopt = false ;
101 static int debugLevel = 0 ;
102 static unsigned int initialSleep = 2 ;
103 static char *sopt = NULL ;
104 static char *lopt = NULL ;
105 static bool eopt = false ;
106 static int elimit = 0 ;
108 int main (int argc, char **argv)
111 InnListener listener ;
112 int optVal, fd, rval ;
113 const char *subProgram = NULL ;
115 bool dynamicPeers = false ;
116 time_t now = theTime() ;
117 char dateString [30] ;
120 bool checkConfig = false ;
123 strlcpy (dateString,ctime(&now),sizeof (dateString)) ;
125 message_program_name = strrchr (argv [0],'/');
126 if (message_program_name == NULL)
127 message_program_name = argv [0] ;
129 message_program_name++;
131 gPrintInfo = gprintinfo ;
133 openlog (message_program_name,(int)(L_OPENLOG_FLAGS|LOG_PID),LOG_INN_PROG) ;
134 if (!innconf_read(NULL)) {
135 syslog(LOG_ERR, "cant read inn.conf\n");
139 #if defined (HAVE_MMAP)
145 message_handlers_die (2, error_log_stderr_date, message_log_syslog_err) ;
146 message_handlers_warn (1, message_log_syslog_warning);
147 message_handlers_notice (1, message_log_syslog_notice) ;
149 #define OPT_STRING "a:b:c:Cd:e:hl:mMo:p:S:s:vxyz"
151 while ((optVal = getopt (argc,argv,OPT_STRING)) != EOF)
160 if ( !isDirectory (optarg) )
161 logAndExit (1,"Not a directory: %s\n",optarg) ;
174 loggingLevel = atoi (optarg) ;
175 debugLevel = loggingLevel ;
181 elimit = atoi (optarg) ;
184 fprintf (stderr,"Illegal value for -e option\n") ;
202 artLogMissingArticles (true) ;
206 artSetMaxBytesInUse (atoi (optarg)) ;
214 subProgram = optarg ;
230 dynamicPeers = true ;
252 warn ("%s version: %s\n",message_program_name, versionInfo) ;
256 /* make sure we have valid fds 0, 1 & 2 so it is not taken by
257 something else, probably openlog(). fd 0 will be freopen()ed on the
258 inputFile, the subProgram, or ourself. fd 1 and fd 2 will
259 be freopen()ed on the log file (or will stay pointed at /dev/null).
261 without doing this, if the descriptors were closed then the
262 freopen calls on some systems (like BSDI 2.1) will really close
263 whatever has aquired the stdio descriptors, such as the socket
266 XXX possible problems: what if fd 0 is closed but no inputFile,
267 XXX subProgram or talkToSelf is true? it will not be freopen()ed, so
268 XXX innfeed won't have any fresh data (besides, fd 0 is only writable
269 XXX here). perhaps a warning should be issued.
273 fd = open("/dev/null", O_WRONLY);
277 logAndExit (1,"open(\"/dev/null\", O_WRONLY): %s",
283 /* good, we saved an fd from being trounced */
292 notice ("ME starting %s at %s", versionInfo, dateString) ;
296 if (!SMsetup(SM_PREOPEN, (void *)&val)) {
297 syslog(LOG_ERR, "cant setup the storage subsystem\n");
301 d_printf(0, "Storage manager initialization failed\n");
302 syslog(LOG_ERR, "Storage manager initialization failed\n");
306 if (subProgram == NULL && talkToSelf == false)
310 if (fstat (0,&buf) < 0)
311 logAndExit (1,"ME oserr fstat stdin: %s", strerror (errno)) ;
312 else if (S_ISREG (buf.st_mode))
317 * set up the config file name and then read the file in. Order is important.
319 configAddLoadCallback (mainOptionsProcess,(checkConfig ? stderr : NULL)) ;
320 configAddLoadCallback (tapeConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
322 configAddLoadCallback (endpointConfigLoadCbk,(checkConfig ? stderr : NULL));
323 configAddLoadCallback (hostConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
324 configAddLoadCallback (cxnConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
325 configAddLoadCallback (mainConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
326 configAddLoadCallback (listenerConfigLoadCbk,(checkConfig ? stderr : NULL));
328 if (copt != NULL && *copt == '\0')
330 logOrPrint (LOG_CRIT,(checkConfig ? stderr : NULL),
331 "Empty pathname for ``-c'' option") ;
334 configFile = buildFilename(innconf->pathetc, copt ? copt : CONFIG_FILE);
335 dflTapeDir = buildFilename(innconf->pathspool, TAPE_DIRECTORY);
337 rval = readConfig (configFile,(checkConfig ? stderr : NULL),
338 checkConfig,loggingLevel > 0);
340 if (subProgram != NULL && (talkToSelf == true || InputFile))
342 d_printf (0,"Cannot specify '-s' with '-x' or an input file\n") ;
343 syslog (LOG_ERR,"Incorrect arguments: '-s' with '-x' or an input file\n");
351 fprintf (stderr,"config loading failed.\n") ;
356 fprintf (stderr,"config loading succeeded.\n") ;
363 debugFile = buildFilename (innconf->pathlog, DEBUG_FILE);
364 if (loggingLevel == 0 && fileExistsP (debugFile))
367 if (logFile == NULL && ! isatty (fileno (stderr)))
368 logFile = buildFilename (innconf->pathlog, LOG_FILE) ;
373 openfds = 4 ; /* stdin, stdout, stderr and syslog */
377 if (subProgram != NULL)
383 sysdie ("ME fatal pipe") ;
385 if ((pid = fork ()) < 0)
387 sysdie ("ME fatal fork") ;
397 execlp ("sh", "sh", "-c", subProgram, (char *) 0) ;
406 xsignal(SIGCHLD,sigchld) ;
412 /* We're not really getting information from innd or a subprogram,
413 but are just processing backlog files. We set up a pipe to ourself
414 that we never write to, to simulate an idle innd. */
417 if (pipe (pipefds) != 0)
418 sysdie ("ME fatal pipe") ;
421 dup2 (pipefds [0], 0) ;
427 if (chdir (newsspool) != 0)
428 sysdie ("ME fatal chdir %s", newsspool) ;
430 /* hook up the endpoint to the source of new article information (usually
432 ep = newEndPoint (0) ; /* fd 0, i.e. stdin */
434 /* now arrange for this endpoint to always be the first one checked for
435 possible activity. */
436 setMainEndPoint (ep) ;
438 listener = newListener (ep, talkToSelf,dynamicPeers) ;
439 mainListener = listener ;
441 sleep (initialSleep) ;
443 if (innconf->rlimitnofile >= 0)
444 if (setfdlimit (innconf->rlimitnofile) < 0)
445 syswarn ("ME oserr setrlimit(RLIM_NOFILE,%ld)", innconf->rlimitnofile) ;
447 if (innconf->timer > 0)
450 configHosts (talkToSelf) ;
452 if (InputFile && *InputFile) {
456 /* handle signal to shutdown */
457 setSigHandler (SIGTERM,sigterm) ;
458 setSigHandler (SIGQUIT,sigquit) ;
460 /* handle signal to reload config */
461 setSigHandler (SIGHUP,sighup) ;
463 /* handle signal to print snapshot. */
464 setSigHandler (SIGINT,sigint) ;
466 /* handle signal to roll input file */
467 setSigHandler (SIGALRM,sigalrm) ;
469 /* handle signal to flush all the backlog files */
470 setSigHandler (SIGCHLD,sigemt) ;
472 /* we can increment and decrement logging levels by sending SIGUSR{1,2} */
473 setSigHandler (SIGUSR1,sigusr) ;
474 setSigHandler (SIGUSR2,sigusr) ;
476 atexit (mainCleanup) ;
483 static void usage (int val)
485 fprintf (stderr,"usage: %s [ options ] [ file ]\n\n",
486 message_program_name) ;
487 fprintf (stderr,"Version: %s\n\n",versionInfo) ;
488 fprintf (stderr,"Config file: %s\n",CONFIG_FILE) ;
489 fprintf (stderr,"Backlog directory: %s/%s\n", innconf->pathspool, TAPE_DIRECTORY) ;
490 fprintf (stderr,"\nLegal options are:\n") ;
491 fprintf (stderr,"\t-a dir Use the given directory as the top of the article spool\n") ;
493 fprintf (stderr,"\t-b dir Use the given directory as the the storage\n");
494 fprintf (stderr,"\t place for backlog files and lock files.\n");
496 fprintf (stderr,"\t-c file Use the given file as the config file instead of the\n");
497 fprintf (stderr,"\t default of %s\n",CONFIG_FILE);
499 fprintf (stderr,"\t-C Check the config file and then exit.\n") ;
500 fprintf (stderr,"\t-d num set the logging level to num (an integer).\n");
501 fprintf (stderr,"\t Larger value means more logging. 0 means no\n");
502 fprintf (stderr,"\t logging. The default is 0\n");
504 fprintf (stderr,"\t-e bytes Keep the output backlog files to no bigger\n");
505 fprintf (stderr,"\t than %.2f times this number\n",LIMIT_FUDGE);
507 fprintf (stderr,"\t-h print this message\n");
509 fprintf (stderr,"\t-l file redirect stderr and stdout to the given file.\n");
510 fprintf (stderr,"\t When run under INN they normally are redirected to\n");
511 fprintf (stderr,"\t /dev/null. This is needed if using '-d'.\n");
513 fprintf (stderr,"\t-m Log information on all missing articles\n");
515 fprintf (stderr,"\t-M Turn *off* use of mmap\n") ;
516 #if ! defined (HAVE_MMAP)
517 fprintf (stderr,"\t (a no-op as this excutable has been built without mmap support\n") ;
520 fprintf (stderr,"\t-p file Write the process id to the given file\n") ;
521 fprintf (stderr,"\t instead of the default of %s\n",PID_FILE);
522 fprintf (stderr,"\t A relative path is relative to %s\n", innconf->pathrun) ;
524 fprintf (stderr,"\t-s command run the given command in a subprocess and use\n");
525 fprintf (stderr,"\t its output as article information instead of\n");
526 fprintf (stderr,"\t running under innd\n");
528 fprintf (stderr,"\t-S file Use the give filename instead of innfeed.status\n") ;
529 fprintf (stderr,"\t relative pathnames start from %s\n", innconf->pathlog) ;
531 fprintf (stderr,"\t-v print version information\n");
533 fprintf (stderr,"\t-x Do not read any article information off stdin,\n");
534 fprintf (stderr,"\t but simply process backlog files and then exit\n");
535 fprintf (stderr,"\t when done\n");
537 fprintf (stderr,"\t-y Add peers dynamically. If an unrecognized peername\n");
538 fprintf (stderr,"\t is received from innd, then it is presumed to also\n");
539 fprintf (stderr,"\t be the ip name and a new peer binding is set up\n");
541 fprintf (stderr,"\t-z have each of the connections issue their own stats\n");
542 fprintf (stderr,"\t whenever they close, or whenever their controller\n");
543 fprintf (stderr,"\t issues its own stats\n");
548 static void sigterm (int sig UNUSED)
550 notice ("ME received shutdown signal") ;
551 shutDown (mainListener) ;
554 static void sigquit (int sig UNUSED)
559 static void sigint (int sig UNUSED)
564 static void sighup (int sig UNUSED)
566 notice ("ME reloading config file %s", configFile) ;
568 if (!readConfig (configFile,NULL,false,loggingLevel > 0))
570 die ("ME config aborting, error parsing config file") ;
573 configHosts (talkToSelf) ;
576 static void sigemt (int sig UNUSED)
581 static void sigalrm (int sig UNUSED)
583 if (InputFile == NULL)
584 warn ("ME signal SIGALRM in non-funnel-file mode ignored") ;
587 RollInputFile = true;
588 syslog(LOG_NOTICE, "ME preparing to roll %s", InputFile);
592 static void sigchld (int sig UNUSED)
595 wait (&status) ; /* we don't care */
598 xsignal (sig,sigchld) ;
601 /* SIGUSR1 increments logging level. SIGUSR2 decrements. */
602 static void sigusr (int sig)
604 if (sig == SIGUSR1) {
606 notice ("ME increasing logging level to %d", loggingLevel) ;
607 } else if (sig == SIGUSR2 && loggingLevel > 0) {
609 notice ("ME decreasing logging level to %d", loggingLevel) ;
613 static void openLogFile (void)
619 fpr = freopen (logFile,"a",stdout) ;
621 logAndExit (1,"freopen (%s, \"a\", stdout): %s",
622 logFile, strerror (errno)) ;
624 fpr = freopen (logFile,"a",stderr) ;
626 logAndExit (1,"freopen (%s, \"a\", stderr): %s",
627 logFile, strerror (errno)) ;
629 #if defined (HAVE_SETBUFFER)
630 setbuffer (stdout, NULL, 0) ;
631 setbuffer (stderr, NULL, 0) ;
633 setbuf (stdout, NULL) ;
634 setbuf (stderr, NULL) ;
639 static void writePidFile (void)
645 logAndExit (1,"NULL pidFile\n") ;
647 /* Record our PID. */
649 if ((F = fopen(pidFile, "w")) == NULL)
651 syslog(LOG_ERR, "ME cant fopen %s %m", pidFile);
655 if (fprintf(F, "%ld\n", (long)pid) == EOF || ferror(F))
657 syslog(LOG_ERR, "ME cant fprintf %s %m", pidFile);
659 if (fclose(F) == EOF)
661 syslog(LOG_ERR, "ME cant fclose %s %m", pidFile);
663 if (chmod(pidFile, 0664) < 0)
665 syslog(LOG_ERR, "ME cant chmod %s %m", pidFile);
670 static void gprintinfo (void)
674 time_t now = theTime() ;
676 snapshotFile = buildFilename(innconf->pathlog, SNAPSHOT_FILE);
677 fp = fopen (snapshotFile,"a") ;
680 syswarn ("ME fopen %s", snapshotFile) ;
686 #if defined (HAVE_SETBUFFER)
687 setbuffer (fp, NULL, 0) ;
692 fprintf (fp,"----------------------------System snaphot taken at: %s\n",
694 gPrintListenerInfo (fp,0) ;
695 fprintf (fp,"\n\n\n\n") ;
696 gPrintHostInfo (fp,0) ;
697 fprintf (fp,"\n\n\n\n") ;
698 gPrintCxnInfo (fp,0) ;
699 fprintf (fp,"\n\n\n\n") ;
700 gPrintArticleInfo (fp,0) ;
701 fprintf (fp,"\n\n\n\n") ;
702 gPrintBufferInfo (fp,0) ;
703 fprintf (fp,"\n\n\n\n") ;
707 /* called after the config file is loaded and after the config data has
708 been updated with command line options. */
709 static int mainConfigLoadCbk (void *data)
711 FILE *fp = (FILE *) data ;
716 if (getString (topScope,"news-spool", &p,NO_INHERIT))
718 if ( !isDirectory (p) && isDirectory (innconf->patharticles) )
720 logOrPrint (LOG_WARNING,fp,
721 "ME config: definition of news-spool (%s) is a"
722 " non-existant directory. Using %s",p,
723 innconf->patharticles) ;
724 p = xstrdup (innconf->patharticles) ;
726 else if (!isDirectory (p))
727 logAndExit (1,"Bad spool directories: %s, %s\n",p,innconf->patharticles) ;
729 else if (!isDirectory (innconf->patharticles))
730 logAndExit (1,"ME config: no definition of news-spool, and %s is no good",
731 innconf->patharticles);
733 p = xstrdup (innconf->patharticles) ;
736 /***************************************************/
738 if (getString (topScope,"input-file",&p,NO_INHERIT))
741 InputFile = buildFilename (getTapeDirectory(),p) ;
747 if (getString (topScope,"pid-file",&p,NO_INHERIT))
749 pidFile = buildFilename (innconf->pathrun,p) ;
753 pidFile = buildFilename (innconf->pathrun,PID_FILE) ;
755 if (getInteger (topScope,"debug-level",&ival,NO_INHERIT))
756 loggingLevel = (unsigned int) ival ;
759 if (getInteger (topScope,"initial-sleep",&ival,NO_INHERIT))
760 initialSleep = (unsigned int) ival ;
763 if (getBool (topScope,"use-mmap",&bval,NO_INHERIT))
764 useMMap = (bval ? true : false) ;
767 if (getString (topScope,"log-file",&p,NO_INHERIT))
769 logFile = buildFilename (innconf->pathlog,p) ;
773 /* For imap/lmtp delivering */
774 if (getString (topScope,"deliver-username",&p, NO_INHERIT))
776 deliver_username = p;
777 /* don't need to free */
780 if (getString (topScope,"deliver-authname",&p, NO_INHERIT))
782 deliver_authname = p;
783 /* don't need to free */
786 if (getString (topScope,"deliver-password",&p, NO_INHERIT))
788 deliver_password = p;
789 /* don't need to free */
792 if (getString (topScope,"deliver-realm",&p, NO_INHERIT))
795 /* don't need to free */
798 if (getString (topScope,"deliver-rcpt-to",&p, NO_INHERIT))
801 /* don't need to free */
804 if (getString (topScope,"deliver-to-header",&p, NO_INHERIT))
806 deliver_to_header = p;
807 /* don't need to free */
816 * called after config file is loaded but before other callbacks, so we
817 * can adjust config file values from options. They will be validated in the
820 static int mainOptionsProcess (void *data UNUSED)
826 if ((v = findValue (topScope,"backlog-directory",NO_INHERIT)) != NULL)
828 free (v->v.charp_val) ;
829 v->v.charp_val = xstrdup (bopt) ;
832 addString (topScope,"backlog-directory",xstrdup (bopt)) ;
837 if ((v = findValue (topScope,"news-spool",NO_INHERIT)) != NULL)
839 free (v->v.charp_val) ;
840 v->v.charp_val = xstrdup (aopt) ;
843 addString (topScope,"news-spool",xstrdup (aopt)) ;
848 if ((v = findValue (topScope,"status-file",NO_INHERIT)) != NULL)
850 free (v->v.charp_val) ;
851 v->v.charp_val = xstrdup (sopt) ;
854 addString (topScope,"status-file",xstrdup (sopt)) ;
860 if ((v = findValue (topScope,"debug-level",NO_INHERIT)) != NULL)
861 v->v.int_val = debugLevel ;
863 addInteger (topScope,"debug-level",debugLevel) ;
867 if (eopt || talkToSelf)
872 if ((v = findValue (topScope,"backlog-limit",NO_INHERIT)) != NULL)
873 v->v.int_val = elimit ;
875 addInteger (topScope,"backlog-limit",elimit) ;
881 if ((v = findValue (topScope,"use-mmap",NO_INHERIT)) != NULL)
884 addBoolean (topScope,"use-mmap",0) ;
890 if ((v = findValue (topScope,"pid-file",NO_INHERIT)) != NULL)
892 free (v->v.charp_val) ;
893 v->v.charp_val = xstrdup (popt) ;
896 addString (topScope,"pid-file",xstrdup (popt)) ;
901 if ((v = findValue (topScope,"connection-stats",NO_INHERIT)) != NULL)
904 addBoolean (topScope,"connection-stats",1) ;
909 if ((v = findValue (topScope,"log-file",NO_INHERIT)) != NULL)
911 free (v->v.charp_val) ;
912 v->v.charp_val = xstrdup (lopt) ;
915 addString (topScope,"log-file",xstrdup (lopt)) ;
918 if (InputFile != NULL)
920 if ((v = findValue (topScope,"input-file",NO_INHERIT)) != NULL)
922 free (v->v.charp_val) ;
923 v->v.charp_val = xstrdup (InputFile) ;
926 addString (topScope,"input-file",xstrdup (InputFile)) ;
934 static void mainCleanup (void)
936 free ((void *)configFile) ;
937 free ((void *)pidFile) ;
947 void mainLogStatus (FILE *fp)
949 fprintf (fp,"%sGlobal configuration parameters:%s\n",
950 genHtml ? "<B>" : "", genHtml ? "</B>" : "") ;
951 fprintf (fp," Mode: ") ;
952 if (InputFile != NULL)
953 fprintf (fp,"Funnel file") ;
955 fprintf (fp,"Batch") ;
957 fprintf (fp,"Channel") ;
958 if (InputFile != NULL)
959 fprintf (fp," (%s)",(*InputFile == '\0' ? "stdin" : InputFile)) ;
961 fprintf (fp," News spool: %s\n",newsspool) ;
962 fprintf (fp," Pid file: %s\n",pidFile) ;
963 fprintf (fp," Log file: %s\n",(logFile == NULL ? "(none)" : logFile));
964 fprintf (fp," Debug level: %2ld Mmap: %s\n",
965 (long)loggingLevel,boolToString(useMMap)) ;