home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / n / bind / bind-4.001 / bind-4~ / bind-4.9.3-BETA9 / contrib / ninit / ninit.c < prev    next >
C/C++ Source or Header  |  1991-05-13  |  11KB  |  504 lines

  1. /*
  2.  * Named init --- sits around and restarts named when necessary
  3.  *
  4.  * Written by Theodore Ts'o.  Copyright 1991.
  5.  * 
  6.  * Use this code however you want, as long as you don't try to make
  7.  * money off of it and as long as you don't claim it's yours.
  8.  *
  9.  *    $Header: /afs/net.mit.edu/project/bind/named/RCS/ninit.c,v 1.3 91/05/13 17:30:58 tytso Exp $
  10.  *     $Source: /afs/net.mit.edu/project/bind/named/RCS/ninit.c,v $
  11.  *
  12.  * Note that ninit requires that named be modified so that it accepts
  13.  * the -n option.  This option to named asks named not to fork into
  14.  * the background when it is started up.  This is necessary because
  15.  * ninit wants to be notified when the named process exits.
  16.  *
  17.  * Usage: ninit [options] [named boot file]
  18.  *
  19.  * Options:
  20.  *     -n <negative niceness>    Run the named process with the nice level
  21.  *                 *reduced* argument level.  This is useful
  22.  *                 on mailhub, when you have 30+ sendmail
  23.  *                 processes and one named process, and you
  24.  *                 want to give the named process a
  25.  *                 higher priority.
  26.  *
  27.  *     -s <stats interval>    This option controls how often (in
  28.  *                 seconds) named should be strobed to
  29.  *                 update the named.stats file.
  30.  *
  31.  *     -c <check interval>    This option controls how often (in
  32.  *                 seconds) ninit should check to make
  33.  *                 sure named is still working.
  34.  *
  35.  *     -f <syslog facility>    This option controls which syslog facility
  36.  *                 ninit should use.
  37.  *
  38.  *     -d <debug level>    This option is passed to named to turn
  39.  *                 on nameserver debugging.
  40.  *
  41.  *     -N            This option tells ninit not fork and go
  42.  *                 into the background when it is started
  43.  *                 up.
  44.  *
  45.  */
  46.  
  47. #include <stdio.h>
  48. #include <sys/types.h>
  49. #include <syslog.h>
  50. #include <errno.h>
  51. #include <signal.h>
  52. #include <netdb.h>
  53. #include <ctype.h>
  54. #include <sys/socket.h>
  55. #include <arpa/nameser.h>
  56. #include <netinet/in.h>
  57. #include <resolv.h>
  58. #include <sys/wait.h>
  59. #include <sys/file.h>
  60. #include <sys/time.h>
  61. #include <sys/resource.h>
  62.  
  63. /*
  64.  * Configuration section.  It should be fairly obvious.....
  65.  *
  66.  * FORKING_NAMED is intended for site who do not have source code to
  67.  * their named, and so cannot add the -n option to named.
  68.  *
  69.  * TEST_HOST specifies the host which ninit should try to resolve when
  70.  * checking to see if named is working.
  71.  */
  72.  
  73. #define TEST_HOST "MIT.EDU"
  74. #define NAMED "/etc/named"
  75. #define NAMED_PID_FILE "/etc/named.pid"
  76. #define PID_FILE "/etc/ninit.pid"
  77. #define DEFAULT_FACILITY LOG_LOCAL4
  78. /* #define FORKING_NAMED */
  79.  
  80. extern int h_errno;
  81.  
  82. int    named_pid = -1;
  83. int    terminating_named = 0;
  84. int    named_restarting = 0;
  85. int    child_died = 0;
  86.  
  87. int    stats_timer = 0;
  88. int    check_timer = 0;
  89.  
  90. char    *named_file = 0;
  91. int    named_nice = 0;
  92. int    debug = 0;
  93. int    syslog_facility = DEFAULT_FACILITY;
  94. char    *progname = 0;
  95. int    nofork = 0;
  96. int    stats_interval = 300;
  97. int    check_interval = 60;
  98.  
  99. struct in_addr local_addr;
  100.     
  101. void    start_named(), PRS(), usage();
  102. int    decode_facility();
  103.  
  104. struct code {
  105.         char    *name;
  106.         int     facility;
  107. };
  108.  
  109. struct code     FacNames[] = {
  110.         "kern",         LOG_KERN,
  111.         "user",         LOG_USER,
  112.         "mail",         LOG_MAIL,
  113.         "daemon",       LOG_DAEMON,
  114.         "auth",         LOG_AUTH,
  115.         "syslog",       LOG_SYSLOG,
  116.         "lpr",          LOG_LPR,
  117. #ifdef LOG_NEWS
  118.         "news",         LOG_NEWS,
  119. #endif
  120. #ifdef LOG_UUCP
  121.         "uucp",         LOG_UUCP,
  122. #endif
  123.         "local0",       LOG_LOCAL0,
  124.         "local1",       LOG_LOCAL1,
  125.         "local2",       LOG_LOCAL2,
  126.         "local3",       LOG_LOCAL3,
  127.         "local4",       LOG_LOCAL4,
  128.         "local5",       LOG_LOCAL5,
  129.         "local6",       LOG_LOCAL6,
  130.         "local7",       LOG_LOCAL7,
  131.         "security",     LOG_AUTH,
  132. #ifdef LOG_MARK
  133.         "mark",         LOG_MARK,
  134. #endif
  135.         NULL,           -1
  136. };
  137.  
  138. int alarm_chk()
  139. {
  140.     int    named_active = 1;
  141.     int    do_check = 0;
  142.     int    next_alarm;
  143.     
  144.     if ((named_pid <= 0) || named_restarting || terminating_named) {
  145.         named_active = 0;
  146.     }
  147.     /*
  148.      * We could put something here to increment the timers by alarm(0),
  149.      * but we won't bother for now.
  150.      *
  151.      * Check to see if the timers have expired; if so, run the events
  152.      */
  153.     if (named_active && (stats_timer <= 0)) {
  154.         (void) rename("/usr/tmp/named.stats",
  155.                   "/usr/tmp/named.stats.old");
  156.         kill(named_pid, SIGIOT);
  157.         stats_timer = stats_interval;
  158.     }
  159.     if (named_active && (check_timer <= 0)) {
  160.         /*
  161.          * We run this one after restarting the alarm because
  162.          * it might take a while
  163.          */
  164.         do_check++;
  165.         check_timer = check_interval;
  166.     }
  167.     /*
  168.      * Now figure out when the next time we need to run.
  169.      */
  170.     if (stats_timer < check_timer)
  171.         next_alarm = stats_timer;
  172.     else
  173.         next_alarm = check_timer;
  174.     stats_timer -= next_alarm;
  175.     check_timer -= next_alarm;
  176.     alarm(next_alarm);
  177.     /*
  178.      * Now, perform the named check if necessary
  179.      */
  180.     if (do_check) {
  181.         (void) gethostbyname(TEST_HOST);
  182.         if (h_errno == TRY_AGAIN) {
  183.             syslog(LOG_ERR, "Named hosed!  Restarting...");
  184.             restart_named();
  185.         }
  186.     }
  187. }
  188.  
  189. int restart_named()
  190. {
  191.     if ((named_pid <= 0) || named_restarting || terminating_named)
  192.         return;
  193.     terminating_named++;
  194.     kill(named_pid, SIGTERM);
  195. }
  196.  
  197. int die()
  198. {
  199.     syslog(LOG_NOTICE, "/etc/ninit received SIGTERM, exiting");
  200.     if (named_pid > 0) {
  201.         kill(named_pid, SIGTERM);
  202.         syslog(LOG_NOTICE, "named killed as part of ninit shutdown");
  203.     }
  204.     (void) unlink(NAMED_PID_FILE);
  205.     (void) unlink(PID_FILE);
  206.     exit(0);
  207. }
  208.  
  209. int mourner()
  210. {
  211.     child_died++;
  212. }
  213.  
  214. main(argc, argv)
  215.     int    argc;
  216.     char    **argv;
  217. {
  218.     int    pid;
  219.     union wait status;
  220.     
  221.     PRS(argc, argv);
  222.     daemon_setup();
  223.     openlog("ninit", LOG_PID|LOG_CONS, syslog_facility);
  224.     syslog(LOG_INFO, "ninit started, pid = %d", getpid());
  225.     signal(SIGALRM, alarm_chk);
  226.     signal(SIGHUP, restart_named);
  227.     signal(SIGTERM, die);
  228.     signal(SIGCHLD, mourner);
  229.     start_named();
  230.     alarm_chk();
  231.     
  232.     while (1) {
  233.         pid = wait(&status);
  234.  
  235.         if (pid == -1) {
  236.             if (errno == ECHILD) {
  237.                 named_pid = -1;
  238.                 start_named();
  239.                 continue;
  240.             } else {
  241.                 syslog(LOG_CRIT,
  242.                        "Error in wait: %s",
  243.                        sys_errlist[errno]);
  244.                 sleep(120);
  245.                 continue;
  246.             }
  247.         }
  248.         named_pid = -1;
  249.         if (terminating_named || (status.w_termsig == SIGTERM)) {
  250.             terminating_named = 0;
  251.         } else if (status.w_termsig) {
  252.             syslog(LOG_ERR, "Named terinated with signal %d%s",
  253.                    status.w_termsig,
  254.                    status.w_coredump ? " (core dumped)" : "");
  255.         } else {
  256.             syslog(status.w_retcode ? LOG_ERR : LOG_NOTICE,
  257.                    "named termined with exit status %d",
  258.                    status.w_retcode);
  259.         }
  260.         start_named();
  261.     }
  262.     
  263. }
  264.  
  265. void start_named()
  266. {
  267.     int    pid;
  268.     int    tries = 3;
  269.     FILE    *f;
  270.     char    debug_buf[24];
  271.     char    *argv[10];
  272.     int    argc = 0;
  273.  
  274.     if (f = fopen(NAMED_PID_FILE, "r")) {
  275.         fscanf(f, "%d", &pid);
  276.         fclose(f);
  277.         if ((pid > 0) && !kill(pid, 0)) {
  278.             /* There is a running named already, kill it. */
  279.             syslog(LOG_WARNING,
  280.                    "Killing already existing named, pid = %d",
  281.                    pid);
  282.             kill(pid, 15);
  283.         }
  284.     }
  285.  
  286.     if (named_restarting++ > 3) {
  287.         syslog(LOG_ALERT,
  288.                "Couldn't start named after three tries, sleeping");
  289.         sleep(180);
  290.         named_restarting = 1;
  291.     }
  292.     child_died = 0;
  293.     if ((pid = vfork()) == 0) {
  294.         argv[argc++]  = "named";
  295.         if (debug) {
  296.             argv[argc++] = "-d";
  297.             sprintf(debug_buf, "%d", debug);
  298.             argv[argc++] = debug_buf;
  299.         }
  300. #ifdef FORKING_NAMED
  301.         else
  302.             argv[argc++] = "-d";
  303. #else
  304.         argv[argc++] = "-n";
  305. #endif
  306.         argv[argc++] = named_file;
  307.         argv[argc++] = 0;
  308.         execv(NAMED, argv);
  309.         syslog(LOG_ERR, "Couldn't start named: %s",
  310.                sys_errlist[errno]);
  311.         exit(1);
  312.     } else if (pid == -1) {
  313.         syslog(LOG_ERR, "Couldn't fork to start named: %s",
  314.                sys_errlist[errno]);
  315.         sleep(180);
  316.         return;
  317.     } else {
  318.         syslog(LOG_DEBUG, "Named process started, pid = %d", pid);
  319.         named_pid = pid;
  320.         setpriority(PRIO_PROCESS, pid, named_nice);
  321.         named_restarting++;
  322.         /* Stall until the named is really working */
  323.         do {
  324.             if (child_died)
  325.                 return;
  326.             (void) gethostbyname(TEST_HOST);
  327.         } while (h_errno == TRY_AGAIN);
  328.         syslog(LOG_INFO, "Named successfully started, pid = %d\n",
  329.                pid);
  330.         named_restarting = 0;
  331. #ifdef FORKING_NAMED
  332.         /* Turn off debugging */
  333.         if (!debug)
  334.             kill(named_pid, SIGUSR2);
  335. #endif
  336.         /*
  337.          * Reset timers...
  338.          */
  339.         stats_timer = stats_interval;
  340.         check_timer = check_interval;
  341.         alarm_chk();
  342.     }
  343. }
  344.  
  345. void PRS(argc, argv)
  346.     int    argc;
  347.     char    **argv;
  348. {
  349.     register char    *word, ch;
  350.     char    *buf;
  351.     char    *fac = NULL;
  352.     
  353.     local_addr.s_addr = inet_addr("127.0.0.1");
  354.     res_init();
  355.     _res.nscount = 1;    /* One nameserver --- the local one */
  356.     _res.nsaddr.sin_addr = local_addr;
  357.     _res.retry = 2;
  358.     _res.retrans = RES_TIMEOUT;
  359.     progname = *argv++;
  360.     while (word = *argv++) {
  361.         if (*word == '-') {
  362.             word++;
  363.             while (word && (ch = *word++)) {
  364.                 switch(ch){
  365.                 case 'n':     /* Niceness */
  366.                     if (*word)
  367.                         buf = word;
  368.                     else
  369.                         buf = *argv++;
  370.                     if (!buf)
  371.                         usage();
  372.                     named_nice = 0 - atoi(buf);
  373.                     word = 0;
  374.                     break;
  375.                 case 's':     /* Stats int. */
  376.                     if (*word)
  377.                         buf = word;
  378.                     else
  379.                         buf = *argv++;
  380.                     if (!buf)
  381.                         usage();
  382.                     stats_interval = atoi(buf);
  383.                     word = 0;
  384.                     break;
  385.                 case 'c':     /* Check int. */
  386.                     if (*word)
  387.                         buf = word;
  388.                     else
  389.                         buf = *argv++;
  390.                     if (!buf)
  391.                         usage();
  392.                     check_interval = atoi(buf);
  393.                     word = 0;
  394.                     break;
  395.                 case 'f':     /* Syslog facility */
  396.                     if (*word)
  397.                         fac = word;
  398.                     else
  399.                         fac = *argv++;
  400.                     if (!fac)
  401.                         usage();
  402.                     word = 0;
  403.                     break;
  404.                 case 'd':     /* Debug */
  405.                     if (*word)
  406.                         buf = word;
  407.                     else
  408.                         buf = *argv++;
  409.                     if (!buf)
  410.                         usage();
  411.                     debug = atoi(buf);
  412.                     word = 0;
  413.                     break;
  414.                 case 'N':     /* Nofork */
  415.                     nofork++;
  416.                     break;
  417.                 default:
  418.                     usage();
  419.                 }
  420.                 
  421.             }
  422.         } else {
  423.             if (named_file)
  424.                 usage();
  425.             else
  426.                 named_file = word;
  427.         }
  428.     }
  429.     if (fac) {
  430.         syslog_facility = decode_facility(fac);
  431.         if (syslog_facility < 0) {
  432.             fprintf(stderr, "%s is not a valid facility\n", fac);
  433.             exit(1);
  434.         }
  435.     }
  436. }
  437.  
  438. void usage()
  439. {
  440.     fprintf(stderr,
  441.         "Usage: %s [OPTIONS] named_boot_file\n\n", progname);
  442.     fprintf(stderr, "Where the options can be: \n");
  443.     fprintf(stderr, "\t-d debug_level\tTurns on debugging\n");
  444.     fprintf(stderr, "\t-n nice arg\tRenices the named by -nice arg\n");
  445.     fprintf(stderr, "\t-f facility\tUses the specified syslog facility\n");
  446.     fprintf(stderr, "\t-s stats_interval\tSpecifies the statistics polling interval\n");
  447.     fprintf(stderr, "\t-c check_interval\tSpecifies the named check interval\n");
  448.     fprintf(stderr, "\t-N\t\tCauses ninit not to fork on startup.\n");
  449.     exit(1);
  450. }
  451.  
  452. daemon_setup()
  453. {
  454.     int    n, pid;
  455.     FILE    *f;
  456.  
  457.     if (f = fopen(PID_FILE, "r")) {
  458.         fscanf(f, "%d", &pid);
  459.         fclose(f);
  460.         if (!kill(pid, 0)) {
  461.             fprintf(stderr,
  462.                 "ninit is already running on pid = %d\n",
  463.                 pid);
  464.             exit(1);
  465.         }
  466.     }
  467.     
  468.     if (!nofork) {
  469.         if (fork() > 0)
  470.             exit(0);
  471.         n = open("/dev/null", O_RDONLY);
  472.         (void) dup2(n, 0);
  473.         (void) dup2(n, 1);
  474.         (void) dup2(n, 2);
  475.         if (n > 2)
  476.             (void) close(n);
  477.     }
  478.     
  479.     if ((f = fopen(PID_FILE, "w")) == NULL)
  480.         syslog(LOG_ERR, "Couldn't open pid file for write, %s",
  481.                sys_errlist[errno]);
  482.     fprintf(f, "%d\n", getpid());
  483.     fclose(f);
  484. }
  485.  
  486. int decode_facility(name)
  487.     char    *name;
  488. {
  489.     char    buf[40], *src, *dest;
  490.     struct code    *p;
  491.     int    i;
  492.  
  493.     for (src = name, dest = buf, i = 0; *src && i < 40; src++, dest++, i++)
  494.         *dest = (isupper(*src)) ? tolower(*src) : *src;
  495.     for (p = FacNames; p->name; p++)
  496.         if (!strcmp(buf, p->name))
  497.             return(p->facility);
  498.     return(-1);
  499. }
  500.  
  501.         
  502.     
  503.     
  504.