Kannel: Open Source WAP and SMS gateway  svn-r5335
run_kannel_box.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 #include <stdio.h>
58 #include <stddef.h>
59 #include <stdlib.h>
60 #include <limits.h>
61 
62 #include <unistd.h>
63 #include <errno.h>
64 #include <string.h>
65 #include <time.h>
66 
67 #include <sys/types.h>
68 #include <sys/stat.h>
69 #include <sys/wait.h>
70 #include <fcntl.h>
71 #include <signal.h>
72 
73 static char *progname; /* The name of this program (for error messages) */
74 static char **box_arglist;
75 static int min_restart_delay = 60; /* in seconds */
76 static pid_t child_box; /* used in main_loop, available to signal handlers */
77 static char *pidfile; /* The name of the pidfile to use. NULL if no pidfile */
78 static int use_extra_args = 1; /* Add "extra_arguments" list to argv? */
79 
80 /* Extra arguments to pass to the box */
81 static char *extra_arguments[] = {
82  "-v", "4", /* Minimal output on stderr, goes to /dev/null anyway */
83 };
84 #define NUM_EXTRA ((int) (sizeof(extra_arguments) / sizeof(*extra_arguments)))
85 
86 static void print_usage(FILE *stream)
87 {
88  fprintf(stream,
89  "Usage: %s [--pidfile PIDFILE] [--min-delay SECONDS] BOXPATH [boxoptions...]\n",
90  progname);
91 }
92 
93 /* Create the argument list to pass to the box process, and put it
94  * in the box_arglist global variable. This is also the right place
95  * to add any standard arguments that we want to pass. */
96 static void build_box_arglist(char *boxfile, int argc, char **argv)
97 {
98  int i;
99  char **argp;
100 
101  if (box_arglist) {
102  free(box_arglist);
103  }
104 
105  /* one for the boxfile name itself, one for each extra argument,
106  * one for each normal argument, and one for the terminating NULL */
107  box_arglist = malloc((1 + NUM_EXTRA + argc + 1) * sizeof(*box_arglist));
108  if (!box_arglist) {
109  fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
110  exit(1);
111  }
112 
113  /* Have argp walk down box_arglist and set each argument. */
114  argp = box_arglist;
115 
116  *argp++ = boxfile;
117  if (use_extra_args) {
118  for (i = 0; i < NUM_EXTRA; i++) {
119  *argp++ = extra_arguments[i];
120  }
121  }
122  for (i = 0; i < argc; i++) {
123  *argp++ = argv[i];
124  }
125  *argp++ = (char *)NULL;
126 }
127 
128 static void write_pidfile(void)
129 {
130  int fd;
131  FILE *f;
132 
133  if (!pidfile)
134  return;
135 
136  fd = open(pidfile, O_WRONLY|O_NOCTTY|O_TRUNC|O_CREAT, 0644);
137  if (fd < 0) {
138  fprintf(stderr, "%s: open: %s: %s\n", progname, pidfile, strerror(errno));
139  exit(1);
140  }
141 
142  f = fdopen(fd, "w");
143  if (!f) {
144  fprintf(stderr, "%s: fdopen: %s\n", progname, strerror(errno));
145  exit(1);
146  }
147 
148  fprintf(f, "%ld\n", (long)getpid());
149  if (fclose(f) < 0) {
150  fprintf(stderr, "%s: writing %s: %s\n", progname, pidfile, strerror(errno));
151  exit(1);
152  }
153 }
154 
155 static void remove_pidfile(void)
156 {
157  if (!pidfile)
158  return;
159 
160  unlink(pidfile);
161 }
162 
163 /* Set 0 (stdin) to /dev/null, and 1 and 2 (stdout and stderr) to /dev/full
164  * if it's available and /dev/null otherwise. */
165 static void rebind_standard_streams(void)
166 {
167  int devnullfd;
168  int devfullfd;
169 
170  devnullfd = open("/dev/null", O_RDONLY);
171  if (devnullfd < 0) {
172  fprintf(stderr, "%s: cannot open /dev/null: %s\n",
173  progname, strerror(errno));
174  exit(2);
175  }
176  devfullfd = open("/dev/full", O_WRONLY);
177  if (devfullfd < 0) {
178  devfullfd = devnullfd;
179  }
180 
181  /* Alert: The dup on stderr is done last, so that the error message
182  * works regardless of which dup fails. */
183  if (dup2(devnullfd, 0) < 0 ||
184  dup2(devfullfd, 1) < 0 ||
185  dup2(devfullfd, 2) < 0) {
186  fprintf(stderr, "%s: dup2: %s\n", progname, strerror(errno));
187  exit(1);
188  }
189 }
190 
191 /* Some code to determine the highest possible file descriptor number,
192  * so that we can close them all. */
193 static int open_max(void)
194 {
195 #ifdef OPEN_MAX
196  return OPEN_MAX;
197 #else
198  int max;
199 
200  max = sysconf(_SC_OPEN_MAX);
201  if (max <= 0) {
202  return 1024; /* guess */
203  }
204  return max;
205 #endif
206 }
207 
208 /* Close all file descriptors other than 0, 1, and 2. */
209 static void close_extra_files(void)
210 {
211  int max = open_max();
212  int fd;
213 
214  for (fd = 3; fd < max; fd++) {
215  close(fd);
216  }
217 }
218 
219 /* We received a signal that we should pass on to the child box.
220  * We ignore it ourselves. */
221 static void signal_transfer(int signum)
222 {
223  if (child_box > 0) {
224  kill(child_box, signum);
225  }
226 }
227 
228 /* We received a signal that we should pass on to the child box,
229  * and then die from ourselves. It has to be a signal that
230  * terminates the process as its default action! */
231 static void signal_transfer_and_die(int signum)
232 {
233  /* First send it to the child process */
234  if (child_box > 0) {
235  kill(child_box, signum);
236  }
237 
238  /* Prepare to die. Normally the atexit handler would take care
239  * of this when we exit(), but we're going to die from a signal. */
240  remove_pidfile();
241 
242  /* Then send it to self. First set the default handler, to
243  * avoid catching the signal with this handler again. This
244  * is not a race, because it doesn't matter if we die from
245  * the signal we're going to send or from a different one. */
246  signal(signum, SIG_DFL);
247  kill(getpid(), signum);
248 }
249 
250 static void setup_signals(void)
251 {
252  signal(SIGHUP, &signal_transfer);
253  signal(SIGINT, &signal_transfer_and_die);
254  signal(SIGQUIT, &signal_transfer_and_die);
255  signal(SIGTERM, &signal_transfer_and_die);
256  signal(SIGUSR1, &signal_transfer);
257  signal(SIGUSR2, &signal_transfer);
258 }
259 
260 /* Fork off a box process and loop indefinitely, forking a new one
261  * every time it dies. */
262 static int main_loop(char *boxfile)
263 {
264  time_t next_fork = 0;
265 
266  /* We can't report any errors here, because we are running
267  * as a daemon and we have no logfile of our own. So we
268  * exit with errno as the exit code, to offer a minimal clue. */
269 
270  for (;;) {
271 
272  /* Make sure we don't fork in an endless loop if something
273  * is drastically wrong. This code limits it to one
274  * per minute (or whatever min_restart_delay is set to). */
275  time_t this_time = time(NULL);
276  if (this_time <= next_fork) {
277  sleep(next_fork - this_time);
278  }
279  next_fork = this_time + min_restart_delay;
280 
281  child_box = fork();
282  if (child_box < 0) {
283  return errno;
284  }
285  if (child_box == 0) {
286  /* child. exec the box */
287  execvp(boxfile, box_arglist);
288  exit(127);
289  }
290 
291  while (waitpid(child_box, (int *)NULL, 0) != child_box) {
292  if (errno == ECHILD) {
293  /* Something went wrong... we don't know what,
294  * but we do know that our child does not
295  * exist. So restart it. */
296  break;
297  }
298  if (errno == EINTR) {
299  continue;
300  }
301  /* Something weird happened. */
302  return errno;
303  }
304  }
305 }
306 
307 int main(int argc, char *argv[])
308 {
309  int i;
310  char *boxfile = NULL;
311  pid_t childpid;
312 
313  progname = argv[0];
314 
315  if (argc == 1) {
316  print_usage(stderr);
317  exit(2);
318  }
319 
320  /* Parse the options meant for the wrapper, and get the name of
321  * the box to wrap. */
322  for (i = 1; i < argc && !boxfile; i++) {
323  if (strcmp(argv[i], "--pidfile") == 0) {
324  if (i+1 >= argc) {
325  fprintf(stderr, "Missing argument for option %s\n", argv[i]);
326  exit(2);
327  }
328  pidfile = argv[i+1];
329  i++;
330  } else if (strcmp(argv[i], "--min-delay") == 0) {
331  if (i+1 >= argc) {
332  fprintf(stderr, "Missing argument for option %s", argv[i]);
333  exit(2);
334  }
335  min_restart_delay = atoi(argv[i+1]);
336  i++;
337  } else if (strcmp(argv[i], "--no-extra-args") == 0) {
338  use_extra_args = 0;
339  } else if (argv[i][0] == '-') {
340  fprintf(stderr, "Unknown option %s\n", argv[i]);
341  exit(2);
342  } else {
343  boxfile = argv[i];
344  }
345  }
346 
347  /* Check if we have everything */
348  if (!boxfile) {
349  print_usage(stderr);
350  exit(2);
351  }
352 
353  /* The remaining arguments should be passed to the box */
354  build_box_arglist(boxfile, argc - i, argv + i);
355 
356  /* Ready to rock. Begin daemonization. */
357 
358  /* Fork a child process and have the parent exit.
359  * This makes us run in the background. */
360  childpid = fork();
361  if (childpid < 0) {
362  fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
363  exit(1);
364  }
365  if (childpid != 0) {
366  exit(0); /* parent exits immediately */
367  }
368 
369  /* The child continues here. Now call setsid() to disconnect
370  * from our terminal and from the parent's session and process
371  * group. */
372  if (setsid() < 0) {
373  fprintf(stderr, "%s: setsid: %s\n", progname, strerror(errno));
374  exit(1);
375  }
376 
377  /* Change to the root directory, so that we don't keep a
378  * file descriptor open on an unknown directory. */
379  if (chdir("/") < 0) {
380  fprintf(stderr, "%s: chdir to root: %s\n", progname, strerror(errno));
381  exit(1);
382  }
383 
384  atexit(remove_pidfile);
385  write_pidfile();
386 
387  /* Set the umask to a known value, rather than inheriting
388  * an unknown one. */
389  umask(077);
390 
391  /* Leave file descriptors 0, 1, and 2 pointing to harmless
392  * places, and close all other file descriptors. */
395 
396  setup_signals();
397  return main_loop(boxfile);
398 }
static void rebind_standard_streams(void)
static void remove_pidfile(void)
static char * pidfile
int main(int argc, char *argv[])
void * malloc(YYSIZE_T)
#define NUM_EXTRA
static void setup_signals(void)
static int main_loop(char *boxfile)
static char * extra_arguments[]
static pid_t child_box
static char ** box_arglist
static char * progname
void free(void *)
static void signal_transfer(int signum)
static void signal_transfer_and_die(int signum)
static void close_extra_files(void)
static int use_extra_args
static void print_usage(FILE *stream)
static void write_pidfile(void)
static void build_box_arglist(char *boxfile, int argc, char **argv)
static int open_max(void)
static int min_restart_delay
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.