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