Backport logging changes.
[shibboleth/cpp-sp.git] / shar / shar.cpp
1 /*
2  *  Copyright 2001-2005 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  * shar.cpp -- the shibd "main" code.  All the functionality is elsewhere
19  *
20  * Created By:  Derek Atkins <derek@ihtfp.com>
21  *
22  * $Id$
23  */
24
25 // eventually we might be able to support autoconf via cygwin...
26 #if defined (_MSC_VER) || defined(__BORLANDC__)
27 # include "config_win32.h"
28 #else
29 # include "config.h"
30 #endif
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #include <sys/select.h>
35 #endif
36
37 #include <stdio.h>
38 #include <errno.h>
39 #include <signal.h>
40
41 #include "shar-utils.h"
42
43 using namespace std;
44 using namespace saml;
45 using namespace shibboleth;
46 using namespace shibtarget;
47 using namespace shibd::logging;
48
49 #ifndef FD_SETSIZE
50 # define FD_SETSIZE 1024
51 #endif
52
53 extern "C" void shibrpc_prog_2(struct svc_req* rqstp, register SVCXPRT* transp);
54
55 // Declare a "MemoryListener" that our server methods will forward their work to.
56 IListener* g_MemoryListener = NULL;
57
58 int shar_run = 1;
59 const char* shar_config = NULL;
60 const char* shar_schemadir = NULL;
61 bool shar_checkonly = false;
62 static int unlink_socket = 0;
63 const char* pidfile = NULL;
64
65 static bool new_connection(IListener::ShibSocket& listener, const Iterator<ShibRPCProtocols>& protos)
66 {
67     IListener::ShibSocket sock;
68
69     // Accept the connection.
70     if (!ShibTargetConfig::getConfig().getINI()->getListener()->accept(listener, sock))
71         return false;
72
73     // We throw away the result because the children manage themselves...
74     try {
75         new SharChild(sock,protos);
76     }
77     catch (...) {
78         saml::NDC ndc("new_connection");
79         Category& log=Category::getInstance("shibd");
80         log.crit("error starting new child thread to service request");
81         return false;
82     }
83     return true;
84 }
85
86 static void shar_svc_run(IListener::ShibSocket& listener, const Iterator<ShibRPCProtocols>& protos)
87 {
88 #ifdef _DEBUG
89     saml::NDC ndc("shar_svc_run");
90 #endif
91     Category& log=Category::getInstance("shibd");
92
93     while (shar_run) {
94         fd_set readfds;
95         FD_ZERO(&readfds);
96         FD_SET(listener, &readfds);
97         struct timeval tv = { 0, 0 };
98         tv.tv_sec = 5;
99     
100         switch (select(listener + 1, &readfds, 0, 0, &tv)) {
101 #ifdef WIN32
102             case SOCKET_ERROR:
103 #else
104             case -1:
105 #endif
106                 if (errno == EINTR) continue;
107                 SHARUtils::log_error();
108                 log.error("select() on main listener socket failed");
109                 return;
110         
111             case 0:
112                 continue;
113         
114             default:
115                 if (!new_connection(listener, protos))
116                     log.crit("new_connection failed");
117         }
118     }
119     log.info("shar_svc_run ended");
120 }
121
122 #ifdef WIN32
123
124 //#include <CRTDBG.H>
125
126 #define nNoMansLandSize 4
127 typedef struct _CrtMemBlockHeader
128 {
129         struct _CrtMemBlockHeader * pBlockHeaderNext;
130         struct _CrtMemBlockHeader * pBlockHeaderPrev;
131         char *                      szFileName;
132         int                         nLine;
133         size_t                      nDataSize;
134         int                         nBlockUse;
135         long                        lRequest;
136         unsigned char               gap[nNoMansLandSize];
137         /* followed by:
138          *  unsigned char           data[nDataSize];
139          *  unsigned char           anotherGap[nNoMansLandSize];
140          */
141 } _CrtMemBlockHeader;
142
143 /*
144 int MyAllocHook(int nAllocType, void *pvData,
145       size_t nSize, int nBlockUse, long lRequest,
146       const unsigned char * szFileName, int nLine)
147 {
148     if ( nBlockUse == _CRT_BLOCK )
149       return( TRUE );
150     if (nAllocType == _HOOK_FREE) {
151         _CrtMemBlockHeader* ptr = (_CrtMemBlockHeader*)(((_CrtMemBlockHeader *)pvData)-1);
152         if (ptr->nDataSize == 8192)
153             fprintf(stderr,"free  request %u size %u\n", ptr->lRequest, ptr->nDataSize);
154     }
155     else if (nAllocType == _HOOK_ALLOC && nSize == 8192)
156         fprintf(stderr,"%s request %u size %u\n", ((nAllocType == _HOOK_ALLOC) ? "alloc" : "realloc"), lRequest, nSize);
157     return (TRUE);
158 }
159 */
160
161 int real_main(int preinit)
162 {
163     static IListener::ShibSocket sock;
164     ShibRPCProtocols protos[1] = {
165         { SHIBRPC_PROG, SHIBRPC_VERS_2, shibrpc_prog_2 }
166     };
167
168     ShibTargetConfig& conf=ShibTargetConfig::getConfig();
169     if (preinit) {
170
171         // initialize the shib-target library
172         conf.setFeatures(
173             ShibTargetConfig::Listener |
174             ShibTargetConfig::Caching |
175             ShibTargetConfig::Metadata |
176             ShibTargetConfig::Trust |
177             ShibTargetConfig::Credentials |
178             ShibTargetConfig::AAP |
179             ShibTargetConfig::GlobalExtensions |
180             (shar_checkonly ? (ShibTargetConfig::LocalExtensions | ShibTargetConfig::RequestMapper) : ShibTargetConfig::Logging)
181             );
182         if (!shar_config)
183             shar_config=getenv("SHIBCONFIG");
184         if (!shar_schemadir)
185             shar_schemadir=getenv("SHIBSCHEMAS");
186         if (!shar_schemadir)
187             shar_schemadir=SHIB_SCHEMAS;
188         if (!shar_config)
189             shar_config=SHIB_CONFIG;
190         if (!conf.init(shar_schemadir) || !conf.load(shar_config)) {
191             fprintf(stderr, "configuration is invalid, see console for specific problems\n");
192             return -2;
193         }
194
195         // If just a test run, bail.
196         if (shar_checkonly) {
197             fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");
198             return 0;
199         }
200         
201         // Build an internal "listener" to handle the work.
202         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::MemoryListenerType,NULL);
203         g_MemoryListener=dynamic_cast<IListener*>(plugin);
204         if (!g_MemoryListener) {
205             delete plugin;
206             fprintf(stderr, "MemoryListener plugin failed to load");
207             conf.shutdown();
208             return -3;
209         }
210
211         const IListener* listener=conf.getINI()->getListener();
212         
213         // Create the SHAR listener socket
214         if (!listener->create(sock)) {
215             delete g_MemoryListener;
216             conf.shutdown();
217             return -4;
218         }
219
220         // Bind to the proper port
221         if (!listener->bind(sock)) {
222             delete g_MemoryListener;
223             conf.shutdown();
224             return -5;
225         }
226
227         // Initialize the SHAR Utilitites
228         SHARUtils::init();
229     }
230     else {
231
232         //_CrtSetAllocHook(MyAllocHook);
233
234         // Run the listener
235         if (!shar_checkonly) {
236             shar_svc_run(sock, ArrayIterator<ShibRPCProtocols>(protos,1));
237             fprintf(stdout,"shar_svc_run returned\n");
238
239             // Finalize the SHAR, close all clients
240             SHARUtils::fini();
241             conf.getINI()->getListener()->close(sock);
242         }
243
244         delete g_MemoryListener;
245         conf.shutdown();
246         fprintf(stdout, "shibd shutdown complete\n");
247     }
248     return 0;
249 }
250
251 #else
252
253 static void term_handler(int arg)
254 {
255     shar_run = 0;
256 }
257
258 static int setup_signals(void)
259 {
260     NDC ndc("setup_signals");
261     
262     struct sigaction sa;
263     memset(&sa, 0, sizeof (sa));
264     sa.sa_handler = SIG_IGN;
265     sa.sa_flags = SA_RESTART;
266
267     if (sigaction(SIGPIPE, &sa, NULL) < 0) {
268         SHARUtils::log_error();
269         return -1;
270     }
271
272     memset(&sa, 0, sizeof (sa));
273     sa.sa_handler = term_handler;
274     sa.sa_flags = SA_RESTART;
275
276     if (sigaction(SIGHUP, &sa, NULL) < 0) {
277         SHARUtils::log_error();
278         return -1;
279     }
280     if (sigaction(SIGINT, &sa, NULL) < 0) {
281         SHARUtils::log_error();
282         return -1;
283     }
284     if (sigaction(SIGQUIT, &sa, NULL) < 0) {
285         SHARUtils::log_error();
286         return -1;
287     }
288     if (sigaction(SIGTERM, &sa, NULL) < 0) {
289         SHARUtils::log_error();
290         return -1;
291     }
292     return 0;
293 }
294
295 static void usage(char* whoami)
296 {
297     fprintf(stderr, "usage: %s [-fcdt]\n", whoami);
298     fprintf(stderr, "  -c\tconfig file to use.\n");
299     fprintf(stderr, "  -d\tschema directory to use.\n");
300     fprintf(stderr, "  -t\tcheck configuration file for problems.\n");
301     fprintf(stderr, "  -f\tforce removal of listener socket.\n");
302     fprintf(stderr, "  -p\tpid file to use.\n");
303     fprintf(stderr, "  -h\tprint this help message.\n");
304     exit(1);
305 }
306
307 static int parse_args(int argc, char* argv[])
308 {
309     int opt;
310
311     while ((opt = getopt(argc, argv, "c:d:p:fth")) > 0) {
312         switch (opt) {
313             case 'c':
314                 shar_config=optarg;
315                 break;
316             case 'd':
317                 shar_schemadir=optarg;
318                 break;
319             case 'f':
320                 unlink_socket = 1;
321                 break;
322             case 't':
323                 shar_checkonly=true;
324                 break;
325             case 'p':
326                 pidfile=optarg;
327                 break;
328             default:
329                 return -1;
330         }
331     }
332     return 0;
333 }
334
335 int main(int argc, char *argv[])
336 {
337     IListener::ShibSocket sock;
338     ShibRPCProtocols protos[] = {
339         { SHIBRPC_PROG, SHIBRPC_VERS_2, shibrpc_prog_2 }
340     };
341
342     if (setup_signals() != 0)
343         return -1;
344
345     if (parse_args(argc, argv) != 0)
346         usage(argv[0]);
347
348     if (!shar_config)
349         shar_config=getenv("SHIBCONFIG");
350     if (!shar_schemadir)
351         shar_schemadir=getenv("SHIBSCHEMAS");
352     if (!shar_schemadir)
353         shar_schemadir=SHIB_SCHEMAS;
354     if (!shar_config)
355         shar_config=SHIB_CONFIG;
356
357     // initialize the shib-target library
358     ShibTargetConfig& conf=ShibTargetConfig::getConfig();
359     conf.setFeatures(
360         ShibTargetConfig::Listener |
361         ShibTargetConfig::Caching |
362         ShibTargetConfig::Metadata |
363         ShibTargetConfig::Trust |
364         ShibTargetConfig::Credentials |
365         ShibTargetConfig::AAP |
366         ShibTargetConfig::GlobalExtensions |
367         (shar_checkonly ? (ShibTargetConfig::LocalExtensions | ShibTargetConfig::RequestMapper) : ShibTargetConfig::Logging)
368         );
369     if (!conf.init(shar_schemadir) || !conf.load(shar_config)) {
370         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
371         return -2;
372     }
373
374     if (shar_checkonly)
375         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");
376     else {
377
378         // Build an internal "listener" to handle the work.
379         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::MemoryListenerType,NULL);
380         g_MemoryListener=dynamic_cast<IListener*>(plugin);
381         if (!g_MemoryListener) {
382             delete plugin;
383             fprintf(stderr, "MemoryListener plugin failed to load");
384             conf.shutdown();
385             return -3;
386         }
387
388         const IListener* listener=conf.getINI()->getListener();
389         
390         // Create the SHAR listener socket
391         if (!listener->create(sock)) {
392             delete g_MemoryListener;
393             conf.shutdown();
394             return -4;
395         }
396     
397         // Bind to the proper port
398         if (!listener->bind(sock, unlink_socket==1)) {
399             delete g_MemoryListener;
400             conf.shutdown();
401             return -5;
402         }
403
404         // Write the pid file
405         if (pidfile) {
406             FILE* pidf = fopen(pidfile, "w");
407             if (pidf) {
408                 fprintf(pidf, "%d\n", getpid());
409                 fclose(pidf);
410             } else {
411                 perror(pidfile);  // keep running though
412             }
413         }
414     
415         // Initialize the SHAR Utilitites
416         SHARUtils::init();
417     
418         // Run the listener
419         shar_svc_run(sock, ArrayIterator<ShibRPCProtocols>(protos,1));
420     
421         /* Finalize the SHAR, close all clients */
422         SHARUtils::fini();
423         fprintf(stderr, "shar utils finalized\n");
424     
425         listener->close(sock);
426         fprintf(stderr, "shib socket closed\n");
427     }
428
429     conf.shutdown();
430     if (pidfile)
431         unlink(pidfile);
432     fprintf(stderr, "shibd shutdown complete\n");
433     return 0;
434 }
435
436 #endif