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