SSPCPP-412 - systemd integration changes
[shibboleth/cpp-sp.git] / shibd / shibd.cpp
index c5f5ba6..e8afdea 100644 (file)
-/*\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
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/*\r
- * shar.cpp -- the shibd "main" code.  All the functionality is elsewhere\r
- *\r
- * Created By: Derek Atkins <derek@ihtfp.com>\r
- *\r
- * $Id: shar.cpp 2164 2007-02-11 05:26:18 +0000 (Sun, 11 Feb 2007) cantor $\r
- */\r
-\r
-\r
-// eventually we might be able to support autoconf via cygwin...\r
-#if defined (_MSC_VER) || defined(__BORLANDC__)\r
-# include "config_win32.h"\r
-#else\r
-# include "config.h"\r
-#endif\r
-\r
-#ifdef WIN32\r
-# define _CRT_NONSTDC_NO_DEPRECATE 1\r
-# define _CRT_SECURE_NO_DEPRECATE 1\r
-#endif\r
-\r
-#include <shibsp/SPConfig.h>\r
-\r
-#ifdef HAVE_UNISTD_H\r
-#include <unistd.h>\r
-#include <sys/select.h>\r
-#endif\r
-\r
-#include <stdio.h>\r
-#include <signal.h>\r
-#include <shibsp/ServiceProvider.h>\r
-#include <shibsp/remoting/ListenerService.h>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/util/XMLConstants.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-\r
-using namespace shibsp;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-bool shibd_shutdown = false;\r
-const char* shar_config = NULL;\r
-const char* shar_schemadir = NULL;\r
-const char* shar_prefix = NULL;\r
-bool shar_checkonly = false;\r
-bool shar_version = false;\r
-static bool unlink_socket = false;\r
-const char* pidfile = NULL;\r
-\r
-#ifdef WIN32\r
-\r
-//#include <CRTDBG.H>\r
-\r
-#define nNoMansLandSize 4\r
-typedef struct _CrtMemBlockHeader\r
-{\r
-        struct _CrtMemBlockHeader * pBlockHeaderNext;\r
-        struct _CrtMemBlockHeader * pBlockHeaderPrev;\r
-        char *                      szFileName;\r
-        int                         nLine;\r
-        size_t                      nDataSize;\r
-        int                         nBlockUse;\r
-        long                        lRequest;\r
-        unsigned char               gap[nNoMansLandSize];\r
-        /* followed by:\r
-         *  unsigned char           data[nDataSize];\r
-         *  unsigned char           anotherGap[nNoMansLandSize];\r
-         */\r
-} _CrtMemBlockHeader;\r
-\r
-/*\r
-int MyAllocHook(int nAllocType, void *pvData,\r
-      size_t nSize, int nBlockUse, long lRequest,\r
-      const unsigned char * szFileName, int nLine)\r
-{\r
-    if ( nBlockUse == _CRT_BLOCK )\r
-      return( TRUE );\r
-    if (nAllocType == _HOOK_FREE) {\r
-        _CrtMemBlockHeader* ptr = (_CrtMemBlockHeader*)(((_CrtMemBlockHeader *)pvData)-1);\r
-        if (ptr->nDataSize == 8192)\r
-            fprintf(stderr,"free  request %u size %u\n", ptr->lRequest, ptr->nDataSize);\r
-    }\r
-    else if (nAllocType == _HOOK_ALLOC && nSize == 8192)\r
-        fprintf(stderr,"%s request %u size %u\n", ((nAllocType == _HOOK_ALLOC) ? "alloc" : "realloc"), lRequest, nSize);\r
-    return (TRUE);\r
-}\r
-*/\r
-\r
-int real_main(int preinit)\r
-{\r
-    SPConfig& conf=SPConfig::getConfig();\r
-    if (preinit) {\r
-\r
-        // Initialize the SP library.\r
-        conf.setFeatures(\r
-            SPConfig::Listener |\r
-            SPConfig::Caching |\r
-            SPConfig::Metadata |\r
-            SPConfig::Trust |\r
-            SPConfig::Credentials |\r
-            SPConfig::AttributeResolution |\r
-            SPConfig::Handlers |\r
-            SPConfig::OutOfProcess |\r
-            (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)\r
-            );\r
-        if (!conf.init(shar_schemadir, shar_prefix)) {\r
-            fprintf(stderr, "configuration is invalid, see console for specific problems\n");\r
-            return -1;\r
-        }\r
-\r
-        if (!conf.instantiate(shar_config)) {\r
-            fprintf(stderr, "configuration is invalid, check console for specific problems\n");\r
-            conf.term();\r
-            return -2;\r
-        }\r
-\r
-        // If just a test run, bail.\r
-        if (shar_checkonly) {\r
-            fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");\r
-            return 0;\r
-        }\r
-    }\r
-    else {\r
-\r
-        //_CrtSetAllocHook(MyAllocHook);\r
-\r
-        if (!shar_checkonly) {\r
-            // Run the listener.\r
-            if (!conf.getServiceProvider()->getListenerService()->run(unlink_socket, &shibd_shutdown)) {\r
-                fprintf(stderr, "listener failed to enter listen loop\n");\r
-                return -3;\r
-            }\r
-        }\r
-\r
-        conf.term();\r
-    }\r
-    return 0;\r
-}\r
-\r
-#else\r
-\r
-int daemon_wait = 3;\r
-bool shibd_running = false;\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 int setup_signals(void)\r
-{\r
-    struct sigaction sa;\r
-    memset(&sa, 0, sizeof (sa));\r
-    sa.sa_handler = SIG_IGN;\r
-    sa.sa_flags = SA_RESTART;\r
-\r
-    if (sigaction(SIGPIPE, &sa, NULL) < 0) {\r
-        return -1;\r
-    }\r
-\r
-    memset(&sa, 0, sizeof (sa));\r
-    sa.sa_handler = term_handler;\r
-    sa.sa_flags = SA_RESTART;\r
-\r
-    if (sigaction(SIGHUP, &sa, NULL) < 0) {\r
-        return -1;\r
-    }\r
-    if (sigaction(SIGINT, &sa, NULL) < 0) {\r
-        return -1;\r
-    }\r
-    if (sigaction(SIGQUIT, &sa, NULL) < 0) {\r
-        return -1;\r
-    }\r
-    if (sigaction(SIGTERM, &sa, NULL) < 0) {\r
-        return -1;\r
-    }\r
-\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
-    return 0;\r
-}\r
-\r
-static void usage(char* whoami)\r
-{\r
-    fprintf(stderr, "usage: %s [-dcxtfpvh]\n", whoami);\r
-    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, "  -f\tforce removal of listener socket.\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
-}\r
-\r
-static int parse_args(int argc, char* argv[])\r
-{\r
-    int opt;\r
-\r
-    while ((opt = getopt(argc, argv, "d:c:x:p:w:ftvh")) > 0) {\r
-        switch (opt) {\r
-            case 'd':\r
-                shar_prefix=optarg;\r
-                break;\r
-            case 'c':\r
-                shar_config=optarg;\r
-                break;\r
-            case 'x':\r
-                shar_schemadir=optarg;\r
-                break;\r
-            case 'f':\r
-                unlink_socket = true;\r
-                break;\r
-            case 't':\r
-                shar_checkonly=true;\r
-                break;\r
-            case 'v':\r
-                shar_version=true;\r
-                break;\r
-            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
-    }\r
-    return 0;\r
-}\r
-\r
-int main(int argc, char *argv[])\r
-{\r
-    if (parse_args(argc, argv) != 0)\r
-        usage(argv[0]);\r
-    else if (shar_version) {\r
-        fprintf(stdout, PACKAGE_STRING"\n");\r
-        return 0;\r
-    }\r
-\r
-    if (setup_signals() != 0)\r
-        return -1;\r
-\r
-    // initialize the shib-target library\r
-    SPConfig& conf=SPConfig::getConfig();\r
-    conf.setFeatures(\r
-        SPConfig::Listener |\r
-        SPConfig::Caching |\r
-        SPConfig::Metadata |\r
-        SPConfig::Trust |\r
-        SPConfig::Credentials |\r
-        SPConfig::AttributeResolution |\r
-        SPConfig::Handlers |\r
-        SPConfig::OutOfProcess |\r
-        (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)\r
-        );\r
-    if (!conf.init(shar_schemadir, shar_prefix)) {\r
-        fprintf(stderr, "configuration is invalid, check console for specific problems\n");\r
-        return -1;\r
-    }\r
-\r
-    // We must fork() early, while we're single threaded.\r
-    // StorageService cleanup thread is about to start.\r
-    if (!shar_checkonly) {\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
-        return -2;\r
-    }\r
-\r
-    if (shar_checkonly)\r
-        fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");\r
-    else {\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
-        // 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
-            }\r
-        }\r
-\r
-        freopen("/dev/null", "r", stdin);\r
-        freopen("/dev/null", "w", stdout);\r
-        freopen("/dev/null", "w", stderr);\r
-\r
-        // Run the listener\r
-        if (!conf.getServiceProvider()->getListenerService()->run(unlink_socket, &shibd_shutdown)) {\r
-            fprintf(stderr, "listener failed to enter listen loop\n");\r
-            return -3;\r
-        }\r
-    }\r
-\r
-    conf.term();\r
-    if (pidfile)\r
-        unlink(pidfile);\r
-    return 0;\r
-}\r
-\r
-#endif\r
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/*
+ * shibd.cpp -- the shibd "main" code.
+ */
+
+
+// eventually we might be able to support autoconf via cygwin...
+#if defined (_MSC_VER) || defined(__BORLANDC__)
+# include "config_win32.h"
+#else
+# include "config.h"
+#endif
+
+#ifdef WIN32
+# define _CRT_NONSTDC_NO_DEPRECATE 1
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+
+#include <shibsp/SPConfig.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+# include <sys/select.h>
+#endif
+
+#if defined(HAVE_GRP_H) && defined(HAVE_PWD_H)
+# include <pwd.h>
+# include <grp.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <shibsp/ServiceProvider.h>
+#include <shibsp/remoting/ListenerService.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/XMLConstants.h>
+#include <xmltooling/util/XMLHelper.h>
+
+#ifdef HAVE_SD_NOTIFY
+#include <systemd/sd-daemon.h>
+#else
+#define SD_EMERG   ""
+#define SD_ALERT   ""
+#define SD_CRIT    ""
+#define SD_ERR     ""
+#define SD_WARNING ""
+#define SD_NOTICE  ""
+#define SD_INFO    ""
+#define SD_DEBUG   ""
+#endif
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+bool shibd_shutdown = false;
+const char* shar_config = nullptr;
+const char* shar_schemadir = nullptr;
+const char* shar_prefix = nullptr;
+bool shar_checkonly = false;
+bool shar_version = false;
+static bool unlink_socket = false;
+const char* pidfile = nullptr;
+
+#ifdef WIN32
+
+//#include <CRTDBG.H>
+
+#define nNoMansLandSize 4
+typedef struct _CrtMemBlockHeader
+{
+        struct _CrtMemBlockHeader * pBlockHeaderNext;
+        struct _CrtMemBlockHeader * pBlockHeaderPrev;
+        char *                      szFileName;
+        int                         nLine;
+        size_t                      nDataSize;
+        int                         nBlockUse;
+        long                        lRequest;
+        unsigned char               gap[nNoMansLandSize];
+        /* followed by:
+         *  unsigned char           data[nDataSize];
+         *  unsigned char           anotherGap[nNoMansLandSize];
+         */
+} _CrtMemBlockHeader;
+
+/*
+int MyAllocHook(int nAllocType, void *pvData,
+      size_t nSize, int nBlockUse, long lRequest,
+      const unsigned char * szFileName, int nLine)
+{
+    if ( nBlockUse == _CRT_BLOCK )
+      return( TRUE );
+    if (nAllocType == _HOOK_FREE) {
+        _CrtMemBlockHeader* ptr = (_CrtMemBlockHeader*)(((_CrtMemBlockHeader *)pvData)-1);
+        if (ptr->nDataSize == 8192)
+            fprintf(stderr,"free  request %u size %u\n", ptr->lRequest, ptr->nDataSize);
+    }
+    else if (nAllocType == _HOOK_ALLOC && nSize == 8192)
+        fprintf(stderr,"%s request %u size %u\n", ((nAllocType == _HOOK_ALLOC) ? "alloc" : "realloc"), lRequest, nSize);
+    return (TRUE);
+}
+*/
+
+int real_main(int preinit)
+{
+    if (shar_version) {
+        if (preinit)
+            fprintf(stdout, PACKAGE_STRING"\n");
+        return 0;
+    }
+
+    SPConfig& conf = SPConfig::getConfig();
+    if (preinit) {
+        // Initialize the SP library.
+        conf.setFeatures(
+            SPConfig::Listener |
+            SPConfig::Caching |
+            SPConfig::Metadata |
+            SPConfig::Trust |
+            SPConfig::Credentials |
+            SPConfig::AttributeResolution |
+            SPConfig::Handlers |
+            SPConfig::OutOfProcess |
+            (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)
+            );
+        if (!conf.init(shar_schemadir, shar_prefix)) {
+            fprintf(stderr, "configuration is invalid, see console for specific problems\n");
+            return -1;
+        }
+
+        if (!conf.instantiate(shar_config)) {
+            fprintf(stderr, "configuration is invalid, check console for specific problems\n");
+            conf.term();
+            return -2;
+        }
+
+        // If just a test run, bail.
+        if (shar_checkonly) {
+            fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");
+            return 0;
+        }
+    }
+    else {
+
+        //_CrtSetAllocHook(MyAllocHook);
+
+        if (!shar_checkonly) {
+            // Run the listener.
+            ListenerService* listener = conf.getServiceProvider()->getListenerService();
+            if (!listener->init(unlink_socket)) {
+                fprintf(stderr, "listener failed to initialize\n");
+                conf.term();
+                return -3;
+            }
+            else if (!listener->run(&shibd_shutdown)) {
+                fprintf(stderr, "listener failed during service\n");
+                listener->term();
+                conf.term();
+                return -3;
+            }
+            listener->term();
+        }
+
+        conf.term();
+    }
+    return 0;
+}
+
+#else
+
+int daemon_wait = 3;
+bool shibd_running = false;
+bool daemonize = true;
+const char* runasuser = nullptr;
+const char* runasgroup = nullptr;
+
+static void term_handler(int arg)
+{
+    shibd_shutdown = true;
+}
+
+static void run_handler(int arg)
+{
+    shibd_running = true;
+}
+
+static void child_handler(int arg)
+{
+    // Terminate the parent's wait/sleep if the newly born daemon dies early.
+}
+
+static int setup_signals(void)
+{
+    struct sigaction sa;
+    memset(&sa, 0, sizeof (sa));
+    sa.sa_handler = SIG_IGN;
+    sa.sa_flags = SA_RESTART;
+
+    if (sigaction(SIGPIPE, &sa, nullptr) < 0) {
+        return -1;
+    }
+
+    memset(&sa, 0, sizeof (sa));
+    sa.sa_handler = term_handler;
+    sa.sa_flags = SA_RESTART;
+
+    if (sigaction(SIGHUP, &sa, nullptr) < 0) {
+        return -1;
+    }
+    if (sigaction(SIGINT, &sa, nullptr) < 0) {
+        return -1;
+    }
+    if (sigaction(SIGQUIT, &sa, nullptr) < 0) {
+        return -1;
+    }
+    if (sigaction(SIGTERM, &sa, nullptr) < 0) {
+        return -1;
+    }
+
+    if (daemonize) {
+        memset(&sa, 0, sizeof (sa));
+        sa.sa_handler = run_handler;
+
+        if (sigaction(SIGUSR1, &sa, nullptr) < 0) {
+            return -1;
+        }
+
+        memset(&sa, 0, sizeof (sa));
+        sa.sa_handler = child_handler;
+
+        if (sigaction(SIGCHLD, &sa, nullptr) < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static void usage(char* whoami)
+{
+    fprintf(stderr, "usage: %s [-dcxtfFpwugvh]\n", whoami);
+    fprintf(stderr, "  -d\tinstallation prefix to use\n");
+    fprintf(stderr, "  -c\tconfig file to use\n");
+    fprintf(stderr, "  -x\tXML schema catalogs to use\n");
+    fprintf(stderr, "  -t\ttest configuration file for problems\n");
+    fprintf(stderr, "  -f\tforce removal of listener socket\n");
+    fprintf(stderr, "  -F\tstay in the foreground\n");
+    fprintf(stderr, "  -p\tpid file to use\n");
+    fprintf(stderr, "  -w\tseconds to wait for successful daemonization\n");
+    fprintf(stderr, "  -u\tuser to run under\n");
+    fprintf(stderr, "  -g\tgroup to run under\n");
+    fprintf(stderr, "  -v\tprint software version\n");
+    fprintf(stderr, "  -h\tprint this help message\n");
+    exit(1);
+}
+
+static int parse_args(int argc, char* argv[])
+{
+    int opt;
+
+    while ((opt = getopt(argc, argv, "d:c:x:p:w:u:g:fFtvh")) > 0) {
+        switch (opt) {
+            case 'd':
+                shar_prefix=optarg;
+                break;
+            case 'c':
+                shar_config=optarg;
+                break;
+            case 'x':
+                shar_schemadir=optarg;
+                break;
+            case 'f':
+                unlink_socket = true;
+                break;
+            case 'F':
+                daemonize = false;
+                break;
+            case 't':
+                shar_checkonly=true;
+                daemonize=false;
+                break;
+            case 'v':
+                shar_version=true;
+                break;
+            case 'p':
+                pidfile=optarg;
+                break;
+            case 'w':
+                if (optarg)
+                    daemon_wait = atoi(optarg);
+                if (daemon_wait <= 0)
+                    daemon_wait = 3;
+                break;
+            case 'u':
+                if (optarg)
+                    runasuser = optarg;
+                break;
+            case 'g':
+                if (optarg)
+                    runasgroup = optarg;
+                break;
+            default:
+                return -1;
+        }
+    }
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    if (parse_args(argc, argv) != 0)
+        usage(argv[0]);
+    else if (shar_version) {
+        fprintf(stdout, PACKAGE_STRING"\n");
+        return 0;
+    }
+
+    if (setup_signals() != 0)
+        return -1;
+
+    if (runasgroup) {
+#ifdef HAVE_GETGRNAM
+        struct group* grp = getgrnam(runasgroup);
+        if (!grp) {
+            fprintf(stderr, "getgrnam failed, check -g option\n");
+            return -1;
+        }
+        if (setgid(grp->gr_gid) != 0) {
+            fprintf(stderr, "setgid failed, check -g option\n");
+            return -1;
+        }
+#else
+        fprintf(stderr, "-g not supported on this platform");
+        return -1;
+#endif
+    }
+
+    if (runasuser) {
+#ifdef HAVE_GETPWNAM
+        struct passwd* pwd = getpwnam(runasuser);
+        if (!pwd) {
+            fprintf(stderr, "getpwnam failed, check -u option\n");
+            return -1;
+        }
+#ifdef HAVE_INITGROUPS
+        // w/out initgroups/setgroups process retains supplementary groups
+        if (initgroups(pwd->pw_name, pwd->pw_gid) != 0) {
+            fprintf(stderr, "initgroups failed, check -u option\n");
+            return -1;
+        }
+#endif
+        if (setuid(pwd->pw_uid) != 0) {
+            fprintf(stderr, "setuid failed, check -u option\n");
+            return -1;
+        }
+#else
+        fprintf(stderr, "-u not supported on this platform");
+        return -1;
+#endif
+    }
+
+    // initialize the shib-target library
+    SPConfig& conf=SPConfig::getConfig();
+    conf.setFeatures(
+        SPConfig::Listener |
+        SPConfig::Caching |
+        SPConfig::Metadata |
+        SPConfig::Trust |
+        SPConfig::Credentials |
+        SPConfig::AttributeResolution |
+        SPConfig::Handlers |
+        SPConfig::OutOfProcess |
+        (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)
+        );
+    if (!conf.init(shar_schemadir, shar_prefix)) {
+        fprintf(stderr, SD_ERR "configuration is invalid, check console for specific problems\n");
+        return -1;
+    }
+
+    if (daemonize) {
+        // We must fork() early, while we're single threaded.
+        // StorageService cleanup thread is about to start.
+        switch (fork()) {
+            case 0:
+                break;
+            case -1:
+                perror("forking");
+                exit(EXIT_FAILURE);
+            default:
+                sleep(daemon_wait);
+                exit(shibd_running ? EXIT_SUCCESS : EXIT_FAILURE);
+        }
+    }
+
+    if (!conf.instantiate(shar_config)) {
+        fprintf(stderr, SD_ERR "configuration is invalid, check console for specific problems\n");
+        conf.term();
+        return -2;
+    }
+
+    if (shar_checkonly)
+        fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");
+    else {
+        // Init the listener.
+        ListenerService* listener = conf.getServiceProvider()->getListenerService();
+        if (!listener->init(unlink_socket)) {
+            fprintf(stderr, SD_ERR "listener failed to initialize\n");
+            conf.term();
+            return -3;
+        }
+
+        if (daemonize) {
+            if (setsid() == -1) {
+                perror("setsid");
+                exit(EXIT_FAILURE);
+            }
+            if (chdir("/") == -1) {
+                perror("chdir to root");
+                exit(EXIT_FAILURE);
+            }
+
+            if (pidfile) {
+                FILE* pidf = fopen(pidfile, "w");
+                if (pidf) {
+                    fprintf(pidf, "%d\n", getpid());
+                    fclose(pidf);
+                }
+                else {
+                    perror(pidfile);
+                }
+            }
+
+            freopen("/dev/null", "r", stdin);
+            freopen("/dev/null", "w", stdout);
+            freopen("/dev/null", "w", stderr);
+
+            // Signal our parent that we are A-OK.
+            kill(getppid(), SIGUSR1);
+        }
+
+        // Run the listener.
+#ifdef HAVE_SD_NOTIFY
+        sd_notify(0, "READY=1");
+#endif
+        if (!listener->run(&shibd_shutdown)) {
+            fprintf(stderr, SD_ERR "listener failure during service\n");
+            listener->term();
+            conf.term();
+            if (daemonize && pidfile)
+                unlink(pidfile);
+            return -3;
+        }
+        listener->term();
+    }
+#ifdef HAVE_SD_NOTIFY
+    sd_notify(0, "STOPPING=1");
+#endif
+    conf.term();
+    if (daemonize && pidfile)
+        unlink(pidfile);
+    return 0;
+}
+
+#endif