]> jfr.im git - irc/rqf/shadowircd.git/blob - libcharybdis/commio.c
[svn] - fix for 100% cpu use
[irc/rqf/shadowircd.git] / libcharybdis / commio.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * commio.c: Network/file related functions
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
24 * $Id: commio.c 3237 2007-03-05 17:35:17Z nenolod $
25 */
26
27 #include "libcharybdis.h"
28
29 #ifndef IN_LOOPBACKNET
30 #define IN_LOOPBACKNET 0x7f
31 #endif
32
33 #ifndef INADDR_NONE
34 #define INADDR_NONE ((unsigned int) 0xffffffff)
35 #endif
36
37 const char *const NONB_ERROR_MSG = "set_non_blocking failed for %s:%s";
38 const char *const SETBUF_ERROR_MSG = "set_sock_buffers failed for server %s:%s";
39
40 static const char *comm_err_str[] = { "Comm OK", "Error during bind()",
41 "Error during DNS lookup", "connect timeout",
42 "Error during connect()",
43 "Comm Error"
44 };
45
46 #define FD_HASH_SIZE 128
47 static dlink_list fd_table[FD_HASH_SIZE];
48
49 static void fdlist_update_biggest(int fd, int opening);
50
51 /* Highest FD and number of open FDs .. */
52 int highest_fd = -1; /* Its -1 because we haven't started yet -- adrian */
53 int number_fd = 0;
54
55 static void comm_connect_callback(int fd, int status);
56 static PF comm_connect_timeout;
57 static void comm_connect_dns_callback(void *vptr, struct DNSReply *reply);
58 static PF comm_connect_tryconnect;
59
60 inline fde_t *
61 comm_locate_fd(int fd)
62 {
63 int bucket = fd % FD_HASH_SIZE;
64 dlink_list *list = &fd_table[bucket];
65 dlink_node *n;
66
67 DLINK_FOREACH(n, list->head)
68 {
69 fde_t *F = (fde_t *) n->data;
70
71 if (F->fd == fd)
72 return F;
73 }
74
75 return NULL;
76 }
77
78 inline fde_t *
79 comm_add_fd(int fd)
80 {
81 fde_t *F = comm_locate_fd(fd);
82 dlink_list *list;
83
84 if (F != NULL)
85 return F;
86
87 F = calloc(sizeof(fde_t), 1);
88 F->fd = fd;
89 list = &fd_table[fd % FD_HASH_SIZE];
90 dlinkAdd(F, &F->node, list);
91
92 return F;
93 }
94
95 inline void
96 comm_remove_fd(int fd)
97 {
98 int bucket = fd % FD_HASH_SIZE;
99 fde_t *F;
100 dlink_list *list = &fd_table[bucket];
101
102 F = comm_locate_fd(fd);
103 if (F == NULL)
104 return;
105
106 dlinkDelete(&F->node, list);
107 MyFree(F);
108 }
109
110 /* 32bit solaris is kinda slow and stdio only supports fds < 256
111 * so we got to do this crap below.
112 * (BTW Fuck you Sun, I hate your guts and I hope you go bankrupt soon)
113 * XXX: this is no longer needed in Solaris 10. --nenolod
114 */
115 #if defined (__SVR4) && defined (__sun)
116 static void comm_fd_hack(int *fd)
117 {
118 int newfd;
119 if(*fd > 256 || *fd < 0)
120 return;
121 if((newfd = fcntl(*fd, F_DUPFD, 256)) != -1)
122 {
123 close(*fd);
124 *fd = newfd;
125 }
126 return;
127 }
128 #else
129 #define comm_fd_hack(fd)
130 #endif
131
132
133 /* close_all_connections() can be used *before* the system come up! */
134
135 void
136 comm_close_all(void)
137 {
138 int i;
139 #ifndef NDEBUG
140 int fd;
141 #endif
142
143 /* XXX someone tell me why we care about 4 fd's ? */
144 /* XXX btw, fd 3 is used for profiler ! */
145
146 for (i = 4; i < MAXCONNECTIONS; ++i)
147 {
148 fde_t *F = comm_locate_fd(i);
149
150 if(F != NULL && F->flags.open)
151 comm_close(i);
152 else
153 close(i);
154 }
155
156 /* XXX should his hack be done in all cases? */
157 #ifndef NDEBUG
158 /* fugly hack to reserve fd == 2 */
159 (void) close(2);
160 fd = open("stderr.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
161 if(fd >= 0)
162 {
163 dup2(fd, 2);
164 close(fd);
165 }
166 #endif
167 }
168
169 /*
170 * get_sockerr - get the error value from the socket or the current errno
171 *
172 * Get the *real* error from the socket (well try to anyway..).
173 * This may only work when SO_DEBUG is enabled but its worth the
174 * gamble anyway.
175 */
176 int
177 comm_get_sockerr(int fd)
178 {
179 int errtmp = errno;
180 #ifdef SO_ERROR
181 int err = 0;
182 socklen_t len = sizeof(err);
183
184 if(-1 < fd && !getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &err, (socklen_t *) & len))
185 {
186 if(err)
187 errtmp = err;
188 }
189 errno = errtmp;
190 #endif
191 return errtmp;
192 }
193
194 /*
195 * set_sock_buffers - set send and receive buffers for socket
196 *
197 * inputs - fd file descriptor
198 * - size to set
199 * output - returns true (1) if successful, false (0) otherwise
200 * side effects -
201 */
202 int
203 comm_set_buffers(int fd, int size)
204 {
205 if(setsockopt
206 (fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size))
207 || setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size)))
208 return 0;
209 return 1;
210 }
211
212 /*
213 * set_non_blocking - Set the client connection into non-blocking mode.
214 *
215 * inputs - fd to set into non blocking mode
216 * output - 1 if successful 0 if not
217 * side effects - use POSIX compliant non blocking and
218 * be done with it.
219 */
220 int
221 comm_set_nb(int fd)
222 {
223 int nonb = 0;
224 int res;
225 fde_t *F = comm_locate_fd(fd);
226
227 nonb |= O_NONBLOCK;
228 res = fcntl(fd, F_GETFL, 0);
229 if(-1 == res || fcntl(fd, F_SETFL, res | nonb) == -1)
230 return 0;
231
232 if (F != NULL)
233 F->flags.nonblocking = 1;
234
235 return 1;
236 }
237
238
239 /*
240 * stolen from squid - its a neat (but overused! :) routine which we
241 * can use to see whether we can ignore this errno or not. It is
242 * generally useful for non-blocking network IO related errnos.
243 * -- adrian
244 */
245 int
246 ignoreErrno(int ierrno)
247 {
248 switch (ierrno)
249 {
250 case EINPROGRESS:
251 case EWOULDBLOCK:
252 #if EAGAIN != EWOULDBLOCK
253 case EAGAIN:
254 #endif
255 case EALREADY:
256 case EINTR:
257 #ifdef ERESTART
258 case ERESTART:
259 #endif
260 return 1;
261 default:
262 return 0;
263 }
264 }
265
266
267 /*
268 * comm_settimeout() - set the socket timeout
269 *
270 * Set the timeout for the fd
271 */
272 void
273 comm_settimeout(int fd, time_t timeout, PF * callback, void *cbdata)
274 {
275 fde_t *F;
276 s_assert(fd >= 0);
277 F = comm_locate_fd(fd);
278 s_assert(F->flags.open);
279
280 F->timeout = CurrentTime + (timeout / 1000);
281 F->timeout_handler = callback;
282 F->timeout_data = cbdata;
283 }
284
285
286 /*
287 * comm_setflush() - set a flush function
288 *
289 * A flush function is simply a function called if found during
290 * comm_timeouts(). Its basically a second timeout, except in this case
291 * I'm too lazy to implement multiple timeout functions! :-)
292 * its kinda nice to have it seperate, since this is designed for
293 * flush functions, and when comm_close() is implemented correctly
294 * with close functions, we _actually_ don't call comm_close() here ..
295 */
296 void
297 comm_setflush(int fd, time_t timeout, PF * callback, void *cbdata)
298 {
299 fde_t *F;
300 s_assert(fd >= 0);
301 F = comm_locate_fd(fd);
302 s_assert(F->flags.open);
303
304 F->flush_timeout = CurrentTime + (timeout / 1000);
305 F->flush_handler = callback;
306 F->flush_data = cbdata;
307 }
308
309
310 /*
311 * comm_checktimeouts() - check the socket timeouts
312 *
313 * All this routine does is call the given callback/cbdata, without closing
314 * down the file descriptor. When close handlers have been implemented,
315 * this will happen.
316 */
317 void
318 comm_checktimeouts(void *notused)
319 {
320 PF *hdl;
321 void *data;
322 fde_t *F;
323 dlink_list *bucket;
324 int i;
325 dlink_node *n, *n2;
326
327 for (i = 0; i <= FD_HASH_SIZE; i++)
328 {
329 bucket = &fd_table[i];
330
331 if (dlink_list_length(bucket) <= 0)
332 continue;
333
334 DLINK_FOREACH_SAFE(n, n2, bucket->head)
335 {
336 F = (fde_t *) n->data;
337
338 if(F == NULL)
339 continue;
340 if(!F->flags.open)
341 continue;
342 if(F->flags.closing)
343 continue;
344
345 /* check flush functions */
346 if(F->flush_handler &&
347 F->flush_timeout > 0 && F->flush_timeout < CurrentTime)
348 {
349 hdl = F->flush_handler;
350 data = F->flush_data;
351 comm_setflush(F->fd, 0, NULL, NULL);
352 hdl(F->fd, data);
353 }
354
355 /* check timeouts */
356 if(F->timeout_handler &&
357 F->timeout > 0 && F->timeout < CurrentTime)
358 {
359 /* Call timeout handler */
360 hdl = F->timeout_handler;
361 data = F->timeout_data;
362 comm_settimeout(F->fd, 0, NULL, NULL);
363 hdl(F->fd, data);
364 }
365 }
366 }
367 }
368
369 /*
370 * void comm_connect_tcp(int fd, const char *host, u_short port,
371 * struct sockaddr *clocal, int socklen,
372 * CNCB *callback, void *data, int aftype, int timeout)
373 * Input: An fd to connect with, a host and port to connect to,
374 * a local sockaddr to connect from + length(or NULL to use the
375 * default), a callback, the data to pass into the callback, the
376 * address family.
377 * Output: None.
378 * Side-effects: A non-blocking connection to the host is started, and
379 * if necessary, set up for selection. The callback given
380 * may be called now, or it may be called later.
381 */
382 void
383 comm_connect_tcp(int fd, const char *host, u_short port,
384 struct sockaddr *clocal, int socklen, CNCB * callback,
385 void *data, int aftype, int timeout)
386 {
387 void *ipptr = NULL;
388 fde_t *F;
389 s_assert(fd >= 0);
390 F = comm_locate_fd(fd);
391 F->flags.called_connect = 1;
392 s_assert(callback);
393 F->connect.callback = callback;
394 F->connect.data = data;
395
396 memset(&F->connect.hostaddr, 0, sizeof(F->connect.hostaddr));
397 #ifdef IPV6
398 if(aftype == AF_INET6)
399 {
400 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr;
401 SET_SS_LEN(F->connect.hostaddr, sizeof(struct sockaddr_in6));
402 in6->sin6_port = htons(port);
403 in6->sin6_family = AF_INET6;
404 ipptr = &in6->sin6_addr;
405 } else
406 #endif
407 {
408 struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr;
409 SET_SS_LEN(F->connect.hostaddr, sizeof(struct sockaddr_in));
410 in->sin_port = htons(port);
411 in->sin_family = AF_INET;
412 ipptr = &in->sin_addr;
413 }
414
415 /* Note that we're using a passed sockaddr here. This is because
416 * generally you'll be bind()ing to a sockaddr grabbed from
417 * getsockname(), so this makes things easier.
418 * XXX If NULL is passed as local, we should later on bind() to the
419 * virtual host IP, for completeness.
420 * -- adrian
421 */
422 if((clocal != NULL) && (bind(F->fd, clocal, socklen) < 0))
423 {
424 /* Failure, call the callback with COMM_ERR_BIND */
425 comm_connect_callback(F->fd, COMM_ERR_BIND);
426 /* ... and quit */
427 return;
428 }
429
430 /* Next, if we have been given an IP, get the addr and skip the
431 * DNS check (and head direct to comm_connect_tryconnect().
432 */
433 if(inetpton(aftype, host, ipptr) <= 0)
434 {
435 /* Send the DNS request, for the next level */
436 F->dns_query = MyMalloc(sizeof(struct DNSQuery));
437 F->dns_query->ptr = F;
438 F->dns_query->callback = comm_connect_dns_callback;
439 #ifdef IPV6
440 if (aftype == AF_INET6)
441 gethost_byname_type(host, F->dns_query, T_AAAA);
442 else
443 #endif
444 gethost_byname_type(host, F->dns_query, T_A);
445 }
446 else
447 {
448 /* We have a valid IP, so we just call tryconnect */
449 /* Make sure we actually set the timeout here .. */
450 comm_settimeout(F->fd, timeout * 1000, comm_connect_timeout, NULL);
451 comm_connect_tryconnect(F->fd, NULL);
452 }
453 }
454
455 /*
456 * comm_connect_callback() - call the callback, and continue with life
457 */
458 static void
459 comm_connect_callback(int fd, int status)
460 {
461 CNCB *hdl;
462 fde_t *F = comm_locate_fd(fd);
463
464 /* This check is gross..but probably necessary */
465 if(F == NULL || F->connect.callback == NULL)
466 return;
467
468 /* Clear the connect flag + handler */
469 hdl = F->connect.callback;
470 F->connect.callback = NULL;
471 F->flags.called_connect = 0;
472
473 /* Clear the timeout handler */
474 comm_settimeout(F->fd, 0, NULL, NULL);
475
476 /* Call the handler */
477 hdl(F->fd, status, F->connect.data);
478 }
479
480
481 /*
482 * comm_connect_timeout() - this gets called when the socket connection
483 * times out. This *only* can be called once connect() is initially
484 * called ..
485 */
486 static void
487 comm_connect_timeout(int fd, void *notused)
488 {
489 /* error! */
490 comm_connect_callback(fd, COMM_ERR_TIMEOUT);
491 }
492
493
494 /*
495 * comm_connect_dns_callback() - called at the completion of the DNS request
496 *
497 * The DNS request has completed, so if we've got an error, return it,
498 * otherwise we initiate the connect()
499 */
500 static void
501 comm_connect_dns_callback(void *vptr, struct DNSReply *reply)
502 {
503 fde_t *F = vptr;
504
505 /* Free dns_query now to avoid double reslist free -- jilles */
506 MyFree(F->dns_query);
507 F->dns_query = NULL;
508
509 if(!reply)
510 {
511 comm_connect_callback(F->fd, COMM_ERR_DNS);
512 return;
513 }
514
515 /* No error, set a 10 second timeout */
516 comm_settimeout(F->fd, 30 * 1000, comm_connect_timeout, NULL);
517
518 /* Copy over the DNS reply info so we can use it in the connect() */
519 #ifdef IPV6
520 if(reply->addr.ss_family == AF_INET6)
521 {
522 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&F->connect.hostaddr;
523 memcpy(&in6->sin6_addr, &((struct sockaddr_in6 *)&reply->addr)->sin6_addr, sizeof(struct in6_addr));
524 }
525 else
526 #endif
527 {
528 struct sockaddr_in *in = (struct sockaddr_in *)&F->connect.hostaddr;
529 in->sin_addr.s_addr = ((struct sockaddr_in *)&reply->addr)->sin_addr.s_addr;
530 }
531
532 /* Now, call the tryconnect() routine to try a connect() */
533 comm_connect_tryconnect(F->fd, NULL);
534 }
535
536
537 /* static void comm_connect_tryconnect(int fd, void *notused)
538 * Input: The fd, the handler data(unused).
539 * Output: None.
540 * Side-effects: Try and connect with pending connect data for the FD. If
541 * we succeed or get a fatal error, call the callback.
542 * Otherwise, it is still blocking or something, so register
543 * to select for a write event on this FD.
544 */
545 static void
546 comm_connect_tryconnect(int fd, void *notused)
547 {
548 int retval;
549 fde_t *F = comm_locate_fd(fd);
550
551 if(F->connect.callback == NULL)
552 return;
553 /* Try the connect() */
554 retval = connect(fd, (struct sockaddr *) &F->connect.hostaddr,
555 GET_SS_LEN(F->connect.hostaddr));
556 /* Error? */
557 if(retval < 0)
558 {
559 /*
560 * If we get EISCONN, then we've already connect()ed the socket,
561 * which is a good thing.
562 * -- adrian
563 */
564 if(errno == EISCONN)
565 comm_connect_callback(F->fd, COMM_OK);
566 else if(ignoreErrno(errno))
567 /* Ignore error? Reschedule */
568 comm_setselect(F->fd, FDLIST_SERVER, COMM_SELECT_WRITE|COMM_SELECT_RETRY,
569 comm_connect_tryconnect, NULL, 0);
570 else
571 /* Error? Fail with COMM_ERR_CONNECT */
572 comm_connect_callback(F->fd, COMM_ERR_CONNECT);
573 return;
574 }
575 /* If we get here, we've suceeded, so call with COMM_OK */
576 comm_connect_callback(F->fd, COMM_OK);
577 }
578
579 /*
580 * comm_error_str() - return an error string for the given error condition
581 */
582 const char *
583 comm_errstr(int error)
584 {
585 if(error < 0 || error >= COMM_ERR_MAX)
586 return "Invalid error number!";
587 return comm_err_str[error];
588 }
589
590
591 /*
592 * comm_socket() - open a socket
593 *
594 * This is a highly highly cut down version of squid's comm_open() which
595 * for the most part emulates socket(), *EXCEPT* it fails if we're about
596 * to run out of file descriptors.
597 */
598 int
599 comm_socket(int family, int sock_type, int proto, const char *note)
600 {
601 int fd;
602 /* First, make sure we aren't going to run out of file descriptors */
603 if(number_fd >= MASTER_MAX)
604 {
605 errno = ENFILE;
606 return -1;
607 }
608
609 /*
610 * Next, we try to open the socket. We *should* drop the reserved FD
611 * limit if/when we get an error, but we can deal with that later.
612 * XXX !!! -- adrian
613 */
614 fd = socket(family, sock_type, proto);
615 comm_fd_hack(&fd);
616 if(fd < 0)
617 return -1; /* errno will be passed through, yay.. */
618
619 #if defined(IPV6) && defined(IPV6_V6ONLY)
620 /*
621 * Make sure we can take both IPv4 and IPv6 connections
622 * on an AF_INET6 socket
623 */
624 if(family == AF_INET6)
625 {
626 int off = 1;
627 if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) == -1)
628 {
629 libcharybdis_log("comm_socket: Could not set IPV6_V6ONLY option to 1 on FD %d: %s",
630 fd, strerror(errno));
631 close(fd);
632 return -1;
633 }
634 }
635 #endif
636
637 /* Set the socket non-blocking, and other wonderful bits */
638 if(!comm_set_nb(fd))
639 {
640 libcharybdis_log("comm_open: Couldn't set FD %d non blocking: %s", fd, strerror(errno));
641 close(fd);
642 return -1;
643 }
644
645 /* Next, update things in our fd tracking */
646 comm_open(fd, FD_SOCKET, note);
647 return fd;
648 }
649
650
651 /*
652 * comm_accept() - accept an incoming connection
653 *
654 * This is a simple wrapper for accept() which enforces FD limits like
655 * comm_open() does.
656 */
657 int
658 comm_accept(int fd, struct sockaddr *pn, socklen_t *addrlen)
659 {
660 int newfd;
661 if(number_fd >= MASTER_MAX)
662 {
663 errno = ENFILE;
664 return -1;
665 }
666
667 /*
668 * Next, do the accept(). if we get an error, we should drop the
669 * reserved fd limit, but we can deal with that when comm_open()
670 * also does it. XXX -- adrian
671 */
672 newfd = accept(fd, (struct sockaddr *) pn, addrlen);
673 comm_fd_hack(&newfd);
674
675 if(newfd < 0)
676 return -1;
677
678 /* Set the socket non-blocking, and other wonderful bits */
679 if(!comm_set_nb(newfd))
680 {
681 libcharybdis_log("comm_accept: Couldn't set FD %d non blocking!", newfd);
682 close(newfd);
683 return -1;
684 }
685
686 /* Next, tag the FD as an incoming connection */
687 comm_open(newfd, FD_SOCKET, "Incoming connection");
688
689 /* .. and return */
690 return newfd;
691 }
692
693 /*
694 * If a sockaddr_storage is AF_INET6 but is a mapped IPv4
695 * socket manged the sockaddr.
696 */
697 #ifndef mangle_mapped_sockaddr
698 void
699 mangle_mapped_sockaddr(struct sockaddr *in)
700 {
701 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)in;
702
703 if(in->sa_family == AF_INET)
704 return;
705
706 if(in->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr))
707 {
708 struct sockaddr_in in4;
709 memset(&in4, 0, sizeof(struct sockaddr_in));
710 in4.sin_family = AF_INET;
711 in4.sin_port = in6->sin6_port;
712 in4.sin_addr.s_addr = ((uint32_t *)&in6->sin6_addr)[3];
713 memcpy(in, &in4, sizeof(struct sockaddr_in));
714 }
715 return;
716 }
717 #endif
718
719
720 static void
721 fdlist_update_biggest(int fd, int opening)
722 {
723 if(fd < highest_fd)
724 return;
725 s_assert(fd < MAXCONNECTIONS);
726
727 if(fd > highest_fd)
728 {
729 /*
730 * s_assert that we are not closing a FD bigger than
731 * our known biggest FD
732 */
733 s_assert(opening);
734 highest_fd = fd;
735 return;
736 }
737 /* if we are here, then fd == Biggest_FD */
738 /*
739 * s_assert that we are closing the biggest FD; we can't be
740 * re-opening it
741 */
742 s_assert(!opening);
743 while (highest_fd >= 0 && comm_locate_fd(fd)->flags.open) /* XXX */
744 highest_fd--;
745 }
746
747
748 void
749 fdlist_init(void)
750 {
751 static int initialized = 0;
752
753 if(!initialized)
754 {
755 memset(&fd_table, '\0', sizeof(dlink_list) * FD_HASH_SIZE);
756 initialized = 1;
757 }
758 }
759
760 /* Called to open a given filedescriptor */
761 void
762 comm_open(int fd, unsigned int type, const char *desc)
763 {
764 fde_t *F = comm_add_fd(fd);
765 s_assert(fd >= 0);
766
767 if(F->flags.open)
768 {
769 comm_close(fd);
770 }
771 s_assert(!F->flags.open);
772 F->fd = fd;
773 F->type = type;
774 F->flags.open = 1;
775 #ifdef NOTYET
776 F->defer.until = 0;
777 F->defer.n = 0;
778 F->defer.handler = NULL;
779 #endif
780 fdlist_update_biggest(fd, 1);
781 F->comm_index = -1;
782 F->list = FDLIST_NONE;
783 if(desc)
784 strlcpy(F->desc, desc, sizeof(F->desc));
785 number_fd++;
786 }
787
788
789 /* Called to close a given filedescriptor */
790 void
791 comm_close(int fd)
792 {
793 fde_t *F = comm_locate_fd(fd);
794 s_assert(F->flags.open);
795 /* All disk fd's MUST go through file_close() ! */
796 s_assert(F->type != FD_FILE);
797 if(F->type == FD_FILE)
798 {
799 s_assert(F->read_handler == NULL);
800 s_assert(F->write_handler == NULL);
801 }
802 comm_setselect(F->fd, FDLIST_NONE, COMM_SELECT_WRITE | COMM_SELECT_READ, NULL, NULL, 0);
803 comm_setflush(F->fd, 0, NULL, NULL);
804
805 if (F->dns_query != NULL)
806 {
807 delete_resolver_queries(F->dns_query);
808 MyFree(F->dns_query);
809 F->dns_query = NULL;
810 }
811
812 F->flags.open = 0;
813 fdlist_update_biggest(fd, 0);
814 number_fd--;
815 comm_remove_fd(fd);
816
817 F->timeout = 0;
818 /* Unlike squid, we're actually closing the FD here! -- adrian */
819 close(fd);
820 }
821
822 /*
823 * comm_dump() - dump the list of active filedescriptors
824 */
825 void
826 comm_dump(struct Client *source_p)
827 {
828 int i;
829
830 for (i = 0; i <= FD_HASH_SIZE; i++)
831 {
832 dlink_node *n;
833
834 if (dlink_list_length(&fd_table[i]) <= 0)
835 continue;
836
837 DLINK_FOREACH(n, fd_table[i].head)
838 {
839 fde_t *F = (fde_t *) n->data;
840
841 if(F == NULL || !F->flags.open)
842 continue;
843
844 sendto_one_numeric(source_p, RPL_STATSDEBUG,
845 "F :fd %-3d bucket %-3d desc '%s'",
846 F->fd, i, F->desc);
847 }
848 }
849 }
850
851 /*
852 * comm_note() - set the fd note
853 *
854 * Note: must be careful not to overflow fd_table[fd].desc when
855 * calling.
856 */
857 void
858 comm_note(int fd, const char *format, ...)
859 {
860 va_list args;
861 fde_t *F = comm_add_fd(fd); /* XXX: epoll, kqueue. */
862
863 if(format)
864 {
865 va_start(args, format);
866 ircvsnprintf(F->desc, FD_DESC_SZ, format, args);
867 va_end(args);
868 }
869 else
870 F->desc[0] = '\0';
871 }
872
873