Hi List,
attached you can find a patch that adds some usefull command line parameters
for all boxes.
1) -P/--parachute : start watcher father process that catch and restart
crashed child process
2) -X/--panic-script : execute this script if child process crashed
3) -u/--user : change process user-id (security)
4) -p/--pid-file : write PID into the given file
5) -d/--daemonize : daemonize the process
Comments and votes please!
--
Best regards / Mit besten Gr��en aus D�sseldorf
Dipl.-Ing.
Alexander Malysh
___________________________________________
Centrium GmbH
Vogelsanger Weg 80
40470 D�sseldorf
Fon: +49 (0211) 74 84 51 80
Fax: +49 (0211) 277 49 109
email: [EMAIL PROTECTED]
web: www.centrium.de
msn: [EMAIL PROTECTED]
icq: 98063111
___________________________________________
Please avoid sending me Word, Excel or PowerPoint attachments.
See http://www.fsf.org/philosophy/no-word-attachments.html
Index: gw/bearerbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/bearerbox.c,v
retrieving revision 1.150
diff -a -u -r1.150 bearerbox.c
--- gw/bearerbox.c 24 Nov 2003 20:08:24 -0000 1.150
+++ gw/bearerbox.c 15 Jan 2004 13:49:41 -0000
@@ -335,10 +335,6 @@
bb_status = BB_SUSPENDED;
else if (strcmp(argv[i], "-I")==0 || strcmp(argv[i], "--isolated")==0)
bb_status = BB_ISOLATED;
- else if (strcmp(argv[i], "-g")==0 || strcmp(argv[i], "--generate")==0) {
- cfg_dump_all();
- exit(0);
- }
else
return -1;
Index: gw/smsbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsbox.c,v
retrieving revision 1.240
diff -a -u -r1.240 smsbox.c
--- gw/smsbox.c 8 Dec 2003 11:25:44 -0000 1.240
+++ gw/smsbox.c 15 Jan 2004 13:49:42 -0000
@@ -111,7 +111,6 @@
static Octstr *sendota_url = NULL;
static Octstr *xmlrpc_url = NULL;
static Octstr *bb_host;
-static char *pid_file;
static Octstr *accepted_chars = NULL;
static int only_try_http = 0;
static URLTranslationList *translations = NULL;
@@ -3003,17 +3002,6 @@
* Main program. Configuration, signal handling, etc.
*/
-static void write_pid_file(void) {
- FILE *f;
-
- if (pid_file != NULL) {
- f = fopen(pid_file, "w");
- fprintf(f, "%d\n", (int)getpid());
- fclose(f);
- }
-}
-
-
static void signal_handler(int signum) {
/* On some implementations (i.e. linuxthreads), signals are delivered
* to all threads. We only want to handle each signal once for the
@@ -3299,7 +3287,6 @@
debug("sms", 0, "----------------------------------------------");
debug("sms", 0, GW_NAME " smsbox version %s starting", GW_VERSION);
- write_pid_file();
translations = urltrans_create();
if (translations == NULL)
Index: gwlib/utils.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/utils.c,v
retrieving revision 1.41
diff -a -u -r1.41 utils.c
--- gwlib/utils.c 17 Dec 2003 16:34:26 -0000 1.41
+++ gwlib/utils.c 15 Jan 2004 13:49:42 -0000
@@ -68,9 +68,307 @@
#include <time.h>
#include <unistd.h>
#include <termios.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
#include "gwlib.h"
+/* pid of child process when parachute is used */
+static pid_t child_pid = -1;
+/* saved child signal handlers */
+static struct sigaction child_actions[32];
+/* just a flag that child signal handlers are stored */
+static int child_actions_init = 0;
+/* our pid file name */
+static char *pid_file = NULL;
+static volatile sig_atomic_t parachute_shutdown = 0;
+
+
+static void parachute_sig_handler(int signum)
+{
+ info(0, "Signal %d received, forward to child pid (%ld)", signum, (long) child_pid);
+
+ /* we do not handle any signal, just forward these to child process */
+ if (child_pid != -1 && getpid() != child_pid)
+ kill(child_pid, signum);
+
+ /* if signal received and no child there, terminating */
+ switch(signum) {
+ case SIGTERM:
+ case SIGINT:
+ case SIGABRT:
+ if (child_pid == -1)
+ exit(0);
+ else
+ parachute_shutdown = 1;
+ }
+}
+
+static void parachute_init_signals(int child)
+{
+ struct sigaction sa;
+
+ if (child_actions_init && child) {
+ sigaction(SIGTERM, &child_actions[SIGTERM], NULL);
+ sigaction(SIGQUIT, &child_actions[SIGQUIT], NULL);
+ sigaction(SIGINT, &child_actions[SIGINT], NULL);
+ sigaction(SIGABRT, &child_actions[SIGABRT], NULL);
+ sigaction(SIGHUP, &child_actions[SIGHUP], NULL);
+ sigaction(SIGALRM, &child_actions[SIGALRM], NULL);
+ sigaction(SIGUSR1, &child_actions[SIGUSR1], NULL);
+ sigaction(SIGUSR2, &child_actions[SIGUSR2], NULL);
+ sigaction(SIGPIPE, &child_actions[SIGPIPE], NULL);
+ }
+ else if (!child && !child_actions_init) {
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = parachute_sig_handler;
+ sigaction(SIGTERM, &sa, &child_actions[SIGTERM]);
+ sigaction(SIGQUIT, &sa, &child_actions[SIGQUIT]);
+ sigaction(SIGINT, &sa, &child_actions[SIGINT]);
+ sigaction(SIGABRT, &sa, &child_actions[SIGABRT]);
+ sigaction(SIGHUP, &sa, &child_actions[SIGHUP]);
+ sigaction(SIGALRM, &sa, &child_actions[SIGALRM]);
+ sigaction(SIGUSR1, &sa, &child_actions[SIGUSR1]);
+ sigaction(SIGUSR2, &sa, &child_actions[SIGUSR2]);
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, &child_actions[SIGPIPE]);
+ sigaction(SIGTTOU, &sa, NULL);
+ sigaction(SIGTTIN, &sa, NULL);
+ sigaction(SIGTSTP, &sa, NULL);
+ child_actions_init = 1;
+ }
+ else
+ panic(0, "Child process signal handlers not initialized before.");
+}
+
+static int is_executable(const char *filename)
+{
+ struct stat buf;
+
+ if (stat(filename, &buf)) {
+ error(errno, "Error while stat of file `%s'", filename);
+ return 0;
+ }
+ if (!S_ISREG(buf.st_mode) && !S_ISLNK(buf.st_mode)) {
+ error(0, "File `%s' is not a regular file.", filename);
+ return 0;
+ }
+ /* others has exec permission */
+ if (S_IXOTH & buf.st_mode) return 1;
+ /* group has exec permission */
+ if ((S_IXGRP & buf.st_mode) && buf.st_gid == getgid())
+ return 1;
+ /* owner has exec permission */
+ if ((S_IXUSR & buf.st_mode) && buf.st_uid == getuid())
+ return 1;
+
+ return 0;
+}
+
+/*
+ * become daemon.
+ * returns 0 for father process; 1 for child process
+ */
+static int become_daemon(void)
+{
+ if (getppid() != 1) {
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ if (fork())
+ return 0;
+ setsid();
+ }
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ /* XXX chdir breaks restart of boxes when
+ started w/o a full path to binary */
+ /* chdir("/"); */
+ return 1;
+}
+
+#define PANIC_SCRIPT_MAX_LEN 4096
+
+static void execute_panic_script(const char *panic_script, const char *format, ...)
+{
+ char *args[3];
+ char buf[PANIC_SCRIPT_MAX_LEN + 1];
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(buf, PANIC_SCRIPT_MAX_LEN, format, ap);
+ va_end(ap);
+
+ if (fork())
+ return;
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ args[0] = (char*) panic_script;
+ args[1] = buf;
+ args[2] = NULL;
+
+ execv(args[0], args);
+}
+
+
+static void parachute_start(const char *myname, const char *panic_script) {
+ time_t last_start = 0, last_panic = 0;
+ long respawn_count = 0;
+ int status;
+
+
+ if (panic_script && !is_executable(panic_script))
+ panic(0, "Panic script `%s' is not executable for us.", panic_script);
+
+ /* setup sighandler */
+ parachute_init_signals(0);
+
+ for (;;) {
+ if (respawn_count > 0 && difftime(time(NULL), last_start) < 10) {
+ error(0, "Child process died too fast, disabling for 30 sec.");
+ gwthread_sleep(30.0);
+ }
+ if (!(child_pid = fork())) { /* child process */
+ child_pid = getpid();
+ parachute_init_signals(1); /* reset sighandlers */
+ return;
+ }
+ else if (child_pid < 0) {
+ error(errno, "Couldnot start child process ! Will retry in 5 sec.");
+ gwthread_sleep(5.0);
+ continue;
+ }
+ else { /* father process */
+ time(&last_start);
+ info(0, "Child process with PID (%ld) started.", (long) child_pid);
+ do {
+ if (waitpid(child_pid, &status, 0) == child_pid) {
+ /* check here why child terminated */
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ info(0, "Child process exited gracefully, exit...");
+ gwlib_shutdown();
+ exit(0);
+ }
+ else if (WIFEXITED(status)) {
+ error(0, "Caught child %d which died with return code %d",
+ child_pid, WEXITSTATUS(status));
+ child_pid = -1;
+ }
+ else if (WIFSIGNALED(status)) {
+ error(0, "Caught child %d which died due to signal %d",
+ child_pid, WTERMSIG(status));
+ child_pid = -1;
+ }
+ }
+ else if (errno != EINTR) {
+ error(errno, "Error while waiting of child process.");
+ }
+ } while(child_pid > 0);
+
+ if (parachute_shutdown) {
+ /* may only happens if child process crashed while shutdown */
+ info(0, "Child process crashed while shutdown. Exiting due to signal...");
+ gwlib_shutdown();
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 0);
+ }
+
+ respawn_count++;
+ if (panic_script && myname && difftime(time(NULL), last_panic) > 300) {
+ time(&last_panic);
+ debug("kannel", 0, "Executing panic script: %s %s %ld", panic_script, myname, respawn_count);
+ execute_panic_script(panic_script, "%s %ld", myname, respawn_count);
+ }
+ /* sleep a while to get e.g. sockets released */
+ gwthread_sleep(5.0);
+ }
+ }
+}
+
+
+static void write_pid_file(void)
+{
+ int fd;
+ FILE *file;
+
+ if (!pid_file)
+ return;
+
+ fd = open(pid_file, O_WRONLY|O_NOCTTY|O_TRUNC|O_CREAT|O_EXCL, 0644);
+ if (fd == -1)
+ panic(errno, "Couldnot open pid-file `%s'", pid_file);
+
+ file = fdopen(fd, "w");
+ if (!file)
+ panic(errno, "Couldnot open file-stream `%s'", pid_file);
+
+ fprintf(file, "%ld\n", (long) getpid());
+ fclose(file);
+}
+
+static void remove_pid_file(void)
+{
+ if (!pid_file)
+ return;
+
+ /* ensure we don't called from child process */
+ if (child_pid != -1 && child_pid == getpid())
+ return;
+
+ if (-1 == unlink(pid_file))
+ error(errno, "Couldnot unlink pid-file `%s'", pid_file);
+}
+
+static int change_user(const char *user)
+{
+ struct passwd *pass;
+
+ if (!user)
+ return -1;
+
+ pass = getpwnam(user);
+ if (!pass) {
+ error(0, "Couldnot find a user `%s' in system.", user);
+ return -1;
+ }
+ gw_claim_area(pass);
+ gw_claim_area(pass->pw_name);
+ gw_claim_area(pass->pw_passwd);
+ gw_claim_area(pass->pw_gecos);
+ gw_claim_area(pass->pw_dir);
+ gw_claim_area(pass->pw_shell);
+
+ if (-1 == setgid(pass->pw_gid)) {
+ error(errno, "Couldnot change group id %ld -> %ld.", (long) getgid(), (long) pass->pw_gid);
+ goto out;
+ }
+
+ if (-1 == setuid(pass->pw_uid)) {
+ error(errno, "Couldnot change user id %ld -> %ld.", (long) getuid(), (long) pass->pw_uid);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ gw_free(pass->pw_name);
+ gw_free(pass->pw_passwd);
+ gw_free(pass->pw_gecos);
+ gw_free(pass->pw_dir);
+ gw_free(pass->pw_shell);
+ gw_free(pass);
+ return -1;
+}
/*
* new datatype functions
@@ -136,6 +434,8 @@
int file_lvl = GW_DEBUG;
char *log_file = NULL;
char *debug_places = NULL;
+ char *panic_script = NULL, *user = NULL;
+ int parachute = 0, daemonize = 0;
for(i=1; i < argc; i++) {
if (strcmp(argv[i],"-v")==0 ||
@@ -145,28 +445,60 @@
debug_lvl = atoi(argv[i+1]);
i++;
} else
- fprintf(stderr, "Missing argument for option %s\n", argv[i]);
+ panic(0, "Missing argument for option %s\n", argv[i]);
} else if (strcmp(argv[i],"-F")==0 ||
strcmp(argv[i],"--logfile")==0) {
if (i+1 < argc && *(argv[i+1]) != '-') {
log_file = argv[i+1];
i++;
} else
- fprintf(stderr, "Missing argument for option %s\n", argv[i]);
+ panic(0, "Missing argument for option %s\n", argv[i]);
} else if (strcmp(argv[i],"-V")==0 ||
strcmp(argv[i],"--fileverbosity")==0) {
if (i+1 < argc) {
file_lvl = atoi(argv[i+1]);
i++;
} else
- fprintf(stderr, "Missing argument for option %s\n", argv[i]);
+ panic(0, "Missing argument for option %s\n", argv[i]);
} else if (strcmp(argv[i],"-D")==0 ||
strcmp(argv[i],"--debug")==0) {
if (i+1 < argc) {
debug_places = argv[i+1];
i++;
} else
- fprintf(stderr, "Missing argument for option %s\n", argv[i]);
+ panic(0, "Missing argument for option %s\n", argv[i]);
+ } else if (strcmp(argv[i], "-X")==0 ||
+ strcmp(argv[i], "--panic-script")==0) {
+ if (i+1 < argc) {
+ panic_script = argv[i+1];
+ i++;
+ }
+ else
+ panic(0, "Missing argument for option %s\n", argv[i]);
+ } else if (strcmp(argv[i], "-P")==0 ||
+ strcmp(argv[i], "--parachute")==0) {
+ parachute = 1;
+ } else if (strcmp(argv[i], "-d")==0 ||
+ strcmp(argv[i], "--daemonize")==0) {
+ daemonize = 1;
+ } else if (strcmp(argv[i], "-p")==0 ||
+ strcmp(argv[i], "--pid-file")==0) {
+ if (i+1 < argc) {
+ pid_file = argv[i+1];
+ i++;
+ } else
+ panic(0, "Missing argument for option %s\n", argv[i]);
+ } else if (strcmp(argv[i], "-u")==0 ||
+ strcmp(argv[i], "--user")==0) {
+ if (i+1 < argc) {
+ user = argv[i+1];
+ i++;
+ } else
+ panic(0, "Missing argument for option %s\n", argv[i]);
+ } else if (strcmp(argv[i], "-g")==0 ||
+ strcmp(argv[i], "--generate")==0) {
+ cfg_dump_all();
+ exit(0);
} else if (strcmp(argv[i],"--")==0) {
i++;
break;
@@ -178,12 +510,45 @@
}
if (ret < 0) {
fprintf(stderr, "Unknown option %s, exiting.\n", argv[i]);
- panic(0, "Option parsing failed");
+ panic(0, "Option parsing failed");
}
else
i += ret; /* advance additional args */
}
}
+
+ if (user && -1 == change_user(user)) {
+ panic(0, "Couldnot change to user `%s'.", user);
+ }
+
+ /* deamonize */
+ if (daemonize && !become_daemon())
+ exit(0);
+
+ if (pid_file) {
+ write_pid_file();
+ atexit(remove_pid_file);
+ }
+
+ if (parachute) {
+ /*
+ * if we are running as daemon so open syslog
+ * in order not to deal with i.e. log rotate.
+ */
+ if (daemonize) {
+ char *ident = strrchr(argv[0], '/');
+ if (!ident)
+ ident = argv[0];
+ else
+ ident++;
+ log_set_syslog(ident, (debug_lvl > -1 ? debug_lvl : 0));
+ }
+ parachute_start(argv[0], panic_script);
+ /* now we are in child process so close syslog */
+ if (daemonize)
+ log_close_all();
+ }
+
if (debug_lvl > -1)
log_set_output_level(debug_lvl);
if (debug_places != NULL)
Index: doc/userguide/userguide.xml
===================================================================
RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v
retrieving revision 1.261
diff -a -u -r1.261 userguide.xml
--- doc/userguide/userguide.xml 14 Jan 2004 13:55:32 -0000 1.261
+++ doc/userguide/userguide.xml 15 Jan 2004 13:49:42 -0000
@@ -1600,9 +1600,43 @@
<row><entry><literal>-g</literal></entry>
<entry morerows="1" valign="bottom">
Dump all known config groups and config keys to stdout
- and exit. (bearerbox only)
+ and exit.
</entry></row>
<row><entry><literal>--generate</literal></entry></row>
+
+ <row><entry><literal>-u <username></literal></entry>
+ <entry morerows="1" valign="bottom">
+ Change process user-id to the given.
+ </entry></row>
+ <row><entry><literal>--user <username></literal></entry></row>
+
+ <row><entry><literal>-p <filename></literal></entry>
+ <entry morerows="1" valign="bottom">
+ Write process PID to the given file.
+ </entry></row>
+ <row><entry><literal>--pid-file <filename></literal></entry></row>
+
+ <row><entry><literal>-d</literal></entry>
+ <entry morerows="1" valign="bottom">
+ Start process in as daemon (detached from a current shell session).
+ </entry></row>
+ <row><entry><literal>--daemonize</literal></entry></row>
+
+ <row><entry><literal>-P</literal></entry>
+ <entry morerows="1" valign="bottom">
+ Start watcher process. This process watch a child process and if child process
+ crached will restart them automaticaly.
+ </entry></row>
+ <row><entry><literal>--parachute</literal></entry></row>
+
+ <row><entry><literal>-X <scriptname></literal></entry>
+ <entry morerows="1" valign="bottom">
+ Execute a given shell script or binary when child process crash detected. This option
+ is usable only with <literal>--parachute/-P</literal>.
+ Script will be executed with 2 arguments:
+ scriptname 'processname' 'respawn-count'.
+ </entry></row>
+ <row><entry><literal>--panic-script <scriptname></literal></entry></row>
</tbody>
</tgroup>