Process management/respawn. Bug #1173. PID file pazpar2.pid generated
authorAdam Dickmeiss <adam@indexdata.dk>
Fri, 8 Jun 2007 13:57:19 +0000 (13:57 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Fri, 8 Jun 2007 13:57:19 +0000 (13:57 +0000)
by default.

src/Makefile.am
src/pazpar2.c
src/pazpar2.h
src/process.c [new file with mode: 0644]

index 28f5788..7a70b39 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile.am,v 1.25 2007-05-23 11:19:31 marc Exp $
+# $Id: Makefile.am,v 1.26 2007-06-08 13:57:19 adam Exp $
 
 bin_PROGRAMS = pazpar2 icu_chain_test
 
@@ -29,7 +29,8 @@ libpazpar2_a_SOURCES = config.c config.h eventl.c eventl.h \
        util.c util.h zeerex.c zeerex.h database.c database.h \
        settings.h settings.c sel_thread.c sel_thread.h getaddrinfo.c \
        charsets.c charsets.h \
-       client.c client.h connection.c connection.h host.h parameters.h
+       client.c client.h connection.c connection.h host.h parameters.h \
+       process.c
 
 pazpar2_SOURCES = pazpar2.c
 pazpar2_LDADD = libpazpar2.a $(YAZLIB) $(ICU_LIBS)
index e19a98a..6efd068 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: pazpar2.c,v 1.86 2007-06-06 11:49:48 marc Exp $
+/* $Id: pazpar2.c,v 1.87 2007-06-08 13:57:19 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -31,10 +31,32 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "database.h"
 #include "settings.h"
 
+void child_handler(void *data)
+{
+    yaz_log(YLOG_LOG, "child_handler");
+
+    start_proxy();
+    //start_zproxy();
+    init_settings();
+
+    if (*global_parameters.settings_path_override)
+        settings_read(global_parameters.settings_path_override);
+    else if (global_parameters.server->settings)
+        settings_read(global_parameters.server->settings);
+    else
+        yaz_log(YLOG_WARN, "No settings-directory specified");
+    global_parameters.odr_in = odr_createmem(ODR_DECODE);
+    global_parameters.odr_out = odr_createmem(ODR_ENCODE);
+
+
+    pazpar2_event_loop();
+}
+
 int main(int argc, char **argv)
 {
     int ret;
     char *arg;
+    const char *pidfile = "pazpar2.pid";
 
     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
         yaz_log(YLOG_WARN|YLOG_ERRNO, "signal");
@@ -94,21 +116,8 @@ int main(int argc, char **argv)
     global_parameters.server = config->servers;
 
     start_http_listener();
-    start_proxy();
-    //start_zproxy();
-    init_settings();
-
-    if (*global_parameters.settings_path_override)
-        settings_read(global_parameters.settings_path_override);
-    else if (global_parameters.server->settings)
-        settings_read(global_parameters.server->settings);
-    else
-        yaz_log(YLOG_WARN, "No settings-directory specified");
-    global_parameters.odr_in = odr_createmem(ODR_DECODE);
-    global_parameters.odr_out = odr_createmem(ODR_ENCODE);
-
-    pazpar2_event_loop();
-
+    pazpar2_process(global_parameters.debug_mode, 0, child_handler, 0,
+                    pidfile, 0);
     return 0;
 }
 
index c74b5fe..ff005ee 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: pazpar2.h,v 1.37 2007-06-06 11:49:48 marc Exp $
+/* $Id: pazpar2.h,v 1.38 2007-06-08 13:57:19 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -175,6 +175,10 @@ struct record *ingest_record(struct client *cl, Z_External *rec,
 void session_alert_watch(struct session *s, int what);
 void pull_terms(NMEM nmem, struct ccl_rpn_node *n, char **termlist, int *num);
 
+int pazpar2_process(int debug, int flags,
+                    void (*work)(void *data), void *data,
+                    const char *pidfile, const char *uid);
+
 #endif
 
 /*
diff --git a/src/process.c b/src/process.c
new file mode 100644 (file)
index 0000000..9a97da7
--- /dev/null
@@ -0,0 +1,173 @@
+/* $Id: process.c,v 1.1 2007-06-08 13:57:19 adam Exp $
+   Copyright (c) 2006-2007, Index Data.
+
+This file is part of Pazpar2.
+
+Pazpar2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Pazpar2; see the file LICENSE.  If not, write to the
+Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "cconfig.h"
+#endif
+
+#include <signal.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <yaz/log.h>
+
+#include "pazpar2.h"
+
+static void write_pidfile(const char *pidfile)
+{
+    if (pidfile)
+    {
+        FILE *f = fopen(pidfile, "w");
+        if (!f)
+        {
+            yaz_log(YLOG_ERRNO|YLOG_FATAL, "Couldn't create %s", pidfile);
+            exit(0);
+        }
+        fprintf(f, "%ld", (long) getpid());
+        fclose(f);
+    }
+}
+
+pid_t child_pid = 0;
+void kill_child_handler(int num)
+{
+    if (child_pid)
+        kill(child_pid, num);
+}
+
+int pazpar2_process(int debug, int flags,
+                    void (*work)(void *data), void *data,
+                    const char *pidfile, const char *uid /* not yet used */)
+{
+    int run = 1;
+    int cont = 1;
+    void (*old_sighup)(int);
+    void (*old_sigterm)(int);
+
+
+    if (debug)
+    {
+        /* in debug mode.. it's quite simple */
+        write_pidfile(pidfile);
+        work(data);
+        exit(0);
+    }
+    /* running in production mode. */
+
+    /* keep signals in their original state and make sure that some signals
+       to parent process also gets sent to the child.. Normally this
+       should not happen. We want the _child_ process to be terminated
+       normally. However, if the parent process is terminated, we
+       kill the child too */
+    old_sighup = signal(SIGHUP, kill_child_handler);
+    old_sigterm = signal(SIGTERM, kill_child_handler);
+    while (cont)
+    {
+        pid_t p = fork();
+        pid_t p1;
+        int status;
+        if (p == (pid_t) (-1))
+        {
+            
+            yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
+            exit(1);
+        }
+        else if (p == 0)
+        {
+            /* child */
+            signal(SIGHUP, old_sighup);  /* restore */
+            signal(SIGTERM, old_sigterm);/* restore */
+
+            write_pidfile(pidfile);
+            work(data);
+            exit(0);
+        }
+
+        /* enable signalling in kill_child_handler */
+        child_pid = p;
+        
+        p1 = wait(&status);
+        yaz_log_reopen();
+
+        /* disable signalling in kill_child_handler */
+        child_pid = 0;
+
+        if (p1 != p)
+        {
+            yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
+            exit(1);
+        }
+        
+        if (WIFSIGNALED(status))
+        {
+            /*  keep the child alive in case of errors, but _log_ */
+            switch(WTERMSIG(status)) {
+            case SIGILL:
+                yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
+                cont = 1;
+                break;
+            case SIGABRT:
+                yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
+                cont = 1;
+                break ;
+            case SIGSEGV:
+                yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
+                cont = 1;
+                break;
+            case SIGBUS:        
+                yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
+                cont = 1;
+                break;
+            case SIGTERM:
+                yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
+                        (long) p);
+                cont = 0;
+                break;
+            default:
+                yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
+                        WTERMSIG(status), (long) p);
+                cont = 0;
+            }
+        }
+        else if (status == 0)
+            cont = 0; /* child exited normally */
+        else
+        {   /* child exited in an abnormal way */
+            yaz_log(YLOG_LOG, "Exit %d from child %ld", status, (long) p);
+            cont = 1;
+        }
+        if (cont) /* respawn slower as we get more errors */
+            sleep(1 + run/5);
+        run++;
+    }
+    return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */