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