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