Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

run_kannel_box.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 #include <stdio.h>
00058 #include <stddef.h>
00059 #include <stdlib.h>
00060 #include <limits.h>
00061 
00062 #include <unistd.h>
00063 #include <errno.h>
00064 #include <string.h>
00065 #include <time.h>
00066 
00067 #include <sys/types.h>
00068 #include <sys/stat.h>
00069 #include <sys/wait.h>
00070 #include <fcntl.h>
00071 #include <signal.h>
00072 
00073 static char *progname;  /* The name of this program (for error messages) */
00074 static char **box_arglist;
00075 static int min_restart_delay = 60; /* in seconds */
00076 static pid_t child_box; /* used in main_loop, available to signal handlers */
00077 static char *pidfile; /* The name of the pidfile to use.  NULL if no pidfile */
00078 static int use_extra_args = 1; /* Add "extra_arguments" list to argv? */
00079 
00080 /* Extra arguments to pass to the box */
00081 static char *extra_arguments[] = {
00082     "-v", "4",   /* Minimal output on stderr, goes to /dev/null anyway */
00083 };
00084 #define NUM_EXTRA ((int) (sizeof(extra_arguments) / sizeof(*extra_arguments)))
00085 
00086 static void print_usage(FILE *stream)
00087 {
00088     fprintf(stream,
00089         "Usage: %s [--pidfile PIDFILE] [--min-delay SECONDS] BOXPATH [boxoptions...]\n",
00090         progname);
00091 }
00092 
00093 /* Create the argument list to pass to the box process, and put it
00094  * in the box_arglist global variable.  This is also the right place
00095  * to add any standard arguments that we want to pass. */
00096 static void build_box_arglist(char *boxfile, int argc, char **argv)
00097 {
00098     int i;
00099     char **argp;
00100 
00101     if (box_arglist) {
00102         free(box_arglist);
00103     }
00104 
00105     /* one for the boxfile name itself, one for each extra argument,
00106      * one for each normal argument, and one for the terminating NULL */
00107     box_arglist = malloc((1 + NUM_EXTRA + argc + 1) * sizeof(*box_arglist));
00108     if (!box_arglist) {
00109         fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
00110         exit(1);
00111     }
00112 
00113     /* Have argp walk down box_arglist and set each argument. */
00114     argp = box_arglist;
00115 
00116     *argp++ = boxfile;
00117         if (use_extra_args) {
00118         for (i = 0; i < NUM_EXTRA; i++) {
00119             *argp++ = extra_arguments[i];
00120         }
00121     }
00122     for (i = 0; i < argc; i++) {
00123         *argp++ = argv[i];
00124     }
00125     *argp++ = (char *)NULL;
00126 }
00127 
00128 static void write_pidfile(void)
00129 {
00130     int fd;
00131     FILE *f;
00132 
00133     if (!pidfile)
00134         return;
00135 
00136     fd = open(pidfile, O_WRONLY|O_NOCTTY|O_TRUNC|O_CREAT, 0644);
00137     if (fd < 0) {
00138         fprintf(stderr, "%s: open: %s: %s\n", progname, pidfile, strerror(errno));
00139         exit(1);
00140     }
00141 
00142     f = fdopen(fd, "w");
00143     if (!f) {
00144         fprintf(stderr, "%s: fdopen: %s\n", progname, strerror(errno));
00145         exit(1);
00146     }
00147 
00148     fprintf(f, "%ld\n", (long)getpid());
00149     if (fclose(f) < 0) {
00150         fprintf(stderr, "%s: writing %s: %s\n", progname, pidfile, strerror(errno));
00151         exit(1);
00152     }
00153 }
00154 
00155 static void remove_pidfile(void)
00156 {
00157     if (!pidfile)
00158         return;
00159 
00160     unlink(pidfile);
00161 }
00162 
00163 /* Set 0 (stdin) to /dev/null, and 1 and 2 (stdout and stderr) to /dev/full
00164  * if it's available and /dev/null otherwise. */
00165 static void rebind_standard_streams(void)
00166 {
00167     int devnullfd;
00168     int devfullfd;
00169 
00170     devnullfd = open("/dev/null", O_RDONLY);
00171     if (devnullfd < 0) {
00172         fprintf(stderr, "%s: cannot open /dev/null: %s\n",
00173             progname, strerror(errno));
00174         exit(2);
00175     }
00176     devfullfd = open("/dev/full", O_WRONLY);
00177     if (devfullfd < 0) {
00178         devfullfd = devnullfd;
00179     }
00180 
00181     /* Alert: The dup on stderr is done last, so that the error message
00182      * works regardless of which dup fails. */
00183     if (dup2(devnullfd, 0) < 0 ||
00184         dup2(devfullfd, 1) < 0 ||
00185         dup2(devfullfd, 2) < 0) {
00186         fprintf(stderr, "%s: dup2: %s\n", progname, strerror(errno));
00187         exit(1);
00188     }
00189 }
00190 
00191 /* Some code to determine the highest possible file descriptor number,
00192  * so that we can close them all.  */
00193 static int open_max(void)
00194 {
00195 #ifdef OPEN_MAX
00196     return OPEN_MAX;
00197 #else
00198     int max;
00199 
00200     max = sysconf(_SC_OPEN_MAX);
00201     if (max <= 0) {
00202         return 1024;  /* guess */
00203     }
00204     return max;
00205 #endif
00206 }
00207 
00208 /* Close all file descriptors other than 0, 1, and 2. */
00209 static void close_extra_files(void)
00210 {
00211     int max = open_max();
00212     int fd;
00213 
00214     for (fd = 3; fd < max; fd++) {
00215         close(fd);
00216     }
00217 }
00218 
00219 /* We received a signal that we should pass on to the child box.
00220  * We ignore it ourselves. */
00221 static void signal_transfer(int signum)
00222 {
00223     if (child_box > 0) {
00224         kill(child_box, signum);
00225     }
00226 }
00227 
00228 /* We received a signal that we should pass on to the child box,
00229  * and then die from ourselves.  It has to be a signal that
00230  * terminates the process as its default action! */
00231 static void signal_transfer_and_die(int signum)
00232 {
00233     /* First send it to the child process */
00234     if (child_box > 0) {
00235         kill(child_box, signum);
00236     }
00237 
00238     /* Prepare to die.  Normally the atexit handler would take care
00239      * of this when we exit(), but we're going to die from a signal. */
00240     remove_pidfile();
00241 
00242     /* Then send it to self.  First set the default handler, to
00243      * avoid catching the signal with this handler again.  This
00244      * is not a race, because it doesn't matter if we die from
00245      * the signal we're going to send or from a different one.  */
00246     signal(signum, SIG_DFL);
00247     kill(getpid(), signum);
00248 }
00249 
00250 static void setup_signals(void)
00251 {
00252     signal(SIGHUP, &signal_transfer);
00253     signal(SIGINT, &signal_transfer_and_die);
00254     signal(SIGQUIT, &signal_transfer_and_die);
00255     signal(SIGTERM, &signal_transfer_and_die);
00256     signal(SIGUSR1, &signal_transfer);
00257     signal(SIGUSR2, &signal_transfer);
00258 }
00259 
00260 /* Fork off a box process and loop indefinitely, forking a new one
00261  * every time it dies. */
00262 static int main_loop(char *boxfile)
00263 {
00264     time_t next_fork = 0;
00265 
00266     /* We can't report any errors here, because we are running
00267      * as a daemon and we have no logfile of our own.  So we
00268      * exit with errno as the exit code, to offer a minimal clue. */
00269 
00270     for (;;) {
00271 
00272         /* Make sure we don't fork in an endless loop if something
00273          * is drastically wrong.  This code limits it to one
00274          * per minute (or whatever min_restart_delay is set to). */
00275         time_t this_time = time(NULL);
00276         if (this_time <= next_fork) {
00277             sleep(next_fork - this_time);
00278         }
00279         next_fork = this_time + min_restart_delay;
00280 
00281         child_box = fork();
00282         if (child_box < 0) {
00283             return errno;
00284         }
00285         if (child_box == 0) {
00286             /* child.  exec the box */
00287             execvp(boxfile, box_arglist);
00288             exit(127);
00289         }
00290         
00291         while (waitpid(child_box, (int *)NULL, 0) != child_box) {
00292             if (errno == ECHILD) {
00293                 /* Something went wrong... we don't know what,
00294                  * but we do know that our child does not
00295                  * exist.  So restart it. */
00296                 break;
00297             }
00298             if (errno == EINTR) {
00299                 continue;
00300             }
00301             /* Something weird happened. */
00302             return errno;
00303         }
00304     }
00305 }
00306 
00307 int main(int argc, char *argv[])
00308 {
00309     int i;
00310     char *boxfile = NULL;
00311     pid_t childpid;
00312 
00313     progname = argv[0];
00314 
00315     if (argc == 1) {
00316         print_usage(stderr);
00317         exit(2);
00318     }
00319 
00320     /* Parse the options meant for the wrapper, and get the name of
00321      * the box to wrap. */
00322     for (i = 1; i < argc && !boxfile; i++) {
00323         if (strcmp(argv[i], "--pidfile") == 0) {
00324             if (i+1 >= argc) {
00325                 fprintf(stderr, "Missing argument for option %s\n", argv[i]);
00326                 exit(2);
00327             }
00328             pidfile = argv[i+1];
00329             i++;
00330         } else if (strcmp(argv[i], "--min-delay") == 0) {
00331             if (i+1 >= argc) {
00332                 fprintf(stderr, "Missing argument for option %s", argv[i]);
00333                 exit(2);
00334             }
00335             min_restart_delay = atoi(argv[i+1]);
00336             i++;
00337         } else if (strcmp(argv[i], "--no-extra-args") == 0) {
00338                 use_extra_args = 0;
00339         } else if (argv[i][0] == '-') {
00340             fprintf(stderr, "Unknown option %s\n", argv[i]);
00341             exit(2);
00342         } else {
00343             boxfile = argv[i];
00344         }
00345     }
00346 
00347     /* Check if we have everything */
00348     if (!boxfile) {
00349         print_usage(stderr);
00350         exit(2);
00351     }
00352 
00353     /* The remaining arguments should be passed to the box */
00354     build_box_arglist(boxfile, argc - i, argv + i);
00355 
00356     /* Ready to rock.  Begin daemonization. */
00357 
00358     /* Fork a child process and have the parent exit.
00359          * This makes us run in the background. */
00360     childpid = fork();
00361     if (childpid < 0) {
00362         fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
00363         exit(1);
00364     }
00365     if (childpid != 0) {
00366         exit(0); /* parent exits immediately */
00367     }
00368     
00369     /* The child continues here.  Now call setsid() to disconnect
00370      * from our terminal and from the parent's session and process
00371      * group. */
00372     if (setsid() < 0) {
00373         fprintf(stderr, "%s: setsid: %s\n", progname, strerror(errno));
00374         exit(1);
00375     }
00376 
00377     /* Change to the root directory, so that we don't keep a
00378      * file descriptor open on an unknown directory. */
00379     if (chdir("/") < 0) {
00380         fprintf(stderr, "%s: chdir to root: %s\n", progname, strerror(errno));
00381         exit(1);
00382     }
00383 
00384     atexit(remove_pidfile);
00385     write_pidfile();
00386 
00387     /* Set the umask to a known value, rather than inheriting
00388      * an unknown one. */
00389     umask(077);
00390 
00391     /* Leave file descriptors 0, 1, and 2 pointing to harmless
00392      * places, and close all other file descriptors. */
00393     rebind_standard_streams();
00394     close_extra_files();
00395 
00396     setup_signals();
00397     return main_loop(boxfile);
00398 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.