Home | History | Annotate | Line # | Download | only in kadm5
      1  1.1     elric /*	$NetBSD: ipropd_common.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
      2  1.1     elric 
      3  1.1     elric /*
      4  1.1     elric  * Copyright (c) 1997 - 2007 Kungliga Tekniska Hgskolan
      5  1.1     elric  * (Royal Institute of Technology, Stockholm, Sweden).
      6  1.1     elric  * All rights reserved.
      7  1.1     elric  *
      8  1.1     elric  * Redistribution and use in source and binary forms, with or without
      9  1.1     elric  * modification, are permitted provided that the following conditions
     10  1.1     elric  * are met:
     11  1.1     elric  *
     12  1.1     elric  * 1. Redistributions of source code must retain the above copyright
     13  1.1     elric  *    notice, this list of conditions and the following disclaimer.
     14  1.1     elric  *
     15  1.1     elric  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1     elric  *    notice, this list of conditions and the following disclaimer in the
     17  1.1     elric  *    documentation and/or other materials provided with the distribution.
     18  1.1     elric  *
     19  1.1     elric  * 3. Neither the name of the Institute nor the names of its contributors
     20  1.1     elric  *    may be used to endorse or promote products derived from this software
     21  1.1     elric  *    without specific prior written permission.
     22  1.1     elric  *
     23  1.1     elric  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  1.1     elric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  1.1     elric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  1.1     elric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  1.1     elric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  1.1     elric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  1.1     elric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  1.1     elric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  1.1     elric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  1.1     elric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  1.1     elric  * SUCH DAMAGE.
     34  1.1     elric  */
     35  1.1     elric 
     36  1.1     elric #include "iprop.h"
     37  1.2  christos 
     38  1.2  christos #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
     39  1.2  christos #include <sys/types.h>
     40  1.2  christos #include <sys/wait.h>
     41  1.2  christos #endif
     42  1.1     elric 
     43  1.1     elric sig_atomic_t exit_flag;
     44  1.1     elric 
     45  1.1     elric static RETSIGTYPE
     46  1.1     elric sigterm(int sig)
     47  1.1     elric {
     48  1.1     elric     exit_flag = sig;
     49  1.1     elric }
     50  1.1     elric 
     51  1.1     elric void
     52  1.1     elric setup_signal(void)
     53  1.1     elric {
     54  1.1     elric #ifdef HAVE_SIGACTION
     55  1.1     elric     {
     56  1.1     elric 	struct sigaction sa;
     57  1.1     elric 
     58  1.1     elric 	sa.sa_flags = 0;
     59  1.1     elric 	sa.sa_handler = sigterm;
     60  1.1     elric 	sigemptyset(&sa.sa_mask);
     61  1.1     elric 
     62  1.1     elric 	sigaction(SIGINT, &sa, NULL);
     63  1.1     elric 	sigaction(SIGTERM, &sa, NULL);
     64  1.1     elric 	sigaction(SIGXCPU, &sa, NULL);
     65  1.1     elric 
     66  1.1     elric 	sa.sa_handler = SIG_IGN;
     67  1.1     elric 	sigaction(SIGPIPE, &sa, NULL);
     68  1.1     elric     }
     69  1.1     elric #else
     70  1.1     elric     signal(SIGINT, sigterm);
     71  1.1     elric     signal(SIGTERM, sigterm);
     72  1.1     elric #ifndef NO_SIGXCPU
     73  1.1     elric     signal(SIGXCPU, sigterm);
     74  1.1     elric #endif
     75  1.1     elric #ifndef NO_SIGPIPE
     76  1.1     elric     signal(SIGPIPE, SIG_IGN);
     77  1.1     elric #endif
     78  1.1     elric #endif
     79  1.1     elric }
     80  1.2  christos 
     81  1.2  christos /*
     82  1.2  christos  * Fork a child to run the service, and restart it if it dies.
     83  1.2  christos  *
     84  1.2  christos  * Returns -1 if not supported, else a file descriptor that the service
     85  1.2  christos  * should select() for.  Any events on that file descriptor should cause
     86  1.2  christos  * the caller to exit immediately, as that means that the restarter
     87  1.2  christos  * exited.
     88  1.2  christos  *
     89  1.2  christos  * The service's normal exit status values should be should be taken
     90  1.2  christos  * from enum ipropd_exit_code.  IPROPD_FATAL causes the restarter to
     91  1.2  christos  * stop restarting the service and to exit.
     92  1.2  christos  *
     93  1.2  christos  * A count of restarts is output via the `countp' argument, if it is
     94  1.2  christos  * non-NULL.  This is useful for testing this function (e.g., kill the
     95  1.2  christos  * restarter after N restarts and check that the child gets the signal
     96  1.2  christos  * sent to it).
     97  1.2  christos  *
     98  1.2  christos  * This requires fork() and waitpid() (otherwise returns -1).  Ignoring
     99  1.2  christos  * SIGCHLD, of course, would be bad.
    100  1.2  christos  *
    101  1.2  christos  * We could support this on Windows by spawning a child with mostly the
    102  1.2  christos  * same arguments as the restarter process.
    103  1.2  christos  */
    104  1.2  christos int
    105  1.2  christos restarter(krb5_context context, size_t *countp)
    106  1.2  christos {
    107  1.2  christos #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
    108  1.2  christos     struct timeval tmout;
    109  1.2  christos     pid_t pid = -1;
    110  1.2  christos     pid_t wpid = -1;
    111  1.2  christos     int status;
    112  1.2  christos     int fds[2];
    113  1.2  christos     int fds2[2];
    114  1.2  christos     size_t count = 0;
    115  1.2  christos     fd_set readset;
    116  1.2  christos 
    117  1.2  christos     fds[0] = -1;
    118  1.2  christos     fds[1] = -1;
    119  1.2  christos     fds2[0] = -1;
    120  1.2  christos     fds2[1] = -1;
    121  1.2  christos 
    122  1.2  christos     signal(SIGCHLD, SIG_DFL);
    123  1.2  christos 
    124  1.2  christos     while (!exit_flag) {
    125  1.2  christos         /* Close the pipe ends we keep open */
    126  1.2  christos         if (fds[1] != -1)
    127  1.2  christos             (void) close(fds[1]);
    128  1.2  christos         if (fds2[0] != -1)
    129  1.2  christos             (void) close(fds2[1]);
    130  1.2  christos 
    131  1.2  christos         /* A pipe so the child can detect the parent's death */
    132  1.2  christos         if (pipe(fds) == -1) {
    133  1.2  christos             krb5_err(context, 1, errno,
    134  1.2  christos                      "Could not setup pipes in service restarter");
    135  1.2  christos         }
    136  1.2  christos 
    137  1.2  christos         /* A pipe so the parent can detect the child's death */
    138  1.2  christos         if (pipe(fds2) == -1) {
    139  1.2  christos             krb5_err(context, 1, errno,
    140  1.2  christos                      "Could not setup pipes in service restarter");
    141  1.2  christos         }
    142  1.2  christos 
    143  1.2  christos         fflush(stdout);
    144  1.2  christos         fflush(stderr);
    145  1.2  christos 
    146  1.2  christos         pid = fork();
    147  1.2  christos         if (pid == -1)
    148  1.2  christos             krb5_err(context, 1, errno, "Could not fork in service restarter");
    149  1.2  christos         if (pid == 0) {
    150  1.2  christos             if (countp != NULL)
    151  1.2  christos                 *countp = count;
    152  1.2  christos             (void) close(fds[1]);
    153  1.2  christos             (void) close(fds2[0]);
    154  1.2  christos             return fds[0];
    155  1.2  christos         }
    156  1.2  christos 
    157  1.2  christos         count++;
    158  1.2  christos 
    159  1.2  christos         (void) close(fds[0]);
    160  1.2  christos         (void) close(fds2[1]);
    161  1.2  christos 
    162  1.2  christos         do {
    163  1.2  christos             wpid = waitpid(pid, &status, 0);
    164  1.2  christos         } while (wpid == -1 && errno == EINTR && !exit_flag);
    165  1.2  christos         if (wpid == -1 && errno == EINTR)
    166  1.2  christos             break; /* We were signaled; gotta kill the child and exit */
    167  1.2  christos         if (wpid == -1) {
    168  1.2  christos             if (errno != ECHILD) {
    169  1.2  christos                 warn("waitpid() failed; killing restarter's child process");
    170  1.2  christos                 kill(pid, SIGTERM);
    171  1.2  christos             }
    172  1.2  christos             krb5_err(context, 1, errno, "restarter failed waiting for child");
    173  1.2  christos         }
    174  1.2  christos 
    175  1.2  christos         assert(wpid == pid);
    176  1.2  christos         wpid = -1;
    177  1.2  christos         pid = -1;
    178  1.2  christos         if (WIFEXITED(status)) {
    179  1.2  christos             switch (WEXITSTATUS(status)) {
    180  1.2  christos             case IPROPD_DONE:
    181  1.2  christos                 exit(0);
    182  1.2  christos             case IPROPD_RESTART_SLOW:
    183  1.2  christos                 if (exit_flag)
    184  1.2  christos                     exit(1);
    185  1.2  christos                 krb5_warnx(context, "Waiting 2 minutes to restart");
    186  1.2  christos                 sleep(120);
    187  1.2  christos                 continue;
    188  1.2  christos             case IPROPD_FATAL:
    189  1.2  christos                 krb5_errx(context, WEXITSTATUS(status),
    190  1.2  christos                          "Sockets and pipes not supported for "
    191  1.2  christos                          "iprop log files");
    192  1.2  christos             case IPROPD_RESTART:
    193  1.2  christos             default:
    194  1.2  christos                 if (exit_flag)
    195  1.2  christos                     exit(1);
    196  1.2  christos                 /* Add exponential backoff (with max backoff)? */
    197  1.2  christos                 krb5_warnx(context, "Waiting 30 seconds to restart");
    198  1.2  christos                 sleep(30);
    199  1.2  christos                 continue;
    200  1.2  christos             }
    201  1.2  christos         }
    202  1.2  christos         /* else */
    203  1.2  christos         krb5_warnx(context, "Child was killed; waiting 30 seconds to restart");
    204  1.2  christos         sleep(30);
    205  1.2  christos     }
    206  1.2  christos 
    207  1.2  christos     if (pid == -1)
    208  1.2  christos         exit(0); /* No dead child to reap; done */
    209  1.2  christos 
    210  1.2  christos     assert(pid > 0);
    211  1.2  christos     if (wpid != pid) {
    212  1.2  christos         warnx("Interrupted; killing child (pid %ld) with %d",
    213  1.2  christos               (long)pid, exit_flag);
    214  1.2  christos         krb5_warnx(context, "Interrupted; killing child (pid %ld) with %d",
    215  1.2  christos                    (long)pid, exit_flag);
    216  1.2  christos         kill(pid, exit_flag);
    217  1.2  christos 
    218  1.2  christos         /* Wait up to one second for the child */
    219  1.2  christos         tmout.tv_sec = 1;
    220  1.2  christos         tmout.tv_usec = 0;
    221  1.2  christos         FD_ZERO(&readset);
    222  1.2  christos         FD_SET(fds2[0], &readset);
    223  1.2  christos         /* We don't care why select() returns */
    224  1.2  christos         (void) select(fds2[0] + 1, &readset, NULL, NULL, &tmout);
    225  1.2  christos         /*
    226  1.2  christos          * We haven't reaped the child yet; if it's a zombie, then
    227  1.2  christos          * SIGKILLing it won't hurt.  If it's not a zombie yet, well,
    228  1.2  christos          * we're out of patience.
    229  1.2  christos          */
    230  1.2  christos         kill(pid, SIGKILL);
    231  1.2  christos         do {
    232  1.2  christos             wpid = waitpid(pid, &status, 0);
    233  1.2  christos         } while (wpid != pid && errno == EINTR);
    234  1.2  christos         if (wpid == -1)
    235  1.2  christos             krb5_err(context, 1, errno, "restarter failed waiting for child");
    236  1.2  christos     }
    237  1.2  christos 
    238  1.2  christos     /* Finally, the child is dead and reaped */
    239  1.2  christos     if (WIFEXITED(status))
    240  1.2  christos         exit(WEXITSTATUS(status));
    241  1.2  christos     if (WIFSIGNALED(status)) {
    242  1.2  christos         switch (WTERMSIG(status)) {
    243  1.2  christos         case SIGTERM:
    244  1.2  christos         case SIGXCPU:
    245  1.2  christos         case SIGINT:
    246  1.2  christos             exit(0);
    247  1.2  christos         default:
    248  1.2  christos             /*
    249  1.2  christos              * Attempt to set the same exit status for the parent as for
    250  1.2  christos              * the child.
    251  1.2  christos              */
    252  1.2  christos             kill(getpid(), WTERMSIG(status));
    253  1.2  christos             /*
    254  1.2  christos              * We can get past the self-kill if we inherited a SIG_IGN
    255  1.2  christos              * disposition that the child reset to SIG_DFL.
    256  1.2  christos              */
    257  1.2  christos         }
    258  1.2  christos     }
    259  1.2  christos     exit(1);
    260  1.2  christos #else
    261  1.2  christos     if (countp != NULL)
    262  1.2  christos         *countp = 0;
    263  1.2  christos     errno = ENOTSUP;
    264  1.2  christos     return -1;
    265  1.2  christos #endif
    266  1.2  christos }
    267  1.2  christos 
    268