Only send USR1 signal when daemonized.
[shibboleth/cpp-sp.git] / shibd / shibd.cpp
1 /*\r
2  *  Copyright 2001-2009 Internet2\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /*\r
18  * shar.cpp -- the shibd "main" code.  All the functionality is elsewhere\r
19  *\r
20  * Created By:  Derek Atkins <derek@ihtfp.com>\r
21  *\r
22  * $Id: shar.cpp 2164 2007-02-11 05:26:18 +0000 (Sun, 11 Feb 2007) cantor $\r
23  */\r
24 \r
25 \r
26 // eventually we might be able to support autoconf via cygwin...\r
27 #if defined (_MSC_VER) || defined(__BORLANDC__)\r
28 # include "config_win32.h"\r
29 #else\r
30 # include "config.h"\r
31 #endif\r
32 \r
33 #ifdef WIN32\r
34 # define _CRT_NONSTDC_NO_DEPRECATE 1\r
35 # define _CRT_SECURE_NO_DEPRECATE 1\r
36 #endif\r
37 \r
38 #include <shibsp/SPConfig.h>\r
39 \r
40 #ifdef HAVE_UNISTD_H\r
41 #include <unistd.h>\r
42 #include <sys/select.h>\r
43 #endif\r
44 \r
45 #include <stdio.h>\r
46 #include <signal.h>\r
47 #include <shibsp/ServiceProvider.h>\r
48 #include <shibsp/remoting/ListenerService.h>\r
49 #include <xercesc/util/XMLUniDefs.hpp>\r
50 #include <xmltooling/XMLToolingConfig.h>\r
51 #include <xmltooling/util/XMLConstants.h>\r
52 #include <xmltooling/util/XMLHelper.h>\r
53 \r
54 using namespace shibsp;\r
55 using namespace xmltooling;\r
56 using namespace std;\r
57 \r
58 bool shibd_shutdown = false;\r
59 const char* shar_config = NULL;\r
60 const char* shar_schemadir = NULL;\r
61 const char* shar_prefix = NULL;\r
62 bool shar_checkonly = false;\r
63 bool shar_version = false;\r
64 static bool unlink_socket = false;\r
65 const char* pidfile = NULL;\r
66 \r
67 #ifdef WIN32\r
68 \r
69 //#include <CRTDBG.H>\r
70 \r
71 #define nNoMansLandSize 4\r
72 typedef struct _CrtMemBlockHeader\r
73 {\r
74         struct _CrtMemBlockHeader * pBlockHeaderNext;\r
75         struct _CrtMemBlockHeader * pBlockHeaderPrev;\r
76         char *                      szFileName;\r
77         int                         nLine;\r
78         size_t                      nDataSize;\r
79         int                         nBlockUse;\r
80         long                        lRequest;\r
81         unsigned char               gap[nNoMansLandSize];\r
82         /* followed by:\r
83          *  unsigned char           data[nDataSize];\r
84          *  unsigned char           anotherGap[nNoMansLandSize];\r
85          */\r
86 } _CrtMemBlockHeader;\r
87 \r
88 /*\r
89 int MyAllocHook(int nAllocType, void *pvData,\r
90       size_t nSize, int nBlockUse, long lRequest,\r
91       const unsigned char * szFileName, int nLine)\r
92 {\r
93     if ( nBlockUse == _CRT_BLOCK )\r
94       return( TRUE );\r
95     if (nAllocType == _HOOK_FREE) {\r
96         _CrtMemBlockHeader* ptr = (_CrtMemBlockHeader*)(((_CrtMemBlockHeader *)pvData)-1);\r
97         if (ptr->nDataSize == 8192)\r
98             fprintf(stderr,"free  request %u size %u\n", ptr->lRequest, ptr->nDataSize);\r
99     }\r
100     else if (nAllocType == _HOOK_ALLOC && nSize == 8192)\r
101         fprintf(stderr,"%s request %u size %u\n", ((nAllocType == _HOOK_ALLOC) ? "alloc" : "realloc"), lRequest, nSize);\r
102     return (TRUE);\r
103 }\r
104 */\r
105 \r
106 int real_main(int preinit)\r
107 {\r
108     SPConfig& conf=SPConfig::getConfig();\r
109     if (preinit) {\r
110 \r
111         // Initialize the SP library.\r
112         conf.setFeatures(\r
113             SPConfig::Listener |\r
114             SPConfig::Caching |\r
115             SPConfig::Metadata |\r
116             SPConfig::Trust |\r
117             SPConfig::Credentials |\r
118             SPConfig::AttributeResolution |\r
119             SPConfig::Handlers |\r
120             SPConfig::OutOfProcess |\r
121             (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)\r
122             );\r
123         if (!conf.init(shar_schemadir, shar_prefix)) {\r
124             fprintf(stderr, "configuration is invalid, see console for specific problems\n");\r
125             return -1;\r
126         }\r
127 \r
128         if (!conf.instantiate(shar_config)) {\r
129             fprintf(stderr, "configuration is invalid, check console for specific problems\n");\r
130             conf.term();\r
131             return -2;\r
132         }\r
133 \r
134         // If just a test run, bail.\r
135         if (shar_checkonly) {\r
136             fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");\r
137             return 0;\r
138         }\r
139     }\r
140     else {\r
141 \r
142         //_CrtSetAllocHook(MyAllocHook);\r
143 \r
144         if (!shar_checkonly) {\r
145             // Run the listener.\r
146             ListenerService* listener = conf.getServiceProvider()->getListenerService();\r
147             if (!listener->init(unlink_socket)) {\r
148                 fprintf(stderr, "listener failed to initialize\n");\r
149                 return -3;\r
150             }\r
151             else if (!listener->run(&shibd_shutdown)) {\r
152                 listener->term();\r
153                 fprintf(stderr, "listener failed to begin service\n");\r
154                 return -3;\r
155             }\r
156             listener->term();\r
157         }\r
158 \r
159         conf.term();\r
160     }\r
161     return 0;\r
162 }\r
163 \r
164 #else\r
165 \r
166 int daemon_wait = 3;\r
167 bool shibd_running = false;\r
168 bool daemonize = true;\r
169 \r
170 static void term_handler(int arg)\r
171 {\r
172     shibd_shutdown = true;\r
173 }\r
174 \r
175 static void run_handler(int arg)\r
176 {\r
177     shibd_running = true;\r
178 }\r
179 \r
180 static void child_handler(int arg)\r
181 {\r
182     // Terminate the parent's wait/sleep if the newly born daemon dies early.\r
183 }\r
184 \r
185 static int setup_signals(void)\r
186 {\r
187     struct sigaction sa;\r
188     memset(&sa, 0, sizeof (sa));\r
189     sa.sa_handler = SIG_IGN;\r
190     sa.sa_flags = SA_RESTART;\r
191 \r
192     if (sigaction(SIGPIPE, &sa, NULL) < 0) {\r
193         return -1;\r
194     }\r
195 \r
196     memset(&sa, 0, sizeof (sa));\r
197     sa.sa_handler = term_handler;\r
198     sa.sa_flags = SA_RESTART;\r
199 \r
200     if (sigaction(SIGHUP, &sa, NULL) < 0) {\r
201         return -1;\r
202     }\r
203     if (sigaction(SIGINT, &sa, NULL) < 0) {\r
204         return -1;\r
205     }\r
206     if (sigaction(SIGQUIT, &sa, NULL) < 0) {\r
207         return -1;\r
208     }\r
209     if (sigaction(SIGTERM, &sa, NULL) < 0) {\r
210         return -1;\r
211     }\r
212 \r
213     if (daemonize) {\r
214         memset(&sa, 0, sizeof (sa));\r
215         sa.sa_handler = run_handler;\r
216 \r
217         if (sigaction(SIGUSR1, &sa, NULL) < 0) {\r
218             return -1;\r
219         }\r
220 \r
221         memset(&sa, 0, sizeof (sa));\r
222         sa.sa_handler = child_handler;\r
223 \r
224         if (sigaction(SIGCHLD, &sa, NULL) < 0) {\r
225             return -1;\r
226         }\r
227     }\r
228 \r
229     return 0;\r
230 }\r
231 \r
232 static void usage(char* whoami)\r
233 {\r
234     fprintf(stderr, "usage: %s [-dcxtfpvh]\n", whoami);\r
235     fprintf(stderr, "  -d\tinstallation prefix to use.\n");\r
236     fprintf(stderr, "  -c\tconfig file to use.\n");\r
237     fprintf(stderr, "  -x\tXML schema catalogs to use.\n");\r
238     fprintf(stderr, "  -t\ttest configuration file for problems.\n");\r
239     fprintf(stderr, "  -f\tforce removal of listener socket.\n");\r
240     fprintf(stderr, "  -F\tstay in the foreground.\n");\r
241     fprintf(stderr, "  -p\tpid file to use.\n");\r
242     fprintf(stderr, "  -w\tseconds to wait for successful daemonization.\n");\r
243     fprintf(stderr, "  -v\tprint software version.\n");\r
244     fprintf(stderr, "  -h\tprint this help message.\n");\r
245     exit(1);\r
246 }\r
247 \r
248 static int parse_args(int argc, char* argv[])\r
249 {\r
250     int opt;\r
251 \r
252     while ((opt = getopt(argc, argv, "d:c:x:p:w:fFtvh")) > 0) {\r
253         switch (opt) {\r
254             case 'd':\r
255                 shar_prefix=optarg;\r
256                 break;\r
257             case 'c':\r
258                 shar_config=optarg;\r
259                 break;\r
260             case 'x':\r
261                 shar_schemadir=optarg;\r
262                 break;\r
263             case 'f':\r
264                 unlink_socket = true;\r
265                 break;\r
266             case 'F':\r
267                 daemonize = false;\r
268                 break;\r
269             case 't':\r
270                 shar_checkonly=true;\r
271                 daemonize=false;\r
272                 break;\r
273             case 'v':\r
274                 shar_version=true;\r
275                 break;\r
276             case 'p':\r
277                 pidfile=optarg;\r
278                 break;\r
279             case 'w':\r
280                 if (optarg)\r
281                     daemon_wait = atoi(optarg);\r
282                 if (daemon_wait <= 0)\r
283                     daemon_wait = 3;\r
284                 break;\r
285             default:\r
286                 return -1;\r
287         }\r
288     }\r
289     return 0;\r
290 }\r
291 \r
292 int main(int argc, char *argv[])\r
293 {\r
294     if (parse_args(argc, argv) != 0)\r
295         usage(argv[0]);\r
296     else if (shar_version) {\r
297         fprintf(stdout, PACKAGE_STRING"\n");\r
298         return 0;\r
299     }\r
300 \r
301     if (setup_signals() != 0)\r
302         return -1;\r
303 \r
304     // initialize the shib-target library\r
305     SPConfig& conf=SPConfig::getConfig();\r
306     conf.setFeatures(\r
307         SPConfig::Listener |\r
308         SPConfig::Caching |\r
309         SPConfig::Metadata |\r
310         SPConfig::Trust |\r
311         SPConfig::Credentials |\r
312         SPConfig::AttributeResolution |\r
313         SPConfig::Handlers |\r
314         SPConfig::OutOfProcess |\r
315         (shar_checkonly ? SPConfig::RequestMapping : SPConfig::Logging)\r
316         );\r
317     if (!conf.init(shar_schemadir, shar_prefix)) {\r
318         fprintf(stderr, "configuration is invalid, check console for specific problems\n");\r
319         return -1;\r
320     }\r
321 \r
322     if (daemonize) {\r
323         // We must fork() early, while we're single threaded.\r
324         // StorageService cleanup thread is about to start.\r
325         pid_t pid = fork();\r
326         switch (pid) {\r
327             case 0:\r
328                 break;\r
329             case -1:\r
330                 perror("forking");\r
331                 exit(EXIT_FAILURE);\r
332             default:\r
333                 sleep(daemon_wait);\r
334                 if (shibd_running) {\r
335                     if (pidfile) {\r
336                         FILE* pidf = fopen(pidfile, "w");\r
337                         if (pidf) {\r
338                             fprintf(pidf, "%d\n", pid);\r
339                             fclose(pidf);\r
340                         }\r
341                         else {\r
342                             perror(pidfile);\r
343                         }\r
344                     }\r
345                     exit(EXIT_SUCCESS);\r
346                 }\r
347                 else {\r
348                     exit(EXIT_FAILURE);\r
349                 }\r
350         }\r
351     }\r
352 \r
353     if (!conf.instantiate(shar_config)) {\r
354         fprintf(stderr, "configuration is invalid, check console for specific problems\n");\r
355         conf.term();\r
356         return -2;\r
357     }\r
358 \r
359     if (shar_checkonly)\r
360         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");\r
361     else {\r
362         if (daemonize) {\r
363             if (setsid() == -1) {\r
364                 perror("setsid");\r
365                 exit(EXIT_FAILURE);\r
366             }\r
367             if (chdir("/") == -1) {\r
368                 perror("chdir to root");\r
369                 exit(EXIT_FAILURE);\r
370             }\r
371         }\r
372 \r
373         if (daemonize) {\r
374             freopen("/dev/null", "r", stdin);\r
375             freopen("/dev/null", "w", stdout);\r
376             freopen("/dev/null", "w", stderr);\r
377         }\r
378 \r
379         // Init the listener.\r
380         ListenerService* listener = conf.getServiceProvider()->getListenerService();\r
381         if (!listener->init(unlink_socket)) {\r
382             fprintf(stderr, "listener failed to initialize\n");\r
383             return -3;\r
384         }\r
385 \r
386         // Signal our parent.\r
387         if (daemonize) {\r
388             pid_t ppid = getppid();\r
389             kill(ppid, SIGUSR1);\r
390         }\r
391 \r
392         // Run the listener.\r
393         if (!listener->run(&shibd_shutdown)) {\r
394             listener->term();\r
395             fprintf(stderr, "listener failed to begin service\n");\r
396             return -3;\r
397         }\r
398         listener->term();\r
399     }\r
400 \r
401     conf.term();\r
402     if (pidfile)\r
403         unlink(pidfile);\r
404     return 0;\r
405 }\r
406 \r
407 #endif\r