]> git.ipfire.org Git - thirdparty/squid.git/blame - src/comm.cc
s/FD_SETSIZE|SQUID_FD_SETSIZE/SQUID_MAXFD/
[thirdparty/squid.git] / src / comm.cc
CommitLineData
52e1d7e2 1
30a4f2a8 2/*
431d84c5 3 * $Id: comm.cc,v 1.107 1996/11/22 05:07:12 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"
090089c4 108
30a4f2a8 109#ifdef HAVE_NETINET_TCP_H
110#include <netinet/tcp.h>
111#endif
090089c4 112
113/* Block processing new client requests (accepts on ascii port) when we start
114 * running shy of free file descriptors. For example, under SunOS, we'll keep
115 * 64 file descriptors free for disk-i/o and connections to remote servers */
116
da22ac20 117int RESERVED_FD = 64;
090089c4 118
119#define min(x,y) ((x)<(y)? (x) : (y))
120#define max(a,b) ((a)>(b)? (a) : (b))
121
30a4f2a8 122struct _RWStateData {
123 char *buf;
124 long size;
125 long offset;
126 int timeout; /* XXX Not used at present. */
127 time_t time; /* XXX Not used at present. */
128 rw_complete_handler *handler;
129 void *handler_data;
130 int handle_immed;
4a63c85f 131 void (*free) (void *);
30a4f2a8 132};
090089c4 133
134/* GLOBAL */
090089c4 135FD_ENTRY *fd_table = NULL; /* also used in disk.c */
136
137/* STATIC */
24382924 138static int commBind _PARAMS((int s, struct in_addr, u_short port));
139static int comm_cleanup_fd_entry _PARAMS((int));
5742d7c9 140static int examine_select _PARAMS((fd_set *, fd_set *));
67508012 141static void checkTimeouts _PARAMS((void));
142static void checkLifetimes _PARAMS((void));
143static void Reserve_More_FDs _PARAMS((void));
144static void commSetReuseAddr _PARAMS((int));
67508012 145static void commSetNoLinger _PARAMS((int));
146static void comm_select_incoming _PARAMS((void));
67508012 147static void RWStateCallbackAndFree _PARAMS((int fd, int code));
30a4f2a8 148#ifdef TCP_NODELAY
67508012 149static void commSetTcpNoDelay _PARAMS((int));
30a4f2a8 150#endif
67508012 151static void commSetTcpRcvbuf _PARAMS((int, int));
30a4f2a8 152
153static int *fd_lifetime = NULL;
154static struct timeval zero_tv;
090089c4 155
81f754fa 156void
157commCancelRWHandler(int fd)
158{
159 RWStateData *RWState = fd_table[fd].rwstate;
160 if (RWState) {
38cac3c8 161 RWState->handler = NULL;
162 RWState->handler_data = NULL;
81f754fa 163 }
164}
165
166
b8d8561b 167static void
168RWStateCallbackAndFree(int fd, int code)
9864ee44 169{
a56a3abe 170 RWStateData *RWState = fd_table[fd].rwstate;
9864ee44 171 rw_complete_handler *callback = NULL;
a56a3abe 172 fd_table[fd].rwstate = NULL;
9864ee44 173 if (RWState == NULL)
174 return;
175 if (RWState->free) {
176 RWState->free(RWState->buf);
177 RWState->buf = NULL;
178 }
179 callback = RWState->handler;
180 RWState->handler = NULL;
181 if (callback) {
182 callback(fd,
183 RWState->buf,
184 RWState->offset,
185 code,
186 RWState->handler_data);
187 }
188 safe_free(RWState);
189}
190
090089c4 191/* Return the local port associated with fd. */
b8d8561b 192u_short
193comm_local_port(int fd)
090089c4 194{
195 struct sockaddr_in addr;
196 int addr_len = 0;
9864ee44 197 FD_ENTRY *fde = &fd_table[fd];
090089c4 198
090089c4 199 /* If the fd is closed already, just return */
9864ee44 200 if (!fde->openned) {
30a4f2a8 201 debug(5, 0, "comm_local_port: FD %d has been closed.\n", fd);
202 return 0;
090089c4 203 }
9864ee44 204 if (fde->local_port)
205 return fde->local_port;
090089c4 206 addr_len = sizeof(addr);
207 if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {
881f7a6c 208 debug(50, 1, "comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());
30a4f2a8 209 return 0;
090089c4 210 }
30a4f2a8 211 debug(5, 6, "comm_local_port: FD %d: sockaddr %u.\n", fd, addr.sin_addr.s_addr);
9864ee44 212 fde->local_port = ntohs(addr.sin_port);
213 return fde->local_port;
090089c4 214}
215
b8d8561b 216static int
217commBind(int s, struct in_addr in_addr, u_short port)
090089c4 218{
219 struct sockaddr_in S;
090089c4 220
090089c4 221 memset(&S, '\0', sizeof(S));
222 S.sin_family = AF_INET;
223 S.sin_port = htons(port);
30a4f2a8 224 S.sin_addr = in_addr;
090089c4 225 if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)
226 return COMM_OK;
881f7a6c 227 debug(50, 0, "commBind: Cannot bind socket FD %d to %s:%d: %s\n",
090089c4 228 s,
30a4f2a8 229 S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),
090089c4 230 port, xstrerror());
231 return COMM_ERROR;
232}
233
234/* Create a socket. Default is blocking, stream (TCP) socket. IO_TYPE
235 * is OR of flags specified in comm.h. */
b8d8561b 236int
16b204c4 237comm_open(int sock_type,
cc6a9d2e 238 int proto,
239 struct in_addr addr,
240 u_short port,
241 int flags,
0ee4272b 242 const char *note)
090089c4 243{
244 int new_socket;
245 FD_ENTRY *conn = NULL;
b6f794d6 246 int tcp_rcv_bufsz = Config.tcpRcvBufsz;
090089c4 247
248 /* Create socket for accepting new connections. */
16b204c4 249 if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {
090089c4 250 /* Increase the number of reserved fd's if calls to socket()
251 * are failing because the open file table is full. This
252 * limits the number of simultaneous clients */
253 switch (errno) {
254 case ENFILE:
255 case EMFILE:
881f7a6c 256 debug(50, 1, "comm_open: socket failure: %s\n", xstrerror());
090089c4 257 Reserve_More_FDs();
258 break;
259 default:
881f7a6c 260 debug(50, 0, "comm_open: socket failure: %s\n", xstrerror());
090089c4 261 }
262 return (COMM_ERROR);
263 }
264 /* update fdstat */
30a4f2a8 265 fdstat_open(new_socket, FD_SOCKET);
090089c4 266
267 conn = &fd_table[new_socket];
268 memset(conn, '\0', sizeof(FD_ENTRY));
30a4f2a8 269 if (note)
270 fd_note(new_socket, note);
090089c4 271 conn->openned = 1;
272
16b204c4 273 if (!BIT_TEST(flags, COMM_NOCLOEXEC))
3ca60c86 274 commSetCloseOnExec(new_socket);
7690e8eb 275 if (port > (u_short) 0) {
30a4f2a8 276 commSetNoLinger(new_socket);
277 if (do_reuse)
090089c4 278 commSetReuseAddr(new_socket);
090089c4 279 }
30a4f2a8 280 if (addr.s_addr != INADDR_NONE)
281 if (commBind(new_socket, addr, port) != COMM_OK)
282 return COMM_ERROR;
283 conn->local_port = port;
090089c4 284
16b204c4 285 if (BIT_TEST(flags, COMM_NONBLOCKING))
30a4f2a8 286 if (commSetNonBlocking(new_socket) == COMM_ERROR)
287 return COMM_ERROR;
288#ifdef TCP_NODELAY
289 if (sock_type == SOCK_STREAM)
290 commSetTcpNoDelay(new_socket);
291#endif
f868539a 292 if (tcp_rcv_bufsz > 0 && sock_type == SOCK_STREAM)
293 commSetTcpRcvbuf(new_socket, tcp_rcv_bufsz);
16b204c4 294 conn->comm_type = sock_type;
090089c4 295 return new_socket;
296}
297
298 /*
431d84c5 299 * NOTE: set the listen queue to SQUID_MAXFD/4 and rely on the kernel to
090089c4 300 * impose an upper limit. Solaris' listen(3n) page says it has
301 * no limit on this parameter, but sys/socket.h sets SOMAXCONN
302 * to 5. HP-UX currently has a limit of 20. SunOS is 5 and
303 * OSF 3.0 is 8.
304 */
b8d8561b 305int
306comm_listen(int sock)
090089c4 307{
308 int x;
431d84c5 309 if ((x = listen(sock, SQUID_MAXFD >> 2)) < 0) {
881f7a6c 310 debug(50, 0, "comm_listen: listen(%d, %d): %s\n",
431d84c5 311 SQUID_MAXFD >> 2,
090089c4 312 sock, xstrerror());
313 return x;
314 }
315 return sock;
316}
317
090089c4 318/* Connect SOCK to specified DEST_PORT at DEST_HOST. */
e5f6c5c2 319void
320comm_nbconnect(int fd, void *data)
090089c4 321{
e5f6c5c2 322 ConnectStateData *connectState = data;
0ee4272b 323 const ipcache_addrs *ia = NULL;
e5f6c5c2 324 if (connectState->S.sin_addr.s_addr == 0) {
325 ia = ipcache_gethostbyname(connectState->host, IP_BLOCKING_LOOKUP);
326 if (ia == NULL) {
327 debug(5, 3, "comm_nbconnect: Unknown host: %s\n",
328 connectState->host);
329 connectState->handler(fd,
330 COMM_ERROR,
331 connectState->data);
332 return;
333 }
334 connectState->S.sin_family = AF_INET;
335 connectState->S.sin_addr = ia->in_addrs[ia->cur];
336 connectState->S.sin_port = htons(connectState->port);
337 if (Config.Log.log_fqdn)
338 fqdncache_gethostbyaddr(connectState->S.sin_addr, FQDN_LOOKUP_IF_MISS);
339 }
340 switch (comm_connect_addr(fd, &connectState->S)) {
341 case COMM_INPROGRESS:
b177367b 342 commSetSelect(fd,
e5f6c5c2 343 COMM_SELECT_WRITE,
344 comm_nbconnect,
b177367b 345 (void *) connectState,
85d7ea98 346 0);
e5f6c5c2 347 break;
348 case COMM_OK:
349 connectState->handler(fd, COMM_OK, connectState->data);
350 ipcacheCycleAddr(connectState->host);
351 break;
352 default:
353 ipcacheRemoveBadAddr(connectState->host, connectState->S.sin_addr);
354 connectState->handler(fd, COMM_ERROR, connectState->data);
355 break;
090089c4 356 }
090089c4 357}
358
b8d8561b 359int
360comm_set_fd_lifetime(int fd, int lifetime)
090089c4 361{
30a4f2a8 362 debug(5, 3, "comm_set_fd_lifetime: FD %d lft %d\n", fd, lifetime);
431d84c5 363 if (fd < 0 || fd > SQUID_MAXFD)
090089c4 364 return 0;
365 if (lifetime < 0)
366 return fd_lifetime[fd] = -1;
30a4f2a8 367 if (shutdown_pending || reread_pending) {
368 /* don't increase the lifetime if something pending */
369 if (fd_lifetime[fd] > -1 && (fd_lifetime[fd] - squid_curtime) < lifetime)
370 return fd_lifetime[fd];
371 }
b8de7ebe 372 return fd_lifetime[fd] = (int) squid_curtime + lifetime;
090089c4 373}
374
b8d8561b 375int
376comm_get_fd_lifetime(int fd)
090089c4 377{
378 if (fd < 0)
379 return 0;
380 return fd_lifetime[fd];
381}
382
b8d8561b 383int
384comm_get_fd_timeout(int fd)
090089c4 385{
386 if (fd < 0)
387 return 0;
388 return fd_table[fd].timeout_time;
389}
390
b8d8561b 391int
0ee4272b 392comm_connect_addr(int sock, const struct sockaddr_in *address)
090089c4 393{
394 int status = COMM_OK;
395 FD_ENTRY *conn = &fd_table[sock];
396 int len;
397 int x;
398 int lft;
399
400 /* sanity check */
401 if (ntohs(address->sin_port) == 0) {
d1f14731 402 debug(5, 10, "comm_connect_addr: %s:%d: URL uses port 0?\n",
090089c4 403 inet_ntoa(address->sin_addr), ntohs(address->sin_port));
404 errno = 0;
405 return COMM_ERROR;
406 }
407 /* Establish connection. */
0ee4272b 408 if (connect(sock, (const struct sockaddr *) address, sizeof(struct sockaddr_in)) < 0) {
090089c4 409 switch (errno) {
410 case EALREADY:
411 return COMM_ERROR;
412 /* NOTREACHED */
30a4f2a8 413#if EAGAIN != EWOULDBLOCK
414 case EAGAIN:
415#endif
416 case EWOULDBLOCK:
090089c4 417 case EINPROGRESS:
e5f6c5c2 418 status = COMM_INPROGRESS;
090089c4 419 break;
420 case EISCONN:
421 status = COMM_OK;
422 break;
423 case EINVAL:
424 len = sizeof(x);
425 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &x, &len) >= 0)
426 errno = x;
427 default:
881f7a6c 428 debug(50, 2, "connect: %s:%d: %s.\n",
28ab0c0a 429 fqdnFromAddr(address->sin_addr),
090089c4 430 ntohs(address->sin_port),
431 xstrerror());
432 return COMM_ERROR;
433 }
e5f6c5c2 434 }
7450ce42 435 strcpy(conn->ipaddr, inet_ntoa(address->sin_addr));
436 conn->remote_port = ntohs(address->sin_port);
090089c4 437 /* set the lifetime for this client */
438 if (status == COMM_OK) {
b6f794d6 439 lft = comm_set_fd_lifetime(sock, Config.lifetimeDefault);
7450ce42 440 debug(5, 10, "comm_connect_addr: FD %d connected to %s:%d, lifetime %d.\n",
441 sock, conn->ipaddr, conn->remote_port, lft);
090089c4 442 } else if (status == EINPROGRESS) {
b6f794d6 443 lft = comm_set_fd_lifetime(sock, Config.connectTimeout);
d1f14731 444 debug(5, 10, "comm_connect_addr: FD %d connection pending, lifetime %d\n",
090089c4 445 sock, lft);
446 }
447 /* Add new socket to list of open sockets. */
090089c4 448 conn->sender = 1;
090089c4 449 return status;
450}
451
452/* Wait for an incoming connection on FD. FD should be a socket returned
453 * from comm_listen. */
b8d8561b 454int
455comm_accept(int fd, struct sockaddr_in *peer, struct sockaddr_in *me)
090089c4 456{
457 int sock;
1f9afe33 458 struct sockaddr_in P;
459 struct sockaddr_in M;
090089c4 460 int Slen;
1f9afe33 461 FD_ENTRY *conn = NULL;
090089c4 462 FD_ENTRY *listener = &fd_table[fd];
463
1f9afe33 464 Slen = sizeof(P);
465 while ((sock = accept(fd, (struct sockaddr *) &P, &Slen)) < 0) {
090089c4 466 switch (errno) {
467#if EAGAIN != EWOULDBLOCK
468 case EAGAIN:
469#endif
470 case EWOULDBLOCK:
471 return COMM_NOMESSAGE;
472 case EINTR:
473 break; /* if accept interrupted, try again */
474 case ENFILE:
475 case EMFILE:
476 Reserve_More_FDs();
477 return COMM_ERROR;
478 default:
881f7a6c 479 debug(50, 1, "comm_accept: FD %d: accept failure: %s\n",
090089c4 480 fd, xstrerror());
481 return COMM_ERROR;
482 }
483 }
484
485 if (peer)
1f9afe33 486 *peer = P;
4053a845 487 Slen = sizeof(M);
488 memset(&M, '\0', Slen);
489 getsockname(sock, (struct sockaddr *) &M, &Slen);
490 if (me)
1f9afe33 491 *me = M;
3ca60c86 492 commSetCloseOnExec(sock);
090089c4 493 /* fdstat update */
30a4f2a8 494 fdstat_open(sock, FD_SOCKET);
090089c4 495 conn = &fd_table[sock];
496 conn->openned = 1;
497 conn->sender = 0; /* This is an accept, therefore receiver. */
498 conn->comm_type = listener->comm_type;
1f9afe33 499 strcpy(conn->ipaddr, inet_ntoa(P.sin_addr));
30a4f2a8 500 conn->remote_port = htons(P.sin_port);
501 conn->local_port = htons(M.sin_port);
090089c4 502 commSetNonBlocking(sock);
090089c4 503 return sock;
504}
505
cb201b7e 506void
507commCallCloseHandlers(int fd)
508{
509 FD_ENTRY *conn = &fd_table[fd];
510 struct close_handler *ch;
511 while ((ch = conn->close_handler) != NULL) {
512 conn->close_handler = ch->next;
513 ch->handler(fd, ch->data);
514 safe_free(ch);
515 }
516}
517
b8d8561b 518void
519comm_close(int fd)
090089c4 520{
521 FD_ENTRY *conn = NULL;
d9ec4825 522 debug(5, 5, "comm_close: FD %d\n", fd);
431d84c5 523 if (fd < 0 || fd >= SQUID_MAXFD)
9864ee44 524 return;
525 conn = &fd_table[fd];
526 if (!conn->openned)
527 return;
7450ce42 528 if (fdstatGetType(fd) == FD_FILE) {
d1f14731 529 debug(5, 0, "FD %d: Someone called comm_close() on a File\n", fd);
090089c4 530 fatal_dump(NULL);
531 }
9864ee44 532 conn->openned = 0;
a56a3abe 533 RWStateCallbackAndFree(fd, COMM_ERROR);
090089c4 534 comm_set_fd_lifetime(fd, -1); /* invalidate the lifetime */
4a63c85f 535 fdstat_close(fd); /* update fdstat */
cb201b7e 536 commCallCloseHandlers(fd);
090089c4 537 memset(conn, '\0', sizeof(FD_ENTRY));
9864ee44 538 close(fd);
090089c4 539}
540
541/* use to clean up fdtable when socket is closed without
542 * using comm_close */
24382924 543static int
b8d8561b 544comm_cleanup_fd_entry(int fd)
090089c4 545{
546 FD_ENTRY *conn = &fd_table[fd];
a56a3abe 547 RWStateCallbackAndFree(fd, COMM_ERROR);
090089c4 548 memset(conn, 0, sizeof(FD_ENTRY));
549 return 0;
550}
551
552
553/* Send a udp datagram to specified PORT at HOST. */
b8d8561b 554int
0ee4272b 555comm_udp_send(int fd, const char *host, u_short port, const char *buf, int len)
090089c4 556{
0ee4272b 557 const ipcache_addrs *ia = NULL;
090089c4 558 static struct sockaddr_in to_addr;
559 int bytes_sent;
560
561 /* Set up the destination socket address for message to send to. */
562 to_addr.sin_family = AF_INET;
563
e5f6c5c2 564 if ((ia = ipcache_gethostbyname(host, IP_BLOCKING_LOOKUP)) == 0) {
881f7a6c 565 debug(50, 1, "comm_udp_send: gethostbyname failure: %s: %s\n",
090089c4 566 host, xstrerror());
567 return (COMM_ERROR);
568 }
e5f6c5c2 569 to_addr.sin_addr = ia->in_addrs[ia->cur];
090089c4 570 to_addr.sin_port = htons(port);
571 if ((bytes_sent = sendto(fd, buf, len, 0, (struct sockaddr *) &to_addr,
572 sizeof(to_addr))) < 0) {
881f7a6c 573 debug(50, 1, "comm_udp_send: sendto failure: FD %d: %s\n",
090089c4 574 fd, xstrerror());
575 return COMM_ERROR;
576 }
577 return bytes_sent;
578}
579
580/* Send a udp datagram to specified TO_ADDR. */
b8d8561b 581int
5df61230 582comm_udp_sendto(int fd,
583 const struct sockaddr_in *to_addr,
584 int addr_len,
585 const char *buf,
586 int len)
090089c4 587{
5df61230 588 int x;
589 x = sendto(fd, buf, len, 0, (struct sockaddr *) to_addr, addr_len);
590 if (x < 0) {
881f7a6c 591 debug(50, 1, "comm_udp_sendto: FD %d, %s, port %d: %s\n",
5df61230 592 fd,
593 inet_ntoa(to_addr->sin_addr),
594 (int) htons(to_addr->sin_port),
595 xstrerror());
090089c4 596 return COMM_ERROR;
597 }
5df61230 598 return x;
090089c4 599}
600
b8d8561b 601void
602comm_set_stall(int fd, int delta)
4883993a 603{
604 if (fd < 0)
605 return;
b8de7ebe 606 fd_table[fd].stall_until = squid_curtime + delta;
4883993a 607}
608
b8d8561b 609static void
0673c0ba 610comm_select_incoming(void)
055f4d4d 611{
612 fd_set read_mask;
613 fd_set write_mask;
614 int maxfd = 0;
615 int fd = 0;
616 int fds[3];
617 int N = 0;
618 int i = 0;
ff8d0ea6 619 PF hdl = NULL;
055f4d4d 620
621 FD_ZERO(&read_mask);
622 FD_ZERO(&write_mask);
623
30a4f2a8 624 if (theHttpConnection >= 0 && fdstat_are_n_free_fd(RESERVED_FD))
625 fds[N++] = theHttpConnection;
626 if (theInIcpConnection >= 0)
627 fds[N++] = theInIcpConnection;
055f4d4d 628 fds[N++] = 0;
629
630 for (i = 0; i < N; i++) {
631 fd = fds[i];
632 if (fd_table[fd].read_handler) {
633 FD_SET(fd, &read_mask);
634 if (fd > maxfd)
635 maxfd = fd;
636 }
637 if (fd_table[fd].write_handler) {
638 FD_SET(fd, &write_mask);
639 if (fd > maxfd)
640 maxfd = fd;
641 }
642 }
643
644 if (maxfd++ == 0)
645 return;
646 if (select(maxfd, &read_mask, &write_mask, NULL, &zero_tv) > 0) {
67508012 647 getCurrentTime();
31feba03 648 for (i = 0; i < N; i++) {
055f4d4d 649 fd = fds[i];
650 if (FD_ISSET(fd, &read_mask)) {
ff8d0ea6 651 hdl = fd_table[fd].read_handler;
055f4d4d 652 fd_table[fd].read_handler = 0;
ff8d0ea6 653 hdl(fd, fd_table[fd].read_data);
055f4d4d 654 }
655 if (FD_ISSET(fd, &write_mask)) {
ff8d0ea6 656 hdl = fd_table[fd].write_handler;
055f4d4d 657 fd_table[fd].write_handler = 0;
ff8d0ea6 658 hdl(fd, fd_table[fd].write_data);
055f4d4d 659 }
660 }
661 }
662}
090089c4 663
664
665/* Select on all sockets; call handlers for those that are ready. */
b8d8561b 666int
667comm_select(time_t sec)
090089c4 668{
090089c4 669 fd_set readfds;
670 fd_set writefds;
ff8d0ea6 671 PF hdl = NULL;
7d49daab 672 int fd;
673 int i;
674 int maxfd;
675 int nfds;
090089c4 676 int num;
090089c4 677 static time_t last_timeout = 0;
678 struct timeval poll_time;
7d49daab 679 time_t timeout;
090089c4 680
681 /* assume all process are very fast (less than 1 second). Call
682 * time() only once */
e069e535 683 getCurrentTime();
090089c4 684 /* use only 1 second granularity */
b8de7ebe 685 timeout = squid_curtime + sec;
090089c4 686
f7361640 687 do {
4610e312 688 if (sec > 60)
d556a268 689 fatal_dump(NULL);
090089c4 690 FD_ZERO(&readfds);
691 FD_ZERO(&writefds);
090089c4 692
30a4f2a8 693 if (shutdown_pending || reread_pending) {
694 serverConnectionsClose();
695 ftpServerClose();
f88bb09c 696 dnsShutdownServers();
d2af9477 697 redirectShutdownServers();
f3753518 698 if (shutdown_pending > 0)
6759a5fb 699 setSocketShutdownLifetimes(Config.lifetimeShutdown);
f3753518 700 else
701 setSocketShutdownLifetimes(0);
30a4f2a8 702 }
4d64d74a 703 nfds = 0;
704 maxfd = fdstat_biggest_fd() + 1;
705 for (i = 0; i < maxfd; i++) {
898f5d1d 706#if USE_ASYNC_IO
b560dd20 707 /* Using async IO for disk handle, so don't select on them */
7450ce42 708 if (fdstatGetType(i) == FD_FILE)
b560dd20 709 continue;
898f5d1d 710#endif
090089c4 711 /* Check each open socket for a handler. */
b8de7ebe 712 if (fd_table[i].read_handler && fd_table[i].stall_until <= squid_curtime) {
4d64d74a 713 nfds++;
090089c4 714 FD_SET(i, &readfds);
4d64d74a 715 }
716 if (fd_table[i].write_handler) {
717 nfds++;
090089c4 718 FD_SET(i, &writefds);
4d64d74a 719 }
090089c4 720 }
721 if (!fdstat_are_n_free_fd(RESERVED_FD)) {
30a4f2a8 722 FD_CLR(theHttpConnection, &readfds);
090089c4 723 }
234967c9 724 if (shutdown_pending || reread_pending)
983061ed 725 debug(5, 2, "comm_select: Still waiting on %d FDs\n", nfds);
4d64d74a 726 if (nfds == 0)
727 return COMM_SHUTDOWN;
7690e8eb 728 for (;;) {
898f5d1d 729#if USE_ASYNC_IO
b560dd20 730 /* Another CPU vs latency tradeoff for async IO */
731 poll_time.tv_sec = 0;
732 poll_time.tv_usec = 250000;
733#else
89fb2544 734 poll_time.tv_sec = sec > 0 ? 1 : 0;
090089c4 735 poll_time.tv_usec = 0;
898f5d1d 736#endif
d0217c9b 737 num = select(maxfd, &readfds, &writefds, NULL, &poll_time);
67508012 738 getCurrentTime();
090089c4 739 if (num >= 0)
740 break;
4d64d74a 741 if (errno == EINTR)
742 break;
881f7a6c 743 debug(50, 0, "comm_select: select failure: %s\n",
30a4f2a8 744 xstrerror());
d0217c9b 745 examine_select(&readfds, &writefds);
bf9f8f2b 746 return COMM_ERROR;
30a4f2a8 747 /* NOTREACHED */
090089c4 748 }
898f5d1d 749#if USE_ASYNC_IO
b560dd20 750 aioExamine(); /* See if any IO completed */
898f5d1d 751#endif
4d64d74a 752 if (num < 0)
753 continue;
d1f14731 754 debug(5, num ? 5 : 8, "comm_select: %d sockets ready at %d\n",
30a4f2a8 755 num, (int) squid_curtime);
090089c4 756
757 /* Check lifetime and timeout handlers ONCE each second.
758 * Replaces brain-dead check every time through the loop! */
b8de7ebe 759 if (squid_curtime > last_timeout) {
760 last_timeout = squid_curtime;
090089c4 761 checkTimeouts();
762 checkLifetimes();
763 }
7d49daab 764 if (num == 0)
765 continue;
766
090089c4 767 /* scan each socket but the accept socket. Poll this
768 * more frequently to minimiize losses due to the 5 connect
769 * limit in SunOS */
770
5742d7c9 771 for (fd = 0; fd < maxfd; fd++) {
d0217c9b 772 if (!FD_ISSET(fd, &readfds) && !FD_ISSET(fd, &writefds))
7d49daab 773 continue;
7d49daab 774
775 /*
776 * Admit more connections quickly until we hit the hard limit.
777 * Don't forget to keep the UDP acks coming and going.
778 */
055f4d4d 779 comm_select_incoming();
7d49daab 780
30a4f2a8 781 if ((fd == theInIcpConnection) || (fd == theHttpConnection))
7d49daab 782 continue;
783
784 if (FD_ISSET(fd, &readfds)) {
785 debug(5, 6, "comm_select: FD %d ready for reading\n", fd);
786 if (fd_table[fd].read_handler) {
ff8d0ea6 787 hdl = fd_table[fd].read_handler;
7d49daab 788 fd_table[fd].read_handler = 0;
ff8d0ea6 789 hdl(fd, fd_table[fd].read_data);
090089c4 790 }
7d49daab 791 }
792 if (FD_ISSET(fd, &writefds)) {
793 debug(5, 5, "comm_select: FD %d ready for writing\n", fd);
794 if (fd_table[fd].write_handler) {
ff8d0ea6 795 hdl = fd_table[fd].write_handler;
7d49daab 796 fd_table[fd].write_handler = 0;
ff8d0ea6 797 hdl(fd, fd_table[fd].write_data);
090089c4 798 }
7d49daab 799 }
090089c4 800 }
7d49daab 801 return COMM_OK;
f7361640 802 } while (timeout > getCurrentTime());
090089c4 803
b8de7ebe 804 debug(5, 8, "comm_select: time out: %d.\n", squid_curtime);
090089c4 805 return COMM_TIMEOUT;
806}
807
b8d8561b 808void
b177367b 809commSetSelect(int fd, unsigned int type, PF handler, void *client_data, time_t timeout)
090089c4 810{
811 if (type & COMM_SELECT_TIMEOUT) {
e069e535 812 fd_table[fd].timeout_time = (getCurrentTime() + timeout);
090089c4 813 fd_table[fd].timeout_delta = timeout;
814 fd_table[fd].timeout_handler = handler;
815 fd_table[fd].timeout_data = client_data;
816 if ((timeout <= 0) && handler) {
b177367b 817 debug(5, 2, "commSetSelect: Zero timeout doesn't make sense\n");
090089c4 818 }
819 }
820 if (type & COMM_SELECT_READ) {
821 fd_table[fd].read_handler = handler;
822 fd_table[fd].read_data = client_data;
823 }
824 if (type & COMM_SELECT_WRITE) {
825 fd_table[fd].write_handler = handler;
826 fd_table[fd].write_data = client_data;
827 }
090089c4 828 if (type & COMM_SELECT_LIFETIME) {
829 fd_table[fd].lifetime_handler = handler;
830 fd_table[fd].lifetime_data = client_data;
831 }
090089c4 832}
833
b8d8561b 834int
67508012 835comm_get_select_handler(int fd,
836 unsigned int type,
e5f6c5c2 837 void (**handler_ptr) _PARAMS((int, void *)),
67508012 838 void **client_data_ptr)
090089c4 839{
840 if (type & COMM_SELECT_TIMEOUT) {
841 *handler_ptr = fd_table[fd].timeout_handler;
842 *client_data_ptr = fd_table[fd].timeout_data;
843 }
844 if (type & COMM_SELECT_READ) {
845 *handler_ptr = fd_table[fd].read_handler;
846 *client_data_ptr = fd_table[fd].read_data;
847 }
848 if (type & COMM_SELECT_WRITE) {
849 *handler_ptr = fd_table[fd].write_handler;
850 *client_data_ptr = fd_table[fd].write_data;
851 }
090089c4 852 if (type & COMM_SELECT_LIFETIME) {
853 *handler_ptr = fd_table[fd].lifetime_handler;
854 *client_data_ptr = fd_table[fd].lifetime_data;
855 }
856 return 0; /* XXX What is meaningful? */
857}
858
b8d8561b 859void
860comm_add_close_handler(int fd, PF handler, void *data)
30a4f2a8 861{
862 struct close_handler *new = xmalloc(sizeof(*new));
863
44e323cc 864 debug(5, 5, "comm_add_close_handler: fd=%d handler=%p data=%p\n", fd, handler, data);
090089c4 865
30a4f2a8 866 new->handler = handler;
867 new->data = data;
868 new->next = fd_table[fd].close_handler;
869 fd_table[fd].close_handler = new;
870}
871
b8d8561b 872void
873comm_remove_close_handler(int fd, PF handler, void *data)
090089c4 874{
30a4f2a8 875 struct close_handler *p, *last = NULL;
876
877 /* Find handler in list */
878 for (p = fd_table[fd].close_handler; p != NULL; last = p, p = p->next)
879 if (p->handler == handler && p->data == data)
880 break; /* This is our handler */
881 if (!p)
882 fatal_dump("comm_remove_close_handler: Handler not found!\n");
883
884 /* Remove list entry */
885 if (last)
886 last->next = p->next;
887 else
888 fd_table[fd].close_handler = p->next;
889 safe_free(p);
890}
090089c4 891
e90100aa 892int
893comm_set_mcast_ttl(int fd, int mcast_ttl)
894{
895#ifdef IP_MULTICAST_TTL
896 debug(5, 10, "comm_set_mcast_ttl: setting multicast TTL %d on FD %d\n",
897 mcast_ttl, fd);
898 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
899 (char *) &mcast_ttl, sizeof(char)) < 0)
881f7a6c 900 debug(50, 1, "comm_set_mcast_ttl: FD %d, TTL: %d: %s\n",
e90100aa 901 fd, mcast_ttl, xstrerror());
902#endif
903 return 0;
904}
905
906int
907comm_join_mcast_groups(int fd)
908{
909#ifdef IP_MULTICAST_TTL
910 struct ip_mreq mr;
911 wordlist *s = NULL;
912
913 for (s = Config.mcast_group_list; s; s = s->next) {
ff8d0ea6 914 debug(5, 10, "comm_join_mcast_groups: joining group %s on FD %d\n",
8eb58c9c 915 s->key, fd);
e90100aa 916 mr.imr_multiaddr.s_addr = inet_addr(s->key);
917 mr.imr_interface.s_addr = INADDR_ANY;
918 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
919 (char *) &mr, sizeof(struct ip_mreq)) < 0)
920 debug(5, 1, "comm_join_mcast_groups: FD %d, addr: %s\n",
921 fd, s->key);
922 }
923#endif
924 return 0;
925}
926
b8d8561b 927static void
928commSetNoLinger(int fd)
30a4f2a8 929{
930 struct linger L;
090089c4 931 L.l_onoff = 0; /* off */
932 L.l_linger = 0;
30a4f2a8 933 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0)
881f7a6c 934 debug(50, 0, "commSetNoLinger: FD %d: %s\n", fd, xstrerror());
090089c4 935}
936
b8d8561b 937static void
938commSetReuseAddr(int fd)
090089c4 939{
940 int on = 1;
30a4f2a8 941 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
881f7a6c 942 debug(50, 1, "commSetReuseAddr: FD %d: %s\n", fd, xstrerror());
090089c4 943}
944
b8d8561b 945static void
946commSetTcpRcvbuf(int fd, int size)
f868539a 947{
948 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)) < 0)
881f7a6c 949 debug(50, 1, "commSetTcpRcvbuf: FD %d, SIZE %d: %s\n",
b6f794d6 950 fd, size, xstrerror());
f868539a 951}
952
b8d8561b 953int
954commSetNonBlocking(int fd)
30a4f2a8 955{
731e4d49 956 int flags;
957 if ((flags = fcntl(fd, F_GETFL)) < 0) {
881f7a6c 958 debug(50, 0, "FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
731e4d49 959 return COMM_ERROR;
960 }
ed43818f 961#if defined(O_NONBLOCK) && !defined(_SQUID_SUNOS_) && !defined(_SQUID_SOLARIS_)
731e4d49 962 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
881f7a6c 963 debug(50, 0, "FD %d: error setting O_NONBLOCK: %s\n", fd, xstrerror());
30a4f2a8 964 return COMM_ERROR;
090089c4 965 }
966#else
731e4d49 967 if (fcntl(fd, F_SETFL, flags | O_NDELAY) < 0) {
881f7a6c 968 debug(50, 0, "FD %d: error setting O_NDELAY: %s\n", fd, xstrerror());
30a4f2a8 969 return COMM_ERROR;
090089c4 970 }
30a4f2a8 971#endif
090089c4 972 return 0;
973}
974
b8d8561b 975void
976commSetCloseOnExec(int fd)
3ca60c86 977{
978#ifdef FD_CLOEXEC
731e4d49 979 int flags;
980 if ((flags = fcntl(fd, F_GETFL)) < 0) {
881f7a6c 981 debug(50, 0, "FD %d: fcntl F_GETFL: %s\n", fd, xstrerror());
24382924 982 return;
3ca60c86 983 }
24382924 984 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
881f7a6c 985 debug(50, 0, "FD %d: set close-on-exec failed: %s\n", fd, xstrerror());
3ca60c86 986#endif
987}
988
e90100aa 989#ifdef TCP_NODELAY
990static void
991commSetTcpNoDelay(int fd)
992{
993 int on = 1;
994 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0)
881f7a6c 995 debug(50, 1, "commSetTcpNoDelay: FD %d: %s\n", fd, xstrerror());
e90100aa 996}
997#endif
998
090089c4 999/*
1000 * the fd_lifetime is used as a hardlimit to timeout dead sockets.
1001 * The basic problem is that many WWW clients are abusive and
b8de7ebe 1002 * it results in squid having lots of CLOSE_WAIT states. Until
090089c4 1003 * we can find a better solution, we give all asciiPort or
b8de7ebe 1004 * squid initiated clients a maximum lifetime.
090089c4 1005 */
b8d8561b 1006int
0673c0ba 1007comm_init(void)
090089c4 1008{
30a4f2a8 1009 int i;
090089c4 1010
431d84c5 1011 fd_table = xcalloc(SQUID_MAXFD, sizeof(FD_ENTRY));
1012 meta_data.misc += SQUID_MAXFD * sizeof(FD_ENTRY);
090089c4 1013 /* Keep a few file descriptors free so that we don't run out of FD's
1014 * after accepting a client but before it opens a socket or a file.
431d84c5 1015 * Since SQUID_MAXFD can be as high as several thousand, don't waste them */
1016 RESERVED_FD = min(100, SQUID_MAXFD / 4);
090089c4 1017 /* hardwired lifetimes */
431d84c5 1018 fd_lifetime = xmalloc(sizeof(int) * SQUID_MAXFD);
1019 for (i = 0; i < SQUID_MAXFD; i++)
090089c4 1020 comm_set_fd_lifetime(i, -1); /* denotes invalid */
431d84c5 1021 meta_data.misc += SQUID_MAXFD * sizeof(int);
055f4d4d 1022 zero_tv.tv_sec = 0;
1023 zero_tv.tv_usec = 0;
090089c4 1024 return 0;
1025}
1026
1027
1028/*
1029 * examine_select - debug routine.
1030 *
1031 * I spend the day chasing this core dump that occurs when both the client
1032 * and the server side of a cache fetch simultaneoulsy abort the
1033 * connection. While I haven't really studied the code to figure out how
1034 * it happens, the snippet below may prevent the cache from exitting:
1035 *
1036 * Call this from where the select loop fails.
1037 */
b8d8561b 1038static int
5742d7c9 1039examine_select(fd_set * readfds, fd_set * writefds)
090089c4 1040{
1041 int fd = 0;
bbc5ea8f 1042 fd_set read_x;
1043 fd_set write_x;
090089c4 1044 int num;
1045 struct timeval tv;
30a4f2a8 1046 struct close_handler *ch = NULL;
1047 struct close_handler *next = NULL;
117531df 1048 FD_ENTRY *f = NULL;
090089c4 1049
d1f14731 1050 debug(5, 0, "examine_select: Examining open file descriptors...\n");
431d84c5 1051 for (fd = 0; fd < SQUID_MAXFD; fd++) {
090089c4 1052 FD_ZERO(&read_x);
1053 FD_ZERO(&write_x);
090089c4 1054 tv.tv_sec = tv.tv_usec = 0;
af00901c 1055 if (FD_ISSET(fd, readfds))
090089c4 1056 FD_SET(fd, &read_x);
af00901c 1057 else if (FD_ISSET(fd, writefds))
1058 FD_SET(fd, &write_x);
af00901c 1059 else
1060 continue;
431d84c5 1061 num = select(SQUID_MAXFD, &read_x, &write_x, NULL, &tv);
af00901c 1062 if (num > -1) {
1063 debug(5, 5, "FD %d is valid.\n", fd);
1064 continue;
1065 }
1066 f = &fd_table[fd];
1067 debug(5, 0, "WARNING: FD %d has handlers, but it's invalid.\n", fd);
1068 debug(5, 0, "FD %d is a %s\n", fd, fdstatTypeStr[fdstatGetType(fd)]);
1069 debug(5, 0, "--> %s\n", fd_note(fd, NULL));
d0217c9b 1070 debug(5, 0, "lifetm:%p tmout:%p read:%p write:%p\n",
af00901c 1071 f->lifetime_handler,
1072 f->timeout_handler,
1073 f->read_handler,
d0217c9b 1074 f->write_handler);
af00901c 1075 for (ch = f->close_handler; ch; ch = ch->next)
1076 debug(5, 0, " close handler: %p\n", ch->handler);
1077 if (f->close_handler) {
1078 for (ch = f->close_handler; ch; ch = next) {
1079 next = ch->next;
1080 ch->handler(fd, ch->data);
1081 safe_free(ch);
090089c4 1082 }
af00901c 1083 } else if (f->lifetime_handler) {
1084 debug(5, 0, "examine_select: Calling Lifetime Handler\n");
1085 f->lifetime_handler(fd, f->lifetime_data);
1086 } else if (f->timeout_handler) {
1087 debug(5, 0, "examine_select: Calling Timeout Handler\n");
1088 f->timeout_handler(fd, f->timeout_data);
090089c4 1089 }
af00901c 1090 f->close_handler = NULL;
1091 f->lifetime_handler = NULL;
1092 f->timeout_handler = NULL;
1093 f->read_handler = NULL;
1094 f->write_handler = NULL;
af00901c 1095 FD_CLR(fd, readfds);
1096 FD_CLR(fd, writefds);
090089c4 1097 }
090089c4 1098 return 0;
1099}
1100
b8d8561b 1101char *
0ee4272b 1102fd_note(int fd, const char *s)
090089c4 1103{
1104 if (s == NULL)
1105 return (fd_table[fd].ascii_note);
f2052513 1106 xstrncpy(fd_table[fd].ascii_note, s, FD_ASCII_NOTE_SZ);
090089c4 1107 return (NULL);
1108}
1109
b8d8561b 1110static void
0673c0ba 1111checkTimeouts(void)
090089c4 1112{
1113 int fd;
ff8d0ea6 1114 PF hdl = NULL;
4d64d74a 1115 FD_ENTRY *f = NULL;
56c6c1f6 1116 void *data;
090089c4 1117 /* scan for timeout */
431d84c5 1118 for (fd = 0; fd < SQUID_MAXFD; ++fd) {
4d64d74a 1119 f = &fd_table[fd];
56c6c1f6 1120 if ((hdl = f->timeout_handler) == NULL)
1121 continue;
1122 if (f->timeout_time > squid_curtime)
1123 continue;
1124 debug(5, 5, "checkTimeouts: FD %d timeout at %d\n", fd, squid_curtime);
1125 data = f->timeout_data;
1126 f->timeout_handler = NULL;
1127 f->timeout_data = NULL;
1128 hdl(fd, data);
090089c4 1129 }
1130}
1131
b8d8561b 1132static void
0673c0ba 1133checkLifetimes(void)
090089c4 1134{
1135 int fd;
090089c4 1136 time_t lft;
9864ee44 1137 FD_ENTRY *fde = NULL;
090089c4 1138
ff8d0ea6 1139 PF hdl = NULL;
30a4f2a8 1140
431d84c5 1141 for (fd = 0; fd < SQUID_MAXFD; fd++) {
30a4f2a8 1142 if ((lft = comm_get_fd_lifetime(fd)) == -1)
1143 continue;
1144 if (lft > squid_curtime)
1145 continue;
1146 debug(5, 5, "checkLifetimes: FD %d Expired\n", fd);
9864ee44 1147 fde = &fd_table[fd];
ff8d0ea6 1148 if ((hdl = fde->lifetime_handler) != NULL) {
30a4f2a8 1149 debug(5, 5, "checkLifetimes: FD %d: Calling lifetime handler\n", fd);
ff8d0ea6 1150 hdl(fd, fde->lifetime_data);
9864ee44 1151 fde->lifetime_handler = NULL;
ff8d0ea6 1152 } else if ((hdl = fde->read_handler) != NULL) {
30a4f2a8 1153 debug(5, 5, "checkLifetimes: FD %d: Calling read handler\n", fd);
ff8d0ea6 1154 hdl(fd, fd_table[fd].read_data);
caebbe00 1155 fd_table[fd].read_handler = NULL;
ff8d0ea6 1156 } else if ((hdl = fd_table[fd].write_handler)) {
30a4f2a8 1157 debug(5, 5, "checkLifetimes: FD %d: Calling write handler\n", fd);
ff8d0ea6 1158 hdl(fd, fde->write_data);
9864ee44 1159 fde->write_handler = NULL;
30a4f2a8 1160 } else {
1161 debug(5, 5, "checkLifetimes: FD %d: No handlers, calling comm_close()\n", fd);
1162 comm_close(fd);
1163 comm_cleanup_fd_entry(fd);
1164 }
9864ee44 1165 if (fde->openned) {
30a4f2a8 1166 /* still opened */
1167 debug(5, 5, "checkLifetimes: FD %d: Forcing comm_close()\n", fd);
1168 comm_close(fd);
1169 comm_cleanup_fd_entry(fd);
090089c4 1170 }
1171 }
1172}
1173
1174/*
1175 * Reserve_More_FDs() called when acceopt(), open(), or socket is failing
1176 */
b8d8561b 1177static void
0673c0ba 1178Reserve_More_FDs(void)
090089c4 1179{
431d84c5 1180 if (RESERVED_FD < SQUID_MAXFD - 64) {
090089c4 1181 RESERVED_FD = RESERVED_FD + 1;
431d84c5 1182 } else if (RESERVED_FD == SQUID_MAXFD - 64) {
090089c4 1183 RESERVED_FD = RESERVED_FD + 1;
d1f14731 1184 debug(5, 0, "Don't you have a tiny open-file table size of %d\n",
431d84c5 1185 SQUID_MAXFD - RESERVED_FD);
090089c4 1186 }
1187}
1188
30a4f2a8 1189/* Read from FD. */
b8d8561b 1190static int
1191commHandleRead(int fd, RWStateData * state)
30a4f2a8 1192{
1193 int len;
1194
1195 len = read(fd, state->buf + state->offset, state->size - state->offset);
1196 debug(5, 5, "commHandleRead: FD %d: read %d bytes\n", fd, len);
1197
1198 if (len <= 0) {
9864ee44 1199 if (errno == EWOULDBLOCK || errno == EAGAIN) {
30a4f2a8 1200 /* reschedule self */
b177367b 1201 commSetSelect(fd,
30a4f2a8 1202 COMM_SELECT_READ,
1203 (PF) commHandleRead,
b177367b 1204 state,
85d7ea98 1205 0);
30a4f2a8 1206 return COMM_OK;
9864ee44 1207 } else {
30a4f2a8 1208 /* Len == 0 means connection closed; otherwise would not have been
1209 * called by comm_select(). */
881f7a6c 1210 debug(50, len == 0 ? 2 : 1,
a56a3abe 1211 "commHandleRead: FD %d: read failure: %s\n",
1212 fd,
1213 len == 0 ? "connection closed" : xstrerror());
1214 RWStateCallbackAndFree(fd, COMM_ERROR);
30a4f2a8 1215 return COMM_ERROR;
1216 }
1217 }
1218 state->offset += len;
1219
1220 /* Call handler if we have read enough */
1221 if (state->offset >= state->size || state->handle_immed) {
a56a3abe 1222 RWStateCallbackAndFree(fd, COMM_OK);
30a4f2a8 1223 } else {
1224 /* Reschedule until we are done */
b177367b 1225 commSetSelect(fd,
30a4f2a8 1226 COMM_SELECT_READ,
1227 (PF) commHandleRead,
b177367b 1228 state,
85d7ea98 1229 0);
30a4f2a8 1230 }
1231 return COMM_OK;
1232}
1233
1234/* Select for reading on FD, until SIZE bytes are received. Call
1235 * HANDLER when complete. */
b8d8561b 1236void
1237comm_read(int fd,
1238 char *buf,
1239 int size,
1240 int timeout,
1241 int immed,
1242 rw_complete_handler * handler,
1243 void *handler_data)
30a4f2a8 1244{
1245 RWStateData *state = NULL;
1246
1247 debug(5, 5, "comm_read: FD %d: sz %d: tout %d: hndl %p: data %p.\n",
1248 fd, size, timeout, handler, handler_data);
1249
0757f0b5 1250 if (fd_table[fd].rwstate) {
1251 debug(5, 1, "comm_read: WARNING! FD %d: A comm_read/comm_write is already active.\n", fd);
a56a3abe 1252 RWStateCallbackAndFree(fd, COMM_ERROR);
30a4f2a8 1253 }
1254 state = xcalloc(1, sizeof(RWStateData));
30a4f2a8 1255 state->buf = buf;
1256 state->size = size;
1257 state->offset = 0;
1258 state->handler = handler;
1259 state->timeout = timeout;
1260 state->handle_immed = immed;
1261 state->time = squid_curtime;
1262 state->handler_data = handler_data;
9864ee44 1263 state->free = NULL;
a56a3abe 1264 fd_table[fd].rwstate = state;
b177367b 1265 commSetSelect(fd,
30a4f2a8 1266 COMM_SELECT_READ,
1267 (PF) commHandleRead,
b177367b 1268 state,
1269 0);
30a4f2a8 1270}
1271
1272/* Write to FD. */
b8d8561b 1273static void
1274commHandleWrite(int fd, RWStateData * state)
30a4f2a8 1275{
1276 int len = 0;
1277 int nleft;
1278
1279 debug(5, 5, "commHandleWrite: FD %d: state=%p, off %d, sz %d.\n",
1280 fd, state, state->offset, state->size);
1281
1282 nleft = state->size - state->offset;
1283 len = write(fd, state->buf + state->offset, nleft);
1284
1285 if (len == 0) {
1286 /* Note we even call write if nleft == 0 */
1287 /* We're done */
1288 if (nleft != 0)
1289 debug(5, 2, "commHandleWrite: FD %d: write failure: connection closed with %d bytes remaining.\n", fd, nleft);
a56a3abe 1290 RWStateCallbackAndFree(fd, nleft ? COMM_ERROR : COMM_OK);
30a4f2a8 1291 } else if (len < 0) {
1292 /* An error */
1293 if (errno == EWOULDBLOCK || errno == EAGAIN) {
881f7a6c 1294 debug(50, 10, "commHandleWrite: FD %d: write failure: %s.\n",
30a4f2a8 1295 fd, xstrerror());
b177367b 1296 commSetSelect(fd,
30a4f2a8 1297 COMM_SELECT_WRITE,
1298 (PF) commHandleWrite,
b177367b 1299 state,
85d7ea98 1300 0);
9864ee44 1301 } else {
881f7a6c 1302 debug(50, 2, "commHandleWrite: FD %d: write failure: %s.\n",
9864ee44 1303 fd, xstrerror());
a56a3abe 1304 RWStateCallbackAndFree(fd, COMM_ERROR);
30a4f2a8 1305 }
30a4f2a8 1306 } else {
1307 /* A successful write, continue */
1308 state->offset += len;
1309 if (state->offset < state->size) {
1310 /* Not done, reinstall the write handler and write some more */
b177367b 1311 commSetSelect(fd,
30a4f2a8 1312 COMM_SELECT_WRITE,
1313 (PF) commHandleWrite,
b177367b 1314 state,
85d7ea98 1315 0);
9864ee44 1316 } else {
a56a3abe 1317 RWStateCallbackAndFree(fd, COMM_OK);
30a4f2a8 1318 }
30a4f2a8 1319 }
1320}
1321
1322
1323
1324/* Select for Writing on FD, until SIZE bytes are sent. Call
1325 * * HANDLER when complete. */
b8d8561b 1326void
86ee2017 1327comm_write(int fd, char *buf, int size, int timeout, rw_complete_handler * handler, void *handler_data, void (*free_func) (void *))
30a4f2a8 1328{
1329 RWStateData *state = NULL;
1330
1331 debug(5, 5, "comm_write: FD %d: sz %d: tout %d: hndl %p: data %p.\n",
1332 fd, size, timeout, handler, handler_data);
1333
0757f0b5 1334 if (fd_table[fd].rwstate) {
1e72c32c 1335 debug(5, 1, "WARNING! FD %d: A comm_read/comm_write is already active.\n", fd);
a56a3abe 1336 RWStateCallbackAndFree(fd, COMM_ERROR);
30a4f2a8 1337 }
1338 state = xcalloc(1, sizeof(RWStateData));
1339 state->buf = buf;
1340 state->size = size;
1341 state->offset = 0;
1342 state->handler = handler;
1343 state->timeout = timeout;
1344 state->time = squid_curtime;
1345 state->handler_data = handler_data;
86ee2017 1346 state->free = free_func;
a56a3abe 1347 fd_table[fd].rwstate = state;
b177367b 1348 commSetSelect(fd,
30a4f2a8 1349 COMM_SELECT_WRITE,
1350 (PF) commHandleWrite,
b177367b 1351 fd_table[fd].rwstate,
1352 0);
30a4f2a8 1353}
0a21bd84 1354
1355void
1356commFreeMemory(void)
1357{
3ba50d75 1358 safe_free(fd_table);
df92bd2a 1359 safe_free(fd_lifetime);
0a21bd84 1360}