| |
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| |
2 |
| |
3 /* |
| |
4 * $Id: socketmanager.c 2096 2001-07-31 01:00:39Z warmenhoven $ |
| |
5 * |
| |
6 * Copyright (C) 1998-2001, Denis V. Dmitrienko <denis@null.net> and |
| |
7 * Bill Soudan <soudan@kde.org> |
| |
8 * |
| |
9 * This program is free software; you can redistribute it and/or modify |
| |
10 * it under the terms of the GNU General Public License as published by |
| |
11 * the Free Software Foundation; either version 2 of the License, or |
| |
12 * (at your option) any later version. |
| |
13 * |
| |
14 * This program is distributed in the hope that it will be useful, |
| |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
17 * GNU General Public License for more details. |
| |
18 * |
| |
19 * You should have received a copy of the GNU General Public License |
| |
20 * along with this program; if not, write to the Free Software |
| |
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
22 * |
| |
23 */ |
| |
24 |
| |
25 /** |
| |
26 * The icqlib socket manager is a simple socket abstraction layer, which |
| |
27 * supports opening and closing sockets as well as installing handler |
| |
28 * functions for read ready and write ready events. Its purpose is to |
| |
29 * both unify socket handling in icqlib and expose icqlib's socket |
| |
30 * requirements so the library client can assist with socket housekeeping. |
| |
31 * |
| |
32 * Library clients have two options to support icqlib: |
| |
33 * |
| |
34 * 1. Periodically call icq_Main. This will handle all select logic |
| |
35 * internally. Advantage is implementation ease, disadvantage is wasted |
| |
36 * CPU cycles because of polling and poor TCP file transfer performance. |
| |
37 * |
| |
38 * 2. Install a icq_SocketNotify callback, perform your own socket |
| |
39 * management, and notify icqlib using the icq_SocketReady method when |
| |
40 * a socket is ready for reading or writing. Advantage is efficiency, |
| |
41 * disadvantage is extra code. |
| |
42 * |
| |
43 */ |
| |
44 |
| |
45 #include <stdlib.h> |
| |
46 |
| |
47 #ifdef _WIN32 |
| |
48 #include <winsock.h> |
| |
49 #endif |
| |
50 |
| |
51 #include "socketmanager.h" |
| |
52 |
| |
53 icq_List *icq_SocketList = NULL; |
| |
54 fd_set icq_FdSets[ICQ_SOCKET_MAX]; |
| |
55 int icq_MaxSocket; |
| |
56 |
| |
57 void (*icq_SocketNotify)(int socket_fd, int type, int status); |
| |
58 |
| |
59 /** |
| |
60 * Creates a new socket using the operating system's socket creation |
| |
61 * facility. |
| |
62 */ |
| |
63 int icq_SocketNew(int domain, int type, int protocol) |
| |
64 { |
| |
65 int s = socket(domain, type, protocol); |
| |
66 |
| |
67 icq_SocketAlloc(s); |
| |
68 |
| |
69 return s; |
| |
70 } |
| |
71 |
| |
72 |
| |
73 /** |
| |
74 * Creates a new socket by accepting a connection from a listening |
| |
75 * socket. |
| |
76 */ |
| |
77 int icq_SocketAccept(int listens, struct sockaddr *addr, socklen_t *addrlen) |
| |
78 { |
| |
79 int s = accept(listens, addr, addrlen); |
| |
80 |
| |
81 icq_SocketAlloc(s); |
| |
82 |
| |
83 return s; |
| |
84 } |
| |
85 |
| |
86 /** |
| |
87 * Creates a new icq_Socket structure, and appends it to the |
| |
88 * socketmanager's global socket list. |
| |
89 */ |
| |
90 void icq_SocketAlloc(int s) |
| |
91 { |
| |
92 if (s != -1) |
| |
93 { |
| |
94 icq_Socket *psocket = (icq_Socket *)malloc(sizeof(icq_Socket)); |
| |
95 int i; |
| |
96 psocket->socket = s; |
| |
97 |
| |
98 for (i=0; i<ICQ_SOCKET_MAX; i++) |
| |
99 psocket->handlers[i] = NULL; |
| |
100 |
| |
101 icq_ListEnqueue(icq_SocketList, psocket); |
| |
102 } |
| |
103 } |
| |
104 |
| |
105 /** |
| |
106 * Closes a socket. This function will notify the library client |
| |
107 * through the icq_SocketNotify callback if the socket had an installed |
| |
108 * read or write handler. |
| |
109 */ |
| |
110 int icq_SocketDelete(int socket_fd) |
| |
111 { |
| |
112 #ifdef _WIN32 |
| |
113 int result = closesocket(socket_fd); |
| |
114 #else |
| |
115 int result = close(socket_fd); |
| |
116 #endif |
| |
117 |
| |
118 if (result != -1) |
| |
119 { |
| |
120 icq_Socket *s = icq_FindSocket(socket_fd); |
| |
121 int i; |
| |
122 |
| |
123 /* uninstall all handlers - this will take care of notifing library |
| |
124 * client */ |
| |
125 for (i=0; i<ICQ_SOCKET_MAX; i++) |
| |
126 { |
| |
127 if (s->handlers[i]) |
| |
128 icq_SocketSetHandler(s->socket, i, NULL, NULL); |
| |
129 } |
| |
130 |
| |
131 icq_ListRemove(icq_SocketList, s); |
| |
132 free(s); |
| |
133 } |
| |
134 |
| |
135 return result; |
| |
136 } |
| |
137 |
| |
138 /** |
| |
139 * Installs a socket event handler. The handler will be called when |
| |
140 * the socket is ready for reading or writing, depending on the type |
| |
141 * which should be either ICQ_SOCKET_READ or ICQ_SOCKET_WRITE. In |
| |
142 * addition, user data can be passed to the callback function through |
| |
143 * the data member. |
| |
144 */ |
| |
145 void icq_SocketSetHandler(int socket_fd, int type, icq_SocketHandler handler, |
| |
146 void *data) |
| |
147 { |
| |
148 icq_Socket *s = icq_FindSocket(socket_fd); |
| |
149 if (s) |
| |
150 { |
| |
151 s->data[type] = data; |
| |
152 s->handlers[type] = handler; |
| |
153 if (icq_SocketNotify) |
| |
154 (*icq_SocketNotify)(socket_fd, type, handler ? 1 : 0); |
| |
155 } |
| |
156 } |
| |
157 |
| |
158 /** |
| |
159 * Handles a socket ready event by calling the installed callback |
| |
160 * function, if any. |
| |
161 */ |
| |
162 void icq_SocketReady(icq_Socket *s, int type) |
| |
163 { |
| |
164 if (s && s->handlers[type]) |
| |
165 { |
| |
166 (*s->handlers[type])(s->data[type]); |
| |
167 } |
| |
168 } |
| |
169 |
| |
170 void icq_HandleReadySocket(int socket_fd, int type) |
| |
171 { |
| |
172 icq_SocketReady(icq_FindSocket(socket_fd), type); |
| |
173 } |
| |
174 |
| |
175 int _icq_SocketBuildFdSets(void *p, va_list data) |
| |
176 { |
| |
177 icq_Socket *s = p; |
| |
178 int i; |
| |
179 (void)data; |
| |
180 |
| |
181 for (i=0; i<ICQ_SOCKET_MAX; i++) |
| |
182 if (s->handlers[i]) { |
| |
183 FD_SET(s->socket, &(icq_FdSets[i])); |
| |
184 if (s->socket > icq_MaxSocket) |
| |
185 icq_MaxSocket = s->socket; |
| |
186 } |
| |
187 |
| |
188 return 0; /* traverse entire icq_List */ |
| |
189 } |
| |
190 |
| |
191 void icq_SocketBuildFdSets() |
| |
192 { |
| |
193 int i; |
| |
194 |
| |
195 /* clear fdsets */ |
| |
196 for (i=0; i<ICQ_SOCKET_MAX; i++) |
| |
197 FD_ZERO(&(icq_FdSets[i])); |
| |
198 |
| |
199 icq_MaxSocket = 0; |
| |
200 |
| |
201 /* build fd lists for open sockets */ |
| |
202 (void)icq_ListTraverse(icq_SocketList, _icq_SocketBuildFdSets); |
| |
203 } |
| |
204 |
| |
205 int _icq_SocketHandleReady(void *p, va_list data) |
| |
206 { |
| |
207 icq_Socket *s = p; |
| |
208 int i; |
| |
209 (void)data; |
| |
210 |
| |
211 for (i=0; i<ICQ_SOCKET_MAX; i++) |
| |
212 if (FD_ISSET(s->socket, &(icq_FdSets[i]))) { |
| |
213 icq_SocketReady(s, i); |
| |
214 } |
| |
215 |
| |
216 return 0; /* traverse entire icq_List */ |
| |
217 } |
| |
218 |
| |
219 void icq_SocketPoll() |
| |
220 { |
| |
221 struct timeval tv; |
| |
222 |
| |
223 icq_SocketBuildFdSets(); |
| |
224 |
| |
225 tv.tv_sec = 0; tv.tv_usec = 0; |
| |
226 |
| |
227 /* determine which sockets require maintenance */ |
| |
228 select(icq_MaxSocket+1, &(icq_FdSets[ICQ_SOCKET_READ]), |
| |
229 &(icq_FdSets[ICQ_SOCKET_WRITE]), NULL, &tv); |
| |
230 |
| |
231 /* handle ready sockets */ |
| |
232 (void)icq_ListTraverse(icq_SocketList, _icq_SocketHandleReady); |
| |
233 } |
| |
234 |
| |
235 int _icq_FindSocket(void *p, va_list data) |
| |
236 { |
| |
237 int socket_fd = va_arg(data, int); |
| |
238 return (((icq_Socket *)p)->socket == socket_fd); |
| |
239 } |
| |
240 |
| |
241 icq_Socket *icq_FindSocket(int socket_fd) |
| |
242 { |
| |
243 return icq_ListTraverse(icq_SocketList, _icq_FindSocket, socket_fd); |
| |
244 } |
| |
245 |
| |
246 |