Imported Upstream version 2.2.1+dfsg
[shibboleth/sp.git] / shibd / shibd.cpp
index b7a6956..4c86dae 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- *  Copyright 2001-2007 Internet2\r
+ *  Copyright 2001-2009 Internet2\r
  *\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
@@ -61,7 +61,7 @@ const char* shar_schemadir = NULL;
 const char* shar_prefix = NULL;\r
 bool shar_checkonly = false;\r
 bool shar_version = false;\r
-static int unlink_socket = 0;\r
+static bool unlink_socket = false;\r
 const char* pidfile = NULL;\r
 \r
 #ifdef WIN32\r
@@ -141,14 +141,21 @@ int real_main(int preinit)
 \r
         //_CrtSetAllocHook(MyAllocHook);\r
 \r
-        // Run the listener\r
         if (!shar_checkonly) {\r
-\r
             // Run the listener.\r
-            if (!conf.getServiceProvider()->getListenerService()->run(&shibd_shutdown)) {\r
-                fprintf(stderr, "listener failed to enter listen loop\n");\r
+            ListenerService* listener = conf.getServiceProvider()->getListenerService();\r
+            if (!listener->init(unlink_socket)) {\r
+                fprintf(stderr, "listener failed to initialize\n");\r
+                conf.term();\r
+                return -3;\r
+            }\r
+            else if (!listener->run(&shibd_shutdown)) {\r
+                fprintf(stderr, "listener failed during service\n");\r
+                listener->term();\r
+                conf.term();\r
                 return -3;\r
             }\r
+            listener->term();\r
         }\r
 \r
         conf.term();\r
@@ -158,11 +165,25 @@ int real_main(int preinit)
 \r
 #else\r
 \r
+int daemon_wait = 3;\r
+bool shibd_running = false;\r
+bool daemonize = true;\r
+\r
 static void term_handler(int arg)\r
 {\r
     shibd_shutdown = true;\r
 }\r
 \r
+static void run_handler(int arg)\r
+{\r
+    shibd_running = true;\r
+}\r
+\r
+static void child_handler(int arg)\r
+{\r
+    // Terminate the parent's wait/sleep if the newly born daemon dies early.\r
+}\r
+\r
 static int setup_signals(void)\r
 {\r
     struct sigaction sa;\r
@@ -190,6 +211,23 @@ static int setup_signals(void)
     if (sigaction(SIGTERM, &sa, NULL) < 0) {\r
         return -1;\r
     }\r
+\r
+    if (daemonize) {\r
+        memset(&sa, 0, sizeof (sa));\r
+        sa.sa_handler = run_handler;\r
+\r
+        if (sigaction(SIGUSR1, &sa, NULL) < 0) {\r
+            return -1;\r
+        }\r
+\r
+        memset(&sa, 0, sizeof (sa));\r
+        sa.sa_handler = child_handler;\r
+\r
+        if (sigaction(SIGCHLD, &sa, NULL) < 0) {\r
+            return -1;\r
+        }\r
+    }\r
+\r
     return 0;\r
 }\r
 \r
@@ -199,9 +237,11 @@ static void usage(char* whoami)
     fprintf(stderr, "  -d\tinstallation prefix to use.\n");\r
     fprintf(stderr, "  -c\tconfig file to use.\n");\r
     fprintf(stderr, "  -x\tXML schema catalogs to use.\n");\r
-    fprintf(stderr, "  -t\tcheck configuration file for problems.\n");\r
+    fprintf(stderr, "  -t\ttest configuration file for problems.\n");\r
     fprintf(stderr, "  -f\tforce removal of listener socket.\n");\r
+    fprintf(stderr, "  -F\tstay in the foreground.\n");\r
     fprintf(stderr, "  -p\tpid file to use.\n");\r
+    fprintf(stderr, "  -w\tseconds to wait for successful daemonization.\n");\r
     fprintf(stderr, "  -v\tprint software version.\n");\r
     fprintf(stderr, "  -h\tprint this help message.\n");\r
     exit(1);\r
@@ -211,7 +251,7 @@ static int parse_args(int argc, char* argv[])
 {\r
     int opt;\r
 \r
-    while ((opt = getopt(argc, argv, "d:c:x:p:ftvh")) > 0) {\r
+    while ((opt = getopt(argc, argv, "d:c:x:p:w:fFtvh")) > 0) {\r
         switch (opt) {\r
             case 'd':\r
                 shar_prefix=optarg;\r
@@ -223,10 +263,14 @@ static int parse_args(int argc, char* argv[])
                 shar_schemadir=optarg;\r
                 break;\r
             case 'f':\r
-                unlink_socket = 1;\r
+                unlink_socket = true;\r
+                break;\r
+            case 'F':\r
+                daemonize = false;\r
                 break;\r
             case 't':\r
                 shar_checkonly=true;\r
+                daemonize=false;\r
                 break;\r
             case 'v':\r
                 shar_version=true;\r
@@ -234,6 +278,12 @@ static int parse_args(int argc, char* argv[])
             case 'p':\r
                 pidfile=optarg;\r
                 break;\r
+            case 'w':\r
+                if (optarg)\r
+                    daemon_wait = atoi(optarg);\r
+                if (daemon_wait <= 0)\r
+                    daemon_wait = 3;\r
+                break;\r
             default:\r
                 return -1;\r
         }\r
@@ -271,6 +321,21 @@ int main(int argc, char *argv[])
         return -1;\r
     }\r
 \r
+    if (daemonize) {\r
+        // We must fork() early, while we're single threaded.\r
+        // StorageService cleanup thread is about to start.\r
+        switch (fork()) {\r
+            case 0:\r
+                break;\r
+            case -1:\r
+                perror("forking");\r
+                exit(EXIT_FAILURE);\r
+            default:\r
+                sleep(daemon_wait);\r
+                exit(shibd_running ? EXIT_SUCCESS : EXIT_FAILURE);\r
+        }\r
+    }\r
+\r
     if (!conf.instantiate(shar_config)) {\r
         fprintf(stderr, "configuration is invalid, check console for specific problems\n");\r
         conf.term();\r
@@ -280,23 +345,53 @@ int main(int argc, char *argv[])
     if (shar_checkonly)\r
         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");\r
     else {\r
+        // Init the listener.\r
+        ListenerService* listener = conf.getServiceProvider()->getListenerService();\r
+        if (!listener->init(unlink_socket)) {\r
+            fprintf(stderr, "listener failed to initialize\n");\r
+            conf.term();\r
+            return -3;\r
+        }\r
 \r
-        // Write the pid file\r
-        if (pidfile) {\r
-            FILE* pidf = fopen(pidfile, "w");\r
-            if (pidf) {\r
-                fprintf(pidf, "%d\n", getpid());\r
-                fclose(pidf);\r
-            } else {\r
-                perror(pidfile);  // keep running though\r
+        if (daemonize) {\r
+            if (setsid() == -1) {\r
+                perror("setsid");\r
+                exit(EXIT_FAILURE);\r
             }\r
+            if (chdir("/") == -1) {\r
+                perror("chdir to root");\r
+                exit(EXIT_FAILURE);\r
+            }\r
+\r
+            if (pidfile) {\r
+                FILE* pidf = fopen(pidfile, "w");\r
+                if (pidf) {\r
+                    fprintf(pidf, "%d\n", getpid());\r
+                    fclose(pidf);\r
+                }\r
+                else {\r
+                    perror(pidfile);\r
+                }\r
+            }\r
+\r
+            freopen("/dev/null", "r", stdin);\r
+            freopen("/dev/null", "w", stdout);\r
+            freopen("/dev/null", "w", stderr);\r
+\r
+            // Signal our parent that we are A-OK.\r
+            kill(getppid(), SIGUSR1);\r
         }\r
 \r
-        // Run the listener\r
-        if (!conf.getServiceProvider()->getListenerService()->run(&shibd_shutdown)) {\r
-            fprintf(stderr, "listener failed to enter listen loop\n");\r
+        // Run the listener.\r
+        if (!listener->run(&shibd_shutdown)) {\r
+            fprintf(stderr, "listener failure during service\n");\r
+            listener->term();\r
+            conf.term();\r
+            if (pidfile)\r
+                unlink(pidfile);\r
             return -3;\r
         }\r
+        listener->term();\r
     }\r
 \r
     conf.term();\r