]>
Commit | Line | Data |
---|---|---|
da2b3a17 | 1 | |
30a4f2a8 | 2 | /* |
e1b16349 | 3 | * $Id: comm.cc,v 1.238 1998/03/27 19:42:22 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 | |
f88211e8 | 114 | typedef struct { |
115 | char *host; | |
116 | u_short port; | |
117 | struct sockaddr_in S; | |
118 | CNCB *callback; | |
119 | void *data; | |
f88211e8 | 120 | struct in_addr in_addr; |
121 | int locks; | |
03a1ee42 | 122 | int fd; |
22c653cd | 123 | int tries; |
124 | int addrcount; | |
125 | int connstart; | |
f88211e8 | 126 | } ConnectStateData; |
127 | ||
090089c4 | 128 | /* STATIC */ |
0b2db32c | 129 | static int polledinc = 0; |
f5b8bbc4 | 130 | static int commBind(int s, struct in_addr, u_short port); |
f88211e8 | 131 | #if !HAVE_POLL |
f5b8bbc4 | 132 | static int examine_select(fd_set *, fd_set *); |
dcfe6390 | 133 | #endif |
f5b8bbc4 | 134 | static void checkTimeouts(void); |
135 | static void commSetReuseAddr(int); | |
136 | static void commSetNoLinger(int); | |
812ed90c | 137 | #if HAVE_POLL |
f5b8bbc4 | 138 | static void comm_poll_incoming(void); |
812ed90c | 139 | #else |
f5b8bbc4 | 140 | static void comm_select_incoming(void); |
812ed90c | 141 | #endif |
f5b8bbc4 | 142 | static void CommWriteStateCallbackAndFree(int fd, int code); |
30a4f2a8 | 143 | #ifdef TCP_NODELAY |
f5b8bbc4 | 144 | static void commSetTcpNoDelay(int); |
30a4f2a8 | 145 | #endif |
f5b8bbc4 | 146 | static void commSetTcpRcvbuf(int, int); |
f88211e8 | 147 | static PF commConnectFree; |
03a1ee42 | 148 | static PF commConnectHandle; |
149 | static PF commHandleWrite; | |
f5b8bbc4 | 150 | static int fdIsHttpOrIcp(int fd); |
edeb28fd | 151 | static IPH commConnectDnsHandle; |
f5b8bbc4 | 152 | static void commConnectCallback(ConnectStateData * cs, int status); |
da2b3a17 | 153 | static int commDeferRead(int fd); |
22c653cd | 154 | static void commSetConnectTimeout(int fd, time_t timeout); |
155 | static int commResetFD(ConnectStateData * cs); | |
156 | static int commRetryConnect(ConnectStateData * cs); | |
79d39a72 | 157 | static time_t commBackoffTimeout(int); |
30a4f2a8 | 158 | |
30a4f2a8 | 159 | static struct timeval zero_tv; |
090089c4 | 160 | |
b8d8561b | 161 | static void |
f17936ab | 162 | CommWriteStateCallbackAndFree(int fd, int code) |
9864ee44 | 163 | { |
f17936ab | 164 | CommWriteStateData *CommWriteState = fd_table[fd].rwstate; |
165 | CWCB *callback = NULL; | |
1a8f5ed6 | 166 | void *data; |
a56a3abe | 167 | fd_table[fd].rwstate = NULL; |
f17936ab | 168 | if (CommWriteState == NULL) |
9864ee44 | 169 | return; |
c0dec081 | 170 | if (CommWriteState->free_func) { |
171 | CommWriteState->free_func(CommWriteState->buf); | |
f17936ab | 172 | CommWriteState->buf = NULL; |
9864ee44 | 173 | } |
f17936ab | 174 | callback = CommWriteState->handler; |
1a8f5ed6 | 175 | data = CommWriteState->handler_data; |
f17936ab | 176 | CommWriteState->handler = NULL; |
1a8f5ed6 | 177 | if (callback && cbdataValid(data)) |
178 | callback(fd, CommWriteState->buf, CommWriteState->offset, code, data); | |
179 | cbdataUnlock(data); | |
f17936ab | 180 | safe_free(CommWriteState); |
9864ee44 | 181 | } |
182 | ||
090089c4 | 183 | /* Return the local port associated with fd. */ |
b8d8561b | 184 | u_short |
185 | comm_local_port(int fd) | |
090089c4 | 186 | { |
187 | struct sockaddr_in addr; | |
188 | int addr_len = 0; | |
76f87348 | 189 | fde *F = &fd_table[fd]; |
090089c4 | 190 | |
090089c4 | 191 | /* If the fd is closed already, just return */ |
76f87348 | 192 | if (!F->open) { |
a3d5953d | 193 | debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd); |
30a4f2a8 | 194 | return 0; |
090089c4 | 195 | } |
76f87348 | 196 | if (F->local_port) |
197 | return F->local_port; | |
090089c4 | 198 | addr_len = sizeof(addr); |
199 | if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) { | |
a3d5953d | 200 | debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror()); |
30a4f2a8 | 201 | return 0; |
090089c4 | 202 | } |
a3d5953d | 203 | debug(5, 6) ("comm_local_port: FD %d: sockaddr %u.\n", fd, addr.sin_addr.s_addr); |
76f87348 | 204 | F->local_port = ntohs(addr.sin_port); |
205 | return F->local_port; | |
090089c4 | 206 | } |
207 | ||
b8d8561b | 208 | static int |
209 | commBind(int s, struct in_addr in_addr, u_short port) | |
090089c4 | 210 | { |
211 | struct sockaddr_in S; | |
090089c4 | 212 | |
090089c4 | 213 | memset(&S, '\0', sizeof(S)); |
214 | S.sin_family = AF_INET; | |
215 | S.sin_port = htons(port); | |
30a4f2a8 | 216 | S.sin_addr = in_addr; |
090089c4 | 217 | if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0) |
218 | return COMM_OK; | |
a3d5953d | 219 | debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n", |
090089c4 | 220 | s, |
30a4f2a8 | 221 | S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr), |
44a62238 | 222 | (int) port, |
223 | xstrerror()); | |
090089c4 | 224 | return COMM_ERROR; |
225 | } | |
226 | ||
227 | /* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE | |
228 | * is OR of flags specified in comm.h. */ | |
b8d8561b | 229 | int |
16b204c4 | 230 | comm_open(int sock_type, |
cc6a9d2e | 231 | int proto, |
232 | struct in_addr addr, | |
233 | u_short port, | |
234 | int flags, | |
0ee4272b | 235 | const char *note) |
090089c4 | 236 | { |
237 | int new_socket; | |
76f87348 | 238 | fde *F = NULL; |
b6f794d6 | 239 | int tcp_rcv_bufsz = Config.tcpRcvBufsz; |
090089c4 | 240 | |
241 | /* Create socket for accepting new connections. */ | |
16b204c4 | 242 | if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) { |
090089c4 | 243 | /* Increase the number of reserved fd's if calls to socket() |
244 | * are failing because the open file table is full. This | |
245 | * limits the number of simultaneous clients */ | |
246 | switch (errno) { | |
247 | case ENFILE: | |
248 | case EMFILE: | |
a3d5953d | 249 | debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 250 | break; |
251 | default: | |
a3d5953d | 252 | debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror()); |
090089c4 | 253 | } |
4b23e114 | 254 | fdAdjustReserved(); |
603a02fd | 255 | return -1; |
090089c4 | 256 | } |
257 | /* update fdstat */ | |
365e5b34 | 258 | debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket); |
5c5783a2 | 259 | fd_open(new_socket, FD_SOCKET, note); |
76f87348 | 260 | F = &fd_table[new_socket]; |
79a15e0a | 261 | if (!(flags & COMM_NOCLOEXEC)) |
3ca60c86 | 262 | commSetCloseOnExec(new_socket); |
cdc33f35 | 263 | if ((flags & COMM_REUSEADDR)) |
264 | commSetReuseAddr(new_socket); | |
7690e8eb | 265 | if (port > (u_short) 0) { |
30a4f2a8 | 266 | commSetNoLinger(new_socket); |
3b4be6a6 | 267 | if (opt_reuseaddr) |
090089c4 | 268 | commSetReuseAddr(new_socket); |
090089c4 | 269 | } |
a3724d50 | 270 | if (addr.s_addr != no_addr.s_addr) { |
271 | if (commBind(new_socket, addr, port) != COMM_OK) { | |
272 | comm_close(new_socket); | |
603a02fd | 273 | return -1; |
a3724d50 | 274 | } |
23ff6968 | 275 | } |
76f87348 | 276 | F->local_port = port; |
090089c4 | 277 | |
79a15e0a | 278 | if (flags & COMM_NONBLOCKING) |
30a4f2a8 | 279 | if (commSetNonBlocking(new_socket) == COMM_ERROR) |
603a02fd | 280 | return -1; |
30a4f2a8 | 281 | #ifdef TCP_NODELAY |
282 | if (sock_type == SOCK_STREAM) | |
283 | commSetTcpNoDelay(new_socket); | |
284 | #endif | |
f868539a | 285 | if (tcp_rcv_bufsz > 0 && sock_type == SOCK_STREAM) |
286 | commSetTcpRcvbuf(new_socket, tcp_rcv_bufsz); | |
090089c4 | 287 | return new_socket; |
288 | } | |
289 | ||
b4ea1f2e | 290 | /* |
291 | * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to | |
292 | * impose an upper limit. Solaris' listen(3n) page says it has | |
293 | * no limit on this parameter, but sys/socket.h sets SOMAXCONN | |
294 | * to 5. HP-UX currently has a limit of 20. SunOS is 5 and | |
295 | * OSF 3.0 is 8. | |
296 | */ | |
b8d8561b | 297 | int |
298 | comm_listen(int sock) | |
090089c4 | 299 | { |
300 | int x; | |
e83892e9 | 301 | if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) { |
a3d5953d | 302 | debug(50, 0) ("comm_listen: listen(%d, %d): %s\n", |
e83892e9 | 303 | Squid_MaxFD >> 2, |
090089c4 | 304 | sock, xstrerror()); |
305 | return x; | |
306 | } | |
307 | return sock; | |
308 | } | |
309 | ||
e5f6c5c2 | 310 | void |
4f92c80c | 311 | commConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data) |
e924600d | 312 | { |
313 | ConnectStateData *cs = xcalloc(1, sizeof(ConnectStateData)); | |
3f6c0fb2 | 314 | cbdataAdd(cs, MEM_NONE); |
03a1ee42 | 315 | cs->fd = fd; |
e924600d | 316 | cs->host = xstrdup(host); |
317 | cs->port = port; | |
318 | cs->callback = callback; | |
319 | cs->data = data; | |
b716a8ad | 320 | cbdataLock(cs->data); |
e924600d | 321 | comm_add_close_handler(fd, commConnectFree, cs); |
f88211e8 | 322 | cs->locks++; |
8407afee | 323 | ipcache_nbgethostbyname(host, commConnectDnsHandle, cs); |
edeb28fd | 324 | } |
325 | ||
326 | static void | |
03a1ee42 | 327 | commConnectDnsHandle(const ipcache_addrs * ia, void *data) |
edeb28fd | 328 | { |
329 | ConnectStateData *cs = data; | |
f88211e8 | 330 | assert(cs->locks == 1); |
331 | cs->locks--; | |
edeb28fd | 332 | if (ia == NULL) { |
a3d5953d | 333 | debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host); |
6cf028ab | 334 | if (!dns_error_message) { |
335 | dns_error_message = "Unknown DNS error"; | |
0e473d70 | 336 | debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n"); |
6cf028ab | 337 | } |
a64c2869 | 338 | assert(dns_error_message != NULL); |
03a1ee42 | 339 | commConnectCallback(cs, COMM_ERR_DNS); |
edeb28fd | 340 | return; |
341 | } | |
f076b37b | 342 | assert(ia->cur < ia->count); |
edeb28fd | 343 | cs->in_addr = ia->in_addrs[ia->cur]; |
22c653cd | 344 | ipcacheCycleAddr(cs->host); |
345 | cs->addrcount = ia->count; | |
346 | cs->connstart = squid_curtime; | |
79d39a72 | 347 | commSetConnectTimeout(cs->fd, commBackoffTimeout((int) ia->count)); |
03a1ee42 | 348 | commConnectHandle(cs->fd, cs); |
e924600d | 349 | } |
350 | ||
f88211e8 | 351 | static void |
03a1ee42 | 352 | commConnectCallback(ConnectStateData * cs, int status) |
f88211e8 | 353 | { |
a3d5953d | 354 | CNCB *callback = cs->callback; |
355 | void *data = cs->data; | |
03a1ee42 | 356 | int fd = cs->fd; |
a3d5953d | 357 | comm_remove_close_handler(fd, commConnectFree, cs); |
9daca08e | 358 | cs->callback = NULL; |
359 | cs->data = NULL; | |
e1b16349 | 360 | commSetTimeout(fd, -1, NULL, NULL); |
a3d5953d | 361 | commConnectFree(fd, cs); |
8407afee | 362 | if (cbdataValid(data)) |
365e5b34 | 363 | callback(fd, status, data); |
1790d392 | 364 | cbdataUnlock(data); |
f88211e8 | 365 | } |
366 | ||
e924600d | 367 | static void |
9daca08e | 368 | commConnectFree(int fd, void *data) |
e924600d | 369 | { |
370 | ConnectStateData *cs = data; | |
9daca08e | 371 | debug(5, 3) ("commConnectFree: FD %d\n", fd); |
8407afee | 372 | if (cs->locks) |
365e5b34 | 373 | ipcacheUnregister(cs->host, cs); |
9daca08e | 374 | if (cs->data) |
375 | cbdataUnlock(cs->data); | |
8407afee | 376 | safe_free(cs->host); |
377 | cbdataFree(cs); | |
e924600d | 378 | } |
379 | ||
22c653cd | 380 | /* Reset FD so that we can connect() again */ |
edeb28fd | 381 | static int |
22c653cd | 382 | commResetFD(ConnectStateData * cs) |
edeb28fd | 383 | { |
384 | int fd2; | |
7dd44885 | 385 | if (!cbdataValid(cs->data)) |
386 | return 0; | |
edeb28fd | 387 | fd2 = socket(AF_INET, SOCK_STREAM, 0); |
388 | if (fd2 < 0) { | |
22c653cd | 389 | debug(5, 0) ("commResetFD: socket: %s\n", xstrerror()); |
4b23e114 | 390 | fdAdjustReserved(); |
edeb28fd | 391 | return 0; |
392 | } | |
22c653cd | 393 | if (dup2(fd2, cs->fd) < 0) { |
394 | debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror()); | |
4b23e114 | 395 | fdAdjustReserved(); |
edeb28fd | 396 | return 0; |
397 | } | |
edeb28fd | 398 | close(fd2); |
79d39a72 | 399 | commSetConnectTimeout(cs->fd, commBackoffTimeout((int) cs->addrcount)); |
22c653cd | 400 | commSetNonBlocking(cs->fd); |
edeb28fd | 401 | return 1; |
402 | } | |
403 | ||
22c653cd | 404 | static int |
405 | commRetryConnect(ConnectStateData * cs) | |
406 | { | |
407 | assert(cs->addrcount > 0); | |
408 | if (cs->addrcount == 1) { | |
409 | if (cs->tries >= Config.retry.maxtries) | |
410 | return 0; | |
411 | if (squid_curtime - cs->connstart > Config.Timeout.connect) | |
412 | return 0; | |
79d39a72 | 413 | commSetConnectTimeout(cs->fd, commBackoffTimeout(100)); |
22c653cd | 414 | } else { |
415 | if (cs->tries > cs->addrcount) | |
416 | return 0; | |
417 | } | |
418 | return commResetFD(cs); | |
419 | } | |
420 | ||
421 | /* Back off the socket timeout if there are several addresses available */ | |
422 | static time_t | |
79d39a72 | 423 | commBackoffTimeout(int numaddrs) |
22c653cd | 424 | { |
425 | time_t timeout; | |
426 | timeout = (time_t) Config.Timeout.connect; | |
427 | if (numaddrs > 2) { | |
428 | timeout = (time_t) (Config.Timeout.connect / numaddrs); | |
429 | if (timeout < Config.retry.timeout) | |
430 | timeout = (time_t) Config.retry.timeout; | |
431 | } | |
432 | return timeout; | |
433 | } | |
434 | ||
e924600d | 435 | /* Connect SOCK to specified DEST_PORT at DEST_HOST. */ |
436 | static void | |
437 | commConnectHandle(int fd, void *data) | |
090089c4 | 438 | { |
f88211e8 | 439 | ConnectStateData *cs = data; |
440 | if (cs->S.sin_addr.s_addr == 0) { | |
441 | cs->S.sin_family = AF_INET; | |
442 | cs->S.sin_addr = cs->in_addr; | |
443 | cs->S.sin_port = htons(cs->port); | |
17a0a4ee | 444 | if (Config.onoff.log_fqdn) |
f88211e8 | 445 | fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS); |
e5f6c5c2 | 446 | } |
f88211e8 | 447 | switch (comm_connect_addr(fd, &cs->S)) { |
e5f6c5c2 | 448 | case COMM_INPROGRESS: |
11994bb9 | 449 | debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd); |
f88211e8 | 450 | commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0); |
e5f6c5c2 | 451 | break; |
452 | case COMM_OK: | |
22c653cd | 453 | ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr); |
03a1ee42 | 454 | commConnectCallback(cs, COMM_OK); |
e5f6c5c2 | 455 | break; |
456 | default: | |
22c653cd | 457 | cs->tries++; |
458 | ipcacheMarkBadAddr(cs->host, cs->S.sin_addr); | |
194dd3b8 | 459 | if (Config.onoff.test_reachability) |
460 | netdbDeleteAddrNetwork(cs->S.sin_addr); | |
22c653cd | 461 | if (commRetryConnect(cs)) { |
f88211e8 | 462 | cs->locks++; |
8407afee | 463 | ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs); |
edeb28fd | 464 | } else { |
03a1ee42 | 465 | commConnectCallback(cs, COMM_ERR_CONNECT); |
edeb28fd | 466 | } |
e5f6c5c2 | 467 | break; |
090089c4 | 468 | } |
090089c4 | 469 | } |
22c653cd | 470 | |
b8d8561b | 471 | int |
4f92c80c | 472 | commSetTimeout(int fd, int timeout, PF * handler, void *data) |
090089c4 | 473 | { |
76f87348 | 474 | fde *F; |
a3d5953d | 475 | debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout); |
03eb2f01 | 476 | assert(fd >= 0); |
477 | assert(fd < Squid_MaxFD); | |
76f87348 | 478 | F = &fd_table[fd]; |
0ca54a51 | 479 | assert(F->open); |
5c5783a2 | 480 | if (timeout < 0) { |
76f87348 | 481 | F->timeout_handler = NULL; |
482 | F->timeout_data = NULL; | |
483 | return F->timeout = 0; | |
5c5783a2 | 484 | } |
bbdb774b | 485 | if (shutdown_pending || reconfigure_pending) { |
4f92c80c | 486 | /* don't increase the timeout if something pending */ |
76f87348 | 487 | if (F->timeout > 0 && (int) (F->timeout - squid_curtime) < timeout) |
488 | return F->timeout; | |
5c5783a2 | 489 | } |
76f87348 | 490 | assert(handler || F->timeout_handler); |
5c5783a2 | 491 | if (handler || data) { |
76f87348 | 492 | F->timeout_handler = handler; |
493 | F->timeout_data = data; | |
30a4f2a8 | 494 | } |
76f87348 | 495 | return F->timeout = squid_curtime + (time_t) timeout; |
090089c4 | 496 | } |
497 | ||
b8d8561b | 498 | int |
0ee4272b | 499 | comm_connect_addr(int sock, const struct sockaddr_in *address) |
090089c4 | 500 | { |
501 | int status = COMM_OK; | |
76f87348 | 502 | fde *F = &fd_table[sock]; |
090089c4 | 503 | int len; |
504 | int x; | |
489b22c1 | 505 | assert(ntohs(address->sin_port) != 0); |
090089c4 | 506 | /* Establish connection. */ |
086bce16 | 507 | if (connect(sock, (struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) { |
365e5b34 | 508 | debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror()); |
090089c4 | 509 | switch (errno) { |
510 | case EALREADY: | |
f81b2bec | 511 | #ifdef _SQUID_HPUX_ |
512 | /* | |
513 | * On my HP-UX box (HP-UX tirana B.10.10 A 9000/851), | |
514 | * we get into fast loops on EALREADY. select(2) continually | |
515 | * says the FD is ready for writing, but connect always | |
516 | * returns EALREADY. I applied a patch (PHNE_12906) but | |
164f7660 | 517 | * it didn't help. -DW Dec 1, 1997 |
f81b2bec | 518 | */ |
519 | debug(50, 1) ("connect: %s:%d: %s.\n", | |
520 | fqdnFromAddr(address->sin_addr), | |
521 | ntohs(address->sin_port), | |
522 | xstrerror()); | |
523 | return COMM_ERROR; | |
524 | /* NOTREACHEd */ | |
525 | #endif | |
30a4f2a8 | 526 | #if EAGAIN != EWOULDBLOCK |
527 | case EAGAIN: | |
528 | #endif | |
0a0bf5db | 529 | case EINTR: |
30a4f2a8 | 530 | case EWOULDBLOCK: |
090089c4 | 531 | case EINPROGRESS: |
e5f6c5c2 | 532 | status = COMM_INPROGRESS; |
090089c4 | 533 | break; |
534 | case EISCONN: | |
535 | status = COMM_OK; | |
536 | break; | |
537 | case EINVAL: | |
538 | len = sizeof(x); | |
539 | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &x, &len) >= 0) | |
540 | errno = x; | |
541 | default: | |
a3d5953d | 542 | debug(50, 2) ("connect: %s:%d: %s.\n", |
28ab0c0a | 543 | fqdnFromAddr(address->sin_addr), |
090089c4 | 544 | ntohs(address->sin_port), |
545 | xstrerror()); | |
546 | return COMM_ERROR; | |
547 | } | |
e5f6c5c2 | 548 | } |
76f87348 | 549 | xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16); |
550 | F->remote_port = ntohs(address->sin_port); | |
090089c4 | 551 | if (status == COMM_OK) { |
a3d5953d | 552 | debug(5, 10) ("comm_connect_addr: FD %d connected to %s:%d\n", |
76f87348 | 553 | sock, F->ipaddr, F->remote_port); |
f21cd581 | 554 | } else if (status == COMM_INPROGRESS) { |
a3d5953d | 555 | debug(5, 10) ("comm_connect_addr: FD %d connection pending\n", sock); |
090089c4 | 556 | } |
557 | /* Add new socket to list of open sockets. */ | |
090089c4 | 558 | return status; |
559 | } | |
560 | ||
561 | /* Wait for an incoming connection on FD. FD should be a socket returned | |
562 | * from comm_listen. */ | |
b8d8561b | 563 | int |
564 | comm_accept(int fd, struct sockaddr_in *peer, struct sockaddr_in *me) | |
090089c4 | 565 | { |
566 | int sock; | |
1f9afe33 | 567 | struct sockaddr_in P; |
568 | struct sockaddr_in M; | |
090089c4 | 569 | int Slen; |
76f87348 | 570 | fde *F = NULL; |
090089c4 | 571 | |
1f9afe33 | 572 | Slen = sizeof(P); |
573 | while ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) { | |
090089c4 | 574 | switch (errno) { |
575 | #if EAGAIN != EWOULDBLOCK | |
576 | case EAGAIN: | |
577 | #endif | |
578 | case EWOULDBLOCK: | |
090089c4 | 579 | case EINTR: |
0a0bf5db | 580 | return COMM_NOMESSAGE; |
090089c4 | 581 | case ENFILE: |
582 | case EMFILE: | |
090089c4 | 583 | return COMM_ERROR; |
584 | default: | |
a3d5953d | 585 | debug(50, 1) ("comm_accept: FD %d: accept failure: %s\n", |
090089c4 | 586 | fd, xstrerror()); |
587 | return COMM_ERROR; | |
588 | } | |
79d39a72 | 589 | /* NOTREACHED */ |
090089c4 | 590 | } |
591 | ||
592 | if (peer) | |
1f9afe33 | 593 | *peer = P; |
4053a845 | 594 | Slen = sizeof(M); |
595 | memset(&M, '\0', Slen); | |
596 | getsockname(sock, (struct sockaddr *) &M, &Slen); | |
597 | if (me) | |
1f9afe33 | 598 | *me = M; |
3ca60c86 | 599 | commSetCloseOnExec(sock); |
090089c4 | 600 | /* fdstat update */ |
5c5783a2 | 601 | fd_open(sock, FD_SOCKET, "HTTP Request"); |
76f87348 | 602 | F = &fd_table[sock]; |
c0dec081 | 603 | xstrncpy(F->ipaddr, inet_ntoa(P.sin_addr), 16); |
76f87348 | 604 | F->remote_port = htons(P.sin_port); |
605 | F->local_port = htons(M.sin_port); | |
090089c4 | 606 | commSetNonBlocking(sock); |
090089c4 | 607 | return sock; |
608 | } | |
609 | ||
cb201b7e | 610 | void |
611 | commCallCloseHandlers(int fd) | |
612 | { | |
76f87348 | 613 | fde *F = &fd_table[fd]; |
f1dc9b30 | 614 | close_handler *ch; |
a3d5953d | 615 | debug(5, 5) ("commCallCloseHandlers: FD %d\n", fd); |
76f87348 | 616 | while ((ch = F->close_handler) != NULL) { |
617 | F->close_handler = ch->next; | |
9daca08e | 618 | debug(5, 5) ("commCallCloseHandlers: ch->handler=%p\n", ch->handler); |
603a02fd | 619 | if (cbdataValid(ch->data)) |
620 | ch->handler(fd, ch->data); | |
621 | cbdataUnlock(ch->data); | |
cb201b7e | 622 | safe_free(ch); |
623 | } | |
624 | } | |
625 | ||
b8d8561b | 626 | void |
627 | comm_close(int fd) | |
090089c4 | 628 | { |
76f87348 | 629 | fde *F = NULL; |
a3d5953d | 630 | debug(5, 5) ("comm_close: FD %d\n", fd); |
03eb2f01 | 631 | assert(fd >= 0); |
632 | assert(fd < Squid_MaxFD); | |
76f87348 | 633 | F = &fd_table[fd]; |
79a15e0a | 634 | if (EBIT_TEST(F->flags, FD_CLOSING)) |
e102ebda | 635 | return; |
0e473d70 | 636 | if (shutdown_pending && (!F->open || F->type == FD_FILE)) |
6cf028ab | 637 | return; |
b716a8ad | 638 | assert(F->open); |
76f87348 | 639 | assert(F->type != FD_FILE); |
79a15e0a | 640 | EBIT_SET(F->flags, FD_CLOSING); |
96f1be5d | 641 | CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); |
cb201b7e | 642 | commCallCloseHandlers(fd); |
b716a8ad | 643 | if (F->uses) /* assume persistent connect count */ |
644 | pconnHistCount(1, F->uses); | |
5c5783a2 | 645 | fd_close(fd); /* update fdstat */ |
5874bf28 | 646 | #if defined(_SQUID_LINUX_) |
647 | /* | |
648 | * michael@metal.iinet.net.au sez close() on | |
649 | * network sockets never blocks. | |
650 | */ | |
651 | close(fd); | |
652 | #elsif USE_ASYNC_IO | |
0a0bf5db | 653 | aioClose(fd); |
654 | #else | |
9864ee44 | 655 | close(fd); |
0a0bf5db | 656 | #endif |
090089c4 | 657 | } |
658 | ||
090089c4 | 659 | /* Send a udp datagram to specified TO_ADDR. */ |
b8d8561b | 660 | int |
5df61230 | 661 | comm_udp_sendto(int fd, |
662 | const struct sockaddr_in *to_addr, | |
663 | int addr_len, | |
664 | const char *buf, | |
665 | int len) | |
090089c4 | 666 | { |
5df61230 | 667 | int x; |
668 | x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len); | |
669 | if (x < 0) { | |
a3d5953d | 670 | debug(50, 1) ("comm_udp_sendto: FD %d, %s, port %d: %s\n", |
5df61230 | 671 | fd, |
672 | inet_ntoa(to_addr->sin_addr), | |
673 | (int) htons(to_addr->sin_port), | |
674 | xstrerror()); | |
090089c4 | 675 | return COMM_ERROR; |
676 | } | |
5df61230 | 677 | return x; |
090089c4 | 678 | } |
679 | ||
b8d8561b | 680 | void |
70a9dab4 | 681 | commSetDefer(int fd, DEFER * func, void *data) |
4883993a | 682 | { |
da2b3a17 | 683 | fde *F = &fd_table[fd]; |
684 | F->defer_check = func; | |
70a9dab4 | 685 | F->defer_data = data; |
4883993a | 686 | } |
687 | ||
da2b3a17 | 688 | static int |
689 | commDeferRead(int fd) | |
690 | { | |
691 | fde *F = &fd_table[fd]; | |
692 | if (F->defer_check == NULL) | |
693 | return 0; | |
70a9dab4 | 694 | return F->defer_check(fd, F->defer_data); |
da2b3a17 | 695 | } |
dcfe6390 | 696 | |
f88211e8 | 697 | #if HAVE_POLL |
dcfe6390 | 698 | |
699 | /* poll() version by: | |
700 | * Stewart Forster <slf@connect.com.au>, and | |
701 | * Anthony Baxter <arb@connect.com.au> */ | |
702 | ||
703 | static void | |
812ed90c | 704 | comm_poll_incoming(void) |
dcfe6390 | 705 | { |
429fdbec | 706 | int fd; |
996a0a51 | 707 | int fds[4]; |
0b2421ea | 708 | struct pollfd pfds[3 + MAXHTTPPORTS]; |
996a0a51 | 709 | unsigned long N = 0; |
429fdbec | 710 | unsigned long i, nfds; |
812ed90c | 711 | int j; |
582b6456 | 712 | PF *hdl = NULL; |
97c03d3c | 713 | polledinc = 0; |
dcfe6390 | 714 | if (theInIcpConnection >= 0) |
715 | fds[N++] = theInIcpConnection; | |
933c6d93 | 716 | if (theInIcpConnection != theOutIcpConnection) |
1793867a | 717 | if (theOutIcpConnection >= 0) |
933c6d93 | 718 | fds[N++] = theOutIcpConnection; |
0b2421ea | 719 | for (j = 0; j < NHttpSockets; j++) { |
812ed90c | 720 | if (HttpSockets[j] < 0) |
0b2421ea | 721 | continue; |
da2b3a17 | 722 | if (commDeferRead(HttpSockets[j])) |
0b2421ea | 723 | continue; |
812ed90c | 724 | fds[N++] = HttpSockets[j]; |
725 | } | |
429fdbec | 726 | for (i = nfds = 0; i < N; i++) { |
727 | int events; | |
dcfe6390 | 728 | fd = fds[i]; |
429fdbec | 729 | events = 0; |
730 | if (fd_table[fd].read_handler) | |
731 | events |= POLLRDNORM; | |
732 | if (fd_table[fd].write_handler) | |
733 | events |= POLLWRNORM; | |
734 | if (events) { | |
735 | pfds[nfds].fd = fd; | |
736 | pfds[nfds].events = events; | |
737 | pfds[nfds].revents = 0; | |
738 | nfds++; | |
dcfe6390 | 739 | } |
dcfe6390 | 740 | } |
429fdbec | 741 | if (!nfds) |
742 | return; | |
dc835977 | 743 | #if !ALARM_UPDATES_TIME |
744 | getCurrentTime(); | |
745 | #endif | |
97c03d3c | 746 | polledinc = poll(pfds, nfds, 0); |
747 | if (polledinc < 1) { | |
748 | polledinc = 0; | |
996a0a51 | 749 | return; |
97c03d3c | 750 | } |
429fdbec | 751 | for (i = 0; i < nfds; i++) { |
752 | int revents; | |
753 | if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1)) | |
dcfe6390 | 754 | continue; |
429fdbec | 755 | if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) { |
0e473d70 | 756 | if (hdl = fd_table[fd].read_handler) { |
6cf028ab | 757 | fd_table[fd].read_handler = NULL; |
758 | hdl(fd, fd_table[fd].read_data); | |
759 | } else | |
0e473d70 | 760 | debug(5, 1) ("comm_poll_incoming: NULL read handler\n"); |
dcfe6390 | 761 | } |
429fdbec | 762 | if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) { |
0e473d70 | 763 | if (hdl = fd_table[fd].write_handler) { |
6cf028ab | 764 | fd_table[fd].write_handler = NULL; |
765 | hdl(fd, fd_table[fd].write_data); | |
766 | } else | |
0e473d70 | 767 | debug(5, 1) ("comm_poll_incoming: NULL write handler\n"); |
dcfe6390 | 768 | } |
769 | } | |
770 | /* TO FIX: repoll ICP connection here */ | |
771 | } | |
772 | ||
ca98227c | 773 | #else |
dcfe6390 | 774 | |
b8d8561b | 775 | static void |
0673c0ba | 776 | comm_select_incoming(void) |
055f4d4d | 777 | { |
778 | fd_set read_mask; | |
779 | fd_set write_mask; | |
780 | int maxfd = 0; | |
781 | int fd = 0; | |
0b2421ea | 782 | int fds[3 + MAXHTTPPORTS]; |
055f4d4d | 783 | int N = 0; |
784 | int i = 0; | |
582b6456 | 785 | PF *hdl = NULL; |
97c03d3c | 786 | polledinc = 0; |
055f4d4d | 787 | FD_ZERO(&read_mask); |
788 | FD_ZERO(&write_mask); | |
0b2421ea | 789 | for (i = 0; i < NHttpSockets; i++) { |
812ed90c | 790 | if (HttpSockets[i] < 0) |
0b2421ea | 791 | continue; |
da2b3a17 | 792 | if (commDeferRead(HttpSockets[i])) |
0b2421ea | 793 | continue; |
812ed90c | 794 | fds[N++] = HttpSockets[i]; |
795 | } | |
30a4f2a8 | 796 | if (theInIcpConnection >= 0) |
797 | fds[N++] = theInIcpConnection; | |
933c6d93 | 798 | if (theInIcpConnection != theOutIcpConnection) |
799 | if (theOutIcpConnection >= 0) | |
800 | fds[N++] = theOutIcpConnection; | |
055f4d4d | 801 | fds[N++] = 0; |
055f4d4d | 802 | for (i = 0; i < N; i++) { |
803 | fd = fds[i]; | |
804 | if (fd_table[fd].read_handler) { | |
805 | FD_SET(fd, &read_mask); | |
806 | if (fd > maxfd) | |
807 | maxfd = fd; | |
808 | } | |
809 | if (fd_table[fd].write_handler) { | |
810 | FD_SET(fd, &write_mask); | |
811 | if (fd > maxfd) | |
812 | maxfd = fd; | |
813 | } | |
814 | } | |
055f4d4d | 815 | if (maxfd++ == 0) |
816 | return; | |
dc835977 | 817 | #if !ALARM_UPDATES_TIME |
818 | getCurrentTime(); | |
819 | #endif | |
97c03d3c | 820 | polledinc = select(maxfd, &read_mask, &write_mask, NULL, &zero_tv); |
821 | if (polledinc < 1) { | |
822 | polledinc = 0; | |
dcfe6390 | 823 | return; |
97c03d3c | 824 | } |
dcfe6390 | 825 | for (i = 0; i < N; i++) { |
826 | fd = fds[i]; | |
827 | if (FD_ISSET(fd, &read_mask)) { | |
0e473d70 | 828 | if ((hdl = fd_table[fd].read_handler) != NULL) { |
6cf028ab | 829 | fd_table[fd].read_handler = NULL; |
830 | hdl(fd, fd_table[fd].read_data); | |
831 | } else { | |
0e473d70 | 832 | debug(5, 1) ("comm_select_incoming: NULL read handler\n"); |
6cf028ab | 833 | } |
dcfe6390 | 834 | } |
835 | if (FD_ISSET(fd, &write_mask)) { | |
0e473d70 | 836 | if ((hdl = fd_table[fd].write_handler) != NULL) { |
6cf028ab | 837 | fd_table[fd].write_handler = NULL; |
838 | hdl(fd, fd_table[fd].write_data); | |
839 | } else { | |
0e473d70 | 840 | debug(5, 1) ("comm_select_incoming: NULL write handler\n"); |
6cf028ab | 841 | } |
dcfe6390 | 842 | } |
843 | } | |
844 | } | |
845 | #endif | |
846 | ||
812ed90c | 847 | static int |
848 | fdIsHttpOrIcp(int fd) | |
849 | { | |
850 | int j; | |
851 | if (fd == theInIcpConnection) | |
852 | return 1; | |
853 | if (fd == theOutIcpConnection) | |
854 | return 1; | |
855 | for (j = 0; j < NHttpSockets; j++) { | |
856 | if (fd == HttpSockets[j]) | |
857 | return 1; | |
858 | } | |
859 | return 0; | |
860 | } | |
861 | ||
f88211e8 | 862 | #if HAVE_POLL |
dcfe6390 | 863 | /* poll all sockets; call handlers for those that are ready. */ |
864 | int | |
812ed90c | 865 | comm_poll(time_t sec) |
dcfe6390 | 866 | { |
0a0bf5db | 867 | struct pollfd pfds[SQUID_MAXFD]; |
582b6456 | 868 | PF *hdl = NULL; |
dcfe6390 | 869 | int fd; |
870 | int i; | |
871 | int maxfd; | |
996a0a51 | 872 | unsigned long nfds; |
dcfe6390 | 873 | int num; |
dcfe6390 | 874 | static time_t last_timeout = 0; |
97c03d3c | 875 | static int lastinc = 0; |
429fdbec | 876 | int poll_time; |
812ed90c | 877 | static int incoming_counter = 0; |
dcfe6390 | 878 | time_t timeout; |
dcfe6390 | 879 | timeout = squid_curtime + sec; |
880 | do { | |
b984c445 | 881 | #if !ALARM_UPDATES_TIME |
882 | getCurrentTime(); | |
883 | #endif | |
80a7ba7f | 884 | if (shutdown_pending) { |
dcfe6390 | 885 | serverConnectionsClose(); |
dcfe6390 | 886 | dnsShutdownServers(); |
887 | redirectShutdownServers(); | |
429fdbec | 888 | /* shutdown_pending will be set to |
889 | * +1 for SIGTERM | |
890 | * -1 for SIGINT */ | |
bbdb774b | 891 | /* reconfigure_pending always == 1 when SIGHUP received */ |
892 | if (shutdown_pending > 0 || reconfigure_pending > 0) | |
5c5783a2 | 893 | setSocketShutdownLifetimes(Config.shutdownLifetime); |
dcfe6390 | 894 | else |
9e4ad609 | 895 | setSocketShutdownLifetimes(1); |
dcfe6390 | 896 | } |
6cf028ab | 897 | #if USE_ASYNC_IO |
898 | aioCheckCallbacks(); | |
899 | #endif | |
7a974f67 | 900 | comm_poll_incoming(); |
429fdbec | 901 | nfds = 0; |
902 | maxfd = Biggest_FD + 1; | |
429fdbec | 903 | for (i = 0; i < maxfd; i++) { |
904 | int events; | |
905 | events = 0; | |
dcfe6390 | 906 | /* Check each open socket for a handler. */ |
da2b3a17 | 907 | if (fd_table[i].read_handler && !commDeferRead(i)) |
429fdbec | 908 | events |= POLLRDNORM; |
909 | if (fd_table[i].write_handler) | |
910 | events |= POLLWRNORM; | |
911 | if (events) { | |
429fdbec | 912 | pfds[nfds].fd = i; |
913 | pfds[nfds].events = events; | |
914 | pfds[nfds].revents = 0; | |
915 | nfds++; | |
055f4d4d | 916 | } |
0b2421ea | 917 | } |
bbdb774b | 918 | if (shutdown_pending || reconfigure_pending) |
a3d5953d | 919 | debug(5, 2) ("comm_poll: Still waiting on %d FDs\n", nfds); |
dcfe6390 | 920 | if (nfds == 0) |
921 | return COMM_SHUTDOWN; | |
0a0bf5db | 922 | #if USE_ASYNC_IO |
69b63e70 | 923 | poll_time = sec > 0 ? 50 : 0; |
69b63e70 | 924 | #else |
925 | poll_time = sec > 0 ? 1000 : 0; | |
0a0bf5db | 926 | #endif |
dcfe6390 | 927 | for (;;) { |
429fdbec | 928 | num = poll(pfds, nfds, poll_time); |
f2908497 | 929 | Counter.select_loops++; |
dcfe6390 | 930 | if (num >= 0) |
931 | break; | |
932 | if (errno == EINTR) | |
0a0bf5db | 933 | continue; |
a3d5953d | 934 | debug(5, 0) ("comm_poll: poll failure: %s\n", xstrerror()); |
03eb2f01 | 935 | assert(errno != EINVAL); |
dcfe6390 | 936 | return COMM_ERROR; |
937 | /* NOTREACHED */ | |
938 | } | |
a3d5953d | 939 | debug(5, num ? 5 : 8) ("comm_poll: %d sockets ready\n", num); |
5c5783a2 | 940 | /* Check timeout handlers ONCE each second. */ |
dcfe6390 | 941 | if (squid_curtime > last_timeout) { |
942 | last_timeout = squid_curtime; | |
943 | checkTimeouts(); | |
dcfe6390 | 944 | } |
945 | if (num == 0) | |
946 | continue; | |
947 | /* scan each socket but the accept socket. Poll this | |
2c5294ce | 948 | * more frequently to minimize losses due to the 5 connect |
dcfe6390 | 949 | * limit in SunOS */ |
429fdbec | 950 | for (i = 0; i < nfds; i++) { |
951 | int revents; | |
952 | if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1)) | |
dcfe6390 | 953 | continue; |
7a974f67 | 954 | if (fdIsHttpOrIcp(fd)) |
996a0a51 | 955 | continue; |
429fdbec | 956 | if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) { |
a3d5953d | 957 | debug(5, 6) ("comm_poll: FD %d ready for reading\n", fd); |
0b2421ea | 958 | if ((hdl = fd_table[fd].read_handler)) { |
76f87348 | 959 | fd_table[fd].read_handler = NULL; |
0b2421ea | 960 | hdl(fd, fd_table[fd].read_data); |
961 | } | |
7a974f67 | 962 | if ((++incoming_counter & 15) == 0) |
963 | comm_poll_incoming(); | |
dcfe6390 | 964 | } |
429fdbec | 965 | if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) { |
a3d5953d | 966 | debug(5, 5) ("comm_poll: FD %d ready for writing\n", fd); |
0b2421ea | 967 | if ((hdl = fd_table[fd].write_handler)) { |
76f87348 | 968 | fd_table[fd].write_handler = NULL; |
0b2421ea | 969 | hdl(fd, fd_table[fd].write_data); |
970 | } | |
7a974f67 | 971 | if ((++incoming_counter & 15) == 0) |
972 | comm_poll_incoming(); | |
dcfe6390 | 973 | } |
429fdbec | 974 | if (revents & POLLNVAL) { |
f1dc9b30 | 975 | close_handler *ch; |
76f87348 | 976 | fde *F = &fd_table[fd]; |
a3d5953d | 977 | debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd); |
22f5d1ca | 978 | debug(5, 0) ("FD %d is a %s\n", fd, fdTypeStr[fd_table[fd].type]); |
a3d5953d | 979 | debug(5, 0) ("--> %s\n", fd_table[fd].desc); |
980 | debug(5, 0) ("tmout:%p read:%p write:%p\n", | |
76f87348 | 981 | F->timeout_handler, |
982 | F->read_handler, | |
983 | F->write_handler); | |
76f87348 | 984 | for (ch = F->close_handler; ch; ch = ch->next) |
a3d5953d | 985 | debug(5, 0) (" close handler: %p\n", ch->handler); |
76f87348 | 986 | if (F->close_handler) { |
3be3bbc6 | 987 | commCallCloseHandlers(fd); |
76f87348 | 988 | } else if (F->timeout_handler) { |
a3d5953d | 989 | debug(5, 0) ("comm_poll: Calling Timeout Handler\n"); |
76f87348 | 990 | F->timeout_handler(fd, F->timeout_data); |
dcfe6390 | 991 | } |
76f87348 | 992 | F->close_handler = NULL; |
993 | F->timeout_handler = NULL; | |
994 | F->read_handler = NULL; | |
995 | F->write_handler = NULL; | |
0e473d70 | 996 | if (F->open != 0) |
6cf028ab | 997 | fd_close(fd); |
dcfe6390 | 998 | } |
97c03d3c | 999 | lastinc = polledinc; |
dcfe6390 | 1000 | } |
1001 | return COMM_OK; | |
97c03d3c | 1002 | } while (timeout > squid_curtime); |
a3d5953d | 1003 | debug(5, 8) ("comm_poll: time out: %d.\n", squid_curtime); |
dcfe6390 | 1004 | return COMM_TIMEOUT; |
055f4d4d | 1005 | } |
090089c4 | 1006 | |
dcfe6390 | 1007 | #else |
090089c4 | 1008 | |
1009 | /* Select on all sockets; call handlers for those that are ready. */ | |
b8d8561b | 1010 | int |
1011 | comm_select(time_t sec) | |
090089c4 | 1012 | { |
090089c4 | 1013 | fd_set readfds; |
1014 | fd_set writefds; | |
582b6456 | 1015 | PF *hdl = NULL; |
7d49daab | 1016 | int fd; |
1017 | int i; | |
1018 | int maxfd; | |
1019 | int nfds; | |
090089c4 | 1020 | int num; |
97c03d3c | 1021 | static int incoming_counter = 0; |
090089c4 | 1022 | static time_t last_timeout = 0; |
1023 | struct timeval poll_time; | |
97c03d3c | 1024 | static int lastinc; |
7d49daab | 1025 | time_t timeout; |
090089c4 | 1026 | |
1027 | /* assume all process are very fast (less than 1 second). Call | |
1028 | * time() only once */ | |
090089c4 | 1029 | /* use only 1 second granularity */ |
b8de7ebe | 1030 | timeout = squid_curtime + sec; |
090089c4 | 1031 | |
f7361640 | 1032 | do { |
b984c445 | 1033 | #if !ALARM_UPDATES_TIME |
1034 | getCurrentTime(); | |
1035 | #endif | |
6cf028ab | 1036 | |
1037 | #if USE_ASYNC_IO | |
1038 | aioCheckCallbacks(); | |
1039 | #endif | |
1040 | ||
090089c4 | 1041 | FD_ZERO(&readfds); |
1042 | FD_ZERO(&writefds); | |
bbdb774b | 1043 | if (shutdown_pending || reconfigure_pending) { |
30a4f2a8 | 1044 | serverConnectionsClose(); |
f88bb09c | 1045 | dnsShutdownServers(); |
d2af9477 | 1046 | redirectShutdownServers(); |
429fdbec | 1047 | /* shutdown_pending will be set to |
1048 | * +1 for SIGTERM | |
1049 | * -1 for SIGINT */ | |
bbdb774b | 1050 | /* reconfigure_pending always == 1 when SIGHUP received */ |
1051 | if (shutdown_pending > 0 || reconfigure_pending > 0) | |
5c5783a2 | 1052 | setSocketShutdownLifetimes(Config.shutdownLifetime); |
f3753518 | 1053 | else |
3ed6e11e | 1054 | setSocketShutdownLifetimes(1); |
30a4f2a8 | 1055 | } |
7a974f67 | 1056 | comm_select_incoming(); |
4d64d74a | 1057 | nfds = 0; |
429fdbec | 1058 | maxfd = Biggest_FD + 1; |
4d64d74a | 1059 | for (i = 0; i < maxfd; i++) { |
090089c4 | 1060 | /* Check each open socket for a handler. */ |
da2b3a17 | 1061 | if (fd_table[i].read_handler && !commDeferRead(i)) { |
4d64d74a | 1062 | nfds++; |
090089c4 | 1063 | FD_SET(i, &readfds); |
4d64d74a | 1064 | } |
1065 | if (fd_table[i].write_handler) { | |
1066 | nfds++; | |
090089c4 | 1067 | FD_SET(i, &writefds); |
4d64d74a | 1068 | } |
090089c4 | 1069 | } |
bbdb774b | 1070 | if (shutdown_pending || reconfigure_pending) |
a3d5953d | 1071 | debug(5, 2) ("comm_select: Still waiting on %d FDs\n", nfds); |
4d64d74a | 1072 | if (nfds == 0) |
1073 | return COMM_SHUTDOWN; | |
7690e8eb | 1074 | for (;;) { |
89fb2544 | 1075 | poll_time.tv_sec = sec > 0 ? 1 : 0; |
090089c4 | 1076 | poll_time.tv_usec = 0; |
d0217c9b | 1077 | num = select(maxfd, &readfds, &writefds, NULL, &poll_time); |
f2908497 | 1078 | Counter.select_loops++; |
090089c4 | 1079 | if (num >= 0) |
1080 | break; | |
4d64d74a | 1081 | if (errno == EINTR) |
1082 | break; | |
a3d5953d | 1083 | debug(50, 0) ("comm_select: select failure: %s\n", |
30a4f2a8 | 1084 | xstrerror()); |
d0217c9b | 1085 | examine_select(&readfds, &writefds); |
bf9f8f2b | 1086 | return COMM_ERROR; |
30a4f2a8 | 1087 | /* NOTREACHED */ |
090089c4 | 1088 | } |
4d64d74a | 1089 | if (num < 0) |
1090 | continue; | |
a3d5953d | 1091 | debug(5, num ? 5 : 8) ("comm_select: %d sockets ready at %d\n", |
30a4f2a8 | 1092 | num, (int) squid_curtime); |
090089c4 | 1093 | |
1094 | /* Check lifetime and timeout handlers ONCE each second. | |
1095 | * Replaces brain-dead check every time through the loop! */ | |
b8de7ebe | 1096 | if (squid_curtime > last_timeout) { |
1097 | last_timeout = squid_curtime; | |
090089c4 | 1098 | checkTimeouts(); |
090089c4 | 1099 | } |
7d49daab | 1100 | if (num == 0) |
1101 | continue; | |
1102 | ||
090089c4 | 1103 | /* scan each socket but the accept socket. Poll this |
2c5294ce | 1104 | * more frequently to minimize losses due to the 5 connect |
090089c4 | 1105 | * limit in SunOS */ |
1106 | ||
5742d7c9 | 1107 | for (fd = 0; fd < maxfd; fd++) { |
d0217c9b | 1108 | if (!FD_ISSET(fd, &readfds) && !FD_ISSET(fd, &writefds)) |
7d49daab | 1109 | continue; |
7a974f67 | 1110 | if (fdIsHttpOrIcp(fd)) |
7d49daab | 1111 | continue; |
7d49daab | 1112 | if (FD_ISSET(fd, &readfds)) { |
a3d5953d | 1113 | debug(5, 6) ("comm_select: FD %d ready for reading\n", fd); |
7d49daab | 1114 | if (fd_table[fd].read_handler) { |
ff8d0ea6 | 1115 | hdl = fd_table[fd].read_handler; |
76f87348 | 1116 | fd_table[fd].read_handler = NULL; |
ff8d0ea6 | 1117 | hdl(fd, fd_table[fd].read_data); |
090089c4 | 1118 | } |
7a974f67 | 1119 | if ((++incoming_counter & 15) == 0) |
1120 | comm_select_incoming(); | |
7d49daab | 1121 | } |
1122 | if (FD_ISSET(fd, &writefds)) { | |
a3d5953d | 1123 | debug(5, 5) ("comm_select: FD %d ready for writing\n", fd); |
7d49daab | 1124 | if (fd_table[fd].write_handler) { |
ff8d0ea6 | 1125 | hdl = fd_table[fd].write_handler; |
76f87348 | 1126 | fd_table[fd].write_handler = NULL; |
ff8d0ea6 | 1127 | hdl(fd, fd_table[fd].write_data); |
090089c4 | 1128 | } |
7a974f67 | 1129 | if ((++incoming_counter & 15) == 0) |
1130 | comm_select_incoming(); | |
7d49daab | 1131 | } |
97c03d3c | 1132 | lastinc = polledinc; |
090089c4 | 1133 | } |
7d49daab | 1134 | return COMM_OK; |
97c03d3c | 1135 | } while (timeout > squid_curtime); |
a3d5953d | 1136 | debug(5, 8) ("comm_select: time out: %d.\n", squid_curtime); |
090089c4 | 1137 | return COMM_TIMEOUT; |
1138 | } | |
dcfe6390 | 1139 | #endif |
090089c4 | 1140 | |
b8d8561b | 1141 | void |
582b6456 | 1142 | commSetSelect(int fd, unsigned int type, PF * handler, void *client_data, time_t timeout) |
090089c4 | 1143 | { |
89de058c | 1144 | fde *F = &fd_table[fd]; |
489b22c1 | 1145 | assert(fd >= 0); |
89de058c | 1146 | assert(F->open == FD_OPEN); |
cebe3f19 | 1147 | debug(5, 5) ("commSetSelect: FD %d type %d\n", fd, type); |
090089c4 | 1148 | if (type & COMM_SELECT_READ) { |
76f87348 | 1149 | F->read_handler = handler; |
1150 | F->read_data = client_data; | |
090089c4 | 1151 | } |
1152 | if (type & COMM_SELECT_WRITE) { | |
76f87348 | 1153 | F->write_handler = handler; |
1154 | F->write_data = client_data; | |
090089c4 | 1155 | } |
5c5783a2 | 1156 | if (timeout) |
76f87348 | 1157 | F->timeout = squid_curtime + timeout; |
090089c4 | 1158 | } |
1159 | ||
b8d8561b | 1160 | void |
582b6456 | 1161 | comm_add_close_handler(int fd, PF * handler, void *data) |
30a4f2a8 | 1162 | { |
f1dc9b30 | 1163 | close_handler *new = xmalloc(sizeof(*new)); |
a3d5953d | 1164 | debug(5, 5) ("comm_add_close_handler: FD %d, handler=%p, data=%p\n", |
e0c42e90 | 1165 | fd, handler, data); |
30a4f2a8 | 1166 | new->handler = handler; |
1167 | new->data = data; | |
1168 | new->next = fd_table[fd].close_handler; | |
1169 | fd_table[fd].close_handler = new; | |
603a02fd | 1170 | cbdataLock(data); |
30a4f2a8 | 1171 | } |
1172 | ||
b8d8561b | 1173 | void |
582b6456 | 1174 | comm_remove_close_handler(int fd, PF * handler, void *data) |
090089c4 | 1175 | { |
f1dc9b30 | 1176 | close_handler *p; |
1177 | close_handler *last = NULL; | |
30a4f2a8 | 1178 | /* Find handler in list */ |
e869f2bd | 1179 | debug(5, 5) ("comm_remove_close_handler: FD %d, handler=%p, data=%p\n", |
1180 | fd, handler, data); | |
30a4f2a8 | 1181 | for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next) |
1182 | if (p->handler == handler && p->data == data) | |
1183 | break; /* This is our handler */ | |
f88211e8 | 1184 | assert(p != NULL); |
30a4f2a8 | 1185 | /* Remove list entry */ |
1186 | if (last) | |
1187 | last->next = p->next; | |
1188 | else | |
1189 | fd_table[fd].close_handler = p->next; | |
1390960a | 1190 | cbdataUnlock(p->data); |
30a4f2a8 | 1191 | safe_free(p); |
1192 | } | |
090089c4 | 1193 | |
b8d8561b | 1194 | static void |
1195 | commSetNoLinger(int fd) | |
30a4f2a8 | 1196 | { |
1197 | struct linger L; | |
090089c4 | 1198 | L.l_onoff = 0; /* off */ |
1199 | L.l_linger = 0; | |
30a4f2a8 | 1200 | if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) |
a3d5953d | 1201 | debug(50, 0) ("commSetNoLinger: FD %d: %s\n", fd, xstrerror()); |
090089c4 | 1202 | } |
1203 | ||
b8d8561b | 1204 | static void |
1205 | commSetReuseAddr(int fd) | |
090089c4 | 1206 | { |
1207 | int on = 1; | |
30a4f2a8 | 1208 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) |
a3d5953d | 1209 | debug(50, 1) ("commSetReuseAddr: FD %d: %s\n", fd, xstrerror()); |
090089c4 | 1210 | } |
1211 | ||
b8d8561b | 1212 | static void |
1213 | commSetTcpRcvbuf(int fd, int size) | |
f868539a | 1214 | { |
1215 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0) | |
a3d5953d | 1216 | debug(50, 1) ("commSetTcpRcvbuf: FD %d, SIZE %d: %s\n", |
b6f794d6 | 1217 | fd, size, xstrerror()); |
f868539a | 1218 | } |
1219 | ||
b8d8561b | 1220 | int |
1221 | commSetNonBlocking(int fd) | |
30a4f2a8 | 1222 | { |
731e4d49 | 1223 | int flags; |
9e205701 | 1224 | int dummy = 0; |
95cf2361 | 1225 | if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) { |
a3d5953d | 1226 | debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror()); |
731e4d49 | 1227 | return COMM_ERROR; |
1228 | } | |
4f92c80c | 1229 | if (fcntl(fd, F_SETFL, flags | SQUID_NONBLOCK) < 0) { |
a3d5953d | 1230 | debug(50, 0) ("commSetNonBlocking: FD %d: %s\n", fd, xstrerror()); |
30a4f2a8 | 1231 | return COMM_ERROR; |
090089c4 | 1232 | } |
090089c4 | 1233 | return 0; |
1234 | } | |
1235 | ||
b8d8561b | 1236 | void |
1237 | commSetCloseOnExec(int fd) | |
3ca60c86 | 1238 | { |
1239 | #ifdef FD_CLOEXEC | |
731e4d49 | 1240 | int flags; |
7a18b487 | 1241 | int dummy = 0; |
c7989865 | 1242 | if ((flags = fcntl(fd, F_GETFL, dummy)) < 0) { |
a3d5953d | 1243 | debug(50, 0) ("FD %d: fcntl F_GETFL: %s\n", fd, xstrerror()); |
24382924 | 1244 | return; |
3ca60c86 | 1245 | } |
24382924 | 1246 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) |
a3d5953d | 1247 | debug(50, 0) ("FD %d: set close-on-exec failed: %s\n", fd, xstrerror()); |
3ca60c86 | 1248 | #endif |
1249 | } | |
1250 | ||
e90100aa | 1251 | #ifdef TCP_NODELAY |
1252 | static void | |
1253 | commSetTcpNoDelay(int fd) | |
1254 | { | |
1255 | int on = 1; | |
1256 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) | |
a3d5953d | 1257 | debug(50, 1) ("commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror()); |
e90100aa | 1258 | } |
1259 | #endif | |
1260 | ||
b8d8561b | 1261 | int |
0673c0ba | 1262 | comm_init(void) |
090089c4 | 1263 | { |
f1dc9b30 | 1264 | fd_table = xcalloc(Squid_MaxFD, sizeof(fde)); |
59c4d35b | 1265 | /* XXX account fd_table */ |
090089c4 | 1266 | /* Keep a few file descriptors free so that we don't run out of FD's |
1267 | * after accepting a client but before it opens a socket or a file. | |
e83892e9 | 1268 | * Since Squid_MaxFD can be as high as several thousand, don't waste them */ |
0254ee29 | 1269 | RESERVED_FD = XMIN(100, Squid_MaxFD / 4); |
055f4d4d | 1270 | zero_tv.tv_sec = 0; |
1271 | zero_tv.tv_usec = 0; | |
090089c4 | 1272 | return 0; |
1273 | } | |
1274 | ||
1275 | ||
f88211e8 | 1276 | #if !HAVE_POLL |
090089c4 | 1277 | /* |
1278 | * examine_select - debug routine. | |
1279 | * | |
1280 | * I spend the day chasing this core dump that occurs when both the client | |
1281 | * and the server side of a cache fetch simultaneoulsy abort the | |
1282 | * connection. While I haven't really studied the code to figure out how | |
1283 | * it happens, the snippet below may prevent the cache from exitting: | |
1284 | * | |
1285 | * Call this from where the select loop fails. | |
1286 | */ | |
b8d8561b | 1287 | static int |
5742d7c9 | 1288 | examine_select(fd_set * readfds, fd_set * writefds) |
090089c4 | 1289 | { |
1290 | int fd = 0; | |
bbc5ea8f | 1291 | fd_set read_x; |
1292 | fd_set write_x; | |
090089c4 | 1293 | int num; |
1294 | struct timeval tv; | |
f1dc9b30 | 1295 | close_handler *ch = NULL; |
76f87348 | 1296 | fde *F = NULL; |
090089c4 | 1297 | |
a3d5953d | 1298 | debug(5, 0) ("examine_select: Examining open file descriptors...\n"); |
e83892e9 | 1299 | for (fd = 0; fd < Squid_MaxFD; fd++) { |
090089c4 | 1300 | FD_ZERO(&read_x); |
1301 | FD_ZERO(&write_x); | |
090089c4 | 1302 | tv.tv_sec = tv.tv_usec = 0; |
af00901c | 1303 | if (FD_ISSET(fd, readfds)) |
090089c4 | 1304 | FD_SET(fd, &read_x); |
af00901c | 1305 | else if (FD_ISSET(fd, writefds)) |
1306 | FD_SET(fd, &write_x); | |
af00901c | 1307 | else |
1308 | continue; | |
e83892e9 | 1309 | num = select(Squid_MaxFD, &read_x, &write_x, NULL, &tv); |
af00901c | 1310 | if (num > -1) { |
a3d5953d | 1311 | debug(5, 5) ("FD %d is valid.\n", fd); |
af00901c | 1312 | continue; |
1313 | } | |
76f87348 | 1314 | F = &fd_table[fd]; |
a3d5953d | 1315 | debug(5, 0) ("FD %d: %s\n", fd, xstrerror()); |
1316 | debug(5, 0) ("WARNING: FD %d has handlers, but it's invalid.\n", fd); | |
1317 | debug(5, 0) ("FD %d is a %s called '%s'\n", | |
ca98227c | 1318 | fd, |
22f5d1ca | 1319 | fdTypeStr[fd_table[fd].type], |
76f87348 | 1320 | F->desc); |
a3d5953d | 1321 | debug(5, 0) ("tmout:%p read:%p write:%p\n", |
76f87348 | 1322 | F->timeout_handler, |
1323 | F->read_handler, | |
1324 | F->write_handler); | |
1325 | for (ch = F->close_handler; ch; ch = ch->next) | |
a3d5953d | 1326 | debug(5, 0) (" close handler: %p\n", ch->handler); |
76f87348 | 1327 | if (F->close_handler) { |
3be3bbc6 | 1328 | commCallCloseHandlers(fd); |
76f87348 | 1329 | } else if (F->timeout_handler) { |
a3d5953d | 1330 | debug(5, 0) ("examine_select: Calling Timeout Handler\n"); |
76f87348 | 1331 | F->timeout_handler(fd, F->timeout_data); |
090089c4 | 1332 | } |
76f87348 | 1333 | F->close_handler = NULL; |
1334 | F->timeout_handler = NULL; | |
1335 | F->read_handler = NULL; | |
1336 | F->write_handler = NULL; | |
af00901c | 1337 | FD_CLR(fd, readfds); |
1338 | FD_CLR(fd, writefds); | |
090089c4 | 1339 | } |
090089c4 | 1340 | return 0; |
1341 | } | |
dcfe6390 | 1342 | #endif |
090089c4 | 1343 | |
b8d8561b | 1344 | static void |
0673c0ba | 1345 | checkTimeouts(void) |
090089c4 | 1346 | { |
1347 | int fd; | |
76f87348 | 1348 | fde *F = NULL; |
5c5783a2 | 1349 | PF *callback; |
429fdbec | 1350 | for (fd = 0; fd <= Biggest_FD; fd++) { |
76f87348 | 1351 | F = &fd_table[fd]; |
1352 | if (F->open != FD_OPEN) | |
429fdbec | 1353 | continue; |
76f87348 | 1354 | if (F->timeout == 0) |
30a4f2a8 | 1355 | continue; |
76f87348 | 1356 | if (F->timeout > squid_curtime) |
30a4f2a8 | 1357 | continue; |
a3d5953d | 1358 | debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd); |
76f87348 | 1359 | if (F->timeout_handler) { |
a3d5953d | 1360 | debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd); |
76f87348 | 1361 | callback = F->timeout_handler; |
1362 | F->timeout_handler = NULL; | |
1363 | callback(fd, F->timeout_data); | |
30a4f2a8 | 1364 | } else { |
a3d5953d | 1365 | debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd); |
30a4f2a8 | 1366 | comm_close(fd); |
090089c4 | 1367 | } |
1368 | } | |
1369 | } | |
1370 | ||
30a4f2a8 | 1371 | /* Write to FD. */ |
b8d8561b | 1372 | static void |
582b6456 | 1373 | commHandleWrite(int fd, void *data) |
30a4f2a8 | 1374 | { |
f17936ab | 1375 | CommWriteStateData *state = data; |
30a4f2a8 | 1376 | int len = 0; |
1377 | int nleft; | |
1378 | ||
cebe3f19 | 1379 | debug(5, 5) ("commHandleWrite: FD %d: off %d, sz %d.\n", |
1380 | fd, state->offset, state->size); | |
30a4f2a8 | 1381 | |
1382 | nleft = state->size - state->offset; | |
1383 | len = write(fd, state->buf + state->offset, nleft); | |
b69f7771 | 1384 | fd_bytes(fd, len, FD_WRITE); |
30a4f2a8 | 1385 | |
1386 | if (len == 0) { | |
1387 | /* Note we even call write if nleft == 0 */ | |
1388 | /* We're done */ | |
1389 | if (nleft != 0) | |
02be0294 | 1390 | debug(5, 1) ("commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft); |
f17936ab | 1391 | CommWriteStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK); |
30a4f2a8 | 1392 | } else if (len < 0) { |
1393 | /* An error */ | |
26a880e2 | 1394 | if (ignoreErrno(errno)) { |
a3d5953d | 1395 | debug(50, 10) ("commHandleWrite: FD %d: write failure: %s.\n", |
30a4f2a8 | 1396 | fd, xstrerror()); |
b177367b | 1397 | commSetSelect(fd, |
30a4f2a8 | 1398 | COMM_SELECT_WRITE, |
cd1fb0eb | 1399 | commHandleWrite, |
b177367b | 1400 | state, |
85d7ea98 | 1401 | 0); |
9864ee44 | 1402 | } else { |
a3d5953d | 1403 | debug(50, 2) ("commHandleWrite: FD %d: write failure: %s.\n", |
9864ee44 | 1404 | fd, xstrerror()); |
f17936ab | 1405 | CommWriteStateCallbackAndFree(fd, COMM_ERROR); |
30a4f2a8 | 1406 | } |
30a4f2a8 | 1407 | } else { |
1408 | /* A successful write, continue */ | |
1409 | state->offset += len; | |
1410 | if (state->offset < state->size) { | |
1411 | /* Not done, reinstall the write handler and write some more */ | |
b177367b | 1412 | commSetSelect(fd, |
30a4f2a8 | 1413 | COMM_SELECT_WRITE, |
cd1fb0eb | 1414 | commHandleWrite, |
b177367b | 1415 | state, |
85d7ea98 | 1416 | 0); |
9864ee44 | 1417 | } else { |
f17936ab | 1418 | CommWriteStateCallbackAndFree(fd, COMM_OK); |
30a4f2a8 | 1419 | } |
30a4f2a8 | 1420 | } |
1421 | } | |
1422 | ||
1423 | ||
1424 | ||
1425 | /* Select for Writing on FD, until SIZE bytes are sent. Call | |
1426 | * * HANDLER when complete. */ | |
b8d8561b | 1427 | void |
9e4ad609 | 1428 | comm_write(int fd, char *buf, int size, CWCB * handler, void *handler_data, FREE * free_func) |
30a4f2a8 | 1429 | { |
aa9e2cab | 1430 | CommWriteStateData *state = fd_table[fd].rwstate; |
a3d5953d | 1431 | debug(5, 5) ("comm_write: FD %d: sz %d: hndl %p: data %p.\n", |
787869c5 | 1432 | fd, size, handler, handler_data); |
aa9e2cab | 1433 | if (NULL != state) { |
0e473d70 | 1434 | debug(5, 1) ("comm_write: fd_table[%d].rwstate != NULL", fd); |
aa9e2cab | 1435 | safe_free(state); |
6cf028ab | 1436 | fd_table[fd].rwstate = NULL; |
1437 | } | |
aa9e2cab | 1438 | assert(state == NULL); |
1439 | fd_table[fd].rwstate = state = xcalloc(1, sizeof(CommWriteStateData)); | |
30a4f2a8 | 1440 | state->buf = buf; |
1441 | state->size = size; | |
1442 | state->offset = 0; | |
1443 | state->handler = handler; | |
30a4f2a8 | 1444 | state->handler_data = handler_data; |
c0dec081 | 1445 | state->free_func = free_func; |
1a8f5ed6 | 1446 | cbdataLock(handler_data); |
aa9e2cab | 1447 | commSetSelect(fd, COMM_SELECT_WRITE, commHandleWrite, state, 0); |
30a4f2a8 | 1448 | } |
26a880e2 | 1449 | |
cb69b4c7 | 1450 | /* a wrapper around comm_write to allow for MemBuf to comm_written in a snap */ |
1451 | void | |
1452 | comm_write_mbuf(int fd, MemBuf mb, CWCB * handler, void *handler_data) | |
1453 | { | |
1454 | comm_write(fd, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb)); | |
1455 | } | |
1456 | ||
b224ea98 | 1457 | int |
edd2eb63 | 1458 | ignoreErrno(int ierrno) |
26a880e2 | 1459 | { |
edd2eb63 | 1460 | if (ierrno == EWOULDBLOCK) |
26a880e2 | 1461 | return 1; |
1462 | #if EAGAIN != EWOULDBLOCK | |
edd2eb63 | 1463 | if (ierrno == EAGAIN) |
26a880e2 | 1464 | return 1; |
1465 | #endif | |
edd2eb63 | 1466 | if (ierrno == EALREADY) |
26a880e2 | 1467 | return 1; |
edd2eb63 | 1468 | if (ierrno == EINTR) |
26a880e2 | 1469 | return 1; |
1470 | return 0; | |
1471 | } | |
22c653cd | 1472 | |
1473 | static void | |
1474 | commSetConnectTimeout(int fd, time_t timeout) | |
1475 | { | |
1476 | fde *F = &fd_table[fd]; | |
1477 | F->connect_timeout = timeout; | |
1478 | } |