https://issues.shibboleth.net/jira/browse/SSPCPP-202
[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 bool shar_version = false;
63 static int unlink_socket = 0;
64 const char* pidfile = NULL;
65
66 static bool new_connection(IListener::ShibSocket& listener, const Iterator<ShibRPCProtocols>& protos)
67 {
68     IListener::ShibSocket sock;
69
70     // Accept the connection.
71     if (!ShibTargetConfig::getConfig().getINI()->getListener()->accept(listener, sock))
72         return false;
73
74     // We throw away the result because the children manage themselves...
75     try {
76         new SharChild(sock,protos);
77     }
78     catch (...) {
79         saml::NDC ndc("new_connection");
80         Category& log=Category::getInstance("shibd");
81         log.crit("error starting new child thread to service request");
82         return false;
83     }
84     return true;
85 }
86
87 static void shar_svc_run(IListener::ShibSocket& listener, const Iterator<ShibRPCProtocols>& protos)
88 {
89 #ifdef _DEBUG
90     saml::NDC ndc("shar_svc_run");
91 #endif
92     Category& log=Category::getInstance("shibd");
93
94     while (shar_run) {
95         fd_set readfds;
96         FD_ZERO(&readfds);
97         FD_SET(listener, &readfds);
98         struct timeval tv = { 0, 0 };
99         tv.tv_sec = 5;
100     
101         switch (select(listener + 1, &readfds, 0, 0, &tv)) {
102 #ifdef WIN32
103             case SOCKET_ERROR:
104 #else
105             case -1:
106 #endif
107                 if (errno == EINTR) continue;
108                 SHARUtils::log_error();
109                 log.error("select() on main listener socket failed");
110                 return;
111         
112             case 0:
113                 continue;
114         
115             default:
116                 if (!new_connection(listener, protos))
117                     log.crit("new_connection failed");
118         }
119     }
120     log.info("shar_svc_run ended");
121 }
122
123 #ifdef WIN32
124
125 //#include <CRTDBG.H>
126
127 #define nNoMansLandSize 4
128 typedef struct _CrtMemBlockHeader
129 {
130         struct _CrtMemBlockHeader * pBlockHeaderNext;
131         struct _CrtMemBlockHeader * pBlockHeaderPrev;
132         char *                      szFileName;
133         int                         nLine;
134         size_t                      nDataSize;
135         int                         nBlockUse;
136         long                        lRequest;
137         unsigned char               gap[nNoMansLandSize];
138         /* followed by:
139          *  unsigned char           data[nDataSize];
140          *  unsigned char           anotherGap[nNoMansLandSize];
141          */
142 } _CrtMemBlockHeader;
143
144 /*
145 int MyAllocHook(int nAllocType, void *pvData,
146       size_t nSize, int nBlockUse, long lRequest,
147       const unsigned char * szFileName, int nLine)
148 {
149     if ( nBlockUse == _CRT_BLOCK )
150       return( TRUE );
151     if (nAllocType == _HOOK_FREE) {
152         _CrtMemBlockHeader* ptr = (_CrtMemBlockHeader*)(((_CrtMemBlockHeader *)pvData)-1);
153         if (ptr->nDataSize == 8192)
154             fprintf(stderr,"free  request %u size %u\n", ptr->lRequest, ptr->nDataSize);
155     }
156     else if (nAllocType == _HOOK_ALLOC && nSize == 8192)
157         fprintf(stderr,"%s request %u size %u\n", ((nAllocType == _HOOK_ALLOC) ? "alloc" : "realloc"), lRequest, nSize);
158     return (TRUE);
159 }
160 */
161
162 int real_main(int preinit)
163 {
164     static IListener::ShibSocket sock;
165     ShibRPCProtocols protos[1] = {
166         { SHIBRPC_PROG, SHIBRPC_VERS_2, shibrpc_prog_2 }
167     };
168
169     ShibTargetConfig& conf=ShibTargetConfig::getConfig();
170     if (preinit) {
171
172         // initialize the shib-target library
173         conf.setFeatures(
174             ShibTargetConfig::Listener |
175             ShibTargetConfig::Caching |
176             ShibTargetConfig::Metadata |
177             ShibTargetConfig::Trust |
178             ShibTargetConfig::Credentials |
179             ShibTargetConfig::AAP |
180             ShibTargetConfig::GlobalExtensions |
181             (shar_checkonly ? (ShibTargetConfig::LocalExtensions | ShibTargetConfig::RequestMapper) : ShibTargetConfig::Logging)
182             );
183         if (!shar_config)
184             shar_config=getenv("SHIBCONFIG");
185         if (!shar_schemadir)
186             shar_schemadir=getenv("SHIBSCHEMAS");
187         if (!shar_schemadir)
188             shar_schemadir=SHIB_SCHEMAS;
189         if (!shar_config)
190             shar_config=SHIB_CONFIG;
191         if (!conf.init(shar_schemadir) || !conf.load(shar_config)) {
192             fprintf(stderr, "configuration is invalid, see console for specific problems\n");
193             return -2;
194         }
195
196         // If just a test run, bail.
197         if (shar_checkonly) {
198             fprintf(stdout, "overall configuration is loadable, check console for non-fatal problems\n");
199             return 0;
200         }
201         
202         // Build an internal "listener" to handle the work.
203         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::MemoryListenerType,NULL);
204         g_MemoryListener=dynamic_cast<IListener*>(plugin);
205         if (!g_MemoryListener) {
206             delete plugin;
207             fprintf(stderr, "MemoryListener plugin failed to load");
208             conf.shutdown();
209             return -3;
210         }
211
212         const IListener* listener=conf.getINI()->getListener();
213         
214         // Create the SHAR listener socket
215         if (!listener->create(sock)) {
216             delete g_MemoryListener;
217             conf.shutdown();
218             return -4;
219         }
220
221         // Bind to the proper port
222         if (!listener->bind(sock)) {
223             delete g_MemoryListener;
224             conf.shutdown();
225             return -5;
226         }
227
228         // Initialize the SHAR Utilitites
229         SHARUtils::init();
230     }
231     else {
232
233         //_CrtSetAllocHook(MyAllocHook);
234
235         // Run the listener
236         if (!shar_checkonly) {
237             shar_svc_run(sock, ArrayIterator<ShibRPCProtocols>(protos,1));
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     }
247     return 0;
248 }
249
250 #else
251
252 static void term_handler(int arg)
253 {
254     shar_run = 0;
255 }
256
257 static int setup_signals(void)
258 {
259     NDC ndc("setup_signals");
260     
261     struct sigaction sa;
262     memset(&sa, 0, sizeof (sa));
263     sa.sa_handler = SIG_IGN;
264     sa.sa_flags = SA_RESTART;
265
266     if (sigaction(SIGPIPE, &sa, NULL) < 0) {
267         SHARUtils::log_error();
268         return -1;
269     }
270
271     memset(&sa, 0, sizeof (sa));
272     sa.sa_handler = term_handler;
273     sa.sa_flags = SA_RESTART;
274
275     if (sigaction(SIGHUP, &sa, NULL) < 0) {
276         SHARUtils::log_error();
277         return -1;
278     }
279     if (sigaction(SIGINT, &sa, NULL) < 0) {
280         SHARUtils::log_error();
281         return -1;
282     }
283     if (sigaction(SIGQUIT, &sa, NULL) < 0) {
284         SHARUtils::log_error();
285         return -1;
286     }
287     if (sigaction(SIGTERM, &sa, NULL) < 0) {
288         SHARUtils::log_error();
289         return -1;
290     }
291     return 0;
292 }
293
294 static void usage(char* whoami)
295 {
296     fprintf(stderr, "usage: %s [-fcdt]\n", whoami);
297     fprintf(stderr, "  -c\tconfig file to use.\n");
298     fprintf(stderr, "  -d\tschema directory to use.\n");
299     fprintf(stderr, "  -t\tcheck configuration file for problems.\n");
300     fprintf(stderr, "  -f\tforce removal of listener socket.\n");
301     fprintf(stderr, "  -p\tpid file to use.\n");
302     fprintf(stderr, "  -v\tprint software version.\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:ftvh")) > 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 'v':
326                 shar_version=true;
327                 break;
328             case 'p':
329                 pidfile=optarg;
330                 break;
331             default:
332                 return -1;
333         }
334     }
335     return 0;
336 }
337
338 int main(int argc, char *argv[])
339 {
340     IListener::ShibSocket sock;
341     ShibRPCProtocols protos[] = {
342         { SHIBRPC_PROG, SHIBRPC_VERS_2, shibrpc_prog_2 }
343     };
344
345     if (parse_args(argc, argv) != 0)
346         usage(argv[0]);
347     else if (shar_version) {
348         fprintf(stdout, PACKAGE_STRING);
349         return 0;
350     }
351
352     if (setup_signals() != 0)
353         return -1;
354
355     if (!shar_config)
356         shar_config=getenv("SHIBCONFIG");
357     if (!shar_schemadir)
358         shar_schemadir=getenv("SHIBSCHEMAS");
359     if (!shar_schemadir)
360         shar_schemadir=SHIB_SCHEMAS;
361     if (!shar_config)
362         shar_config=SHIB_CONFIG;
363
364     // initialize the shib-target library
365     ShibTargetConfig& conf=ShibTargetConfig::getConfig();
366     conf.setFeatures(
367         ShibTargetConfig::Listener |
368         ShibTargetConfig::Caching |
369         ShibTargetConfig::Metadata |
370         ShibTargetConfig::Trust |
371         ShibTargetConfig::Credentials |
372         ShibTargetConfig::AAP |
373         ShibTargetConfig::GlobalExtensions |
374         (shar_checkonly ? (ShibTargetConfig::LocalExtensions | ShibTargetConfig::RequestMapper) : ShibTargetConfig::Logging)
375         );
376     if (!conf.init(shar_schemadir) || !conf.load(shar_config)) {
377         fprintf(stderr, "configuration is invalid, check console for specific problems\n");
378         return -2;
379     }
380
381     if (shar_checkonly)
382         fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n");
383     else {
384
385         // Build an internal "listener" to handle the work.
386         IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(shibtarget::XML::MemoryListenerType,NULL);
387         g_MemoryListener=dynamic_cast<IListener*>(plugin);
388         if (!g_MemoryListener) {
389             delete plugin;
390             fprintf(stderr, "MemoryListener plugin failed to load");
391             conf.shutdown();
392             return -3;
393         }
394
395         const IListener* listener=conf.getINI()->getListener();
396         
397         // Create the SHAR listener socket
398         if (!listener->create(sock)) {
399             delete g_MemoryListener;
400             conf.shutdown();
401             return -4;
402         }
403     
404         // Bind to the proper port
405         if (!listener->bind(sock, unlink_socket==1)) {
406             delete g_MemoryListener;
407             conf.shutdown();
408             return -5;
409         }
410
411         // Write the pid file
412         if (pidfile) {
413             FILE* pidf = fopen(pidfile, "w");
414             if (pidf) {
415                 fprintf(pidf, "%d\n", getpid());
416                 fclose(pidf);
417             } else {
418                 perror(pidfile);  // keep running though
419             }
420         }
421     
422         // Initialize the SHAR Utilitites
423         SHARUtils::init();
424     
425         // Run the listener
426         shar_svc_run(sock, ArrayIterator<ShibRPCProtocols>(protos,1));
427     
428         /* Finalize the SHAR, close all clients */
429         SHARUtils::fini();
430     
431         listener->close(sock);
432     }
433
434     conf.shutdown();
435     if (pidfile)
436         unlink(pidfile);
437     return 0;
438 }
439
440 #endif