6248c1ea2fb1b51831eea7d8f1c4e58b776fb93b
[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
238             // Finalize the SHAR, close all clients
239             SHARUtils::fini();
240             conf.getINI()->getListener()->close(sock);
241         }
242
243         delete g_MemoryListener;
244         conf.shutdown();
245     }
246     return 0;
247 }
248
249 #else
250
251 static void term_handler(int arg)
252 {
253     shar_run = 0;
254 }
255
256 static int setup_signals(void)
257 {
258     NDC ndc("setup_signals");
259     
260     struct sigaction sa;
261     memset(&sa, 0, sizeof (sa));
262     sa.sa_handler = SIG_IGN;
263     sa.sa_flags = SA_RESTART;
264
265     if (sigaction(SIGPIPE, &sa, NULL) < 0) {
266         SHARUtils::log_error();
267         return -1;
268     }
269
270     memset(&sa, 0, sizeof (sa));
271     sa.sa_handler = term_handler;
272     sa.sa_flags = SA_RESTART;
273
274     if (sigaction(SIGHUP, &sa, NULL) < 0) {
275         SHARUtils::log_error();
276         return -1;
277     }
278     if (sigaction(SIGINT, &sa, NULL) < 0) {
279         SHARUtils::log_error();
280         return -1;
281     }
282     if (sigaction(SIGQUIT, &sa, NULL) < 0) {
283         SHARUtils::log_error();
284         return -1;
285     }
286     if (sigaction(SIGTERM, &sa, NULL) < 0) {
287         SHARUtils::log_error();
288         return -1;
289     }
290     return 0;
291 }
292
293 static void usage(char* whoami)
294 {
295     fprintf(stderr, "usage: %s [-fcdt]\n", whoami);
296     fprintf(stderr, "  -c\tconfig file to use.\n");
297     fprintf(stderr, "  -d\tschema directory to use.\n");
298     fprintf(stderr, "  -t\tcheck configuration file for problems.\n");
299     fprintf(stderr, "  -f\tforce removal of listener socket.\n");
300     fprintf(stderr, "  -p\tpid file to use.\n");
301     fprintf(stderr, "  -h\tprint this help message.\n");
302     exit(1);
303 }
304
305 static int parse_args(int argc, char* argv[])
306 {
307     int opt;
308
309     while ((opt = getopt(argc, argv, "c:d:p:fth")) > 0) {
310         switch (opt) {
311             case 'c':
312                 shar_config=optarg;
313                 break;
314             case 'd':
315                 shar_schemadir=optarg;
316                 break;
317             case 'f':
318                 unlink_socket = 1;
319                 break;
320             case 't':
321                 shar_checkonly=true;
322                 break;
323             case 'p':
324                 pidfile=optarg;
325                 break;
326             default:
327                 return -1;
328         }
329     }
330     return 0;
331 }
332
333 int main(int argc, char *argv[])
334 {
335     IListener::ShibSocket sock;
336     ShibRPCProtocols protos[] = {
337         { SHIBRPC_PROG, SHIBRPC_VERS_2, shibrpc_prog_2 }
338     };
339
340     if (setup_signals() != 0)
341         return -1;
342
343     if (parse_args(argc, argv) != 0)
344         usage(argv[0]);
345
346     if (!shar_config)
347         shar_config=getenv("SHIBCONFIG");
348     if (!shar_schemadir)
349         shar_schemadir=getenv("SHIBSCHEMAS");
350     if (!shar_schemadir)
351         shar_schemadir=SHIB_SCHEMAS;
352     if (!shar_config)
353         shar_config=SHIB_CONFIG;
354
355     // initialize the shib-target library
356     ShibTargetConfig& conf=ShibTargetConfig::getConfig();
357     conf.setFeatures(
358         ShibTargetConfig::Listener |
359         ShibTargetConfig::Caching |
360         ShibTargetConfig::Metadata |
361         ShibTargetConfig::Trust |
362         ShibTargetConfig::Credentials |
363         ShibTargetConfig::AAP |
364         ShibTargetConfig::GlobalExtensions |
365         (shar_checkonly ? (ShibTargetConfig::LocalExtensions | ShibTargetConfig::RequestMapper) : ShibTargetConfig::Logging)
366         );
367     if (!conf.init(shar_schemadir) || !conf.load(shar_config)) {
368         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
369         return -2;
370     }
371
372     if (shar_checkonly)
373         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");
374     else {
375
376         // Build an internal "listener" to handle the work.
377         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::MemoryListenerType,NULL);
378         g_MemoryListener=dynamic_cast<IListener*>(plugin);
379         if (!g_MemoryListener) {
380             delete plugin;
381             fprintf(stderr, "MemoryListener plugin failed to load");
382             conf.shutdown();
383             return -3;
384         }
385
386         const IListener* listener=conf.getINI()->getListener();
387         
388         // Create the SHAR listener socket
389         if (!listener->create(sock)) {
390             delete g_MemoryListener;
391             conf.shutdown();
392             return -4;
393         }
394     
395         // Bind to the proper port
396         if (!listener->bind(sock, unlink_socket==1)) {
397             delete g_MemoryListener;
398             conf.shutdown();
399             return -5;
400         }
401
402         // Write the pid file
403         if (pidfile) {
404             FILE* pidf = fopen(pidfile, "w");
405             if (pidf) {
406                 fprintf(pidf, "%d\n", getpid());
407                 fclose(pidf);
408             } else {
409                 perror(pidfile);  // keep running though
410             }
411         }
412     
413         // Initialize the SHAR Utilitites
414         SHARUtils::init();
415     
416         // Run the listener
417         shar_svc_run(sock, ArrayIterator<ShibRPCProtocols>(protos,1));
418     
419         /* Finalize the SHAR, close all clients */
420         SHARUtils::fini();
421     
422         listener->close(sock);
423     }
424
425     conf.shutdown();
426     if (pidfile)
427         unlink(pidfile);
428     return 0;
429 }
430
431 #endif