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