Consolidated exception and status handling into a single class.
[shibboleth/sp.git] / shib-target / shib-rpchandle.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /*
51  * shib-rpchandle.cpp -- the RPC Handle abstraction
52  *
53  * Created by:  Derek Atkins <derek@ihtfp.com>
54  *
55  * $Id$
56  */
57
58 #include "internal.h"
59
60 #ifdef HAVE_UNISTD_H
61 # include <unistd.h>
62 #endif
63 #include <shib/shib-threads.h>
64
65 #include <stdexcept>
66 #include <log4cpp/Category.hh>
67
68 using namespace std;
69 using namespace log4cpp;
70 using namespace saml;
71 using namespace shibboleth;
72 using namespace shibtarget;
73
74 RPCHandle::RPCHandle() : m_clnt(NULL), m_sock((IListener::ShibSocket)0), log(&(log4cpp::Category::getInstance("shibtarget.RPCHandle")))
75 {
76     log->debug("New RPCHandle created: %p", this);
77 }
78
79 RPCHandle::~RPCHandle()
80 {
81     log->debug("Destroying RPC Handle: %p", this);
82     disconnect();
83 }
84
85 void RPCHandle::disconnect()
86 {
87     if (m_clnt) {
88         clnt_destroy(m_clnt);
89         m_clnt=NULL;
90         IConfig* conf=ShibTargetConfig::getConfig().getINI();
91         if (conf) {
92             Locker locker(conf);
93             conf->getListener()->close(m_sock);
94             m_sock=(IListener::ShibSocket)0;
95         }
96         else
97             m_sock=(IListener::ShibSocket)0;
98     }
99 }
100
101 CLIENT* RPCHandle::connect()
102 {
103 #ifdef _DEBUG
104     saml::NDC ndc("connect");
105 #endif
106     if (m_clnt) {
107         log->debug("returning existing connection: %p -> %p", this, m_clnt);
108         return m_clnt;
109     }
110
111     log->debug("trying to connect to listener");
112
113     IListener::ShibSocket sock;
114     IConfig* conf=ShibTargetConfig::getConfig().getINI();
115     Locker locker(conf);
116     const IListener* listener=conf->getListener();
117     if (!listener->create(sock)) {
118         log->error("cannot create socket");
119         throw ListenerException("Cannot create socket");
120     }
121
122     bool connected = false;
123     int num_tries = 3;
124
125     for (int i = num_tries-1; i >= 0; i--) {
126         if (listener->connect(sock)) {
127             connected = true;
128             break;
129         }
130     
131         log->warn("cannot connect %p to listener...%s", this, (i > 0 ? "retrying" : ""));
132
133         if (i) {
134 #ifdef WIN32
135             Sleep(2000*(num_tries-i));
136 #else
137             sleep(2*(num_tries-i));
138 #endif
139         }
140     }
141
142     if (!connected) {
143         log->crit("listener unavailable, failing");
144         listener->close(sock);
145         throw ListenerException("Cannot connect to listener process, a site adminstrator should be notified.");
146     }
147
148     CLIENT *clnt = listener->getClientHandle(sock, SHIBRPC_PROG, SHIBRPC_VERS_2);
149     if (!clnt) {
150         const char* rpcerror = clnt_spcreateerror("RPCHandle::connect");
151         log->crit("RPC failed for %p: %s", this, rpcerror);
152         listener->close(sock);
153         throw ListenerException(rpcerror);
154     }
155
156     // Set the RPC timeout to a fairly high value...
157     struct timeval tv;
158     tv.tv_sec = 300;    /* change timeout to 5 minutes */
159     tv.tv_usec = 0;     /* this should always be set  */
160     clnt_control(clnt, CLSET_TIMEOUT, (char*)&tv);
161
162     m_clnt = clnt;
163     m_sock = sock;
164
165     log->debug("success: %p -> %p", this, m_clnt);
166     return m_clnt;
167 }
168
169 RPCHandlePool::~RPCHandlePool()
170 {
171     while (!m_pool.empty()) {
172         delete m_pool.top();
173         m_pool.pop();
174     }
175 }
176
177 RPCHandle* RPCHandlePool::get()
178 {
179     m_lock->lock();
180     if (m_pool.empty()) {
181         m_lock->unlock();
182         return new RPCHandle();
183     }
184     RPCHandle* ret=m_pool.top();
185     m_pool.pop();
186     m_lock->unlock();
187     return ret;
188 }
189
190 void RPCHandlePool::put(RPCHandle* handle)
191 {
192     m_lock->lock();
193     m_pool.push(handle);
194     m_lock->unlock();
195 }
196
197 RPC::RPC() : m_pool(dynamic_cast<STConfig&>(ShibTargetConfig::getConfig()).getRPCHandlePool())
198 {
199     m_handle=m_pool.get();
200 }