]>
Commit | Line | Data |
---|---|---|
52e1d7e2 | 1 | |
30a4f2a8 | 2 | /* |
1a8f5ed6 | 3 | * $Id: comm.cc,v 1.167 1997/06/18 16:00:09 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 5 Socket Functions | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
30a4f2a8 | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
d1f14731 | 31 | |
30a4f2a8 | 32 | /* |
33 | * Copyright (c) 1994, 1995. All rights reserved. | |
34 | * | |
35 | * The Harvest software was developed by the Internet Research Task | |
36 | * Force Research Group on Resource Discovery (IRTF-RD): | |
37 | * | |
38 | * Mic Bowman of Transarc Corporation. | |
39 | * Peter Danzig of the University of Southern California. | |
40 | * Darren R. Hardy of the University of Colorado at Boulder. | |
41 | * Udi Manber of the University of Arizona. | |
42 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
43 | * Duane Wessels of the University of Colorado at Boulder. | |
44 | * | |
45 | * This copyright notice applies to software in the Harvest | |
46 | * ``src/'' directory only. Users should consult the individual | |
47 | * copyright notices in the ``components/'' subdirectories for | |
48 | * copyright information about other software bundled with the | |
49 | * Harvest source code distribution. | |
50 | * | |
51 | * TERMS OF USE | |
52 | * | |
53 | * The Harvest software may be used and re-distributed without | |
54 | * charge, provided that the software origin and research team are | |
55 | * cited in any use of the system. Most commonly this is | |
56 | * accomplished by including a link to the Harvest Home Page | |
57 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
58 | * Broker you deploy, as well as in the query result pages. These | |
59 | * links are generated automatically by the standard Broker | |
60 | * software distribution. | |
61 | * | |
62 | * The Harvest software is provided ``as is'', without express or | |
63 | * implied warranty, and with no support nor obligation to assist | |
64 | * in its use, correction, modification or enhancement. We assume | |
65 | * no liability with respect to the infringement of copyrights, | |
66 | * trade secrets, or any patents, and are not responsible for | |
67 | * consequential damages. Proper use of the Harvest software is | |
68 | * entirely the responsibility of the user. | |
69 | * | |
70 | * DERIVATIVE WORKS | |
71 | * | |
72 | * Users may make derivative works from the Harvest software, subject | |
73 | * to the following constraints: | |
74 | * | |
75 | * - You must include the above copyright notice and these | |
76 | * accompanying paragraphs in all forms of derivative works, | |
77 | * and any documentation and other materials related to such | |
78 | * distribution and use acknowledge that the software was | |
79 | * developed at the above institutions. | |
80 | * | |
81 | * - You must notify IRTF-RD regarding your distribution of | |
82 | * the derivative work. | |
83 | * | |
84 | * - You must clearly notify users that your are distributing | |
85 | * a modified version and not the original Harvest software. | |
86 | * | |
87 | * - Any derivative product is also subject to these copyright | |
88 | * and use restrictions. | |
89 | * | |
90 | * Note that the Harvest software is NOT in the public domain. We | |
91 | * retain copyright, as specified above. | |
92 | * | |
93 | * HISTORY OF FREE SOFTWARE STATUS | |
94 | * | |
95 | * Originally we required sites to license the software in cases | |
96 | * where they were going to build commercial products/services | |
97 | * around Harvest. In June 1995 we changed this policy. We now | |
98 | * allow people to use the core Harvest software (the code found in | |
99 | * the Harvest ``src/'' directory) for free. We made this change | |
100 | * in the interest of encouraging the widest possible deployment of | |
101 | * the technology. The Harvest software is really a reference | |
102 | * implementation of a set of protocols and formats, some of which | |
103 | * we intend to standardize. We encourage commercial | |
104 | * re-implementations of code complying to this set of standards. | |
105 | */ | |
090089c4 | 106 | |
44a47c6e | 107 | #include "squid.h" |
0a0bf5db | 108 | #include <errno.h> |
090089c4 | 109 | |
30a4f2a8 | 110 | #ifdef HAVE_NETINET_TCP_H |
111 | #include <netinet/tcp.h> | |
112 | #endif | |
090089c4 | 113 | |
114 | /* Block processing new client requests (accepts on ascii port) when we start | |
115 | * running shy of free file descriptors. For example, under SunOS, we'll keep | |
116 | * 64 file descriptors free for disk-i/o and connections to remote servers */ | |
117 | ||
da22ac20 | 118 | int RESERVED_FD = 64; |
97c03d3c | 119 | int polledinc = 0; |
090089c4 | 120 | |
121 | #define min(x,y) ((x)<(y)? (x) : (y)) | |
122 | #define max(a,b) ((a)>(b)? (a) : (b)) | |
123 | ||
f17936ab | 124 | struct _cwstate { |
30a4f2a8 | 125 | char *buf; |
126 | long size; | |
127 | long offset; | |
f17936ab | 128 | CWCB *handler; |
30a4f2a8 | 129 | void *handler_data; |
4a63c85f | 130 | void (*free) (void *); |
f17936ab | 131 | }; |
090089c4 | 132 | |
f88211e8 | 133 | typedef struct { |
134 | char *host; | |
135 | u_short port; | |
136 | struct sockaddr_in S; | |
137 | CNCB *callback; | |
138 | void *data; | |
139 | int tries; | |
140 | struct in_addr in_addr; | |
141 | int locks; | |
03a1ee42 | 142 | int fd; |
f88211e8 | 143 | } ConnectStateData; |
144 | ||
090089c4 | 145 | /* GLOBAL */ |
090089c4 | 146 | FD_ENTRY *fd_table = NULL; /* also used in disk.c */ |
147 | ||
148 | /* STATIC */ | |
24382924 | 149 | static int commBind _PARAMS((int s, struct in_addr, u_short port)); |
f88211e8 | 150 | #if !HAVE_POLL |
5742d7c9 | 151 | static int examine_select _PARAMS((fd_set *, fd_set *)); |
dcfe6390 | 152 | #endif |
67508012 | 153 | static void checkTimeouts _PARAMS((void)); |
67508012 | 154 | static void commSetReuseAddr _PARAMS((int)); |
67508012 | 155 | static void commSetNoLinger _PARAMS((int)); |
812ed90c | 156 | #if HAVE_POLL |
157 | static void comm_poll_incoming _PARAMS((void)); | |
158 | #else | |
67508012 | 159 | static void comm_select_incoming _PARAMS((void)); |
812ed90c | 160 | #endif |
f17936ab | 161 | static void CommWriteStateCallbackAndFree _PARAMS((int fd, int code)); |
30a4f2a8 | 162 | #ifdef TCP_NODELAY |
67508012 | 163 | static void commSetTcpNoDelay _PARAMS((int)); |
30a4f2a8 | 164 | #endif |
67508012 | 165 | static void commSetTcpRcvbuf _PARAMS((int, int)); |
f88211e8 | 166 | static PF commConnectFree; |
03a1ee42 | 167 | static PF commConnectHandle; |
168 | static PF commHandleWrite; | |
812ed90c | 169 | static int fdIsHttpOrIcp _PARAMS((int fd)); |
edeb28fd | 170 | static IPH commConnectDnsHandle; |
03a1ee42 | 171 | static void commConnectCallback _PARAMS((ConnectStateData * cs, int status)); |
30a4f2a8 | 172 | |
30a4f2a8 | 173 | static struct timeval zero_tv; |
090089c4 | 174 | |
81f754fa | 175 | void |
f17936ab | 176 | commCancelWriteHandler(int fd) |
81f754fa | 177 | { |
f17936ab | 178 | CommWriteStateData *CommWriteState = fd_table[fd].rwstate; |
179 | if (CommWriteState) { | |
180 | CommWriteState->handler = NULL; | |
181 | CommWriteState->handler_data = NULL; | |
81f754fa | 182 | } |
183 | } | |
184 | ||
b8d8561b | 185 | static void |
f17936ab | 186 | CommWriteStateCallbackAndFree(int fd, int code) |
9864ee44 | 187 | { |
f17936ab | 188 | CommWriteStateData *CommWriteState = fd_table[fd].rwstate; |
189 | CWCB *callback = NULL; | |
1a8f5ed6 | 190 | void *data; |
a56a3abe | 191 | fd_table[fd].rwstate = NULL; |
f17936ab | 192 | if (CommWriteState == NULL) |
9864ee44 | 193 | return; |
f17936ab | 194 | if (CommWriteState->free) { |
195 | CommWriteState->free(CommWriteState->buf); | |
196 | CommWriteState->buf = NULL; | |
9864ee44 | 197 | } |
f17936ab | 198 | callback = CommWriteState->handler; |
1a8f5ed6 | 199 | data = CommWriteState->handler_data; |
f17936ab | 200 | CommWriteState->handler = NULL; |
1a8f5ed6 | 201 | if (callback && cbdataValid(data)) |
202 | callback(fd, CommWriteState->buf, CommWriteState->offset, code, data); | |
203 | cbdataUnlock(data); | |
f17936ab | 204 | safe_free(CommWriteState); |
9864ee44 | 205 | } |
206 | ||
090089c4 | 207 | /* Return the local port associated with fd. */ |
b8d8561b | 208 | u_short |
209 | comm_local_port(int fd) | |
090089c4 | 210 | { |
211 | struct sockaddr_in addr; | |
212 | int addr_len = 0; | |
9864ee44 | 213 | FD_ENTRY *fde = &fd_table[fd]; |
090089c4 | 214 | |
090089c4 | 215 | /* If the fd is closed already, just return */ |
95d15928 | 216 | if (!fde->open) { |
a3d5953d | 217 | debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd); |
30a4f2a8 | 218 | return 0; |
090089c4 | 219 | } |
9864ee44 | 220 | if (fde->local_port) |
221 | return fde->local_port; | |
090089c4 | 222 | addr_len = sizeof(addr); |
223 | if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) { | |
a3d5953d | 224 | debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror()); |
30a4f2a8 | 225 | return 0; |
090089c4 | 226 | } |
a3d5953d | 227 | debug(5, 6) ("comm_local_port: FD %d: sockaddr %u.\n", fd, addr.sin_addr.s_addr); |
9864ee44 | 228 | fde->local_port = ntohs(addr.sin_port); |
229 | return fde->local_port; | |
090089c4 | 230 | } |
231 | ||
b8d8561b | 232 | static int |
233 | commBind(int s, struct in_addr in_addr, u_short port) | |
090089c4 | 234 | { |
235 | struct sockaddr_in S; | |
090089c4 | 236 | |
090089c4 | 237 | memset(&S, '\0', sizeof(S)); |
238 | S.sin_family = AF_INET; | |
239 | S.sin_port = htons(port); | |
30a4f2a8 | 240 | S.sin_addr = in_addr; |
090089c4 | 241 | if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0) |
242 | return COMM_OK; | |
a3d5953d | 243 | debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n", |
090089c4 | 244 | s, |
30a4f2a8 | 245 | S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr), |
44a62238 | 246 | (int) port, |
247 | xstrerror()); | |
090089c4 | 248 | return COMM_ERROR; |
249 | } | |
250 | ||
251 | /* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE | |
252 | * is OR of flags specified in comm.h. */ | |
b8d8561b | 253 | int |
16b204c4 | 254 | comm_open(int sock_type, |
cc6a9d2e | 255 | int proto, |
256 | struct in_addr addr, | |
257 | u_short port, | |
258 | int flags, | |
0ee4272b | 259 | const char *note) |
090089c4 | 260 | { |
261 | int new_socket; | |
95d15928 | 262 | FD_ENTRY *fde = NULL; |
b6f794d6 | 263 | int tcp_rcv_bufsz = Config.tcpRcvBufsz; |
090089c4 | 264 | |
265 | /* Create socket for accepting new connections. */ | |
16b204c4 | 266 | if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) { |
090089c4 | 267 | /* Increase the number of reserved fd's if calls to socket() |
268 | * are failing because the open file table is full. This | |
269 | * limits the number of simultaneous clients */ | |
270 | switch (errno) { | |
271 | case ENFILE: | |
272 | case EMFILE: | |
a3d5953d | 273 | debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 274 | break; |
275 | default: | |
a3d5953d | 276 | debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 277 | } |
278 | return (COMM_ERROR); | |
279 | } | |
280 | /* update fdstat */ | |
489b22c1 | 281 | debug(5,5)("comm_open: FD %d is a new socket\n", new_socket); |
5c5783a2 | 282 | fd_open(new_socket, FD_SOCKET, note); |
95d15928 | 283 | fde = &fd_table[new_socket]; |
16b204c4 | 284 | if (!BIT_TEST(flags, COMM_NOCLOEXEC)) |
3ca60c86 | 285 | commSetCloseOnExec(new_socket); |
7690e8eb | 286 | if (port > (u_short) 0) { |
30a4f2a8 | 287 | commSetNoLinger(new_socket); |
288 | if (do_reuse) | |
090089c4 | 289 | commSetReuseAddr(new_socket); |
090089c4 | 290 | } |
429fdbec | 291 | if (addr.s_addr != no_addr.s_addr) |
30a4f2a8 | 292 | if (commBind(new_socket, addr, port) != COMM_OK) |
293 | return COMM_ERROR; | |
95d15928 | 294 | fde->local_port = port; |
090089c4 | 295 | |
16b204c4 | 296 | if (BIT_TEST(flags, COMM_NONBLOCKING)) |
30a4f2a8 | 297 | if (commSetNonBlocking(new_socket) == COMM_ERROR) |
298 | return COMM_ERROR; | |
299 | #ifdef TCP_NODELAY | |
300 | if (sock_type == SOCK_STREAM) | |
301 | commSetTcpNoDelay(new_socket); | |
302 | #endif | |
f868539a | 303 | if (tcp_rcv_bufsz > 0 && sock_type == SOCK_STREAM) |
304 | commSetTcpRcvbuf(new_socket, tcp_rcv_bufsz); | |
090089c4 | 305 | return new_socket; |
306 | } | |
307 | ||
308 | /* | |
e83892e9 | 309 | * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to |
090089c4 | 310 | * impose an upper limit. Solaris' listen(3n) page says it has |
311 | * no limit on this parameter, but sys/socket.h sets SOMAXCONN | |
312 | * to 5. HP-UX currently has a limit of 20. SunOS is 5 and | |
313 | * OSF 3.0 is 8. | |
314 | */ | |
b8d8561b | 315 | int |
316 | comm_listen(int sock) | |
090089c4 | 317 | { |
318 | int x; | |
e83892e9 | 319 | if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) { |
a3d5953d | 320 | debug(50, 0) ("comm_listen: listen(%d, %d): %s\n", |
e83892e9 | 321 | Squid_MaxFD >> 2, |
090089c4 | 322 | sock, xstrerror()); |
323 | return x; | |
324 | } | |
325 | return sock; | |
326 | } | |
327 | ||
e5f6c5c2 | 328 | void |
4f92c80c | 329 | commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data) |
e924600d | 330 | { |
331 | ConnectStateData *cs = xcalloc(1, sizeof(ConnectStateData)); | |
8407afee | 332 | cbdataAdd(cs); |
03a1ee42 | 333 | cs->fd = fd; |
e924600d | 334 | cs->host = xstrdup(host); |
335 | cs->port = port; | |
336 | cs->callback = callback; | |
337 | cs->data = data; | |
8407afee | 338 | cbdataLock(data); |
e924600d | 339 | comm_add_close_handler(fd, commConnectFree, cs); |
f88211e8 | 340 | cs->locks++; |
8407afee | 341 | ipcache_nbgethostbyname(host, commConnectDnsHandle, cs); |
edeb28fd | 342 | } |
343 | ||
344 | static void | |
03a1ee42 | 345 | commConnectDnsHandle(const ipcache_addrs * ia, void *data) |
edeb28fd | 346 | { |
347 | ConnectStateData *cs = data; | |
f88211e8 | 348 | assert(cs->locks == 1); |
349 | cs->locks--; | |
edeb28fd | 350 | if (ia == NULL) { |
a3d5953d | 351 | debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host); |
03a1ee42 | 352 | commConnectCallback(cs, COMM_ERR_DNS); |
edeb28fd | 353 | return; |
354 | } | |
355 | cs->in_addr = ia->in_addrs[ia->cur]; | |
03a1ee42 | 356 | commConnectHandle(cs->fd, cs); |
e924600d | 357 | } |
358 | ||
f88211e8 | 359 | static void |
03a1ee42 | 360 | commConnectCallback(ConnectStateData * cs, int status) |
f88211e8 | 361 | { |
a3d5953d | 362 | CNCB *callback = cs->callback; |
363 | void *data = cs->data; | |
03a1ee42 | 364 | int fd = cs->fd; |
a3d5953d | 365 | comm_remove_close_handler(fd, commConnectFree, cs); |
366 | commConnectFree(fd, cs); | |
8407afee | 367 | if (cbdataValid(data)) |
368 | callback(fd, status, data); | |
369 | cbdataUnlock(data); | |
f88211e8 | 370 | } |
371 | ||
e924600d | 372 | static void |
03a1ee42 | 373 | commConnectFree(int fdunused, void *data) |
e924600d | 374 | { |
375 | ConnectStateData *cs = data; | |
8407afee | 376 | if (cs->locks) |
377 | ipcacheUnregister(cs->host, cs); | |
378 | safe_free(cs->host); | |
379 | cbdataFree(cs); | |
e924600d | 380 | } |
381 | ||
edeb28fd | 382 | static int |
f88211e8 | 383 | commRetryConnect(int fd, ConnectStateData * cs) |
edeb28fd | 384 | { |
385 | int fd2; | |
f88211e8 | 386 | if (++cs->tries == 4) |
edeb28fd | 387 | return 0; |
7dd44885 | 388 | if (!cbdataValid(cs->data)) |
389 | return 0; | |
edeb28fd | 390 | fd2 = socket(AF_INET, SOCK_STREAM, 0); |
391 | if (fd2 < 0) { | |
a3d5953d | 392 | debug(5, 0) ("commRetryConnect: socket: %s\n", xstrerror()); |
edeb28fd | 393 | return 0; |
394 | } | |
395 | if (dup2(fd2, fd) < 0) { | |
a3d5953d | 396 | debug(5, 0) ("commRetryConnect: dup2: %s\n", xstrerror()); |
edeb28fd | 397 | return 0; |
398 | } | |
399 | commSetNonBlocking(fd); | |
400 | close(fd2); | |
401 | return 1; | |
402 | } | |
403 | ||
e924600d | 404 | /* Connect SOCK to specified DEST_PORT at DEST_HOST. */ |
405 | static void | |
406 | commConnectHandle(int fd, void *data) | |
090089c4 | 407 | { |
f88211e8 | 408 | ConnectStateData *cs = data; |
409 | if (cs->S.sin_addr.s_addr == 0) { | |
410 | cs->S.sin_family = AF_INET; | |
411 | cs->S.sin_addr = cs->in_addr; | |
412 | cs->S.sin_port = htons(cs->port); | |
e5f6c5c2 | 413 | if (Config.Log.log_fqdn) |
f88211e8 | 414 | fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS); |
e5f6c5c2 | 415 | } |
f88211e8 | 416 | switch (comm_connect_addr(fd, &cs->S)) { |
e5f6c5c2 | 417 | case COMM_INPROGRESS: |
489b22c1 | 418 | debug(5, 5) ("FD %d: COMM_INPROGRESS\n", fd); |
f88211e8 | 419 | commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0); |
e5f6c5c2 | 420 | break; |
421 | case COMM_OK: | |
e924600d | 422 | if (vizSock > -1) |
f88211e8 | 423 | vizHackSendPkt(&cs->S, 2); |
424 | ipcacheCycleAddr(cs->host); | |
03a1ee42 | 425 | commConnectCallback(cs, COMM_OK); |
e5f6c5c2 | 426 | break; |
427 | default: | |
f88211e8 | 428 | if (commRetryConnect(fd, cs)) { |
a3d5953d | 429 | debug(5, 1) ("Retrying connection to %s: %s\n", |
f88211e8 | 430 | cs->host, xstrerror()); |
431 | cs->S.sin_addr.s_addr = 0; | |
432 | ipcacheCycleAddr(cs->host); | |
433 | cs->locks++; | |
8407afee | 434 | ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs); |
edeb28fd | 435 | } else { |
f88211e8 | 436 | ipcacheRemoveBadAddr(cs->host, cs->S.sin_addr); |
03a1ee42 | 437 | commConnectCallback(cs, COMM_ERR_CONNECT); |
edeb28fd | 438 | } |
e5f6c5c2 | 439 | break; |
090089c4 | 440 | } |
090089c4 | 441 | } |
b8d8561b | 442 | int |
4f92c80c | 443 | commSetTimeout(int fd, int timeout, PF * handler, void *data) |
090089c4 | 444 | { |
5c5783a2 | 445 | FD_ENTRY *fde; |
a3d5953d | 446 | debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout); |
e83892e9 | 447 | if (fd < 0 || fd > Squid_MaxFD) |
4f92c80c | 448 | fatal_dump("commSetTimeout: bad FD"); |
5c5783a2 | 449 | fde = &fd_table[fd]; |
450 | if (timeout < 0) { | |
4f92c80c | 451 | fde->timeout_handler = NULL; |
452 | fde->timeout_data = NULL; | |
453 | return fde->timeout = 0; | |
5c5783a2 | 454 | } |
bbdb774b | 455 | if (shutdown_pending || reconfigure_pending) { |
4f92c80c | 456 | /* don't increase the timeout if something pending */ |
457 | if (fde->timeout > 0 && (int) (fde->timeout - squid_curtime) < timeout) | |
458 | return fde->timeout; | |
5c5783a2 | 459 | } |
460 | if (handler || data) { | |
4f92c80c | 461 | fde->timeout_handler = handler; |
462 | fde->timeout_data = data; | |
5c5783a2 | 463 | } else if (fde->timeout_handler == NULL) { |
4f92c80c | 464 | debug_trap("commSetTimeout: setting timeout, but no handler"); |
30a4f2a8 | 465 | } |
5c5783a2 | 466 | return fde->timeout = squid_curtime + (time_t) timeout; |
090089c4 | 467 | } |
468 | ||
b8d8561b | 469 | int |
0ee4272b | 470 | comm_connect_addr(int sock, const struct sockaddr_in *address) |
090089c4 | 471 | { |
472 | int status = COMM_OK; | |
95d15928 | 473 | FD_ENTRY *fde = &fd_table[sock]; |
090089c4 | 474 | int len; |
475 | int x; | |
489b22c1 | 476 | assert(ntohs(address->sin_port) != 0); |
090089c4 | 477 | /* Establish connection. */ |
086bce16 | 478 | if (connect(sock, (struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) { |
489b22c1 | 479 | debug(5,9)("connect FD %d: %s\n", sock, xstrerror()); |
090089c4 | 480 | switch (errno) { |
481 | case EALREADY: | |
30a4f2a8 | 482 | #if EAGAIN != EWOULDBLOCK |
483 | case EAGAIN: | |
484 | #endif | |
0a0bf5db | 485 | case EINTR: |
30a4f2a8 | 486 | case EWOULDBLOCK: |
090089c4 | 487 | case EINPROGRESS: |
e5f6c5c2 | 488 | status = COMM_INPROGRESS; |
090089c4 | 489 | break; |
490 | case EISCONN: | |
491 | status = COMM_OK; | |
492 | break; | |
493 | case EINVAL: | |
494 | len = sizeof(x); | |
495 | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &x, &len) >= 0) | |
496 | errno = x; | |
497 | default: | |
a3d5953d | 498 | debug(50, 2) ("connect: %s:%d: %s.\n", |
28ab0c0a | 499 | fqdnFromAddr(address->sin_addr), |
090089c4 | 500 | ntohs(address->sin_port), |
501 | xstrerror()); | |
502 | return COMM_ERROR; | |
503 | } | |
e5f6c5c2 | 504 | } |
95d15928 | 505 | xstrncpy(fde->ipaddr, inet_ntoa(address->sin_addr), 16); |
506 | fde->remote_port = ntohs(address->sin_port); | |
090089c4 | 507 | if (status == COMM_OK) { |
a3d5953d | 508 | debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n", |
5c5783a2 | 509 | sock, fde->ipaddr, fde->remote_port); |
f21cd581 | 510 | } else if (status == COMM_INPROGRESS) { |
a3d5953d | 511 | debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock); |
090089c4 | 512 | } |
513 | /* Add new socket to list of open sockets. */ | |
090089c4 | 514 | return status; |
515 | } | |
516 | ||
517 | /* Wait for an incoming connection on FD. FD should be a socket returned | |
518 | * from comm_listen. */ | |
b8d8561b | 519 | int |
520 | comm_accept(int fd, struct sockaddr_in *peer, struct sockaddr_in *me) | |
090089c4 | 521 | { |
522 | int sock; | |
1f9afe33 | 523 | struct sockaddr_in P; |
524 | struct sockaddr_in M; | |
090089c4 | 525 | int Slen; |
95d15928 | 526 | FD_ENTRY *fde = NULL; |
090089c4 | 527 | |
1f9afe33 | 528 | Slen = sizeof(P); |
529 | while ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) { | |
090089c4 | 530 | switch (errno) { |
531 | #if EAGAIN != EWOULDBLOCK | |
532 | case EAGAIN: | |
533 | #endif | |
534 | case EWOULDBLOCK: | |
090089c4 | 535 | case EINTR: |
0a0bf5db | 536 | return COMM_NOMESSAGE; |
090089c4 | 537 | case ENFILE: |
538 | case EMFILE: | |
090089c4 | 539 | return COMM_ERROR; |
540 | default: | |
a3d5953d | 541 | debug(50, 1) ("comm_accept: FD %d: accept failure: %s\n", |
090089c4 | 542 | fd, xstrerror()); |
543 | return COMM_ERROR; | |
544 | } | |
545 | } | |
546 | ||
547 | if (peer) | |
1f9afe33 | 548 | *peer = P; |
4053a845 | 549 | Slen = sizeof(M); |
550 | memset(&M, '\0', Slen); | |
551 | getsockname(sock, (struct sockaddr *) &M, &Slen); | |
552 | if (me) | |
1f9afe33 | 553 | *me = M; |
3ca60c86 | 554 | commSetCloseOnExec(sock); |
090089c4 | 555 | /* fdstat update */ |
5c5783a2 | 556 | fd_open(sock, FD_SOCKET, "HTTP Request"); |
95d15928 | 557 | fde = &fd_table[sock]; |
95d15928 | 558 | strcpy(fde->ipaddr, inet_ntoa(P.sin_addr)); |
559 | fde->remote_port = htons(P.sin_port); | |
560 | fde->local_port = htons(M.sin_port); | |
090089c4 | 561 | commSetNonBlocking(sock); |
090089c4 | 562 | return sock; |
563 | } | |
564 | ||
cb201b7e | 565 | void |
566 | commCallCloseHandlers(int fd) | |
567 | { | |
95d15928 | 568 | FD_ENTRY *fde = &fd_table[fd]; |
cb201b7e | 569 | struct close_handler *ch; |
a3d5953d | 570 | debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd); |
95d15928 | 571 | while ((ch = fde->close_handler) != NULL) { |
572 | fde->close_handler = ch->next; | |
cb201b7e | 573 | ch->handler(fd, ch->data); |
574 | safe_free(ch); | |
575 | } | |
576 | } | |
577 | ||
b8d8561b | 578 | void |
579 | comm_close(int fd) | |
090089c4 | 580 | { |
95d15928 | 581 | FD_ENTRY *fde = NULL; |
a3d5953d | 582 | debug(5, 5) ("comm_close: FD %d\n", fd); |
95d15928 | 583 | if (fd < 0) |
4f92c80c | 584 | fatal_dump("comm_close: bad FD"); |
95d15928 | 585 | if (fd >= Squid_MaxFD) |
4f92c80c | 586 | fatal_dump("comm_close: bad FD"); |
95d15928 | 587 | fde = &fd_table[fd]; |
588 | if (!fde->open) | |
9864ee44 | 589 | return; |
95d15928 | 590 | if (fd_table[fd].type == FD_FILE) |
591 | fatal_dump("comm_close: not a SOCKET"); | |
592 | fde->open = 0; | |
f17936ab | 593 | CommWriteStateCallbackAndFree(fd, COMM_ERROR); |
cb201b7e | 594 | commCallCloseHandlers(fd); |
5c5783a2 | 595 | fd_close(fd); /* update fdstat */ |
0a0bf5db | 596 | #if USE_ASYNC_IO |
597 | aioClose(fd); | |
598 | #else | |
9864ee44 | 599 | close(fd); |
0a0bf5db | 600 | #endif |
090089c4 | 601 | } |
602 | ||
090089c4 | 603 | |
604 | /* Send a udp datagram to specified PORT at HOST. */ | |
b8d8561b | 605 | int |
0ee4272b | 606 | comm_udp_send(int fd, const char *host, u_short port, const char *buf, int len) |
090089c4 | 607 | { |
0ee4272b | 608 | const ipcache_addrs *ia = NULL; |
090089c4 | 609 | static struct sockaddr_in to_addr; |
610 | int bytes_sent; | |
611 | ||
612 | /* Set up the destination socket address for message to send to. */ | |
613 | to_addr.sin_family = AF_INET; | |
614 | ||
e5f6c5c2 | 615 | if ((ia = ipcache_gethostbyname(host, IP_BLOCKING_LOOKUP)) == 0) { |
a3d5953d | 616 | debug(50, 1) ("comm_udp_send: gethostbyname failure: %s: %s\n", |
090089c4 | 617 | host, xstrerror()); |
618 | return (COMM_ERROR); | |
619 | } | |
e5f6c5c2 | 620 | to_addr.sin_addr = ia->in_addrs[ia->cur]; |
090089c4 | 621 | to_addr.sin_port = htons(port); |
622 | if ((bytes_sent = sendto(fd, buf, len, 0, (struct sockaddr *) &to_addr, | |
623 | sizeof(to_addr))) < 0) { | |
a3d5953d | 624 | debug(50, 1) ("comm_udp_send: sendto failure: FD %d: %s\n", |
090089c4 | 625 | fd, xstrerror()); |
626 | return COMM_ERROR; | |
627 | } | |
628 | return bytes_sent; | |
629 | } | |
630 | ||
631 | /* Send a udp datagram to specified TO_ADDR. */ | |
b8d8561b | 632 | int |
5df61230 | 633 | comm_udp_sendto(int fd, |
634 | const struct sockaddr_in *to_addr, | |
635 | int addr_len, | |
636 | const char *buf, | |
637 | int len) | |
090089c4 | 638 | { |
5df61230 | 639 | int x; |
640 | x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len); | |
641 | if (x < 0) { | |
a3d5953d | 642 | debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n", |
5df61230 | 643 | fd, |
644 | inet_ntoa(to_addr->sin_addr), | |
645 | (int) htons(to_addr->sin_port), | |
646 | xstrerror()); | |
090089c4 | 647 | return COMM_ERROR; |
648 | } | |
5df61230 | 649 | return x; |
090089c4 | 650 | } |
651 | ||
b8d8561b | 652 | void |
653 | comm_set_stall(int fd, int delta) | |
4883993a | 654 | { |
655 | if (fd < 0) | |
656 | return; | |
b8de7ebe | 657 | fd_table[fd].stall_until = squid_curtime + delta; |
4883993a | 658 | } |
659 | ||
dcfe6390 | 660 | |
f88211e8 | 661 | #if HAVE_POLL |
dcfe6390 | 662 | |
663 | /* poll() version by: | |
664 | * Stewart Forster <slf@connect.com.au>, and | |
665 | * Anthony Baxter <arb@connect.com.au> */ | |
666 | ||
667 | static void | |
812ed90c | 668 | comm_poll_incoming(void) |
dcfe6390 | 669 | { |
429fdbec | 670 | int fd; |
996a0a51 | 671 | int fds[4]; |
0b2421ea | 672 | struct pollfd pfds[3 + MAXHTTPPORTS]; |
996a0a51 | 673 | unsigned long N = 0; |
429fdbec | 674 | unsigned long i, nfds; |
812ed90c | 675 | int j; |
582b6456 | 676 | PF *hdl = NULL; |
97c03d3c | 677 | polledinc = 0; |
dcfe6390 | 678 | if (theInIcpConnection >= 0) |
679 | fds[N++] = theInIcpConnection; | |
933c6d93 | 680 | if (theInIcpConnection != theOutIcpConnection) |
1793867a | 681 | if (theOutIcpConnection >= 0) |
933c6d93 | 682 | fds[N++] = theOutIcpConnection; |
0b2421ea | 683 | for (j = 0; j < NHttpSockets; j++) { |
812ed90c | 684 | if (HttpSockets[j] < 0) |
0b2421ea | 685 | continue; |
812ed90c | 686 | if (fd_table[HttpSockets[j]].stall_until > squid_curtime) |
0b2421ea | 687 | continue; |
812ed90c | 688 | fds[N++] = HttpSockets[j]; |
689 | } | |
429fdbec | 690 | for (i = nfds = 0; i < N; i++) { |
691 | int events; | |
dcfe6390 | 692 | fd = fds[i]; |
429fdbec | 693 | events = 0; |
694 | if (fd_table[fd].read_handler) | |
695 | events |= POLLRDNORM; | |
696 | if (fd_table[fd].write_handler) | |
697 | events |= POLLWRNORM; | |
698 | if (events) { | |
699 | pfds[nfds].fd = fd; | |
700 | pfds[nfds].events = events; | |
701 | pfds[nfds].revents = 0; | |
702 | nfds++; | |
dcfe6390 | 703 | } |
dcfe6390 | 704 | } |
429fdbec | 705 | if (!nfds) |
706 | return; | |
97c03d3c | 707 | polledinc = poll(pfds, nfds, 0); |
708 | if (polledinc < 1) { | |
709 | polledinc = 0; | |
996a0a51 | 710 | return; |
97c03d3c | 711 | } |
429fdbec | 712 | for (i = 0; i < nfds; i++) { |
713 | int revents; | |
714 | if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1)) | |
dcfe6390 | 715 | continue; |
429fdbec | 716 | if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) { |
717 | hdl = fd_table[fd].read_handler; | |
718 | fd_table[fd].read_handler = 0; | |
719 | hdl(fd, fd_table[fd].read_data); | |
dcfe6390 | 720 | } |
429fdbec | 721 | if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) { |
722 | hdl = fd_table[fd].write_handler; | |
723 | fd_table[fd].write_handler = 0; | |
724 | hdl(fd, fd_table[fd].write_data); | |
dcfe6390 | 725 | } |
726 | } | |
727 | /* TO FIX: repoll ICP connection here */ | |
728 | } | |
729 | ||
ca98227c | 730 | #else |
dcfe6390 | 731 | |
b8d8561b | 732 | static void |
0673c0ba | 733 | comm_select_incoming(void) |
055f4d4d | 734 | { |
735 | fd_set read_mask; | |
736 | fd_set write_mask; | |
737 | int maxfd = 0; | |
738 | int fd = 0; | |
0b2421ea | 739 | int fds[3 + MAXHTTPPORTS]; |
055f4d4d | 740 | int N = 0; |
741 | int i = 0; | |
582b6456 | 742 | PF *hdl = NULL; |
97c03d3c | 743 | polledinc = 0; |
055f4d4d | 744 | FD_ZERO(&read_mask); |
745 | FD_ZERO(&write_mask); | |
0b2421ea | 746 | for (i = 0; i < NHttpSockets; i++) { |
812ed90c | 747 | if (HttpSockets[i] < 0) |
0b2421ea | 748 | continue; |
812ed90c | 749 | if (fd_table[HttpSockets[i]].stall_until > squid_curtime) |
0b2421ea | 750 | continue; |
812ed90c | 751 | fds[N++] = HttpSockets[i]; |
752 | } | |
30a4f2a8 | 753 | if (theInIcpConnection >= 0) |
754 | fds[N++] = theInIcpConnection; | |
933c6d93 | 755 | if (theInIcpConnection != theOutIcpConnection) |
756 | if (theOutIcpConnection >= 0) | |
757 | fds[N++] = theOutIcpConnection; | |
055f4d4d | 758 | fds[N++] = 0; |
055f4d4d | 759 | for (i = 0; i < N; i++) { |
760 | fd = fds[i]; | |
761 | if (fd_table[fd].read_handler) { | |
762 | FD_SET(fd, &read_mask); | |
763 | if (fd > maxfd) | |
764 | maxfd = fd; | |
765 | } | |
766 | if (fd_table[fd].write_handler) { | |
767 | FD_SET(fd, &write_mask); | |
768 | if (fd > maxfd) | |
769 | maxfd = fd; | |
770 | } | |
771 | } | |
055f4d4d | 772 | if (maxfd++ == 0) |
773 | return; | |
97c03d3c | 774 | polledinc = select(maxfd, &read_mask, &write_mask, NULL, &zero_tv); |
775 | if (polledinc < 1) { | |
776 | polledinc = 0; | |
dcfe6390 | 777 | return; |
97c03d3c | 778 | } |
dcfe6390 | 779 | for (i = 0; i < N; i++) { |
780 | fd = fds[i]; | |
781 | if (FD_ISSET(fd, &read_mask)) { | |
782 | hdl = fd_table[fd].read_handler; | |
783 | fd_table[fd].read_handler = 0; | |
784 | hdl(fd, fd_table[fd].read_data); | |
785 | } | |
786 | if (FD_ISSET(fd, &write_mask)) { | |
787 | hdl = fd_table[fd].write_handler; | |
788 | fd_table[fd].write_handler = 0; | |
789 | hdl(fd, fd_table[fd].write_data); | |
790 | } | |
791 | } | |
792 | } | |
793 | #endif | |
794 | ||
812ed90c | 795 | static int |
796 | fdIsHttpOrIcp(int fd) | |
797 | { | |
798 | int j; | |
799 | if (fd == theInIcpConnection) | |
800 | return 1; | |
801 | if (fd == theOutIcpConnection) | |
802 | return 1; | |
803 | for (j = 0; j < NHttpSockets; j++) { | |
804 | if (fd == HttpSockets[j]) | |
805 | return 1; | |
806 | } | |
807 | return 0; | |
808 | } | |
809 | ||
f88211e8 | 810 | #if HAVE_POLL |
dcfe6390 | 811 | /* poll all sockets; call handlers for those that are ready. */ |
812 | int | |
812ed90c | 813 | comm_poll(time_t sec) |
dcfe6390 | 814 | { |
0a0bf5db | 815 | struct pollfd pfds[SQUID_MAXFD]; |
582b6456 | 816 | PF *hdl = NULL; |
dcfe6390 | 817 | int fd; |
818 | int i; | |
819 | int maxfd; | |
996a0a51 | 820 | unsigned long nfds; |
dcfe6390 | 821 | int num; |
dcfe6390 | 822 | static time_t last_timeout = 0; |
97c03d3c | 823 | static int lastinc = 0; |
429fdbec | 824 | int poll_time; |
812ed90c | 825 | static int incoming_counter = 0; |
dcfe6390 | 826 | time_t timeout; |
dcfe6390 | 827 | /* assume all process are very fast (less than 1 second). Call |
828 | * time() only once */ | |
dcfe6390 | 829 | /* use only 1 second granularity */ |
830 | timeout = squid_curtime + sec; | |
831 | do { | |
bbdb774b | 832 | if (shutdown_pending || reconfigure_pending) { |
dcfe6390 | 833 | serverConnectionsClose(); |
dcfe6390 | 834 | dnsShutdownServers(); |
835 | redirectShutdownServers(); | |
429fdbec | 836 | /* shutdown_pending will be set to |
837 | * +1 for SIGTERM | |
838 | * -1 for SIGINT */ | |
bbdb774b | 839 | /* reconfigure_pending always == 1 when SIGHUP received */ |
840 | if (shutdown_pending > 0 || reconfigure_pending > 0) | |
5c5783a2 | 841 | setSocketShutdownLifetimes(Config.shutdownLifetime); |
dcfe6390 | 842 | else |
9e4ad609 | 843 | setSocketShutdownLifetimes(1); |
dcfe6390 | 844 | } |
429fdbec | 845 | nfds = 0; |
846 | maxfd = Biggest_FD + 1; | |
429fdbec | 847 | for (i = 0; i < maxfd; i++) { |
848 | int events; | |
849 | events = 0; | |
dcfe6390 | 850 | /* Check each open socket for a handler. */ |
429fdbec | 851 | if (fd_table[i].read_handler && fd_table[i].stall_until <= squid_curtime) |
852 | events |= POLLRDNORM; | |
853 | if (fd_table[i].write_handler) | |
854 | events |= POLLWRNORM; | |
855 | if (events) { | |
429fdbec | 856 | pfds[nfds].fd = i; |
857 | pfds[nfds].events = events; | |
858 | pfds[nfds].revents = 0; | |
859 | nfds++; | |
055f4d4d | 860 | } |
0b2421ea | 861 | } |
bbdb774b | 862 | if (shutdown_pending || reconfigure_pending) |
a3d5953d | 863 | debug(5, 2) ("comm_poll: Still waiting on %d FDs\n", nfds); |
dcfe6390 | 864 | if (nfds == 0) |
865 | return COMM_SHUTDOWN; | |
0a0bf5db | 866 | poll_time = sec > 0 ? 100 : 0; |
867 | #if USE_ASYNC_IO | |
868 | aioCheckCallbacks(); | |
869 | #endif | |
dcfe6390 | 870 | for (;;) { |
429fdbec | 871 | poll_time = sec > 0 ? 1000 : 0; |
872 | num = poll(pfds, nfds, poll_time); | |
873 | select_loops++; | |
dcfe6390 | 874 | if (num >= 0) |
875 | break; | |
876 | if (errno == EINTR) | |
0a0bf5db | 877 | continue; |
a3d5953d | 878 | debug(5, 0) ("comm_poll: poll failure: %s\n", xstrerror()); |
0b2421ea | 879 | if (errno == EINVAL) |
812ed90c | 880 | fatal_dump("Poll returned EINVAL"); |
dcfe6390 | 881 | return COMM_ERROR; |
882 | /* NOTREACHED */ | |
883 | } | |
a3d5953d | 884 | debug(5, num ? 5 : 8) ("comm_poll: %d sockets ready\n", num); |
5c5783a2 | 885 | /* Check timeout handlers ONCE each second. */ |
dcfe6390 | 886 | if (squid_curtime > last_timeout) { |
887 | last_timeout = squid_curtime; | |
888 | checkTimeouts(); | |
dcfe6390 | 889 | } |
890 | if (num == 0) | |
891 | continue; | |
892 | /* scan each socket but the accept socket. Poll this | |
2c5294ce | 893 | * more frequently to minimize losses due to the 5 connect |
dcfe6390 | 894 | * limit in SunOS */ |
429fdbec | 895 | for (i = 0; i < nfds; i++) { |
896 | int revents; | |
897 | if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1)) | |
dcfe6390 | 898 | continue; |
97c03d3c | 899 | if ((incoming_counter++ & (lastinc > 0 ? 1 : 7)) == 0) |
812ed90c | 900 | comm_poll_incoming(); |
901 | if (fdIsHttpOrIcp(fd)) | |
996a0a51 | 902 | continue; |
429fdbec | 903 | if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) { |
a3d5953d | 904 | debug(5, 6) ("comm_poll: FD %d ready for reading\n", fd); |
0b2421ea | 905 | if ((hdl = fd_table[fd].read_handler)) { |
906 | fd_table[fd].read_handler = 0; | |
907 | hdl(fd, fd_table[fd].read_data); | |
908 | } | |
dcfe6390 | 909 | } |
429fdbec | 910 | if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) { |
a3d5953d | 911 | debug(5, 5) ("comm_poll: FD %d ready for writing\n", fd); |
0b2421ea | 912 | if ((hdl = fd_table[fd].write_handler)) { |
913 | fd_table[fd].write_handler = 0; | |
914 | hdl(fd, fd_table[fd].write_data); | |
915 | } | |
dcfe6390 | 916 | } |
429fdbec | 917 | if (revents & POLLNVAL) { |
918 | struct close_handler *ch; | |
919 | struct close_handler *next; | |
5c5783a2 | 920 | FD_ENTRY *fde = &fd_table[fd]; |
a3d5953d | 921 | debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd); |
922 | debug(5, 0) ("FD %d is a %s\n", fd, fdstatTypeStr[fd_table[fd].type]); | |
923 | debug(5, 0) ("--> %s\n", fd_table[fd].desc); | |
924 | debug(5, 0) ("tmout:%p read:%p write:%p\n", | |
5c5783a2 | 925 | fde->timeout_handler, |
926 | fde->read_handler, | |
927 | fde->write_handler); | |
928 | for (ch = fde->close_handler; ch; ch = ch->next) | |
a3d5953d | 929 | debug(5, 0) (" close handler: %p\n", ch->handler); |
5c5783a2 | 930 | if (fde->close_handler) { |
931 | for (ch = fde->close_handler; ch; ch = next) { | |
dcfe6390 | 932 | next = ch->next; |
933 | ch->handler(fd, ch->data); | |
934 | safe_free(ch); | |
935 | } | |
5c5783a2 | 936 | } else if (fde->timeout_handler) { |
a3d5953d | 937 | debug(5, 0) ("comm_poll: Calling Timeout Handler\n"); |
5c5783a2 | 938 | fde->timeout_handler(fd, fde->timeout_data); |
dcfe6390 | 939 | } |
5c5783a2 | 940 | fde->close_handler = NULL; |
941 | fde->timeout_handler = NULL; | |
942 | fde->read_handler = NULL; | |
943 | fde->write_handler = NULL; | |
dcfe6390 | 944 | } |
97c03d3c | 945 | lastinc = polledinc; |
dcfe6390 | 946 | } |
947 | return COMM_OK; | |
97c03d3c | 948 | } while (timeout > squid_curtime); |
a3d5953d | 949 | debug(5, 8) ("comm_poll: time out: %d.\n", squid_curtime); |
dcfe6390 | 950 | return COMM_TIMEOUT; |
055f4d4d | 951 | } |
090089c4 | 952 | |
dcfe6390 | 953 | #else |
090089c4 | 954 | |
955 | /* Select on all sockets; call handlers for those that are ready. */ | |
b8d8561b | 956 | int |
957 | comm_select(time_t sec) | |
090089c4 | 958 | { |
090089c4 | 959 | fd_set readfds; |
960 | fd_set writefds; | |
582b6456 | 961 | PF *hdl = NULL; |
7d49daab | 962 | int fd; |
963 | int i; | |
964 | int maxfd; | |
965 | int nfds; | |
090089c4 | 966 | int num; |
97c03d3c | 967 | static int incoming_counter = 0; |
090089c4 | 968 | static time_t last_timeout = 0; |
969 | struct timeval poll_time; | |
97c03d3c | 970 | static int lastinc; |
7d49daab | 971 | time_t timeout; |
090089c4 | 972 | |
973 | /* assume all process are very fast (less than 1 second). Call | |
974 | * time() only once */ | |
090089c4 | 975 | /* use only 1 second granularity */ |
b8de7ebe | 976 | timeout = squid_curtime + sec; |
090089c4 | 977 | |
f7361640 | 978 | do { |
090089c4 | 979 | FD_ZERO(&readfds); |
980 | FD_ZERO(&writefds); | |
bbdb774b | 981 | if (shutdown_pending || reconfigure_pending) { |
30a4f2a8 | 982 | serverConnectionsClose(); |
f88bb09c | 983 | dnsShutdownServers(); |
d2af9477 | 984 | redirectShutdownServers(); |
429fdbec | 985 | /* shutdown_pending will be set to |
986 | * +1 for SIGTERM | |
987 | * -1 for SIGINT */ | |
bbdb774b | 988 | /* reconfigure_pending always == 1 when SIGHUP received */ |
989 | if (shutdown_pending > 0 || reconfigure_pending > 0) | |
5c5783a2 | 990 | setSocketShutdownLifetimes(Config.shutdownLifetime); |
f3753518 | 991 | else |
992 | setSocketShutdownLifetimes(0); | |
30a4f2a8 | 993 | } |
4d64d74a | 994 | nfds = 0; |
429fdbec | 995 | maxfd = Biggest_FD + 1; |
4d64d74a | 996 | for (i = 0; i < maxfd; i++) { |
090089c4 | 997 | /* Check each open socket for a handler. */ |
ab1afadb | 998 | if (fd_table[i].stall_until > squid_curtime) |
999 | continue; | |
1000 | if (fd_table[i].read_handler) { | |
4d64d74a | 1001 | nfds++; |
090089c4 | 1002 | FD_SET(i, &readfds); |
4d64d74a | 1003 | } |
1004 | if (fd_table[i].write_handler) { | |
1005 | nfds++; | |
090089c4 | 1006 | FD_SET(i, &writefds); |
4d64d74a | 1007 | } |
090089c4 | 1008 | } |
bbdb774b | 1009 | if (shutdown_pending || reconfigure_pending) |
a3d5953d | 1010 | debug(5, 2) ("comm_select: Still waiting on %d FDs\n", nfds); |
4d64d74a | 1011 | if (nfds == 0) |
1012 | return COMM_SHUTDOWN; | |
0a0bf5db | 1013 | #if USE_ASYNC_IO |
1014 | aioCheckCallbacks(); | |
1015 | #endif | |
7690e8eb | 1016 | for (;;) { |
89fb2544 | 1017 | poll_time.tv_sec = sec > 0 ? 1 : 0; |
090089c4 | 1018 | poll_time.tv_usec = 0; |
d0217c9b | 1019 | num = select(maxfd, &readfds, &writefds, NULL, &poll_time); |
429fdbec | 1020 | select_loops++; |
090089c4 | 1021 | if (num >= 0) |
1022 | break; | |
4d64d74a | 1023 | if (errno == EINTR) |
1024 | break; | |
a3d5953d | 1025 | debug(50, 0) ("comm_select: select failure: %s\n", |
30a4f2a8 | 1026 | xstrerror()); |
d0217c9b | 1027 | examine_select(&readfds, &writefds); |
bf9f8f2b | 1028 | return COMM_ERROR; |
30a4f2a8 | 1029 | /* NOTREACHED */ |
090089c4 | 1030 | } |
4d64d74a | 1031 | if (num < 0) |
1032 | continue; | |
a3d5953d | 1033 | debug(5, num ? 5 : 8) ("comm_select: %d sockets ready at %d\n", |
30a4f2a8 | 1034 | num, (int) squid_curtime); |
090089c4 | 1035 | |
1036 | /* Check lifetime and timeout handlers ONCE each second. | |
1037 | * Replaces brain-dead check every time through the loop! */ | |
b8de7ebe | 1038 | if (squid_curtime > last_timeout) { |
1039 | last_timeout = squid_curtime; | |
090089c4 | 1040 | checkTimeouts(); |
090089c4 | 1041 | } |
7d49daab | 1042 | if (num == 0) |
1043 | continue; | |
1044 | ||
090089c4 | 1045 | /* scan each socket but the accept socket. Poll this |
2c5294ce | 1046 | * more frequently to minimize losses due to the 5 connect |
090089c4 | 1047 | * limit in SunOS */ |
1048 | ||
5742d7c9 | 1049 | for (fd = 0; fd < maxfd; fd++) { |
d0217c9b | 1050 | if (!FD_ISSET(fd, &readfds) && !FD_ISSET(fd, &writefds)) |
7d49daab | 1051 | continue; |
cb2f803a | 1052 | if ((incoming_counter++ & (lastinc > 0 ? 1 : 7)) == 0) |
1053 | comm_select_incoming(); | |
812ed90c | 1054 | if (fdIsHttpOrIcp(fd)) |
7d49daab | 1055 | continue; |
7d49daab | 1056 | if (FD_ISSET(fd, &readfds)) { |
a3d5953d | 1057 | debug(5, 6) ("comm_select: FD %d ready for reading\n", fd); |
7d49daab | 1058 | if (fd_table[fd].read_handler) { |
ff8d0ea6 | 1059 | hdl = fd_table[fd].read_handler; |
7d49daab | 1060 | fd_table[fd].read_handler = 0; |
ff8d0ea6 | 1061 | hdl(fd, fd_table[fd].read_data); |
090089c4 | 1062 | } |
7d49daab | 1063 | } |
1064 | if (FD_ISSET(fd, &writefds)) { | |
a3d5953d | 1065 | debug(5, 5) ("comm_select: FD %d ready for writing\n", fd); |
7d49daab | 1066 | if (fd_table[fd].write_handler) { |
ff8d0ea6 | 1067 | hdl = fd_table[fd].write_handler; |
7d49daab | 1068 | fd_table[fd].write_handler = 0; |
ff8d0ea6 | 1069 | hdl(fd, fd_table[fd].write_data); |
090089c4 | 1070 | } |
7d49daab | 1071 | } |
97c03d3c | 1072 | lastinc = polledinc; |
090089c4 | 1073 | } |
7d49daab | 1074 | return COMM_OK; |
97c03d3c | 1075 | } while (timeout > squid_curtime); |
a3d5953d | 1076 | debug(5, 8) ("comm_select: time out: %d.\n", squid_curtime); |
090089c4 | 1077 | return COMM_TIMEOUT; |
1078 | } | |
dcfe6390 | 1079 | #endif |
090089c4 | 1080 | |
b8d8561b | 1081 | void |
582b6456 | 1082 | commSetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout) |
090089c4 | 1083 | { |
5c5783a2 | 1084 | FD_ENTRY *fde; |
489b22c1 | 1085 | assert(fd >= 0); |
5c5783a2 | 1086 | fde = &fd_table[fd]; |
489b22c1 | 1087 | debug(5,5)("commSetSelect: FD %d, handler=%p, data=%p\n", fd, handler, client_data); |
090089c4 | 1088 | if (type & COMM_SELECT_READ) { |
4f92c80c | 1089 | fde->read_handler = handler; |
1090 | fde->read_data = client_data; | |
090089c4 | 1091 | } |
1092 | if (type & COMM_SELECT_WRITE) { | |
4f92c80c | 1093 | fde->write_handler = handler; |
1094 | fde->write_data = client_data; | |
090089c4 | 1095 | } |
5c5783a2 | 1096 | if (timeout) |
4f92c80c | 1097 | fde->timeout = squid_curtime + timeout; |
090089c4 | 1098 | } |
1099 | ||
b8d8561b | 1100 | void |
582b6456 | 1101 | comm_add_close_handler(int fd, PF * handler, void *data) |
30a4f2a8 | 1102 | { |
1103 | struct close_handler *new = xmalloc(sizeof(*new)); | |
a3d5953d | 1104 | debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n", |
e0c42e90 | 1105 | fd, handler, data); |
30a4f2a8 | 1106 | new->handler = handler; |
1107 | new->data = data; | |
1108 | new->next = fd_table[fd].close_handler; | |
1109 | fd_table[fd].close_handler = new; | |
1110 | } | |
1111 | ||
b8d8561b | 1112 | void |
582b6456 | 1113 | comm_remove_close_handler(int fd, PF * handler, void *data) |
090089c4 | 1114 | { |
f88211e8 | 1115 | struct close_handler *p; |
1116 | struct close_handler *last = NULL; | |
30a4f2a8 | 1117 | /* Find handler in list */ |
1118 | for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next) | |
1119 | if (p->handler == handler && p->data == data) | |
1120 | break; /* This is our handler */ | |
f88211e8 | 1121 | assert(p != NULL); |
30a4f2a8 | 1122 | /* Remove list entry */ |
1123 | if (last) | |
1124 | last->next = p->next; | |
1125 | else | |
1126 | fd_table[fd].close_handler = p->next; | |
1127 | safe_free(p); | |
1128 | } | |
090089c4 | 1129 | |
b8d8561b | 1130 | static void |
1131 | commSetNoLinger(int fd) | |
30a4f2a8 | 1132 | { |
1133 | struct linger L; | |
090089c4 | 1134 | L.l_onoff = 0; /* off */ |
1135 | L.l_linger = 0; | |
30a4f2a8 | 1136 | if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) |
a3d5953d | 1137 | debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror()); |
090089c4 | 1138 | } |
1139 | ||
b8d8561b | 1140 | static void |
1141 | commSetReuseAddr(int fd) | |
090089c4 | 1142 | { |
1143 | int on = 1; | |
30a4f2a8 | 1144 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) |
a3d5953d | 1145 | debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror()); |
090089c4 | 1146 | } |
1147 | ||
b8d8561b | 1148 | static void |
1149 | commSetTcpRcvbuf(int fd, int size) | |
f868539a | 1150 | { |
1151 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0) | |
a3d5953d | 1152 | debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n", |
b6f794d6 | 1153 | fd, size, xstrerror()); |
f868539a | 1154 | } |
1155 | ||
b8d8561b | 1156 | int |
1157 | commSetNonBlocking(int fd) | |
30a4f2a8 | 1158 | { |
731e4d49 | 1159 | int flags; |
9e205701 | 1160 | int dummy = 0; |
95cf2361 | 1161 | if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) { |
a3d5953d | 1162 | debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror()); |
731e4d49 | 1163 | return COMM_ERROR; |
1164 | } | |
4f92c80c | 1165 | if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) { |
a3d5953d | 1166 | debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror()); |
30a4f2a8 | 1167 | return COMM_ERROR; |
090089c4 | 1168 | } |
090089c4 | 1169 | return 0; |
1170 | } | |
1171 | ||
b8d8561b | 1172 | void |
1173 | commSetCloseOnExec(int fd) | |
3ca60c86 | 1174 | { |
1175 | #ifdef FD_CLOEXEC | |
731e4d49 | 1176 | int flags; |
1177 | if ((flags = fcntl(fd, F_GETFL)) < 0) { | |
a3d5953d | 1178 | debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror()); |
24382924 | 1179 | return; |
3ca60c86 | 1180 | } |
24382924 | 1181 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) |
a3d5953d | 1182 | debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror()); |
3ca60c86 | 1183 | #endif |
1184 | } | |
1185 | ||
e90100aa | 1186 | #ifdef TCP_NODELAY |
1187 | static void | |
1188 | commSetTcpNoDelay(int fd) | |
1189 | { | |
1190 | int on = 1; | |
1191 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) | |
a3d5953d | 1192 | debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror()); |
e90100aa | 1193 | } |
1194 | #endif | |
1195 | ||
b8d8561b | 1196 | int |
0673c0ba | 1197 | comm_init(void) |
090089c4 | 1198 | { |
e83892e9 | 1199 | fd_table = xcalloc(Squid_MaxFD, sizeof(FD_ENTRY)); |
1200 | meta_data.misc += Squid_MaxFD * sizeof(FD_ENTRY); | |
090089c4 | 1201 | /* Keep a few file descriptors free so that we don't run out of FD's |
1202 | * after accepting a client but before it opens a socket or a file. | |
e83892e9 | 1203 | * Since Squid_MaxFD can be as high as several thousand, don't waste them */ |
1204 | RESERVED_FD = min(100, Squid_MaxFD / 4); | |
090089c4 | 1205 | /* hardwired lifetimes */ |
e83892e9 | 1206 | meta_data.misc += Squid_MaxFD * sizeof(int); |
055f4d4d | 1207 | zero_tv.tv_sec = 0; |
1208 | zero_tv.tv_usec = 0; | |
090089c4 | 1209 | return 0; |
1210 | } | |
1211 | ||
1212 | ||
f88211e8 | 1213 | #if !HAVE_POLL |
090089c4 | 1214 | /* |
1215 | * examine_select - debug routine. | |
1216 | * | |
1217 | * I spend the day chasing this core dump that occurs when both the client | |
1218 | * and the server side of a cache fetch simultaneoulsy abort the | |
1219 | * connection. While I haven't really studied the code to figure out how | |
1220 | * it happens, the snippet below may prevent the cache from exitting: | |
1221 | * | |
1222 | * Call this from where the select loop fails. | |
1223 | */ | |
b8d8561b | 1224 | static int |
5742d7c9 | 1225 | examine_select(fd_set * readfds, fd_set * writefds) |
090089c4 | 1226 | { |
1227 | int fd = 0; | |
bbc5ea8f | 1228 | fd_set read_x; |
1229 | fd_set write_x; | |
090089c4 | 1230 | int num; |
1231 | struct timeval tv; | |
30a4f2a8 | 1232 | struct close_handler *ch = NULL; |
1233 | struct close_handler *next = NULL; | |
5c5783a2 | 1234 | FD_ENTRY *fde = NULL; |
090089c4 | 1235 | |
a3d5953d | 1236 | debug(5, 0) ("examine_select: Examining open file descriptors...\n"); |
e83892e9 | 1237 | for (fd = 0; fd < Squid_MaxFD; fd++) { |
090089c4 | 1238 | FD_ZERO(&read_x); |
1239 | FD_ZERO(&write_x); | |
090089c4 | 1240 | tv.tv_sec = tv.tv_usec = 0; |
af00901c | 1241 | if (FD_ISSET(fd, readfds)) |
090089c4 | 1242 | FD_SET(fd, &read_x); |
af00901c | 1243 | else if (FD_ISSET(fd, writefds)) |
1244 | FD_SET(fd, &write_x); | |
af00901c | 1245 | else |
1246 | continue; | |
e83892e9 | 1247 | num = select(Squid_MaxFD, &read_x, &write_x, NULL, &tv); |
af00901c | 1248 | if (num > -1) { |
a3d5953d | 1249 | debug(5, 5) ("FD %d is valid.\n", fd); |
af00901c | 1250 | continue; |
1251 | } | |
5c5783a2 | 1252 | fde = &fd_table[fd]; |
a3d5953d | 1253 | debug(5, 0) ("FD %d: %s\n", fd, xstrerror()); |
1254 | debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd); | |
1255 | debug(5, 0) ("FD %d is a %s called '%s'\n", | |
ca98227c | 1256 | fd, |
95d15928 | 1257 | fdstatTypeStr[fd_table[fd].type], |
5c5783a2 | 1258 | fde->desc); |
a3d5953d | 1259 | debug(5, 0) ("tmout:%p read:%p write:%p\n", |
5c5783a2 | 1260 | fde->timeout_handler, |
1261 | fde->read_handler, | |
1262 | fde->write_handler); | |
1263 | for (ch = fde->close_handler; ch; ch = ch->next) | |
a3d5953d | 1264 | debug(5, 0) (" close handler: %p\n", ch->handler); |
5c5783a2 | 1265 | if (fde->close_handler) { |
1266 | for (ch = fde->close_handler; ch; ch = next) { | |
af00901c | 1267 | next = ch->next; |
1268 | ch->handler(fd, ch->data); | |
1269 | safe_free(ch); | |
090089c4 | 1270 | } |
5c5783a2 | 1271 | } else if (fde->timeout_handler) { |
a3d5953d | 1272 | debug(5, 0) ("examine_select: Calling Timeout Handler\n"); |
5c5783a2 | 1273 | fde->timeout_handler(fd, fde->timeout_data); |
090089c4 | 1274 | } |
5c5783a2 | 1275 | fde->close_handler = NULL; |
1276 | fde->timeout_handler = NULL; | |
1277 | fde->read_handler = NULL; | |
1278 | fde->write_handler = NULL; | |
af00901c | 1279 | FD_CLR(fd, readfds); |
1280 | FD_CLR(fd, writefds); | |
090089c4 | 1281 | } |
090089c4 | 1282 | return 0; |
1283 | } | |
dcfe6390 | 1284 | #endif |
090089c4 | 1285 | |
b8d8561b | 1286 | static void |
0673c0ba | 1287 | checkTimeouts(void) |
090089c4 | 1288 | { |
1289 | int fd; | |
9864ee44 | 1290 | FD_ENTRY *fde = NULL; |
5c5783a2 | 1291 | PF *callback; |
429fdbec | 1292 | for (fd = 0; fd <= Biggest_FD; fd++) { |
1293 | fde = &fd_table[fd]; | |
5c5783a2 | 1294 | if (fde->open != FD_OPEN) |
429fdbec | 1295 | continue; |
5c5783a2 | 1296 | if (fde->timeout == 0) |
30a4f2a8 | 1297 | continue; |
5c5783a2 | 1298 | if (fde->timeout > squid_curtime) |
30a4f2a8 | 1299 | continue; |
a3d5953d | 1300 | debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd); |
5c5783a2 | 1301 | if (fde->timeout_handler) { |
a3d5953d | 1302 | debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd); |
5c5783a2 | 1303 | callback = fde->timeout_handler; |
1304 | fde->timeout_handler = NULL; | |
1305 | callback(fd, fde->timeout_data); | |
30a4f2a8 | 1306 | } else { |
a3d5953d | 1307 | debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd); |
30a4f2a8 | 1308 | comm_close(fd); |
090089c4 | 1309 | } |
1310 | } | |
1311 | } | |
1312 | ||
30a4f2a8 | 1313 | /* Write to FD. */ |
b8d8561b | 1314 | static void |
582b6456 | 1315 | commHandleWrite(int fd, void *data) |
30a4f2a8 | 1316 | { |
f17936ab | 1317 | CommWriteStateData *state = data; |
30a4f2a8 | 1318 | int len = 0; |
1319 | int nleft; | |
1320 | ||
a3d5953d | 1321 | debug(5, 5) ("commHandleWrite: FD %d: state=%p, off %d, sz %d.\n", |
30a4f2a8 | 1322 | fd, state, state->offset, state->size); |
1323 | ||
1324 | nleft = state->size - state->offset; | |
1325 | len = write(fd, state->buf + state->offset, nleft); | |
b69f7771 | 1326 | fd_bytes(fd, len, FD_WRITE); |
30a4f2a8 | 1327 | |
1328 | if (len == 0) { | |
1329 | /* Note we even call write if nleft == 0 */ | |
1330 | /* We're done */ | |
1331 | if (nleft != 0) | |
a3d5953d | 1332 | debug(5, 2) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft); |
f17936ab | 1333 | CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK); |
30a4f2a8 | 1334 | } else if (len < 0) { |
1335 | /* An error */ | |
0a0bf5db | 1336 | if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) { |
a3d5953d | 1337 | debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n", |
30a4f2a8 | 1338 | fd, xstrerror()); |
b177367b | 1339 | commSetSelect(fd, |
30a4f2a8 | 1340 | COMM_SELECT_WRITE, |
cd1fb0eb | 1341 | commHandleWrite, |
b177367b | 1342 | state, |
85d7ea98 | 1343 | 0); |
9864ee44 | 1344 | } else { |
a3d5953d | 1345 | debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n", |
9864ee44 | 1346 | fd, xstrerror()); |
f17936ab | 1347 | CommWriteStateCallbackAndFree(fd, COMM_ERROR); |
30a4f2a8 | 1348 | } |
30a4f2a8 | 1349 | } else { |
1350 | /* A successful write, continue */ | |
1351 | state->offset += len; | |
1352 | if (state->offset < state->size) { | |
1353 | /* Not done, reinstall the write handler and write some more */ | |
b177367b | 1354 | commSetSelect(fd, |
30a4f2a8 | 1355 | COMM_SELECT_WRITE, |
cd1fb0eb | 1356 | commHandleWrite, |
b177367b | 1357 | state, |
85d7ea98 | 1358 | 0); |
9864ee44 | 1359 | } else { |
f17936ab | 1360 | CommWriteStateCallbackAndFree(fd, COMM_OK); |
30a4f2a8 | 1361 | } |
30a4f2a8 | 1362 | } |
1363 | } | |
1364 | ||
1365 | ||
1366 | ||
1367 | /* Select for Writing on FD, until SIZE bytes are sent. Call | |
1368 | * * HANDLER when complete. */ | |
b8d8561b | 1369 | void |
9e4ad609 | 1370 | comm_write(int fd, char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func) |
30a4f2a8 | 1371 | { |
f17936ab | 1372 | CommWriteStateData *state = NULL; |
a3d5953d | 1373 | debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n", |
787869c5 | 1374 | fd, size, handler, handler_data); |
9e4ad609 | 1375 | if (fd_table[fd].rwstate) |
1376 | fatal_dump("comm_write: comm_write is already active"); | |
f17936ab | 1377 | state = xcalloc(1, sizeof(CommWriteStateData)); |
30a4f2a8 | 1378 | state->buf = buf; |
1379 | state->size = size; | |
1380 | state->offset = 0; | |
1381 | state->handler = handler; | |
30a4f2a8 | 1382 | state->handler_data = handler_data; |
86ee2017 | 1383 | state->free = free_func; |
a56a3abe | 1384 | fd_table[fd].rwstate = state; |
1a8f5ed6 | 1385 | cbdataLock(handler_data); |
b177367b | 1386 | commSetSelect(fd, |
30a4f2a8 | 1387 | COMM_SELECT_WRITE, |
cd1fb0eb | 1388 | commHandleWrite, |
b177367b | 1389 | fd_table[fd].rwstate, |
1390 | 0); | |
30a4f2a8 | 1391 | } |