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