mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-06-28 16:16:38 +02:00
ba8e5cd7a0
- Added set::nick-length so the nick length can be dynamically changed (from 1-30), requested by various people including IdiotStic (#0003042).
727 lines
16 KiB
C
727 lines
16 KiB
C
/************************************************************************
|
|
* Unreal Internet Relay Chat Daemon, src/fdlist.c
|
|
* Copyright (C) Mika Nystrom
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#include "struct.h"
|
|
#include "common.h"
|
|
#include "sys.h"
|
|
#include "h.h"
|
|
#include "config.h"
|
|
#include "fdlist.h"
|
|
#include "proto.h"
|
|
#include <string.h>
|
|
#ifndef _WIN32
|
|
#include <sys/resource.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
|
|
/*
|
|
* Hybrid Support Routines At first stage we can use it. In time i will port
|
|
* them to Unreal stuff.
|
|
*/
|
|
#ifdef NEW_IO
|
|
|
|
|
|
const unsigned char ToUpperTab[] = {
|
|
0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
|
|
0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
|
|
0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
|
|
0x1e, 0x1f,
|
|
' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
|
|
'*', '+', ',', '-', '.', '/',
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
':', ';', '<', '=', '>', '?',
|
|
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
|
|
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
|
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
|
|
0x5f,
|
|
'`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
|
|
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
|
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
|
|
0x7f,
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
|
0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
|
|
0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
|
|
0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
|
|
0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
|
|
0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
|
|
0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
|
0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
|
|
0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
|
};
|
|
|
|
|
|
/*
|
|
* irccmp - case insensitive comparison of two 0 terminated strings.
|
|
*
|
|
* returns 0, if s1 equal to s2
|
|
* 1, if not
|
|
*/
|
|
int
|
|
irccmp(const char *s1, const char *s2)
|
|
{
|
|
const unsigned char *str1 = (const unsigned char *) s1;
|
|
const unsigned char *str2 = (const unsigned char *) s2;
|
|
|
|
assert(s1 != NULL);
|
|
assert(s2 != NULL);
|
|
|
|
while (ToUpper(*str1) == ToUpper(*str2))
|
|
{
|
|
if (*str1 == '\0')
|
|
return 0;
|
|
str1++;
|
|
str2++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* dlink_ routines are stolen from squid, except for dlinkAddBefore,
|
|
* which is mine.
|
|
* -- adrian
|
|
*/
|
|
void dlinkAdd(void *data, dlink_node * m, dlink_list * list)
|
|
{
|
|
m->data = data;
|
|
m->prev = NULL;
|
|
m->next = list->head;
|
|
|
|
/* Assumption: If list->tail != NULL, list->head != NULL */
|
|
if (list->head != NULL)
|
|
list->head->prev = m;
|
|
else /* if (list->tail == NULL) */
|
|
list->tail = m;
|
|
|
|
list->head = m;
|
|
list->length++;
|
|
}
|
|
|
|
void
|
|
dlinkAddBefore(dlink_node *b, void *data, dlink_node *m, dlink_list *list)
|
|
{
|
|
/* Shortcut - if its the first one, call dlinkAdd only */
|
|
if (b == list->head)
|
|
{
|
|
dlinkAdd(data, m, list);
|
|
}
|
|
else
|
|
{
|
|
m->data = data;
|
|
b->prev->next = m;
|
|
m->prev = b->prev;
|
|
b->prev = m;
|
|
m->next = b;
|
|
list->length++;
|
|
}
|
|
}
|
|
|
|
void
|
|
dlinkAddTail(void *data, dlink_node *m, dlink_list *list)
|
|
{
|
|
m->data = data;
|
|
m->next = NULL;
|
|
m->prev = list->tail;
|
|
/* Assumption: If list->tail != NULL, list->head != NULL */
|
|
if (list->tail != NULL)
|
|
list->tail->next = m;
|
|
else /* if (list->head == NULL) */
|
|
list->head = m;
|
|
|
|
list->tail = m;
|
|
list->length++;
|
|
}
|
|
|
|
/* Execution profiles show that this function is called the most
|
|
* often of all non-spontaneous functions. So it had better be
|
|
* efficient. */
|
|
void
|
|
dlinkDelete(dlink_node *m, dlink_list *list)
|
|
{
|
|
/* Assumption: If m->next == NULL, then list->tail == m
|
|
* and: If m->prev == NULL, then list->head == m
|
|
*/
|
|
if (m->next)
|
|
m->next->prev = m->prev;
|
|
else {
|
|
assert(list->tail == m);
|
|
list->tail = m->prev;
|
|
}
|
|
if (m->prev)
|
|
m->prev->next = m->next;
|
|
else {
|
|
assert(list->head == m);
|
|
list->head = m->next;
|
|
}
|
|
|
|
/* Set this to NULL does matter */
|
|
m->next = m->prev = NULL;
|
|
list->length--;
|
|
}
|
|
|
|
/*
|
|
* dlinkFind
|
|
* inputs - list to search
|
|
* - data
|
|
* output - pointer to link or NULL if not found
|
|
* side effects - Look for ptr in the linked listed pointed to by link.
|
|
*/
|
|
dlink_node *
|
|
dlinkFind(dlink_list *list, void *data)
|
|
{
|
|
dlink_node *ptr;
|
|
|
|
DLINK_FOREACH(ptr, list->head)
|
|
{
|
|
if (ptr->data == data)
|
|
return(ptr);
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
void
|
|
dlinkMoveList(dlink_list *from, dlink_list *to)
|
|
{
|
|
/* There are three cases */
|
|
/* case one, nothing in from list */
|
|
if(from->head == NULL)
|
|
return;
|
|
|
|
/* case two, nothing in to list */
|
|
/* actually if to->head is NULL and to->tail isn't, thats a bug */
|
|
|
|
if(to->head == NULL)
|
|
{
|
|
to->head = from->head;
|
|
to->tail = from->tail;
|
|
from->head = from->tail = NULL;
|
|
to->length = from->length;
|
|
from->length = 0;
|
|
return;
|
|
}
|
|
|
|
/* third case play with the links */
|
|
|
|
from->tail->next = to->head;
|
|
from->head->prev = to->head->prev;
|
|
to->head->prev = from->tail;
|
|
to->head = from->head;
|
|
from->head = from->tail = NULL;
|
|
to->length += from->length;
|
|
from->length = 0;
|
|
/* I think I got that right */
|
|
}
|
|
|
|
dlink_node *
|
|
dlinkFindDelete(dlink_list *list, void *data)
|
|
{
|
|
dlink_node *m;
|
|
|
|
DLINK_FOREACH(m, list->head)
|
|
{
|
|
if (m->data == data)
|
|
{
|
|
if (m->next)
|
|
m->next->prev = m->prev;
|
|
else
|
|
{
|
|
assert(list->tail == m);
|
|
list->tail = m->prev;
|
|
}
|
|
if (m->prev)
|
|
m->prev->next = m->next;
|
|
else
|
|
{
|
|
assert(list->head == m);
|
|
list->head = m->next;
|
|
}
|
|
/* Set this to NULL does matter */
|
|
m->next = m->prev = NULL;
|
|
list->length--;
|
|
|
|
return(m);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
dlink_list callback_list = {NULL, NULL, 0};
|
|
|
|
struct Callback *register_callback(const char *name, CBFUNC *func)
|
|
{
|
|
struct Callback *cb;
|
|
|
|
if (name != NULL)
|
|
if ((cb = find_callback(name)) != NULL)
|
|
{
|
|
if (func != NULL)
|
|
dlinkAddTail(func, MyMalloc(sizeof(dlink_node)), &cb->chain);
|
|
return (cb);
|
|
}
|
|
|
|
cb = MyMalloc(sizeof(struct Callback));
|
|
if (func != NULL)
|
|
dlinkAdd(func, MyMalloc(sizeof(dlink_node)), &cb->chain);
|
|
if (name != NULL)
|
|
{
|
|
DupString(cb->name, name);
|
|
dlinkAdd(cb, &cb->node, &callback_list);
|
|
}
|
|
return (cb);
|
|
}
|
|
|
|
/*
|
|
* find_callback()
|
|
*
|
|
* Finds a named callback.
|
|
*
|
|
* inputs:
|
|
* name - name of the callback
|
|
* output: pointer to Callback structure or NULL if not found
|
|
*/
|
|
struct Callback *
|
|
find_callback(const char *name)
|
|
{
|
|
struct Callback *cb;
|
|
dlink_node *ptr;
|
|
|
|
DLINK_FOREACH(ptr, callback_list.head)
|
|
{
|
|
cb = ptr->data;
|
|
if (!irccmp(cb->name, name))
|
|
return (cb);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* install_hook()
|
|
*
|
|
* Installs a hook for the given callback.
|
|
*
|
|
* inputs:
|
|
* cb - pointer to Callback structure
|
|
* hook - address of hook function
|
|
* output: pointer to dlink_node of the hook (used when
|
|
* passing control to the next hook in the chain);
|
|
* valid till uninstall_hook() is called
|
|
*
|
|
* NOTE: The new hook is installed at the beginning of the chain,
|
|
* so it has full control over functions installed earlier.
|
|
*/
|
|
dlink_node *install_hook(struct Callback *cb, CBFUNC *hook)
|
|
{
|
|
dlink_node *node = MyMalloc(sizeof(dlink_node));
|
|
|
|
dlinkAdd(hook, node, &cb->chain);
|
|
return (node);
|
|
}
|
|
|
|
/*
|
|
* uninstall_hook()
|
|
*
|
|
* Removes a specific hook for the given callback.
|
|
*
|
|
* inputs:
|
|
* cb - pointer to Callback structure
|
|
* hook - address of hook function
|
|
* output: none
|
|
*/
|
|
void
|
|
uninstall_hook(struct Callback *cb, CBFUNC *hook)
|
|
{
|
|
/* let it core if not found */
|
|
dlink_node *ptr = dlinkFind(&cb->chain, hook);
|
|
|
|
dlinkDelete(ptr, &cb->chain);
|
|
MyFree(ptr);
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef NEW_IO
|
|
extern fdlist default_fdlist;
|
|
extern fdlist busycli_fdlist;
|
|
extern fdlist serv_fdlist;
|
|
extern fdlist oper_fdlist;
|
|
|
|
#define FDLIST_DEBUG
|
|
|
|
void addto_fdlist(int fd, fdlist * listp)
|
|
{
|
|
int index;
|
|
#ifdef FDLIST_DEBUG
|
|
int i;
|
|
#endif
|
|
|
|
/* I prefer this little 5-cpu-cycles-check over memory corruption. -- Syzop */
|
|
if ((fd < 0) || (fd >= MAXCONNECTIONS))
|
|
{
|
|
sendto_realops("[BUG] trying to add fd #%d to %p (%p/%p/%p/%p), range is 0..%d",
|
|
fd, listp, &default_fdlist, &busycli_fdlist, &serv_fdlist, &oper_fdlist,
|
|
MAXCONNECTIONS);
|
|
ircd_log(LOG_ERROR, "[BUG] trying to add fd #%d to %p (%p/%p/%p/%p), range is 0..%d",
|
|
fd, listp, &default_fdlist, &busycli_fdlist, &serv_fdlist, &oper_fdlist,
|
|
MAXCONNECTIONS);
|
|
return;
|
|
}
|
|
|
|
#ifdef FDLIST_DEBUG
|
|
for (i = listp->last_entry; i; i--)
|
|
{
|
|
if (listp->entry[i] == fd)
|
|
{
|
|
char buf[2048];
|
|
ircsprintf(buf, "[BUG] addto_fdlist() called for duplicate entry! fd=%d, fdlist=%p, client=%s (%p/%p/%p/%p)",
|
|
fd, listp, local[fd] ? local[fd]->name : "<null>", &default_fdlist, &busycli_fdlist, &serv_fdlist, &oper_fdlist);
|
|
sendto_realops("%s", buf);
|
|
ircd_log(LOG_ERROR, "%s", buf);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((index = ++listp->last_entry) >= MAXCONNECTIONS)
|
|
{
|
|
/*
|
|
* list too big.. must exit
|
|
*/
|
|
--listp->last_entry;
|
|
ircd_log(LOG_ERROR, "fdlist.c list too big, must exit...");
|
|
abort();
|
|
}
|
|
else
|
|
listp->entry[index] = fd;
|
|
return;
|
|
}
|
|
|
|
void delfrom_fdlist(int fd, fdlist * listp)
|
|
{
|
|
int i;
|
|
#ifdef FDLIST_DEBUG
|
|
int cnt = 0;
|
|
#endif
|
|
|
|
/* I prefer this little 5-cpu-cycles-check over memory corruption. -- Syzop */
|
|
if ((fd < 0) || (fd >= MAXCONNECTIONS))
|
|
{
|
|
sendto_realops("[BUG] trying to remove fd #%d to %p (%p/%p/%p/%p), range is 0..%d",
|
|
fd, listp, &default_fdlist, &busycli_fdlist, &serv_fdlist, &oper_fdlist,
|
|
MAXCONNECTIONS);
|
|
ircd_log(LOG_ERROR, "[BUG] trying to remove fd #%d to %p (%p/%p/%p/%p), range is 0..%d",
|
|
fd, listp, &default_fdlist, &busycli_fdlist, &serv_fdlist, &oper_fdlist,
|
|
MAXCONNECTIONS);
|
|
return;
|
|
}
|
|
|
|
#ifdef FDLIST_DEBUG
|
|
for (i = listp->last_entry; i; i--)
|
|
{
|
|
if (listp->entry[i] == fd)
|
|
cnt++;
|
|
}
|
|
if (cnt > 1)
|
|
{
|
|
char buf[2048];
|
|
ircsprintf(buf, "[BUG] delfrom_fdlist() called, duplicate entries detected! fd=%d, fdlist=%p, client=%s (%p/%p/%p/%p)",
|
|
fd, listp, local[fd] ? local[fd]->name : "<null>", &default_fdlist, &busycli_fdlist, &serv_fdlist, &oper_fdlist);
|
|
sendto_realops("%s", buf);
|
|
ircd_log(LOG_ERROR, "%s", buf);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
for (i = listp->last_entry; i; i--)
|
|
{
|
|
if (listp->entry[i] == fd)
|
|
break;
|
|
}
|
|
if (!i)
|
|
return; /*
|
|
* could not find it!
|
|
*/
|
|
/*
|
|
* swap with last_entry
|
|
*/
|
|
if (i == listp->last_entry)
|
|
{
|
|
listp->entry[i] = 0;
|
|
listp->last_entry--;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
listp->entry[i] = listp->entry[listp->last_entry];
|
|
listp->entry[listp->last_entry] = 0;
|
|
listp->last_entry--;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void init_fdlist(fdlist * listp)
|
|
{
|
|
listp->last_entry = 0;
|
|
memset((char *)listp->entry, '\0', sizeof(listp->entry));
|
|
return;
|
|
}
|
|
|
|
#else /* ifndef NEW_IO */
|
|
fde_t *fd_hash[FD_HASH_SIZE];
|
|
fde_t *fd_next_in_loop = NULL;
|
|
int number_fd = LEAKED_FDS;
|
|
int hard_fdlimit = 0;
|
|
struct Callback *fdlimit_cb = NULL;
|
|
|
|
static void *changing_fdlimit(va_list args)
|
|
{
|
|
int old_fdlimit = hard_fdlimit;
|
|
|
|
hard_fdlimit = va_arg(args, int);
|
|
/* FIXME: Hybrid uses ServerInfo structure for server specific flags
|
|
* have to convert it to Unreal
|
|
* if (ServerInfo.max_clients > MAXCLIENTS_MAX)
|
|
*/
|
|
if (1024 > MAXCLIENTS_MAX)
|
|
{
|
|
if (old_fdlimit != 0)
|
|
sendto_realops("HARD_FDLIMIT changed to %d, adjusting MAXCLIENTS to %d", hard_fdlimit, MAXCLIENTS_MAX);
|
|
|
|
/* FIXME: ServerInfo.max_clients = MAXCLIENTS_MAX; */
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void fdlist_init(void)
|
|
{
|
|
memset(&fd_hash, 0, sizeof(fd_hash));
|
|
|
|
fdlimit_cb = register_callback("changing_fdlimit", changing_fdlimit);
|
|
eventAddIsh("recalc_fdlimit", recalc_fdlimit, NULL, 58);
|
|
recalc_fdlimit(NULL);
|
|
}
|
|
|
|
void
|
|
recalc_fdlimit(void *unused)
|
|
{
|
|
#ifdef _WIN32
|
|
/* this is what WSAStartup() usually returns. Even though they say
|
|
* the value is for compatibility reasons and should be ignored,
|
|
* we actually can create even more sockets... */
|
|
hard_fdlimit = 32767;
|
|
#else
|
|
int fdmax;
|
|
struct rlimit limit;
|
|
|
|
if (!getrlimit(RLIMIT_FD_MAX, &limit))
|
|
{
|
|
limit.rlim_cur = limit.rlim_max;
|
|
setrlimit(RLIMIT_FD_MAX, &limit);
|
|
}
|
|
|
|
fdmax = getdtablesize();
|
|
|
|
/* allow MAXCLIENTS_MIN clients even at the cost of MAX_BUFFER and
|
|
* some not really LEAKED_FDS */
|
|
fdmax = IRCD_MAX(fdmax, LEAKED_FDS + MAX_BUFFER + MAXCLIENTS_MIN);
|
|
|
|
/* under no condition shall this raise over 65536
|
|
* for example user ip heap is sized 2*hard_fdlimit */
|
|
fdmax = IRCD_MIN(fdmax, 65536);
|
|
|
|
if (fdmax != hard_fdlimit)
|
|
execute_callback(fdlimit_cb, fdmax);
|
|
#endif
|
|
}
|
|
|
|
static inline unsigned int hash_fd(int fd)
|
|
{
|
|
#ifdef _WIN32
|
|
return ((((unsigned) fd) >> 2) % FD_HASH_SIZE);
|
|
#else
|
|
return (((unsigned) fd) % FD_HASH_SIZE);
|
|
#endif
|
|
}
|
|
|
|
fde_t * lookup_fd(int fd)
|
|
{
|
|
fde_t *F = fd_hash[hash_fd(fd)];
|
|
|
|
while (F)
|
|
{
|
|
if (F->fd == fd)
|
|
return (F);
|
|
F = F->hnext;
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/* Called to open a given filedescriptor */
|
|
void fd_open(fde_t *F, int fd, int is_socket, const char *desc)
|
|
{
|
|
unsigned int hashv = hash_fd(fd);
|
|
assert(fd >= 0);
|
|
|
|
F->fd = fd;
|
|
F->comm_index = -1;
|
|
if (desc)
|
|
strlcpy(F->desc, desc, sizeof(F->desc));
|
|
/* Note: normally we'd have to clear the other flags,
|
|
* but currently F is always cleared before calling us.. */
|
|
F->flags.open = 1;
|
|
F->flags.is_socket = is_socket;
|
|
F->hnext = fd_hash[hashv];
|
|
fd_hash[hashv] = F;
|
|
|
|
number_fd++;
|
|
}
|
|
|
|
/* Called to close a given filedescriptor */
|
|
void fd_close(fde_t *F)
|
|
{
|
|
unsigned int hashv = hash_fd(F->fd);
|
|
|
|
if (F == fd_next_in_loop)
|
|
fd_next_in_loop = F->hnext;
|
|
|
|
if (F->flags.is_socket)
|
|
comm_setselect(F, COMM_SELECT_WRITE | COMM_SELECT_READ, NULL, NULL, 0);
|
|
|
|
if (F->dns_query != NULL)
|
|
{
|
|
delete_resolver_queries(F->dns_query);
|
|
MyFree(F->dns_query);
|
|
}
|
|
|
|
#ifdef HAVE_LIBCRYPTO
|
|
if (F->ssl)
|
|
SSL_free(F->ssl);
|
|
#endif
|
|
|
|
if (fd_hash[hashv] == F)
|
|
fd_hash[hashv] = F->hnext;
|
|
else {
|
|
fde_t *prev;
|
|
|
|
/* let it core if not found */
|
|
for (prev = fd_hash[hashv]; prev->hnext != F; prev = prev->hnext)
|
|
;
|
|
prev->hnext = F->hnext;
|
|
}
|
|
|
|
/* Unlike squid, we're actually closing the FD here! -- adrian */
|
|
#ifdef _WIN32
|
|
if (F->flags.is_socket)
|
|
closesocket(F->fd);
|
|
else
|
|
CloseHandle((HANDLE)F->fd);
|
|
#else
|
|
close(F->fd);
|
|
#endif
|
|
number_fd--;
|
|
|
|
memset(F, 0, sizeof(fde_t));
|
|
}
|
|
|
|
/*
|
|
* fd_note() - set the fd note
|
|
*
|
|
* Note: must be careful not to overflow fd_table[fd].desc when
|
|
* calling.
|
|
*/
|
|
void fd_note(fde_t *F, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (format != NULL)
|
|
{
|
|
va_start(args, format);
|
|
vsnprintf(F->desc, sizeof(F->desc), format, args);
|
|
va_end(args);
|
|
}
|
|
else
|
|
F->desc[0] = '\0';
|
|
}
|
|
|
|
/* Make sure stdio descriptors (0-2) and profiler descriptor (3)
|
|
* always go somewhere harmless. Use -foreground for profiling
|
|
* or executing from gdb */
|
|
#ifndef _WIN32
|
|
void close_standard_fds(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < LOWEST_SAFE_FD; i++)
|
|
{
|
|
close(i);
|
|
if (open(PATH_DEVNULL, O_RDWR) < 0)
|
|
exit(-1); /* we're hosed if we can't even open /dev/null */
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void close_fds(fde_t *one)
|
|
{
|
|
int i;
|
|
fde_t *F;
|
|
|
|
for (i = 0; i < FD_HASH_SIZE; i++)
|
|
for (F = fd_hash[i]; F != NULL; F = F->hnext)
|
|
if (F != one)
|
|
{
|
|
#ifdef _WIN32
|
|
if (F->flags.is_socket)
|
|
closesocket(F->fd);
|
|
else
|
|
CloseHandle((HANDLE)F->fd);
|
|
#else
|
|
close(F->fd);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
#endif /* ifndef NEW_IO */
|