Reduce catch all handlers, and make them optional.
[shibboleth/cpp-sp.git] / shar / shar-utils.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-utils.cpp -- utility functions for the SHAR
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 #ifndef FD_SETSIZE
33 # define FD_SETSIZE 1024
34 #endif
35
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 # include <sys/select.h>
39 #endif
40
41 #include <stdio.h>
42 #include <errno.h>
43 #include <signal.h>
44
45 #include "shar-utils.h"
46
47 #include <shib/shib-threads.h>
48
49 // Deal with inadequate Sun RPC libraries
50
51 #if !HAVE_DECL_SVCFD_CREATE
52   extern "C" SVCXPRT* svcfd_create(int, u_int, u_int);
53 #endif
54
55 #ifndef HAVE_WORKING_SVC_DESTROY
56 struct tcp_conn {  /* kept in xprt->xp_p1 */
57     enum xprt_stat strm_stat;
58     u_long x_id;
59     XDR xdrs;
60     char verf_body[MAX_AUTH_BYTES];
61 };
62 #endif
63
64 using namespace std;
65 using namespace saml;
66 using namespace shibboleth;
67 using namespace shibtarget;
68 using namespace shibd::logging;
69
70 namespace {
71   map<IListener::ShibSocket,Thread*> children;
72   Mutex*        child_lock = NULL;
73   CondWait*     child_wait = NULL;
74   bool          running;
75 };
76
77 void* shar_client_thread (void* arg)
78 {
79   SharChild* child = (SharChild*)arg;
80
81   // First, let's block all signals
82   Thread::mask_all_signals();
83
84   ShibTargetConfig::getConfig().getINI()->getSessionCache()->thread_init();
85   ShibTargetConfig::getConfig().getINI()->getReplayCache()->thread_init();
86
87   // the run the child until they exit.
88   child->run();
89
90   ShibTargetConfig::getConfig().getINI()->getSessionCache()->thread_end();
91   ShibTargetConfig::getConfig().getINI()->getReplayCache()->thread_end();
92
93   // now we can clean up and exit the thread.
94   delete child;
95   return NULL;
96 }
97
98 SharChild::SharChild(IListener::ShibSocket& s, const Iterator<ShibRPCProtocols>& protos) : sock(s), child(NULL)
99 {
100   protos.reset();
101   while (protos.hasNext())
102     v_protos.push_back(protos.next());
103   
104   // Create the child thread
105   child = Thread::create(shar_client_thread, (void*)this);
106   child->detach();
107 }
108
109 SharChild::~SharChild()
110 {
111   // Then lock the children map, remove this socket/thread, signal waiters, and return
112   child_lock->lock();
113   children.erase(sock);
114   child_lock->unlock();
115   child_wait->signal();
116   
117   delete child;
118 }
119
120 void SharChild::run()
121 {
122     // Before starting up, make sure we fully "own" this socket.
123     child_lock->lock();
124     while (children.find(sock)!=children.end())
125         child_wait->wait(child_lock);
126     children[sock] = child;
127     child_lock->unlock();
128     
129   if (!svc_create())
130    return;
131
132   fd_set readfds;
133   struct timeval tv = { 0, 0 };
134
135   while(running && FD_ISSET(sock, &svc_fdset)) {
136     FD_ZERO(&readfds);
137     FD_SET(sock, &readfds);
138     tv.tv_sec = 1;
139
140     switch (select(sock+1, &readfds, 0, 0, &tv)) {
141 #ifdef WIN32
142     case SOCKET_ERROR:
143 #else
144     case -1:
145 #endif
146       if (errno == EINTR) continue;
147       SHARUtils::log_error();
148       Category::getInstance("SHAR.SharChild").error("select() on incoming request socket (%u) returned error",sock);
149       return;
150
151     case 0:
152       break;
153
154     default:
155       svc_getreqset(&readfds);
156     }
157   }
158 }
159
160 bool SharChild::svc_create()
161 {
162   /* Wrap an RPC Service around the new connection socket. */
163   SVCXPRT* transp = svcfd_create(sock, 0, 0);
164   if (!transp) {
165     NDC ndc("svc_create");
166     Category::getInstance("SHAR.SharChild").error("cannot create RPC listener");
167     return false;
168   }
169
170   /* Register the SHIBRPC RPC Program */
171   Iterator<ShibRPCProtocols> i(v_protos);
172   while (i.hasNext()) {
173     const ShibRPCProtocols& p=i.next();
174     if (!svc_register (transp, p.prog, p.vers, p.dispatch, 0)) {
175 #ifdef HAVE_WORKING_SVC_DESTROY
176       svc_destroy(transp);
177 #else
178       /* we have to inline svc_destroy because we can't pass in the xprt variable */
179       struct tcp_conn *cd = (struct tcp_conn *)transp->xp_p1;
180       xprt_unregister(transp);
181       close(transp->xp_sock);
182       if (transp->xp_port != 0) {
183         /* a rendezvouser socket */
184         transp->xp_port = 0;
185       } else {
186         /* an actual connection socket */
187         XDR_DESTROY(&(cd->xdrs));
188       }
189       mem_free((caddr_t)cd, sizeof(struct tcp_conn));
190       mem_free((caddr_t)transp, sizeof(SVCXPRT));
191 #endif
192       NDC ndc("svc_create");
193       Category::getInstance("SHAR.SharChild").error("cannot register RPC program");
194       return false;
195     }
196   }
197   return true;
198 }
199
200 void SHARUtils::log_error()
201 {
202 #ifdef WIN32
203     int rc=WSAGetLastError();
204 #else
205     int rc=errno;
206 #endif
207 #ifdef HAVE_STRERROR_R
208     char buf[256];
209     strerror_r(rc,buf,sizeof(buf));
210     buf[255]=0;
211     Category::getInstance("SHAR.SHARUtils").error("system call resulted in error (%d): %s",rc,buf);
212 #else
213     Category::getInstance("SHAR.SHARUtils").error("system call resulted in error (%d): %s",rc,strerror(rc));
214 #endif
215 }
216
217 void SHARUtils::init()
218 {
219   child_lock = Mutex::create();
220   child_wait = CondWait::create();
221   running = true;
222 }
223
224 void SHARUtils::fini()
225 {
226   running = false;
227
228   // wait for all children to exit.
229   child_lock->lock();
230   while (children.size())
231     child_wait->wait(child_lock);
232   child_lock->unlock();
233
234   // Ok, we're done.
235   delete child_wait;
236   child_wait = NULL;
237   delete child_lock;
238   child_lock = NULL;
239 }