Fixed bug in proxy where a Yaz_ProxyClient could be owned by
[yazpp-moved-to-github.git] / src / yaz-socket-manager.cpp
1 /*
2  * Copyright (c) 1998-2000, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Log: yaz-socket-manager.cpp,v $
6  * Revision 1.12  2000-10-24 12:29:57  adam
7  * Fixed bug in proxy where a Yaz_ProxyClient could be owned by
8  * two Yaz_Proxy's (fatal).
9  *
10  * Revision 1.11  2000/10/11 11:58:17  adam
11  * Moved header files to include/yaz++. Switched to libtool and automake.
12  * Configure script creates yaz++-config script.
13  *
14  * Revision 1.10  2000/09/08 10:23:42  adam
15  * Added skeleton of yaz-z-server.
16  *
17  * Revision 1.9  2000/08/07 14:19:59  adam
18  * Fixed serious bug regarding timeouts. Improved logging for proxy.
19  *
20  * Revision 1.8  1999/12/06 13:52:45  adam
21  * Modified for new location of YAZ header files. Experimental threaded
22  * operation.
23  *
24  * Revision 1.7  1999/04/28 13:02:08  adam
25  * Added include of string.h.
26  *
27  * Revision 1.6  1999/04/21 12:09:01  adam
28  * Many improvements. Modified to proxy server to work with "sessions"
29  * based on cookies.
30  *
31  * Revision 1.5  1999/04/09 11:46:57  adam
32  * Added object Yaz_Z_Assoc. Much more functional client.
33  *
34  * Revision 1.4  1999/03/23 14:17:57  adam
35  * More work on timeout handling. Work on yaz-client.
36  *
37  * Revision 1.3  1999/02/02 14:01:23  adam
38  * First WIN32 port of YAZ++.
39  *
40  * Revision 1.2  1999/01/28 13:08:48  adam
41  * Yaz_PDU_Assoc better encapsulated. Memory leak fix in
42  * yaz-socket-manager.cc.
43  *
44  * Revision 1.1.1.1  1999/01/28 09:41:07  adam
45  * First implementation of YAZ++.
46  *
47  */
48 #include <assert.h>
49 #ifdef WIN32
50 #include <winsock.h>
51 #else
52 #include <sys/time.h>
53 #include <sys/types.h>
54 #include <unistd.h>
55 #endif
56 #include <errno.h>
57 #include <string.h>
58
59 #include <yaz/log.h>
60 #include <yaz++/yaz-socket-manager.h>
61
62 Yaz_SocketManager::YazSocketEntry **Yaz_SocketManager::lookupObserver(
63     IYazSocketObserver *observer)
64 {
65     YazSocketEntry **se;
66     
67     for (se = &m_observers; *se; se = &(*se)->next)
68         if ((*se)->observer == observer)
69             break;
70     return se;
71 }
72
73 void Yaz_SocketManager::addObserver(int fd, IYazSocketObserver *observer)
74 {
75     YazSocketEntry *se;
76
77     se = *lookupObserver(observer);
78     if (!se)
79     {
80         se = new YazSocketEntry;
81         se->next= m_observers;
82         m_observers = se;
83         se->observer = observer;
84     }
85     se->fd = fd;
86     se->mask = 0;
87     se->last_activity = 0;
88     se->timeout = 0;
89 }
90
91 void Yaz_SocketManager::deleteObserver(IYazSocketObserver *observer)
92 {
93     YazSocketEntry **se = lookupObserver(observer);
94     if (*se)
95     {
96         removeEvent (observer);
97         YazSocketEntry *se_tmp = *se;
98         *se = (*se)->next;
99         delete se_tmp;
100     }
101 }
102
103 void Yaz_SocketManager::deleteObservers()
104 {
105     YazSocketEntry *se = m_observers;
106     
107     while (se)
108     {
109         YazSocketEntry *se_next = se->next;
110         delete se;
111         se = se_next;
112     }
113     m_observers = 0;
114 }
115
116 void Yaz_SocketManager::maskObserver(IYazSocketObserver *observer, int mask)
117 {
118     YazSocketEntry *se;
119
120     se = *lookupObserver(observer);
121     if (se)
122         se->mask = mask;
123 }
124
125 void Yaz_SocketManager::timeoutObserver(IYazSocketObserver *observer,
126                                         unsigned timeout)
127 {
128     YazSocketEntry *se;
129
130     se = *lookupObserver(observer);
131     if (se)
132         se->timeout = timeout;
133 }
134
135 int Yaz_SocketManager::processEvent()
136 {
137     YazSocketEntry *p;
138     YazSocketEvent *event = getEvent();
139     unsigned timeout = 0;
140     logf (m_log, "processEvent");
141     if (event)
142     {
143         event->observer->socketNotify(event->event);
144         delete event;
145         return 1;
146     }
147
148     fd_set in, out, except;
149     int res;
150     int max = 0;
151     int no = 0;
152
153     FD_ZERO(&in);
154     FD_ZERO(&out);
155     FD_ZERO(&except);
156
157     time_t now = time(0);
158     for (p = m_observers; p; p = p->next)
159     {
160         int fd = p->fd;
161         if (p->mask)
162             no++;
163         if (p->mask & YAZ_SOCKET_OBSERVE_READ)
164             FD_SET(fd, &in);
165         if (p->mask & YAZ_SOCKET_OBSERVE_WRITE)
166             FD_SET(fd, &out);
167         if (p->mask & YAZ_SOCKET_OBSERVE_EXCEPT)
168             FD_SET(fd, &except);
169         if (fd > max)
170             max = fd;
171         if (p->timeout)
172         {
173             unsigned timeout_this;
174             timeout_this = p->timeout;
175             if (p->last_activity)
176                 timeout_this -= now - p->last_activity;
177             if (timeout_this < 1)
178                 timeout_this = 1;
179             if (!timeout || timeout_this < timeout)
180                 timeout = timeout_this;
181         }
182     }
183     if (!no)
184     {
185         logf (m_log, "no pending events return 0");
186         if (!m_observers)
187             logf (m_log, "no observers");
188         return 0;
189     }
190
191     struct timeval to;
192     to.tv_sec = timeout;
193     to.tv_usec = 0;
194     
195     logf (m_log, "select pending=%d timeout=%d", no, timeout);
196     while ((res = select(max + 1, &in, &out, &except, timeout ? &to : 0)) < 0)
197         if (errno != EINTR)
198             return -1;
199     now = time(0);
200     for (p = m_observers; p; p = p->next)
201     {
202         int fd = p->fd;
203         int mask = 0;
204         if (FD_ISSET(fd, &in))
205             mask |= YAZ_SOCKET_OBSERVE_READ;
206
207         if (FD_ISSET(fd, &out))
208             mask |= YAZ_SOCKET_OBSERVE_WRITE;
209
210         if (FD_ISSET(fd, &except))
211             mask |= YAZ_SOCKET_OBSERVE_EXCEPT;
212         
213         if (mask)
214         {
215             YazSocketEvent *event = new YazSocketEvent;
216             p->last_activity = now;
217             event->observer = p->observer;
218             event->event = mask;
219             putEvent (event);
220         }
221         else if (p->timeout && p->last_activity && 
222                  now >= p->last_activity + (int) (p->timeout))
223         {
224             YazSocketEvent *event = new YazSocketEvent;
225             logf (LOG_LOG, "timeout now = %ld last_activity=%ld timeout=%d",
226                   now, p->last_activity, p->timeout);
227             p->last_activity = now;
228             event->observer = p->observer;
229             event->event = YAZ_SOCKET_OBSERVE_TIMEOUT;
230             putEvent (event);
231         }
232     }
233     if ((event = getEvent()))
234     {
235         event->observer->socketNotify(event->event);
236         delete event;
237         return 1;
238     }
239     return 0;
240 }
241
242
243 //    n p    n p  ......   n p    n p
244 //   front                        back
245
246 void Yaz_SocketManager::putEvent(YazSocketEvent *event)
247 {
248     // put in back of queue
249     if (m_queue_back)
250     {
251         m_queue_back->prev = event;
252         assert (m_queue_front);
253     }
254     else
255     {
256         assert (!m_queue_front);
257         m_queue_front = event;
258     }
259     event->next = m_queue_back;
260     event->prev = 0;
261     m_queue_back = event;
262 }
263
264 Yaz_SocketManager::YazSocketEvent *Yaz_SocketManager::getEvent()
265 {
266     // get from front of queue
267     YazSocketEvent *event = m_queue_front;
268     if (!event)
269         return 0;
270     assert (m_queue_back);
271     m_queue_front = event->prev;
272     if (m_queue_front)
273     {
274         assert (m_queue_back);
275         m_queue_front->next = 0;
276     }
277     else
278         m_queue_back = 0;
279     return event;
280 }
281
282 void Yaz_SocketManager::removeEvent(IYazSocketObserver *observer)
283 {
284     YazSocketEvent *ev = m_queue_back;
285     while (ev)
286     {
287         YazSocketEvent *ev_next = ev->next;
288         if (observer == ev->observer)
289         {
290             if (ev->prev)
291                 ev->prev->next = ev->next;
292             else
293                 m_queue_back = ev->next;
294             if (ev->next)
295                 ev->next->prev = ev->prev;
296             else
297                 m_queue_front = ev->prev;
298             delete ev;
299         }
300         ev = ev_next;
301     }
302 }
303
304 Yaz_SocketManager::Yaz_SocketManager()
305 {
306     m_observers = 0;
307     m_queue_front = 0;
308     m_queue_back = 0;
309     m_log = LOG_DEBUG;
310 }
311
312 Yaz_SocketManager::~Yaz_SocketManager()
313 {
314     deleteObservers();
315 }