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