Initial Checking of MessageQueue Server

This commit is contained in:
Fish 2004-04-27 13:45:47 +00:00
parent 26b50de4c8
commit 452fffb460
88 changed files with 40535 additions and 0 deletions

87
.gitattributes vendored
View file

@ -1 +1,88 @@
* text=auto !eol
/Makefile.in -text
/aclocal.m4 -text
adns/Makefile -text
adns/adns.h -text
adns/check.c -text
adns/config.h -text
adns/config.h.in -text
adns/dlist.h -text
adns/event.c -text
adns/general.c -text
adns/internal.h -text
adns/parse.c -text
adns/query.c -text
adns/reply.c -text
adns/setup.c -text
adns/transmit.c -text
adns/tvarith.h -text
adns/types.c -text
/client.c -text
/client.h -text
/conf.c -text
/conf.h -text
/config.guess -text
/config.h.in -text
/config.sub -text
/configure -text
/configure.in -text
/defines.h -text
/dict.c -text
/dict.h -text
/dotconf.c -text
/dotconf.h -text
/hash.c -text
/hash.h -text
/install-sh -text
libevent/Makefile.am -text
libevent/Makefile.in -text
libevent/acconfig.h -text
libevent/aclocal.m4 -text
libevent/buffer.c -text
libevent/compat/err.h -text
libevent/compat/sys/_time.h -text
libevent/compat/sys/queue.h -text
libevent/compat/sys/tree.h -text
libevent/config.h.in -text
libevent/configure -text
libevent/configure.in -text
libevent/epoll.c -text
libevent/epoll_sub.c -text
libevent/err.c -text
libevent/evbuffer.c -text
libevent/event.3 -text
libevent/event.c -text
libevent/event.h -text
libevent/evsignal.h -text
libevent/install-sh -text
libevent/kqueue.c -text
libevent/missing -text
libevent/mkinstalldirs -text
libevent/poll.c -text
libevent/rtsig.c -text
libevent/sample/Makefile.am -text
libevent/sample/Makefile.in -text
libevent/sample/event-test.c -text
libevent/sample/signal-test.c -text
libevent/sample/time-test.c -text
libevent/select.c -text
libevent/signal.c -text
libevent/stamp-h.in -text
libevent/test/Makefile.am -text
libevent/test/Makefile.in -text
libevent/test/bench.c -text
libevent/test/regress.c -text
libevent/test/test-eof.c -text
libevent/test/test-init.c -text
libevent/test/test-time.c -text
libevent/test/test-weof.c -text
libevent/test/test.sh -text
/list.c -text
/list.h -text
/log.c -text
/log.h -text
/main.c -text
/mqserver.cfg -text
/sock.c -text
/sock.h -text
/version.h -text

147
Makefile.in Normal file
View file

@ -0,0 +1,147 @@
# Makefile for NeoStats
CC=@CC@
CFLAGS =@CFLAGS@
LD=ld
RM=/bin/rm -rf
LDFLAGS = @LDFLAGS@ @LIBS@
MODLDFLAGS = @MODLDFLAGS@
INSTALL = @INSTALL@
INSTALL_MOD = @INSTALL_DATA@
NEOINCLUDES = -I. -Iadns -Ilibevent
MODINCLUDES = -I. -I../.. -I../../adns
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
MODULES = @MODULES@
DIRECTORY = @prefix@
MODDIRECTORY = @prefix@/dl
DATADIRECTORY = @prefix@/data
INCDIRECTORY = @prefix@/include
DISTDIR = @PACKAGE@-@VERSION@
extauth_src = @EXTAUTH_SRC@
extauth_objs = @EXTAUTH_OBJS@
LEX = @LEX@
LEXLIBS = @LEXLIBS@
YACC = @YACC@
NEOCFLAGS = $(CFLAGS)
EXTRA_LDFLAGS = -Llibevent -levent
PROGS = mqserver
CONF =
DOCS =
DOCS_PROGS =
DATA =
OBJS = ${SRCS:.c=.o}
SRCS = main.c hash.c list.c log.c dotconf.c sock.c conf.c client.c dict.c
INCLUDES = config.h hash.h list.h mqserver.h log.h dotconf.h dict.h
BUILDFILES = configure config.sub config.guess *.in
DISTFILES = $(INCLUDES) $(COREINCS) $(SRCS) $(IRCDSRC) $(IRCDINC) \
$(BUILDFILES) $(DATA) $(DOCS) $(DOCS_PROGS) $(CONF)
ADNSFILES = adns/check.o adns/event.o adns/general.o adns/parse.o \
adns/query.o adns/reply.o adns/setup.o adns/transmit.o adns/types.o
SUBDIRS =
DISTLIBS =
DISTMOD =
all: mqserver
modules:
(cd adns; $(MAKE) $@)
(cd libevent; $(MAKE) libevent.a)
buildversion:
@(if test -f version.sh ; then $(SHELL) version.sh; else echo > version.h; fi)
mqserver: buildversion modules $(OBJS)
$(CC) $(LDFLAGS) $(OBJS) $(EXTRA_LDFLAGS) $(ADNSFILES) -o $@
# include dependency info
@MAKEDEPENDENCIES@
.c.o:
$(CC) $(NEOCFLAGS) $(NEOINCLUDES) -c $<
$(CC) -MM $(NEOINCLUDES) -c $< > $*.d
clean:
(cd dl; $(MAKE) $@)
$(RM) *.o neostats *.cache Makefile config.h *.log *.a *.d *.exe version.h
distclean:
(cd dl; $(MAKE) $@)
$(RM) *.o neostats *.cache Makefile config.h *.log *.a *.d *.exe config.status version.h
install: neostats
@echo "Installing ..."
@$(RM) @prefix@/include/*.h
$(INSTALL) -m 755 -d @prefix@
$(INSTALL) -m 755 -d @prefix@/dl
$(INSTALL) -m 755 -d @prefix@/include
$(INSTALL) -m 755 -d @prefix@/doc
$(INSTALL) -m 755 -d @prefix@/data
$(INSTALL) -m 750 -d @prefix@/logs
$(INSTALL) -m 755 $(PROGS) @prefix@
$(INSTALL_DATA) $(CONF) @prefix@
$(INSTALL_DATA) $(DOCS) @prefix@/doc
$(INSTALL) -m 755 $(DOCS_PROGS) @prefix@/doc
$(INSTALL_DATA) $(DATA) @prefix@/data
$(INSTALL_DATA) $(INCLUDES) @prefix@/include
$(INSTALL_DATA) version.h @prefix@/include
$(INSTALL_DATA) @IRCD_FILES_INC@ @prefix@/include
(cd adns; $(MAKE) $@)
(cd sqlsrv; $(MAKE) $@)
(cd dl; $(MAKE) $@)
(cd tools; $(MAKE) $@)
@echo "Done."
@cat INSTNOTES
@if test -f INSTNOTES.svn ; then cat INSTNOTES.svn ; fi
dist:
@echo -n "Creating Directories"
@$(RM) $(DISTDIR)
@echo -n "."
@mkdir $(DISTDIR)
@echo -n "."
@mkdir $(DISTDIR)/dl
@echo -n "."
@for subdir in $(SUBDIRS); do \
echo -n "."; \
mkdir $(DISTDIR)/$$subdir; \
chmod 777 $(DISTDIR)/$$subdir; \
done
@echo "Done"
@echo -n "Copying Core Distribution files"
@for file in $(DISTFILES); do \
echo -n "."; \
cp -pr $$file $(DISTDIR)/$$file; \
done
@$(RM) $(DISTDIR)/config.h
@$(RM) $(DISTDIR)/configure.in
@echo "Done"
@echo "Copying Library Distribution files"
@for subdir in $(DISTLIBS); do \
mkdir $(DISTDIR)/$$subdir; \
chmod 777 $(DISTDIR)/$$subdir; \
(cd $$subdir && $(MAKE) dist DISTDIR=../$(DISTDIR)/$$subdir && cd ..) || exit 1; \
done
@echo "Done"
@echo "Copying Tools Distribution files"
@(cd tools && $(MAKE) dist DISTDIR=../$(DISTDIR)/tools && cd ..) || exit 1;
@echo "Done"
@echo "Copying Module Distribution files"
@for mod in $(DISTMOD); do \
mkdir $(DISTDIR)/dl/$$mod; \
(cd dl/$$mod && $(MAKE) dist DISTDIR=../../$(DISTDIR)/dl/$$mod && cd ../..) || exit 1; \
done
@echo "Done"
@tar -czf $(DISTDIR).tar.gz $(DISTDIR)/*
# @$(RM) $(DISTDIR)
@echo "Tar file $(DISTDIR).tar.gz created. Freshmeat time!"
# Catch any changes in compilation options at the top of this file
$(OBJS): Makefile

4243
aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load diff

43
adns/Makefile Normal file
View file

@ -0,0 +1,43 @@
INCLUDES = -I..
SRCS = check.c event.c general.c parse.c query.c reply.c setup.c transmit.c types.c
OBJS = ${SRCS:.c=.o}
all: $(OBJS)
modules: $(OBJS)
.c.o:
$(CC) $(CFLAGS) $(INCLUDES) -c $<
clean:
$(RM) *.o *.lo *.so *.a *.exe config.h
distclean:
$(RM) *.o *.lo *.so *.a *.exe config.h
depend:
$(CC) -MM $(INCLUDES) $(CFLAGS) $(SRCS) > .depend
install:
$(INSTALL_DATA) adns.h $(INCDIRECTORY)
DISTFILES =*.c adns.h dlist.h internal.h tvarith.h Makefile *.in
dist:
@for file in $(DISTFILES); do \
cp -pr $$file $(DISTDIR)/$$file; \
done
$(OBJS): Makefile
check.o: check.c internal.h config.h adns.h dlist.h
event.o: event.c internal.h config.h adns.h dlist.h tvarith.h
general.o: general.c internal.h config.h adns.h dlist.h
parse.o: parse.c internal.h config.h adns.h dlist.h
query.o: query.c internal.h config.h adns.h dlist.h
reply.o: reply.c internal.h config.h adns.h dlist.h
setup.o: setup.c internal.h config.h adns.h dlist.h
transmit.o: transmit.c internal.h config.h adns.h dlist.h tvarith.h
types.o: types.c internal.h config.h adns.h dlist.h

873
adns/adns.h Normal file
View file

@ -0,0 +1,873 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* adns.h
* - adns user-visible API (single-threaded, without any locking)
*/
/*
*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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.
*
*
* For the benefit of certain LGPL'd `omnibus' software which
* provides a uniform interface to various things including adns, I
* make the following additional licence. I do this because the GPL
* would otherwise force either the omnibus software to be GPL'd or
* the adns-using part to be distributed separately.
*
* So: you may also redistribute and/or modify adns.h (but only the
* public header file adns.h and not any other part of adns) under the
* terms of the GNU Library General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* Note that adns itself is GPL'd. Authors of adns-using applications
* with GPL-incompatible licences, and people who distribute adns with
* applications where the whole distribution is not GPL'd, are still
* likely to be in violation of the GPL. Anyone who wants to do this
* should contact Ian Jackson. Please note that to avoid encouraging
* people to infringe the GPL as it applies to the body of adns, Ian
* thinks that if you take advantage of the special exception to
* redistribute just adns.h under the LGPL, you should retain this
* paragraph in its place in the appropriate copyright statements.
*
*
* You should have received a copy of the GNU General Public License,
* or the GNU Library General Public License, as appropriate, along
* with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
* $Id$
*/
#ifndef ADNS_H_INCLUDED
#define ADNS_H_INCLUDED
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C" { /* I really dislike this - iwj. */
#endif
/* All struct in_addr anywhere in adns are in NETWORK byte order. */
typedef struct adns__state *adns_state;
typedef struct adns__query *adns_query;
typedef enum {
adns_if_noenv = 0x0001, /* do not look at environment */
adns_if_noerrprint = 0x0002, /* never print output to stderr (_debug overrides) */
adns_if_noserverwarn = 0x0004, /* do not warn to stderr about duff nameservers etc */
adns_if_debug = 0x0008, /* enable all output to stderr plus debug msgs */
adns_if_logpid = 0x0080, /* include pid in diagnostic output */
adns_if_noautosys = 0x0010, /* do not make syscalls at every opportunity */
adns_if_eintr = 0x0020, /* allow _wait and _synchronous to return EINTR */
adns_if_nosigpipe = 0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */
adns_if_checkc_entex = 0x0100, /* do consistency checks on entry/exit to adns funcs */
adns_if_checkc_freq = 0x0300 /* do consistency checks very frequently (slow!) */
} adns_initflags;
typedef enum {
adns_qf_search = 0x00000001, /* use the searchlist */
adns_qf_usevc = 0x00000002, /* use a virtual circuit (TCP connection) */
adns_qf_owner = 0x00000004, /* fill in the owner field in the answer */
adns_qf_quoteok_query = 0x00000010, /* allow special chars in query domain */
adns_qf_quoteok_cname = 0x00000000, /* allow ... in CNAME we go via - now default */
adns_qf_quoteok_anshost = 0x00000040, /* allow ... in things supposed to be hostnames */
adns_qf_quotefail_cname = 0x00000080, /* refuse if quote-req chars in CNAME we go via */
adns_qf_cname_loose = 0x00000100, /* allow refs to CNAMEs - without, get _s_cname */
adns_qf_cname_forbid = 0x00000200, /* don't follow CNAMEs, instead give _s_cname */
adns__qf_internalmask = 0x0ff00000
} adns_queryflags;
typedef enum {
adns__rrt_typemask = 0x0ffff,
adns__qtf_deref = 0x10000, /* dereference domains and perhaps produce extra data */
adns__qtf_mail822 = 0x20000, /* make mailboxes be in RFC822 rcpt field format */
adns_r_none = 0,
adns_r_a = 1,
adns_r_ns_raw = 2,
adns_r_ns = adns_r_ns_raw | adns__qtf_deref,
adns_r_cname = 5,
adns_r_soa_raw = 6,
adns_r_soa = adns_r_soa_raw | adns__qtf_mail822,
adns_r_ptr_raw = 12,
adns_r_ptr = adns_r_ptr_raw | adns__qtf_deref,
adns_r_hinfo = 13,
adns_r_mx_raw = 15,
adns_r_mx = adns_r_mx_raw | adns__qtf_deref,
adns_r_txt = 16,
adns_r_rp_raw = 17,
adns_r_rp = adns_r_rp_raw | adns__qtf_mail822,
adns_r_addr = adns_r_a | adns__qtf_deref
} adns_rrtype;
/*
* In queries without qf_quoteok_*, all domains must have standard
* legal syntax, or you get adns_s_querydomainvalid (if the query
* domain contains bad characters) or adns_s_answerdomaininvalid (if
* the answer contains bad characters).
*
* In queries _with_ qf_quoteok_*, domains in the query or response
* may contain any characters, quoted according to RFC1035 5.1. On
* input to adns, the char* is a pointer to the interior of a "
* delimited string, except that " may appear in it unquoted. On
* output, the char* is a pointer to a string which would be legal
* either inside or outside " delimiters; any character which isn't
* legal in a hostname (ie alphanumeric or hyphen) or one of _ / +
* (the three other punctuation characters commonly abused in domain
* names) will be quoted, as \X if it is a printing ASCII character or
* \DDD otherwise.
*
* If the query goes via a CNAME then the canonical name (ie, the
* thing that the CNAME record refers to) is usually allowed to
* contain any characters, which will be quoted as above. With
* adns_qf_quotefail_cname you get adns_s_answerdomaininvalid when
* this happens. (This is a change from version 0.4 and earlier, in
* which failing the query was the default, and you had to say
* adns_qf_quoteok_cname to avoid this; that flag is now deprecated.)
*
* In version 0.4 and earlier, asking for _raw records containing
* mailboxes without specifying _qf_quoteok_anshost was silly. This
* is no longer the case. In this version only parts of responses
* that are actually supposed to be hostnames will be refused by
* default if quote-requiring characters are found.
*/
/*
* If you ask for an RR which contains domains which are actually
* encoded mailboxes, and don't ask for the _raw version, then adns
* returns the mailbox formatted suitably for an RFC822 recipient
* header field. The particular format used is that if the mailbox
* requires quoting according to the rules in RFC822 then the
* local-part is quoted in double quotes, which end at the next
* unescaped double quote (\ is the escape char, and is doubled, and
* is used to escape only \ and "). If the local-part is legal
* without quoting according to RFC822, it is presented as-is. In any
* case the local-part is followed by an @ and the domain. The domain
* will not contain any characters not legal in hostnames.
*
* Unquoted local-parts may contain any printing 7-bit ASCII
* except the punctuation characters ( ) < > @ , ; : \ " [ ]
* I.e. they may contain alphanumerics, and the following
* punctuation characters: ! # % ^ & * - _ = + { } .
*
* adns will reject local parts containing control characters (byte
* values 0-31, 127-159, and 255) - these appear to be legal according
* to RFC822 (at least 0-127) but are clearly a bad idea. RFC1035
* syntax does not make any distinction between a single RFC822
* quoted-string containing full stops, and a series of quoted-strings
* separated by full stops; adns will return anything that isn't all
* valid atoms as a single quoted-string. RFC822 does not allow
* high-bit-set characters at all, but adns does allow them in
* local-parts, treating them as needing quoting.
*
* If you ask for the domain with _raw then _no_ checking is done
* (even on the host part, regardless of adns_qf_quoteok_anshost), and
* you just get the domain name in master file format.
*
* If no mailbox is supplied the returned string will be `.' in either
* case.
*/
typedef enum {
adns_s_ok,
/* locally induced errors */
adns_s_nomemory,
adns_s_unknownrrtype,
adns_s_systemfail,
adns_s_max_localfail = 29,
/* remotely induced errors, detected locally */
adns_s_timeout,
adns_s_allservfail,
adns_s_norecurse,
adns_s_invalidresponse,
adns_s_unknownformat,
adns_s_max_remotefail = 59,
/* remotely induced errors, reported by remote server to us */
adns_s_rcodeservfail,
adns_s_rcodeformaterror,
adns_s_rcodenotimplemented,
adns_s_rcoderefused,
adns_s_rcodeunknown,
adns_s_max_tempfail = 99,
/* remote configuration errors */
adns_s_inconsistent, /* PTR gives domain whose A does not exist and match */
adns_s_prohibitedcname, /* CNAME found where eg A expected (not if _qf_loosecname) */
adns_s_answerdomaininvalid,
adns_s_answerdomaintoolong,
adns_s_invaliddata,
adns_s_max_misconfig = 199,
/* permanent problems with the query */
adns_s_querydomainwrong,
adns_s_querydomaininvalid,
adns_s_querydomaintoolong,
adns_s_max_misquery = 299,
/* permanent errors */
adns_s_nxdomain,
adns_s_nodata,
adns_s_max_permfail = 499
} adns_status;
typedef struct {
int len;
union {
struct sockaddr sa;
struct sockaddr_in inet;
} addr;
} adns_rr_addr;
typedef struct {
char *host;
adns_status astatus;
int naddrs; /* temp fail => -1, perm fail => 0, s_ok => >0 */
adns_rr_addr *addrs;
} adns_rr_hostaddr;
typedef struct {
char *(array[2]);
} adns_rr_strpair;
typedef struct {
int i;
adns_rr_hostaddr ha;
} adns_rr_inthostaddr;
typedef struct {
/* Used both for mx_raw, in which case i is the preference and str the domain,
* and for txt, in which case each entry has i for the `text' length,
* and str for the data (which will have had an extra nul appended
* so that if it was plain text it is now a null-terminated string).
*/
int i;
char *str;
} adns_rr_intstr;
typedef struct {
adns_rr_intstr array[2];
} adns_rr_intstrpair;
typedef struct {
char *mname, *rname;
unsigned long serial, refresh, retry, expire, minimum;
} adns_rr_soa;
typedef struct {
adns_status status;
char *cname; /* always NULL if query was for CNAME records */
char *owner; /* only set if requested in query flags, and may be 0 on error anyway */
adns_rrtype type; /* guaranteed to be same as in query */
time_t expires; /* expiry time, defined only if _s_ok, nxdomain or nodata. NOT TTL! */
int nrrs, rrsz; /* nrrs is 0 if an error occurs */
union {
void *untyped;
unsigned char *bytes;
char *(*str); /* ns_raw, cname, ptr, ptr_raw */
adns_rr_intstr *(*manyistr); /* txt (list of strings ends with i=-1, str=0) */
adns_rr_addr *addr; /* addr */
struct in_addr *inaddr; /* a */
adns_rr_hostaddr *hostaddr; /* ns */
adns_rr_intstrpair *intstrpair; /* hinfo */
adns_rr_strpair *strpair; /* rp, rp_raw */
adns_rr_inthostaddr *inthostaddr; /* mx */
adns_rr_intstr *intstr; /* mx_raw */
adns_rr_soa *soa; /* soa, soa_raw */
} rrs;
} adns_answer;
/* Memory management:
* adns_state and adns_query are actually pointers to malloc'd state;
* On submission questions are copied, including the owner domain;
* Answers are malloc'd as a single piece of memory; pointers in the
* answer struct point into further memory in the answer.
* query_io:
* Must always be non-null pointer;
* If *query_io is 0 to start with then any query may be returned;
* If *query_io is !0 adns_query then only that query may be returned.
* If the call is successful, *query_io, *answer_r, and *context_r
* will all be set.
* Errors:
* Return values are 0 or an errno value.
*
* For _init, _init_strcfg, _submit and _synchronous, system errors
* (eg, failure to create sockets, malloc failure, etc.) return errno
* values.
*
* For _wait and _check failures are reported in the answer
* structure, and only 0, ESRCH or (for _check) EAGAIN is
* returned: if no (appropriate) requests are done adns_check returns
* EAGAIN; if no (appropriate) requests are outstanding both
* adns_query and adns_wait return ESRCH.
*
* Additionally, _wait can return EINTR if you set adns_if_eintr.
*
* All other errors (nameserver failure, timed out connections, &c)
* are returned in the status field of the answer. After a
* successful _wait or _check, if status is nonzero then nrrs will be
* 0, otherwise it will be >0. type will always be the type
* requested.
*/
int adns_init(adns_state * newstate_r, adns_initflags flags,
FILE * diagfile /*0=>stderr */ );
int adns_init_strcfg(adns_state * newstate_r, adns_initflags flags,
FILE * diagfile /*0=>discard */ ,
const char *configtext);
/* Configuration:
* adns_init reads /etc/resolv.conf, which is expected to be (broadly
* speaking) in the format expected by libresolv, and then
* /etc/resolv-adns.conf if it exists. adns_init_strcfg is instead
* passed a string which is interpreted as if it were the contents of
* resolv.conf or resolv-adns.conf. In general, configuration which
* is set later overrides any that is set earlier.
*
* Standard directives understood in resolv[-adns].conf:
*
* nameserver <address>
* Must be followed by the IP address of a nameserver. Several
* nameservers may be specified, and they will be tried in the order
* found. There is a compiled in limit, currently 5, on the number
* of nameservers. (libresolv supports only 3 nameservers.)
*
* search <domain> ...
* Specifies the search list for queries which specify
* adns_qf_search. This is a list of domains to append to the query
* domain. The query domain will be tried as-is either before all
* of these or after them, depending on the ndots option setting
* (see below).
*
* domain <domain>
* This is present only for backward compatibility with obsolete
* versions of libresolv. It should not be used, and is interpreted
* by adns as if it were `search' - note that this is subtly
* different to libresolv's interpretation of this directive.
*
* sortlist <addr>/<mask> ...
* Should be followed by a sequence of IP-address and netmask pairs,
* separated by spaces. They may be specified as
* eg. 172.30.206.0/24 or 172.30.206.0/255.255.255.0. Currently up
* to 15 pairs may be specified (but note that libresolv only
* supports up to 10).
*
* options
* Should followed by one or more options, separated by spaces.
* Each option consists of an option name, followed by optionally
* a colon and a value. Options are listed below.
*
* Non-standard directives understood in resolv[-adns].conf:
*
* clearnameservers
* Clears the list of nameservers, so that further nameserver lines
* start again from the beginning.
*
* include <filename>
* The specified file will be read.
*
* Additionally, adns will ignore lines in resolv[-adns].conf which
* start with a #.
*
* Standard options understood:
*
* debug
* Enables debugging output from the resolver, which will be written
* to stderr.
*
* ndots:<count>
* Affects whether queries with adns_qf_search will be tried first
* without adding domains from the searchlist, or whether the bare
* query domain will be tried last. Queries which contain at least
* <count> dots will be tried bare first. The default is 1.
*
* Non-standard options understood:
*
* adns_checkc:none
* adns_checkc:entex
* adns_checkc:freq
* Changes the consistency checking frequency; this overrides the
* setting of adns_if_check_entex, adns_if_check_freq, or neither,
* in the flags passed to adns_init.
*
* There are a number of environment variables which can modify the
* behaviour of adns. They take effect only if adns_init is used, and
* the caller of adns_init can disable them using adns_if_noenv. In
* each case there is both a FOO and an ADNS_FOO; the latter is
* interpreted later so that it can override the former. Unless
* otherwise stated, environment variables are interpreted after
* resolv[-adns].conf are read, in the order they are listed here.
*
* RES_CONF, ADNS_RES_CONF
* A filename, whose contets are in the format of resolv.conf.
*
* RES_CONF_TEXT, ADNS_RES_CONF_TEXT
* A string in the format of resolv.conf.
*
* RES_OPTIONS, ADNS_RES_OPTIONS
* These are parsed as if they appeared in the `options' line of a
* resolv.conf. In addition to being parsed at this point in the
* sequence, they are also parsed at the very beginning before
* resolv.conf or any other environment variables are read, so that
* any debug option can affect the processing of the configuration.
*
* LOCALDOMAIN, ADNS_LOCALDOMAIN
* These are interpreted as if their contents appeared in a `search'
* line in resolv.conf.
*/
int adns_synchronous(adns_state ads,
const char *owner,
adns_rrtype type,
adns_queryflags flags,
adns_answer ** answer_r);
/* NB: if you set adns_if_noautosys then _submit and _check do not
* make any system calls; you must use some of the asynch-io event
* processing functions to actually get things to happen.
*/
int adns_submit(adns_state ads,
const char *owner,
adns_rrtype type,
adns_queryflags flags,
void *context, adns_query * query_r);
/* The owner should be quoted in master file format. */
int adns_check(adns_state ads,
adns_query * query_io,
adns_answer ** answer_r, void **context_r);
int adns_wait(adns_state ads,
adns_query * query_io,
adns_answer ** answer_r, void **context_r);
/* same as adns_wait but uses poll(2) internally */
int adns_wait_poll(adns_state ads,
adns_query * query_io,
adns_answer ** answer_r, void **context_r);
void adns_cancel(adns_query query);
/* The adns_query you get back from _submit is valid (ie, can be
* legitimately passed into adns functions) until it is returned by
* adns_check or adns_wait, or passed to adns_cancel. After that it
* must not be used. You can rely on it not being reused until the
* first adns_submit or _transact call using the same adns_state after
* it became invalid, so you may compare it for equality with other
* query handles until you next call _query or _transact.
*
* _submit and _synchronous return ENOSYS if they don't understand the
* query type.
*/
int adns_submit_reverse(adns_state ads,
const struct sockaddr *addr,
adns_rrtype type,
adns_queryflags flags,
void *context, adns_query * query_r);
/* type must be _r_ptr or _r_ptr_raw. _qf_search is ignored.
* addr->sa_family must be AF_INET or you get ENOSYS.
*/
int adns_submit_reverse_any(adns_state ads,
const struct sockaddr *addr,
const char *rzone,
adns_rrtype type,
adns_queryflags flags,
void *context, adns_query * query_r);
/* For RBL-style reverse `zone's; look up
* <reversed-address>.<zone>
* Any type is allowed. _qf_search is ignored.
* addr->sa_family must be AF_INET or you get ENOSYS.
*/
void adns_finish(adns_state ads);
/* You may call this even if you have queries outstanding;
* they will be cancelled.
*/
void adns_forallqueries_begin(adns_state ads);
adns_query adns_forallqueries_next(adns_state ads,
void **context_r);
/* Iterator functions, which you can use to loop over the outstanding
* (submitted but not yet successfuly checked/waited) queries.
*
* You can only have one iteration going at once. You may call _begin
* at any time; after that, an iteration will be in progress. You may
* only call _next when an iteration is in progress - anything else
* may coredump. The iteration remains in progress until _next
* returns 0, indicating that all the queries have been walked over,
* or ANY other adns function is called with the same adns_state (or a
* query in the same adns_state). There is no need to explicitly
* finish an iteration.
*
* context_r may be 0. *context_r may not be set when _next returns 0.
*/
void adns_checkconsistency(adns_state ads, adns_query qu);
/* Checks the consistency of adns's internal data structures.
* If any error is found, the program will abort().
* You may pass 0 for qu; if you pass non-null then additional checks
* are done to make sure that qu is a valid query.
*/
/*
* Example expected/legal calling sequence for submit/check/wait:
* adns_init
* adns_submit 1
* adns_submit 2
* adns_submit 3
* adns_wait 1
* adns_check 3 -> EAGAIN
* adns_wait 2
* adns_wait 3
* ....
* adns_finish
*/
/*
* Entrypoints for generic asynch io:
* (these entrypoints are not very useful except in combination with *
* some of the other I/O model calls which can tell you which fds to
* be interested in):
*
* Note that any adns call may cause adns to open and close fds, so
* you must call beforeselect or beforepoll again just before
* blocking, or you may not have an up-to-date list of it's fds.
*/
int adns_processany(adns_state ads);
/* Gives adns flow-of-control for a bit. This will never block, and
* can be used with any threading/asynch-io model. If some error
* occurred which might cause an event loop to spin then the errno
* value is returned.
*/
int adns_processreadable(adns_state ads, int fd,
const struct timeval *now);
int adns_processwriteable(adns_state ads, int fd,
const struct timeval *now);
int adns_processexceptional(adns_state ads, int fd,
const struct timeval *now);
/* Gives adns flow-of-control so that it can process incoming data
* from, or send outgoing data via, fd. Very like _processany. If it
* returns zero then fd will no longer be readable or writeable
* (unless of course more data has arrived since). adns will _only_
* use that fd and only in the manner specified, regardless of whether
* adns_if_noautosys was specified.
*
* adns_processexceptional should be called when select(2) reports an
* exceptional condition, or poll(2) reports POLLPRI.
*
* It is fine to call _processreabable or _processwriteable when the
* fd is not ready, or with an fd that doesn't belong to adns; it will
* then just return 0.
*
* If some error occurred which might prevent an event loop to spin
* then the errno value is returned.
*/
void adns_processtimeouts(adns_state ads,
const struct timeval *now);
/* Gives adns flow-of-control so that it can process any timeouts
* which might have happened. Very like _processreadable/writeable.
*
* now may be 0; if it isn't, *now must be the current time, recently
* obtained from gettimeofday.
*/
void adns_firsttimeout(adns_state ads,
struct timeval **tv_mod,
struct timeval *tv_buf, struct timeval now);
/* Asks adns when it would first like the opportunity to time
* something out. now must be the current time, from gettimeofday.
*
* If tv_mod points to 0 then tv_buf must be non-null, and
* _firsttimeout will fill in *tv_buf with the time until the first
* timeout, and make *tv_mod point to tv_buf. If adns doesn't have
* anything that might need timing out it will leave *tv_mod as 0.
*
* If *tv_mod is not 0 then tv_buf is not used. adns will update
* *tv_mod if it has any earlier timeout, and leave it alone if it
* doesn't.
*
* This call will not actually do any I/O, or change the fds that adns
* is using. It always succeeds and never blocks.
*/
void adns_globalsystemfailure(adns_state ads);
/* If serious problem(s) happen which globally affect your ability to
* interact properly with adns, or adns's ability to function
* properly, you or adns can call this function.
*
* All currently outstanding queries will be made to fail with
* adns_s_systemfail, and adns will close any stream sockets it has
* open.
*
* This is used by adns, for example, if gettimeofday() fails.
* Without this the program's event loop might start to spin !
*
* This call will never block.
*/
/*
* Entrypoints for select-loop based asynch io:
*/
void adns_beforeselect(adns_state ads, int *maxfd,
fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval **tv_mod,
struct timeval *tv_buf,
const struct timeval *now);
/* Find out file descriptors adns is interested in, and when it would
* like the opportunity to time something out. If you do not plan to
* block then tv_mod may be 0. Otherwise, tv_mod and tv_buf are as
* for adns_firsttimeout. readfds, writefds, exceptfds and maxfd_io may
* not be 0.
*
* If now is not 0 then this will never actually do any I/O, or change
* the fds that adns is using or the timeouts it wants. In any case
* it won't block, and it will set the timeout to zero if a query
* finishes in _beforeselect.
*/
void adns_afterselect(adns_state ads, int maxfd,
const fd_set * readfds,
const fd_set * writefds,
const fd_set * exceptfds,
const struct timeval *now);
/* Gives adns flow-of-control for a bit; intended for use after
* select. This is just a fancy way of calling adns_processreadable/
* writeable/timeouts as appropriate, as if select had returned the
* data being passed. Always succeeds.
*/
/*
* Example calling sequence:
*
* adns_init _noautosys
* loop {
* adns_beforeselect
* select
* adns_afterselect
* ...
* adns_submit / adns_check
* ...
* }
*/
/*
* Entrypoints for poll-loop based asynch io:
*/
struct pollfd;
/* In case your system doesn't have it or you forgot to include
* <sys/poll.h>, to stop the following declarations from causing
* problems. If your system doesn't have poll then the following
* entrypoints will not be defined in libadns. Sorry !
*/
int adns_beforepoll(adns_state ads, struct pollfd *fds,
int *nfds_io, int *timeout_io,
const struct timeval *now);
/* Finds out which fd's adns is interested in, and when it would like
* to be able to time things out. This is in a form suitable for use
* with poll(2).
*
* On entry, usually fds should point to at least *nfds_io structs.
* adns will fill up to that many structs will information for poll,
* and record in *nfds_io how many structs it filled. If it wants to
* listen for more structs then *nfds_io will be set to the number
* required and _beforepoll will return ERANGE.
*
* You may call _beforepoll with fds==0 and *nfds_io 0, in which case
* adns will fill in the number of fds that it might be interested in
* in *nfds_io, and always return either 0 (if it is not interested in
* any fds) or ERANGE (if it is).
*
* NOTE that (unless now is 0) adns may acquire additional fds
* from one call to the next, so you must put adns_beforepoll in a
* loop, rather than assuming that the second call (with the buffer
* size requested by the first) will not return ERANGE.
*
* adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd
* structs, and only ever looks at those bits. POLLPRI is required to
* detect TCP Urgent Data (which should not be used by a DNS server)
* so that adns can know that the TCP stream is now useless.
*
* In any case, *timeout_io should be a timeout value as for poll(2),
* which adns will modify downwards as required. If the caller does
* not plan to block then *timeout_io should be 0 on entry, or
* alternatively, timeout_io may be 0. (Alternatively, the caller may
* use _beforeselect with timeout_io==0 to find out about file
* descriptors, and use _firsttimeout is used to find out when adns
* might want to time something out.)
*
* adns_beforepoll will return 0 on success, and will not fail for any
* reason other than the fds buffer being too small (ERANGE).
*
* This call will never actually do any I/O. If you supply the
* current time it will not change the fds that adns is using or the
* timeouts it wants.
*
* In any case this call won't block.
*/
#define ADNS_POLLFDS_RECOMMENDED 2
/* If you allocate an fds buf with at least RECOMMENDED entries then
* you are unlikely to need to enlarge it. You are recommended to do
* so if it's convenient. However, you must be prepared for adns to
* require more space than this.
*/
void adns_afterpoll(adns_state ads, const struct pollfd *fds,
int nfds, const struct timeval *now);
/* Gives adns flow-of-control for a bit; intended for use after
* poll(2). fds and nfds should be the results from poll(). pollfd
* structs mentioning fds not belonging to adns will be ignored.
*/
adns_status adns_rr_info(adns_rrtype type,
const char **rrtname_r,
const char **fmtname_r, int *len_r,
const void *datap, char **data_r);
/*
* Get information about a query type, or convert reply data to a
* textual form. type must be specified, and the official name of the
* corresponding RR type will be returned in *rrtname_r, and
* information about the processing style in *fmtname_r. The length
* of the table entry in an answer for that type will be returned in
* in *len_r. Any or all of rrtname_r, fmtname_r and len_r may be 0.
* If fmtname_r is non-null then *fmtname_r may be null on return,
* indicating that no special processing is involved.
*
* data_r be must be non-null iff datap is. In this case *data_r will
* be set to point to a string pointing to a representation of the RR
* data in master file format. (The owner name, timeout, class and
* type will not be present - only the data part of the RR.) The
* memory will have been obtained from malloc() and must be freed by
* the caller.
*
* Usually this routine will succeed. Possible errors include:
* adns_s_nomemory
* adns_s_rrtypeunknown
* adns_s_invaliddata (*datap contained garbage)
* If an error occurs then no memory has been allocated,
* and *rrtname_r, *fmtname_r, *len_r and *data_r are undefined.
*
* There are some adns-invented data formats which are not official
* master file formats. These include:
*
* Mailboxes if __qtf_mail822: these are just included as-is.
*
* Addresses (adns_rr_addr): these may be of pretty much any type.
* The representation is in two parts: first, a word for the address
* family (ie, in AF_XXX, the XXX), and then one or more items for the
* address itself, depending on the format. For an IPv4 address the
* syntax is INET followed by the dotted quad (from inet_ntoa).
* Currently only IPv4 is supported.
*
* Text strings (as in adns_rr_txt) appear inside double quotes, and
* use \" and \\ to represent " and \, and \xHH to represent
* characters not in the range 32-126.
*
* Hostname with addresses (adns_rr_hostaddr): this consists of the
* hostname, as usual, followed by the adns_status value, as an
* abbreviation, and then a descriptive string (encoded as if it were
* a piece of text), for the address lookup, followed by zero or more
* addresses enclosed in ( and ). If the result was a temporary
* failure, then a single ? appears instead of the ( ). If the
* result was a permanent failure then an empty pair of parentheses
* appears (which a space in between). For example, one of the NS
* records for greenend.org.uk comes out like
* ns.chiark.greenend.org.uk ok "OK" ( INET 195.224.76.132 )
* an MX referring to a nonexistent host might come out like:
* 50 sun2.nsfnet-relay.ac.uk nxdomain "No such domain" ( )
* and if nameserver information is not available you might get:
* dns2.spong.dyn.ml.org timeout "DNS query timed out" ?
*/
const char *adns_strerror(adns_status st);
const char *adns_errabbrev(adns_status st);
const char *adns_errtypeabbrev(adns_status st);
/* Like strerror but for adns_status values. adns_errabbrev returns
* the abbreviation of the error - eg, for adns_s_timeout it returns
* "timeout". adns_errtypeabbrev returns the abbreviation of the
* error class: ie, for values up to adns_s_max_XXX it will return the
* string XXX. You MUST NOT call these functions with status values
* not returned by the same adns library.
*/
#ifdef __cplusplus
} /* end of extern "C" */
#endif
#endif

231
adns/check.c Normal file
View file

@ -0,0 +1,231 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* check.c
* - consistency checks
*/
/*
* This file is
* Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "internal.h"
#include <stdlib.h>
void adns_checkconsistency(adns_state ads, adns_query qu)
{
adns__consistency(ads, qu, cc_user);
}
#define DLIST_CHECK(list, nodevar, part, body) \
if ((list).head) { \
assert(! (list).head->part back); \
for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->part next) { \
assert((nodevar)->part next \
? (nodevar) == (nodevar)->part next->part back \
: (nodevar) == (list).tail); \
body \
} \
}
#define DLIST_ASSERTON(node, nodevar, list, part) \
do { \
for ((nodevar)= (list).head; \
(nodevar) != (node); \
(nodevar)= (nodevar)->part next) { \
assert((nodevar)); \
} \
} while(0)
static void checkc_query_alloc(adns_state ads, adns_query qu)
{
allocnode *an;
DLIST_CHECK(qu->allocations, an,, {
});
}
static void checkc_query(adns_state ads, adns_query qu)
{
adns_query child;
assert(qu->udpnextserver < ads->nservers);
assert(!(qu->udpsent & (~0UL << ads->nservers)));
assert(qu->search_pos <= ads->nsearchlist);
if (qu->parent)
DLIST_ASSERTON(qu, child, qu->parent->children, siblings.);
}
static void checkc_notcpbuf(adns_state ads)
{
assert(!ads->tcpsend.used);
assert(!ads->tcprecv.used);
assert(!ads->tcprecv_skip);
}
static void checkc_global(adns_state ads)
{
int i;
assert(ads->udpsocket >= 0);
for (i = 0; i < ads->nsortlist; i++)
assert(!
(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].
mask.s_addr));
assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers);
switch (ads->tcpstate) {
case server_connecting:
assert(ads->tcpsocket >= 0);
checkc_notcpbuf(ads);
break;
case server_disconnected:
case server_broken:
assert(ads->tcpsocket == -1);
checkc_notcpbuf(ads);
break;
case server_ok:
assert(ads->tcpsocket >= 0);
assert(ads->tcprecv_skip <= ads->tcprecv.used);
break;
default:
assert(!"ads->tcpstate value");
}
assert(ads->searchlist || !ads->nsearchlist);
}
static void checkc_queue_udpw(adns_state ads)
{
adns_query qu;
DLIST_CHECK(ads->udpw, qu,, {
assert(qu->state == query_tosend);
assert(qu->retries <= UDPMAXRETRIES);
assert(qu->udpsent);
assert(!qu->children.head && !qu->children.tail);
checkc_query(ads, qu); checkc_query_alloc(ads, qu);});
}
static void checkc_queue_tcpw(adns_state ads)
{
adns_query qu;
DLIST_CHECK(ads->tcpw, qu,, {
assert(qu->state == query_tcpw);
assert(!qu->children.head && !qu->children.tail);
assert(qu->retries <= ads->nservers + 1);
checkc_query(ads, qu); checkc_query_alloc(ads, qu);});
}
static void checkc_queue_childw(adns_state ads)
{
adns_query parent, child;
DLIST_CHECK(ads->childw, parent,, {
assert(parent->state == query_childw);
assert(parent->children.head);
DLIST_CHECK(parent->children, child, siblings., {
assert(child->parent == parent);
assert(child->state != query_done);});
checkc_query(ads, parent);
checkc_query_alloc(ads, parent);});
}
static void checkc_queue_output(adns_state ads)
{
adns_query qu;
DLIST_CHECK(ads->output, qu,, {
assert(qu->state == query_done);
assert(!qu->children.head && !qu->children.tail);
assert(!qu->parent);
assert(!qu->allocations.head && !qu->allocations.tail);
checkc_query(ads, qu);});
}
void adns__consistency(adns_state ads, adns_query qu,
consistency_checks cc)
{
adns_query search;
switch (cc) {
case cc_user:
break;
case cc_entex:
if (!(ads->iflags & adns_if_checkc_entex))
return;
break;
case cc_freq:
if ((ads->iflags & adns_if_checkc_freq) !=
adns_if_checkc_freq)
return;
break;
default:
abort();
}
checkc_global(ads);
checkc_queue_udpw(ads);
checkc_queue_tcpw(ads);
checkc_queue_childw(ads);
checkc_queue_output(ads);
if (qu) {
switch (qu->state) {
case query_tosend:
DLIST_ASSERTON(qu, search, ads->udpw,);
break;
case query_tcpw:
DLIST_ASSERTON(qu, search, ads->tcpw,);
break;
case query_childw:
DLIST_ASSERTON(qu, search, ads->childw,);
break;
case query_done:
DLIST_ASSERTON(qu, search, ads->output,);
break;
default:
assert(!"specific query state");
}
}
}

106
adns/config.h Normal file
View file

@ -0,0 +1,106 @@
/* adns/config.h. Generated by configure. */
/* src/config.h.in. Generated automatically from configure.in by autoheader. */
/* Define if inline functions a la GCC are available. */
#define HAVE_INLINE 1
/* Define if function attributes a la GCC 2.5 and higher are available. */
#define HAVE_GNUC25_ATTRIB 1
/* Define if constant functions a la GCC 2.5 and higher are available. */
#define HAVE_GNUC25_CONST 1
/* Define if nonreturning functions a la GCC 2.5 and higher are available. */
#define HAVE_GNUC25_NORETURN 1
/* Define if printf-format argument lists a la GCC are available. */
#define HAVE_GNUC25_PRINTFFORMAT 1
/* Define if we want to include rpc/types.h. Crap BSDs put INADDR_LOOPBACK there. */
/* #undef HAVEUSE_RPCTYPES_H */
/* Define if you have the poll function. */
/* #undef HAVE_POLL */
/* Define if you have the <sys/select.h> header file. */
#define HAVE_SYS_SELECT_H 1
/* Define if you have the nsl library (-lnsl). */
/* #undef HAVE_LIBNSL */
/* Define if you have the socket library (-lsocket). */
/* #undef HAVE_LIBSOCKET */
/* Use the definitions: */
#ifndef HAVE_INLINE
#define inline
#endif
#ifdef HAVE_POLL
#include <sys/poll.h>
#else
/* kludge it up */
struct pollfd { int fd; short events; short revents; };
#define POLLIN 1
#define POLLPRI 2
#define POLLOUT 4
#endif
/* GNU C attributes. */
#ifndef FUNCATTR
#ifdef HAVE_GNUC25_ATTRIB
#define FUNCATTR(x) __attribute__(x)
#else
#define FUNCATTR(x)
#endif
#endif
/* GNU C printf formats, or null. */
#ifndef ATTRPRINTF
#ifdef HAVE_GNUC25_PRINTFFORMAT
#define ATTRPRINTF(si,tc) format(printf,si,tc)
#else
#define ATTRPRINTF(si,tc)
#endif
#endif
#ifndef PRINTFFORMAT
#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc)))
#endif
/* GNU C nonreturning functions, or null. */
#ifndef ATTRNORETURN
#ifdef HAVE_GNUC25_NORETURN
#define ATTRNORETURN noreturn
#else
#define ATTRNORETURN
#endif
#endif
#ifndef NONRETURNING
#define NONRETURNING FUNCATTR((ATTRNORETURN))
#endif
/* Combination of both the above. */
#ifndef NONRETURNPRINTFFORMAT
#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN))
#endif
/* GNU C constant functions, or null. */
#ifndef ATTRCONST
#ifdef HAVE_GNUC25_CONST
#define ATTRCONST const
#else
#define ATTRCONST
#endif
#endif
#ifndef CONSTANT
#define CONSTANT FUNCATTR((ATTRCONST))
#endif
#ifdef HAVEUSE_RPCTYPES_H
#include <rpc/types.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

105
adns/config.h.in Normal file
View file

@ -0,0 +1,105 @@
/* src/config.h.in. Generated automatically from configure.in by autoheader. */
/* Define if inline functions a la GCC are available. */
#undef HAVE_INLINE
/* Define if function attributes a la GCC 2.5 and higher are available. */
#undef HAVE_GNUC25_ATTRIB
/* Define if constant functions a la GCC 2.5 and higher are available. */
#undef HAVE_GNUC25_CONST
/* Define if nonreturning functions a la GCC 2.5 and higher are available. */
#undef HAVE_GNUC25_NORETURN
/* Define if printf-format argument lists a la GCC are available. */
#undef HAVE_GNUC25_PRINTFFORMAT
/* Define if we want to include rpc/types.h. Crap BSDs put INADDR_LOOPBACK there. */
#undef HAVEUSE_RPCTYPES_H
/* Define if you have the poll function. */
#undef HAVE_POLL
/* Define if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define if you have the nsl library (-lnsl). */
#undef HAVE_LIBNSL
/* Define if you have the socket library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Use the definitions: */
#ifndef HAVE_INLINE
#define inline
#endif
#ifdef HAVE_POLL
#include <sys/poll.h>
#else
/* kludge it up */
struct pollfd { int fd; short events; short revents; };
#define POLLIN 1
#define POLLPRI 2
#define POLLOUT 4
#endif
/* GNU C attributes. */
#ifndef FUNCATTR
#ifdef HAVE_GNUC25_ATTRIB
#define FUNCATTR(x) __attribute__(x)
#else
#define FUNCATTR(x)
#endif
#endif
/* GNU C printf formats, or null. */
#ifndef ATTRPRINTF
#ifdef HAVE_GNUC25_PRINTFFORMAT
#define ATTRPRINTF(si,tc) format(printf,si,tc)
#else
#define ATTRPRINTF(si,tc)
#endif
#endif
#ifndef PRINTFFORMAT
#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc)))
#endif
/* GNU C nonreturning functions, or null. */
#ifndef ATTRNORETURN
#ifdef HAVE_GNUC25_NORETURN
#define ATTRNORETURN noreturn
#else
#define ATTRNORETURN
#endif
#endif
#ifndef NONRETURNING
#define NONRETURNING FUNCATTR((ATTRNORETURN))
#endif
/* Combination of both the above. */
#ifndef NONRETURNPRINTFFORMAT
#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN))
#endif
/* GNU C constant functions, or null. */
#ifndef ATTRCONST
#ifdef HAVE_GNUC25_CONST
#define ATTRCONST const
#else
#define ATTRCONST
#endif
#endif
#ifndef CONSTANT
#define CONSTANT FUNCATTR((ATTRCONST))
#endif
#ifdef HAVEUSE_RPCTYPES_H
#include <rpc/types.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

75
adns/dlist.h Normal file
View file

@ -0,0 +1,75 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* dlist.h
* - macros for handling doubly linked lists
*/
/*
* This file is
* Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ADNS_DLIST_H_INCLUDED
#define ADNS_DLIST_H_INCLUDED
#define ALIST_INIT(list) ((list).head= (list).tail= 0)
#define ALINK_INIT(link) ((link).next= (link).back= 0)
#define LIST_UNLINK_PART(list,node,part) \
do { \
if ((node)->part back) (node)->part back->part next= (node)->part next; \
else (list).head= (node)->part next; \
if ((node)->part next) (node)->part next->part back= (node)->part back; \
else (list).tail= (node)->part back; \
} while(0)
#define LIST_LINK_TAIL_PART(list,node,part) \
do { \
(node)->part next= 0; \
(node)->part back= (list).tail; \
if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \
(list).tail= (node); \
} while(0)
#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
#define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,)
#endif

938
adns/event.c Normal file
View file

@ -0,0 +1,938 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* event.c
* - event loop core
* - TCP connection management
* - user-visible check/wait and event-loop-related functions
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "internal.h"
#include "tvarith.h"
/* TCP connection management. */
static void tcp_close(adns_state ads)
{
int serv;
serv = ads->tcpserver;
close(ads->tcpsocket);
ads->tcpsocket = -1;
ads->tcprecv.used = ads->tcprecv_skip = ads->tcpsend.used = 0;
}
void adns__tcp_broken(adns_state ads, const char *what, const char *why)
{
int serv;
adns_query qu;
assert(ads->tcpstate == server_connecting
|| ads->tcpstate == server_ok);
serv = ads->tcpserver;
if (what)
adns__warn(ads, serv, 0, "TCP connection failed: %s: %s",
what, why);
if (ads->tcpstate == server_connecting) {
/* Counts as a retry for all the queries waiting for TCP. */
for (qu = ads->tcpw.head; qu; qu = qu->next)
qu->retries++;
}
tcp_close(ads);
ads->tcpstate = server_broken;
ads->tcpserver = (serv + 1) % ads->nservers;
}
static void tcp_connected(adns_state ads, struct timeval now)
{
adns_query qu, nqu;
adns__debug(ads, ads->tcpserver, 0, "TCP connected");
ads->tcpstate = server_ok;
for (qu = ads->tcpw.head; qu && ads->tcpstate == server_ok;
qu = nqu) {
nqu = qu->next;
assert(qu->state == query_tcpw);
adns__querysend_tcp(qu, now);
}
}
void adns__tcp_tryconnect(adns_state ads, struct timeval now)
{
int r, fd, tries;
struct sockaddr_in addr;
struct protoent *proto;
for (tries = 0; tries < ads->nservers; tries++) {
switch (ads->tcpstate) {
case server_connecting:
case server_ok:
case server_broken:
return;
case server_disconnected:
break;
default:
abort();
}
assert(!ads->tcpsend.used);
assert(!ads->tcprecv.used);
assert(!ads->tcprecv_skip);
proto = getprotobyname("tcp");
if (!proto) {
adns__diag(ads, -1, 0,
"unable to find protocol no. for TCP !");
return;
}
fd = socket(AF_INET, SOCK_STREAM, proto->p_proto);
if (fd < 0) {
adns__diag(ads, -1, 0,
"cannot create TCP socket: %s",
strerror(errno));
return;
}
r = adns__setnonblock(ads, fd);
if (r) {
adns__diag(ads, -1, 0,
"cannot make TCP socket nonblocking: %s",
strerror(r));
close(fd);
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DNS_PORT);
addr.sin_addr = ads->servers[ads->tcpserver].addr;
r = connect(fd, (const struct sockaddr *) &addr,
sizeof(addr));
ads->tcpsocket = fd;
ads->tcpstate = server_connecting;
if (r == 0) {
tcp_connected(ads, now);
return;
}
if (errno == EWOULDBLOCK || errno == EINPROGRESS) {
ads->tcptimeout = now;
timevaladd(&ads->tcptimeout, TCPCONNMS);
return;
}
adns__tcp_broken(ads, "connect", strerror(errno));
ads->tcpstate = server_disconnected;
}
}
/* Timeout handling functions. */
void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
struct timeval *tv_buf)
{
const struct timeval *now;
int r;
now = *now_io;
if (now)
return;
r = gettimeofday(tv_buf, 0);
if (!r) {
*now_io = tv_buf;
return;
}
adns__diag(ads, -1, 0, "gettimeofday failed: %s", strerror(errno));
adns_globalsystemfailure(ads);
return;
}
static void inter_immed(struct timeval **tv_io, struct timeval *tvbuf)
{
struct timeval *rbuf;
if (!tv_io)
return;
rbuf = *tv_io;
if (!rbuf) {
*tv_io = rbuf = tvbuf;
}
timerclear(rbuf);
}
static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
struct timeval maxto)
{
struct timeval *rbuf;
if (!tv_io)
return;
rbuf = *tv_io;
if (!rbuf) {
*tvbuf = maxto;
*tv_io = tvbuf;
} else {
if (timercmp(rbuf, &maxto, >))
*rbuf = maxto;
}
/*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n",
maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/
}
static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now, struct timeval maxtime)
{
/* tv_io may be 0 */
ldiv_t dr;
/*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n",
now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/
if (!tv_io)
return;
maxtime.tv_sec -= (now.tv_sec + 2);
maxtime.tv_usec -= (now.tv_usec - 2000000);
dr = ldiv(maxtime.tv_usec, 1000000);
maxtime.tv_sec += dr.quot;
maxtime.tv_usec -= dr.quot * 1000000;
if (maxtime.tv_sec < 0)
timerclear(&maxtime);
inter_maxto(tv_io, tvbuf, maxtime);
}
static void timeouts_queue(adns_state ads, int act,
struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now, struct query_queue *queue)
{
adns_query qu, nqu;
for (qu = queue->head; qu; qu = nqu) {
nqu = qu->next;
if (!timercmp(&now, &qu->timeout, >)) {
inter_maxtoabs(tv_io, tvbuf, now, qu->timeout);
} else {
if (!act) {
inter_immed(tv_io, tvbuf);
return;
}
LIST_UNLINK(*queue, qu);
if (qu->state != query_tosend) {
adns__query_fail(qu, adns_s_timeout);
} else {
adns__query_send(qu, now);
}
nqu = queue->head;
}
}
}
static void tcp_events(adns_state ads, int act,
struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now)
{
adns_query qu, nqu;
for (;;) {
switch (ads->tcpstate) {
case server_broken:
if (!act) {
inter_immed(tv_io, tvbuf);
return;
}
for (qu = ads->tcpw.head; qu; qu = nqu) {
nqu = qu->next;
assert(qu->state == query_tcpw);
if (qu->retries > ads->nservers) {
LIST_UNLINK(ads->tcpw, qu);
adns__query_fail(qu,
adns_s_allservfail);
}
}
ads->tcpstate = server_disconnected;
case server_disconnected: /* fall through */
if (!ads->tcpw.head)
return;
if (!act) {
inter_immed(tv_io, tvbuf);
return;
}
adns__tcp_tryconnect(ads, now);
break;
case server_ok:
if (ads->tcpw.head)
return;
if (!ads->tcptimeout.tv_sec) {
assert(!ads->tcptimeout.tv_usec);
ads->tcptimeout = now;
timevaladd(&ads->tcptimeout, TCPIDLEMS);
}
case server_connecting: /* fall through */
if (!act || !timercmp(&now, &ads->tcptimeout, >)) {
inter_maxtoabs(tv_io, tvbuf, now,
ads->tcptimeout);
return;
}
{
/* TCP timeout has happened */
switch (ads->tcpstate) {
case server_connecting: /* failed to connect */
adns__tcp_broken(ads,
"unable to make connection",
"timed out");
break;
case server_ok: /* idle timeout */
tcp_close(ads);
ads->tcpstate =
server_disconnected;
return;
default:
abort();
}
}
break;
default:
abort();
}
}
return;
}
void adns__timeouts(adns_state ads, int act,
struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now)
{
timeouts_queue(ads, act, tv_io, tvbuf, now, &ads->udpw);
timeouts_queue(ads, act, tv_io, tvbuf, now, &ads->tcpw);
tcp_events(ads, act, tv_io, tvbuf, now);
}
void adns_firsttimeout(adns_state ads,
struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now)
{
adns__consistency(ads, 0, cc_entex);
adns__timeouts(ads, 0, tv_io, tvbuf, now);
adns__consistency(ads, 0, cc_entex);
}
void adns_processtimeouts(adns_state ads, const struct timeval *now)
{
struct timeval tv_buf;
adns__consistency(ads, 0, cc_entex);
adns__must_gettimeofday(ads, &now, &tv_buf);
if (now)
adns__timeouts(ads, 1, 0, 0, *now);
adns__consistency(ads, 0, cc_entex);
}
/* fd handling functions. These are the top-level of the real work of
* reception and often transmission.
*/
int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS])
{
/* Returns the number of entries filled in. Always zeroes revents. */
assert(MAX_POLLFDS == 2);
pollfds_buf[0].fd = ads->udpsocket;
pollfds_buf[0].events = POLLIN;
pollfds_buf[0].revents = 0;
switch (ads->tcpstate) {
case server_disconnected:
case server_broken:
return 1;
case server_connecting:
pollfds_buf[1].events = POLLOUT;
break;
case server_ok:
pollfds_buf[1].events =
ads->tcpsend.
used ? POLLIN | POLLOUT | POLLPRI : POLLIN | POLLPRI;
break;
default:
abort();
}
pollfds_buf[1].fd = ads->tcpsocket;
return 2;
}
int adns_processreadable(adns_state ads, int fd, const struct timeval *now)
{
int want, dgramlen, r, udpaddrlen, serv, old_skip;
byte udpbuf[DNS_MAXUDP];
struct sockaddr_in udpaddr;
adns__consistency(ads, 0, cc_entex);
switch (ads->tcpstate) {
case server_disconnected:
case server_broken:
case server_connecting:
break;
case server_ok:
if (fd != ads->tcpsocket)
break;
assert(!ads->tcprecv_skip);
do {
if (ads->tcprecv.used >= ads->tcprecv_skip + 2) {
dgramlen =
((ads->tcprecv.
buf[ads->tcprecv_skip] << 8) | ads->
tcprecv.buf[ads->tcprecv_skip + 1]);
if (ads->tcprecv.used >=
ads->tcprecv_skip + 2 + dgramlen) {
old_skip = ads->tcprecv_skip;
ads->tcprecv_skip += 2 + dgramlen;
adns__procdgram(ads,
ads->tcprecv.buf +
old_skip + 2,
dgramlen,
ads->tcpserver, 1,
*now);
continue;
} else {
want = 2 + dgramlen;
}
} else {
want = 2;
}
ads->tcprecv.used -= ads->tcprecv_skip;
memmove(ads->tcprecv.buf,
ads->tcprecv.buf + ads->tcprecv_skip,
ads->tcprecv.used);
ads->tcprecv_skip = 0;
if (!adns__vbuf_ensure(&ads->tcprecv, want)) {
r = ENOMEM;
goto xit;
}
assert(ads->tcprecv.used <= ads->tcprecv.avail);
if (ads->tcprecv.used == ads->tcprecv.avail)
continue;
r = read(ads->tcpsocket,
ads->tcprecv.buf + ads->tcprecv.used,
ads->tcprecv.avail - ads->tcprecv.used);
if (r > 0) {
ads->tcprecv.used += r;
} else {
if (r) {
if (errno == EAGAIN
|| errno == EWOULDBLOCK) {
r = 0;
goto xit;
}
if (errno == EINTR)
continue;
if (errno_resources(errno)) {
r = errno;
goto xit;
}
}
adns__tcp_broken(ads, "read",
r ? strerror(errno) :
"closed");
}
} while (ads->tcpstate == server_ok);
r = 0;
goto xit;
default:
abort();
}
if (fd == ads->udpsocket) {
for (;;) {
udpaddrlen = sizeof(udpaddr);
r = recvfrom(ads->udpsocket, udpbuf,
sizeof(udpbuf), 0,
(struct sockaddr *) &udpaddr,
&udpaddrlen);
if (r < 0) {
if (errno == EAGAIN
|| errno == EWOULDBLOCK) {
r = 0;
goto xit;
}
if (errno == EINTR)
continue;
if (errno_resources(errno)) {
r = errno;
goto xit;
}
adns__warn(ads, -1, 0,
"datagram receive error: %s",
strerror(errno));
r = 0;
goto xit;
}
if (udpaddrlen != sizeof(udpaddr)) {
adns__diag(ads, -1, 0,
"datagram received with wrong address length %d"
" (expected %lu)", udpaddrlen,
(unsigned long)
sizeof(udpaddr));
continue;
}
if (udpaddr.sin_family != AF_INET) {
adns__diag(ads, -1, 0,
"datagram received with wrong protocol family"
" %u (expected %u)",
udpaddr.sin_family, AF_INET);
continue;
}
if (ntohs(udpaddr.sin_port) != DNS_PORT) {
adns__diag(ads, -1, 0,
"datagram received from wrong port %u (expected %u)",
ntohs(udpaddr.sin_port),
DNS_PORT);
continue;
}
for (serv = 0;
serv < ads->nservers &&
ads->servers[serv].addr.s_addr !=
udpaddr.sin_addr.s_addr; serv++);
if (serv >= ads->nservers) {
adns__warn(ads, -1, 0,
"datagram received from unknown nameserver %s",
inet_ntoa(udpaddr.sin_addr));
continue;
}
adns__procdgram(ads, udpbuf, r, serv, 0, *now);
}
}
r = 0;
xit:
adns__consistency(ads, 0, cc_entex);
return r;
}
int adns_processwriteable(adns_state ads, int fd,
const struct timeval *now)
{
int r;
adns__consistency(ads, 0, cc_entex);
switch (ads->tcpstate) {
case server_disconnected:
case server_broken:
break;
case server_connecting:
if (fd != ads->tcpsocket)
break;
assert(ads->tcprecv.used == 0);
assert(ads->tcprecv_skip == 0);
for (;;) {
if (!adns__vbuf_ensure(&ads->tcprecv, 1)) {
r = ENOMEM;
goto xit;
}
r = read(ads->tcpsocket, &ads->tcprecv.buf, 1);
if (r == 0
|| (r < 0
&& (errno == EAGAIN
|| errno == EWOULDBLOCK))) {
tcp_connected(ads, *now);
r = 0;
goto xit;
}
if (r > 0) {
adns__tcp_broken(ads, "connect/read",
"sent data before first request");
r = 0;
goto xit;
}
if (errno == EINTR)
continue;
if (errno_resources(errno)) {
r = errno;
goto xit;
}
adns__tcp_broken(ads, "connect/read",
strerror(errno));
r = 0;
goto xit;
} /* not reached */
case server_ok:
if (fd != ads->tcpsocket)
break;
while (ads->tcpsend.used) {
adns__sigpipe_protect(ads);
r = write(ads->tcpsocket, ads->tcpsend.buf,
ads->tcpsend.used);
adns__sigpipe_unprotect(ads);
if (r < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN
|| errno == EWOULDBLOCK) {
r = 0;
goto xit;
}
if (errno_resources(errno)) {
r = errno;
goto xit;
}
adns__tcp_broken(ads, "write",
strerror(errno));
r = 0;
goto xit;
} else if (r > 0) {
ads->tcpsend.used -= r;
memmove(ads->tcpsend.buf,
ads->tcpsend.buf + r,
ads->tcpsend.used);
}
}
r = 0;
goto xit;
default:
abort();
}
r = 0;
xit:
adns__consistency(ads, 0, cc_entex);
return r;
}
int adns_processexceptional(adns_state ads, int fd,
const struct timeval *now)
{
adns__consistency(ads, 0, cc_entex);
switch (ads->tcpstate) {
case server_disconnected:
case server_broken:
break;
case server_connecting:
case server_ok:
if (fd != ads->tcpsocket)
break;
adns__tcp_broken(ads, "poll/select",
"exceptional condition detected");
break;
default:
abort();
}
adns__consistency(ads, 0, cc_entex);
return 0;
}
static void fd_event(adns_state ads, int fd,
int revent, int pollflag,
int maxfd, const fd_set * fds,
int (*func) (adns_state, int fd,
const struct timeval * now),
struct timeval now, int *r_r)
{
int r;
if (!(revent & pollflag))
return;
if (fds && !(fd < maxfd && FD_ISSET(fd, fds)))
return;
r = func(ads, fd, &now);
if (r) {
if (r_r) {
*r_r = r;
} else {
adns__diag(ads, -1, 0,
"process fd failed after select: %s",
strerror(errno));
adns_globalsystemfailure(ads);
}
}
}
void adns__fdevents(adns_state ads,
const struct pollfd *pollfds, int npollfds,
int maxfd, const fd_set * readfds,
const fd_set * writefds, const fd_set * exceptfds,
struct timeval now, int *r_r)
{
int i, fd, revents;
for (i = 0; i < npollfds; i++) {
fd = pollfds[i].fd;
if (fd >= maxfd)
maxfd = fd + 1;
revents = pollfds[i].revents;
fd_event(ads, fd, revents, POLLIN, maxfd, readfds,
adns_processreadable, now, r_r);
fd_event(ads, fd, revents, POLLOUT, maxfd, writefds,
adns_processwriteable, now, r_r);
fd_event(ads, fd, revents, POLLPRI, maxfd, exceptfds,
adns_processexceptional, now, r_r);
}
}
/* Wrappers for select(2). */
void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set * readfds_io,
fd_set * writefds_io, fd_set * exceptfds_io,
struct timeval **tv_mod, struct timeval *tv_tobuf,
const struct timeval *now)
{
struct timeval tv_nowbuf;
struct pollfd pollfds[MAX_POLLFDS];
int i, fd, maxfd, npollfds;
adns__consistency(ads, 0, cc_entex);
if (tv_mod
&& (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
/* The caller is planning to sleep. */
adns__must_gettimeofday(ads, &now, &tv_nowbuf);
if (!now) {
inter_immed(tv_mod, tv_tobuf);
adns__consistency(ads, 0, cc_entex);
return;
#if 0
goto xit;
#endif
}
adns__timeouts(ads, 0, tv_mod, tv_tobuf, *now);
}
npollfds = adns__pollfds(ads, pollfds);
maxfd = *maxfd_io;
for (i = 0; i < npollfds; i++) {
fd = pollfds[i].fd;
if (fd >= maxfd)
maxfd = fd + 1;
if (pollfds[i].events & POLLIN)
FD_SET(fd, readfds_io);
if (pollfds[i].events & POLLOUT)
FD_SET(fd, writefds_io);
if (pollfds[i].events & POLLPRI)
FD_SET(fd, exceptfds_io);
}
*maxfd_io = maxfd;
#if 0
xit:
#endif
adns__consistency(ads, 0, cc_entex);
}
void adns_afterselect(adns_state ads, int maxfd, const fd_set * readfds,
const fd_set * writefds, const fd_set * exceptfds,
const struct timeval *now)
{
struct timeval tv_buf;
struct pollfd pollfds[MAX_POLLFDS];
int npollfds, i;
adns__consistency(ads, 0, cc_entex);
adns__must_gettimeofday(ads, &now, &tv_buf);
if (!now)
goto xit;
adns_processtimeouts(ads, now);
npollfds = adns__pollfds(ads, pollfds);
for (i = 0; i < npollfds; i++)
pollfds[i].revents = POLLIN | POLLOUT | POLLPRI;
adns__fdevents(ads,
pollfds, npollfds,
maxfd, readfds, writefds, exceptfds, *now, 0);
xit:
adns__consistency(ads, 0, cc_entex);
}
/* General helpful functions. */
void adns_globalsystemfailure(adns_state ads)
{
adns__consistency(ads, 0, cc_entex);
while (ads->udpw.head)
adns__query_fail(ads->udpw.head, adns_s_systemfail);
while (ads->tcpw.head)
adns__query_fail(ads->tcpw.head, adns_s_systemfail);
switch (ads->tcpstate) {
case server_connecting:
case server_ok:
adns__tcp_broken(ads, 0, 0);
break;
case server_disconnected:
case server_broken:
break;
default:
abort();
}
adns__consistency(ads, 0, cc_entex);
}
int adns_processany(adns_state ads)
{
int r, i;
struct timeval now;
struct pollfd pollfds[MAX_POLLFDS];
int npollfds;
adns__consistency(ads, 0, cc_entex);
r = gettimeofday(&now, 0);
if (!r)
adns_processtimeouts(ads, &now);
/* We just use adns__fdevents to loop over the fd's trying them.
* This seems more sensible than calling select, since we're most
* likely just to want to do a read on one or two fds anyway.
*/
npollfds = adns__pollfds(ads, pollfds);
for (i = 0; i < npollfds; i++)
pollfds[i].revents = pollfds[i].events & ~POLLPRI;
adns__fdevents(ads, pollfds, npollfds, 0, 0, 0, 0, now, &r);
adns__consistency(ads, 0, cc_entex);
return 0;
}
void adns__autosys(adns_state ads, struct timeval now)
{
if (ads->iflags & adns_if_noautosys)
return;
adns_processany(ads);
}
int adns__internal_check(adns_state ads,
adns_query * query_io,
adns_answer ** answer, void **context_r)
{
adns_query qu;
qu = *query_io;
if (!qu) {
if (ads->output.head) {
qu = ads->output.head;
} else if (ads->udpw.head || ads->tcpw.head) {
return EAGAIN;
} else {
return ESRCH;
}
} else {
if (qu->id >= 0)
return EAGAIN;
}
LIST_UNLINK(ads->output, qu);
*answer = qu->answer;
if (context_r)
*context_r = qu->ctx.ext;
*query_io = qu;
free(qu);
return 0;
}
int adns_wait(adns_state ads,
adns_query * query_io,
adns_answer ** answer_r, void **context_r)
{
int r, maxfd, rsel;
fd_set readfds, writefds, exceptfds;
struct timeval tvbuf, *tvp;
adns__consistency(ads, *query_io, cc_entex);
for (;;) {
r = adns__internal_check(ads, query_io, answer_r,
context_r);
if (r != EAGAIN)
break;
maxfd = 0;
tvp = 0;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
adns_beforeselect(ads, &maxfd, &readfds, &writefds,
&exceptfds, &tvp, &tvbuf, 0);
assert(tvp);
rsel = select(maxfd, &readfds, &writefds, &exceptfds, tvp);
if (rsel == -1) {
if (errno == EINTR) {
if (ads->iflags & adns_if_eintr) {
r = EINTR;
break;
}
} else {
adns__diag(ads, -1, 0,
"select failed in wait: %s",
strerror(errno));
adns_globalsystemfailure(ads);
}
} else {
assert(rsel >= 0);
adns_afterselect(ads, maxfd, &readfds, &writefds,
&exceptfds, 0);
}
}
adns__consistency(ads, 0, cc_entex);
return r;
}
int adns_check(adns_state ads,
adns_query * query_io,
adns_answer ** answer_r, void **context_r)
{
struct timeval now;
int r;
adns__consistency(ads, *query_io, cc_entex);
r = gettimeofday(&now, 0);
if (!r)
adns__autosys(ads, now);
r = adns__internal_check(ads, query_io, answer_r, context_r);
adns__consistency(ads, 0, cc_entex);
return r;
}

445
adns/general.c Normal file
View file

@ -0,0 +1,445 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* general.c
* - diagnostic functions
* - vbuf handling
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "internal.h"
/* Core diagnostic functions */
void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
int serv, adns_query qu, const char *fmt, va_list al)
{
const char *bef, *aft;
vbuf vb;
if (!ads->diagfile ||
(!(ads->iflags & adns_if_debug)
&& (!prevent || (ads->iflags & prevent))))
return;
if (ads->iflags & adns_if_logpid) {
fprintf(ads->diagfile, "adns%s [%ld]: ", pfx,
(long) getpid());
} else {
fprintf(ads->diagfile, "adns%s: ", pfx);
}
vfprintf(ads->diagfile, fmt, al);
bef = " (";
aft = "\n";
if (qu && qu->query_dgram) {
adns__vbuf_init(&vb);
fprintf(ads->diagfile, "%sQNAME=%s, QTYPE=%s",
bef,
adns__diag_domain(qu->ads, -1, 0, &vb,
qu->query_dgram, qu->query_dglen,
DNS_HDRSIZE),
qu->typei ? qu->typei->rrtname : "<unknown>");
if (qu->typei && qu->typei->fmtname)
fprintf(ads->diagfile, "(%s)", qu->typei->fmtname);
bef = ", ";
aft = ")\n";
adns__vbuf_free(&vb);
}
if (serv >= 0) {
fprintf(ads->diagfile, "%sNS=%s", bef,
inet_ntoa(ads->servers[serv].addr));
bef = ", ";
aft = ")\n";
}
fputs(aft, ads->diagfile);
}
void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt,
...)
{
va_list al;
va_start(al, fmt);
adns__vdiag(ads, " debug", 0, serv, qu, fmt, al);
va_end(al);
}
void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt,
...)
{
va_list al;
va_start(al, fmt);
adns__vdiag(ads, " warning",
adns_if_noerrprint | adns_if_noserverwarn, serv, qu,
fmt, al);
va_end(al);
}
void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt,
...)
{
va_list al;
va_start(al, fmt);
adns__vdiag(ads, "", adns_if_noerrprint, serv, qu, fmt, al);
va_end(al);
}
/* vbuf functions */
void adns__vbuf_init(vbuf * vb)
{
vb->used = vb->avail = 0;
vb->buf = 0;
}
int adns__vbuf_ensure(vbuf * vb, int want)
{
void *nb;
if (vb->avail >= want)
return 1;
nb = realloc(vb->buf, want);
if (!nb)
return 0;
vb->buf = nb;
vb->avail = want;
return 1;
}
void adns__vbuf_appendq(vbuf * vb, const byte * data, int len)
{
memcpy(vb->buf + vb->used, data, len);
vb->used += len;
}
int adns__vbuf_append(vbuf * vb, const byte * data, int len)
{
int newlen;
void *nb;
newlen = vb->used + len;
if (vb->avail < newlen) {
if (newlen < 20)
newlen = 20;
newlen <<= 1;
nb = realloc(vb->buf, newlen);
if (!nb) {
newlen = vb->used + len;
nb = realloc(vb->buf, newlen);
}
if (!nb)
return 0;
vb->buf = nb;
vb->avail = newlen;
}
adns__vbuf_appendq(vb, data, len);
return 1;
}
int adns__vbuf_appendstr(vbuf * vb, const char *data)
{
int l;
l = strlen(data);
return adns__vbuf_append(vb, data, l);
}
void adns__vbuf_free(vbuf * vb)
{
free(vb->buf);
adns__vbuf_init(vb);
}
/* Additional diagnostic functions */
const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
vbuf * vb, const byte * dgram, int dglen,
int cbyte)
{
adns_status st;
st = adns__parse_domain(ads, serv, qu, vb, pdf_quoteok, dgram,
dglen, &cbyte, dglen);
if (st == adns_s_nomemory) {
return "<cannot report domain... out of memory>";
}
if (st) {
vb->used = 0;
if (!(adns__vbuf_appendstr(vb, "<bad format... ") &&
adns__vbuf_appendstr(vb, adns_strerror(st)) &&
adns__vbuf_appendstr(vb, ">") &&
adns__vbuf_append(vb, "", 1))) {
return
"<cannot report bad format... out of memory>";
}
}
if (!vb->used) {
adns__vbuf_appendstr(vb, "<truncated ...>");
adns__vbuf_append(vb, "", 1);
}
return vb->buf;
}
adns_status adns_rr_info(adns_rrtype type,
const char **rrtname_r, const char **fmtname_r,
int *len_r, const void *datap, char **data_r)
{
const typeinfo *typei;
vbuf vb;
adns_status st;
typei = adns__findtype(type);
if (!typei)
return adns_s_unknownrrtype;
if (rrtname_r)
*rrtname_r = typei->rrtname;
if (fmtname_r)
*fmtname_r = typei->fmtname;
if (len_r)
*len_r = typei->rrsz;
if (!datap)
return adns_s_ok;
adns__vbuf_init(&vb);
st = typei->convstring(&vb, datap);
if (st)
goto x_freevb;
if (!adns__vbuf_append(&vb, "", 1)) {
st = adns_s_nomemory;
goto x_freevb;
}
assert(strlen(vb.buf) == vb.used - 1);
*data_r = realloc(vb.buf, vb.used);
if (!*data_r)
*data_r = vb.buf;
return adns_s_ok;
x_freevb:
adns__vbuf_free(&vb);
return st;
}
#define SINFO(n,s) { adns_s_##n, #n, s }
static const struct sinfo {
adns_status st;
const char *abbrev;
const char *string;
} sinfos[] = {
SINFO(ok, "OK"),
SINFO(nomemory, "Out of memory"),
SINFO(unknownrrtype, "Query not implemented in DNS library"),
SINFO(systemfail, "General resolver or system failure"),
SINFO(timeout, "DNS query timed out"),
SINFO(allservfail, "All nameservers failed"),
SINFO(norecurse, "Recursion denied by nameserver"),
SINFO(invalidresponse, "Nameserver sent bad response"),
SINFO(unknownformat, "Nameserver used unknown format"),
SINFO(rcodeservfail, "Nameserver reports failure"),
SINFO(rcodeformaterror, "Query not understood by nameserver"),
SINFO(rcodenotimplemented,
"Query not implemented by nameserver"),
SINFO(rcoderefused, "Query refused by nameserver"),
SINFO(rcodeunknown, "Nameserver sent unknown response code"),
SINFO(inconsistent, "Inconsistent resource records in DNS"),
SINFO(prohibitedcname,
"DNS alias found where canonical name wanted"),
SINFO(answerdomaininvalid,
"Found syntactically invalid domain name"),
SINFO(answerdomaintoolong, "Found overly-long domain name"),
SINFO(invaliddata, "Found invalid DNS data"),
SINFO(querydomainwrong,
"Domain invalid for particular DNS query type"),
SINFO(querydomaininvalid,
"Domain name is syntactically invalid"),
SINFO(querydomaintoolong,
"Domain name or component is too long"), SINFO(nxdomain,
"No such domain"),
SINFO(nodata, "No such data")
};
static int si_compar(const void *key, const void *elem)
{
const adns_status *st = key;
const struct sinfo *si = elem;
return *st < si->st ? -1 : *st > si->st ? 1 : 0;
}
static const struct sinfo *findsinfo(adns_status st)
{
return bsearch(&st, sinfos, sizeof(sinfos) / sizeof(*sinfos),
sizeof(*sinfos), si_compar);
}
const char *adns_strerror(adns_status st)
{
const struct sinfo *si;
si = findsinfo(st);
return si->string;
}
const char *adns_errabbrev(adns_status st)
{
const struct sinfo *si;
si = findsinfo(st);
return si->abbrev;
}
#define STINFO(max) { adns_s_max_##max, #max }
static const struct stinfo {
adns_status stmax;
const char *abbrev;
} stinfos[] = {
{
adns_s_ok, "ok"},
STINFO(localfail),
STINFO(remotefail),
STINFO(tempfail),
STINFO(misconfig), STINFO(misquery), STINFO(permfail)
};
static int sti_compar(const void *key, const void *elem)
{
const adns_status *st = key;
const struct stinfo *sti = elem;
adns_status here, min, max;
here = *st;
min = (sti == stinfos) ? 0 : sti[-1].stmax + 1;
max = sti->stmax;
return here < min ? -1 : here > max ? 1 : 0;
}
const char *adns_errtypeabbrev(adns_status st)
{
const struct stinfo *sti;
sti =
bsearch(&st, stinfos, sizeof(stinfos) / sizeof(*stinfos),
sizeof(*stinfos), sti_compar);
return sti->abbrev;
}
void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
int (*needswap) (void *context, const void *a,
const void *b), void *context)
{
byte *data = array;
int i, place;
for (i = 0; i < nobjs; i++) {
for (place = i;
place > 0
&& needswap(context, data + (place - 1) * sz,
data + i * sz); place--);
if (place != i) {
memcpy(tempbuf, data + i * sz, sz);
memmove(data + (place + 1) * sz, data + place * sz,
(i - place) * sz);
memcpy(data + place * sz, tempbuf, sz);
}
}
}
/* SIGPIPE protection. */
void adns__sigpipe_protect(adns_state ads)
{
sigset_t toblock;
struct sigaction sa;
int r;
if (ads->iflags & adns_if_nosigpipe)
return;
sigfillset(&toblock);
sigdelset(&toblock, SIGPIPE);
sa.sa_handler = SIG_IGN;
sigfillset(&sa.sa_mask);
sa.sa_flags = 0;
r = sigprocmask(SIG_SETMASK, &toblock, &ads->stdsigmask);
assert(!r);
r = sigaction(SIGPIPE, &sa, &ads->stdsigpipe);
assert(!r);
}
void adns__sigpipe_unprotect(adns_state ads)
{
int r;
if (ads->iflags & adns_if_nosigpipe)
return;
r = sigaction(SIGPIPE, &ads->stdsigpipe, 0);
assert(!r);
r = sigprocmask(SIG_SETMASK, &ads->stdsigmask, 0);
assert(!r);
}

770
adns/internal.h Normal file
View file

@ -0,0 +1,770 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* internal.h
* - declarations of private objects with external linkage (adns__*)
* - definitons of internal macros
* - comments regarding library data structures
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ADNS_INTERNAL_H_INCLUDED
#define ADNS_INTERNAL_H_INCLUDED
#include "config.h"
typedef unsigned char byte;
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include "adns.h"
#include "dlist.h"
#ifdef ADNS_REGRESS_TEST
# include "hredirect.h"
#endif
/* Configuration and constants */
#define MAXSERVERS 5
#define MAXSORTLIST 15
#define UDPMAXRETRIES 15
#define UDPRETRYMS 2000
#define TCPWAITMS 30000
#define TCPCONNMS 14000
#define TCPIDLEMS 30000
#define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */
#define DNS_PORT 53
#define DNS_MAXUDP 512
#define DNS_MAXLABEL 63
#define DNS_MAXDOMAIN 255
#define DNS_HDRSIZE 12
#define DNS_IDOFFSET 0
#define DNS_CLASS_IN 1
#define DNS_INADDR_ARPA "in-addr", "arpa"
#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED
typedef enum {
cc_user,
cc_entex,
cc_freq
} consistency_checks;
typedef enum {
rcode_noerror,
rcode_formaterror,
rcode_servfail,
rcode_nxdomain,
rcode_notimp,
rcode_refused
} dns_rcode;
/* Shared data structures */
typedef union {
adns_status status;
char *cp;
adns_rrtype type;
int i;
struct in_addr ia;
unsigned long ul;
} rr_align;
typedef struct {
int used, avail;
byte *buf;
} vbuf;
typedef struct {
adns_state ads;
adns_query qu;
int serv;
const byte *dgram;
int dglen, nsstart, nscount, arcount;
struct timeval now;
} parseinfo;
typedef struct {
adns_rrtype type;
const char *rrtname;
const char *fmtname;
int rrsz;
void (*makefinal) (adns_query qu, void *data);
/* Change memory management of *data.
* Previously, used alloc_interim, now use alloc_final.
*/
adns_status(*convstring) (vbuf * vb, const void *data);
/* Converts the RR data to a string representation in vbuf.
* vbuf will be appended to (it must have been initialised),
* and will not be null-terminated by convstring.
*/
adns_status(*parse) (const parseinfo * pai, int cbyte, int max,
void *store_r);
/* Parse one RR, in dgram of length dglen, starting at cbyte and
* extending until at most max.
*
* The RR should be stored at *store_r, of length qu->typei->rrsz.
*
* If there is an overrun which might indicate truncation, it should set
* *rdstart to -1; otherwise it may set it to anything else positive.
*
* nsstart is the offset of the authority section.
*/
int (*diff_needswap) (adns_state ads, const void *datap_a,
const void *datap_b);
/* Returns !0 if RR a should be strictly after RR b in the sort order,
* 0 otherwise. Must not fail.
*/
} typeinfo;
typedef struct allocnode {
struct allocnode *next, *back;
} allocnode;
union maxalign {
byte d[1];
struct in_addr ia;
long l;
void *p;
void (*fp) (void);
union maxalign *up;
} data;
typedef struct {
void *ext;
void (*callback) (adns_query parent, adns_query child);
union {
adns_rr_addr ptr_parent_addr;
adns_rr_hostaddr *hostaddr;
} info;
} qcontext;
struct adns__query {
adns_state ads;
enum { query_tosend, query_tcpw, query_childw, query_done } state;
adns_query back, next, parent;
struct {
adns_query head, tail;
} children;
struct {
adns_query back, next;
} siblings;
struct {
allocnode *head, *tail;
} allocations;
int interim_allocd, preserved_allocd;
void *final_allocspace;
const typeinfo *typei;
byte *query_dgram;
int query_dglen;
vbuf vb;
/* General-purpose messing-about buffer.
* Wherever a `big' interface is crossed, this may be corrupted/changed
* unless otherwise specified.
*/
adns_answer *answer;
/* This is allocated when a query is submitted, to avoid being unable
* to relate errors to queries if we run out of memory. During
* query processing status, rrs is 0. cname is set if
* we found a cname (this corresponds to cname_dgram in the query
* structure). type is set from the word go. nrrs and rrs
* are set together, when we find how many rrs there are.
* owner is set during querying unless we're doing searchlist,
* in which case it is set only when we find an answer.
*/
byte *cname_dgram;
int cname_dglen, cname_begin;
/* If non-0, has been allocated using . */
vbuf search_vb;
int search_origlen, search_pos, search_doneabs;
/* Used by the searching algorithm. The query domain in textual form
* is copied into the vbuf, and _origlen set to its length. Then
* we walk the searchlist, if we want to. _pos says where we are
* (next entry to try), and _doneabs says whether we've done the
* absolute query yet (0=not yet, 1=done, -1=must do straight away,
* but not done yet). If flags doesn't have adns_qf_search then
* the vbuf is initialised but empty and everything else is zero.
*/
int id, flags, retries;
int udpnextserver;
unsigned long udpsent; /* bitmap indexed by server */
struct timeval timeout;
time_t expires; /* Earliest expiry time of any record we used. */
qcontext ctx;
/* Possible states:
*
* state Queue child id nextudpserver udpsent tcpfailed
*
* tosend NONE null >=0 0 zero zero
* tosend udpw null >=0 any nonzero zero
* tosend NONE null >=0 any nonzero zero
*
* tcpw tcpw null >=0 irrelevant any any
*
* child childw set >=0 irrelevant irrelevant irrelevant
* child NONE null >=0 irrelevant irrelevant irrelevant
* done output null -1 irrelevant irrelevant irrelevant
*
* Queries are only not on a queue when they are actually being processed.
* Queries in state tcpw/tcpw have been sent (or are in the to-send buffer)
* iff the tcp connection is in state server_ok.
*
* +------------------------+
* START -----> | tosend/NONE |
* +------------------------+
* / |\ \
* too big for UDP / UDP timeout \ \ send via UDP
* send via TCP / more retries \ \
* when conn'd / desired \ \
* | | |
* v | v
* +-----------+ +-------------+
* | tcpw/tcpw | ________ | tosend/udpw |
* +-----------+ \ +-------------+
* | | | UDP timeout | |
* | | | no more | |
* | | | retries | |
* \ | TCP died | desired | |
* \ \ no more | | |
* \ \ servers | TCP / |
* \ \ to try | timeout / |
* got \ \ v |_ | got
* reply \ _| +------------------+ / reply
* \ | done/output FAIL | /
* \ +------------------+ /
* \ /
* _| |_
* (..... got reply ....)
* / \
* need child query/ies / \ no child query
* / \
* |_ _|
* +---------------+ +----------------+
* | childw/childw | ----------------> | done/output OK |
* +---------------+ children done +----------------+
*/
};
struct query_queue {
adns_query head, tail;
};
struct adns__state {
adns_initflags iflags;
FILE *diagfile;
int configerrno;
struct query_queue udpw, tcpw, childw, output;
adns_query forallnext;
int nextid, udpsocket, tcpsocket;
vbuf tcpsend, tcprecv;
int nservers, nsortlist, nsearchlist, searchndots, tcpserver,
tcprecv_skip;
enum adns__tcpstate {
server_disconnected, server_connecting,
server_ok, server_broken
} tcpstate;
struct timeval tcptimeout;
/* This will have tv_sec==0 if it is not valid. It will always be
* valid if tcpstate _connecting. When _ok, it will be nonzero if
* we are idle (ie, tcpw queue is empty), in which case it is the
* absolute time when we will close the connection.
*/
struct sigaction stdsigpipe;
sigset_t stdsigmask;
struct pollfd pollfds_buf[MAX_POLLFDS];
struct server {
struct in_addr addr;
} servers[MAXSERVERS];
struct sortlist {
struct in_addr base, mask;
} sortlist[MAXSORTLIST];
char **searchlist;
};
/* From setup.c: */
int adns__setnonblock(adns_state ads, int fd); /* => errno value */
/* From general.c: */
void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
int serv, adns_query qu, const char *fmt, va_list al);
void adns__debug(adns_state ads, int serv, adns_query qu,
const char *fmt, ...) PRINTFFORMAT(4, 5);
void adns__warn(adns_state ads, int serv, adns_query qu,
const char *fmt, ...) PRINTFFORMAT(4, 5);
void adns__diag(adns_state ads, int serv, adns_query qu,
const char *fmt, ...) PRINTFFORMAT(4, 5);
int adns__vbuf_ensure(vbuf * vb, int want);
int adns__vbuf_appendstr(vbuf * vb, const char *data); /* does not include nul */
int adns__vbuf_append(vbuf * vb, const byte * data, int len);
/* 1=>success, 0=>realloc failed */
void adns__vbuf_appendq(vbuf * vb, const byte * data, int len);
void adns__vbuf_init(vbuf * vb);
void adns__vbuf_free(vbuf * vb);
const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
vbuf * vb, const byte * dgram, int dglen,
int cbyte);
/* Unpicks a domain in a datagram and returns a string suitable for
* printing it as. Never fails - if an error occurs, it will
* return some kind of string describing the error.
*
* serv may be -1 and qu may be 0. vb must have been initialised,
* and will be left in an arbitrary consistent state.
*
* Returns either vb->buf, or a pointer to a string literal. Do not modify
* vb before using the return value.
*/
void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
int (*needswap) (void *context, const void *a,
const void *b), void *context);
/* Does an insertion sort of array which must contain nobjs objects
* each sz bytes long. tempbuf must point to a buffer at least
* sz bytes long. needswap should return !0 if a>b (strictly, ie
* wrong order) 0 if a<=b (ie, order is fine).
*/
void adns__sigpipe_protect(adns_state);
void adns__sigpipe_unprotect(adns_state);
/* If SIGPIPE protection is not disabled, will block all signals except
* SIGPIPE, and set SIGPIPE's disposition to SIG_IGN. (And then restore.)
* Each call to _protect must be followed by a call to _unprotect before
* any significant amount of code gets to run, since the old signal mask
* is stored in the adns structure.
*/
/* From transmit.c: */
adns_status adns__mkquery(adns_state ads, vbuf * vb, int *id_r,
const char *owner, int ol,
const typeinfo * typei, adns_queryflags flags);
/* Assembles a query packet in vb. A new id is allocated and returned.
*/
adns_status adns__mkquery_frdgram(adns_state ads, vbuf * vb, int *id_r,
const byte * qd_dgram, int qd_dglen,
int qd_begin, adns_rrtype type,
adns_queryflags flags);
/* Same as adns__mkquery, but takes the owner domain from an existing datagram.
* That domain must be correct and untruncated.
*/
void adns__querysend_tcp(adns_query qu, struct timeval now);
/* Query must be in state tcpw/tcpw; it will be sent if possible and
* no further processing can be done on it for now. The connection
* might be broken, but no reconnect will be attempted.
*/
void adns__query_send(adns_query qu, struct timeval now);
/* Query must be in state tosend/NONE; it will be moved to a new state,
* and no further processing can be done on it for now.
* (Resulting state is one of udp/timew, tcpwait/timew (if server not connected),
* tcpsent/timew, child/childw or done/output.)
* __query_send may decide to use either UDP or TCP depending whether
* _qf_usevc is set (or has become set) and whether the query is too
* large.
*/
/* From query.c: */
adns_status adns__internal_submit(adns_state ads, adns_query * query_r,
const typeinfo * typei, vbuf * qumsg_vb,
int id, adns_queryflags flags,
struct timeval now,
const qcontext * ctx);
/* Submits a query (for internal use, called during external submits).
*
* The new query is returned in *query_r, or we return adns_s_nomemory.
*
* The query datagram should already have been assembled in qumsg_vb;
* the memory for it is _taken over_ by this routine whether it
* succeeds or fails (if it succeeds, the vbuf is reused for qu->vb).
*
* *ctx is copied byte-for-byte into the query.
*
* When the child query is done, ctx->callback will be called. The
* child will already have been taken off both the global list of
* queries in ads and the list of children in the parent. The child
* will be freed when the callback returns. The parent will have been
* taken off the global childw queue.
*
* The callback should either call adns__query_done, if it is
* complete, or adns__query_fail, if an error has occurred, in which
* case the other children (if any) will be cancelled. If the parent
* has more unfinished children (or has just submitted more) then the
* callback may choose to wait for them - it must then put the parent
* back on the childw queue.
*/
void adns__search_next(adns_state ads, adns_query qu, struct timeval now);
/* Walks down the searchlist for a query with adns_qf_search.
* The query should have just had a negative response, or not had
* any queries sent yet, and should not be on any queue.
* The query_dgram if any will be freed and forgotten and a new
* one constructed from the search_* members of the query.
*
* Cannot fail (in case of error, calls adns__query_fail).
*/
void *adns__alloc_interim(adns_query qu, size_t sz);
void *adns__alloc_preserved(adns_query qu, size_t sz);
/* Allocates some memory, and records which query it came from
* and how much there was.
*
* If an error occurs in the query, all the memory from _interim is
* simply freed. If the query succeeds, one large buffer will be made
* which is big enough for all these allocations, and then
* adns__alloc_final will get memory from this buffer.
*
* _alloc_interim can fail (and return 0).
* The caller must ensure that the query is failed.
*
* The memory from _preserved is is kept and transferred into the
* larger buffer - unless we run out of memory, in which case it too
* is freed. When you use _preserved you have to add code to the
* x_nomem error exit case in adns__makefinal_query to clear out the
* pointers you made to those allocations, because that's when they're
* thrown away; you should also make a note in the declaration of
* those pointer variables, to note that they are _preserved rather
* than _interim. If they're in the answer, note it here:
* answer->cname and answer->owner are _preserved.
*/
void adns__transfer_interim(adns_query from, adns_query to, void *block,
size_t sz);
/* Transfers an interim allocation from one query to another, so that
* the `to' query will have room for the data when we get to makefinal
* and so that the free will happen when the `to' query is freed
* rather than the `from' query.
*
* It is legal to call adns__transfer_interim with a null pointer; this
* has no effect.
*
* _transfer_interim also ensures that the expiry time of the `to' query
* is no later than that of the `from' query, so that child queries'
* TTLs get inherited by their parents.
*/
void *adns__alloc_mine(adns_query qu, size_t sz);
/* Like _interim, but does not record the length for later
* copying into the answer. This just ensures that the memory
* will be freed when we're done with the query.
*/
void *adns__alloc_final(adns_query qu, size_t sz);
/* Cannot fail, and cannot return 0.
*/
void adns__makefinal_block(adns_query qu, void **blpp, size_t sz);
void adns__makefinal_str(adns_query qu, char **strp);
void adns__reset_preserved(adns_query qu);
/* Resets all of the memory management stuff etc. to take account of
* only the _preserved stuff from _alloc_preserved. Used when we find
* an error somewhere and want to just report the error (with perhaps
* CNAME, owner, etc. info), and also when we're halfway through RRs
* in a datagram and discover that we need to retry the query.
*/
void adns__query_done(adns_query qu);
void adns__query_fail(adns_query qu, adns_status stat);
/* From reply.c: */
void adns__procdgram(adns_state ads, const byte * dgram, int len,
int serv, int viatcp, struct timeval now);
/* This function is allowed to cause new datagrams to be constructed
* and sent, or even new queries to be started. However,
* query-sending functions are not allowed to call any general event
* loop functions in case they accidentally call this.
*
* Ie, receiving functions may call sending functions.
* Sending functions may NOT call receiving functions.
*/
/* From types.c: */
const typeinfo *adns__findtype(adns_rrtype type);
/* From parse.c: */
typedef struct {
adns_state ads;
adns_query qu;
int serv;
const byte *dgram;
int dglen, max, cbyte, namelen;
int *dmend_r;
} findlabel_state;
void adns__findlabel_start(findlabel_state * fls, adns_state ads,
int serv, adns_query qu,
const byte * dgram, int dglen, int max,
int dmbegin, int *dmend_rlater);
/* Finds labels in a domain in a datagram.
*
* Call this routine first.
* dmend_rlater may be null. ads (and of course fls) may not be.
* serv may be -1, qu may be null - they are for error reporting.
*/
adns_status adns__findlabel_next(findlabel_state * fls, int *lablen_r,
int *labstart_r);
/* Then, call this one repeatedly.
*
* It will return adns_s_ok if all is well, and tell you the length
* and start of successive labels. labstart_r may be null, but
* lablen_r must not be.
*
* After the last label, it will return with *lablen_r zero.
* Do not then call it again; instead, just throw away the findlabel_state.
*
* *dmend_rlater will have been set to point to the next part of
* the datagram after the label (or after the uncompressed part,
* if compression was used). *namelen_rlater will have been set
* to the length of the domain name (total length of labels plus
* 1 for each intervening dot).
*
* If the datagram appears to be truncated, *lablen_r will be -1.
* *dmend_rlater, *labstart_r and *namelen_r may contain garbage.
* Do not call _next again.
*
* There may also be errors, in which case *dmend_rlater,
* *namelen_rlater, *lablen_r and *labstart_r may contain garbage.
* Do not then call findlabel_next again.
*/
typedef enum {
pdf_quoteok = 0x001
} parsedomain_flags;
adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu,
vbuf * vb, parsedomain_flags flags,
const byte * dgram, int dglen,
int *cbyte_io, int max);
/* vb must already have been initialised; it will be reset if necessary.
* If there is truncation, vb->used will be set to 0; otherwise
* (if there is no error) vb will be null-terminated.
* If there is an error vb and *cbyte_io may be left indeterminate.
*
* serv may be -1 and qu may be 0 - they are used for error reporting only.
*/
adns_status adns__parse_domain_more(findlabel_state * fls, adns_state ads,
adns_query qu, vbuf * vb,
parsedomain_flags flags,
const byte * dgram);
/* Like adns__parse_domain, but you pass it a pre-initialised findlabel_state,
* for continuing an existing domain or some such of some kind. Also, unlike
* _parse_domain, the domain data will be appended to vb, rather than replacing
* the existing contents.
*/
adns_status adns__findrr(adns_query qu, int serv,
const byte * dgram, int dglen, int *cbyte_io,
int *type_r, int *class_r, unsigned long *ttl_r,
int *rdlen_r, int *rdstart_r,
int *ownermatchedquery_r);
/* Finds the extent and some of the contents of an RR in a datagram
* and does some checks. The datagram is *dgram, length dglen, and
* the RR starts at *cbyte_io (which is updated afterwards to point
* to the end of the RR).
*
* The type, class, TTL and RRdata length and start are returned iff
* the corresponding pointer variables are not null. type_r, class_r
* and ttl_r may not be null. The TTL will be capped.
*
* If ownermatchedquery_r != 0 then the owner domain of this
* RR will be compared with that in the query (or, if the query
* has gone to a CNAME lookup, with the canonical name).
* In this case, *ownermatchedquery_r will be set to 0 or 1.
* The query datagram (or CNAME datagram) MUST be valid and not truncated.
*
* If there is truncation then *type_r will be set to -1 and
* *cbyte_io, *class_r, *rdlen_r, *rdstart_r and *eo_matched_r will be
* undefined.
*
* qu must obviously be non-null.
*
* If an error is returned then *type_r will be undefined too.
*/
adns_status adns__findrr_anychk(adns_query qu, int serv,
const byte * dgram, int dglen,
int *cbyte_io, int *type_r, int *class_r,
unsigned long *ttl_r, int *rdlen_r,
int *rdstart_r, const byte * eo_dgram,
int eo_dglen, int eo_cbyte,
int *eo_matched_r);
/* Like adns__findrr_checked, except that the datagram and
* owner to compare with can be specified explicitly.
*
* If the caller thinks they know what the owner of the RR ought to
* be they can pass in details in eo_*: this is another (or perhaps
* the same datagram), and a pointer to where the putative owner
* starts in that datagram. In this case *eo_matched_r will be set
* to 1 if the datagram matched or 0 if it did not. Either
* both eo_dgram and eo_matched_r must both be non-null, or they
* must both be null (in which case eo_dglen and eo_cbyte will be ignored).
* The eo datagram and contained owner domain MUST be valid and
* untruncated.
*/
void adns__update_expires(adns_query qu, unsigned long ttl,
struct timeval now);
/* Updates the `expires' field in the query, so that it doesn't exceed
* now + ttl.
*/
int vbuf__append_quoted1035(vbuf * vb, const byte * buf, int len);
/* From event.c: */
void adns__tcp_broken(adns_state ads, const char *what, const char *why);
/* what and why may be both 0, or both non-0. */
void adns__tcp_tryconnect(adns_state ads, struct timeval now);
void adns__autosys(adns_state ads, struct timeval now);
/* Make all the system calls we want to if the application wants us to.
* Must not be called from within adns internal processing functions,
* lest we end up in recursive descent !
*/
void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
struct timeval *tv_buf);
int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]);
void adns__fdevents(adns_state ads,
const struct pollfd *pollfds, int npollfds,
int maxfd, const fd_set * readfds,
const fd_set * writefds, const fd_set * exceptfds,
struct timeval now, int *r_r);
int adns__internal_check(adns_state ads,
adns_query * query_io,
adns_answer ** answer, void **context_r);
void adns__timeouts(adns_state ads, int act,
struct timeval **tv_io, struct timeval *tvbuf,
struct timeval now);
/* If act is !0, then this will also deal with the TCP connection
* if previous events broke it or require it to be connected.
*/
/* From check.c: */
void adns__consistency(adns_state ads, adns_query qu,
consistency_checks cc);
/* Useful static inline functions: */
static inline int ctype_whitespace(int c)
{
return c == ' ' || c == '\n' || c == '\t';
}
static inline int ctype_digit(int c)
{
return c >= '0' && c <= '9';
}
static inline int ctype_alpha(int c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static inline int ctype_822special(int c)
{
return strchr("()<>@,;:\\\".[]", c) != 0;
}
static inline int ctype_domainunquoted(int c)
{
return ctype_alpha(c) || ctype_digit(c)
|| (strchr("-_/+", c) != 0);
}
static inline int errno_resources(int e)
{
return e == ENOMEM || e == ENOBUFS;
}
/* Useful macros */
#define MEM_ROUND(sz) \
(( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \
* sizeof(union maxalign) )
#define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff)
#define GET_B(cb,tv) ((tv)= GETIL_B((cb)))
#define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
#define GET_L(cb,tv) ( (tv)=0, \
(tv)|=(GETIL_B((cb))<<24), \
(tv)|=(GETIL_B((cb))<<16), \
(tv)|=(GETIL_B((cb))<<8), \
(tv)|=GETIL_B(cb), \
(tv) )
#endif

332
adns/parse.c Normal file
View file

@ -0,0 +1,332 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* parse.c
* - parsing assistance functions (mainly for domains inside datagrams)
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "internal.h"
int vbuf__append_quoted1035(vbuf * vb, const byte * buf, int len)
{
char qbuf[10];
int i, ch;
while (len) {
qbuf[0] = 0;
for (i = 0; i < len; i++) {
ch = buf[i];
if (ch <= ' ' || ch >= 127) {
sprintf(qbuf, "\\%03o", ch);
break;
} else if (!ctype_domainunquoted(ch)) {
sprintf(qbuf, "\\%c", ch);
break;
}
}
if (!adns__vbuf_append(vb, buf, i)
|| !adns__vbuf_append(vb, qbuf, strlen(qbuf)))
return 0;
if (i < len)
i++;
buf += i;
len -= i;
}
return 1;
}
void adns__findlabel_start(findlabel_state * fls, adns_state ads,
int serv, adns_query qu,
const byte * dgram, int dglen, int max,
int dmbegin, int *dmend_rlater)
{
fls->ads = ads;
fls->qu = qu;
fls->serv = serv;
fls->dgram = dgram;
fls->dglen = dglen;
fls->max = max;
fls->cbyte = dmbegin;
fls->namelen = 0;
fls->dmend_r = dmend_rlater;
}
adns_status adns__findlabel_next(findlabel_state * fls,
int *lablen_r, int *labstart_r)
{
int lablen, jumpto;
const char *dgram;
dgram = fls->dgram;
for (;;) {
if (fls->cbyte >= fls->dglen)
goto x_truncated;
if (fls->cbyte >= fls->max)
goto x_badresponse;
GET_B(fls->cbyte, lablen);
if (!(lablen & 0x0c0))
break;
if ((lablen & 0x0c0) != 0x0c0)
return adns_s_unknownformat;
if (fls->cbyte >= fls->dglen)
goto x_truncated;
if (fls->cbyte >= fls->max)
goto x_badresponse;
GET_B(fls->cbyte, jumpto);
jumpto |= (lablen & 0x3f) << 8;
if (fls->dmend_r)
*(fls->dmend_r) = fls->cbyte;
fls->cbyte = jumpto;
fls->dmend_r = 0;
fls->max = fls->dglen + 1;
}
if (labstart_r)
*labstart_r = fls->cbyte;
if (lablen) {
if (fls->namelen)
fls->namelen++;
fls->namelen += lablen;
if (fls->namelen > DNS_MAXDOMAIN)
return adns_s_answerdomaintoolong;
fls->cbyte += lablen;
if (fls->cbyte > fls->dglen)
goto x_truncated;
if (fls->cbyte > fls->max)
goto x_badresponse;
} else {
if (fls->dmend_r)
*(fls->dmend_r) = fls->cbyte;
}
*lablen_r = lablen;
return adns_s_ok;
x_truncated:
*lablen_r = -1;
return adns_s_ok;
x_badresponse:
adns__diag(fls->ads, fls->serv, fls->qu,
"label in domain runs beyond end of domain");
return adns_s_invalidresponse;
}
adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu,
vbuf * vb, adns_queryflags flags,
const byte * dgram, int dglen,
int *cbyte_io, int max)
{
findlabel_state fls;
adns__findlabel_start(&fls, ads, serv, qu, dgram, dglen, max,
*cbyte_io, cbyte_io);
vb->used = 0;
return adns__parse_domain_more(&fls, ads, qu, vb, flags, dgram);
}
adns_status adns__parse_domain_more(findlabel_state * fls, adns_state ads,
adns_query qu, vbuf * vb,
parsedomain_flags flags,
const byte * dgram)
{
int lablen, labstart, i, ch, first;
adns_status st;
first = 1;
for (;;) {
st = adns__findlabel_next(fls, &lablen, &labstart);
if (st)
return st;
if (lablen < 0) {
vb->used = 0;
return adns_s_ok;
}
if (!lablen)
break;
if (first) {
first = 0;
} else {
if (!adns__vbuf_append(vb, ".", 1))
return adns_s_nomemory;
}
if (flags & pdf_quoteok) {
if (!vbuf__append_quoted1035
(vb, dgram + labstart, lablen))
return adns_s_nomemory;
} else {
ch = dgram[labstart];
if (!ctype_alpha(ch) && !ctype_digit(ch))
return adns_s_answerdomaininvalid;
for (i = labstart + 1; i < labstart + lablen; i++) {
ch = dgram[i];
if (ch != '-' && !ctype_alpha(ch)
&& !ctype_digit(ch))
return adns_s_answerdomaininvalid;
}
if (!adns__vbuf_append
(vb, dgram + labstart, lablen))
return adns_s_nomemory;
}
}
if (!adns__vbuf_append(vb, "", 1))
return adns_s_nomemory;
return adns_s_ok;
}
adns_status adns__findrr_anychk(adns_query qu, int serv,
const byte * dgram, int dglen,
int *cbyte_io, int *type_r, int *class_r,
unsigned long *ttl_r, int *rdlen_r,
int *rdstart_r, const byte * eo_dgram,
int eo_dglen, int eo_cbyte,
int *eo_matched_r)
{
findlabel_state fls, eo_fls;
int cbyte;
int tmp, rdlen, mismatch;
unsigned long ttl;
int lablen, labstart, ch;
int eo_lablen, eo_labstart, eo_ch;
adns_status st;
cbyte = *cbyte_io;
adns__findlabel_start(&fls, qu->ads, serv, qu, dgram, dglen, dglen,
cbyte, &cbyte);
if (eo_dgram) {
adns__findlabel_start(&eo_fls, qu->ads, -1, 0, eo_dgram,
eo_dglen, eo_dglen, eo_cbyte, 0);
mismatch = 0;
} else {
mismatch = 1;
}
for (;;) {
st = adns__findlabel_next(&fls, &lablen, &labstart);
if (st)
return st;
if (lablen < 0)
goto x_truncated;
if (!mismatch) {
st = adns__findlabel_next(&eo_fls, &eo_lablen,
&eo_labstart);
assert(!st);
assert(eo_lablen >= 0);
if (lablen != eo_lablen)
mismatch = 1;
while (!mismatch && eo_lablen-- > 0) {
ch = dgram[labstart++];
if (ctype_alpha(ch))
ch &= ~32;
eo_ch = eo_dgram[eo_labstart++];
if (ctype_alpha(eo_ch))
eo_ch &= ~32;
if (ch != eo_ch)
mismatch = 1;
}
}
if (!lablen)
break;
}
if (eo_matched_r)
*eo_matched_r = !mismatch;
if (cbyte + 10 > dglen)
goto x_truncated;
GET_W(cbyte, tmp);
*type_r = tmp;
GET_W(cbyte, tmp);
*class_r = tmp;
GET_L(cbyte, ttl);
if (ttl > MAXTTLBELIEVE)
ttl = MAXTTLBELIEVE;
*ttl_r = ttl;
GET_W(cbyte, rdlen);
if (rdlen_r)
*rdlen_r = rdlen;
if (rdstart_r)
*rdstart_r = cbyte;
cbyte += rdlen;
if (cbyte > dglen)
goto x_truncated;
*cbyte_io = cbyte;
return adns_s_ok;
x_truncated:
*type_r = -1;
return 0;
}
adns_status adns__findrr(adns_query qu, int serv,
const byte * dgram, int dglen, int *cbyte_io,
int *type_r, int *class_r, unsigned long *ttl_r,
int *rdlen_r, int *rdstart_r,
int *ownermatchedquery_r)
{
if (!ownermatchedquery_r) {
return adns__findrr_anychk(qu, serv,
dgram, dglen, cbyte_io,
type_r, class_r, ttl_r, rdlen_r,
rdstart_r, 0, 0, 0, 0);
} else if (!qu->cname_dgram) {
return adns__findrr_anychk(qu, serv,
dgram, dglen, cbyte_io,
type_r, class_r, ttl_r, rdlen_r,
rdstart_r, qu->query_dgram,
qu->query_dglen, DNS_HDRSIZE,
ownermatchedquery_r);
} else {
return adns__findrr_anychk(qu, serv,
dgram, dglen, cbyte_io,
type_r, class_r, ttl_r, rdlen_r,
rdstart_r, qu->cname_dgram,
qu->cname_dglen,
qu->cname_begin,
ownermatchedquery_r);
}
}

684
adns/query.c Normal file
View file

@ -0,0 +1,684 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* query.c
* - overall query management (allocation, completion)
* - per-query memory management
* - query submission and cancellation (user-visible and internal)
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "internal.h"
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include "internal.h"
static adns_query query_alloc(adns_state ads, const typeinfo * typei,
adns_queryflags flags, struct timeval now)
{
/* Allocate a virgin query and return it. */
adns_query qu;
qu = malloc(sizeof(*qu));
if (!qu)
return 0;
qu->answer = malloc(sizeof(*qu->answer));
if (!qu->answer) {
free(qu);
return 0;
}
qu->ads = ads;
qu->state = query_tosend;
qu->back = qu->next = qu->parent = 0;
ALIST_INIT(qu->children);
ALINK_INIT(qu->siblings);
ALIST_INIT(qu->allocations);
qu->interim_allocd = 0;
qu->preserved_allocd = 0;
qu->final_allocspace = 0;
qu->typei = typei;
qu->query_dgram = 0;
qu->query_dglen = 0;
adns__vbuf_init(&qu->vb);
qu->cname_dgram = 0;
qu->cname_dglen = qu->cname_begin = 0;
adns__vbuf_init(&qu->search_vb);
qu->search_origlen = qu->search_pos = qu->search_doneabs = 0;
qu->id = -2; /* will be overwritten with real id before we leave adns */
qu->flags = flags;
qu->retries = 0;
qu->udpnextserver = 0;
qu->udpsent = 0;
timerclear(&qu->timeout);
qu->expires = now.tv_sec + MAXTTLBELIEVE;
memset(&qu->ctx, 0, sizeof(qu->ctx));
qu->answer->status = adns_s_ok;
qu->answer->cname = qu->answer->owner = 0;
qu->answer->type = typei->type;
qu->answer->expires = -1;
qu->answer->nrrs = 0;
qu->answer->rrs.untyped = 0;
qu->answer->rrsz = typei->rrsz;
return qu;
}
static void query_submit(adns_state ads, adns_query qu,
const typeinfo * typei, vbuf * qumsg_vb, int id,
adns_queryflags flags, struct timeval now)
{
/* Fills in the query message in for a previously-allocated query,
* and submits it. Cannot fail. Takes over the memory for qumsg_vb.
*/
qu->vb = *qumsg_vb;
adns__vbuf_init(qumsg_vb);
qu->query_dgram = malloc(qu->vb.used);
if (!qu->query_dgram) {
adns__query_fail(qu, adns_s_nomemory);
return;
}
qu->id = id;
qu->query_dglen = qu->vb.used;
memcpy(qu->query_dgram, qu->vb.buf, qu->vb.used);
adns__query_send(qu, now);
}
adns_status adns__internal_submit(adns_state ads, adns_query * query_r,
const typeinfo * typei, vbuf * qumsg_vb,
int id, adns_queryflags flags,
struct timeval now, const qcontext * ctx)
{
adns_query qu;
qu = query_alloc(ads, typei, flags, now);
if (!qu) {
adns__vbuf_free(qumsg_vb);
return adns_s_nomemory;
}
*query_r = qu;
memcpy(&qu->ctx, ctx, sizeof(qu->ctx));
query_submit(ads, qu, typei, qumsg_vb, id, flags, now);
return adns_s_ok;
}
static void query_simple(adns_state ads, adns_query qu,
const char *owner, int ol,
const typeinfo * typei, adns_queryflags flags,
struct timeval now)
{
vbuf vb_new;
int id;
adns_status stat;
stat = adns__mkquery(ads, &qu->vb, &id, owner, ol, typei, flags);
if (stat) {
if (stat == adns_s_querydomaintoolong
&& (flags & adns_qf_search)) {
adns__search_next(ads, qu, now);
return;
} else {
adns__query_fail(qu, stat);
return;
}
}
vb_new = qu->vb;
adns__vbuf_init(&qu->vb);
query_submit(ads, qu, typei, &vb_new, id, flags, now);
}
void adns__search_next(adns_state ads, adns_query qu, struct timeval now)
{
const char *nextentry;
adns_status stat;
if (qu->search_doneabs < 0) {
nextentry = 0;
qu->search_doneabs = 1;
} else {
if (qu->search_pos >= ads->nsearchlist) {
if (qu->search_doneabs) {
stat = adns_s_nxdomain;
goto x_fail;
return;
} else {
nextentry = 0;
qu->search_doneabs = 1;
}
} else {
nextentry = ads->searchlist[qu->search_pos++];
}
}
qu->search_vb.used = qu->search_origlen;
if (nextentry) {
if (!adns__vbuf_append(&qu->search_vb, ".", 1) ||
!adns__vbuf_appendstr(&qu->search_vb, nextentry)) {
stat = adns_s_nomemory;
goto x_fail;
}
}
free(qu->query_dgram);
qu->query_dgram = 0;
qu->query_dglen = 0;
query_simple(ads, qu, qu->search_vb.buf, qu->search_vb.used,
qu->typei, qu->flags, now);
return;
x_fail:
adns__query_fail(qu, stat);
}
static int save_owner(adns_query qu, const char *owner, int ol)
{
/* Returns 1 if OK, otherwise there was no memory. */
adns_answer *ans;
ans = qu->answer;
assert(!ans->owner);
ans->owner = adns__alloc_preserved(qu, ol + 1);
if (!ans->owner)
return 0;
memcpy(ans->owner, owner, ol);
ans->owner[ol] = 0;
return 1;
}
int adns_submit(adns_state ads,
const char *owner,
adns_rrtype type,
adns_queryflags flags, void *context, adns_query * query_r)
{
int r, ol, ndots;
adns_status stat;
const typeinfo *typei;
struct timeval now;
adns_query qu;
const char *p;
adns__consistency(ads, 0, cc_entex);
typei = adns__findtype(type);
if (!typei)
return ENOSYS;
r = gettimeofday(&now, 0);
if (r)
goto x_errno;
qu = query_alloc(ads, typei, flags, now);
if (!qu)
goto x_errno;
qu->ctx.ext = context;
qu->ctx.callback = 0;
memset(&qu->ctx.info, 0, sizeof(qu->ctx.info));
*query_r = qu;
ol = strlen(owner);
if (!ol) {
stat = adns_s_querydomaininvalid;
goto x_adnsfail;
}
if (ol > DNS_MAXDOMAIN + 1) {
stat = adns_s_querydomaintoolong;
goto x_adnsfail;
}
if (ol >= 1 && owner[ol - 1] == '.'
&& (ol < 2 || owner[ol - 2] != '\\')) {
flags &= ~adns_qf_search;
qu->flags = flags;
ol--;
}
if (flags & adns_qf_search) {
r = adns__vbuf_append(&qu->search_vb, owner, ol);
if (!r) {
stat = adns_s_nomemory;
goto x_adnsfail;
}
for (ndots = 0, p = owner; (p = strchr(p, '.'));
p++, ndots++);
qu->search_doneabs = (ndots >= ads->searchndots) ? -1 : 0;
qu->search_origlen = ol;
adns__search_next(ads, qu, now);
} else {
if (flags & adns_qf_owner) {
if (!save_owner(qu, owner, ol)) {
stat = adns_s_nomemory;
goto x_adnsfail;
}
}
query_simple(ads, qu, owner, ol, typei, flags, now);
}
adns__autosys(ads, now);
adns__consistency(ads, qu, cc_entex);
return 0;
x_adnsfail:
adns__query_fail(qu, stat);
adns__consistency(ads, qu, cc_entex);
return 0;
x_errno:
r = errno;
assert(r);
adns__consistency(ads, 0, cc_entex);
return r;
}
int adns_submit_reverse_any(adns_state ads,
const struct sockaddr *addr,
const char *zone,
adns_rrtype type,
adns_queryflags flags,
void *context, adns_query * query_r)
{
const unsigned char *iaddr;
char *buf, *buf_free;
char shortbuf[100];
int r, lreq;
flags &= ~adns_qf_search;
if (addr->sa_family != AF_INET)
return ENOSYS;
iaddr =
(const unsigned char *) &(((const struct sockaddr_in *) addr)->
sin_addr);
lreq = strlen(zone) + 4 * 4 + 1;
if (lreq > sizeof(shortbuf)) {
buf = malloc(strlen(zone) + 4 * 4 + 1);
if (!buf)
return errno;
buf_free = buf;
} else {
buf = shortbuf;
buf_free = 0;
}
sprintf(buf, "%d.%d.%d.%d.%s", iaddr[3], iaddr[2], iaddr[1],
iaddr[0], zone);
r = adns_submit(ads, buf, type, flags, context, query_r);
free(buf_free);
return r;
}
int adns_submit_reverse(adns_state ads,
const struct sockaddr *addr,
adns_rrtype type,
adns_queryflags flags,
void *context, adns_query * query_r)
{
if (type != adns_r_ptr && type != adns_r_ptr_raw)
return EINVAL;
return adns_submit_reverse_any(ads, addr, "in-addr.arpa", type,
flags, context, query_r);
}
int adns_synchronous(adns_state ads,
const char *owner,
adns_rrtype type,
adns_queryflags flags, adns_answer ** answer_r)
{
adns_query qu;
int r;
r = adns_submit(ads, owner, type, flags, 0, &qu);
if (r)
return r;
r = adns_wait(ads, &qu, answer_r, 0);
if (r)
adns_cancel(qu);
return r;
}
static void *alloc_common(adns_query qu, size_t sz)
{
allocnode *an;
if (!sz)
return qu; /* Any old pointer will do */
assert(!qu->final_allocspace);
an = malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
if (!an)
return 0;
LIST_LINK_TAIL(qu->allocations, an);
return (byte *) an + MEM_ROUND(sizeof(*an));
}
void *adns__alloc_interim(adns_query qu, size_t sz)
{
void *rv;
sz = MEM_ROUND(sz);
rv = alloc_common(qu, sz);
if (!rv)
return 0;
qu->interim_allocd += sz;
return rv;
}
void *adns__alloc_preserved(adns_query qu, size_t sz)
{
void *rv;
sz = MEM_ROUND(sz);
rv = adns__alloc_interim(qu, sz);
if (!rv)
return 0;
qu->preserved_allocd += sz;
return rv;
}
void *adns__alloc_mine(adns_query qu, size_t sz)
{
return alloc_common(qu, MEM_ROUND(sz));
}
void adns__transfer_interim(adns_query from, adns_query to, void *block,
size_t sz)
{
allocnode *an;
if (!block)
return;
an = (void *) ((byte *) block - MEM_ROUND(sizeof(*an)));
assert(!to->final_allocspace);
assert(!from->final_allocspace);
LIST_UNLINK(from->allocations, an);
LIST_LINK_TAIL(to->allocations, an);
sz = MEM_ROUND(sz);
from->interim_allocd -= sz;
to->interim_allocd += sz;
if (to->expires > from->expires)
to->expires = from->expires;
}
void *adns__alloc_final(adns_query qu, size_t sz)
{
/* When we're in the _final stage, we _subtract_ from interim_alloc'd
* each allocation, and use final_allocspace to point to the next free
* bit.
*/
void *rp;
sz = MEM_ROUND(sz);
rp = qu->final_allocspace;
assert(rp);
qu->interim_allocd -= sz;
assert(qu->interim_allocd >= 0);
qu->final_allocspace = (byte *) rp + sz;
return rp;
}
static void cancel_children(adns_query qu)
{
adns_query cqu, ncqu;
for (cqu = qu->children.head; cqu; cqu = ncqu) {
ncqu = cqu->siblings.next;
adns_cancel(cqu);
}
}
void adns__reset_preserved(adns_query qu)
{
assert(!qu->final_allocspace);
cancel_children(qu);
qu->answer->nrrs = 0;
qu->answer->rrs.untyped = 0;
qu->interim_allocd = qu->preserved_allocd;
}
static void free_query_allocs(adns_query qu)
{
allocnode *an, *ann;
cancel_children(qu);
for (an = qu->allocations.head; an; an = ann) {
ann = an->next;
free(an);
}
ALIST_INIT(qu->allocations);
adns__vbuf_free(&qu->vb);
adns__vbuf_free(&qu->search_vb);
free(qu->query_dgram);
qu->query_dgram = 0;
}
void adns_cancel(adns_query qu)
{
adns_state ads;
ads = qu->ads;
adns__consistency(ads, qu, cc_entex);
if (qu->parent)
LIST_UNLINK_PART(qu->parent->children, qu, siblings.);
switch (qu->state) {
case query_tosend:
LIST_UNLINK(ads->udpw, qu);
break;
case query_tcpw:
LIST_UNLINK(ads->tcpw, qu);
break;
case query_childw:
LIST_UNLINK(ads->childw, qu);
break;
case query_done:
LIST_UNLINK(ads->output, qu);
break;
default:
abort();
}
free_query_allocs(qu);
free(qu->answer);
free(qu);
adns__consistency(ads, 0, cc_entex);
}
void adns__update_expires(adns_query qu, unsigned long ttl,
struct timeval now)
{
time_t max;
assert(ttl <= MAXTTLBELIEVE);
max = now.tv_sec + ttl;
if (qu->expires < max)
return;
qu->expires = max;
}
static void makefinal_query(adns_query qu)
{
adns_answer *ans;
int rrn;
ans = qu->answer;
if (qu->interim_allocd) {
ans =
realloc(qu->answer,
MEM_ROUND(MEM_ROUND(sizeof(*ans)) +
qu->interim_allocd));
if (!ans)
goto x_nomem;
qu->answer = ans;
}
qu->final_allocspace = (byte *) ans + MEM_ROUND(sizeof(*ans));
adns__makefinal_str(qu, &ans->cname);
adns__makefinal_str(qu, &ans->owner);
if (ans->nrrs) {
adns__makefinal_block(qu, &ans->rrs.untyped,
ans->nrrs * ans->rrsz);
for (rrn = 0; rrn < ans->nrrs; rrn++)
qu->typei->makefinal(qu,
ans->rrs.bytes +
rrn * ans->rrsz);
}
free_query_allocs(qu);
return;
x_nomem:
qu->preserved_allocd = 0;
qu->answer->cname = 0;
qu->answer->owner = 0;
adns__reset_preserved(qu); /* (but we just threw away the preserved stuff) */
qu->answer->status = adns_s_nomemory;
free_query_allocs(qu);
}
void adns__query_done(adns_query qu)
{
adns_answer *ans;
adns_query parent;
cancel_children(qu);
qu->id = -1;
ans = qu->answer;
if (qu->flags & adns_qf_owner && qu->flags & adns_qf_search &&
ans->status != adns_s_nomemory) {
if (!save_owner(qu, qu->search_vb.buf, qu->search_vb.used)) {
adns__query_fail(qu, adns_s_nomemory);
return;
}
}
if (ans->nrrs && qu->typei->diff_needswap) {
if (!adns__vbuf_ensure(&qu->vb, qu->typei->rrsz)) {
adns__query_fail(qu, adns_s_nomemory);
return;
}
adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz,
qu->vb.buf,
(int (*)(void *, const void *, const void *))
qu->typei->diff_needswap, qu->ads);
}
ans->expires = qu->expires;
parent = qu->parent;
if (parent) {
LIST_UNLINK_PART(parent->children, qu, siblings.);
LIST_UNLINK(qu->ads->childw, parent);
qu->ctx.callback(parent, qu);
free_query_allocs(qu);
free(qu->answer);
free(qu);
} else {
makefinal_query(qu);
LIST_LINK_TAIL(qu->ads->output, qu);
qu->state = query_done;
}
}
void adns__query_fail(adns_query qu, adns_status stat)
{
adns__reset_preserved(qu);
qu->answer->status = stat;
adns__query_done(qu);
}
void adns__makefinal_str(adns_query qu, char **strp)
{
int l;
char *before, *after;
before = *strp;
if (!before)
return;
l = strlen(before) + 1;
after = adns__alloc_final(qu, l);
memcpy(after, before, l);
*strp = after;
}
void adns__makefinal_block(adns_query qu, void **blpp, size_t sz)
{
void *before, *after;
before = *blpp;
if (!before)
return;
after = adns__alloc_final(qu, sz);
memcpy(after, before, sz);
*blpp = after;
}

497
adns/reply.c Normal file
View file

@ -0,0 +1,497 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* reply.c
* - main handling and parsing routine for received datagrams
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include "internal.h"
void adns__procdgram(adns_state ads, const byte * dgram, int dglen,
int serv, int viatcp, struct timeval now)
{
int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here;
int id, f1, f2, qdcount, ancount, nscount, arcount;
int flg_ra, flg_rd, flg_tc, flg_qr, opcode;
int rrtype, rrclass, rdlength, rdstart;
int anstart, nsstart, arstart;
int ownermatched, l, nrrs;
unsigned long ttl, soattl;
const typeinfo *typei;
adns_query qu, nqu;
dns_rcode rcode;
adns_status st;
vbuf tempvb;
byte *newquery, *rrsdata;
parseinfo pai;
if (dglen < DNS_HDRSIZE) {
adns__diag(ads, serv, 0,
"received datagram too short for message header (%d)",
dglen);
return;
}
cbyte = 0;
GET_W(cbyte, id);
GET_B(cbyte, f1);
GET_B(cbyte, f2);
GET_W(cbyte, qdcount);
GET_W(cbyte, ancount);
GET_W(cbyte, nscount);
GET_W(cbyte, arcount);
assert(cbyte == DNS_HDRSIZE);
flg_qr = f1 & 0x80;
opcode = (f1 & 0x78) >> 3;
flg_tc = f1 & 0x02;
flg_rd = f1 & 0x01;
flg_ra = f2 & 0x80;
rcode = (f2 & 0x0f);
cname_here = 0;
if (!flg_qr) {
adns__diag(ads, serv, 0,
"server sent us a query, not a response");
return;
}
if (opcode) {
adns__diag(ads, serv, 0,
"server sent us unknown opcode %d (wanted 0=QUERY)",
opcode);
return;
}
qu = 0;
/* See if we can find the relevant query, or leave qu=0 otherwise ... */
if (qdcount == 1) {
for (qu = viatcp ? ads->tcpw.head : ads->udpw.head; qu;
qu = nqu) {
nqu = qu->next;
if (qu->id != id)
continue;
if (dglen < qu->query_dglen)
continue;
if (memcmp(qu->query_dgram + DNS_HDRSIZE,
dgram + DNS_HDRSIZE,
qu->query_dglen - DNS_HDRSIZE))
continue;
if (viatcp) {
assert(qu->state == query_tcpw);
} else {
assert(qu->state == query_tosend);
if (!(qu->udpsent & (1 << serv)))
continue;
}
break;
}
if (qu) {
/* We're definitely going to do something with this query now */
if (viatcp)
LIST_UNLINK(ads->tcpw, qu);
else
LIST_UNLINK(ads->udpw, qu);
}
}
/* If we're going to ignore the packet, we return as soon as we have
* failed the query (if any) and printed the warning message (if
* any).
*/
switch (rcode) {
case rcode_noerror:
case rcode_nxdomain:
break;
case rcode_formaterror:
adns__warn(ads, serv, qu,
"server cannot understand our query (Format Error)");
if (qu)
adns__query_fail(qu, adns_s_rcodeformaterror);
return;
case rcode_servfail:
if (qu)
adns__query_fail(qu, adns_s_rcodeservfail);
else
adns__debug(ads, serv, qu,
"server failure on unidentifiable query");
return;
case rcode_notimp:
adns__warn(ads, serv, qu,
"server claims not to implement our query");
if (qu)
adns__query_fail(qu, adns_s_rcodenotimplemented);
return;
case rcode_refused:
adns__debug(ads, serv, qu, "server refused our query");
if (qu)
adns__query_fail(qu, adns_s_rcoderefused);
return;
default:
adns__warn(ads, serv, qu,
"server gave unknown response code %d", rcode);
if (qu)
adns__query_fail(qu, adns_s_rcodeunknown);
return;
}
if (!qu) {
if (!qdcount) {
adns__diag(ads, serv, 0,
"server sent reply without quoting our question");
} else if (qdcount > 1) {
adns__diag(ads, serv, 0,
"server claimed to answer %d questions with one message",
qdcount);
} else if (ads->iflags & adns_if_debug) {
adns__vbuf_init(&tempvb);
adns__debug(ads, serv, 0,
"reply not found, id %02x, query owner %s",
id, adns__diag_domain(ads, serv, 0,
&tempvb, dgram,
dglen,
DNS_HDRSIZE));
adns__vbuf_free(&tempvb);
}
return;
}
/* We're definitely going to do something with this packet and this query now. */
anstart = qu->query_dglen;
arstart = -1;
/* Now, take a look at the answer section, and see if it is complete.
* If it has any CNAMEs we stuff them in the answer.
*/
wantedrrs = 0;
cbyte = anstart;
for (rri = 0; rri < ancount; rri++) {
rrstart = cbyte;
st = adns__findrr(qu, serv, dgram, dglen, &cbyte,
&rrtype, &rrclass, &ttl, &rdlength,
&rdstart, &ownermatched);
if (st) {
adns__query_fail(qu, st);
return;
}
if (rrtype == -1)
goto x_truncated;
if (rrclass != DNS_CLASS_IN) {
adns__diag(ads, serv, qu,
"ignoring answer RR with wrong class %d (expected IN=%d)",
rrclass, DNS_CLASS_IN);
continue;
}
if (!ownermatched) {
if (ads->iflags & adns_if_debug) {
adns__debug(ads, serv, qu,
"ignoring RR with an unexpected owner %s",
adns__diag_domain(ads, serv,
qu, &qu->vb,
dgram, dglen,
rrstart));
}
continue;
}
if (rrtype == adns_r_cname &&
(qu->typei->type & adns__rrt_typemask) !=
adns_r_cname) {
if (qu->flags & adns_qf_cname_forbid) {
adns__query_fail(qu,
adns_s_prohibitedcname);
return;
} else if (qu->cname_dgram) { /* Ignore second and subsequent CNAME(s) */
adns__debug(ads, serv, qu,
"allegedly canonical name %s is actually alias for %s",
qu->answer->cname,
adns__diag_domain(ads, serv,
qu, &qu->vb,
dgram, dglen,
rdstart));
adns__query_fail(qu,
adns_s_prohibitedcname);
return;
} else if (wantedrrs) { /* Ignore CNAME(s) after RR(s). */
adns__debug(ads, serv, qu,
"ignoring CNAME (to %s) coexisting with RR",
adns__diag_domain(ads, serv,
qu, &qu->vb,
dgram, dglen,
rdstart));
} else {
qu->cname_begin = rdstart;
qu->cname_dglen = dglen;
st = adns__parse_domain(ads, serv, qu,
&qu->vb,
qu->
flags &
adns_qf_quotefail_cname
? 0 : pdf_quoteok,
dgram, dglen,
&rdstart,
rdstart +
rdlength);
if (!qu->vb.used)
goto x_truncated;
if (st) {
adns__query_fail(qu, st);
return;
}
l = strlen(qu->vb.buf) + 1;
qu->answer->cname =
adns__alloc_preserved(qu, l);
if (!qu->answer->cname) {
adns__query_fail(qu,
adns_s_nomemory);
return;
}
qu->cname_dgram =
adns__alloc_mine(qu, dglen);
memcpy(qu->cname_dgram, dgram, dglen);
memcpy(qu->answer->cname, qu->vb.buf, l);
cname_here = 1;
adns__update_expires(qu, ttl, now);
/* If we find the answer section truncated after this point we restart
* the query at the CNAME; if beforehand then we obviously have to use
* TCP. If there is no truncation we can use the whole answer if
* it contains the relevant info.
*/
}
} else if (rrtype ==
(qu->typei->type & adns__rrt_typemask)) {
wantedrrs++;
} else {
adns__debug(ads, serv, qu,
"ignoring answer RR with irrelevant type %d",
rrtype);
}
}
/* We defer handling truncated responses here, in case there was a CNAME
* which we could use.
*/
if (flg_tc)
goto x_truncated;
nsstart = cbyte;
if (!wantedrrs) {
/* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */
/* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */
foundsoa = 0;
soattl = 0;
foundns = 0;
for (rri = 0; rri < nscount; rri++) {
rrstart = cbyte;
st = adns__findrr(qu, serv, dgram, dglen, &cbyte,
&rrtype, &rrclass, &ttl,
&rdlength, &rdstart, 0);
if (st) {
adns__query_fail(qu, st);
return;
}
if (rrtype == -1)
goto x_truncated;
if (rrclass != DNS_CLASS_IN) {
adns__diag(ads, serv, qu,
"ignoring authority RR with wrong class %d (expected IN=%d)",
rrclass, DNS_CLASS_IN);
continue;
}
if (rrtype == adns_r_soa_raw) {
foundsoa = 1;
soattl = ttl;
break;
} else if (rrtype == adns_r_ns_raw) {
foundns = 1;
}
}
if (rcode == rcode_nxdomain) {
/* We still wanted to look for the SOA so we could find the TTL. */
adns__update_expires(qu, soattl, now);
if (qu->flags & adns_qf_search) {
adns__search_next(ads, qu, now);
} else {
adns__query_fail(qu, adns_s_nxdomain);
}
return;
}
if (foundsoa || !foundns) {
/* Aha ! A NODATA response, good. */
adns__update_expires(qu, soattl, now);
adns__query_fail(qu, adns_s_nodata);
return;
}
/* Now what ? No relevant answers, no SOA, and at least some NS's.
* Looks like a referral. Just one last chance ... if we came across
* a CNAME in this datagram then we should probably do our own CNAME
* lookup now in the hope that we won't get a referral again.
*/
if (cname_here)
goto x_restartquery;
/* Bloody hell, I thought we asked for recursion ? */
if (!flg_ra) {
adns__diag(ads, serv, qu,
"server is not willing to do recursive lookups for us");
adns__query_fail(qu, adns_s_norecurse);
} else {
if (!flg_rd)
adns__diag(ads, serv, qu,
"server thinks we didn't ask for recursive lookup");
else
adns__debug(ads, serv, qu,
"server claims to do recursion, but gave us a referral");
adns__query_fail(qu, adns_s_invalidresponse);
}
return;
}
/* Now, we have some RRs which we wanted. */
qu->answer->rrs.untyped =
adns__alloc_interim(qu, qu->typei->rrsz * wantedrrs);
if (!qu->answer->rrs.untyped) {
adns__query_fail(qu, adns_s_nomemory);
return;
}
typei = qu->typei;
cbyte = anstart;
rrsdata = qu->answer->rrs.bytes;
pai.ads = qu->ads;
pai.qu = qu;
pai.serv = serv;
pai.dgram = dgram;
pai.dglen = dglen;
pai.nsstart = nsstart;
pai.nscount = nscount;
pai.arcount = arcount;
pai.now = now;
for (rri = 0, nrrs = 0; rri < ancount; rri++) {
st = adns__findrr(qu, serv, dgram, dglen, &cbyte,
&rrtype, &rrclass, &ttl, &rdlength,
&rdstart, &ownermatched);
assert(!st);
assert(rrtype != -1);
if (rrclass != DNS_CLASS_IN ||
rrtype != (qu->typei->type & adns__rrt_typemask) ||
!ownermatched)
continue;
adns__update_expires(qu, ttl, now);
st = typei->parse(&pai, rdstart, rdstart + rdlength,
rrsdata + nrrs * typei->rrsz);
if (st) {
adns__query_fail(qu, st);
return;
}
if (rdstart == -1)
goto x_truncated;
nrrs++;
}
assert(nrrs == wantedrrs);
qu->answer->nrrs = nrrs;
/* This may have generated some child queries ... */
if (qu->children.head) {
qu->state = query_childw;
LIST_LINK_TAIL(ads->childw, qu);
return;
}
adns__query_done(qu);
return;
x_truncated:
if (!flg_tc) {
adns__diag(ads, serv, qu,
"server sent datagram which points outside itself");
adns__query_fail(qu, adns_s_invalidresponse);
return;
}
qu->flags |= adns_qf_usevc;
x_restartquery:
if (qu->cname_dgram) {
st = adns__mkquery_frdgram(qu->ads, &qu->vb, &qu->id,
qu->cname_dgram,
qu->cname_dglen,
qu->cname_begin,
qu->typei->type, qu->flags);
if (st) {
adns__query_fail(qu, st);
return;
}
newquery = realloc(qu->query_dgram, qu->vb.used);
if (!newquery) {
adns__query_fail(qu, adns_s_nomemory);
return;
}
qu->query_dgram = newquery;
qu->query_dglen = qu->vb.used;
memcpy(newquery, qu->vb.buf, qu->vb.used);
}
if (qu->state == query_tcpw)
qu->state = query_tosend;
qu->retries = 0;
adns__reset_preserved(qu);
adns__query_send(qu, now);
}

825
adns/setup.c Normal file
View file

@ -0,0 +1,825 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* setup.c
* - configuration file parsing
* - management of global state
*/
/*
* This file is
* Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "internal.h"
static void readconfig(adns_state ads, const char *filename,
int warnmissing);
static void addserver(adns_state ads, struct in_addr addr)
{
int i;
struct server *ss;
for (i = 0; i < ads->nservers; i++) {
if (ads->servers[i].addr.s_addr == addr.s_addr) {
adns__debug(ads, -1, 0,
"duplicate nameserver %s ignored",
inet_ntoa(addr));
return;
}
}
if (ads->nservers >= MAXSERVERS) {
adns__diag(ads, -1, 0, "too many nameservers, ignoring %s",
inet_ntoa(addr));
return;
}
ss = ads->servers + ads->nservers;
ss->addr = addr;
ads->nservers++;
}
static void freesearchlist(adns_state ads)
{
if (ads->nsearchlist)
free(*ads->searchlist);
free(ads->searchlist);
}
static void saveerr(adns_state ads, int en)
{
if (!ads->configerrno)
ads->configerrno = en;
}
static void configparseerr(adns_state ads, const char *fn, int lno,
const char *fmt, ...)
{
va_list al;
saveerr(ads, EINVAL);
if (!ads->diagfile || (ads->iflags & adns_if_noerrprint))
return;
if (lno == -1)
fprintf(ads->diagfile, "adns: %s: ", fn);
else
fprintf(ads->diagfile, "adns: %s:%d: ", fn, lno);
va_start(al, fmt);
vfprintf(ads->diagfile, fmt, al);
va_end(al);
fputc('\n', ads->diagfile);
}
static int nextword(const char **bufp_io, const char **word_r, int *l_r)
{
const char *p, *q;
p = *bufp_io;
while (ctype_whitespace(*p))
p++;
if (!*p)
return 0;
q = p;
while (*q && !ctype_whitespace(*q))
q++;
*l_r = q - p;
*word_r = p;
*bufp_io = q;
return 1;
}
static void ccf_nameserver(adns_state ads, const char *fn, int lno,
const char *buf)
{
struct in_addr ia;
if (!inet_aton(buf, &ia)) {
configparseerr(ads, fn, lno,
"invalid nameserver address `%s'", buf);
return;
}
adns__debug(ads, -1, 0, "using nameserver %s", inet_ntoa(ia));
addserver(ads, ia);
}
static void ccf_search(adns_state ads, const char *fn, int lno,
const char *buf)
{
const char *bufp, *word;
char *newchars, **newptrs, **pp;
int count, tl, l;
if (!buf)
return;
bufp = buf;
count = 0;
tl = 0;
while (nextword(&bufp, &word, &l)) {
count++;
tl += l + 1;
}
newptrs = malloc(sizeof(char *) * count);
if (!newptrs) {
saveerr(ads, errno);
return;
}
newchars = malloc(tl);
if (!newchars) {
saveerr(ads, errno);
free(newptrs);
return;
}
bufp = buf;
pp = newptrs;
while (nextword(&bufp, &word, &l)) {
*pp++ = newchars;
memcpy(newchars, word, l);
newchars += l;
*newchars++ = 0;
}
freesearchlist(ads);
ads->nsearchlist = count;
ads->searchlist = newptrs;
}
static void ccf_sortlist(adns_state ads, const char *fn, int lno,
const char *buf)
{
const char *word;
char tbuf[200], *slash, *ep;
struct in_addr base, mask;
int l;
unsigned long initial, baselocal;
if (!buf)
return;
ads->nsortlist = 0;
while (nextword(&buf, &word, &l)) {
if (ads->nsortlist >= MAXSORTLIST) {
adns__diag(ads, -1, 0,
"too many sortlist entries, ignoring %.*s onwards",
l, word);
return;
}
if (l >= sizeof(tbuf)) {
configparseerr(ads, fn, lno,
"sortlist entry `%.*s' too long", l,
word);
continue;
}
memcpy(tbuf, word, l);
tbuf[l] = 0;
slash = strchr(tbuf, '/');
if (slash)
*slash++ = 0;
if (!inet_aton(tbuf, &base)) {
configparseerr(ads, fn, lno,
"invalid address `%s' in sortlist",
tbuf);
continue;
}
if (slash) {
if (strchr(slash, '.')) {
if (!inet_aton(slash, &mask)) {
configparseerr(ads, fn, lno,
"invalid mask `%s' in sortlist",
slash);
continue;
}
if (base.s_addr & ~mask.s_addr) {
configparseerr(ads, fn, lno,
"mask `%s' in sortlist overlaps address `%s'",
slash, tbuf);
continue;
}
} else {
initial = strtoul(slash, &ep, 10);
if (*ep || initial > 32) {
configparseerr(ads, fn, lno,
"mask length `%s' invalid",
slash);
continue;
}
mask.s_addr =
htonl((0x0ffffffffUL) <<
(32 - initial));
}
} else {
baselocal = ntohl(base.s_addr);
if (!baselocal & 0x080000000UL) /* class A */
mask.s_addr = htonl(0x0ff000000UL);
else if ((baselocal & 0x0c0000000UL) ==
0x080000000UL)
mask.s_addr = htonl(0x0ffff0000UL); /* class B */
else if ((baselocal & 0x0f0000000UL) ==
0x0e0000000UL)
mask.s_addr = htonl(0x0ff000000UL); /* class C */
else {
configparseerr(ads, fn, lno,
"network address `%s' in sortlist is not in classed ranges,"
" must specify mask explicitly",
tbuf);
continue;
}
}
ads->sortlist[ads->nsortlist].base = base;
ads->sortlist[ads->nsortlist].mask = mask;
ads->nsortlist++;
}
}
static void ccf_options(adns_state ads, const char *fn, int lno,
const char *buf)
{
const char *word;
char *ep;
unsigned long v;
int l;
if (!buf)
return;
while (nextword(&buf, &word, &l)) {
if (l == 5 && !memcmp(word, "debug", 5)) {
ads->iflags |= adns_if_debug;
continue;
}
if (l >= 6 && !memcmp(word, "ndots:", 6)) {
v = strtoul(word + 6, &ep, 10);
if (l == 6 || ep != word + l || v > INT_MAX) {
configparseerr(ads, fn, lno,
"option `%.*s' malformed or has bad value",
l, word);
continue;
}
ads->searchndots = v;
continue;
}
if (l >= 12 && !memcmp(word, "adns_checkc:", 12)) {
if (!strcmp(word + 12, "none")) {
ads->iflags &= ~adns_if_checkc_freq;
ads->iflags |= adns_if_checkc_entex;
} else if (!strcmp(word + 12, "entex")) {
ads->iflags &= ~adns_if_checkc_freq;
ads->iflags |= adns_if_checkc_entex;
} else if (!strcmp(word + 12, "freq")) {
ads->iflags |= adns_if_checkc_freq;
} else {
configparseerr(ads, fn, lno,
"option adns_checkc has bad value `%s' "
"(must be none, entex or freq",
word + 12);
}
continue;
}
adns__diag(ads, -1, 0, "%s:%d: unknown option `%.*s'", fn,
lno, l, word);
}
}
static void ccf_clearnss(adns_state ads, const char *fn, int lno,
const char *buf)
{
ads->nservers = 0;
}
static void ccf_include(adns_state ads, const char *fn, int lno,
const char *buf)
{
if (!*buf) {
configparseerr(ads, fn, lno,
"`include' directive with no filename");
return;
}
readconfig(ads, buf, 1);
}
static const struct configcommandinfo {
const char *name;
void (*fn) (adns_state ads, const char *fn, int lno,
const char *buf);
} configcommandinfos[] = {
{
"nameserver", ccf_nameserver}, {
"domain", ccf_search}, {
"search", ccf_search}, {
"sortlist", ccf_sortlist}, {
"options", ccf_options}, {
"clearnameservers", ccf_clearnss}, {
"include", ccf_include}, {
0}
};
typedef union {
FILE *file;
const char *text;
} getline_ctx;
static int gl_file(adns_state ads, getline_ctx * src_io,
const char *filename, int lno, char *buf, int buflen)
{
FILE *file = src_io->file;
int c, i;
char *p;
p = buf;
buflen--;
i = 0;
for (;;) { /* loop over chars */
if (i == buflen) {
adns__diag(ads, -1, 0,
"%s:%d: line too long, ignored",
filename, lno);
goto x_badline;
}
c = getc(file);
if (!c) {
adns__diag(ads, -1, 0,
"%s:%d: line contains nul, ignored",
filename, lno);
goto x_badline;
} else if (c == '\n') {
break;
} else if (c == EOF) {
if (ferror(file)) {
saveerr(ads, errno);
adns__diag(ads, -1, 0,
"%s:%d: read error: %s",
filename, lno, strerror(errno));
return -1;
}
if (!i)
return -1;
break;
} else {
*p++ = c;
i++;
}
}
*p++ = 0;
return i;
x_badline:
saveerr(ads, EINVAL);
while ((c = getc(file)) != EOF && c != '\n');
return -2;
}
static int gl_text(adns_state ads, getline_ctx * src_io,
const char *filename, int lno, char *buf, int buflen)
{
const char *cp = src_io->text;
int l;
if (!cp || !*cp)
return -1;
if (*cp == ';' || *cp == '\n')
cp++;
l = strcspn(cp, ";\n");
src_io->text = cp + l;
if (l >= buflen) {
adns__diag(ads, -1, 0, "%s:%d: line too long, ignored",
filename, lno);
saveerr(ads, EINVAL);
return -2;
}
memcpy(buf, cp, l);
buf[l] = 0;
return l;
}
static void readconfiggeneric(adns_state ads, const char *filename,
int (*getline) (adns_state ads,
getline_ctx *,
const char *filename,
int lno, char *buf,
int buflen),
/* Returns >=0 for success, -1 for EOF or error
* (error will have been reported), or -2 for
* bad line was encountered, try again.
*/
getline_ctx gl_ctx)
{
char linebuf[2000], *p, *q;
int lno, l, dirl;
const struct configcommandinfo *ccip;
for (lno = 1;
(l =
getline(ads, &gl_ctx, filename, lno, linebuf,
sizeof(linebuf))) != -1; lno++) {
if (l == -2)
continue;
while (l > 0 && ctype_whitespace(linebuf[l - 1]))
l--;
linebuf[l] = 0;
p = linebuf;
while (ctype_whitespace(*p))
p++;
if (*p == '#' || !*p)
continue;
q = p;
while (*q && !ctype_whitespace(*q))
q++;
dirl = q - p;
for (ccip = configcommandinfos;
ccip->name && !(strlen(ccip->name) == dirl
&& !memcmp(ccip->name, p, q - p));
ccip++);
if (!ccip->name) {
adns__diag(ads, -1, 0,
"%s:%d: unknown configuration directive `%.*s'",
filename, lno, q - p, p);
continue;
}
while (ctype_whitespace(*q))
q++;
ccip->fn(ads, filename, lno, q);
}
}
static const char *instrum_getenv(adns_state ads, const char *envvar)
{
const char *value;
value = getenv(envvar);
if (!value)
adns__debug(ads, -1, 0, "environment variable %s not set",
envvar);
else
adns__debug(ads, -1, 0,
"environment variable %s set to `%s'", envvar,
value);
return value;
}
static void readconfig(adns_state ads, const char *filename,
int warnmissing)
{
getline_ctx gl_ctx;
gl_ctx.file = fopen(filename, "r");
if (!gl_ctx.file) {
if (errno == ENOENT) {
if (warnmissing)
adns__debug(ads, -1, 0,
"configuration file `%s' does not exist",
filename);
return;
}
saveerr(ads, errno);
adns__diag(ads, -1, 0,
"cannot open configuration file `%s': %s",
filename, strerror(errno));
return;
}
readconfiggeneric(ads, filename, gl_file, gl_ctx);
fclose(gl_ctx.file);
}
static void readconfigtext(adns_state ads, const char *text,
const char *showname)
{
getline_ctx gl_ctx;
gl_ctx.text = text;
readconfiggeneric(ads, showname, gl_text, gl_ctx);
}
static void readconfigenv(adns_state ads, const char *envvar)
{
const char *filename;
if (ads->iflags & adns_if_noenv) {
adns__debug(ads, -1, 0,
"not checking environment variable `%s'",
envvar);
return;
}
filename = instrum_getenv(ads, envvar);
if (filename)
readconfig(ads, filename, 1);
}
static void readconfigenvtext(adns_state ads, const char *envvar)
{
const char *textdata;
if (ads->iflags & adns_if_noenv) {
adns__debug(ads, -1, 0,
"not checking environment variable `%s'",
envvar);
return;
}
textdata = instrum_getenv(ads, envvar);
if (textdata)
readconfigtext(ads, textdata, envvar);
}
int adns__setnonblock(adns_state ads, int fd)
{
int r;
r = fcntl(fd, F_GETFL, 0);
if (r < 0)
return errno;
r |= O_NONBLOCK;
r = fcntl(fd, F_SETFL, r);
if (r < 0)
return errno;
return 0;
}
static int init_begin(adns_state * ads_r, adns_initflags flags,
FILE * diagfile)
{
adns_state ads;
ads = malloc(sizeof(*ads));
if (!ads)
return errno;
ads->iflags = flags;
ads->diagfile = diagfile;
ads->configerrno = 0;
ALIST_INIT(ads->udpw);
ALIST_INIT(ads->tcpw);
ALIST_INIT(ads->childw);
ALIST_INIT(ads->output);
ads->forallnext = 0;
ads->nextid = 0x311f;
ads->udpsocket = ads->tcpsocket = -1;
adns__vbuf_init(&ads->tcpsend);
adns__vbuf_init(&ads->tcprecv);
ads->tcprecv_skip = 0;
ads->nservers = ads->nsortlist = ads->nsearchlist =
ads->tcpserver = 0;
ads->searchndots = 1;
ads->tcpstate = server_disconnected;
timerclear(&ads->tcptimeout);
ads->searchlist = 0;
*ads_r = ads;
return 0;
}
static int init_finish(adns_state ads)
{
struct in_addr ia;
struct protoent *proto;
int r;
if (!ads->nservers) {
if (ads->diagfile && ads->iflags & adns_if_debug)
fprintf(ads->diagfile,
"adns: no nameservers, using localhost\n");
ia.s_addr = htonl(INADDR_LOOPBACK);
addserver(ads, ia);
}
proto = getprotobyname("udp");
if (!proto) {
r = ENOPROTOOPT;
goto x_free;
}
ads->udpsocket = socket(AF_INET, SOCK_DGRAM, proto->p_proto);
if (ads->udpsocket < 0) {
r = errno;
goto x_free;
}
r = adns__setnonblock(ads, ads->udpsocket);
if (r) {
r = errno;
goto x_closeudp;
}
return 0;
x_closeudp:
close(ads->udpsocket);
x_free:
free(ads);
return r;
}
static void init_abort(adns_state ads)
{
if (ads->nsearchlist) {
free(ads->searchlist[0]);
free(ads->searchlist);
}
free(ads);
}
int adns_init(adns_state * ads_r, adns_initflags flags, FILE * diagfile)
{
adns_state ads;
const char *res_options, *adns_res_options;
int r;
r = init_begin(&ads, flags, diagfile ? diagfile : stderr);
if (r)
return r;
res_options = instrum_getenv(ads, "RES_OPTIONS");
adns_res_options = instrum_getenv(ads, "ADNS_RES_OPTIONS");
ccf_options(ads, "RES_OPTIONS", -1, res_options);
ccf_options(ads, "ADNS_RES_OPTIONS", -1, adns_res_options);
readconfig(ads, "/etc/resolv.conf", 1);
readconfig(ads, "/etc/resolv-adns.conf", 0);
readconfigenv(ads, "RES_CONF");
readconfigenv(ads, "ADNS_RES_CONF");
readconfigenvtext(ads, "RES_CONF_TEXT");
readconfigenvtext(ads, "ADNS_RES_CONF_TEXT");
ccf_options(ads, "RES_OPTIONS", -1, res_options);
ccf_options(ads, "ADNS_RES_OPTIONS", -1, adns_res_options);
ccf_search(ads, "LOCALDOMAIN", -1,
instrum_getenv(ads, "LOCALDOMAIN"));
ccf_search(ads, "ADNS_LOCALDOMAIN", -1,
instrum_getenv(ads, "ADNS_LOCALDOMAIN"));
if (ads->configerrno && ads->configerrno != EINVAL) {
r = ads->configerrno;
init_abort(ads);
return r;
}
r = init_finish(ads);
if (r)
return r;
adns__consistency(ads, 0, cc_entex);
*ads_r = ads;
return 0;
}
int adns_init_strcfg(adns_state * ads_r, adns_initflags flags,
FILE * diagfile, const char *configtext)
{
adns_state ads;
int r;
r = init_begin(&ads, flags, diagfile);
if (r)
return r;
readconfigtext(ads, configtext, "<supplied configuration text>");
if (ads->configerrno) {
r = ads->configerrno;
init_abort(ads);
return r;
}
r = init_finish(ads);
if (r)
return r;
adns__consistency(ads, 0, cc_entex);
*ads_r = ads;
return 0;
}
void adns_finish(adns_state ads)
{
adns__consistency(ads, 0, cc_entex);
for (;;) {
if (ads->udpw.head)
adns_cancel(ads->udpw.head);
else if (ads->tcpw.head)
adns_cancel(ads->tcpw.head);
else if (ads->childw.head)
adns_cancel(ads->childw.head);
else if (ads->output.head)
adns_cancel(ads->output.head);
else
break;
}
close(ads->udpsocket);
if (ads->tcpsocket >= 0)
close(ads->tcpsocket);
adns__vbuf_free(&ads->tcpsend);
adns__vbuf_free(&ads->tcprecv);
freesearchlist(ads);
free(ads);
}
void adns_forallqueries_begin(adns_state ads)
{
adns__consistency(ads, 0, cc_entex);
ads->forallnext =
ads->udpw.head ? ads->udpw.head :
ads->tcpw.head ? ads->tcpw.head :
ads->childw.head ? ads->childw.head : ads->output.head;
}
adns_query adns_forallqueries_next(adns_state ads, void **context_r)
{
adns_query qu, nqu;
adns__consistency(ads, 0, cc_entex);
nqu = ads->forallnext;
for (;;) {
qu = nqu;
if (!qu)
return 0;
if (qu->next) {
nqu = qu->next;
} else if (qu == ads->udpw.tail) {
nqu =
ads->tcpw.head ? ads->tcpw.head :
ads->childw.head ? ads->childw.head :
ads->output.head;
} else if (qu == ads->tcpw.tail) {
nqu =
ads->childw.head ? ads->childw.head :
ads->output.head;
} else if (qu == ads->childw.tail) {
nqu = ads->output.head;
} else {
nqu = 0;
}
if (!qu->parent)
break;
}
ads->forallnext = nqu;
if (context_r)
*context_r = qu->ctx.ext;
return qu;
}

330
adns/transmit.c Normal file
View file

@ -0,0 +1,330 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* transmit.c
* - construct queries
* - send queries
*/
/*
* This file is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "internal.h"
#include "tvarith.h"
#define MKQUERY_START(vb) (rqp= (vb)->buf+(vb)->used)
#define MKQUERY_ADDB(b) *rqp++= (b)
#define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
#define MKQUERY_STOP(vb) ((vb)->used= rqp-(vb)->buf)
static adns_status mkquery_header(adns_state ads, vbuf * vb, int *id_r,
int qdlen)
{
int id;
byte *rqp;
if (!adns__vbuf_ensure(vb, DNS_HDRSIZE + qdlen + 4))
return adns_s_nomemory;
vb->used = 0;
MKQUERY_START(vb);
*id_r = id = (ads->nextid++) & 0x0ffff;
MKQUERY_ADDW(id);
MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
MKQUERY_ADDW(1); /* QDCOUNT=1 */
MKQUERY_ADDW(0); /* ANCOUNT=0 */
MKQUERY_ADDW(0); /* NSCOUNT=0 */
MKQUERY_ADDW(0); /* ARCOUNT=0 */
MKQUERY_STOP(vb);
return adns_s_ok;
}
static adns_status mkquery_footer(vbuf * vb, adns_rrtype type)
{
byte *rqp;
MKQUERY_START(vb);
MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */
MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */
MKQUERY_STOP(vb);
assert(vb->used <= vb->avail);
return adns_s_ok;
}
adns_status adns__mkquery(adns_state ads, vbuf * vb, int *id_r,
const char *owner, int ol,
const typeinfo * typei, adns_queryflags flags)
{
int ll, c, nbytes;
byte label[255], *rqp;
const char *p, *pe;
adns_status st;
st = mkquery_header(ads, vb, id_r, ol + 2);
if (st)
return st;
MKQUERY_START(vb);
p = owner;
pe = owner + ol;
nbytes = 0;
while (p != pe) {
ll = 0;
while (p != pe && (c = *p++) != '.') {
if (c == '\\') {
if (!(flags & adns_qf_quoteok_query))
return adns_s_querydomaininvalid;
if (ctype_digit(p[0])) {
if (ctype_digit(p[1])
&& ctype_digit(p[2])) {
c = (p[0] - '0') * 100 +
(p[1] - '0') * 10 +
(p[2] - '0');
p += 3;
if (c >= 256)
return
adns_s_querydomaininvalid;
} else {
return
adns_s_querydomaininvalid;
}
} else if (!(c = *p++)) {
return adns_s_querydomaininvalid;
}
}
if (!(flags & adns_qf_quoteok_query)) {
if (c == '-') {
if (!ll)
return
adns_s_querydomaininvalid;
} else if (!ctype_alpha(c)
&& !ctype_digit(c)) {
return adns_s_querydomaininvalid;
}
}
if (ll == sizeof(label))
return adns_s_querydomaininvalid;
label[ll++] = c;
}
if (!ll)
return adns_s_querydomaininvalid;
if (ll > DNS_MAXLABEL)
return adns_s_querydomaintoolong;
nbytes += ll + 1;
if (nbytes >= DNS_MAXDOMAIN)
return adns_s_querydomaintoolong;
MKQUERY_ADDB(ll);
memcpy(rqp, label, ll);
rqp += ll;
}
MKQUERY_ADDB(0);
MKQUERY_STOP(vb);
st = mkquery_footer(vb, typei->type);
return adns_s_ok;
}
adns_status adns__mkquery_frdgram(adns_state ads, vbuf * vb, int *id_r,
const byte * qd_dgram, int qd_dglen,
int qd_begin, adns_rrtype type,
adns_queryflags flags)
{
byte *rqp;
findlabel_state fls;
int lablen, labstart;
adns_status st;
st = mkquery_header(ads, vb, id_r, qd_dglen);
if (st)
return st;
MKQUERY_START(vb);
adns__findlabel_start(&fls, ads, -1, 0, qd_dgram, qd_dglen,
qd_dglen, qd_begin, 0);
for (;;) {
st = adns__findlabel_next(&fls, &lablen, &labstart);
assert(!st);
if (!lablen)
break;
assert(lablen < 255);
MKQUERY_ADDB(lablen);
memcpy(rqp, qd_dgram + labstart, lablen);
rqp += lablen;
}
MKQUERY_ADDB(0);
MKQUERY_STOP(vb);
st = mkquery_footer(vb, type);
return adns_s_ok;
}
void adns__querysend_tcp(adns_query qu, struct timeval now)
{
byte length[2];
struct iovec iov[2];
int wr, r;
adns_state ads;
if (qu->ads->tcpstate != server_ok)
return;
assert(qu->state == query_tcpw);
length[0] = (qu->query_dglen & 0x0ff00U) >> 8;
length[1] = (qu->query_dglen & 0x0ff);
ads = qu->ads;
if (!adns__vbuf_ensure
(&ads->tcpsend, ads->tcpsend.used + qu->query_dglen + 2))
return;
qu->retries++;
/* Reset idle timeout. */
ads->tcptimeout.tv_sec = ads->tcptimeout.tv_usec = 0;
if (ads->tcpsend.used) {
wr = 0;
} else {
iov[0].iov_base = length;
iov[0].iov_len = 2;
iov[1].iov_base = qu->query_dgram;
iov[1].iov_len = qu->query_dglen;
adns__sigpipe_protect(qu->ads);
wr = writev(qu->ads->tcpsocket, iov, 2);
adns__sigpipe_unprotect(qu->ads);
if (wr < 0) {
if (!
(errno == EAGAIN || errno == EINTR
|| errno == ENOSPC || errno == ENOBUFS
|| errno == ENOMEM)) {
adns__tcp_broken(ads, "write",
strerror(errno));
return;
}
wr = 0;
}
}
if (wr < 2) {
r = adns__vbuf_append(&ads->tcpsend, length, 2 - wr);
assert(r);
wr = 0;
} else {
wr -= 2;
}
if (wr < qu->query_dglen) {
r = adns__vbuf_append(&ads->tcpsend, qu->query_dgram + wr,
qu->query_dglen - wr);
assert(r);
}
}
static void query_usetcp(adns_query qu, struct timeval now)
{
qu->state = query_tcpw;
qu->timeout = now;
timevaladd(&qu->timeout, TCPWAITMS);
LIST_LINK_TAIL(qu->ads->tcpw, qu);
adns__querysend_tcp(qu, now);
adns__tcp_tryconnect(qu->ads, now);
}
void adns__query_send(adns_query qu, struct timeval now)
{
struct sockaddr_in servaddr;
int serv, r;
adns_state ads;
assert(qu->state == query_tosend);
if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) {
query_usetcp(qu, now);
return;
}
if (qu->retries >= UDPMAXRETRIES) {
adns__query_fail(qu, adns_s_timeout);
return;
}
serv = qu->udpnextserver;
memset(&servaddr, 0, sizeof(servaddr));
ads = qu->ads;
servaddr.sin_family = AF_INET;
servaddr.sin_addr = ads->servers[serv].addr;
servaddr.sin_port = htons(DNS_PORT);
r = sendto(ads->udpsocket, qu->query_dgram, qu->query_dglen, 0,
(const struct sockaddr *) &servaddr, sizeof(servaddr));
if (r < 0 && errno == EMSGSIZE) {
qu->retries = 0;
query_usetcp(qu, now);
return;
}
if (r < 0 && errno != EAGAIN)
adns__warn(ads, serv, 0, "sendto failed: %s",
strerror(errno));
qu->timeout = now;
timevaladd(&qu->timeout, UDPRETRYMS);
qu->udpsent |= (1 << serv);
qu->udpnextserver = (serv + 1) % ads->nservers;
qu->retries++;
LIST_LINK_TAIL(ads->udpw, qu);
}

67
adns/tvarith.h Normal file
View file

@ -0,0 +1,67 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/*
* tvarith.h
* - static inline functions for doing arithmetic on timevals
*/
/*
* This file is
* Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
*
* It is part of adns, which is
* Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
*
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ADNS_TVARITH_H_INCLUDED
#define ADNS_TVARITH_H_INCLUDED
static inline void timevaladd(struct timeval *tv_io, long ms)
{
struct timeval tmp;
assert(ms >= 0);
tmp = *tv_io;
tmp.tv_usec += (ms % 1000) * 1000;
tmp.tv_sec += ms / 1000;
if (tmp.tv_usec >= 1000000) {
tmp.tv_sec++;
tmp.tv_usec -= 1000000;
}
*tv_io = tmp;
}
#endif

1281
adns/types.c Normal file

File diff suppressed because it is too large Load diff

101
client.c Normal file
View file

@ -0,0 +1,101 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** Portions Copyright (c) 1999 Johnathan George net@lite.net
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "defines.h"
#include "adns.h"
#include "conf.h"
#include "log.h"
#include "client.h"
#include "list.h"
list_t *clientlist;
int init_client_list() {
clientlist = list_create(-1);
if (!clientlist) {
nlog(LOG_WARNING, LOG_CORE, "Warning, Client List create Failed");
return NS_FAILURE;
} else {
return NS_SUCCESS;
}
}
mqclient *new_client(int fd) {
mqclient *mqc;
lnode_t *node;
if (list_isfull(clientlist)) {
nlog(LOG_WARNING, LOG_CORE, "Client List Full. Bye Bye");
do_exit(NS_EXIT_ERROR, "Client List Full");
}
mqc = malloc(sizeof(mqclient));
bzero(mqc, sizeof(mqclient));
mqc->type = MQC_TYPE_NONE;
mqc->fd = fd;
node = lnode_create(mqc);
mqc->node = node;
list_append(clientlist, node);
nlog(LOG_DEBUG3, LOG_CORE, "New Client on Fd %d", fd);
#ifdef DEBUG
if (!list_verify(clientlist)) {
nlog(LOG_WARNING, LOG_CORE, "Client Verify after insert failed");
}
#endif
return mqc;
}
static int list_find_fd(const void *key1, const void *key2) {
const mqclient *mqc1 = key1;
return ((int)key2 - mqc1->fd);
}
mqclient *find_client_from_fd(int fd) {
lnode_t *node;
node = list_find(clientlist, (void *)fd, list_find_fd);
if (!node) {
nlog(LOG_DEBUG1, LOG_CORE, "Can't Find Client for fd %d", fd);
return NULL;
}
return lnode_get(node);
}
void del_client(mqclient *mqc) {
nlog(LOG_DEBUG1, LOG_CORE, "Deleting Client %s (%d)", mqc->user, mqc->fd);
list_delete(clientlist, mqc->node);
lnode_destroy(mqc->node);
free(mqc);
}

72
client.h Normal file
View file

@ -0,0 +1,72 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#ifndef CLIENT_H
#define CLIENT_H
#include "defines.h"
#include "event.h"
#include "list.h"
typedef struct mqclient {
int fd;
char user[MAXNICK];
char pass[MAXPASS];
char host[MAXHOST];
struct sockaddr_in ip;
long status;
long type;
struct event ev;
lnode_t *node;
} mqclient;
#define MQC_STAT_CONNECT 0x01
#define MQC_STAT_DNSLKUP 0x02
#define MQC_STAT_AUTHSTART 0x04
#define MQC_STAT_AUTHOK 0x08
#define MQC_TYPE_NONE 0x01
#define MQC_TYPE_LISTEN 0x02
#define MQC_TYPE_CLIENT 0x04
#define MQC_TYPE_ADMIN 0x08
#define MQC_TYPE_REPL 0x10
#define MQC_SET_TYPE_LISTEN(x) x->type = MQC_TYPE_LISTEN
#define MQC_IS_TYPE_LISTEN(x) (x->type & MQC_TYPE_LISTEN)
#define MQC_CLEAR_TYPE_LISTEN(x) (x->type &= MQC_TYPE_LISTEN)
#define MQC_SET_STAT_CONNECT(x) x->status |= MQC_STAT_CONNECT
#define MQC_IS_STAT_CONNECT(x) (x->status & MQC_STAT_CONNECT)
#define MQC_CLEAR_STAT_CONNECT(x) (x->status &= MQC_STAT_CONNECT);
#define MQC_SET_STAT_DNSLOOKUP(x) x->status |= MQC_STAT_DNSLKUP
#define MQC_IS_STAT_DNSLOOKUP(x) (x->status & MQC_STAT_DNSLKUP)
#define MQC_CLEAR_STAT_DNSLOOKUP(x) (x->status &= MQC_STAT_DNSLKUP);
mqclient *new_client(int);
void del_client(mqclient *);
#endif

157
conf.c Normal file
View file

@ -0,0 +1,157 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#include "defines.h"
#include "dotconf.h"
#include "conf.h"
#include "log.h"
static void cb_Server (char *, int);
/** @brief Core Configuration Items
*
* Contains Configuration Items for the Core NeoStats service
*/
static config_option options[] = {
{"SERVER_NAME", ARG_STR, cb_Server, 0},
{"SERVER_PORT", ARG_STR, cb_Server, 1},
{"CONNECT_TO", ARG_STR, cb_Server, 2},
{"CONNECT_PASS", ARG_STR, cb_Server, 3},
{"SERVER_INFOLINE", ARG_STR, cb_Server, 4},
{"STATSERV_NETNAME", ARG_STR, cb_Server, 5},
{"RECONNECT_TIME", ARG_STR, cb_Server, 6},
{"NEOSTAT_HOST", ARG_STR, cb_Server, 7},
{"NEOSTAT_USER", ARG_STR, cb_Server, 8},
{"WANT_PRIVMSG", ARG_STR, cb_Server, 9},
{"SERVICES_CHAN", ARG_STR, cb_Server, 10},
{"ONLY_OPERS", ARG_STR, cb_Server, 11},
{"NO_LOAD", ARG_STR, cb_Server, 12},
{"BINDTO", ARG_STR, cb_Server, 13},
{"LOGFILENAMEFORMAT", ARG_STR, cb_Server, 14},
{"SERVER_NUMERIC", ARG_STR, cb_Server, 15},
{"SETSERVERTIMES", ARG_STR, cb_Server, 16},
};
/** @brief initialize the configuration parser
*
* Currently does nothing
*
* @return Nothing
*/
void
init_conf ()
{
}
/** @brief Load the Config file
*
* Parses the Configuration File and optionally loads the external authentication libary
*
* @returns Nothing
*/
int
ConfLoad ()
{
/* Read in the Config File */
printf ("Reading the Config File. Please wait.....\n");
if (!config_read (CONFIG_NAME, options) == 0) {
printf ("***************************************************\n");
printf ("* Error! *\n");
printf ("* *\n");
printf ("* Config File not found, or Unable to Open *\n");
printf ("* Please check its Location, and try again *\n");
printf ("* *\n");
printf ("* NeoStats NOT Started *\n");
printf ("***************************************************\n");
return NS_FAILURE;
}
printf ("Sucessfully Loaded Config File, Now Booting NeoStats\n");
return NS_SUCCESS;
}
/** @brief Process config file items
*
* Processes the config file and sets up the variables. No Error Checking is performed :(
*
* @param arg the variable value as a string
* @param configtype the index of the variable being called now
* @returns Nothing
*/
void
cb_Server (char *arg, int configtype)
{
if (configtype == 0) {
/* Server name */
strncpy (me.name, arg, sizeof (me.name));
} else if (configtype == 1) {
/* Server Port */
me.port = atoi (arg);
} else if (configtype == 2) {
} else if (configtype == 3) {
/* Connect Pass */
strncpy (me.pass, arg, sizeof (me.pass));
} else if (configtype == 4) {
} else if (configtype == 5) {
} else if (configtype == 6) {
/* Reconnect time */
me.r_time = atoi (arg);
} else if (configtype == 7) {
/* NeoStat Host */
strncpy (me.host, arg, MAXHOST);
} else if (configtype == 8) {
/* NeoStat User */
strncpy (me.user, arg, MAXUSER);
} else if (configtype == 9) {
} else if (configtype == 10) {
} else if (configtype == 11) {
} else if (configtype == 12) {
me.die = 1;
} else if (configtype == 13) {
strncpy (me.local, arg, sizeof (me.local));
} else if (configtype == 14) {
strncpy(LogFileNameFormat,arg,MAX_LOGFILENAME);
} else if (configtype == 15) {
} else if (configtype == 16) {
}
}
/** @brief Rehash Function
*
* Called when we recieve a rehash signal. Does nothing atm
*
* @returns Nothing
*/
void
rehash ()
{
/* nothing, yet */
}

54
conf.h Normal file
View file

@ -0,0 +1,54 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#ifndef _conf_h_
#define _conf_h_
/*
* conf.h
* dynamic configuration runtime libary
*/
/* define the config types */
#define CFGSTR 1
#define CFGINT 2
#define CFGFLOAT 3
#define CFGBOOL 4
#define CONFBUFSIZE 256
int GetConf (void **data, int type, const char *item);
int SetConf (void *data, int type, char *item);
int GetDir (char *item, char ***data);
int DelConf (char *item);
int DelRow (char *table, char *row);
int DelTable(char *table);
int SetData (void *data, int type, char *table, char *row, char *field);
int GetTableData (char *table, char ***data);
int GetData (void **data, int type, const char *table, const char *row, const char *field);
void flush_keeper();
#endif

1371
config.guess vendored Executable file

File diff suppressed because it is too large Load diff

140
config.h.in Normal file
View file

@ -0,0 +1,140 @@
/* config.h.in. Generated from configure.in by autoheader. */
/* Enable Debugging */
#undef DEBUG
/* Define to 1 if you have the <db.h> header file. */
#undef HAVE_DB_H
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if your system has a working `fnmatch' function. */
#undef HAVE_FNMATCH
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `regcomp' function. */
#undef HAVE_REGCOMP
/* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET
/* Define if you have fcloseall */
#undef HAVE_FCLOSEALL
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strdup' function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the `strdup' function. */
#undef HAVE_STRNDUP
/* Define to 1 if you have the `strftime' function. */
#undef HAVE_STRFTIME
/* Define to 1 if you have the `strlcpy' function. */
#undef HAVE_STRLCPY
/* Define to 1 if you have the `strlcat' function. */
#undef HAVE_STRLCAT
/* Define to 1 if you have the `strnlen' function. */
#undef HAVE_STRNLEN
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
#undef HAVE_SYS_WAIT_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define as the return type of signal handlers (`int' or `void'). */
#undef RETSIGTYPE
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `unsigned' if <sys/types.h> does not define. */
#undef size_t
/* define if you have Backtrace */
#undef HAVE_BACKTRACE
/* Version number of package */
#undef MQSERVER_PACKAGE_VERSION
/* Install directory */
#undef NEO_PREFIX
/* Major Version */
#undef MAJOR
/* Minor Version */
#undef MINOR
/* Revision Version */
#undef REV
/* Define as __inline if that's what the C compiler calls it. */
#undef inline
/* Have pthread mutex locking and unlocking functions in libc */
#undef HAVE_PTHREAD_MUTEX

1362
config.sub vendored Executable file

File diff suppressed because it is too large Load diff

5758
configure vendored Executable file

File diff suppressed because it is too large Load diff

150
configure.in Normal file
View file

@ -0,0 +1,150 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(main.c)
AC_CONFIG_HEADER(config.h)
PACKAGE=MQServer
MAJOR=0
MINOR=0
REV=1
AC_DEFINE_UNQUOTED(MAJOR, $MAJOR)
AC_DEFINE_UNQUOTED(MINOR, $MINOR)
AC_DEFINE_UNQUOTED(REV, $REV)
VERSION=$MAJOR.$MINOR.$REV
AC_DEFINE_UNQUOTED(MQSERVER_PACKAGE_VERSION, "$VERSION")
dnl Get system type
AC_CANONICAL_HOST
MYHOST=$host_os
case "$host_os" in
*cygwin)
CFLAGS="$CFLAGS -O2 -g"
LDFLAGS="$LDFLAGS -export-dynamic";;
*mingw32)
CFLAGS="$CFLAGS -O2 -g -mno-cygwin"
LDFLAGS="$LDFLAGS -export-dynamic";;
*openbsd)
CFLAGS="$CFLAGS -O2 -g -fPIC -DPIC"
LDFLAGS="-export-dynamic";;
*)
CFLAGS="$CFLAGS -O2 -g -fPIC -DPIC"
LDFLAGS="-rdynamic";;
esac
case "$host_os" in
*openbsd*)
MAKEDEPENDENCIES="";;
*freebsd*)
MAKEDEPENDENCIES="";;
*)
MAKEDEPENDENCIES="-include \$(OBJS:.o=.d)";;
esac
dnl Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
AC_CACHE_SAVE
dnl Checks for libraries.
dnl Replace `main' with a function in -ldl:
AC_CHECK_LIB(dl, dlopen)
AC_CHECK_FUNC(backtrace,
[AC_DEFINE(HAVE_BACKTRACE, 1, 'backtrace function available')
LIBS="-g $LIBS"]
)
AC_CACHE_SAVE
dnl Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(sys/time.h unistd.h limits.h)
AC_CACHE_SAVE
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_STRUCT_TM
AC_CACHE_SAVE
dnl Checks for library functions.
AC_FUNC_FNMATCH
AC_TYPE_SIGNAL
AC_FUNC_STRFTIME
AC_CHECK_FUNCS(strlcpy strlcat strnlen)
AC_CHECK_FUNCS(socket strdup)
AC_CHECK_FUNCS(strndup)
AC_CHECK_FUNCS(bcopy memmove strerror)
AC_CHECK_HEADERS(sys/select.h)
dnl check if we are running with Debug....
AC_MSG_CHECKING(Whether to Enable Debuging...)
AC_ARG_ENABLE(debug,
[ --enable-debug - Enable Debuging],
[ case "$enableval" in
yes)
AC_DEFINE(DEBUG, 1, 'Enable Debugging')
CFLAGS="$CFLAGS -ggdb -Wall"
AC_MSG_RESULT(yes - Watch your Log Files)
;;
*)
CFLAGS="$CFLAGS -DNDEBUG"
AC_MSG_RESULT(no)
;;
esac],
CFLAGS="$CFLAGS -DNDEBUG"
AC_MSG_RESULT(no)
)
AC_CACHE_SAVE
dnl Set NEO_PREFIX in config.h.
if test "x${prefix}" = "xNONE"; then
AC_DEFINE_UNQUOTED(NEO_PREFIX, "${ac_default_prefix}")
else
AC_DEFINE_UNQUOTED(NEO_PREFIX, "${prefix}")
fi
NEO_SUBDIR_CONFIG(libevent, [--disable-shared --enable-static])
AC_SUBST(LIBDB)
AC_SUBST(MAKEDEPENDENCIES)
AC_SUBST(DIRINST)
AC_SUBST(CFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(MODLDFLAGS)
AC_SUBST(LINK_SIZE)
AC_SUBST(MATCH_LIMIT)
AC_SUBST(NEWLINE)
AC_SUBST(UTF8)
AC_SUBST(LIBS)
AC_SUBST(PACKAGE)
AC_SUBST(VERSION)
AC_OUTPUT(Makefile)
echo "(*----------------------------------------------------------*)"
echo "(| Important Instructions |)"
echo "(*----------------------------------------------------------*)"
echo "(| if you just typed ./configure and didnt read the README |)"
echo "(| NeoStats may not be configured correctly. |)"
echo "(| We suggest you at least type ./configure --help to see |)"
echo "(| available options and read the README file for further |)"
echo "(| information on that option. |)"
echo "(*----------------------------------------------------------*)"
echo "(| For Support please visit: |)"
echo "(| IRC: /server irc.irc-chat.net |)"
echo "(| #neostats channel |)"
echo "(| WWW: http://www.neostats.net/boards/ |)"
echo "(*----------------------------------------------------------*)"
echo "(| Now that configure is complete, type 'make' to compile |)"
echo "(| the program. When the compile has completed, type |)"
echo "(| 'make install' to install the program. |)"
echo "(| For BSD you might need to use 'gmake' and 'gmake install'|)"
echo "(*----------------------------------------------------------*)"
#if test "$shush" != yes; then
# read -p "Press Enter Key to Read the Release Notes"
# clear
# more ./RELNOTES
#fi

225
defines.h Normal file
View file

@ -0,0 +1,225 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#ifndef DEFINES_H
#define DEFINES_H
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <time.h>
#define __USE_GNU
#include <string.h>
#undef __USE_GNU
#include <stdarg.h>
#include <stdlib.h>
#include <sys/time.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <setjmp.h>
#include <assert.h>
#include "event.h"
#include "config.h"
#ifdef HAVE_DB_H
/*#define USE_BERKELEY*/
#endif
/* Temp disable for upcoming release until all external modules
* have been released with warnings fixed
*/
#if 0
#define __attribute__(x) /* NOTHING */
#else
/* If we're not using GNU C, elide __attribute__ */
#ifndef __GNUC__
#define __attribute__(x) /* NOTHING */
#endif
#endif
#include "list.h"
#include "hash.h"
#include "adns.h"
#ifdef MQSERVER_REVISION
#define MQSERVER_VERSION MQSERVER_PACKAGE_VERSION " (" MQSERVER_REVISION ")"
#else
#define MQSERVER_VERSION MQSERVER_PACKAGE_VERSION
#endif
#define CONFIG_NAME "mqserver.cfg"
#define PID_FILENAME "mqserver.pid"
#define BUFSIZE 512
#ifndef MAXHOST
#define MAXHOST 128
#endif
#ifndef MAXPASS
#define MAXPASS 32
#endif
#ifndef MAXNICK
#define MAXNICK 32
#endif
#ifndef MAXUSER
#define MAXUSER 15
#endif
#ifndef MAXREALNAME
#define MAXREALNAME 50
#endif
/* MAXPATH
* used to determine buffer sizes for file system operations
*/
#ifndef MAXPATH
#define MAXPATH 1024
#endif /* MAXPATH */
/* TIMEBUFSIZE
* used to determine buffer sizes for time formatting buffers
*/
#define TIMEBUFSIZE 80
/* STR_TIME_T_SIZE
* size of a time_t converted to a string.
*/
#define STR_TIME_T_SIZE 24
/* Buffer size for version string */
#define VERSIONSIZE 32
#define bzero(x, y) memset(x, '\0', y);
/* Early creation of unified return values and error system */
/* These are program exit codes usually defined in stdlib.h but
if not found will be defined here */
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif /* EXIT_FAILURE */
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif /* EXIT_SUCCESS */
#define NS_SUCCESS 1
#define NS_FAILURE -1
/* Specific errors beyond SUCCESS/FAILURE so that functions can handle errors
* Treat as unsigned with top bit set to give us a clear distinction from
* other values and use a typedef ENUM so that we can indicate return type */
typedef enum NS_ERR {
NS_ERR_NICK_IN_USE = 0x8000001,
NS_ERR_OUT_OF_MEMORY = 0x8000002,
NS_ERR_VERSION = 0x8000003,
}NS_ERR ;
/* do_exit call exit type definitions */
typedef enum {
NS_EXIT_NORMAL=0,
NS_EXIT_RELOAD,
NS_EXIT_RECONNECT,
NS_EXIT_ERROR,
NS_EXIT_SEGFAULT,
}NS_EXIT_TYPE;
#define SEGV_LOCATION_BUFSIZE 255
#define SET_SEGV_LOCATION() snprintf(segv_location,SEGV_LOCATION_BUFSIZE,"%s %d %s", __FILE__, __LINE__, __PRETTY_FUNCTION__);
#define SET_SEGV_LOCATION_EXTRA(debug_text) snprintf(segv_location,SEGV_LOCATION_BUFSIZE,"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__,(debug_text));
#define CLEAR_SEGV_LOCATION() segv_location[0]='\0';
#define SEGV_INMODULE_BUFSIZE 255
#define SET_SEGV_INMODULE(module_name) strncpy(segv_inmodule,(module_name),SEGV_INMODULE_BUFSIZE);
#define CLEAR_SEGV_INMODULE() segv_inmodule[0]='\0';
/* macros to provide a couple missing string functions for code legibility
* and to ensure we perform these operations in a standard and optimal manner
*/
/* set a string to NULL */
#define strsetnull(str) (str)[0] = 0
/* test a string for NULL */
#define strisnull(str) ((str) && (str)[0] == 0)
#define ARRAY_COUNT (a) ((sizeof ((a)) / sizeof ((a)[0]))
extern char segv_location[SEGV_LOCATION_BUFSIZE];
extern char segv_inmodule[SEGV_INMODULE_BUFSIZE];
extern jmp_buf sigvbuf;
/* this is the dns structure */
extern adns_state ads;
/* version info */
extern const char version_date[], version_time[];
/** @brief me structure
* structure containing information about the neostats core
*/
struct me {
char name[MAXHOST];
int port;
int r_time;
char pass[MAXPASS];
char local[MAXHOST];
char user[MAXUSER]; /* bot user */
char host[MAXHOST]; /* bot host */
char realname[MAXREALNAME]; /* bot real name */
time_t t_start;
unsigned int die:1;
unsigned int debug_mode:1;
time_t now;
char strnow[STR_TIME_T_SIZE];
char version[VERSIONSIZE];
char versionfull[VERSIONSIZE];
} me;
/* conf.c */
int ConfLoad (void);
void rehash (void);
int ConfLoadModules (void);
/* main.c */
void do_exit (NS_EXIT_TYPE exitcode, char* quitmsg) __attribute__((noreturn));
void fatal_error(char* file, int line, char* func, char* error_text) __attribute__((noreturn));;
#define FATAL_ERROR(error_text) fatal_error(__FILE__, __LINE__, __PRETTY_FUNCTION__,(error_text));
/* misc.c */
void strip (char * line);
void *smalloc (long size);
char *sstrdup (const char * s);
char *strlwr (char * s);
void AddStringToList (char ***List, char S[], int *C);
char *sctime (time_t t);
char *sftime (time_t t);
#endif

1142
dict.c Normal file

File diff suppressed because it is too large Load diff

128
dict.h Normal file
View file

@ -0,0 +1,128 @@
/*
* Dictionary Abstract Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* Free Software License:
*
* All rights are reserved by the author, with the following exceptions:
* Permission is granted to freely reproduce and distribute this software,
* possibly in exchange for a fee, provided that this copyright notice appears
* intact. Permission is also granted to adapt this software to produce
* derivative works, as long as the modified versions carry this copyright
* notice and additional notices stating that the work has been modified.
* This source code may be translated into executable form and incorporated
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
* $Id$
* $Name: kazlib_1_15 $
*/
#ifndef DICT_H
#define DICT_H
#include <limits.h>
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#include "sfx.h"
#endif
/*
* Blurb for inclusion into C++ translation units
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned long dictcount_t;
#define DICTCOUNT_T_MAX ULONG_MAX
/*
* The dictionary is implemented as a red-black tree
*/
typedef enum { dnode_red, dnode_black } dnode_color_t;
typedef struct dnode_t {
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
struct dnode_t *dict_left;
struct dnode_t *dict_right;
struct dnode_t *dict_parent;
dnode_color_t dict_color;
const void *dict_key;
void *dict_data;
#else
int dict_dummy;
#endif
} dnode_t;
typedef int (*dict_comp_t)(const void *, const void *);
typedef dnode_t *(*dnode_alloc_t)(void *);
typedef void (*dnode_free_t)(dnode_t *, void *);
typedef struct dict_t {
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
dnode_t dict_nilnode;
dictcount_t dict_nodecount;
dictcount_t dict_maxcount;
dict_comp_t dict_compare;
dnode_alloc_t dict_allocnode;
dnode_free_t dict_freenode;
void *dict_context;
int dict_dupes;
#else
int dict_dummmy;
#endif
} dict_t;
typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
extern dict_t *dict_create(dictcount_t, dict_comp_t);
extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
extern void dict_destroy(dict_t *);
extern void dict_free(dict_t *);
extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
extern int dict_verify(dict_t *);
extern dnode_t *dict_lookup(dict_t *, const void *);
extern dnode_t *dict_lower_bound(dict_t *, void *);
extern dnode_t *dict_upper_bound(dict_t *, void *);
extern void dict_insert(dict_t *, dnode_t *, const void *);
extern dnode_t *dict_delete(dict_t *, dnode_t *);
extern int dict_alloc_insert(dict_t *, const void *, void *);
extern void dict_delete_free(dict_t *, dnode_t *);
extern dnode_t *dict_first(dict_t *);
extern dnode_t *dict_last(dict_t *);
extern dnode_t *dict_next(dict_t *, dnode_t *);
extern dnode_t *dict_prev(dict_t *, dnode_t *);
extern dictcount_t dict_count(dict_t *);
extern int dict_isempty(dict_t *);
extern int dict_isfull(dict_t *);
extern int dict_contains(dict_t *, dnode_t *);
extern void dict_allow_dupes(dict_t *);
extern int dnode_is_in_a_dict(dnode_t *);
extern dnode_t *dnode_create(void *);
extern dnode_t *dnode_init(dnode_t *, void *);
extern void dnode_destroy(dnode_t *);
extern void *dnode_get(dnode_t *);
extern const void *dnode_getkey(dnode_t *);
extern void dnode_put(dnode_t *, void *);
extern void dict_process(dict_t *, void *, dnode_process_t);
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
#else
#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
#endif
#define dict_count(D) ((D)->dict_nodecount)
#define dict_isempty(D) ((D)->dict_nodecount == 0)
#define dnode_get(N) ((N)->dict_data)
#define dnode_getkey(N) ((N)->dict_key)
#define dnode_put(N, X) ((N)->dict_data = (X))
#endif
#ifdef __cplusplus
}
#endif
#endif

453
dotconf.c Normal file
View file

@ -0,0 +1,453 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** Code portions Borrowed from the dotconf libary. Header follows:
*/
/* dotconf
* Copyright (C) 1999 Lukas Schröder
*
* 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 2 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
/*
** NeoStats CVS Identification
** $Id$
*/
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include "config.h"
#include "defines.h"
#include "dotconf.h"
static int word_count; /* no. of option arguments */
static char name[CFG_MAX_OPTION + 1]; /* option name */
static char values[CFG_VALUES][CFG_MAX_VALUE + 1]; /* holds the arguments */
static char dotconf_includepath[CFG_MAX_FILENAME + 1];
char *dotconf_file; /* filename of current file */
int dotconf_line = 0; /* line in file we are currently reading */
/*
* the list of config_options where a match for each name-token is
* searched for; i prefer the use of a fixed-size array, b/c special
* memory handling (malloc,free) would be necessary
*/
static config_option *config_options[CFG_MODULES];
/*
* some 'magic' options that are predefined by dot.conf itself for
* advanced functionality
*/
static void dotconf_cb_include (char *); /* magic 'Include' */
static void dotconf_cb_includepath (char *); /* magic 'IncludePath' */
static config_option dotconf_options[] = { {"Include", ARG_STR, dotconf_cb_include, 0},
{"IncludePath", ARG_STR, dotconf_cb_includepath, 0},
LAST_OPTION
};
void
config_substitute_env (char *str)
{
char *cp1, *cp2, *cp3, *eos, *eob;
char *env_value;
char env_name[CFG_MAX_VALUE + 1];
char env_default[CFG_MAX_VALUE + 1];
char tmp_value[CFG_MAX_VALUE + 1];
bzero (env_name, CFG_MAX_VALUE + 1);
bzero (env_default, CFG_MAX_VALUE + 1);
bzero (tmp_value, CFG_MAX_VALUE + 1);
cp1 = str;
eob = cp1 + CFG_MAX_VALUE + 1;
cp2 = tmp_value;
eos = cp2 + CFG_MAX_VALUE + 1;
while ((cp1 != eos) && (cp2 != eos) && (*cp1 != '\0')) {
/* substitution needed ?? */
if (*cp1 == '$' && *(cp1 + 1) == '{') {
/* yeah */
cp1 += 2; /* skip ${ */
cp3 = env_name;
while ((cp1 != eos)
&& !(*cp1 == '}' || *cp1 == ':'))
*cp3++ = *cp1++;
*cp3 = '\0'; /* terminate */
/* default substitution */
if (*cp1 == ':' && *(cp1 + 1) == '-') {
cp1 += 2; /* skip :- */
cp3 = env_default;
while ((cp1 != eos) && (*cp1 != '}'))
*cp3++ = *cp1++;
*cp3 = '\0'; /* terminate */
} else
while ((cp1 != eos) && (*cp1 != '}'))
cp1++;
if (*cp1 != '}')
fprintf (stderr, "%s:%d: Unbalanced '{'\n", dotconf_file, dotconf_line);
else {
cp1++; /* skip } */
if ((env_value = getenv (env_name)) != NULL) {
strncat (cp2, env_value, eos - cp2);
cp2 += strlen (env_value);
} else {
strncat (cp2, env_default, eos - cp2);
cp2 += strlen (env_default);
}
}
}
*cp2++ = *cp1++;
}
*cp2 = '\0'; /* terminate buffer */
strncpy (str, tmp_value, CFG_MAX_VALUE + 1);
}
void
config_register_options (config_option * options)
{
int i;
for (i = 0; i < CFG_MODULES && config_options[i]; i++) {
}
config_options[i] = options;
}
int
config_parse (FILE * config)
{
static char buffer[CFG_BUFSIZE];
static char *here_string; /* Damn FreeBSD */
static char *here_limit;
static char *here_doc;
while ((fgets (buffer, CFG_BUFSIZE, config)) != NULL) { /* for each line */
char *cp1, *cp2; /* generic char pointer */
char *eob, *eos; /* end of buffer; end of string */
char sq, dq; /* state flags: single/double quote */
int i, mod; /* generic counter, mod holds the module index */
config_option opt; /* matched option from config_options */
dotconf_line++;
/* ignore #-comments and empty lines */
if ((buffer[0] == '#' || buffer[0] == '\n'))
continue;
/* clear fields */
word_count = 0;
name[0] = 0;
for (i = 0; i < CFG_VALUES; i++)
values[i][0] = 0;
/* initialize char pointer */
cp1 = buffer;
eob = cp1 + strlen (cp1); /* calculate end of buffer */
/* skip any whitspace of indented lines */
while ((cp1 != eob) && (isspace (*cp1)))
cp1++;
/* skip line if it only contains whitespace */
if (cp1 == eob)
continue;
/* get first token: read the name of a possible option */
cp2 = name;
while ((*cp1 != '\0') && (!isspace (*cp1)))
*cp2++ = *cp1++;
*cp2 = '\0';
/* and now find the entry in the option table, and call the callback */
bzero (&opt, sizeof (config_option));
for (mod = 0; mod < CFG_MODULES && config_options[mod]; mod++)
for (i = 0; config_options[mod][i].name[0]; i++)
if (!strncmp (name, config_options[mod][i].name, CFG_MAX_OPTION)) {
opt = config_options[mod][i];
break; /* found it; break out of for */
}
if (opt.name[0] == 0) {
/* cannot find it
fprintf(stderr, "Unknown Config-Option: '%s' in %s:%d\n",
name, dotconf_file, dotconf_line);
continue; move on to the next line immediately */
} else if (opt.type == ARG_RAW) {
/* if it is an ARG_RAW type, save some time and call the
callback now */
opt.callback (cp1, opt.userdata);
continue;
} else if (opt.type == ARG_STR) {
/* check if it's a here-document and act accordingly */
char *cp3 = cp1;
bzero (&here_limit, 9);
/* skip whitespace */
while ((cp3 < eob) && (*cp3 != '\0')
&& (isspace (*cp3)))
cp3++;
if (!strncmp ("<<", cp3, 2)) { /* here string sign */
/* it's a here-document: yeah, what a cool feature ;) */
struct stat finfo;
stat (dotconf_file, &finfo);
/*
* allocate a buffer of filesize bytes; should be enough to
* prevent buffer overflows
*/
here_doc = malloc (finfo.st_size + 1); /* allocate buffer memory */
bzero (here_doc, finfo.st_size + 1);
strncpy (here_limit, cp3 + 2, 8); /* copy here-delimiter */
while (fgets (buffer, CFG_BUFSIZE, config)) {
if (!strncmp (here_limit, buffer, strlen (here_limit))) {
here_string = 0;
break;
}
strcat (here_doc, buffer); /* append to buffer */
}
if (here_string)
fprintf (stderr, "Line %d: Unterminated here-document!\n", dotconf_line);
here_doc[strlen (here_doc) - 1] = '\0'; /* strip newline */
opt.callback (here_doc, opt.userdata); /* call back */
free (here_doc); /* free buffer memory */
continue;
}
}
free (here_doc);
/* skip whitespace */
while ((cp1 < eob) && (*cp1 != '\0') && (isspace (*cp1)))
cp1++;
/* start reading option arguments */
cp2 = values[word_count];
eos = cp2 + CFG_MAX_VALUE - 1;
sq = 0;
dq = 0; /* clear quoting flags */
while ((*cp1 != '\0') && (cp2 != eos)
&& (word_count < CFG_VALUES)) {
switch (*cp1) {
case '\'': /* single quote */
if (dq)
break; /* already double quoting, break out */
if (sq) {
sq--;
} /* already single quoting, clear state */
else if (!sq) {
sq++;
} /* set state for single quoting */
break;
case '"': /* double quote */
if (sq)
break; /* already single quoting, break out */
if (dq) {
dq--;
} /* already double quoting, clear state */
else if (!dq) {
dq++;
} /* set state for double quoting */
break;
default:
break;
}
/* unquoted space: start a new option argument */
if (isspace (*cp1) && !dq && !sq) {
*cp2 = '\0'; /* terminate current argument */
/* increment word counter and update char pointer */
cp2 = values[++word_count];
/* skip all whitespace between 2 arguments */
while (isspace (*(cp1 + 1))
&& (*cp1 != '\0'))
cp1++;
}
/* not space or quoted ; eat it: */
else if ((((!isspace (*cp1) && !dq && !sq && *cp1 != '"' && *cp1 != '\'')
/* dont take quote if quoting: */
|| (dq && (*cp1 != '"'))
|| (sq && *cp1 != '\''))))
*cp2++ = *cp1;
cp1++;
}
if (opt.name[0] > 32) { /* has an option entry been found before? */
/* found it, now check the type of args it wants */
#define USER_DATA opt.userdata
switch (opt.type) {
case ARG_TOGGLE:
{
/* the value is true if the argument is Yes, On or 1 */
/* kludge code follows ;) */
int arg = ((values[0][0] == 'Y' || values[0][1] == 'y')
|| (values[0][0] == '1')
|| ((values[0][0] == 'o' || values[0][0] == 'O')
&& (values[0][1] == 'n' || values[0][1]
== 'N')));
opt.callback (arg, USER_DATA);
break;
}
case ARG_INT:
{
int arg = atoi (values[0]);
opt.callback (arg, USER_DATA);
break;
}
case ARG_STR:
{
config_substitute_env (values[0]);
opt.callback (values[0], USER_DATA);
break;
}
case ARG_LIST:
{
char *data[CFG_VALUES];
int i;
for (i = 0; i < word_count; i++) { /* prepare list */
config_substitute_env (values[i]);
data[i] = strdup (values[i]);
}
opt.callback (data, word_count, USER_DATA);
for (i = 0; i < word_count; i++) /* dump list */
free (data[i]);
break;
}
case ARG_NONE:
{
opt.callback ();
break;
}
case ARG_RAW: /* this has been handled before */
default:
{
break;
}
}
}
}
return -1;
}
/*
* open and parse the config-file using the config_options list
* as reference for what callback to call and what type of arguments to provide
*/
int
config_read (char *fname, config_option * options)
{
FILE *config;
char *dc_env; /* pointer to DC_INCLUDEPATH */
if (access (fname, R_OK)) {
fprintf (stderr, "Error opening configuration file '%s'\n", fname);
return 1;
}
dotconf_file = malloc (CFG_MAX_FILENAME + 1); /* allocate fname buffer */
bzero (dotconf_file, CFG_MAX_FILENAME + 1);
bzero (dotconf_includepath, CFG_MAX_FILENAME + 1);
strncpy (dotconf_file, fname, CFG_MAX_FILENAME); /* fill fname buffer */
/* take includepath from environment if present */
if ((dc_env = getenv (CFG_INCLUDEPATH_ENV)) != NULL)
strncpy (dotconf_includepath, dc_env, CFG_MAX_FILENAME);
config_register_options (dotconf_options); /* internal options */
config_register_options (options); /* register main options */
config = fopen (dotconf_file, "r");
config_parse (config); /* fire off parser */
fclose (config);
free (dotconf_file); /* free fname buffer */
return 0;
}
/* callbacks for internal options */
void
dotconf_cb_include (char *str)
{
FILE *config;
char old_fname[CFG_MAX_FILENAME];
bzero (&old_fname, CFG_MAX_FILENAME);
strncpy (old_fname, dotconf_file, CFG_MAX_FILENAME);
if (str[0] != '/' && dotconf_includepath[0] != '\0') {
/* relative file AND include path is used */
/* check for length of fully qualified filename */
if ((strlen (str) + strlen (dotconf_includepath) + 1) == CFG_MAX_FILENAME) {
fprintf (stderr, "%s:%d: Absolute filename too long (>%d)\n", dotconf_file, dotconf_line, CFG_MAX_FILENAME);
return;
}
snprintf (dotconf_file, CFG_MAX_FILENAME + 1, "%s/%s", dotconf_includepath, str);
} else /* fully qualified, or no includepath */
strncpy (dotconf_file, str, CFG_MAX_FILENAME);
if (access (dotconf_file, R_OK)) {
fprintf (stderr, "Error in %s line %d: Cannot open %s for inclusion\n", old_fname, dotconf_line, dotconf_file);
strncpy (dotconf_file, old_fname, CFG_MAX_FILENAME); /* restore settings */
return;
}
config = fopen (dotconf_file, "r");
config_parse (config);
fclose (config);
strncpy (dotconf_file, old_fname, CFG_MAX_FILENAME);
}
void
dotconf_cb_includepath (char *str)
{
char *env = getenv ("DC_INCLUDEPATH");
if (!env) /* environment overrides configuration file setting */
strncpy (dotconf_includepath, str, CFG_MAX_FILENAME);
}

81
dotconf.h Normal file
View file

@ -0,0 +1,81 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#ifndef HAVE_CFG_H
#define HAVE_CFG_H
/* some buffersize definitions */
#define CFG_BUFSIZE 4096 /* max length of one line */
#define CFG_MAX_OPTION 32 /* max length of any option name */
#define CFG_MAX_VALUE 4064 /* max length of any options value */
#define CFG_MAX_FILENAME 256 /* max length of a filename */
#define CFG_VALUES 16 /* max # of arguments an option takes */
#define CFG_MODULES 64 /* max # of dynamically loadable modules */
#define CFG_INCLUDEPATH_ENV "DC_INCLUDEPATH"
/* constants for type of option */
#define ARG_TOGGLE 0 /* TOGGLE on,off; yes,no; 1, 0; */
#define ARG_INT 1 /* callback wants an integer */
#define ARG_STR 2 /* callback expects a \0 terminated str */
#define ARG_LIST 3 /* wants list of strings */
#define ARG_NAME 4 /* wants option name */
#define ARG_RAW 5 /* wants raw argument data */
#define ARG_NONE 6 /* does not expect ANY args */
/* for convenience of terminating the config_options list */
#define LAST_OPTION { "", 0, NULL, 0 }
typedef struct _cfgoption {
char name[CFG_MAX_OPTION]; /* name of configuration option */
int type; /* for possible values, see above */
void (*callback) (); /* callback function */
int userdata; /* userdefinable value/flag */
} config_option;
/* general configuration items */
struct config {
/* debug level */
unsigned int debug;
/* enable recv.log */
unsigned int recvlog:1;
/* dont load modules on startup */
unsigned int modnoload:1;
/* dont output anything on start */
unsigned int quiet:1;
/* dont detach into background */
unsigned int foreground:1;
} config;
/* config_read takes the following arguments:
* 1. the filename of the configuration file to read
* 2. the list of optionnames to recognize
*
* returns 0 on success; !0 on error */
int config_read (char *, config_option *);
void config_register_options (config_option *);
#endif /* HAVE_CFG_H */

898
hash.c Normal file
View file

@ -0,0 +1,898 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** Code portions borrowed from kazlib. Header Follows:
*/
/*
* Hash Table Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* Free Software License:
*
* All rights are reserved by the author, with the following exceptions:
* Permission is granted to freely reproduce and distribute this software,
* possibly in exchange for a fee, provided that this copyright notice appears
* intact. Permission is also granted to adapt this software to produce
* derivative works, as long as the modified versions carry this copyright
* notice and additional notices stating that the work has been modified.
* This source code may be translated into executable form and incorporated
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
* $Id$
* $Name: $
*/
#include <stdlib.h>
#include <stddef.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#define HASH_IMPLEMENTATION
#include "hash.h"
#include "log.h"
#ifdef KAZLIB_RCSID
static const char rcsid[] = "$Id$";
#endif
#define INIT_BITS 6
#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */
#define INIT_MASK ((INIT_SIZE) - 1)
#define next hash_next
#define key hash_key
#define data hash_data
#define hkey hash_hkey
#define table hash_table
#define nchains hash_nchains
#define nodecount hash_nodecount
#define maxcount hash_maxcount
#define highmark hash_highmark
#define lowmark hash_lowmark
#define compare hash_compare
#define function hash_function
#define allocnode hash_allocnode
#define freenode hash_freenode
#define context hash_context
#define mask hash_mask
#define dynamic hash_dynamic
#define table hash_table
#define chain hash_chain
static hnode_t *hnode_alloc (void *context);
static void hnode_free (hnode_t * node, void *context);
static hash_val_t hash_fun_default (const void *key);
static int hash_comp_default (const void *key1, const void *key2);
int hash_val_t_bit;
/*
* Compute the number of bits in the hash_val_t type. We know that hash_val_t
* is an unsigned integral type. Thus the highest value it can hold is a
* Mersenne number (power of two, less one). We initialize a hash_val_t
* object with this value and then shift bits out one by one while counting.
* Notes:
* 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power
* of two. This means that its binary representation consists of all one
* bits, and hence ``val'' is initialized to all one bits.
* 2. While bits remain in val, we increment the bit count and shift it to the
* right, replacing the topmost bit by zero.
*/
static void
compute_bits (void)
{
hash_val_t val = HASH_VAL_T_MAX; /* 1 */
int bits = 0;
while (val) { /* 2 */
bits++;
val >>= 1;
}
hash_val_t_bit = bits;
}
/*
* Verify whether the given argument is a power of two.
*/
static int
is_power_of_two (hash_val_t arg)
{
if (arg == 0)
return 0;
while ((arg & 1) == 0)
arg >>= 1;
return (arg == 1);
}
/*
* Compute a shift amount from a given table size
*/
static hash_val_t
compute_mask (hashcount_t size)
{
nassert (is_power_of_two (size));
nassert (size >= 2);
return size - 1;
}
/*
* Initialize the table of pointers to null.
*/
static void
clear_table (hash_t * hash)
{
hash_val_t i;
for (i = 0; i < hash->nchains; i++)
hash->table[i] = NULL;
}
/*
* Double the size of a dynamic table. This works as follows. Each chain splits
* into two adjacent chains. The shift amount increases by one, exposing an
* additional bit of each hashed key. For each node in the original chain, the
* value of this newly exposed bit will decide which of the two new chains will
* receive the node: if the bit is 1, the chain with the higher index will have
* the node, otherwise the lower chain will receive the node. In this manner,
* the hash table will continue to function exactly as before without having to
* rehash any of the keys.
* Notes:
* 1. Overflow check.
* 2. The new number of chains is twice the old number of chains.
* 3. The new mask is one bit wider than the previous, revealing a
* new bit in all hashed keys.
* 4. Allocate a new table of chain pointers that is twice as large as the
* previous one.
* 5. If the reallocation was successful, we perform the rest of the growth
* algorithm, otherwise we do nothing.
* 6. The exposed_bit variable holds a mask with which each hashed key can be
* AND-ed to test the value of its newly exposed bit.
* 7. Now loop over each chain in the table and sort its nodes into two
* chains based on the value of each node's newly exposed hash bit.
* 8. The low chain replaces the current chain. The high chain goes
* into the corresponding sister chain in the upper half of the table.
* 9. We have finished dealing with the chains and nodes. We now update
* the various bookeeping fields of the hash structure.
*/
static void
grow_table (hash_t * hash)
{
hnode_t **newtable;
nassert (2 * hash->nchains > hash->nchains); /* 1 */
newtable = realloc (hash->table, sizeof *newtable * hash->nchains * 2); /* 4 */
if (newtable) { /* 5 */
hash_val_t mask = (hash->mask << 1) | 1; /* 3 */
hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */
hash_val_t chain;
nassert (mask != hash->mask);
for (chain = 0; chain < hash->nchains; chain++) { /* 7 */
hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next;
for (hptr = newtable[chain]; hptr != 0; hptr = next) {
next = hptr->next;
if (hptr->hkey & exposed_bit) {
hptr->next = high_chain;
high_chain = hptr;
} else {
hptr->next = low_chain;
low_chain = hptr;
}
}
newtable[chain] = low_chain; /* 8 */
newtable[chain + hash->nchains] = high_chain;
}
hash->table = newtable; /* 9 */
hash->mask = mask;
hash->nchains *= 2;
hash->lowmark *= 2;
hash->highmark *= 2;
}
nassert (hash_verify (hash));
}
/*
* Cut a table size in half. This is done by folding together adjacent chains
* and populating the lower half of the table with these chains. The chains are
* simply spliced together. Once this is done, the whole table is reallocated
* to a smaller object.
* Notes:
* 1. It is illegal to have a hash table with one slot. This would mean that
* hash->shift is equal to hash_val_t_bit, an illegal shift value.
* Also, other things could go wrong, such as hash->lowmark becoming zero.
* 2. Looping over each pair of sister chains, the low_chain is set to
* point to the head node of the chain in the lower half of the table,
* and high_chain points to the head node of the sister in the upper half.
* 3. The intent here is to compute a pointer to the last node of the
* lower chain into the low_tail variable. If this chain is empty,
* low_tail ends up with a null value.
* 4. If the lower chain is not empty, we simply tack the upper chain onto it.
* If the upper chain is a null pointer, nothing happens.
* 5. Otherwise if the lower chain is empty but the upper one is not,
* If the low chain is empty, but the high chain is not, then the
* high chain is simply transferred to the lower half of the table.
* 6. Otherwise if both chains are empty, there is nothing to do.
* 7. All the chain pointers are in the lower half of the table now, so
* we reallocate it to a smaller object. This, of course, invalidates
* all pointer-to-pointers which reference into the table from the
* first node of each chain.
* 8. Though it's unlikely, the reallocation may fail. In this case we
* pretend that the table _was_ reallocated to a smaller object.
* 9. Finally, update the various table parameters to reflect the new size.
*/
static void
shrink_table (hash_t * hash)
{
hash_val_t chain, nchains;
hnode_t **newtable, *low_tail, *low_chain, *high_chain;
nassert (hash->nchains >= 2); /* 1 */
nchains = hash->nchains / 2;
for (chain = 0; chain < nchains; chain++) {
low_chain = hash->table[chain]; /* 2 */
high_chain = hash->table[chain + nchains];
for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next); /* 3 */
if (low_chain != 0) /* 4 */
low_tail->next = high_chain;
else if (high_chain != 0) /* 5 */
hash->table[chain] = high_chain;
else
nassert (hash->table[chain] == NULL); /* 6 */
}
newtable = realloc (hash->table, sizeof *newtable * nchains); /* 7 */
if (newtable) /* 8 */
hash->table = newtable;
hash->mask >>= 1; /* 9 */
hash->nchains = nchains;
hash->lowmark /= 2;
hash->highmark /= 2;
nassert (hash_verify (hash));
}
/*
* Create a dynamic hash table. Both the hash table structure and the table
* itself are dynamically allocated. Furthermore, the table is extendible in
* that it will automatically grow as its load factor increases beyond a
* certain threshold.
* Notes:
* 1. If the number of bits in the hash_val_t type has not been computed yet,
* we do so here, because this is likely to be the first function that the
* user calls.
* 2. Allocate a hash table control structure.
* 3. If a hash table control structure is successfully allocated, we
* proceed to initialize it. Otherwise we return a null pointer.
* 4. We try to allocate the table of hash chains.
* 5. If we were able to allocate the hash chain table, we can finish
* initializing the hash structure and the table. Otherwise, we must
* backtrack by freeing the hash structure.
* 6. INIT_SIZE should be a power of two. The high and low marks are always set
* to be twice the table size and half the table size respectively. When the
* number of nodes in the table grows beyond the high size (beyond load
* factor 2), it will double in size to cut the load factor down to about
* about 1. If the table shrinks down to or beneath load factor 0.5,
* it will shrink, bringing the load up to about 1. However, the table
* will never shrink beneath INIT_SIZE even if it's emptied.
* 7. This indicates that the table is dynamically allocated and dynamically
* resized on the fly. A table that has this value set to zero is
* assumed to be statically allocated and will not be resized.
* 8. The table of chains must be properly reset to all null pointers.
*/
hash_t *
hash_create (hashcount_t maxcount, hash_comp_t compfun, hash_fun_t hashfun)
{
hash_t *hash;
if (hash_val_t_bit == 0) /* 1 */
compute_bits ();
hash = malloc (sizeof *hash); /* 2 */
if (hash) { /* 3 */
hash->table = malloc (sizeof *hash->table * INIT_SIZE); /* 4 */
if (hash->table) { /* 5 */
hash->nchains = INIT_SIZE; /* 6 */
hash->highmark = INIT_SIZE * 2;
hash->lowmark = INIT_SIZE / 2;
hash->nodecount = 0;
hash->maxcount = maxcount;
hash->compare = compfun ? compfun : hash_comp_default;
hash->function = hashfun ? hashfun : hash_fun_default;
hash->allocnode = hnode_alloc;
hash->freenode = hnode_free;
hash->context = NULL;
hash->mask = INIT_MASK;
hash->dynamic = 1; /* 7 */
clear_table (hash); /* 8 */
nassert (hash_verify (hash));
return hash;
}
free (hash);
}
return NULL;
}
/*
* Select a different set of node allocator routines.
*/
void
hash_set_allocator (hash_t * hash, hnode_alloc_t al, hnode_free_t fr, void *context)
{
nassert (hash_count (hash) == 0);
nassert ((al == 0 && fr == 0) || (al != 0 && fr != 0));
hash->allocnode = al ? al : hnode_alloc;
hash->freenode = fr ? fr : hnode_free;
hash->context = context;
}
/*
* Free every node in the hash using the hash->freenode() function pointer, and
* cause the hash to become empty.
*/
void
hash_free_nodes (hash_t * hash)
{
hscan_t hs;
hnode_t *node;
hash_scan_begin (&hs, hash);
while ((node = hash_scan_next (&hs))) {
hash_scan_delete (hash, node);
hash->freenode (node, hash->context);
}
hash->nodecount = 0;
clear_table (hash);
}
/*
* Obsolescent function for removing all nodes from a table,
* freeing them and then freeing the table all in one step.
*/
void
hash_free (hash_t * hash)
{
#ifdef KAZLIB_OBSOLESCENT_DEBUG
nassert ("call to obsolescent function hash_free()" && 0);
#endif
hash_free_nodes (hash);
hash_destroy (hash);
}
/*
* Free a dynamic hash table structure.
*/
void
hash_destroy (hash_t * hash)
{
nassert (hash_val_t_bit != 0);
nassert (hash_isempty (hash));
free (hash->table);
free (hash);
}
/*
* Initialize a user supplied hash structure. The user also supplies a table of
* chains which is assigned to the hash structure. The table is static---it
* will not grow or shrink.
* 1. See note 1. in hash_create().
* 2. The user supplied array of pointers hopefully contains nchains nodes.
* 3. See note 7. in hash_create().
* 4. We must dynamically compute the mask from the given power of two table
* size.
* 5. The user supplied table can't be assumed to contain null pointers,
* so we reset it here.
*/
hash_t *
hash_init (hash_t * hash, hashcount_t maxcount, hash_comp_t compfun, hash_fun_t hashfun, hnode_t ** table, hashcount_t nchains)
{
if (hash_val_t_bit == 0) /* 1 */
compute_bits ();
nassert (is_power_of_two (nchains));
hash->table = table; /* 2 */
hash->nchains = nchains;
hash->nodecount = 0;
hash->maxcount = maxcount;
hash->compare = compfun ? compfun : hash_comp_default;
hash->function = hashfun ? hashfun : hash_fun_default;
hash->dynamic = 0; /* 3 */
hash->mask = compute_mask (nchains); /* 4 */
clear_table (hash); /* 5 */
nassert (hash_verify (hash));
return hash;
}
/*
* Reset the hash scanner so that the next element retrieved by
* hash_scan_next() shall be the first element on the first non-empty chain.
* Notes:
* 1. Locate the first non empty chain.
* 2. If an empty chain is found, remember which one it is and set the next
* pointer to refer to its first element.
* 3. Otherwise if a chain is not found, set the next pointer to NULL
* so that hash_scan_next() shall indicate failure.
*/
void
hash_scan_begin (hscan_t * scan, hash_t * hash)
{
hash_val_t nchains = hash->nchains;
hash_val_t chain;
scan->table = hash;
/* 1 */
for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++);
if (chain < nchains) { /* 2 */
scan->chain = chain;
scan->next = hash->table[chain];
} else { /* 3 */
scan->next = NULL;
}
}
/*
* Retrieve the next node from the hash table, and update the pointer
* for the next invocation of hash_scan_next().
* Notes:
* 1. Remember the next pointer in a temporary value so that it can be
* returned.
* 2. This nassertion essentially checks whether the module has been properly
* initialized. The first point of interaction with the module should be
* either hash_create() or hash_init(), both of which set hash_val_t_bit to
* a non zero value.
* 3. If the next pointer we are returning is not NULL, then the user is
* allowed to call hash_scan_next() again. We prepare the new next pointer
* for that call right now. That way the user is allowed to delete the node
* we are about to return, since we will no longer be needing it to locate
* the next node.
* 4. If there is a next node in the chain (next->next), then that becomes the
* new next node, otherwise ...
* 5. We have exhausted the current chain, and must locate the next subsequent
* non-empty chain in the table.
* 6. If a non-empty chain is found, the first element of that chain becomes
* the new next node. Otherwise there is no new next node and we set the
* pointer to NULL so that the next time hash_scan_next() is called, a null
* pointer shall be immediately returned.
*/
hnode_t *
hash_scan_next (hscan_t * scan)
{
hnode_t *next = scan->next; /* 1 */
hash_t *hash = scan->table;
hash_val_t chain = scan->chain + 1;
hash_val_t nchains = hash->nchains;
nassert (hash_val_t_bit != 0); /* 2 */
if (next) { /* 3 */
if (next->next) { /* 4 */
scan->next = next->next;
} else {
while (chain < nchains && hash->table[chain] == 0) /* 5 */
chain++;
if (chain < nchains) { /* 6 */
scan->chain = chain;
scan->next = hash->table[chain];
} else {
scan->next = NULL;
}
}
}
return next;
}
/*
* Insert a node into the hash table.
* Notes:
* 1. It's illegal to insert more than the maximum number of nodes. The client
* should verify that the hash table is not full before attempting an
* insertion.
* 2. The same key may not be inserted into a table twice.
* 3. If the table is dynamic and the load factor is already at >= 2,
* grow the table.
* 4. We take the bottom N bits of the hash value to derive the chain index,
* where N is the base 2 logarithm of the size of the hash table.
*/
void
hash_insert (hash_t * hash, hnode_t * node, const void *key)
{
hash_val_t hkey, chain;
nassert (hash_val_t_bit != 0);
nassert (node->next == NULL);
nassert (hash->nodecount < hash->maxcount); /* 1 */
nassert (hash_lookup (hash, key) == NULL); /* 2 */
if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */
grow_table (hash);
hkey = hash->function (key);
chain = hkey & hash->mask; /* 4 */
node->key = key;
node->hkey = hkey;
node->next = hash->table[chain];
hash->table[chain] = node;
hash->nodecount++;
nassert (hash_verify (hash));
}
/*
* Find a node in the hash table and return a pointer to it.
* Notes:
* 1. We hash the key and keep the entire hash value. As an optimization, when
* we descend down the chain, we can compare hash values first and only if
* hash values match do we perform a full key comparison.
* 2. To locate the chain from among 2^N chains, we look at the lower N bits of
* the hash value by anding them with the current mask.
* 3. Looping through the chain, we compare the stored hash value inside each
* node against our computed hash. If they match, then we do a full
* comparison between the unhashed keys. If these match, we have located the
* entry.
*/
hnode_t *
hash_lookup (hash_t * hash, const void *key)
{
hash_val_t hkey, chain;
hnode_t *nptr;
hkey = hash->function (key); /* 1 */
chain = hkey & hash->mask; /* 2 */
for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */
if (nptr->hkey == hkey && hash->compare (nptr->key, key) == 0)
return nptr;
}
return NULL;
}
/*
* Delete the given node from the hash table. Since the chains
* are singly linked, we must locate the start of the node's chain
* and traverse.
* Notes:
* 1. The node must belong to this hash table, and its key must not have
* been tampered with.
* 2. If this deletion will take the node count below the low mark, we
* shrink the table now.
* 3. Determine which chain the node belongs to, and fetch the pointer
* to the first node in this chain.
* 4. If the node being deleted is the first node in the chain, then
* simply update the chain head pointer.
* 5. Otherwise advance to the node's predecessor, and splice out
* by updating the predecessor's next pointer.
* 6. Indicate that the node is no longer in a hash table.
*/
hnode_t *
hash_delete (hash_t * hash, hnode_t * node)
{
hash_val_t chain;
hnode_t *hptr;
nassert (hash_lookup (hash, node->key) == node); /* 1 */
nassert (hash_val_t_bit != 0);
if (hash->dynamic && hash->nodecount <= hash->lowmark && hash->nodecount > INIT_SIZE)
shrink_table (hash); /* 2 */
chain = node->hkey & hash->mask; /* 3 */
hptr = hash->table[chain];
if (hptr == node) { /* 4 */
hash->table[chain] = node->next;
} else {
while (hptr->next != node) { /* 5 */
nassert (hptr != 0);
hptr = hptr->next;
}
nassert (hptr->next == node);
hptr->next = node->next;
}
hash->nodecount--;
nassert (hash_verify (hash));
node->next = NULL; /* 6 */
return node;
}
int
hash_alloc_insert (hash_t * hash, const void *key, void *data)
{
hnode_t *node = hash->allocnode (hash->context);
if (node) {
hnode_init (node, data);
hash_insert (hash, node, key);
return 1;
}
return 0;
}
void
hash_delete_free (hash_t * hash, hnode_t * node)
{
hash_delete (hash, node);
hash->freenode (node, hash->context);
}
/*
* Exactly like hash_delete, except does not trigger table shrinkage. This is to be
* used from within a hash table scan operation. See notes for hash_delete.
*/
hnode_t *
hash_scan_delete (hash_t * hash, hnode_t * node)
{
hash_val_t chain;
hnode_t *hptr;
nassert (hash_lookup (hash, node->key) == node);
nassert (hash_val_t_bit != 0);
chain = node->hkey & hash->mask;
hptr = hash->table[chain];
if (hptr == node) {
hash->table[chain] = node->next;
} else {
while (hptr->next != node)
hptr = hptr->next;
hptr->next = node->next;
}
hash->nodecount--;
nassert (hash_verify (hash));
node->next = NULL;
return node;
}
/*
* Like hash_delete_free but based on hash_scan_delete.
*/
void
hash_scan_delfree (hash_t * hash, hnode_t * node)
{
hash_scan_delete (hash, node);
hash->freenode (node, hash->context);
}
/*
* Verify whether the given object is a valid hash table. This means
* Notes:
* 1. If the hash table is dynamic, verify whether the high and
* low expansion/shrinkage thresholds are powers of two.
* 2. Count all nodes in the table, and test each hash value
* to see whether it is correct for the node's chain.
*/
int
hash_verify (hash_t * hash)
{
hashcount_t count = 0;
hash_val_t chain;
hnode_t *hptr;
if (hash->dynamic) { /* 1 */
if (hash->lowmark >= hash->highmark)
return 0;
if (!is_power_of_two (hash->highmark))
return 0;
if (!is_power_of_two (hash->lowmark))
return 0;
}
for (chain = 0; chain < hash->nchains; chain++) { /* 2 */
for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) {
if ((hptr->hkey & hash->mask) != chain)
return 0;
count++;
}
}
if (count != hash->nodecount)
return 0;
return 1;
}
/*
* Test whether the hash table is full and return 1 if this is true,
* 0 if it is false.
*/
#undef hash_isfull
int
hash_isfull (hash_t * hash)
{
return hash->nodecount == hash->maxcount;
}
/*
* Test whether the hash table is empty and return 1 if this is true,
* 0 if it is false.
*/
#undef hash_isempty
int
hash_isempty (hash_t * hash)
{
return hash->nodecount == 0;
}
static hnode_t *
hnode_alloc (void *context)
{
return malloc (sizeof *hnode_alloc (NULL));
}
static void
hnode_free (hnode_t * node, void *context)
{
free (node);
}
/*
* Create a hash table node dynamically and assign it the given data.
*/
hnode_t *
hnode_create (void *data)
{
hnode_t *node = malloc (sizeof *node);
if (node) {
node->data = data;
node->next = NULL;
}
return node;
}
/*
* Initialize a client-supplied node
*/
hnode_t *
hnode_init (hnode_t * hnode, void *data)
{
hnode->data = data;
hnode->next = NULL;
return hnode;
}
/*
* Destroy a dynamically allocated node.
*/
void
hnode_destroy (hnode_t * hnode)
{
free (hnode);
}
#undef hnode_put
void
hnode_put (hnode_t * node, void *data)
{
node->data = data;
}
#undef hnode_get
void *
hnode_get (hnode_t * node)
{
return node->data;
}
#undef hnode_getkey
const void *
hnode_getkey (hnode_t * node)
{
return node->key;
}
#undef hash_count
hashcount_t
hash_count (hash_t * hash)
{
return hash->nodecount;
}
#undef hash_size
hashcount_t
hash_size (hash_t * hash)
{
return hash->nchains;
}
static hash_val_t
hash_fun_default (const void *key)
{
static unsigned long randbox[] = {
0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
};
const unsigned char *str = key;
hash_val_t acc = 0;
while (*str) {
acc ^= randbox[(tolower (*str) + acc) & 0xf];
acc = (acc << 1) | (acc >> 31);
acc &= 0xffffffffU;
acc ^= randbox[((tolower (*str++) >> 4) + acc) & 0xf];
acc = (acc << 2) | (acc >> 30);
acc &= 0xffffffffU;
}
return acc;
}
static int
hash_comp_default (const void *key1, const void *key2)
{
return strcasecmp (key1, key2);
}

261
hash.h Normal file
View file

@ -0,0 +1,261 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** Portions borrowed from kazlib. Original Header follows:
*/
/*
* Hash Table Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* Free Software License:
*
* All rights are reserved by the author, with the following exceptions:
* Permission is granted to freely reproduce and distribute this software,
* possibly in exchange for a fee, provided that this copyright notice appears
* intact. Permission is also granted to adapt this software to produce
* derivative works, as long as the modified versions carry this copyright
* notice and additional notices stating that the work has been modified.
* This source code may be translated into executable form and incorporated
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
* $Id$
* $Name: $
*/
#ifndef HASH_H
#define HASH_H
#include <limits.h>
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#include "sfx.h"
#endif
/*
* Blurb for inclusion into C++ translation units
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned long hashcount_t;
#define HASHCOUNT_T_MAX ULONG_MAX
typedef unsigned long hash_val_t;
#define HASH_VAL_T_MAX ULONG_MAX
extern int hash_val_t_bit;
#ifndef HASH_VAL_T_BIT
#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
#endif
/*
* Hash chain node structure.
* Notes:
* 1. This preprocessing directive is for debugging purposes. The effect is
* that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
* inclusion of this header, then the structure shall be declared as having
* the single member int __OPAQUE__. This way, any attempts by the
* client code to violate the principles of information hiding (by accessing
* the structure directly) can be diagnosed at translation time. However,
* note the resulting compiled unit is not suitable for linking.
* 2. This is a pointer to the next node in the chain. In the last node of a
* chain, this pointer is null.
* 3. The key is a pointer to some user supplied data that contains a unique
* identifier for each hash node in a given table. The interpretation of
* the data is up to the user. When creating or initializing a hash table,
* the user must supply a pointer to a function for comparing two keys,
* and a pointer to a function for hashing a key into a numeric value.
* 4. The value is a user-supplied pointer to void which may refer to
* any data object. It is not interpreted in any way by the hashing
* module.
* 5. The hashed key is stored in each node so that we don't have to rehash
* each key when the table must grow or shrink.
*/
typedef struct hnode_t {
#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */
struct hnode_t *hash_next; /* 2 */
const void *hash_key; /* 3 */
void *hash_data; /* 4 */
hash_val_t hash_hkey; /* 5 */
#else
int hash_dummy;
#endif
} hnode_t;
/*
* The comparison function pointer type. A comparison function takes two keys
* and produces a value of -1 if the left key is less than the right key, a
* value of 0 if the keys are equal, and a value of 1 if the left key is
* greater than the right key.
*/
typedef int (*hash_comp_t) (const void *, const void *);
/*
* The hashing function performs some computation on a key and produces an
* integral value of type hash_val_t based on that key. For best results, the
* function should have a good randomness properties in *all* significant bits
* over the set of keys that are being inserted into a given hash table. In
* particular, the most significant bits of hash_val_t are most significant to
* the hash module. Only as the hash table expands are less significant bits
* examined. Thus a function that has good distribution in its upper bits but
* not lower is preferrable to one that has poor distribution in the upper bits
* but not the lower ones.
*/
typedef hash_val_t (*hash_fun_t) (const void *);
/*
* allocator functions
*/
typedef hnode_t *(*hnode_alloc_t) (void *);
typedef void (*hnode_free_t) (hnode_t *, void *);
/*
* This is the hash table control structure. It keeps track of information
* about a hash table, as well as the hash table itself.
* Notes:
* 1. Pointer to the hash table proper. The table is an array of pointers to
* hash nodes (of type hnode_t). If the table is empty, every element of
* this table is a null pointer. A non-null entry points to the first
* element of a chain of nodes.
* 2. This member keeps track of the size of the hash table---that is, the
* number of chain pointers.
* 3. The count member maintains the number of elements that are presently
* in the hash table.
* 4. The maximum count is the greatest number of nodes that can populate this
* table. If the table contains this many nodes, no more can be inserted,
* and the hash_isfull() function returns true.
* 5. The high mark is a population threshold, measured as a number of nodes,
* which, if exceeded, will trigger a table expansion. Only dynamic hash
* tables are subject to this expansion.
* 6. The low mark is a minimum population threshold, measured as a number of
* nodes. If the table population drops below this value, a table shrinkage
* will occur. Only dynamic tables are subject to this reduction. No table
* will shrink beneath a certain absolute minimum number of nodes.
* 7. This is the a pointer to the hash table's comparison function. The
* function is set once at initialization or creation time.
* 8. Pointer to the table's hashing function, set once at creation or
* initialization time.
* 9. The current hash table mask. If the size of the hash table is 2^N,
* this value has its low N bits set to 1, and the others clear. It is used
* to select bits from the result of the hashing function to compute an
* index into the table.
* 10. A flag which indicates whether the table is to be dynamically resized. It
* is set to 1 in dynamically allocated tables, 0 in tables that are
* statically allocated.
*/
typedef struct hash_t {
#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
struct hnode_t **hash_table; /* 1 */
hashcount_t hash_nchains; /* 2 */
hashcount_t hash_nodecount; /* 3 */
hashcount_t hash_maxcount; /* 4 */
hashcount_t hash_highmark; /* 5 */
hashcount_t hash_lowmark; /* 6 */
hash_comp_t hash_compare; /* 7 */
hash_fun_t hash_function; /* 8 */
hnode_alloc_t hash_allocnode;
hnode_free_t hash_freenode;
void *hash_context;
hash_val_t hash_mask; /* 9 */
int hash_dynamic; /* 10 */
#else
int hash_dummy;
#endif
} hash_t;
/*
* Hash scanner structure, used for traversals of the data structure.
* Notes:
* 1. Pointer to the hash table that is being traversed.
* 2. Reference to the current chain in the table being traversed (the chain
* that contains the next node that shall be retrieved).
* 3. Pointer to the node that will be retrieved by the subsequent call to
* hash_scan_next().
*/
typedef struct hscan_t {
#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
hash_t *hash_table; /* 1 */
hash_val_t hash_chain; /* 2 */
hnode_t *hash_next; /* 3 */
#else
int hash_dummy;
#endif
} hscan_t;
extern hash_t *hash_create (hashcount_t, hash_comp_t, hash_fun_t);
extern void hash_set_allocator (hash_t *, hnode_alloc_t, hnode_free_t, void *);
extern void hash_destroy (hash_t *);
extern void hash_free_nodes (hash_t *);
extern void hash_free (hash_t *);
extern hash_t *hash_init (hash_t *, hashcount_t, hash_comp_t, hash_fun_t, hnode_t **, hashcount_t);
extern void hash_insert (hash_t *, hnode_t *, const void *);
extern hnode_t *hash_lookup (hash_t *, const void *);
extern hnode_t *hash_delete (hash_t *, hnode_t *);
extern int hash_alloc_insert (hash_t *, const void *, void *);
extern void hash_delete_free (hash_t *, hnode_t *);
extern void hnode_put (hnode_t *, void *);
extern void *hnode_get (hnode_t *);
extern const void *hnode_getkey (hnode_t *);
extern hashcount_t hash_count (hash_t *);
extern hashcount_t hash_size (hash_t *);
extern int hash_isfull (hash_t *);
extern int hash_isempty (hash_t *);
extern void hash_scan_begin (hscan_t *, hash_t *);
extern hnode_t *hash_scan_next (hscan_t *);
extern hnode_t *hash_scan_delete (hash_t *, hnode_t *);
extern void hash_scan_delfree (hash_t *, hnode_t *);
extern int hash_verify (hash_t *);
extern hnode_t *hnode_create (void *);
extern hnode_t *hnode_init (hnode_t *, void *);
extern void hnode_destroy (hnode_t *);
#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount)
#else
#define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount)
#endif
#define hash_isempty(H) ((H)->hash_nodecount == 0)
#define hash_count(H) ((H)->hash_nodecount)
#define hash_size(H) ((H)->hash_nchains)
#define hnode_get(N) ((N)->hash_data)
#define hnode_getkey(N) ((N)->hash_key)
#define hnode_put(N, V) ((N)->hash_data = (V))
#endif
#ifdef __cplusplus
}
#endif
#endif

251
install-sh Executable file
View file

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
:
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=$mkdirprog
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f "$src" ] || [ -d "$src" ]
then
:
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
:
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
:
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
:
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
:
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

33
libevent/Makefile.am Normal file
View file

@ -0,0 +1,33 @@
AUTOMAKE_OPTIONS = foreign no-dependencies
CFLAGS = -Wall @CFLAGS@
SUBDIRS = . sample test
EXTRA_DIST = acconfig.h err.c event.h evsignal.h event.3 kqueue.c \
epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
sample/Makefile.am sample/Makefile.in sample/event-test.c \
sample/signal-test.c sample/time-test.c \
test/Makefile.am test/Makefile.in test/bench.c test/regress.c \
test/test-eof.c test/test-weof.c test/test-time.c \
test/test-init.c test/test.sh \
compat/err.h compat/sys/queue.h compat/sys/tree.h compat/sys/_time.h \
WIN32-Code WIN32-Code/config.h WIN32-Code/misc.c \
WIN32-Code/win32.c WIN32-Code/misc.h \
WIN32-Prj WIN32-Prj/event_test WIN32-Prj/event_test/event_test.dsp \
WIN32-Prj/event_test/test.txt WIN32-Prj/libevent.dsp \
WIN32-Prj/libevent.dsw WIN32-Prj/signal_test \
WIN32-Prj/signal_test/signal_test.dsp WIN32-Prj/time_test \
WIN32-Prj/time_test/time_test.dsp
lib_LIBRARIES = libevent.a
libevent_a_SOURCES = event.c buffer.c evbuffer.c
libevent_a_LIBADD = @LIBOBJS@
include_HEADERS = event.h
INCLUDES = -Icompat
man_MANS = event.3
DISTCLEANFILES = *~

523
libevent/Makefile.in Normal file
View file

@ -0,0 +1,523 @@
# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
DESTDIR =
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = .
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
CC = @CC@
LN_S = @LN_S@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
PACKAGE = @PACKAGE@
RANLIB = @RANLIB@
VERSION = @VERSION@
AUTOMAKE_OPTIONS = foreign no-dependencies
CFLAGS = -Wall @CFLAGS@
SUBDIRS = . sample test
EXTRA_DIST = acconfig.h err.c event.h evsignal.h event.3 kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c sample/Makefile.am sample/Makefile.in sample/event-test.c sample/signal-test.c sample/time-test.c test/Makefile.am test/Makefile.in test/bench.c test/regress.c test/test-eof.c test/test-weof.c test/test-time.c test/test-init.c test/test.sh compat/err.h compat/sys/queue.h compat/sys/tree.h compat/sys/_time.h WIN32-Code WIN32-Code/config.h WIN32-Code/misc.c WIN32-Code/win32.c WIN32-Code/misc.h WIN32-Prj WIN32-Prj/event_test WIN32-Prj/event_test/event_test.dsp WIN32-Prj/event_test/test.txt WIN32-Prj/libevent.dsp WIN32-Prj/libevent.dsw WIN32-Prj/signal_test WIN32-Prj/signal_test/signal_test.dsp WIN32-Prj/time_test WIN32-Prj/time_test/time_test.dsp
lib_LIBRARIES = libevent.a
libevent_a_SOURCES = event.c buffer.c evbuffer.c
libevent_a_LIBADD = @LIBOBJS@
include_HEADERS = event.h
INCLUDES = -Icompat
man_MANS = event.3
DISTCLEANFILES = *~
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
LIBRARIES = $(lib_LIBRARIES)
DEFS = @DEFS@ -I. -I$(srcdir) -I.
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
libevent_a_DEPENDENCIES = @LIBOBJS@
libevent_a_OBJECTS = event.o buffer.o evbuffer.o
AR = ar
COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
man3dir = $(mandir)/man3
MANS = $(man_MANS)
NROFF = nroff
HEADERS = $(include_HEADERS)
DIST_COMMON = ./stamp-h.in Makefile.am Makefile.in acconfig.h \
aclocal.m4 config.h.in configure configure.in err.c install-sh missing \
mkinstalldirs
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = tar
GZIP_ENV = --best
SOURCES = $(libevent_a_SOURCES)
OBJECTS = $(libevent_a_OBJECTS)
all: all-redirect
.SUFFIXES:
.SUFFIXES: .S .c .o .s
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ configure.in
cd $(srcdir) && $(ACLOCAL)
config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
$(srcdir)/configure: @MAINTAINER_MODE_TRUE@$(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES)
cd $(srcdir) && $(AUTOCONF)
config.h: stamp-h
@if test ! -f $@; then \
rm -f stamp-h; \
$(MAKE) stamp-h; \
else :; fi
stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES= CONFIG_HEADERS=config.h \
$(SHELL) ./config.status
@echo timestamp > stamp-h 2> /dev/null
$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@$(srcdir)/stamp-h.in
@if test ! -f $@; then \
rm -f $(srcdir)/stamp-h.in; \
$(MAKE) $(srcdir)/stamp-h.in; \
else :; fi
$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) acconfig.h
cd $(top_srcdir) && $(AUTOHEADER)
@echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null
mostlyclean-hdr:
clean-hdr:
distclean-hdr:
-rm -f config.h
maintainer-clean-hdr:
mostlyclean-libLIBRARIES:
clean-libLIBRARIES:
-test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
distclean-libLIBRARIES:
maintainer-clean-libLIBRARIES:
install-libLIBRARIES: $(lib_LIBRARIES)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(libdir)
@list='$(lib_LIBRARIES)'; for p in $$list; do \
if test -f $$p; then \
echo " $(INSTALL_DATA) $$p $(DESTDIR)$(libdir)/$$p"; \
$(INSTALL_DATA) $$p $(DESTDIR)$(libdir)/$$p; \
else :; fi; \
done
@$(POST_INSTALL)
@list='$(lib_LIBRARIES)'; for p in $$list; do \
if test -f $$p; then \
echo " $(RANLIB) $(DESTDIR)$(libdir)/$$p"; \
$(RANLIB) $(DESTDIR)$(libdir)/$$p; \
else :; fi; \
done
uninstall-libLIBRARIES:
@$(NORMAL_UNINSTALL)
list='$(lib_LIBRARIES)'; for p in $$list; do \
rm -f $(DESTDIR)$(libdir)/$$p; \
done
.c.o:
$(COMPILE) -c $<
.s.o:
$(COMPILE) -c $<
.S.o:
$(COMPILE) -c $<
mostlyclean-compile:
-rm -f *.o core *.core
clean-compile:
distclean-compile:
-rm -f *.tab.c
maintainer-clean-compile:
libevent.a: $(libevent_a_OBJECTS) $(libevent_a_DEPENDENCIES)
-rm -f libevent.a
$(AR) cru libevent.a $(libevent_a_OBJECTS) $(libevent_a_LIBADD)
$(RANLIB) libevent.a
install-man3:
$(mkinstalldirs) $(DESTDIR)$(man3dir)
@list='$(man3_MANS)'; \
l2='$(man_MANS)'; for i in $$l2; do \
case "$$i" in \
*.3*) list="$$list $$i" ;; \
esac; \
done; \
for i in $$list; do \
if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
else file=$$i; fi; \
ext=`echo $$i | sed -e 's/^.*\\.//'`; \
inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
inst=`echo $$inst | sed '$(transform)'`.$$ext; \
echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man3dir)/$$inst"; \
$(INSTALL_DATA) $$file $(DESTDIR)$(man3dir)/$$inst; \
done
uninstall-man3:
@list='$(man3_MANS)'; \
l2='$(man_MANS)'; for i in $$l2; do \
case "$$i" in \
*.3*) list="$$list $$i" ;; \
esac; \
done; \
for i in $$list; do \
ext=`echo $$i | sed -e 's/^.*\\.//'`; \
inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
inst=`echo $$inst | sed '$(transform)'`.$$ext; \
echo " rm -f $(DESTDIR)$(man3dir)/$$inst"; \
rm -f $(DESTDIR)$(man3dir)/$$inst; \
done
install-man: $(MANS)
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-man3
uninstall-man:
@$(NORMAL_UNINSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-man3
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(includedir)
@list='$(include_HEADERS)'; for p in $$list; do \
if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \
echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/$$p"; \
$(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/$$p; \
done
uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
list='$(include_HEADERS)'; for p in $$list; do \
rm -f $(DESTDIR)$(includedir)/$$p; \
done
# This directory's subdirectories are mostly independent; you can cd
# into them and run `make' without going through this Makefile.
# To change the values of `make' variables: instead of editing Makefiles,
# (1) if the variable is set in `config.status', edit `config.status'
# (which will cause the Makefiles to be regenerated when you run `make');
# (2) otherwise, pass the desired values on the `make' command line.
@SET_MAKE@
all-recursive install-data-recursive install-exec-recursive \
installdirs-recursive install-recursive uninstall-recursive \
check-recursive installcheck-recursive info-recursive dvi-recursive:
@set fnord $(MAKEFLAGS); amf=$$2; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
mostlyclean-recursive clean-recursive distclean-recursive \
maintainer-clean-recursive:
@set fnord $(MAKEFLAGS); amf=$$2; \
dot_seen=no; \
rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
rev="$$subdir $$rev"; \
test "$$subdir" != "." || dot_seen=yes; \
true; \
done; \
test "$$dot_seen" = "no" && rev=". $$rev"; \
target=`echo $@ | sed s/-recursive//`; \
for subdir in $$rev; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
done && test -z "$$fail"
tags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
done
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP)
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
here=`pwd` && cd $(srcdir) \
&& mkid -f$$here/ID $$unique $(LISP)
TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \
fi; \
done; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) -o $$here/TAGS)
mostlyclean-tags:
clean-tags:
distclean-tags:
-rm -f TAGS ID
maintainer-clean-tags:
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
-rm -rf $(distdir)
GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz
mkdir $(distdir)/=build
mkdir $(distdir)/=inst
dc_install_base=`cd $(distdir)/=inst && pwd`; \
cd $(distdir)/=build \
&& ../configure --srcdir=.. --prefix=$$dc_install_base \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
&& $(MAKE) $(AM_MAKEFLAGS) install \
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
&& $(MAKE) $(AM_MAKEFLAGS) dist
-rm -rf $(distdir)
@banner="$(distdir).tar.gz is ready for distribution"; \
dashes=`echo "$$banner" | sed s/./=/g`; \
echo "$$dashes"; \
echo "$$banner"; \
echo "$$dashes"
dist: distdir
-chmod -R a+r $(distdir)
GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
-rm -rf $(distdir)
dist-all: distdir
-chmod -R a+r $(distdir)
GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
-rm -rf $(distdir)
distdir: $(DISTFILES)
-rm -rf $(distdir)
mkdir $(distdir)
-chmod 777 $(distdir)
$(mkinstalldirs) $(distdir)/WIN32-Code $(distdir)/WIN32-Prj \
$(distdir)/WIN32-Prj/event_test \
$(distdir)/WIN32-Prj/signal_test \
$(distdir)/WIN32-Prj/time_test $(distdir)/compat \
$(distdir)/compat/sys $(distdir)/sample $(distdir)/test
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $$d/$$file $(distdir)/$$file || :; \
fi; \
done
for subdir in $(SUBDIRS); do \
if test "$$subdir" = .; then :; else \
test -d $(distdir)/$$subdir \
|| mkdir $(distdir)/$$subdir \
|| exit 1; \
chmod 777 $(distdir)/$$subdir; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \
|| exit 1; \
fi; \
done
info-am:
info: info-recursive
dvi-am:
dvi: dvi-recursive
check-am: all-am
check: check-recursive
installcheck-am:
installcheck: installcheck-recursive
all-recursive-am: config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
install-exec-am: install-libLIBRARIES
install-exec: install-exec-recursive
install-data-am: install-man install-includeHEADERS
install-data: install-data-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
install: install-recursive
uninstall-am: uninstall-libLIBRARIES uninstall-man \
uninstall-includeHEADERS
uninstall: uninstall-recursive
all-am: Makefile $(LIBRARIES) $(MANS) $(HEADERS) config.h
all-redirect: all-recursive-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
installdirs: installdirs-recursive
installdirs-am:
$(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(mandir)/man3 \
$(DESTDIR)$(includedir)
mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f Makefile $(CONFIG_CLEAN_FILES)
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
-test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
maintainer-clean-generic:
mostlyclean-am: mostlyclean-hdr mostlyclean-libLIBRARIES \
mostlyclean-compile mostlyclean-tags \
mostlyclean-generic
mostlyclean: mostlyclean-recursive
clean-am: clean-hdr clean-libLIBRARIES clean-compile clean-tags \
clean-generic mostlyclean-am
clean: clean-recursive
distclean-am: distclean-hdr distclean-libLIBRARIES distclean-compile \
distclean-tags distclean-generic clean-am
distclean: distclean-recursive
-rm -f config.status
maintainer-clean-am: maintainer-clean-hdr maintainer-clean-libLIBRARIES \
maintainer-clean-compile maintainer-clean-tags \
maintainer-clean-generic distclean-am
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
maintainer-clean: maintainer-clean-recursive
-rm -f config.status
.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
mostlyclean-libLIBRARIES distclean-libLIBRARIES clean-libLIBRARIES \
maintainer-clean-libLIBRARIES uninstall-libLIBRARIES \
install-libLIBRARIES mostlyclean-compile distclean-compile \
clean-compile maintainer-clean-compile install-man3 uninstall-man3 \
install-man uninstall-man uninstall-includeHEADERS \
install-includeHEADERS install-data-recursive uninstall-data-recursive \
install-exec-recursive uninstall-exec-recursive installdirs-recursive \
uninstalldirs-recursive all-recursive check-recursive \
installcheck-recursive info-recursive dvi-recursive \
mostlyclean-recursive distclean-recursive clean-recursive \
maintainer-clean-recursive tags tags-recursive mostlyclean-tags \
distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
dvi-am dvi check check-am installcheck-am installcheck all-recursive-am \
install-exec-am install-exec install-data-am install-data install-am \
install uninstall-am uninstall all-redirect all-am all installdirs-am \
installdirs mostlyclean-generic distclean-generic clean-generic \
maintainer-clean-generic clean mostlyclean distclean maintainer-clean
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

64
libevent/acconfig.h Normal file
View file

@ -0,0 +1,64 @@
/* Define if kqueue works correctly with pipes */
#undef HAVE_WORKING_KQUEUE
/* Define to `unsigned long long' if <sys/types.h> doesn't define. */
#undef u_int64_t
/* Define to `unsigned int' if <sys/types.h> doesn't define. */
#undef u_int32_t
/* Define to `unsigned short' if <sys/types.h> doesn't define. */
#undef u_int16_t
/* Define to `unsigned char' if <sys/types.h> doesn't define. */
#undef u_int8_t
/* Define if timeradd is defined in <sys/time.h> */
#undef HAVE_TIMERADD
#ifndef HAVE_TIMERADD
#undef timerclear
#undef timerisset
#undef timercmp
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timercmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
#endif /* !HAVE_TIMERADD */
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
#undef HAVE_TAILQFOREACH
#ifndef HAVE_TAILQFOREACH
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#endif /* TAILQ_FOREACH */

190
libevent/aclocal.m4 vendored Normal file
View file

@ -0,0 +1,190 @@
dnl aclocal.m4 generated automatically by aclocal 1.4-p6
dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
dnl PARTICULAR PURPOSE.
# Do all the work for Automake. This macro actually does too much --
# some checks are only needed if your package does certain things.
# But this isn't really a big deal.
# serial 1
dnl Usage:
dnl AM_INIT_AUTOMAKE(package,version, [no-define])
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])
PACKAGE=[$1]
AC_SUBST(PACKAGE)
VERSION=[$2]
AC_SUBST(VERSION)
dnl test to see if srcdir already configured
if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
ifelse([$3],,
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package]))
AC_REQUIRE([AM_SANITY_CHECK])
AC_REQUIRE([AC_ARG_PROGRAM])
dnl FIXME This is truly gross.
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AC_REQUIRE([AC_PROG_MAKE_SET])])
# Copyright 2002 Free Software Foundation, Inc.
# 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 2, 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., 59 Temple Place - Suite 330, Boston, MA
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
AC_DEFUN([AM_AUTOMAKE_VERSION],[am__api_version="1.4"])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION so it can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.4-p6])])
#
# Check to make sure that the build environment is sane.
#
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Just in case
sleep 1
echo timestamp > conftestfile
# Do `set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null`
if test "[$]*" = "X"; then
# -L didn't work.
set X `ls -t $srcdir/configure conftestfile`
fi
if test "[$]*" != "X $srcdir/configure conftestfile" \
&& test "[$]*" != "X conftestfile $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
test "[$]2" = conftestfile
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
rm -f conftest*
AC_MSG_RESULT(yes)])
dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY)
dnl The program must properly implement --version.
AC_DEFUN([AM_MISSING_PROG],
[AC_MSG_CHECKING(for working $2)
# Run test in a subshell; some versions of sh will print an error if
# an executable is not found, even if stderr is redirected.
# Redirect stdin to placate older versions of autoconf. Sigh.
if ($2 --version) < /dev/null > /dev/null 2>&1; then
$1=$2
AC_MSG_RESULT(found)
else
$1="$3/missing $2"
AC_MSG_RESULT(missing)
fi
AC_SUBST($1)])
# Like AC_CONFIG_HEADER, but automatically create stamp file.
AC_DEFUN([AM_CONFIG_HEADER],
[AC_PREREQ([2.12])
AC_CONFIG_HEADER([$1])
dnl When config.status generates a header, we must update the stamp-h file.
dnl This file resides in the same directory as the config header
dnl that is generated. We must strip everything past the first ":",
dnl and everything past the last "/".
AC_OUTPUT_COMMANDS(changequote(<<,>>)dnl
ifelse(patsubst(<<$1>>, <<[^ ]>>, <<>>), <<>>,
<<test -z "<<$>>CONFIG_HEADERS" || echo timestamp > patsubst(<<$1>>, <<^\([^:]*/\)?.*>>, <<\1>>)stamp-h<<>>dnl>>,
<<am_indx=1
for am_file in <<$1>>; do
case " <<$>>CONFIG_HEADERS " in
*" <<$>>am_file "*<<)>>
echo timestamp > `echo <<$>>am_file | sed -e 's%:.*%%' -e 's%[^/]*$%%'`stamp-h$am_indx
;;
esac
am_indx=`expr "<<$>>am_indx" + 1`
done<<>>dnl>>)
changequote([,]))])
# Add --enable-maintainer-mode option to configure.
# From Jim Meyering
# serial 1
AC_DEFUN([AM_MAINTAINER_MODE],
[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
dnl maintainer-mode is disabled by default
AC_ARG_ENABLE(maintainer-mode,
[ --enable-maintainer-mode enable make rules and dependencies not useful
(and sometimes confusing) to the casual installer],
USE_MAINTAINER_MODE=$enableval,
USE_MAINTAINER_MODE=no)
AC_MSG_RESULT($USE_MAINTAINER_MODE)
AM_CONDITIONAL(MAINTAINER_MODE, test $USE_MAINTAINER_MODE = yes)
MAINT=$MAINTAINER_MODE_TRUE
AC_SUBST(MAINT)dnl
]
)
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_SUBST($1_TRUE)
AC_SUBST($1_FALSE)
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi])

220
libevent/buffer.c Normal file
View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "event.h"
struct evbuffer *
evbuffer_new(void)
{
struct evbuffer *buffer;
buffer = calloc(1, sizeof(struct evbuffer));
return (buffer);
}
void
evbuffer_free(struct evbuffer *buffer)
{
if (buffer->buffer != NULL)
free(buffer->buffer);
free(buffer);
}
/*
* This is a destructive add. The data from one buffer moves into
* the other buffer.
*/
int
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
{
int res;
res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
if (res == 0)
evbuffer_drain(inbuf, inbuf->off);
return (res);
}
int
evbuffer_add_printf(struct evbuffer *buf, char *fmt, ...)
{
int res = -1;
char *msg;
va_list ap;
va_start(ap, fmt);
if (vasprintf(&msg, fmt, ap) == -1)
goto end;
res = strlen(msg);
if (evbuffer_add(buf, msg, res) == -1)
res = -1;
free(msg);
end:
va_end(ap);
return (res);
}
int
evbuffer_add(struct evbuffer *buf, u_char *data, size_t datlen)
{
size_t need = buf->off + datlen;
size_t oldoff = buf->off;
if (buf->totallen < need) {
void *newbuf;
int length = buf->totallen;
if (length < 256)
length = 256;
while (length < need)
length <<= 1;
if ((newbuf = realloc(buf->buffer, length)) == NULL)
return (-1);
buf->buffer = newbuf;
buf->totallen = length;
}
memcpy(buf->buffer + buf->off, data, datlen);
buf->off += datlen;
if (datlen && buf->cb != NULL)
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
return (0);
}
void
evbuffer_drain(struct evbuffer *buf, size_t len)
{
size_t oldoff = buf->off;
if (len >= buf->off) {
buf->off = 0;
goto done;
}
memmove(buf->buffer, buf->buffer + len, buf->off - len);
buf->off -= len;
done:
/* Tell someone about changes in this buffer */
if (buf->off != oldoff && buf->cb != NULL)
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
}
int
evbuffer_read(struct evbuffer *buffer, int fd, int howmuch)
{
u_char inbuf[4096];
int n;
if (howmuch < 0 || howmuch > sizeof(inbuf))
howmuch = sizeof(inbuf);
n = read(fd, inbuf, howmuch);
if (n == -1)
return (-1);
if (n == 0)
return (0);
evbuffer_add(buffer, inbuf, n);
return (n);
}
int
evbuffer_write(struct evbuffer *buffer, int fd)
{
int n;
n = write(fd, buffer->buffer, buffer->off);
if (n == -1)
return (-1);
if (n == 0)
return (0);
evbuffer_drain(buffer, n);
return (n);
}
u_char *
evbuffer_find(struct evbuffer *buffer, u_char *what, size_t len)
{
size_t remain = buffer->off;
u_char *search = buffer->buffer;
u_char *p;
while ((p = memchr(search, *what, remain)) != NULL && remain >= len) {
if (memcmp(p, what, len) == 0)
return (p);
search = p + 1;
remain = buffer->off - (size_t)(search - buffer->buffer);
}
return (NULL);
}
void evbuffer_setcb(struct evbuffer *buffer,
void (*cb)(struct evbuffer *, size_t, size_t, void *),
void *cbarg)
{
buffer->cb = cb;
buffer->cbarg = cbarg;
}

46
libevent/compat/err.h Normal file
View file

@ -0,0 +1,46 @@
/*
* err.h
*
* Adapted from OpenBSD libc *err* *warn* code.
*
* Copyright (c) 2000 Dug Song <dugsong@monkey.org>
*
* Copyright (c) 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)err.h 8.1 (Berkeley) 6/2/93
*/
#ifndef _ERR_H_
#define _ERR_H_
void err(int eval, const char *fmt, ...);
void warn(const char *fmt, ...);
void errx(int eval, const char *fmt, ...);
void warnx(const char *fmt, ...);
#endif /* !_ERR_H_ */

163
libevent/compat/sys/_time.h Normal file
View file

@ -0,0 +1,163 @@
/* $OpenBSD: time.h,v 1.11 2000/10/10 13:36:48 itojun Exp $ */
/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)time.h 8.2 (Berkeley) 7/10/94
*/
#ifndef _SYS_TIME_H_
#define _SYS_TIME_H_
#include <sys/types.h>
/*
* Structure returned by gettimeofday(2) system call,
* and used in other calls.
*/
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
/*
* Structure defined by POSIX.1b to be like a timeval.
*/
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* and nanoseconds */
};
#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
(ts)->tv_sec = (tv)->tv_sec; \
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
}
#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
(tv)->tv_sec = (ts)->tv_sec; \
(tv)->tv_usec = (ts)->tv_nsec / 1000; \
}
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
#define DST_NONE 0 /* not on dst */
#define DST_USA 1 /* USA style dst */
#define DST_AUST 2 /* Australian style dst */
#define DST_WET 3 /* Western European dst */
#define DST_MET 4 /* Middle European dst */
#define DST_EET 5 /* Eastern European dst */
#define DST_CAN 6 /* Canada */
/* Operations on timevals. */
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timercmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
/* Operations on timespecs. */
#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0
#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
#define timespeccmp(tsp, usp, cmp) \
(((tsp)->tv_sec == (usp)->tv_sec) ? \
((tsp)->tv_nsec cmp (usp)->tv_nsec) : \
((tsp)->tv_sec cmp (usp)->tv_sec))
#define timespecadd(tsp, usp, vsp) \
do { \
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
if ((vsp)->tv_nsec >= 1000000000L) { \
(vsp)->tv_sec++; \
(vsp)->tv_nsec -= 1000000000L; \
} \
} while (0)
#define timespecsub(tsp, usp, vsp) \
do { \
(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
if ((vsp)->tv_nsec < 0) { \
(vsp)->tv_sec--; \
(vsp)->tv_nsec += 1000000000L; \
} \
} while (0)
/*
* Names of the interval timers, and structure
* defining a timer setting.
*/
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
/*
* Getkerninfo clock information structure
*/
struct clockinfo {
int hz; /* clock frequency */
int tick; /* micro-seconds per hz tick */
int tickadj; /* clock skew rate for adjtime() */
int stathz; /* statistics clock frequency */
int profhz; /* profiling clock frequency */
};
#define CLOCK_REALTIME 0
#define CLOCK_VIRTUAL 1
#define CLOCK_PROF 2
#define TIMER_RELTIME 0x0 /* relative timer */
#define TIMER_ABSTIME 0x1 /* absolute timer */
/* --- stuff got cut here - niels --- */
#endif /* !_SYS_TIME_H_ */

488
libevent/compat/sys/queue.h Normal file
View file

@ -0,0 +1,488 @@
/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#ifndef WIN32
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
#endif
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head).cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head).cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

677
libevent/compat/sys/tree.h Normal file
View file

@ -0,0 +1,677 @@
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SYS_TREE_H_
#define _SYS_TREE_H_
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root))) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-back tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define RB_INITIALIZER(root) \
{ NULL }
#define RB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#define RB_LEFT(elm, field) (elm)->field.rbe_left
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_COLOR(elm, field) (elm)->field.rbe_color
#define RB_ROOT(head) (head)->rbh_root
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(elm, parent, field) do { \
RB_PARENT(elm, field) = parent; \
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
RB_COLOR(elm, field) = RB_RED; \
} while (0)
#define RB_SET_BLACKRED(black, red, field) do { \
RB_COLOR(black, field) = RB_BLACK; \
RB_COLOR(red, field) = RB_RED; \
} while (0)
#ifndef RB_AUGMENT
#define RB_AUGMENT(x)
#endif
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
} \
RB_AUGMENT(elm); \
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
else \
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_LEFT(tmp, field) = (elm); \
RB_PARENT(elm, field) = (tmp); \
RB_AUGMENT(tmp); \
if ((RB_PARENT(tmp, field))) \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (0)
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
(tmp) = RB_LEFT(elm, field); \
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
} \
RB_AUGMENT(elm); \
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
else \
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_RIGHT(tmp, field) = (elm); \
RB_PARENT(elm, field) = (tmp); \
RB_AUGMENT(tmp); \
if ((RB_PARENT(tmp, field))) \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (0)
/* Generates prototypes and inline functions */
#define RB_PROTOTYPE(name, type, field, cmp) \
void name##_RB_INSERT_COLOR(struct name *, struct type *); \
void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
struct type *name##_RB_REMOVE(struct name *, struct type *); \
struct type *name##_RB_INSERT(struct name *, struct type *); \
struct type *name##_RB_FIND(struct name *, struct type *); \
struct type *name##_RB_NEXT(struct type *); \
struct type *name##_RB_MINMAX(struct name *, int); \
\
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define RB_GENERATE(name, type, field, cmp) \
void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = RB_PARENT(elm, field)) && \
RB_COLOR(parent, field) == RB_RED) { \
gparent = RB_PARENT(parent, field); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_COLOR(tmp, field) = RB_BLACK; \
RB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_RIGHT(parent, field) == elm) { \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(parent, gparent, field); \
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
} else { \
tmp = RB_LEFT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_COLOR(tmp, field) = RB_BLACK; \
RB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_LEFT(parent, field) == elm) { \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(parent, gparent, field); \
RB_ROTATE_LEFT(head, gparent, tmp, field); \
} \
} \
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
} \
\
void \
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
elm != RB_ROOT(head)) { \
if (RB_LEFT(parent, field) == elm) { \
tmp = RB_RIGHT(parent, field); \
if (RB_COLOR(tmp, field) == RB_RED) { \
RB_SET_BLACKRED(tmp, parent, field); \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
RB_COLOR(tmp, field) = RB_RED; \
elm = parent; \
parent = RB_PARENT(elm, field); \
} else { \
if (RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
struct type *oleft; \
if ((oleft = RB_LEFT(tmp, field)))\
RB_COLOR(oleft, field) = RB_BLACK;\
RB_COLOR(tmp, field) = RB_RED; \
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
tmp = RB_RIGHT(parent, field); \
} \
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
RB_COLOR(parent, field) = RB_BLACK; \
if (RB_RIGHT(tmp, field)) \
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
RB_ROTATE_LEFT(head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} else { \
tmp = RB_LEFT(parent, field); \
if (RB_COLOR(tmp, field) == RB_RED) { \
RB_SET_BLACKRED(tmp, parent, field); \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
RB_COLOR(tmp, field) = RB_RED; \
elm = parent; \
parent = RB_PARENT(elm, field); \
} else { \
if (RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
struct type *oright; \
if ((oright = RB_RIGHT(tmp, field)))\
RB_COLOR(oright, field) = RB_BLACK;\
RB_COLOR(tmp, field) = RB_RED; \
RB_ROTATE_LEFT(head, tmp, oright, field);\
tmp = RB_LEFT(parent, field); \
} \
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
RB_COLOR(parent, field) = RB_BLACK; \
if (RB_LEFT(tmp, field)) \
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
RB_ROTATE_RIGHT(head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
RB_COLOR(elm, field) = RB_BLACK; \
} \
\
struct type * \
name##_RB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (RB_LEFT(elm, field) == NULL) \
child = RB_RIGHT(elm, field); \
else if (RB_RIGHT(elm, field) == NULL) \
child = RB_LEFT(elm, field); \
else { \
struct type *left; \
elm = RB_RIGHT(elm, field); \
while ((left = RB_LEFT(elm, field))) \
elm = left; \
child = RB_RIGHT(elm, field); \
parent = RB_PARENT(elm, field); \
color = RB_COLOR(elm, field); \
if (child) \
RB_PARENT(child, field) = parent; \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
if (RB_PARENT(elm, field) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (RB_PARENT(old, field)) { \
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
RB_LEFT(RB_PARENT(old, field), field) = elm;\
else \
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
RB_AUGMENT(RB_PARENT(old, field)); \
} else \
RB_ROOT(head) = elm; \
RB_PARENT(RB_LEFT(old, field), field) = elm; \
if (RB_RIGHT(old, field)) \
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
if (parent) { \
left = parent; \
do { \
RB_AUGMENT(left); \
} while ((left = RB_PARENT(left, field))); \
} \
goto color; \
} \
parent = RB_PARENT(elm, field); \
color = RB_COLOR(elm, field); \
if (child) \
RB_PARENT(child, field) = parent; \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
color: \
if (color == RB_BLACK) \
name##_RB_REMOVE_COLOR(head, parent, child); \
return (old); \
} \
\
/* Inserts a node into the RB tree */ \
struct type * \
name##_RB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = RB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
RB_SET(elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
RB_LEFT(parent, field) = elm; \
else \
RB_RIGHT(parent, field) = elm; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = elm; \
name##_RB_INSERT_COLOR(head, elm); \
return (NULL); \
} \
\
/* Finds the node with the same key as elm */ \
struct type * \
name##_RB_FIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
} \
\
struct type * \
name##_RB_NEXT(struct type *elm) \
{ \
if (RB_RIGHT(elm, field)) { \
elm = RB_RIGHT(elm, field); \
while (RB_LEFT(elm, field)) \
elm = RB_LEFT(elm, field); \
} else { \
if (RB_PARENT(elm, field) && \
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
elm = RB_PARENT(elm, field); \
else { \
while (RB_PARENT(elm, field) && \
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
elm = RB_PARENT(elm, field); \
elm = RB_PARENT(elm, field); \
} \
} \
return (elm); \
} \
\
struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = RB_LEFT(tmp, field); \
else \
tmp = RB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define RB_NEGINF -1
#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
#define RB_FOREACH(x, name, head) \
for ((x) = RB_MIN(name, head); \
(x) != NULL; \
(x) = name##_RB_NEXT(x))
#endif /* _SYS_TREE_H_ */

191
libevent/config.h.in Normal file
View file

@ -0,0 +1,191 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Define if kqueue works correctly with pipes */
#undef HAVE_WORKING_KQUEUE
/* Define to `unsigned long long' if <sys/types.h> doesn't define. */
#undef u_int64_t
/* Define to `unsigned int' if <sys/types.h> doesn't define. */
#undef u_int32_t
/* Define to `unsigned short' if <sys/types.h> doesn't define. */
#undef u_int16_t
/* Define to `unsigned char' if <sys/types.h> doesn't define. */
#undef u_int8_t
/* Define if timeradd is defined in <sys/time.h> */
#undef HAVE_TIMERADD
#ifndef HAVE_TIMERADD
#undef timerclear
#undef timerisset
#undef timercmp
#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timercmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \
((tvp)->tv_usec cmp (uvp)->tv_usec) : \
((tvp)->tv_sec cmp (uvp)->tv_sec))
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
#endif /* !HAVE_TIMERADD */
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
#undef HAVE_TAILQFOREACH
#ifndef HAVE_TAILQFOREACH
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#endif /* TAILQ_FOREACH */
/* Define if your system supports the epoll system calls */
#undef HAVE_EPOLL
/* Define if you have the `epoll_ctl' function. */
#undef HAVE_EPOLL_CTL
/* Define if you have the `err' function. */
#undef HAVE_ERR
/* Define if you have the `gettimeofday' function. */
#undef HAVE_GETTIMEOFDAY
/* Define if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define if you have the `kqueue' function. */
#undef HAVE_KQUEUE
/* Define if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define if you have the `poll' function. */
#undef HAVE_POLL
/* Define if you have the <poll.h> header file. */
#undef HAVE_POLL_H
/* Define if your system supports POSIX realtime signals */
#undef HAVE_RTSIG
/* Define if you have the `select' function. */
#undef HAVE_SELECT
/* Define if you have the <signal.h> header file. */
#undef HAVE_SIGNAL_H
/* Define if you have the `sigtimedwait' function. */
#undef HAVE_SIGTIMEDWAIT
/* Define if you have the <stdarg.h> header file. */
#undef HAVE_STDARG_H
/* Define if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define if you have the <sys/epoll.h> header file. */
#undef HAVE_SYS_EPOLL_H
/* Define if you have the <sys/event.h> header file. */
#undef HAVE_SYS_EVENT_H
/* Define if you have the <sys/queue.h> header file. */
#undef HAVE_SYS_QUEUE_H
/* Define if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
#undef HAVE_TAILQFOREACH
/* Define if timeradd is defined in <sys/time.h> */
#undef HAVE_TIMERADD
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the `vasprintf' function. */
#undef HAVE_VASPRINTF
/* Define if kqueue works correctly with pipes */
#undef HAVE_WORKING_KQUEUE
/* Define if realtime signals work on pipes */
#undef HAVE_WORKING_RTSIG
/* Name of package */
#undef PACKAGE
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Version number of package */
#undef VERSION
/* Define to `int' if <sys/types.h> does not define. */
#undef pid_t
/* Define to `unsigned' if <sys/types.h> does not define. */
#undef size_t
/* Define to unsigned int if you dont have it */
#undef socklen_t
/* Define to `unsigned short' if <sys/types.h> does not define. */
#undef u_int16_t
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef u_int32_t
/* Define to `unsigned long long' if <sys/types.h> does not define. */
#undef u_int64_t
/* Define to `unsigned char' if <sys/types.h> does not define. */
#undef u_int8_t

4731
libevent/configure vendored Executable file

File diff suppressed because it is too large Load diff

253
libevent/configure.in Normal file
View file

@ -0,0 +1,253 @@
dnl configure.in for libevent
dnl Dug Song <dugsong@monkey.org>
AC_INIT(event.c)
AM_INIT_AUTOMAKE(libevent,0.8)
AM_CONFIG_HEADER(config.h)
AM_MAINTAINER_MODE
dnl Initialize prefix.
if test "$prefix" = "NONE"; then
prefix="/usr/local"
fi
dnl Checks for programs.
AC_PROG_CC
AC_PROG_RANLIB
AC_PROG_INSTALL
AC_PROG_LN_S
dnl Check for optional stuff
AC_ARG_WITH(rtsig,
[ --with-rtsig compile with support for real time signals (experimental)],
[usertsig=yes], [usertsig=no])
dnl Checks for libraries.
AC_CHECK_LIB(socket, socket)
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h)
if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
AC_EGREP_CPP(yes,
[
#include <sys/queue.h>
#ifdef TAILQ_FOREACH
yes
#endif
], [AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_TAILQFOREACH, 1,
[Define if TAILQ_FOREACH is defined in <sys/queue.h>])],
AC_MSG_RESULT(no)
)
fi
if test "x$ac_cv_header_sys_time_h" = "xyes"; then
AC_MSG_CHECKING(for timeradd in sys/time.h)
AC_EGREP_CPP(yes,
[
#include <sys/time.h>
#ifdef timeradd
yes
#endif
], [ AC_DEFINE(HAVE_TIMERADD, 1,
[Define if timeradd is defined in <sys/time.h>])
AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
)
fi
dnl Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_TIME
dnl Checks for library functions.
AC_CHECK_FUNCS(gettimeofday vasprintf)
needsignal=no
haveselect=no
AC_CHECK_FUNCS(select, [haveselect=yes], )
if test "x$haveselect" = "xyes" ; then
AC_LIBOBJ(select)
needsignal=yes
fi
havepoll=no
havertsig=no
AC_CHECK_FUNCS(poll, [havepoll=yes], )
if test "x$havepoll" = "xyes" ; then
AC_LIBOBJ(poll)
needsignal=yes
if test "x$usertsig" = "xyes" ; then
AC_CHECK_FUNCS(sigtimedwait, [havertsig=yes], )
fi
fi
if test "x$havertsig" = "xyes" ; then
AC_MSG_CHECKING(for F_SETSIG in fcntl.h)
AC_EGREP_CPP(yes,
[
#define _GNU_SOURCE
#include <fcntl.h>
#ifdef F_SETSIG
yes
#endif
], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no); havertsig=no])
fi
if test "x$havertsig" = "xyes" ; then
AC_DEFINE(HAVE_RTSIG, 1, [Define if your system supports POSIX realtime signals])
AC_LIBOBJ(rtsig)
AC_MSG_CHECKING(for working rtsig on pipes)
AC_TRY_RUN(
[
#define _GNU_SOURCE
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
int sigio()
{
exit(0);
}
int main()
{
int fd[2];
pipe(fd);
signal(SIGIO, sigio);
fcntl(fd[0], F_SETOWN, getpid());
fcntl(fd[0], F_SETSIG, SIGIO);
fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_ASYNC);
write(fd[1], "", 1);
return 1;
}
], [ AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_WORKING_RTSIG, 1, [Define if realtime signals work on pipes])],
AC_MSG_RESULT(no))
fi
haveepoll=no
AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], )
if test "x$haveepoll" = "xyes" ; then
AC_DEFINE(HAVE_EPOLL, 1,
[Define if your system supports the epoll system calls])
AC_LIBOBJ(epoll)
needsignal=yes
fi
havekqueue=no
if test "x$ac_cv_header_sys_event_h" = "xyes"; then
AC_CHECK_FUNCS(kqueue, [havekqueue=yes], )
if test "x$havekqueue" = "xyes" ; then
AC_MSG_CHECKING(for working kqueue)
AC_TRY_RUN(
#include <sys/types.h>
#include <sys/time.h>
#include <sys/event.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int
main(int argc, char **argv)
{
int kq;
int n;
int fd[[2]];
struct kevent ev;
struct timespec ts;
char buf[[8000]];
if (pipe(fd) == -1)
exit(1);
if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1)
exit(1);
while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf))
;
if ((kq = kqueue()) == -1)
exit(1);
ev.ident = fd[[1]];
ev.filter = EVFILT_WRITE;
ev.flags = EV_ADD | EV_ENABLE;
n = kevent(kq, &ev, 1, NULL, 0, NULL);
if (n == -1)
exit(1);
read(fd[[0]], buf, sizeof(buf));
ts.tv_sec = 0;
ts.tv_nsec = 0;
n = kevent(kq, NULL, 0, &ev, 1, &ts);
if (n == -1 || n == 0)
exit(1);
exit(0);
}, [AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_WORKING_KQUEUE, 1,
[Define if kqueue works correctly with pipes])
AC_LIBOBJ(kqueue)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
fi
fi
haveepollsyscall=no
if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then
if test "x$haveepoll" = "xno" ; then
AC_MSG_CHECKING(for epoll system call)
AC_TRY_RUN(
#include <stdint.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/epoll.h>
#include <unistd.h>
int
epoll_create(int size)
{
return (syscall(__NR_epoll_create, size));
}
int
main(int argc, char **argv)
{
int epfd;
epfd = epoll_create(256);
exit (epfd == -1 ? 1 : 0);
}, [AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_EPOLL, 1,
[Define if your system supports the epoll system calls])
needsignal=yes
AC_LIBOBJ(epoll_sub)
AC_LIBOBJ(epoll)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
fi
fi
if test "x$needsignal" = "xyes" ; then
AC_LIBOBJ(signal)
fi
AC_REPLACE_FUNCS(err)
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_CHECK_TYPE(u_int64_t, unsigned long long)
AC_CHECK_TYPE(u_int32_t, unsigned int)
AC_CHECK_TYPE(u_int16_t, unsigned short)
AC_CHECK_TYPE(u_int8_t, unsigned char)
AC_MSG_CHECKING([for socklen_t])
AC_TRY_COMPILE([
#include <sys/types.h>
#include <sys/socket.h>],
[socklen_t x;],
AC_MSG_RESULT([yes]),
[AC_MSG_RESULT([no])
AC_DEFINE(socklen_t, unsigned int,
[Define to unsigned int if you dont have it])]
)
AC_OUTPUT(Makefile test/Makefile sample/Makefile)

335
libevent/epoll.c Normal file
View file

@ -0,0 +1,335 @@
/*
* Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdint.h>
#include <sys/types.h>
#include <sys/resource.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/epoll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error warn
#endif
#include "event.h"
#include "evsignal.h"
extern struct event_list eventqueue;
extern volatile sig_atomic_t evsignal_caught;
/* due to limitations in the epoll interface, we need to keep track of
* all file descriptors outself.
*/
struct evepoll {
struct event *evread;
struct event *evwrite;
};
struct epollop {
struct evepoll *fds;
int nfds;
struct epoll_event *events;
int nevents;
int epfd;
sigset_t evsigmask;
} epollop;
void *epoll_init (void);
int epoll_add (void *, struct event *);
int epoll_del (void *, struct event *);
int epoll_recalc (void *, int);
int epoll_dispatch (void *, struct timeval *);
struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_recalc,
epoll_dispatch
};
#define NEVENT 32000
void *
epoll_init(void)
{
int epfd, nfiles = NEVENT;
struct rlimit rl;
/* Disable epollueue when this environment variable is set */
if (getenv("EVENT_NOEPOLL"))
return (NULL);
memset(&epollop, 0, sizeof(epollop));
if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
rl.rlim_cur != RLIM_INFINITY)
nfiles = rl.rlim_cur;
/* Initalize the kernel queue */
if ((epfd = epoll_create(nfiles)) == -1) {
log_error("epoll_create");
return (NULL);
}
epollop.epfd = epfd;
/* Initalize fields */
epollop.events = malloc(nfiles * sizeof(struct epoll_event));
if (epollop.events == NULL)
return (NULL);
epollop.nevents = nfiles;
epollop.fds = calloc(nfiles, sizeof(struct evepoll));
if (epollop.fds == NULL) {
free(epollop.events);
return (NULL);
}
epollop.nfds = nfiles;
evsignal_init(&epollop.evsigmask);
return (&epollop);
}
int
epoll_recalc(void *arg, int max)
{
struct epollop *epollop = arg;
if (max > epollop->nfds) {
struct evepoll *fds;
int nfds;
nfds = epollop->nfds;
while (nfds < max)
nfds <<= 1;
fds = realloc(epollop->fds, nfds * sizeof(struct evepoll));
if (fds == NULL) {
log_error("realloc");
return (-1);
}
epollop->fds = fds;
memset(fds + epollop->nfds, 0,
(nfds - epollop->nfds) * sizeof(struct evepoll));
epollop->nfds = nfds;
}
return (evsignal_recalc(&epollop->evsigmask));
}
int
epoll_dispatch(void *arg, struct timeval *tv)
{
struct epollop *epollop = arg;
struct epoll_event *events = epollop->events;
struct evepoll *evep;
int i, res, timeout;
if (evsignal_deliver(&epollop->evsigmask) == -1)
return (-1);
timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
if (evsignal_recalc(&epollop->evsigmask) == -1)
return (-1);
if (res == -1) {
if (errno != EINTR) {
log_error("epoll_wait");
return (-1);
}
evsignal_process();
return (0);
} else if (evsignal_caught)
evsignal_process();
LOG_DBG((LOG_MISC, 80, "%s: epoll_wait reports %d", __func__, res));
for (i = 0; i < res; i++) {
int which = 0;
int what = events[i].events;
struct event *evread = NULL, *evwrite = NULL;
evep = (struct evepoll *)events[i].data.ptr;
if (what & EPOLLHUP)
what |= EPOLLIN | EPOLLOUT;
else if (what & EPOLLERR)
what |= EPOLLIN | EPOLLOUT;
if (what & EPOLLIN) {
evread = evep->evread;
which |= EV_READ;
}
if (what & EPOLLOUT) {
evwrite = evep->evwrite;
which |= EV_WRITE;
}
if (!which)
continue;
if (evread != NULL && !(evread->ev_events & EV_PERSIST))
event_del(evread);
if (evwrite != NULL && evwrite != evread &&
!(evwrite->ev_events & EV_PERSIST))
event_del(evwrite);
if (evread != NULL)
event_active(evread, EV_READ, 1);
if (evwrite != NULL)
event_active(evwrite, EV_WRITE, 1);
}
return (0);
}
int
epoll_add(void *arg, struct event *ev)
{
struct epollop *epollop = arg;
struct epoll_event epev;
struct evepoll *evep;
int fd, op, events;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(&epollop->evsigmask, ev));
fd = ev->ev_fd;
if (fd >= epollop->nfds) {
/* Extent the file descriptor array as necessary */
if (epoll_recalc(epollop, fd) == -1)
return (-1);
}
evep = &epollop->fds[fd];
op = EPOLL_CTL_ADD;
events = 0;
if (evep->evread != NULL) {
events |= EPOLLIN;
op = EPOLL_CTL_MOD;
}
if (evep->evwrite != NULL) {
events |= EPOLLOUT;
op = EPOLL_CTL_MOD;
}
if (ev->ev_events & EV_READ)
events |= EPOLLIN;
if (ev->ev_events & EV_WRITE)
events |= EPOLLOUT;
epev.data.ptr = evep;
epev.events = events;
if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
return (-1);
/* Update events responsible */
if (ev->ev_events & EV_READ)
evep->evread = ev;
if (ev->ev_events & EV_WRITE)
evep->evwrite = ev;
return (0);
}
int
epoll_del(void *arg, struct event *ev)
{
struct epollop *epollop = arg;
struct epoll_event epev;
struct evepoll *evep;
int fd, events, op;
int needwritedelete = 1, needreaddelete = 1;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_del(&epollop->evsigmask, ev));
fd = ev->ev_fd;
if (fd >= epollop->nfds)
return (0);
evep = &epollop->fds[fd];
op = EPOLL_CTL_DEL;
events = 0;
if (ev->ev_events & EV_READ)
events |= EPOLLIN;
if (ev->ev_events & EV_WRITE)
events |= EPOLLOUT;
if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) {
if ((events & EPOLLIN) && evep->evwrite != NULL) {
needwritedelete = 0;
events = EPOLLOUT;
op = EPOLL_CTL_MOD;
} else if ((events & EPOLLOUT) && evep->evread != NULL) {
needreaddelete = 0;
events = EPOLLIN;
op = EPOLL_CTL_MOD;
}
}
epev.events = events;
epev.data.ptr = evep;
if (epoll_ctl(epollop->epfd, op, fd, &epev) == -1)
return (-1);
if (needreaddelete)
evep->evread = NULL;
if (needwritedelete)
evep->evwrite = NULL;
return (0);
}

52
libevent/epoll_sub.c Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/epoll.h>
#include <unistd.h>
int
epoll_create(int size)
{
return (syscall(__NR_epoll_create, size));
}
int
epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
{
return (syscall(__NR_epoll_ctl, epfd, op, fd, event));
}
int
epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
{
return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout));
}

97
libevent/err.c Normal file
View file

@ -0,0 +1,97 @@
/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* err.c
*
* Adapted from OpenBSD libc *err* *warn* code.
*
* Copyright (c) 2000 Dug Song <dugsong@monkey.org>
*
* Copyright (c) 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
void
err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fmt != NULL) {
(void)vfprintf(stderr, fmt, ap);
(void)fprintf(stderr, ": ");
}
va_end(ap);
(void)fprintf(stderr, "%s\n", strerror(errno));
exit(eval);
}
void
warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fmt != NULL) {
(void)vfprintf(stderr, fmt, ap);
(void)fprintf(stderr, ": ");
}
va_end(ap);
(void)fprintf(stderr, "%s\n", strerror(errno));
}
void
errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fmt != NULL)
(void)vfprintf(stderr, fmt, ap);
(void)fprintf(stderr, "\n");
va_end(ap);
exit(eval);
}
void
warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fmt != NULL)
(void)vfprintf(stderr, fmt, ap);
(void)fprintf(stderr, "\n");
va_end(ap);
}

355
libevent/evbuffer.c Normal file
View file

@ -0,0 +1,355 @@
/*
* Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include "event.h"
static int
bufferevent_add(struct event *ev, int timeout)
{
struct timeval tv, *ptv = NULL;
if (timeout) {
timerclear(&tv);
tv.tv_sec = timeout;
ptv = &tv;
}
return (event_add(ev, ptv));
}
/*
* This callback is executed when the size of the input buffer changes.
* We use it to apply back pressure on the reading side.
*/
void
bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
void *arg) {
struct bufferevent *bufev = arg;
/*
* If we are below the watermak then reschedule reading if it's
* still enabled.
*/
if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
evbuffer_setcb(buf, NULL, NULL);
if (bufev->enabled & EV_READ)
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
}
}
static void
bufferevent_readcb(int fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
int res = 0;
short what = EVBUFFER_READ;
size_t len;
if (event == EV_TIMEOUT) {
what |= EVBUFFER_TIMEOUT;
goto error;
}
res = evbuffer_read(bufev->input, fd, -1);
if (res == -1) {
if (errno == EAGAIN || errno == EINTR)
goto reschedule;
/* error case */
what |= EVBUFFER_ERROR;
} else if (res == 0) {
/* eof case */
what |= EVBUFFER_EOF;
}
if (res <= 0)
goto error;
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
/* See if this callbacks meets the water marks */
len = EVBUFFER_LENGTH(bufev->input);
if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
return;
if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
struct evbuffer *buf = bufev->input;
event_del(&bufev->ev_read);
/* Now schedule a callback for us */
evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
return;
}
/* Invoke the user callback - must always be called last */
(*bufev->readcb)(bufev, bufev->cbarg);
return;
reschedule:
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
return;
error:
(*bufev->errorcb)(bufev, what, bufev->cbarg);
}
static void
bufferevent_writecb(int fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
int res = 0;
short what = EVBUFFER_WRITE;
if (event == EV_TIMEOUT) {
what |= EVBUFFER_TIMEOUT;
goto error;
}
if (EVBUFFER_LENGTH(bufev->output)) {
res = evbuffer_write(bufev->output, fd);
if (res == -1) {
if (errno == EAGAIN || errno == EINTR)
goto reschedule;
/* error case */
what |= EVBUFFER_ERROR;
} else if (res == 0) {
/* eof case */
what |= EVBUFFER_EOF;
}
if (res <= 0)
goto error;
}
if (EVBUFFER_LENGTH(bufev->output) != 0)
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
/*
* Invoke the user callback if our buffer is drained or below the
* low watermark.
*/
if (EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
(*bufev->writecb)(bufev, bufev->cbarg);
return;
reschedule:
if (EVBUFFER_LENGTH(bufev->output) != 0)
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
return;
error:
(*bufev->errorcb)(bufev, what, bufev->cbarg);
}
/*
* Create a new buffered event object.
*
* The read callback is invoked whenever we read new data.
* The write callback is invoked whenever the output buffer is drained.
* The error callback is invoked on a write/read error or on EOF.
*/
struct bufferevent *
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
everrorcb errorcb, void *cbarg)
{
struct bufferevent *bufev;
if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
return (NULL);
if ((bufev->input = evbuffer_new()) == NULL) {
free(bufev);
return (NULL);
}
if ((bufev->output = evbuffer_new()) == NULL) {
evbuffer_free(bufev->input);
free(bufev);
return (NULL);
}
event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
bufev->readcb = readcb;
bufev->writecb = writecb;
bufev->errorcb = errorcb;
bufev->cbarg = cbarg;
bufev->enabled = EV_READ | EV_WRITE;
return (bufev);
}
void
bufferevent_free(struct bufferevent *bufev)
{
event_del(&bufev->ev_read);
event_del(&bufev->ev_write);
evbuffer_free(bufev->input);
evbuffer_free(bufev->output);
free(bufev);
}
/*
* Returns 0 on success;
* -1 on failure.
*/
int
bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
{
int res;
res = evbuffer_add(bufev->output, data, size);
if (res == -1)
return (res);
/* If everything is okay, we need to schedule a write */
if (size > 0 && (bufev->enabled & EV_WRITE))
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
return (res);
}
int
bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
{
int res;
res = bufferevent_write(bufev, buf->buffer, buf->off);
if (res != -1)
evbuffer_drain(buf, buf->off);
return (res);
}
size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
struct evbuffer *buf = bufev->input;
if (buf->off < size)
size = buf->off;
/* Copy the available data to the user buffer */
memcpy(data, buf->buffer, size);
if (size)
evbuffer_drain(buf, size);
return (size);
}
int
bufferevent_enable(struct bufferevent *bufev, short event)
{
if (event & EV_READ) {
if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
return (-1);
}
if (event & EV_WRITE) {
if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
return (-1);
}
bufev->enabled |= event;
return (0);
}
int
bufferevent_disable(struct bufferevent *bufev, short event)
{
if (event & EV_READ) {
if (event_del(&bufev->ev_read) == -1)
return (-1);
}
if (event & EV_WRITE) {
if (event_del(&bufev->ev_write) == -1)
return (-1);
}
bufev->enabled &= ~event;
return (0);
}
/*
* Sets the read and write timeout for a buffered event.
*/
void
bufferevent_settimeout(struct bufferevent *bufev,
int timeout_read, int timeout_write) {
bufev->timeout_read = timeout_read;
bufev->timeout_write = timeout_write;
}
/*
* Sets the water marks
*/
void
bufferevent_setwatermark(struct bufferevent *bufev, short events,
size_t lowmark, size_t highmark)
{
if (events & EV_READ) {
bufev->wm_read.low = lowmark;
bufev->wm_read.high = highmark;
}
if (events & EV_WRITE) {
bufev->wm_write.low = lowmark;
bufev->wm_write.high = highmark;
}
/* If the watermarks changed then see if we should call read again */
bufferevent_read_pressure_cb(bufev->input,
0, EVBUFFER_LENGTH(bufev->input), bufev);
}

444
libevent/event.3 Normal file
View file

@ -0,0 +1,444 @@
.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos Exp $
.\"
.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\"
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd August 8, 2000
.Dt EVENT 3
.Os
.Sh NAME
.Nm event_init ,
.Nm event_dispatch ,
.Nm event_loop ,
.Nm event_loopexit ,
.Nm event_set ,
.Nm event_add ,
.Nm event_del ,
.Nm event_once ,
.Nm event_pending ,
.Nm event_initialized ,
.Nm evtimer_set ,
.Nm evtimer_add ,
.Nm evtimer_del
.Nm evtimer_pending ,
.Nm evtimer_initialized ,
.Nm signal_set ,
.Nm signal_add ,
.Nm signal_del
.Nm signal_pending ,
.Nm signal_initialized ,
.Nm bufferevent_new ,
.Nm bufferevent_free ,
.Nm bufferevent_write ,
.Nm bufferevent_write_buffer ,
.Nm bufferevent_read ,
.Nm bufferevent_enable ,
.Nm bufferevent_disable ,
.Nm bufferevent_settimeout ,
.Nm evbuffer_new ,
.Nm evbuffer_free ,
.Nm evbuffer_add ,
.Nm evbuffer_add_buffer ,
.Nm evbuffer_add_printf ,
.Nm evbuffer_drain ,
.Nm evbuffer_write ,
.Nm evbuffer_read ,
.Nm evbuffer_find
.Nd execute a function when a specific event occurs
.Sh SYNOPSIS
.Fd #include <sys/time.h>
.Fd #include <event.h>
.Ft void
.Fn "event_init"
.Ft int
.Fn "event_dispatch"
.Ft int
.Fn "event_loop" "int flags"
.Ft int
.Fn "event_loopexit" "struct timeval *tv"
.Ft void
.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
.Ft int
.Fn "event_add" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "event_del" "struct event *ev"
.Ft int
.Fn "event_once" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv"
.Ft int
.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv"
.Ft int
.Fn "event_initialized" "struct event *ev"
.Ft void
.Fn "evtimer_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg"
.Ft void
.Fn "evtimer_add" "struct event *ev" "struct timeval *"
.Ft void
.Fn "evtimer_del" "struct event *ev"
.Ft int
.Fn "evtimer_pending" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "evtimer_initialized" "struct event *ev"
.Ft void
.Fn "signal_set" "struct event *ev" "int signal" "void (*fn)(int, short, void *)" "void *arg"
.Ft void
.Fn "signal_add" "struct event *ev" "struct timeval *"
.Ft void
.Fn "signal_del" "struct event *ev"
.Ft int
.Fn "signal_pending" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "signal_initialized" "struct event *ev"
.Ft "struct bufferevent *"
.Fn "bufferevent_new" "int fd" "evbuffercb readcb" "evbuffercb writecb" "everrorcb" "void *cbarg"
.Ft void
.Fn "bufferevent_free" "struct bufferevent *bufev"
.Ft int
.Fn "bufferevent_write" "struct bufferevent *bufev" "void *data" "size_t size"
.Ft int
.Fn "bufferevent_write_buffer" "struct bufferevent *bufev" "struct evbuffer *buf"
.Ft size_t
.Fn "bufferevent_read" "struct bufferevent *bufev" "void *data" "size_t size"
.Ft int
.Fn "bufferevent_enable" "struct bufferevent *bufev" "short event"
.Ft int
.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event"
.Ft void
.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write"
.Ft "struct evbuffer *"
.Fn "evbuffer_new" "void"
.Ft void
.Fn "evbuffer_free" "struct evbuffer *buf"
.Ft int
.Fn "evbuffer_add" "struct evbuffer *buf" "u_char *data" "size_t size"
.Ft int
.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src"
.Ft int
.Fn "evbuffer_add_printf" "struct evbuffer *buf" "char *fmt" "..."
.Ft void
.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size"
.Ft int
.Fn "evbuffer_write" "struct evbuffer *buf" "int fd"
.Ft int
.Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size"
.Ft "u_char *"
.Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size"
.Ft int
.Fa (*event_sigcb)(void) ;
.Ft int
.Fa event_gotsig ;
.Sh DESCRIPTION
The
.Nm event
API provides a mechanism to execute a function when a specific event
on a file descriptor occurs or after a given time has passed.
.Pp
The
.Nm event
API needs to be initialized with
.Fn event_init
before it can be used.
.Pp
In order to process events, an application needs to call
.Fn event_dispatch .
This function only returns on error, and should replace the event core
of the application program.
.Pp
In order to avoid races in signal handlers, the
.Nm event
API provides two variables:
.Va event_sigcb
and
.Va event_gotsig .
A signal handler
sets
.Va event_gotsig
to indicate that a signal has been received.
The application sets
.Va event_sigcb
to a callback function. After the signal handler sets
.Va event_gotsig ,
.Nm event_dispatch
will execute the callback function to process received signals. The
callback returns 1 when no events are registered any more. It can
return -1 to indicate an error to the
.Nm event
library, causing
.Fn event_dispatch
to terminate with
.Va errno
set to
.Er EINTR.
.Pp
The
.Nm event_loop
function provides an interface for single pass execution of pending
events. The flags
.Va EVLOOP_ONCE
and
.Va EVLOOP_NONBLOCK
are recognized.
The
.Nm event_loopexit
function allows the loop to be terminated after some amount of time
has passed.
The parameter indicates the time after which the loop should terminate.
.Pp
It is the responsibility of the caller to provide these functions with
pre-allocated event structures.
.Pp
The function
.Fn event_set
prepares the event structure
.Fa ev
to be used in future calls to
.Fn event_add
and
.Fn event_del .
The event will be prepared to call the function specified by the
.Fa fn
argument with an
.Fa int
argument indicating the file descriptor, a
.Fa short
argument indicating the type of event, and a
.Fa void *
argument given in the
.Fa arg
argument.
The
.Fa fd
indicates the file descriptor that should be monitored for events.
The events can be either
.Va EV_READ ,
.Va EV_WRITE ,
or both.
Indicating that an application can read or write from the file descriptor
respectively without blocking.
.Pp
The function
.Fa fn
will be called with the file descriptor that triggered the event and
the type of event which will be either
.Va EV_TIMEOUT ,
.Va EV_SIGNAL ,
.Va EV_READ ,
or
.Va EV_WRITE .
The additional flag
.Va EV_PERSIST
makes an
.Fn event_add
persistent until
.Fn event_del
has been called.
.Pp
Once initialized, the
.Fa ev
structure can be used repeatedly with
.Fn event_add
and
.Fn event_del
and does not need to be reinitialized unless the function called and/or
the argument to it are to be changed.
.Pp
The function
.Fn event_add
schedules the execution of the
.Fa ev
event when the event specified in
.Fn event_set
occurs or in at least the time specified in the
.Fa tv .
If
.Fa tv
is NULL, no timeout occurs and the function will only be called
if a matching event occurs on the file descriptor.
The event in the
.Fa ev
argument must be already initialized by
.Fn event_set
and may not be used in calls to
.Fn event_set
until it has timed out or been removed with
.Fn event_del .
If the event in the
.Fa ev
argument already has a scheduled timeout, the old timeout will be
replaced by the new one.
.Pp
The function
.Fn event_del
will cancel the event in the argument
.Fa ev .
If the event has already executed or has never been added
the call will have no effect.
.Pp
The function
.Fn event_once
is similiar to
.Fn event_set .
However, it schedules a callback to be called exactly once and does not
require the caller to prepare an
.Fa event
structure.
This function supports
.Fa EV_TIMEOUT ,
.Fa EV_READ
and
.Fa EV_WRITE .
.Pp
The
.Fn event_pending
function can be used to check if the event specified by
.Fa event
is pending to run.
If
.Va EV_TIMEOUT
was specified and
.Fa tv
is not
.Va NULL ,
the expiration time of the event will be returned in
.Fa tv .
.Pp
The
.Fn event_initialized
macro can be used to check if an event has been initialized.
.Pp
The functions
.Fn evtimer_set ,
.Fn evtimer_add ,
.Fn evtimer_del ,
.Fn evtimer_initialized ,
and
.Fn evtimer_pending
are abbreviations for common situations where only a timeout is required.
The file descriptor passed will be 0, and the event type will be
.Va EV_TIMEOUT .
.Pp
.Pp
The functions
.Fn signal_set ,
.Fn signal_add ,
.Fn signal_del ,
.Fn signal_initialized ,
and
.Fn signal_pending
are abbreviations.
The event type will be a persistent
.Va EV_SIGNAL .
That means
.Fn signal_set
adds
.Va EV_PERSIST .
.Pp
It is possible to disable support for
.Va epoll , kqueue , poll
or
.Va select
by setting the environment variable
.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NOPOLL
or
.Va EVENT_NOSELECT .
By setting the environment variable
.Va EVENT_SHOW_METHOD ,
.Nm libevent
displays the kernel notification method that it uses.
.Pp
.Sh BUFFERED EVENTS
.Nm libevent
provides an abstraction on top of the regular event callbacks.
This abstraction is called a
.Va "buffered event" .
A buffered event provides input and output buffer that get filled
and drained automatically.
The user of a buffered event no longer deals directly with the IO,
but instead is reading from input and writing to output buffers.
.Pp
A new bufferevent is created by
.Fn bufferevent_new .
The parameter
.Fa "fd"
specifies the file descriptor from which data is read and written to.
This file descriptor is not allowed to be a
.Xr pipe 2 .
The next three parameters are callbacks.
The read and write callback have the following form
.Ft void
.Fn "(*cb)" "struct bufferevent *bufev" "void *arg"
The argument is specified by the fourth parameter
.Fa "cbarg" .
.Pp
By default the buffered event is read enabled and will try to read
from the file descriptor.
The write callback is executed whenever the output buffer is drained
below the write low watermark which is
.Va 0
by default.
.Pp
The
.Fn bufferevent_write
function can be used to write data to the file descriptor.
The data is appended to the output buffer and written to the descriptor
automatically as it becomes available for writing.
The
.Fn bufferevent_read
function is used to read data from the input buffer.
Both functions return the amount of data written or read.
.Pp
.Sh RETURN VALUES
Upon successful completion
.Fn event_add
and
.Fn event_del
return 0.
Otherwise, -1 is returned and the global variable errno is
set to indicate the error.
.Sh SEE ALSO
.Xr timeout 9 ,
.Xr select 2 ,
.Xr kqueue 2
.Sh HISTORY
The
.Nm event
API manpage is based on the
.Xr timeout 9
manpage by Artur Grabowski.
The port of
.Nm libevent
to Windows is due to Michael A. Davis.
Support for real-time signals is due to Taral.
.Sh AUTHORS
The
.Nm event
library was written by Niels Provos.
.Pp
.Sh BUGS
This documentation is neither complete nor authorative.
If you are in doubt about the usage of this API then
check the source code to find out how it works, write
up the missing piece of documentation and send it to
me for inclusion in this man page.

630
libevent/event.c Normal file
View file

@ -0,0 +1,630 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include "misc.h"
#endif
#include <sys/types.h>
#include <sys/tree.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include <string.h>
#include <err.h>
#include <assert.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
#ifdef HAVE_SELECT
extern const struct eventop selectops;
#endif
#ifdef HAVE_POLL
extern const struct eventop pollops;
#endif
#ifdef HAVE_RTSIG
extern const struct eventop rtsigops;
#endif
#ifdef HAVE_EPOLL
extern const struct eventop epollops;
#endif
#ifdef HAVE_WORKING_KQUEUE
extern const struct eventop kqops;
#endif
#ifdef WIN32
extern const struct eventop win32ops;
#endif
/* In order of preference */
const struct eventop *eventops[] = {
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_RTSIG
&rtsigops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
const struct eventop *evsel;
void *evbase;
/* Handle signals - This is a deprecated interface */
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
int event_gotsig; /* Set in signal handler */
int event_gotterm; /* Set to terminate loop */
/* Prototypes */
void event_queue_insert(struct event *, int);
void event_queue_remove(struct event *, int);
int event_haveevents(void);
static void event_process_active(void);
static RB_HEAD(event_tree, event) timetree;
static struct event_list activequeue;
struct event_list signalqueue;
struct event_list eventqueue;
static struct timeval event_tv;
static int
compare(struct event *a, struct event *b)
{
if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
return (-1);
else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
return (1);
if (a < b)
return (-1);
if (a > b)
return (1);
return (0);
}
RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
RB_GENERATE(event_tree, event, ev_timeout_node, compare);
void
event_init(void)
{
int i;
event_sigcb = NULL;
event_gotsig = 0;
gettimeofday(&event_tv, NULL);
RB_INIT(&timetree);
TAILQ_INIT(&eventqueue);
TAILQ_INIT(&activequeue);
TAILQ_INIT(&signalqueue);
evbase = NULL;
for (i = 0; eventops[i] && !evbase; i++) {
evsel = eventops[i];
evbase = evsel->init();
}
if (evbase == NULL)
errx(1, "%s: no event mechanism available", __func__);
if (getenv("EVENT_SHOW_METHOD"))
fprintf(stderr, "libevent using: %s\n", evsel->name);
#if defined(USE_LOG) && defined(USE_DEBUG)
log_to(stderr);
log_debug_cmd(LOG_MISC, 80);
#endif
}
int
event_haveevents(void)
{
return (RB_ROOT(&timetree) || TAILQ_FIRST(&eventqueue) ||
TAILQ_FIRST(&signalqueue) || TAILQ_FIRST(&activequeue));
}
static void
event_process_active(void)
{
struct event *ev;
short ncalls;
for (ev = TAILQ_FIRST(&activequeue); ev;
ev = TAILQ_FIRST(&activequeue)) {
event_queue_remove(ev, EVLIST_ACTIVE);
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
while (ncalls) {
ncalls--;
ev->ev_ncalls = ncalls;
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
}
}
}
/*
* Wait continously for events. We exit only if no events are left.
*/
int
event_dispatch(void)
{
return (event_loop(0));
}
static void
event_loopexit_cb(int fd, short what, void *arg)
{
event_gotterm = 1;
}
int
event_loopexit(struct timeval *tv)
{
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb, NULL, tv));
}
int
event_loop(int flags)
{
struct timeval tv;
int res, done;
/* Calculate the initial events that we are waiting for */
if (evsel->recalc(evbase, 0) == -1)
return (-1);
done = 0;
while (!done) {
/* Terminate the loop if we have been asked to */
if (event_gotterm) {
event_gotterm = 0;
break;
}
while (event_gotsig) {
event_gotsig = 0;
if (event_sigcb) {
res = (*event_sigcb)();
if (res == -1) {
errno = EINTR;
return (-1);
}
}
}
/* Check if time is running backwards */
gettimeofday(&tv, NULL);
if (timercmp(&tv, &event_tv, <)) {
struct timeval off;
LOG_DBG((LOG_MISC, 10,
"%s: time is running backwards, corrected",
__func__));
timersub(&event_tv, &tv, &off);
timeout_correct(&off);
}
event_tv = tv;
if (!(flags & EVLOOP_NONBLOCK))
timeout_next(&tv);
else
timerclear(&tv);
/* If we have no events, we just exit */
if (!event_haveevents())
return (1);
res = evsel->dispatch(evbase, &tv);
if (res == -1)
return (-1);
timeout_process();
if (TAILQ_FIRST(&activequeue)) {
event_process_active();
if (flags & EVLOOP_ONCE)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
if (evsel->recalc(evbase, 0) == -1)
return (-1);
}
return (0);
}
/* Sets up an event for processing once */
struct event_once {
struct event ev;
void (*cb)(int, short, void *);
void *arg;
};
/* One-time callback, it deletes itself */
static void
event_once_cb(int fd, short events, void *arg)
{
struct event_once *eonce = arg;
(*eonce->cb)(fd, events, eonce->arg);
free(eonce);
}
/* Schedules an event once */
int
event_once(int fd, short events,
void (*callback)(int, short, void *), void *arg, struct timeval *tv)
{
struct event_once *eonce;
struct timeval etv;
/* We cannot support signals that just fire once */
if (events & EV_SIGNAL)
return (-1);
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
return (-1);
if (events == EV_TIMEOUT) {
if (tv == NULL) {
timerclear(&etv);
tv = &etv;
}
eonce->cb = callback;
eonce->arg = arg;
evtimer_set(&eonce->ev, event_once_cb, eonce);
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;
event_set(&eonce->ev, fd, events, event_once_cb, eonce);
} else {
/* Bad event combination */
return (-1);
}
event_add(&eonce->ev, tv);
return (0);
}
void
event_set(struct event *ev, int fd, short events,
void (*callback)(int, short, void *), void *arg)
{
ev->ev_callback = callback;
ev->ev_arg = arg;
#ifdef WIN32
ev->ev_fd = (HANDLE)fd;
ev->overlap.hEvent = ev;
#else
ev->ev_fd = fd;
#endif
ev->ev_events = events;
ev->ev_flags = EVLIST_INIT;
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
}
/*
* Checks if a specific event is pending or scheduled.
*/
int
event_pending(struct event *ev, short event, struct timeval *tv)
{
int flags = 0;
if (ev->ev_flags & EVLIST_INSERTED)
flags |= (ev->ev_events & (EV_READ|EV_WRITE));
if (ev->ev_flags & EVLIST_ACTIVE)
flags |= ev->ev_res;
if (ev->ev_flags & EVLIST_TIMEOUT)
flags |= EV_TIMEOUT;
if (ev->ev_flags & EVLIST_SIGNAL)
flags |= EV_SIGNAL;
event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
/* See if there is a timeout that we should report */
if (tv != NULL && (flags & event & EV_TIMEOUT))
*tv = ev->ev_timeout;
return (flags & event);
}
int
event_add(struct event *ev, struct timeval *tv)
{
LOG_DBG((LOG_MISC, 55,
"event_add: event: %p, %s%s%scall %p",
ev,
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback));
assert(!(ev->ev_flags & ~EVLIST_ALL));
if (tv != NULL) {
struct timeval now;
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(ev, EVLIST_TIMEOUT);
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
/* See if we are just active executing this
* event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
event_queue_remove(ev, EVLIST_ACTIVE);
}
gettimeofday(&now, NULL);
timeradd(&now, tv, &ev->ev_timeout);
LOG_DBG((LOG_MISC, 55,
"event_add: timeout in %d seconds, call %p",
tv->tv_sec, ev->ev_callback));
event_queue_insert(ev, EVLIST_TIMEOUT);
}
if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
event_queue_insert(ev, EVLIST_INSERTED);
return (evsel->add(evbase, ev));
} else if ((ev->ev_events & EV_SIGNAL) &&
!(ev->ev_flags & EVLIST_SIGNAL)) {
event_queue_insert(ev, EVLIST_SIGNAL);
return (evsel->add(evbase, ev));
}
return (0);
}
int
event_del(struct event *ev)
{
LOG_DBG((LOG_MISC, 80, "event_del: %p, callback %p",
ev, ev->ev_callback));
assert(!(ev->ev_flags & ~EVLIST_ALL));
/* See if we are just active executing this event in a loop */
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(ev, EVLIST_TIMEOUT);
if (ev->ev_flags & EVLIST_ACTIVE)
event_queue_remove(ev, EVLIST_ACTIVE);
if (ev->ev_flags & EVLIST_INSERTED) {
event_queue_remove(ev, EVLIST_INSERTED);
return (evsel->del(evbase, ev));
} else if (ev->ev_flags & EVLIST_SIGNAL) {
event_queue_remove(ev, EVLIST_SIGNAL);
return (evsel->del(evbase, ev));
}
return (0);
}
void
event_active(struct event *ev, int res, short ncalls)
{
/* We get different kinds of events, add them together */
if (ev->ev_flags & EVLIST_ACTIVE) {
ev->ev_res |= res;
return;
}
ev->ev_res = res;
ev->ev_ncalls = ncalls;
ev->ev_pncalls = NULL;
event_queue_insert(ev, EVLIST_ACTIVE);
}
int
timeout_next(struct timeval *tv)
{
struct timeval dflt = TIMEOUT_DEFAULT;
struct timeval now;
struct event *ev;
if ((ev = RB_MIN(event_tree, &timetree)) == NULL) {
*tv = dflt;
return (0);
}
if (gettimeofday(&now, NULL) == -1)
return (-1);
if (timercmp(&ev->ev_timeout, &now, <=)) {
timerclear(tv);
return (0);
}
timersub(&ev->ev_timeout, &now, tv);
assert(tv->tv_sec >= 0);
assert(tv->tv_usec >= 0);
LOG_DBG((LOG_MISC, 60, "timeout_next: in %d seconds", tv->tv_sec));
return (0);
}
void
timeout_correct(struct timeval *off)
{
struct event *ev;
/* We can modify the key element of the node without destroying
* the key, beause we apply it to all in the right order.
*/
RB_FOREACH(ev, event_tree, &timetree)
timersub(&ev->ev_timeout, off, &ev->ev_timeout);
}
void
timeout_process(void)
{
struct timeval now;
struct event *ev, *next;
gettimeofday(&now, NULL);
for (ev = RB_MIN(event_tree, &timetree); ev; ev = next) {
if (timercmp(&ev->ev_timeout, &now, >))
break;
next = RB_NEXT(event_tree, &timetree, ev);
event_queue_remove(ev, EVLIST_TIMEOUT);
/* delete this event from the I/O queues */
event_del(ev);
LOG_DBG((LOG_MISC, 60, "timeout_process: call %p",
ev->ev_callback));
event_active(ev, EV_TIMEOUT, 1);
}
}
void
event_queue_remove(struct event *ev, int queue)
{
if (!(ev->ev_flags & queue))
errx(1, "%s: %p(fd %d) not on queue %x", __func__,
ev, ev->ev_fd, queue);
ev->ev_flags &= ~queue;
switch (queue) {
case EVLIST_ACTIVE:
TAILQ_REMOVE(&activequeue, ev, ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_REMOVE(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT:
RB_REMOVE(event_tree, &timetree, ev);
break;
case EVLIST_INSERTED:
TAILQ_REMOVE(&eventqueue, ev, ev_next);
break;
default:
errx(1, "%s: unknown queue %x", __func__, queue);
}
}
void
event_queue_insert(struct event *ev, int queue)
{
if (ev->ev_flags & queue)
errx(1, "%s: %p(fd %d) already on queue %x", __func__,
ev, ev->ev_fd, queue);
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_ACTIVE:
TAILQ_INSERT_TAIL(&activequeue, ev, ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT: {
struct event *tmp = RB_INSERT(event_tree, &timetree, ev);
assert(tmp == NULL);
break;
}
case EVLIST_INSERTED:
TAILQ_INSERT_TAIL(&eventqueue, ev, ev_next);
break;
default:
errx(1, "%s: unknown queue %x", __func__, queue);
}
}

249
libevent/event.h Normal file
View file

@ -0,0 +1,249 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _EVENT_H_
#define _EVENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifdef WIN32
#include <windows.h>
#endif
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
#define EVLIST_SIGNAL 0x04
#define EVLIST_ACTIVE 0x08
#define EVLIST_INIT 0x80
/* EVLIST_X_ Private space: 0x1000-0xf000 */
#define EVLIST_ALL (0xf000 | 0x8f)
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10 /* Persistant event */
/* Fix so that ppl dont have to run with <sys/queue.h> */
#ifndef TAILQ_ENTRY
#define _EVENT_DEFINED_TQENTRY
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#endif /* !TAILQ_ENTRY */
#ifndef RB_ENTRY
#define _EVENT_DEFINED_RBENTRY
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#endif /* !RB_ENTRY */
struct event {
TAILQ_ENTRY (event) ev_next;
TAILQ_ENTRY (event) ev_active_next;
TAILQ_ENTRY (event) ev_signal_next;
RB_ENTRY (event) ev_timeout_node;
#ifdef WIN32
HANDLE ev_fd;
OVERLAPPED overlap;
#else
int ev_fd;
#endif
short ev_events;
short ev_ncalls;
short *ev_pncalls; /* Allows deletes in callback */
struct timeval ev_timeout;
void (*ev_callback)(int, short, void *arg);
void *ev_arg;
int ev_res; /* result passed to event callback */
int ev_flags;
};
#define EVENT_SIGNAL(ev) (int)ev->ev_fd
#define EVENT_FD(ev) (int)ev->ev_fd
#ifdef _EVENT_DEFINED_TQENTRY
#undef TAILQ_ENTRY
#undef _EVENT_DEFINED_TQENTRY
#else
TAILQ_HEAD (event_list, event);
#endif /* _EVENT_DEFINED_TQENTRY */
#ifdef _EVENT_DEFINED_RBENTRY
#undef RB_ENTRY
#undef _EVENT_DEFINED_RBENTRY
#endif /* _EVENT_DEFINED_RBENTRY */
struct eventop {
char *name;
void *(*init)(void);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*recalc)(void *, int);
int (*dispatch)(void *, struct timeval *);
};
#define TIMEOUT_DEFAULT {5, 0}
void event_init(void);
int event_dispatch(void);
#define EVLOOP_ONCE 0x01
#define EVLOOP_NONBLOCK 0x02
int event_loop(int);
int event_loopexit(struct timeval *); /* Causes the loop to exit */
int timeout_next(struct timeval *);
void timeout_correct(struct timeval *);
void timeout_process(void);
#define evtimer_add(ev, tv) event_add(ev, tv)
#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define evtimer_del(ev) event_del(ev)
#define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define timeout_add(ev, tv) event_add(ev, tv)
#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define timeout_del(ev) event_del(ev)
#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define signal_add(ev, tv) event_add(ev, tv)
#define signal_set(ev, x, cb, arg) \
event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
#define signal_del(ev) event_del(ev)
#define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv)
#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
int event_add(struct event *, struct timeval *);
int event_del(struct event *);
void event_active(struct event *, int, short);
int event_pending(struct event *, short, struct timeval *);
#ifdef WIN32
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != INVALID_HANDLE_VALUE)
#else
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#endif
/* These functions deal with buffering input and output */
struct evbuffer {
u_char *buffer;
size_t totallen;
size_t off;
void (*cb)(struct evbuffer *, size_t, size_t, void *);
void *cbarg;
};
/* Just for error reporting - use other constants otherwise */
#define EVBUFFER_READ 0x01
#define EVBUFFER_WRITE 0x02
#define EVBUFFER_EOF 0x10
#define EVBUFFER_ERROR 0x20
#define EVBUFFER_TIMEOUT 0x40
struct bufferevent;
typedef void (*evbuffercb)(struct bufferevent *, void *);
typedef void (*everrorcb)(struct bufferevent *, short what, void *);
struct event_watermark {
size_t low;
size_t high;
};
struct bufferevent {
struct event ev_read;
struct event ev_write;
struct evbuffer *input;
struct evbuffer *output;
struct event_watermark wm_read;
struct event_watermark wm_write;
evbuffercb readcb;
evbuffercb writecb;
everrorcb errorcb;
void *cbarg;
int timeout_read; /* in seconds */
int timeout_write; /* in seconds */
short enabled; /* events that are currently enabled */
};
struct bufferevent *bufferevent_new(int fd,
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
void bufferevent_free(struct bufferevent *bufev);
int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_enable(struct bufferevent *bufev, short event);
int bufferevent_disable(struct bufferevent *bufev, short event);
void bufferevent_settimeout(struct bufferevent *bufev,
int timeout_read, int timeout_write);
#define EVBUFFER_LENGTH(x) (x)->off
#define EVBUFFER_DATA(x) (x)->buffer
#define EVBUFFER_INPUT(x) (x)->input
#define EVBUFFER_OUTPUT(x) (x)->output
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *);
int evbuffer_add(struct evbuffer *, u_char *, size_t);
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
int evbuffer_add_printf(struct evbuffer *, char *fmt, ...);
void evbuffer_drain(struct evbuffer *, size_t);
int evbuffer_write(struct evbuffer *, int);
int evbuffer_read(struct evbuffer *, int, int);
u_char *evbuffer_find(struct evbuffer *, u_char *, size_t);
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
#ifdef __cplusplus
}
#endif
#endif /* _EVENT_H_ */

37
libevent/evsignal.h Normal file
View file

@ -0,0 +1,37 @@
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _EVSIGNAL_H_
#define _EVSIGNAL_H_
void evsignal_init(sigset_t *);
void evsignal_process(void);
int evsignal_recalc(sigset_t *);
int evsignal_deliver(sigset_t *);
int evsignal_add(sigset_t *, struct event *);
int evsignal_del(sigset_t *, struct event *);
#endif /* _EVSIGNAL_H_ */

238
libevent/install-sh Executable file
View file

@ -0,0 +1,238 @@
#! /bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
#
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
tranformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

378
libevent/kqueue.c Normal file
View file

@ -0,0 +1,378 @@
/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/event.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error warn
#endif
#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__)
#define INTPTR(x) (intptr_t)x
#else
#define INTPTR(x) x
#endif
#include "event.h"
extern struct event_list timequeue;
extern struct event_list eventqueue;
extern struct event_list addqueue;
#define EVLIST_X_KQINKERNEL 0x1000
#define NEVENT 64
struct kqop {
struct kevent *changes;
int nchanges;
struct kevent *events;
int nevents;
int kq;
} kqueueop;
void *kq_init (void);
int kq_add (void *, struct event *);
int kq_del (void *, struct event *);
int kq_recalc (void *, int);
int kq_dispatch (void *, struct timeval *);
int kq_insert (struct kqop *, struct kevent *);
const struct eventop kqops = {
"kqueue",
kq_init,
kq_add,
kq_del,
kq_recalc,
kq_dispatch
};
void *
kq_init(void)
{
int kq;
/* Disable kqueue when this environment variable is set */
if (getenv("EVENT_NOKQUEUE"))
return (NULL);
memset(&kqueueop, 0, sizeof(kqueueop));
/* Initalize the kernel queue */
if ((kq = kqueue()) == -1) {
log_error("kqueue");
return (NULL);
}
kqueueop.kq = kq;
/* Initalize fields */
kqueueop.changes = malloc(NEVENT * sizeof(struct kevent));
if (kqueueop.changes == NULL)
return (NULL);
kqueueop.events = malloc(NEVENT * sizeof(struct kevent));
if (kqueueop.events == NULL) {
free (kqueueop.changes);
return (NULL);
}
kqueueop.nevents = NEVENT;
return (&kqueueop);
}
int
kq_recalc(void *arg, int max)
{
return (0);
}
int
kq_insert(struct kqop *kqop, struct kevent *kev)
{
int nevents = kqop->nevents;
if (kqop->nchanges == nevents) {
struct kevent *newchange;
struct kevent *newresult;
nevents *= 2;
newchange = realloc(kqop->changes,
nevents * sizeof(struct kevent));
if (newchange == NULL) {
log_error("%s: malloc", __func__);
return (-1);
}
kqop->changes = newchange;
newresult = realloc(kqop->events,
nevents * sizeof(struct kevent));
/*
* If we fail, we don't have to worry about freeing,
* the next realloc will pick it up.
*/
if (newresult == NULL) {
log_error("%s: malloc", __func__);
return (-1);
}
kqop->events = newresult;
kqop->nevents = nevents;
}
memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
LOG_DBG((LOG_MISC, 70, "%s: fd %d %s%s",
__func__, kev->ident,
kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
kev->flags == EV_DELETE ? " (del)" : ""));
return (0);
}
static void
kq_sighandler(int sig)
{
/* Do nothing here */
}
int
kq_dispatch(void *arg, struct timeval *tv)
{
struct kqop *kqop = arg;
struct kevent *changes = kqop->changes;
struct kevent *events = kqop->events;
struct event *ev;
struct timespec ts;
int i, res;
TIMEVAL_TO_TIMESPEC(tv, &ts);
res = kevent(kqop->kq, changes, kqop->nchanges,
events, kqop->nevents, &ts);
kqop->nchanges = 0;
if (res == -1) {
if (errno != EINTR) {
log_error("kevent");
return (-1);
}
return (0);
}
LOG_DBG((LOG_MISC, 80, "%s: kevent reports %d", __func__, res));
for (i = 0; i < res; i++) {
int which = 0;
if (events[i].flags & EV_ERROR) {
/*
* Error messages that can happen, when a delete fails.
* EBADF happens when the file discriptor has been
* closed,
* ENOENT when the file discriptor was closed and
* then reopened.
* An error is also indicated when a callback deletes
* an event we are still processing. In that case
* the data field is set to ENOENT.
*/
if (events[i].data == EBADF ||
events[i].data == ENOENT)
continue;
return (-1);
}
ev = (struct event *)events[i].udata;
if (events[i].filter == EVFILT_READ) {
which |= EV_READ;
} else if (events[i].filter == EVFILT_WRITE) {
which |= EV_WRITE;
} else if (events[i].filter == EVFILT_SIGNAL) {
which |= EV_SIGNAL;
}
if (!which)
continue;
if (!(ev->ev_events & EV_PERSIST)) {
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
event_del(ev);
}
event_active(ev, which,
ev->ev_events & EV_SIGNAL ? events[i].data : 1);
}
return (0);
}
int
kq_add(void *arg, struct event *ev)
{
struct kqop *kqop = arg;
struct kevent kev;
if (ev->ev_events & EV_SIGNAL) {
int nsignal = EVENT_SIGNAL(ev);
memset(&kev, 0, sizeof(kev));
kev.ident = nsignal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
if (signal(nsignal, kq_sighandler) == SIG_ERR)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
return (0);
}
if (ev->ev_events & EV_READ) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_READ;
#ifdef NOTE_EOF
/* Make it behave like select() and poll() */
kev.fflags = NOTE_EOF;
#endif
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
}
if (ev->ev_events & EV_WRITE) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_WRITE;
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
}
return (0);
}
int
kq_del(void *arg, struct event *ev)
{
struct kqop *kqop = arg;
struct kevent kev;
if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
return (0);
if (ev->ev_events & EV_SIGNAL) {
int nsignal = EVENT_SIGNAL(ev);
memset(&kev, 0, sizeof(kev));
kev.ident = (int)signal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
if (signal(nsignal, SIG_DFL) == SIG_ERR)
return (-1);
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
return (0);
}
if (ev->ev_events & EV_READ) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_READ;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
}
if (ev->ev_events & EV_WRITE) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_WRITE;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
}
return (0);
}

198
libevent/missing Executable file
View file

@ -0,0 +1,198 @@
#! /bin/sh
# Common stub for a few missing GNU programs while installing.
# Copyright (C) 1996, 1997, 2001 Free Software Foundation, Inc.
# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996.
# 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 2, 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., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
if test $# -eq 0; then
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
fi
# In the cases where this matters, `missing' is being run in the
# srcdir already.
if test -f configure.in; then
configure_ac=configure.ac
else
configure_ac=configure.in
fi
case "$1" in
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
error status if there is no known handling for PROGRAM.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal touch file \`aclocal.m4'
autoconf touch file \`configure'
autoheader touch file \`config.h.in'
automake touch all \`Makefile.in' files
bison create \`y.tab.[ch]', if possible, from existing .[ch]
flex create \`lex.yy.c', if possible, from existing .c
lex create \`lex.yy.c', if possible, from existing .c
makeinfo touch the output file
yacc create \`y.tab.[ch]', if possible, from existing .[ch]"
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing - GNU libit 0.0"
;;
-*)
echo 1>&2 "$0: Unknown \`$1' option"
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
;;
aclocal)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acinclude.m4' or \`$configure_ac'. You might want
to install the \`Automake' and \`Perl' packages. Grab them from
any GNU archive site."
touch aclocal.m4
;;
autoconf)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`$configure_ac'. You might want to install the
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
archive site."
touch configure
;;
autoheader)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`acconfig.h' or \`$configure_ac'. You might want
to install the \`Autoconf' and \`GNU m4' packages. Grab them
from any GNU archive site."
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' $configure_ac`
test -z "$files" && files="config.h"
touch_files=
for f in $files; do
case "$f" in
*:*) touch_files="$touch_files "`echo "$f" |
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
*) touch_files="$touch_files $f.in";;
esac
done
touch $touch_files
;;
automake)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified \`Makefile.am', \`acinclude.m4' or \`$configure_ac'.
You might want to install the \`Automake' and \`Perl' packages.
Grab them from any GNU archive site."
find . -type f -name Makefile.am -print |
sed 's/\.am$/.in/' |
while read f; do touch "$f"; done
;;
bison|yacc)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a \`.y' file. You may need the \`Bison' package
in order for those modifications to take effect. You can get
\`Bison' from any GNU archive site."
rm -f y.tab.c y.tab.h
if [ $# -ne 1 ]; then
eval LASTARG="\${$#}"
case "$LASTARG" in
*.y)
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" y.tab.c
fi
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" y.tab.h
fi
;;
esac
fi
if [ ! -f y.tab.h ]; then
echo >y.tab.h
fi
if [ ! -f y.tab.c ]; then
echo 'main() { return 0; }' >y.tab.c
fi
;;
lex|flex)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a \`.l' file. You may need the \`Flex' package
in order for those modifications to take effect. You can get
\`Flex' from any GNU archive site."
rm -f lex.yy.c
if [ $# -ne 1 ]; then
eval LASTARG="\${$#}"
case "$LASTARG" in
*.l)
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
if [ -f "$SRCFILE" ]; then
cp "$SRCFILE" lex.yy.c
fi
;;
esac
fi
if [ ! -f lex.yy.c ]; then
echo 'main() { return 0; }' >lex.yy.c
fi
;;
makeinfo)
echo 1>&2 "\
WARNING: \`$1' is missing on your system. You should only need it if
you modified a \`.texi' or \`.texinfo' file, or any other file
indirectly affecting the aspect of the manual. The spurious
call might also be the consequence of using a buggy \`make' (AIX,
DU, IRIX). You might want to install the \`Texinfo' package or
the \`GNU make' package. Grab either from any GNU archive site."
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
if test -z "$file"; then
file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
fi
touch $file
;;
*)
echo 1>&2 "\
WARNING: \`$1' is needed, and you do not seem to have it handy on your
system. You might have modified some files without having the
proper tools for further handling them. Check the \`README' file,
it often tells you about the needed prerequirements for installing
this package. You may also peek at any GNU archive site, in case
some other package would contain this missing \`$1' program."
exit 1
;;
esac
exit 0

40
libevent/mkinstalldirs Executable file
View file

@ -0,0 +1,40 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Public domain
# $Id$
errstatus=0
for file
do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d
do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp"
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# mkinstalldirs ends here

245
libevent/poll.c Normal file
View file

@ -0,0 +1,245 @@
/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
#include "evsignal.h"
extern struct event_list eventqueue;
extern volatile sig_atomic_t evsignal_caught;
struct pollop {
int event_count; /* Highest number alloc */
struct pollfd *event_set;
struct event **event_back;
sigset_t evsigmask;
} pollop;
void *poll_init (void);
int poll_add (void *, struct event *);
int poll_del (void *, struct event *);
int poll_recalc (void *, int);
int poll_dispatch (void *, struct timeval *);
struct eventop pollops = {
"poll",
poll_init,
poll_add,
poll_del,
poll_recalc,
poll_dispatch
};
void *
poll_init(void)
{
/* Disable kqueue when this environment variable is set */
if (getenv("EVENT_NOPOLL"))
return (NULL);
memset(&pollop, 0, sizeof(pollop));
evsignal_init(&pollop.evsigmask);
return (&pollop);
}
/*
* Called with the highest fd that we know about. If it is 0, completely
* recalculate everything.
*/
int
poll_recalc(void *arg, int max)
{
struct pollop *pop = arg;
return (evsignal_recalc(&pop->evsigmask));
}
int
poll_dispatch(void *arg, struct timeval *tv)
{
int res, i, count, sec, nfds;
struct event *ev;
struct pollop *pop = arg;
count = pop->event_count;
nfds = 0;
TAILQ_FOREACH(ev, &eventqueue, ev_next) {
if (nfds + 1 >= count) {
if (count < 32)
count = 32;
else
count *= 2;
/* We need more file descriptors */
pop->event_set = realloc(pop->event_set,
count * sizeof(struct pollfd));
if (pop->event_set == NULL) {
log_error("realloc");
return (-1);
}
pop->event_back = realloc(pop->event_back,
count * sizeof(struct event *));
if (pop->event_back == NULL) {
log_error("realloc");
return (-1);
}
pop->event_count = count;
}
if (ev->ev_events & EV_WRITE) {
struct pollfd *pfd = &pop->event_set[nfds];
pfd->fd = ev->ev_fd;
pfd->events = POLLOUT;
pfd->revents = 0;
pop->event_back[nfds] = ev;
nfds++;
}
if (ev->ev_events & EV_READ) {
struct pollfd *pfd = &pop->event_set[nfds];
pfd->fd = ev->ev_fd;
pfd->events = POLLIN;
pfd->revents = 0;
pop->event_back[nfds] = ev;
nfds++;
}
}
if (evsignal_deliver(&pop->evsigmask) == -1)
return (-1);
sec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
res = poll(pop->event_set, nfds, sec);
if (evsignal_recalc(&pop->evsigmask) == -1)
return (-1);
if (res == -1) {
if (errno != EINTR) {
log_error("poll");
return (-1);
}
evsignal_process();
return (0);
} else if (evsignal_caught)
evsignal_process();
LOG_DBG((LOG_MISC, 80, "%s: poll reports %d", __func__, res));
if (res == 0)
return (0);
for (i = 0; i < nfds; i++) {
int what = pop->event_set[i].revents;
res = 0;
/* If the file gets closed notify */
if (what & POLLHUP)
what |= POLLIN|POLLOUT;
if (what & POLLERR)
what |= POLLIN|POLLOUT;
if (what & POLLIN)
res |= EV_READ;
if (what & POLLOUT)
res |= EV_WRITE;
if (res == 0)
continue;
ev = pop->event_back[i];
res &= ev->ev_events;
if (res) {
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, res, 1);
}
}
return (0);
}
int
poll_add(void *arg, struct event *ev)
{
struct pollop *pop = arg;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(&pop->evsigmask, ev));
return (0);
}
/*
* Nothing to be done here.
*/
int
poll_del(void *arg, struct event *ev)
{
struct pollop *pop = arg;
if (!(ev->ev_events & EV_SIGNAL))
return (0);
return (evsignal_del(&pop->evsigmask, ev));
}

434
libevent/rtsig.c Normal file
View file

@ -0,0 +1,434 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Enable F_SETSIG and F_SETOWN */
#define _GNU_SOURCE
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/queue.h>
#ifndef HAVE_WORKING_RTSIG
#include <sys/stat.h>
#endif
#include <unistd.h>
#define EVLIST_X_NORT 0x1000 /* Skip RT signals (internal) */
#include "event.h"
extern struct event_list eventqueue;
extern struct event_list signalqueue;
struct rtsigop {
sigset_t sigs;
struct pollfd *poll;
struct event **toev;
int cur, max, total;
#ifndef HAVE_WORKING_RTSIG
int pollmode;
#endif
};
#define INIT_MAX 16
static int
poll_add(struct rtsigop *op, struct event *ev)
{
struct pollfd *pfd;
if (op->poll == NULL) return 0;
if (op->cur == op->max) {
void *p;
p = realloc(op->poll, sizeof(*op->poll) * (op->max << 1));
if (!p) {
errno = ENOMEM;
return -1;
}
op->poll = p;
p = realloc(op->toev, sizeof(*op->toev) * (op->max << 1));
if (!p) {
op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
errno = ENOMEM;
return -1;
}
op->toev = p;
op->max <<= 1;
}
pfd = &op->poll[op->cur];
pfd->fd = ev->ev_fd;
pfd->events = 0;
if (ev->ev_events & EV_READ) pfd->events |= POLLIN;
if (ev->ev_events & EV_WRITE) pfd->events |= POLLOUT;
pfd->revents = 0;
op->toev[op->cur] = ev;
op->cur++;
return 0;
}
static void
poll_free(struct rtsigop *op, int n)
{
if (op->poll == NULL) return;
op->cur--;
if (n < op->cur) {
memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll));
op->toev[n] = op->toev[op->cur];
}
if (op->max > INIT_MAX && op->cur < op->max >> 1) {
op->max >>= 1;
op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
op->toev = realloc(op->toev, sizeof(*op->toev) * op->max);
}
}
static void
poll_remove(struct rtsigop *op, struct event *ev)
{
int i;
for (i = 0; i < op->cur; i++) {
if (op->toev[i] == ev) {
poll_free(op, i);
break;
}
}
}
static void
activate(struct event *ev, int flags)
{
if (!(ev->ev_events & EV_PERSIST)) event_del(ev);
event_active(ev, flags, 1);
}
void *rtsig_init(void);
int rtsig_add(void *, struct event *);
int rtsig_del(void *, struct event *);
int rtsig_recalc(void *, int);
int rtsig_dispatch(void *, struct timeval *);
struct eventop rtsigops = {
"rtsig",
rtsig_init,
rtsig_add,
rtsig_del,
rtsig_recalc,
rtsig_dispatch
};
void *
rtsig_init(void)
{
struct rtsigop *op;
if (getenv("EVENT_NORTSIG"))
return (NULL);
op = malloc(sizeof(*op));
if (op == NULL) return (NULL);
memset(op, 0, sizeof(*op));
op->max = INIT_MAX;
op->poll = malloc(sizeof(*op->poll) * op->max);
if (op->poll == NULL) {
free(op);
return (NULL);
}
op->toev = malloc(sizeof(*op->toev) * op->max);
if (op->toev == NULL) {
free(op->poll);
free(op);
return (NULL);
}
sigemptyset(&op->sigs);
sigaddset(&op->sigs, SIGIO);
sigaddset(&op->sigs, SIGRTMIN);
sigprocmask(SIG_BLOCK, &op->sigs, NULL);
return (op);
}
int
rtsig_add(void *arg, struct event *ev)
{
struct rtsigop *op = (struct rtsigop *) arg;
int flags, i;
#ifndef HAVE_WORKING_RTSIG
struct stat st;
#endif
if (ev->ev_events & EV_SIGNAL) {
sigaddset(&op->sigs, EVENT_SIGNAL(ev));
return sigprocmask(SIG_BLOCK, &op->sigs, NULL);
}
if (!(ev->ev_events & (EV_READ | EV_WRITE))) return 0;
#ifndef HAVE_WORKING_RTSIG
if (fstat(ev->ev_fd, &st) == -1) return -1;
if (S_ISFIFO(st.st_mode)) {
ev->ev_flags |= EVLIST_X_NORT;
op->pollmode++;
}
#endif
flags = fcntl(ev->ev_fd, F_GETFL);
if (flags == -1)
return (-1);
if (!(flags & O_ASYNC)) {
if (fcntl(ev->ev_fd, F_SETSIG, SIGRTMIN) == -1
|| fcntl(ev->ev_fd, F_SETOWN, (int) getpid()) == -1)
return (-1);
if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC))
return (-1);
}
#ifdef O_ONESIGFD
fcntl(ev->ev_fd, F_SETAUXFL, O_ONESIGFD);
#endif
op->total++;
if (poll_add(op, ev) == -1)
goto err;
return (0);
err:
i = errno;
fcntl(ev->ev_fd, F_SETFL, flags);
errno = i;
return (-1);
}
int
rtsig_del(void *arg, struct event *ev)
{
struct rtsigop *op = (struct rtsigop *) arg;
if (ev->ev_events & EV_SIGNAL) {
sigset_t sigs;
sigdelset(&op->sigs, EVENT_SIGNAL(ev));
sigemptyset(&sigs);
sigaddset(&sigs, EVENT_SIGNAL(ev));
return (sigprocmask(SIG_UNBLOCK, &sigs, NULL));
}
if (!(ev->ev_events & (EV_READ | EV_WRITE)))
return (0);
#ifndef HAVE_WORKING_RTSIG
if (ev->ev_flags & EVLIST_X_NORT)
op->pollmode--;
#endif
poll_remove(op, ev);
op->total--;
return (0);
}
int
rtsig_recalc(void *arg, int max)
{
return (0);
}
int
rtsig_dispatch(void *arg, struct timeval *tv)
{
struct rtsigop *op = (struct rtsigop *) arg;
struct timespec ts;
int res, i;
if (op->poll == NULL)
goto retry_poll;
#ifndef HAVE_WORKING_RTSIG
if (op->pollmode)
goto poll_all;
#endif
if (op->cur) {
ts.tv_sec = ts.tv_nsec = 0;
} else {
ts.tv_sec = tv->tv_sec;
ts.tv_nsec = tv->tv_usec * 1000;
}
for (;;) {
siginfo_t info;
struct event *ev;
int signum;
signum = sigtimedwait(&op->sigs, &info, &ts);
if (signum == -1) {
if (errno == EAGAIN)
break;
return (errno == EINTR ? 0 : -1);
}
ts.tv_sec = ts.tv_nsec = 0;
if (signum == SIGIO) {
#ifndef HAVE_WORKING_RTSIG
poll_all:
#endif
free(op->poll);
free(op->toev);
retry_poll:
op->cur = 0;
op->max = op->total;
op->poll = malloc(sizeof(*op->poll) * op->total);
if (op->poll == NULL)
return (-1);
op->toev = malloc(sizeof(*op->toev) * op->total);
if (op->toev == NULL) {
free(op->poll);
op->poll = NULL;
return (-1);
}
TAILQ_FOREACH(ev, &eventqueue, ev_next)
if (!(ev->ev_flags & EVLIST_X_NORT))
poll_add(op, ev);
break;
}
if (signum == SIGRTMIN) {
int flags, i, sigok = 0;
if (info.si_band <= 0) { /* SI_SIGIO */
flags = EV_READ | EV_WRITE;
} else {
flags = 0;
if (info.si_band & POLLIN) flags |= EV_READ;
if (info.si_band & POLLOUT) flags |= EV_WRITE;
if (!flags) continue;
}
for (i = 0; flags && i < op->cur; i++) {
ev = op->toev[i];
if (ev->ev_fd == info.si_fd) {
flags &= ~ev->ev_events;
sigok = 1;
}
}
for (ev = TAILQ_FIRST(&eventqueue);
flags && ev != TAILQ_END(&eventqueue);
ev = TAILQ_NEXT(ev, ev_next)) {
if (ev->ev_fd == info.si_fd) {
if (flags & ev->ev_events) {
i = poll_add(op, ev);
if (i == -1) return -1;
flags &= ~ev->ev_events;
}
sigok = 1;
}
}
if (!sigok) {
flags = fcntl(info.si_fd, F_GETFL);
if (flags == -1) return -1;
fcntl(info.si_fd, F_SETFL, flags & ~O_ASYNC);
}
} else {
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
if (EVENT_SIGNAL(ev) == signum)
activate(ev, EV_SIGNAL);
}
}
}
if (!op->cur)
return (0);
res = poll(op->poll, op->cur, tv->tv_sec * 1000 + tv->tv_usec / 1000);
if (res < 0)
return (-1);
i = 0;
#ifdef HAVE_WORKING_RTSIG
while (i < res) {
#else
while (i < op->cur) {
#endif
if (op->poll[i].revents) {
int flags = 0;
struct event *ev = op->toev[i];
if (op->poll[i].revents & POLLIN)
flags |= EV_READ;
if (op->poll[i].revents & POLLOUT)
flags |= EV_WRITE;
if (!(ev->ev_events & EV_PERSIST)) {
event_del(ev);
res--;
} else {
i++;
}
event_active(ev, flags, 1);
} else {
#ifndef HAVE_WORKING_RTSIG
if (op->toev[i]->ev_flags & EVLIST_X_NORT) {
i++;
res++;
continue;
}
#endif
for (;;) {
op->cur--;
if (i == op->cur)
break;
if (op->poll[op->cur].revents) {
memcpy(&op->poll[i], &op->poll[op->cur], sizeof(*op->poll));
op->toev[i] = op->toev[op->cur];
break;
}
}
}
}
#ifdef HAVE_WORKING_RTSIG
op->cur = res;
#endif
if (!op->cur) {
op->max = INIT_MAX;
free(op->poll);
free(op->toev);
/* We just freed it, we shouldn't have a problem getting it back. */
op->poll = malloc(sizeof(*op->poll) * op->max);
op->toev = malloc(sizeof(*op->toev) * op->max);
if (op->poll == NULL || op->toev == NULL)
err(1, "%s: malloc");
}
return (0);
}

View file

@ -0,0 +1,13 @@
AUTOMAKE_OPTIONS = foreign no-dependencies
LDADD = -L.. -levent
CPPFPLAGS = -I..
CFLAGS = -I../compat
noinst_PROGRAMS = event-test time-test signal-test
event_test_sources = event-test.c
time_test_sources = time-test.c
signal_test_sources = signal-test.c
DISTCLEANFILES = *~

287
libevent/sample/Makefile.in Normal file
View file

@ -0,0 +1,287 @@
# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
DESTDIR =
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
CC = @CC@
LN_S = @LN_S@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
PACKAGE = @PACKAGE@
RANLIB = @RANLIB@
VERSION = @VERSION@
AUTOMAKE_OPTIONS = foreign no-dependencies
LDADD = -L.. -levent
CPPFPLAGS = -I..
CFLAGS = -I../compat
noinst_PROGRAMS = event-test time-test signal-test
event_test_sources = event-test.c
time_test_sources = time-test.c
signal_test_sources = signal-test.c
DISTCLEANFILES = *~
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../config.h
CONFIG_CLEAN_FILES =
PROGRAMS = $(noinst_PROGRAMS)
DEFS = @DEFS@ -I. -I$(srcdir) -I..
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
event_test_SOURCES = event-test.c
event_test_OBJECTS = event-test.o
event_test_LDADD = $(LDADD)
event_test_DEPENDENCIES =
event_test_LDFLAGS =
time_test_SOURCES = time-test.c
time_test_OBJECTS = time-test.o
time_test_LDADD = $(LDADD)
time_test_DEPENDENCIES =
time_test_LDFLAGS =
signal_test_SOURCES = signal-test.c
signal_test_OBJECTS = signal-test.o
signal_test_LDADD = $(LDADD)
signal_test_DEPENDENCIES =
signal_test_LDFLAGS =
COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
DIST_COMMON = Makefile.am Makefile.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = tar
GZIP_ENV = --best
SOURCES = event-test.c time-test.c signal-test.c
OBJECTS = event-test.o time-test.o signal-test.o
all: all-redirect
.SUFFIXES:
.SUFFIXES: .S .c .o .s
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --foreign sample/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
mostlyclean-noinstPROGRAMS:
clean-noinstPROGRAMS:
-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
distclean-noinstPROGRAMS:
maintainer-clean-noinstPROGRAMS:
.c.o:
$(COMPILE) -c $<
.s.o:
$(COMPILE) -c $<
.S.o:
$(COMPILE) -c $<
mostlyclean-compile:
-rm -f *.o core *.core
clean-compile:
distclean-compile:
-rm -f *.tab.c
maintainer-clean-compile:
event-test: $(event_test_OBJECTS) $(event_test_DEPENDENCIES)
@rm -f event-test
$(LINK) $(event_test_LDFLAGS) $(event_test_OBJECTS) $(event_test_LDADD) $(LIBS)
time-test: $(time_test_OBJECTS) $(time_test_DEPENDENCIES)
@rm -f time-test
$(LINK) $(time_test_LDFLAGS) $(time_test_OBJECTS) $(time_test_LDADD) $(LIBS)
signal-test: $(signal_test_OBJECTS) $(signal_test_DEPENDENCIES)
@rm -f signal-test
$(LINK) $(signal_test_LDFLAGS) $(signal_test_OBJECTS) $(signal_test_LDADD) $(LIBS)
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP)
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
here=`pwd` && cd $(srcdir) \
&& mkid -f$$here/ID $$unique $(LISP)
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
mostlyclean-tags:
clean-tags:
distclean-tags:
-rm -f TAGS ID
maintainer-clean-tags:
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
subdir = sample
distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $$d/$$file $(distdir)/$$file || :; \
fi; \
done
info-am:
info: info-am
dvi-am:
dvi: dvi-am
check-am: all-am
check: check-am
installcheck-am:
installcheck: installcheck-am
install-exec-am:
install-exec: install-exec-am
install-data-am:
install-data: install-data-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
install: install-am
uninstall-am:
uninstall: uninstall-am
all-am: Makefile $(PROGRAMS)
all-redirect: all-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
installdirs:
mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f Makefile $(CONFIG_CLEAN_FILES)
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
-test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
maintainer-clean-generic:
mostlyclean-am: mostlyclean-noinstPROGRAMS mostlyclean-compile \
mostlyclean-tags mostlyclean-generic
mostlyclean: mostlyclean-am
clean-am: clean-noinstPROGRAMS clean-compile clean-tags clean-generic \
mostlyclean-am
clean: clean-am
distclean-am: distclean-noinstPROGRAMS distclean-compile distclean-tags \
distclean-generic clean-am
distclean: distclean-am
maintainer-clean-am: maintainer-clean-noinstPROGRAMS \
maintainer-clean-compile maintainer-clean-tags \
maintainer-clean-generic distclean-am
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
maintainer-clean: maintainer-clean-am
.PHONY: mostlyclean-noinstPROGRAMS distclean-noinstPROGRAMS \
clean-noinstPROGRAMS maintainer-clean-noinstPROGRAMS \
mostlyclean-compile distclean-compile clean-compile \
maintainer-clean-compile tags mostlyclean-tags distclean-tags \
clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \
check-am installcheck-am installcheck install-exec-am install-exec \
install-data-am install-data install-am install uninstall-am uninstall \
all-redirect all-am all installdirs mostlyclean-generic \
distclean-generic clean-generic maintainer-clean-generic clean \
mostlyclean distclean maintainer-clean
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

View file

@ -0,0 +1,135 @@
/*
* Compile with:
* cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#else
#include <windows.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event.h>
void
fifo_read(int fd, short event, void *arg)
{
char buf[255];
int len;
struct event *ev = arg;
#ifdef WIN32
DWORD dwBytesRead;
#endif
/* Reschedule this event */
event_add(ev, NULL);
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
fd, event, arg);
#ifdef WIN32
len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL);
// Check for end of file.
if(len && dwBytesRead == 0) {
fprintf(stderr, "End Of File");
event_del(ev);
return;
}
buf[dwBytesRead + 1] = '\0';
#else
len = read(fd, buf, sizeof(buf) - 1);
if (len == -1) {
perror("read");
return;
} else if (len == 0) {
fprintf(stderr, "Connection closed\n");
return;
}
buf[len] = '\0';
#endif
fprintf(stdout, "Read: %s\n", buf);
}
int
main (int argc, char **argv)
{
struct event evfifo;
#ifdef WIN32
HANDLE socket;
// Open a file.
socket = CreateFile("test.txt", // open File
GENERIC_READ, // open for reading
0, // do not share
NULL, // no security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if(socket == INVALID_HANDLE_VALUE)
return 1;
#else
struct stat st;
char *fifo = "event.fifo";
int socket;
if (lstat (fifo, &st) == 0) {
if ((st.st_mode & S_IFMT) == S_IFREG) {
errno = EEXIST;
perror("lstat");
exit (1);
}
}
unlink (fifo);
if (mkfifo (fifo, 0600) == -1) {
perror("mkfifo");
exit (1);
}
/* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */
#ifdef __linux
socket = open (fifo, O_RDWR | O_NONBLOCK, 0);
#else
socket = open (fifo, O_RDONLY | O_NONBLOCK, 0);
#endif
if (socket == -1) {
perror("open");
exit (1);
}
fprintf(stderr, "Write data to %s\n", fifo);
#endif
/* Initalize the event library */
event_init();
/* Initalize one event */
#ifdef WIN32
event_set(&evfifo, (int)socket, EV_READ, fifo_read, &evfifo);
#else
event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
#endif
/* Add it to the active events, without a timeout */
event_add(&evfifo, NULL);
event_dispatch();
#ifdef WIN32
CloseHandle(socket);
#endif
return (0);
}

View file

@ -0,0 +1,57 @@
/*
* Compile with:
* cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#else
#include <windows.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event.h>
int called = 0;
void
signal_cb(int fd, short event, void *arg)
{
struct event *signal = arg;
printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal));
if (called >= 2)
event_del(signal);
called++;
}
int
main (int argc, char **argv)
{
struct event signal_int;
/* Initalize the event library */
event_init();
/* Initalize one event */
event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
&signal_int);
event_add(&signal_int, NULL);
event_dispatch();
return (0);
}

View file

@ -0,0 +1,63 @@
/*
* Compile with:
* cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#else
#include <time.h>
#endif
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event.h>
int lasttime;
void
timeout_cb(int fd, short event, void *arg)
{
struct timeval tv;
struct event *timeout = arg;
int newtime = time(NULL);
printf("%s: called at %d: %d\n", __func__, newtime,
newtime - lasttime);
lasttime = newtime;
timerclear(&tv);
tv.tv_sec = 2;
event_add(timeout, &tv);
}
int
main (int argc, char **argv)
{
struct event timeout;
struct timeval tv;
/* Initalize the event library */
event_init();
/* Initalize one event */
evtimer_set(&timeout, timeout_cb, &timeout);
timerclear(&tv);
tv.tv_sec = 2;
event_add(&timeout, &tv);
lasttime = time(NULL);
event_dispatch();
return (0);
}

245
libevent/select.c Normal file
View file

@ -0,0 +1,245 @@
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
#include "evsignal.h"
extern struct event_list eventqueue;
#ifndef howmany
#define howmany(x, y) (((x)+((y)-1))/(y))
#endif
extern volatile sig_atomic_t evsignal_caught;
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
fd_set *event_readset;
fd_set *event_writeset;
sigset_t evsigmask;
} sop;
void *select_init (void);
int select_add (void *, struct event *);
int select_del (void *, struct event *);
int select_recalc (void *, int);
int select_dispatch (void *, struct timeval *);
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_recalc,
select_dispatch
};
void *
select_init(void)
{
/* Disable kqueue when this environment variable is set */
if (getenv("EVENT_NOSELECT"))
return (NULL);
memset(&sop, 0, sizeof(sop));
evsignal_init(&sop.evsigmask);
return (&sop);
}
/*
* Called with the highest fd that we know about. If it is 0, completely
* recalculate everything.
*/
int
select_recalc(void *arg, int max)
{
struct selectop *sop = arg;
fd_set *readset, *writeset;
struct event *ev;
int fdsz;
if (sop->event_fds < max)
sop->event_fds = max;
if (!sop->event_fds) {
TAILQ_FOREACH(ev, &eventqueue, ev_next)
if (ev->ev_fd > sop->event_fds)
sop->event_fds = ev->ev_fd;
}
fdsz = howmany(sop->event_fds + 1, NFDBITS) * sizeof(fd_mask);
if (fdsz > sop->event_fdsz) {
if ((readset = realloc(sop->event_readset, fdsz)) == NULL) {
log_error("malloc");
return (-1);
}
if ((writeset = realloc(sop->event_writeset, fdsz)) == NULL) {
log_error("malloc");
free(readset);
return (-1);
}
memset((char *)readset + sop->event_fdsz, 0,
fdsz - sop->event_fdsz);
memset((char *)writeset + sop->event_fdsz, 0,
fdsz - sop->event_fdsz);
sop->event_readset = readset;
sop->event_writeset = writeset;
sop->event_fdsz = fdsz;
}
return (evsignal_recalc(&sop->evsigmask));
}
int
select_dispatch(void *arg, struct timeval *tv)
{
int maxfd, res;
struct event *ev, *next;
struct selectop *sop = arg;
memset(sop->event_readset, 0, sop->event_fdsz);
memset(sop->event_writeset, 0, sop->event_fdsz);
TAILQ_FOREACH(ev, &eventqueue, ev_next) {
if (ev->ev_events & EV_WRITE)
FD_SET(ev->ev_fd, sop->event_writeset);
if (ev->ev_events & EV_READ)
FD_SET(ev->ev_fd, sop->event_readset);
}
if (evsignal_deliver(&sop->evsigmask) == -1)
return (-1);
res = select(sop->event_fds + 1, sop->event_readset,
sop->event_writeset, NULL, tv);
if (evsignal_recalc(&sop->evsigmask) == -1)
return (-1);
if (res == -1) {
if (errno != EINTR) {
log_error("select");
return (-1);
}
evsignal_process();
return (0);
} else if (evsignal_caught)
evsignal_process();
LOG_DBG((LOG_MISC, 80, "%s: select reports %d", __func__, res));
maxfd = 0;
for (ev = TAILQ_FIRST(&eventqueue); ev != NULL; ev = next) {
next = TAILQ_NEXT(ev, ev_next);
res = 0;
if (FD_ISSET(ev->ev_fd, sop->event_readset))
res |= EV_READ;
if (FD_ISSET(ev->ev_fd, sop->event_writeset))
res |= EV_WRITE;
res &= ev->ev_events;
if (res) {
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, res, 1);
} else if (ev->ev_fd > maxfd)
maxfd = ev->ev_fd;
}
sop->event_fds = maxfd;
return (0);
}
int
select_add(void *arg, struct event *ev)
{
struct selectop *sop = arg;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(&sop->evsigmask, ev));
/*
* Keep track of the highest fd, so that we can calculate the size
* of the fd_sets for select(2)
*/
if (sop->event_fds < ev->ev_fd)
sop->event_fds = ev->ev_fd;
return (0);
}
/*
* Nothing to be done here.
*/
int
select_del(void *arg, struct event *ev)
{
struct selectop *sop = arg;
if (!(ev->ev_events & EV_SIGNAL))
return (0);
return (evsignal_del(&sop->evsigmask, ev));
}

162
libevent/signal.c Normal file
View file

@ -0,0 +1,162 @@
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
#include "evsignal.h"
extern struct event_list signalqueue;
static short evsigcaught[NSIG];
static int needrecalc;
volatile sig_atomic_t evsignal_caught = 0;
void evsignal_process(void);
int evsignal_recalc(sigset_t *);
int evsignal_deliver(sigset_t *);
void
evsignal_init(sigset_t *evsigmask)
{
sigemptyset(evsigmask);
}
int
evsignal_add(sigset_t *evsigmask, struct event *ev)
{
int evsignal;
if (ev->ev_events & (EV_READ|EV_WRITE))
errx(1, "%s: EV_SIGNAL incompatible use", __func__);
evsignal = EVENT_SIGNAL(ev);
sigaddset(evsigmask, evsignal);
return (0);
}
/*
* Nothing to be done here.
*/
int
evsignal_del(sigset_t *evsigmask, struct event *ev)
{
int evsignal;
evsignal = EVENT_SIGNAL(ev);
sigdelset(evsigmask, evsignal);
needrecalc = 1;
return (sigaction(EVENT_SIGNAL(ev),(struct sigaction *)SIG_DFL, NULL));
}
static void
evsignal_handler(int sig)
{
evsigcaught[sig]++;
evsignal_caught = 1;
}
int
evsignal_recalc(sigset_t *evsigmask)
{
struct sigaction sa;
struct event *ev;
if (TAILQ_FIRST(&signalqueue) == NULL && !needrecalc)
return (0);
needrecalc = 0;
if (sigprocmask(SIG_BLOCK, evsigmask, NULL) == -1)
return (-1);
/* Reinstall our signal handler. */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = evsignal_handler;
sa.sa_mask = *evsigmask;
sa.sa_flags |= SA_RESTART;
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
if (sigaction(EVENT_SIGNAL(ev), &sa, NULL) == -1)
return (-1);
}
return (0);
}
int
evsignal_deliver(sigset_t *evsigmask)
{
if (TAILQ_FIRST(&signalqueue) == NULL)
return (0);
return (sigprocmask(SIG_UNBLOCK, evsigmask, NULL));
/* XXX - pending signals handled here */
}
void
evsignal_process(void)
{
struct event *ev;
short ncalls;
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
ncalls = evsigcaught[EVENT_SIGNAL(ev)];
if (ncalls) {
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, EV_SIGNAL, ncalls);
}
}
memset(evsigcaught, 0, sizeof(evsigcaught));
evsignal_caught = 0;
}

1
libevent/stamp-h.in Normal file
View file

@ -0,0 +1 @@
timestamp

23
libevent/test/Makefile.am Normal file
View file

@ -0,0 +1,23 @@
AUTOMAKE_OPTIONS = foreign no-dependencies
LDADD = -L.. -levent
CPPFPLAGS = -I..
CFLAGS = -I../compat -Wall @CFLAGS@
noinst_PROGRAMS = test-init test-eof test-weof test-time regress bench
test_init_sources = test-init.c
test_eof_sources = test-eof.c
test_weof_sources = test-weof.c
test_time_sources = test-time.c
regress_sources = regress.c
bench_sources = bench.c
DISTCLEANFILES = *~
all: test
test: test-init test-eof test-weof test-time regress
@./test.sh
bench test-init test-eof test-weof test-time regress: ../libevent.a

324
libevent/test/Makefile.in Normal file
View file

@ -0,0 +1,324 @@
# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include
DESTDIR =
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
transform = @program_transform_name@
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
CC = @CC@
LN_S = @LN_S@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
PACKAGE = @PACKAGE@
RANLIB = @RANLIB@
VERSION = @VERSION@
AUTOMAKE_OPTIONS = foreign no-dependencies
LDADD = -L.. -levent
CPPFPLAGS = -I..
CFLAGS = -I../compat -Wall @CFLAGS@
noinst_PROGRAMS = test-init test-eof test-weof test-time regress bench
test_init_sources = test-init.c
test_eof_sources = test-eof.c
test_weof_sources = test-weof.c
test_time_sources = test-time.c
regress_sources = regress.c
bench_sources = bench.c
DISTCLEANFILES = *~
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ../config.h
CONFIG_CLEAN_FILES =
PROGRAMS = $(noinst_PROGRAMS)
DEFS = @DEFS@ -I. -I$(srcdir) -I..
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
test_init_SOURCES = test-init.c
test_init_OBJECTS = test-init.o
test_init_LDADD = $(LDADD)
test_init_DEPENDENCIES =
test_init_LDFLAGS =
test_eof_SOURCES = test-eof.c
test_eof_OBJECTS = test-eof.o
test_eof_LDADD = $(LDADD)
test_eof_DEPENDENCIES =
test_eof_LDFLAGS =
test_weof_SOURCES = test-weof.c
test_weof_OBJECTS = test-weof.o
test_weof_LDADD = $(LDADD)
test_weof_DEPENDENCIES =
test_weof_LDFLAGS =
test_time_SOURCES = test-time.c
test_time_OBJECTS = test-time.o
test_time_LDADD = $(LDADD)
test_time_DEPENDENCIES =
test_time_LDFLAGS =
regress_SOURCES = regress.c
regress_OBJECTS = regress.o
regress_LDADD = $(LDADD)
regress_DEPENDENCIES =
regress_LDFLAGS =
bench_SOURCES = bench.c
bench_OBJECTS = bench.o
bench_LDADD = $(LDADD)
bench_DEPENDENCIES =
bench_LDFLAGS =
COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
DIST_COMMON = Makefile.am Makefile.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
TAR = tar
GZIP_ENV = --best
SOURCES = test-init.c test-eof.c test-weof.c test-time.c regress.c bench.c
OBJECTS = test-init.o test-eof.o test-weof.o test-time.o regress.o bench.o
all: all-redirect
.SUFFIXES:
.SUFFIXES: .S .c .o .s
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOMAKE) --foreign test/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
mostlyclean-noinstPROGRAMS:
clean-noinstPROGRAMS:
-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
distclean-noinstPROGRAMS:
maintainer-clean-noinstPROGRAMS:
.c.o:
$(COMPILE) -c $<
.s.o:
$(COMPILE) -c $<
.S.o:
$(COMPILE) -c $<
mostlyclean-compile:
-rm -f *.o core *.core
clean-compile:
distclean-compile:
-rm -f *.tab.c
maintainer-clean-compile:
test-init: $(test_init_OBJECTS) $(test_init_DEPENDENCIES)
@rm -f test-init
$(LINK) $(test_init_LDFLAGS) $(test_init_OBJECTS) $(test_init_LDADD) $(LIBS)
test-eof: $(test_eof_OBJECTS) $(test_eof_DEPENDENCIES)
@rm -f test-eof
$(LINK) $(test_eof_LDFLAGS) $(test_eof_OBJECTS) $(test_eof_LDADD) $(LIBS)
test-weof: $(test_weof_OBJECTS) $(test_weof_DEPENDENCIES)
@rm -f test-weof
$(LINK) $(test_weof_LDFLAGS) $(test_weof_OBJECTS) $(test_weof_LDADD) $(LIBS)
test-time: $(test_time_OBJECTS) $(test_time_DEPENDENCIES)
@rm -f test-time
$(LINK) $(test_time_LDFLAGS) $(test_time_OBJECTS) $(test_time_LDADD) $(LIBS)
regress: $(regress_OBJECTS) $(regress_DEPENDENCIES)
@rm -f regress
$(LINK) $(regress_LDFLAGS) $(regress_OBJECTS) $(regress_LDADD) $(LIBS)
bench: $(bench_OBJECTS) $(bench_DEPENDENCIES)
@rm -f bench
$(LINK) $(bench_LDFLAGS) $(bench_OBJECTS) $(bench_LDADD) $(LIBS)
tags: TAGS
ID: $(HEADERS) $(SOURCES) $(LISP)
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
here=`pwd` && cd $(srcdir) \
&& mkid -f$$here/ID $$unique $(LISP)
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
mostlyclean-tags:
clean-tags:
distclean-tags:
-rm -f TAGS ID
maintainer-clean-tags:
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
subdir = test
distdir: $(DISTFILES)
@for file in $(DISTFILES); do \
d=$(srcdir); \
if test -d $$d/$$file; then \
cp -pr $$d/$$file $(distdir)/$$file; \
else \
test -f $(distdir)/$$file \
|| ln $$d/$$file $(distdir)/$$file 2> /dev/null \
|| cp -p $$d/$$file $(distdir)/$$file || :; \
fi; \
done
info-am:
info: info-am
dvi-am:
dvi: dvi-am
check-am: all-am
check: check-am
installcheck-am:
installcheck: installcheck-am
install-exec-am:
install-exec: install-exec-am
install-data-am:
install-data: install-data-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
install: install-am
uninstall-am:
uninstall: uninstall-am
all-am: Makefile $(PROGRAMS)
all-redirect: all-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
installdirs:
mostlyclean-generic:
clean-generic:
distclean-generic:
-rm -f Makefile $(CONFIG_CLEAN_FILES)
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
-test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
maintainer-clean-generic:
mostlyclean-am: mostlyclean-noinstPROGRAMS mostlyclean-compile \
mostlyclean-tags mostlyclean-generic
mostlyclean: mostlyclean-am
clean-am: clean-noinstPROGRAMS clean-compile clean-tags clean-generic \
mostlyclean-am
clean: clean-am
distclean-am: distclean-noinstPROGRAMS distclean-compile distclean-tags \
distclean-generic clean-am
distclean: distclean-am
maintainer-clean-am: maintainer-clean-noinstPROGRAMS \
maintainer-clean-compile maintainer-clean-tags \
maintainer-clean-generic distclean-am
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
maintainer-clean: maintainer-clean-am
.PHONY: mostlyclean-noinstPROGRAMS distclean-noinstPROGRAMS \
clean-noinstPROGRAMS maintainer-clean-noinstPROGRAMS \
mostlyclean-compile distclean-compile clean-compile \
maintainer-clean-compile tags mostlyclean-tags distclean-tags \
clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \
check-am installcheck-am installcheck install-exec-am install-exec \
install-data-am install-data install-am install uninstall-am uninstall \
all-redirect all-am all installdirs mostlyclean-generic \
distclean-generic clean-generic maintainer-clean-generic clean \
mostlyclean distclean maintainer-clean
all: test
test: test-init test-eof test-weof test-time regress
@./test.sh
bench test-init test-eof test-weof test-time regress: ../libevent.a
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

181
libevent/test/bench.c Normal file
View file

@ -0,0 +1,181 @@
/*
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Mon 03/10/2003 - Modified by Davide Libenzi <davidel@xmailserver.org>
*
* Added chain event propagation to improve the sensitivity of
* the measure respect to the event loop efficency.
*
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <event.h>
static int count, writes, fired;
static int *pipes;
static int num_pipes, num_active, num_writes;
static struct event *events;
void
read_cb(int fd, short which, void *arg)
{
int idx = (int) arg, widx = idx + 1;
u_char ch;
count += read(fd, &ch, sizeof(ch));
if (writes) {
if (widx >= num_pipes)
widx -= num_pipes;
write(pipes[2 * widx + 1], "e", 1);
writes--;
fired++;
}
}
struct timeval *
run_once(void)
{
int *cp, i, space;
static struct timeval ts, te;
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
event_del(&events[i]);
event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *) i);
event_add(&events[i], NULL);
}
event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
fired = 0;
space = num_pipes / num_active;
space = space * 2;
for (i = 0; i < num_active; i++, fired++)
write(pipes[i * space + 1], "e", 1);
count = 0;
writes = num_writes;
{ int xcount = 0;
gettimeofday(&ts, NULL);
do {
event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
xcount++;
} while (count != fired);
gettimeofday(&te, NULL);
if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
}
timersub(&te, &ts, &te);
return (&te);
}
int
main (int argc, char **argv)
{
struct rlimit rl;
int i, c;
struct timeval *tv;
int *cp;
extern char *optarg;
num_pipes = 100;
num_active = 1;
num_writes = num_pipes;
while ((c = getopt(argc, argv, "n:a:w:")) != -1) {
switch (c) {
case 'n':
num_pipes = atoi(optarg);
break;
case 'a':
num_active = atoi(optarg);
break;
case 'w':
num_writes = atoi(optarg);
break;
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
exit(1);
}
}
rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
perror("setrlimit");
exit(1);
}
events = calloc(num_pipes, sizeof(struct event));
pipes = calloc(num_pipes * 2, sizeof(int));
if (events == NULL || pipes == NULL) {
perror("malloc");
exit(1);
}
event_init();
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
#ifdef USE_PIPES
if (pipe(cp) == -1) {
#else
if (socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) {
#endif
perror("pipe");
exit(1);
}
}
for (i = 0; i < 25; i++) {
tv = run_once();
if (tv == NULL)
exit(1);
fprintf(stdout, "%ld\n",
tv->tv_sec * 1000000L + tv->tv_usec);
}
exit(0);
}

518
libevent/test/regress.c Normal file
View file

@ -0,0 +1,518 @@
/*
* Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <event.h>
static int pair[2];
static int test_ok;
static int called;
static char wbuf[4096];
static char rbuf[4096];
static int woff;
static int roff;
static int usepersist;
static struct timeval tset;
static struct timeval tcalled;
#define TEST1 "this is a test"
#define SECONDS 1
void
simple_read_cb(int fd, short event, void *arg)
{
char buf[256];
int len;
len = read(fd, buf, sizeof(buf));
if (len) {
if (!called)
event_add(arg, NULL);
} else if (called == 1)
test_ok = 1;
called++;
}
void
simple_write_cb(int fd, short event, void *arg)
{
int len;
len = write(fd, TEST1, strlen(TEST1) + 1);
if (len == -1)
test_ok = 0;
else
test_ok = 1;
}
void
multiple_write_cb(int fd, short event, void *arg)
{
struct event *ev = arg;
int len;
len = 128;
if (woff + len >= sizeof(wbuf))
len = sizeof(wbuf) - woff;
len = write(fd, wbuf + woff, len);
if (len == -1) {
fprintf(stderr, "%s: write\n", __func__);
if (usepersist)
event_del(ev);
return;
}
woff += len;
if (woff >= sizeof(wbuf)) {
shutdown(fd, SHUT_WR);
if (usepersist)
event_del(ev);
return;
}
if (!usepersist)
event_add(ev, NULL);
}
void
multiple_read_cb(int fd, short event, void *arg)
{
struct event *ev = arg;
int len;
len = read(fd, rbuf + roff, sizeof(rbuf) - roff);
if (len == -1)
fprintf(stderr, "%s: read\n", __func__);
if (len <= 0) {
if (usepersist)
event_del(ev);
return;
}
roff += len;
if (!usepersist)
event_add(ev, NULL);
}
void
timeout_cb(int fd, short event, void *arg)
{
struct timeval tv;
int diff;
gettimeofday(&tcalled, NULL);
if (timercmp(&tcalled, &tset, >))
timersub(&tcalled, &tset, &tv);
else
timersub(&tset, &tcalled, &tv);
diff = tv.tv_sec*1000 + tv.tv_usec/1000 - SECONDS * 1000;
if (diff < 0)
diff = -diff;
if (diff < 100)
test_ok = 1;
}
void
signal_cb(int fd, short event, void *arg)
{
struct event *ev = arg;
signal_del(ev);
test_ok = 1;
}
struct both {
struct event ev;
int nread;
};
void
combined_read_cb(int fd, short event, void *arg)
{
struct both *both = arg;
char buf[128];
int len;
len = read(fd, buf, sizeof(buf));
if (len == -1)
fprintf(stderr, "%s: read\n", __func__);
if (len <= 0)
return;
both->nread += len;
event_add(&both->ev, NULL);
}
void
combined_write_cb(int fd, short event, void *arg)
{
struct both *both = arg;
char buf[128];
int len;
len = sizeof(buf);
if (len > both->nread)
len = both->nread;
len = write(fd, buf, len);
if (len == -1)
fprintf(stderr, "%s: write\n", __func__);
if (len <= 0) {
shutdown(fd, SHUT_WR);
return;
}
both->nread -= len;
event_add(&both->ev, NULL);
}
/* Test infrastructure */
int
setup_test(char *name)
{
fprintf(stdout, "%s", name);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
fprintf(stderr, "%s: socketpair\n", __func__);
exit(1);
}
test_ok = 0;
called = 0;
return (0);
}
int
cleanup_test(void)
{
close(pair[0]);
close(pair[1]);
if (test_ok)
fprintf(stdout, "OK\n");
else {
fprintf(stdout, "FAILED\n");
exit(1);
}
return (0);
}
void
test1(void)
{
struct event ev;
/* Very simple read test */
setup_test("Simple read: ");
write(pair[0], TEST1, strlen(TEST1)+1);
shutdown(pair[0], SHUT_WR);
event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev);
event_add(&ev, NULL);
event_dispatch();
cleanup_test();
}
void
test2(void)
{
struct event ev;
/* Very simple write test */
setup_test("Simple write: ");
event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev);
event_add(&ev, NULL);
event_dispatch();
cleanup_test();
}
void
test3(void)
{
struct event ev, ev2;
int i;
/* Multiple read and write test */
setup_test("Multiple read/write: ");
memset(rbuf, 0, sizeof(rbuf));
for (i = 0; i < sizeof(wbuf); i++)
wbuf[i] = i;
roff = woff = 0;
usepersist = 0;
event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev);
event_add(&ev, NULL);
event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2);
event_add(&ev2, NULL);
event_dispatch();
if (roff == woff)
test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
cleanup_test();
}
void
test4(void)
{
struct event ev, ev2;
int i;
/* Multiple read and write test with persist */
setup_test("Persist read/write: ");
memset(rbuf, 0, sizeof(rbuf));
for (i = 0; i < sizeof(wbuf); i++)
wbuf[i] = i;
roff = woff = 0;
usepersist = 1;
event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev);
event_add(&ev, NULL);
event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2);
event_add(&ev2, NULL);
event_dispatch();
if (roff == woff)
test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
cleanup_test();
}
void
test5(void)
{
struct both r1, r2, w1, w2;
setup_test("Combined read/write: ");
memset(&r1, 0, sizeof(r1));
memset(&r2, 0, sizeof(r2));
memset(&w1, 0, sizeof(w1));
memset(&w2, 0, sizeof(w2));
w1.nread = 4096;
w2.nread = 8192;
event_set(&r1.ev, pair[0], EV_READ, combined_read_cb, &r1);
event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1);
event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2);
event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2);
event_add(&r1.ev, NULL);
event_add(&w1.ev, NULL);
event_add(&r2.ev, NULL);
event_add(&w2.ev, NULL);
event_dispatch();
if (r1.nread == 8192 && r2.nread == 4096)
test_ok = 1;
cleanup_test();
}
void
test6(void)
{
struct timeval tv;
struct event ev;
setup_test("Simple timeout: ");
tv.tv_usec = 0;
tv.tv_sec = SECONDS;
evtimer_set(&ev, timeout_cb, NULL);
evtimer_add(&ev, &tv);
gettimeofday(&tset, NULL);
event_dispatch();
cleanup_test();
}
void
test7(void)
{
struct event ev;
struct itimerval itv;
setup_test("Simple signal: ");
signal_set(&ev, SIGALRM, signal_cb, &ev);
signal_add(&ev, NULL);
memset(&itv, 0, sizeof(itv));
itv.it_value.tv_sec = 1;
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
goto skip_simplesignal;
event_dispatch();
skip_simplesignal:
signal_del(&ev);
cleanup_test();
}
void
test8(void)
{
struct timeval tv, tv_start, tv_end;
struct event ev;
setup_test("Loop exit: ");
tv.tv_usec = 0;
tv.tv_sec = 60*60*24;
evtimer_set(&ev, timeout_cb, NULL);
evtimer_add(&ev, &tv);
tv.tv_usec = 0;
tv.tv_sec = 1;
event_loopexit(&tv);
gettimeofday(&tv_start, NULL);
event_dispatch();
gettimeofday(&tv_end, NULL);
timersub(&tv_end, &tv_start, &tv_end);
evtimer_del(&ev);
if (tv.tv_sec < 2)
test_ok = 1;
cleanup_test();
}
void
readcb(struct bufferevent *bev, void *arg)
{
if (EVBUFFER_LENGTH(bev->input) == 4096) {
bufferevent_disable(bev, EV_READ);
test_ok++;
}
}
void
writecb(struct bufferevent *bev, void *arg)
{
if (EVBUFFER_LENGTH(bev->output) == 0)
test_ok++;
}
void
errorcb(struct bufferevent *bev, short what, void *arg)
{
test_ok = -2;
}
void
test9(void)
{
struct bufferevent *bev1, *bev2;
char buffer[4096];
int i;
setup_test("Bufferevent: ");
bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL);
bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL);
bufferevent_disable(bev1, EV_READ);
bufferevent_enable(bev2, EV_READ);
for (i = 0; i < sizeof(buffer); i++)
buffer[0] = i;
bufferevent_write(bev1, buffer, sizeof(buffer));
event_dispatch();
bufferevent_free(bev1);
bufferevent_free(bev2);
if (test_ok != 2)
test_ok = 0;
cleanup_test();
}
int
main (int argc, char **argv)
{
setvbuf(stdout, NULL, _IONBF, 0);
/* Initalize the event library */
event_init();
test1();
test2();
test3();
test4();
test5();
test6();
test7();
test8();
test9();
return (0);
}

68
libevent/test/test-eof.c Normal file
View file

@ -0,0 +1,68 @@
/*
* Compile with:
* cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <event.h>
int test_okay = 1;
int called = 0;
void
read_cb(int fd, short event, void *arg)
{
char buf[256];
int len;
len = read(fd, buf, sizeof(buf));
printf("%s: read %d%s\n", __func__,
len, len ? "" : " - means EOF");
if (len) {
if (!called)
event_add(arg, NULL);
} else if (called == 1)
test_okay = 0;
called++;
}
int
main (int argc, char **argv)
{
struct event ev;
char *test = "test string";
int pair[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
return (1);
write(pair[0], test, strlen(test)+1);
shutdown(pair[0], SHUT_WR);
/* Initalize the event library */
event_init();
/* Initalize one event */
event_set(&ev, pair[1], EV_READ, read_cb, &ev);
event_add(&ev, NULL);
event_dispatch();
return (test_okay);
}

27
libevent/test/test-init.c Normal file
View file

@ -0,0 +1,27 @@
/*
* Compile with:
* cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <event.h>
int
main(int argc, char **argv)
{
/* Initalize the event library */
event_init();
return (0);
}

68
libevent/test/test-time.c Normal file
View file

@ -0,0 +1,68 @@
/*
* Compile with:
* cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <event.h>
int called = 0;
#define NEVENT 20000
struct event *ev[NEVENT];
void
time_cb(int fd, short event, void *arg)
{
struct timeval tv;
int i, j;
called++;
if (called < 10*NEVENT) {
for (i = 0; i < 10; i++) {
j = random() % NEVENT;
tv.tv_sec = 0;
tv.tv_usec = random() % 50000L;
if (tv.tv_usec % 2)
evtimer_add(ev[j], &tv);
else
evtimer_del(ev[j]);
}
}
}
int
main (int argc, char **argv)
{
struct timeval tv;
int i;
/* Initalize the event library */
event_init();
for (i = 0; i < NEVENT; i++) {
ev[i] = malloc(sizeof(struct event));
/* Initalize one event */
evtimer_set(ev[i], time_cb, ev[i]);
tv.tv_sec = 0;
tv.tv_usec = random() % 50000L;
evtimer_add(ev[i], &tv);
}
event_dispatch();
return (called < NEVENT);
}

68
libevent/test/test-weof.c Normal file
View file

@ -0,0 +1,68 @@
/*
* Compile with:
* cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <event.h>
int pair[2];
int test_okay = 1;
int called = 0;
void
write_cb(int fd, short event, void *arg)
{
char *test = "test string";
int len;
len = write(fd, test, strlen(test) + 1);
printf("%s: write %d%s\n", __func__,
len, len ? "" : " - means EOF");
if (len > 0) {
if (!called)
event_add(arg, NULL);
close(pair[0]);
} else if (called == 1)
test_okay = 0;
called++;
}
int
main (int argc, char **argv)
{
struct event ev;
if (signal(SIGPIPE, SIG_IGN) == SIG_IGN)
return (1);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
return (1);
/* Initalize the event library */
event_init();
/* Initalize one event */
event_set(&ev, pair[1], EV_WRITE, write_cb, &ev);
event_add(&ev, NULL);
event_dispatch();
return (test_okay);
}

77
libevent/test/test.sh Executable file
View file

@ -0,0 +1,77 @@
#!/bin/sh
setup () {
export EVENT_NOKQUEUE=yes
export EVENT_NOPOLL=yes
export EVENT_NOSELECT=yes
export EVENT_NOEPOLL=yes
export EVENT_NORTSIG=yes
}
test () {
if ! ./test-init 2>/dev/null ;
then
echo Skipping test
return
fi
echo -n " test-eof: "
if ./test-eof >/dev/null ;
then
echo OKAY ;
else
echo FAILED ;
fi
echo -n " test-weof: "
if ./test-weof >/dev/null ;
then
echo OKAY ;
else
echo FAILED ;
fi
echo -n " test-time: "
if ./test-time >/dev/null ;
then
echo OKAY ;
else
echo FAILED ;
fi
echo -n " regress: "
if ./regress >/dev/null ;
then
echo OKAY ;
else
echo FAILED ;
fi
}
echo "Running tests:"
# Need to do this by hand?
setup
unset EVENT_NOKQUEUE
echo "KQUEUE"
test
setup
unset EVENT_NOPOLL
echo "POLL"
test
setup
unset EVENT_NOSELECT
echo "SELECT"
test
setup
unset EVENT_NORTSIG
echo "RTSIG"
test
setup
unset EVENT_NOEPOLL
echo "EPOLL"
test

848
list.c Normal file
View file

@ -0,0 +1,848 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** Portions borrowed from kazlib. Header follows:
*/
/*
* List Abstract Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* Free Software License:
*
* All rights are reserved by the author, with the following exceptions:
* Permission is granted to freely reproduce and distribute this software,
* possibly in exchange for a fee, provided that this copyright notice appears
* intact. Permission is also granted to adapt this software to produce
* derivative works, as long as the modified versions carry this copyright
* notice and additional notices stating that the work has been modified.
* This source code may be translated into executable form and incorporated
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
* $Id$
* $Name: $
*/
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#define LIST_IMPLEMENTATION
#include "list.h"
#include "log.h"
#define next list_next
#define prev list_prev
#define data list_data
#define pool list_pool
#define fre list_free
#define size list_size
#define nilnode list_nilnode
#define nodecount list_nodecount
#define maxcount list_maxcount
#define list_nil(L) (&(L)->nilnode)
#define list_first_priv(L) ((L)->nilnode.next)
#define list_last_priv(L) ((L)->nilnode.prev)
#define lnode_next(N) ((N)->next)
#define lnode_prev(N) ((N)->prev)
#ifdef KAZLIB_RCSID
static const char rcsid[] = "$Id$";
#endif
/*
* Initialize a list object supplied by the client such that it becomes a valid
* empty list. If the list is to be ``unbounded'', the maxcount should be
* specified as LISTCOUNT_T_MAX, or, alternately, as -1. The value zero
* is not permitted.
*/
list_t *
list_init (list_t * list, listcount_t maxcount)
{
nassert (maxcount != 0);
list->nilnode.next = &list->nilnode;
list->nilnode.prev = &list->nilnode;
list->nodecount = 0;
list->maxcount = maxcount;
return list;
}
/*
* Dynamically allocate a list object using malloc(), and initialize it so that
* it is a valid empty list. If the list is to be ``unbounded'', the maxcount
* should be specified as LISTCOUNT_T_MAX, or, alternately, as -1.
*/
list_t *
list_create (listcount_t maxcount)
{
list_t *new = malloc (sizeof *new);
if (new) {
nassert (maxcount != 0);
new->nilnode.next = &new->nilnode;
new->nilnode.prev = &new->nilnode;
new->nodecount = 0;
new->maxcount = maxcount;
}
return new;
}
/*
* Destroy a dynamically allocated list object.
* The client must remove the nodes first.
*/
void
list_destroy (list_t * list)
{
nassert (list_isempty (list));
free (list);
}
/*
* Free all of the nodes of a list. The list must contain only
* dynamically allocated nodes. After this call, the list
* is empty.
*/
void
list_destroy_nodes (list_t * list)
{
lnode_t *lnode = list_first_priv (list), *nil = list_nil (list), *tmp;
while (lnode != nil) {
tmp = lnode->next;
lnode->next = NULL;
lnode->prev = NULL;
lnode_destroy (lnode);
lnode = tmp;
}
list_init (list, list->maxcount);
}
/*
* Return all of the nodes of a list to a node pool. The nodes in
* the list must all have come from the same pool.
*/
void
list_return_nodes (list_t * list, lnodepool_t * pool)
{
lnode_t *lnode = list_first_priv (list), *tmp, *nil = list_nil (list);
while (lnode != nil) {
tmp = lnode->next;
lnode->next = NULL;
lnode->prev = NULL;
lnode_return (pool, lnode);
lnode = tmp;
}
list_init (list, list->maxcount);
}
/*
* Insert the node ``new'' into the list immediately after ``this'' node.
*/
void
list_ins_after (list_t * list, lnode_t * new, lnode_t * this)
{
lnode_t *that = this->next;
nassert (new != NULL);
nassert (!list_contains (list, new));
nassert (!lnode_is_in_a_list (new));
nassert (this == list_nil (list) || list_contains (list, this));
nassert (list->nodecount + 1 > list->nodecount);
new->prev = this;
new->next = that;
that->prev = new;
this->next = new;
list->nodecount++;
nassert (list->nodecount <= list->maxcount);
}
/*
* Insert the node ``new'' into the list immediately before ``this'' node.
*/
void
list_ins_before (list_t * list, lnode_t * new, lnode_t * this)
{
lnode_t *that = this->prev;
nassert (new != NULL);
nassert (!list_contains (list, new));
nassert (!lnode_is_in_a_list (new));
nassert (this == list_nil (list) || list_contains (list, this));
nassert (list->nodecount + 1 > list->nodecount);
new->next = this;
new->prev = that;
that->next = new;
this->prev = new;
list->nodecount++;
nassert (list->nodecount <= list->maxcount);
}
/*
* Delete the given node from the list.
*/
lnode_t *
list_delete (list_t * list, lnode_t * del)
{
lnode_t *next = del->next;
lnode_t *prev = del->prev;
nassert (list_contains (list, del));
prev->next = next;
next->prev = prev;
list->nodecount--;
del->next = del->prev = NULL;
return del;
}
/*
* For each node in the list, execute the given function. The list,
* current node and the given context pointer are passed on each
* call to the function.
*/
void
list_process (list_t * list, void *context, void (*function) (list_t * list, lnode_t * lnode, void *context))
{
lnode_t *node = list_first_priv (list), *next, *nil = list_nil (list);
while (node != nil) {
/* check for callback function deleting */
/* the next node from under us */
nassert (list_contains (list, node));
next = node->next;
function (list, node, context);
node = next;
}
}
/*
* Dynamically allocate a list node and assign it the given piece of data.
*/
lnode_t *
lnode_create (void *data)
{
lnode_t *new = malloc (sizeof *new);
if (new) {
new->data = data;
new->next = NULL;
new->prev = NULL;
}
return new;
}
/*
* Initialize a user-supplied lnode.
*/
lnode_t *
lnode_init (lnode_t * lnode, void *data)
{
lnode->data = data;
lnode->next = NULL;
lnode->prev = NULL;
return lnode;
}
/*
* Destroy a dynamically allocated node.
*/
void
lnode_destroy (lnode_t * lnode)
{
nassert (!lnode_is_in_a_list (lnode));
free (lnode);
}
/*
* Initialize a node pool object to use a user-supplied set of nodes.
* The ``nodes'' pointer refers to an array of lnode_t objects, containing
* ``n'' elements.
*/
lnodepool_t *
lnode_pool_init (lnodepool_t * pool, lnode_t * nodes, listcount_t n)
{
listcount_t i;
nassert (n != 0);
pool->pool = nodes;
pool->fre = nodes;
pool->size = n;
for (i = 0; i < n - 1; i++) {
nodes[i].next = nodes + i + 1;
}
nodes[i].next = NULL;
nodes[i].prev = nodes; /* to make sure node is marked ``on list'' */
return pool;
}
/*
* Create a dynamically allocated pool of n nodes.
*/
lnodepool_t *
lnode_pool_create (listcount_t n)
{
lnodepool_t *pool;
lnode_t *nodes;
nassert (n != 0);
pool = malloc (sizeof *pool);
if (!pool)
return NULL;
nodes = malloc (n * sizeof *nodes);
if (!nodes) {
free (pool);
return NULL;
}
lnode_pool_init (pool, nodes, n);
return pool;
}
/*
* Determine whether the given pool is from this pool.
*/
int
lnode_pool_isfrom (lnodepool_t * pool, lnode_t * node)
{
listcount_t i;
/* this is carefully coded this way because ANSI C forbids pointers
to different objects from being subtracted or compared other
than for exact equality */
for (i = 0; i < pool->size; i++) {
if (pool->pool + i == node)
return 1;
}
return 0;
}
/*
* Destroy a dynamically allocated pool of nodes.
*/
void
lnode_pool_destroy (lnodepool_t * p)
{
free (p->pool);
free (p);
}
/*
* Borrow a node from a node pool. Returns a null pointer if the pool
* is exhausted.
*/
lnode_t *
lnode_borrow (lnodepool_t * pool, void *data)
{
lnode_t *new = pool->fre;
if (new) {
pool->fre = new->next;
new->data = data;
new->next = NULL;
new->prev = NULL;
}
return new;
}
/*
* Return a node to a node pool. A node must be returned to the pool
* from which it came.
*/
void
lnode_return (lnodepool_t * pool, lnode_t * node)
{
nassert (lnode_pool_isfrom (pool, node));
nassert (!lnode_is_in_a_list (node));
node->next = pool->fre;
node->prev = node;
pool->fre = node;
}
/*
* Determine whether the given list contains the given node.
* According to this function, a list does not contain its nilnode.
*/
int
list_contains (list_t * list, lnode_t * node)
{
lnode_t *n, *nil = list_nil (list);
for (n = list_first_priv (list); n != nil; n = lnode_next (n)) {
if (node == n)
return 1;
}
return 0;
}
/*
* A more generalized variant of list_transfer. This one removes a
* ``slice'' from the source list and appends it to the destination
* list.
*/
void
list_extract (list_t * dest, list_t * source, lnode_t * first, lnode_t * last)
{
listcount_t moved = 1;
nassert (first == NULL || list_contains (source, first));
nassert (last == NULL || list_contains (source, last));
if (first == NULL || last == NULL)
return;
/* adjust the destination list so that the slice is spliced out */
first->prev->next = last->next;
last->next->prev = first->prev;
/* graft the splice at the end of the dest list */
last->next = &dest->nilnode;
first->prev = dest->nilnode.prev;
dest->nilnode.prev->next = first;
dest->nilnode.prev = last;
while (first != last) {
first = first->next;
nassert (first != list_nil (source)); /* oops, last before first! */
moved++;
}
/* nassert no overflows */
nassert (source->nodecount - moved <= source->nodecount);
nassert (dest->nodecount + moved >= dest->nodecount);
/* nassert no weirdness */
nassert (moved <= source->nodecount);
source->nodecount -= moved;
dest->nodecount += moved;
/* nassert list sanity */
nassert (list_verify (source));
nassert (list_verify (dest));
}
/*
* Split off a trailing sequence of nodes from the source list and relocate
* them to the tail of the destination list. The trailing sequence begins
* with node ``first'' and terminates with the last node of the source
* list. The nodes are added to the end of the new list in their original
* order.
*/
void
list_transfer (list_t * dest, list_t * source, lnode_t * first)
{
listcount_t moved = 1;
lnode_t *last;
nassert (first == NULL || list_contains (source, first));
if (first == NULL)
return;
last = source->nilnode.prev;
source->nilnode.prev = first->prev;
first->prev->next = &source->nilnode;
last->next = &dest->nilnode;
first->prev = dest->nilnode.prev;
dest->nilnode.prev->next = first;
dest->nilnode.prev = last;
while (first != last) {
first = first->next;
moved++;
}
/* nassert no overflows */
nassert (source->nodecount - moved <= source->nodecount);
nassert (dest->nodecount + moved >= dest->nodecount);
/* nassert no weirdness */
nassert (moved <= source->nodecount);
source->nodecount -= moved;
dest->nodecount += moved;
/* nassert list sanity */
nassert (list_verify (source));
nassert (list_verify (dest));
}
void
list_merge (list_t * dest, list_t * sour, int compare (const void *, const void *))
{
lnode_t *dn, *sn, *tn;
lnode_t *d_nil = list_nil (dest), *s_nil = list_nil (sour);
/* Nothing to do if source and destination list are the same. */
if (dest == sour)
return;
/* overflow check */
nassert (list_count (sour) + list_count (dest) >= list_count (sour));
/* lists must be sorted */
nassert (list_is_sorted (sour, compare));
nassert (list_is_sorted (dest, compare));
dn = list_first_priv (dest);
sn = list_first_priv (sour);
while (dn != d_nil && sn != s_nil) {
if (compare (lnode_get (dn), lnode_get (sn)) >= 0) {
tn = lnode_next (sn);
list_delete (sour, sn);
list_ins_before (dest, sn, dn);
sn = tn;
} else {
dn = lnode_next (dn);
}
}
if (dn != d_nil)
return;
if (sn != s_nil)
list_transfer (dest, sour, sn);
}
void
list_sort (list_t * list, int compare (const void *, const void *))
{
list_t extra;
listcount_t middle;
lnode_t *node;
if (list_count (list) > 1) {
middle = list_count (list) / 2;
node = list_first_priv (list);
list_init (&extra, list_count (list) - middle);
while (middle--)
node = lnode_next (node);
list_transfer (&extra, list, node);
list_sort (list, compare);
list_sort (&extra, compare);
list_merge (list, &extra, compare);
}
nassert (list_is_sorted (list, compare));
}
lnode_t *
list_find (list_t * list, const void *key, int compare (const void *, const void *))
{
lnode_t *node;
for (node = list_first_priv (list); node != list_nil (list); node = node->next) {
if (compare (lnode_get (node), key) == 0)
return node;
}
return 0;
}
/*
* Return 1 if the list is in sorted order, 0 otherwise
*/
int
list_is_sorted (list_t * list, int compare (const void *, const void *))
{
lnode_t *node, *next, *nil;
next = nil = list_nil (list);
node = list_first_priv (list);
if (node != nil)
next = lnode_next (node);
for (; next != nil; node = next, next = lnode_next (next)) {
if (compare (lnode_get (node), lnode_get (next)) > 0)
return 0;
}
return 1;
}
/*
* Get rid of macro functions definitions so they don't interfere
* with the actual definitions
*/
#undef list_isempty
#undef list_isfull
#undef lnode_pool_isempty
#undef list_append
#undef list_prepend
#undef list_first
#undef list_last
#undef list_next
#undef list_prev
#undef list_count
#undef list_del_first
#undef list_del_last
#undef lnode_put
#undef lnode_get
/*
* Return 1 if the list is empty, 0 otherwise
*/
int
list_isempty (list_t * list)
{
return list->nodecount == 0;
}
/*
* Return 1 if the list is full, 0 otherwise
* Permitted only on bounded lists.
*/
int
list_isfull (list_t * list)
{
return list->nodecount == list->maxcount;
}
/*
* Check if the node pool is empty.
*/
int
lnode_pool_isempty (lnodepool_t * pool)
{
return (pool->fre == NULL);
}
/*
* Add the given node at the end of the list
*/
void
list_append (list_t * list, lnode_t * node)
{
list_ins_before (list, node, &list->nilnode);
}
/*
* Add the given node at the beginning of the list.
*/
void
list_prepend (list_t * list, lnode_t * node)
{
list_ins_after (list, node, &list->nilnode);
}
/*
* Retrieve the first node of the list
*/
lnode_t *
list_first (list_t * list)
{
if (list->nilnode.next == &list->nilnode)
return NULL;
return list->nilnode.next;
}
/*
* Retrieve the last node of the list
*/
lnode_t *
list_last (list_t * list)
{
if (list->nilnode.prev == &list->nilnode)
return NULL;
return list->nilnode.prev;
}
/*
* Retrieve the count of nodes in the list
*/
listcount_t
list_count (list_t * list)
{
return list->nodecount;
}
/*
* Remove the first node from the list and return it.
*/
lnode_t *
list_del_first (list_t * list)
{
return list_delete (list, list->nilnode.next);
}
/*
* Remove the last node from the list and return it.
*/
lnode_t *
list_del_last (list_t * list)
{
return list_delete (list, list->nilnode.prev);
}
/*
* Associate a data item with the given node.
*/
void
lnode_put (lnode_t * lnode, void *data)
{
lnode->data = data;
}
/*
* Retrieve the data item associated with the node.
*/
void *
lnode_get (lnode_t * lnode)
{
return lnode->data;
}
/*
* Retrieve the node's successor. If there is no successor,
* NULL is returned.
*/
lnode_t *
list_next (list_t * list, lnode_t * lnode)
{
nassert (list_contains (list, lnode));
if (lnode->next == list_nil (list))
return NULL;
return lnode->next;
}
/*
* Retrieve the node's predecessor. See comment for lnode_next().
*/
lnode_t *
list_prev (list_t * list, lnode_t * lnode)
{
nassert (list_contains (list, lnode));
if (lnode->prev == list_nil (list))
return NULL;
return lnode->prev;
}
/*
* Return 1 if the lnode is in some list, otherwise return 0.
*/
int
lnode_is_in_a_list (lnode_t * lnode)
{
return (lnode->next != NULL || lnode->prev != NULL);
}
int
list_verify (list_t * list)
{
lnode_t *node = list_first_priv (list), *nil = list_nil (list);
listcount_t count = list_count (list);
if (node->prev != nil) {
return 0;
}
if (count > list->maxcount) {
return 0;
}
while (node != nil && count--) {
if (node->next->prev != node) {
return 0;
}
node = node->next;
}
if (count != 0 || node != nil) {
return 0;
}
return 1;
}
int
comparef (const void *key1, const void *key2)
{
return strcasecmp (key1, key2);
}

179
list.h Normal file
View file

@ -0,0 +1,179 @@
/* NeoStats - IRC Statistical Services Copyright (c) 1999-2002 NeoStats Group Inc.
** Copyright (c) 1999-2002 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** Portions borrowed from kazlib. Original Header follows:
*/
/*
* List Abstract Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* Free Software License:
*
* All rights are reserved by the author, with the following exceptions:
* Permission is granted to freely reproduce and distribute this software,
* possibly in exchange for a fee, provided that this copyright notice appears
* intact. Permission is also granted to adapt this software to produce
* derivative works, as long as the modified versions carry this copyright
* notice and additional notices stating that the work has been modified.
* This source code may be translated into executable form and incorporated
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
* $Id$
* $Name: $
*/
#ifndef LIST_H
#define LIST_H
#include <limits.h>
#ifdef KAZLIB_SIDEEFFECT_DEBUG
#include "sfx.h"
#define LIST_SFX_CHECK(E) SFX_CHECK(E)
#else
#define LIST_SFX_CHECK(E) (E)
#endif
/*
* Blurb for inclusion into C++ translation units
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned long listcount_t;
#define LISTCOUNT_T_MAX ULONG_MAX
typedef struct lnode_t {
#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
struct lnode_t *list_next;
struct lnode_t *list_prev;
void *list_data;
#else
int list_dummy;
#endif
} lnode_t;
typedef struct lnodepool_t {
#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
struct lnode_t *list_pool;
struct lnode_t *list_free;
listcount_t list_size;
#else
int list_dummy;
#endif
} lnodepool_t;
typedef struct list_t {
#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
lnode_t list_nilnode;
listcount_t list_nodecount;
listcount_t list_maxcount;
#else
int list_dummy;
#endif
} list_t;
lnode_t *lnode_create (void *);
lnode_t *lnode_init (lnode_t *, void *);
void lnode_destroy (lnode_t *);
void lnode_put (lnode_t *, void *);
void *lnode_get (lnode_t *);
int lnode_is_in_a_list (lnode_t *);
#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
#define lnode_put(N, D) ((N)->list_data = (D))
#define lnode_get(N) ((N)->list_data)
#endif
lnodepool_t *lnode_pool_init (lnodepool_t *, lnode_t *, listcount_t);
lnodepool_t *lnode_pool_create (listcount_t);
void lnode_pool_destroy (lnodepool_t *);
lnode_t *lnode_borrow (lnodepool_t *, void *);
void lnode_return (lnodepool_t *, lnode_t *);
int lnode_pool_isempty (lnodepool_t *);
int lnode_pool_isfrom (lnodepool_t *, lnode_t *);
int comparef (const void *, const void *);
list_t *list_init (list_t *, listcount_t);
list_t *list_create (listcount_t);
void list_destroy (list_t *);
void list_destroy_nodes (list_t *);
void list_return_nodes (list_t *, lnodepool_t *);
listcount_t list_count (list_t *);
int list_isempty (list_t *);
int list_isfull (list_t *);
int list_contains (list_t *, lnode_t *);
void list_append (list_t *, lnode_t *);
void list_prepend (list_t *, lnode_t *);
void list_ins_before (list_t *, lnode_t *, lnode_t *);
void list_ins_after (list_t *, lnode_t *, lnode_t *);
lnode_t *list_first (list_t *);
lnode_t *list_last (list_t *);
lnode_t *list_next (list_t *, lnode_t *);
lnode_t *list_prev (list_t *, lnode_t *);
lnode_t *list_del_first (list_t *);
lnode_t *list_del_last (list_t *);
lnode_t *list_delete (list_t *, lnode_t *);
void list_process (list_t *, void *, void (*)(list_t *, lnode_t *, void *));
int list_verify (list_t *);
#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
#define lnode_pool_isempty(P) ((P)->list_free == 0)
#define list_count(L) ((L)->list_nodecount)
#define list_isempty(L) ((L)->list_nodecount == 0)
#define list_isfull(L) (LIST_SFX_CHECK(L)->list_nodecount == (L)->list_maxcount)
#define list_next(L, N) (LIST_SFX_CHECK(N)->list_next == &(L)->list_nilnode ? NULL : (N)->list_next)
#define list_prev(L, N) (LIST_SFX_CHECK(N)->list_prev == &(L)->list_nilnode ? NULL : (N)->list_prev)
#define list_first(L) list_next(LIST_SFX_CHECK(L), &(L)->list_nilnode)
#define list_last(L) list_prev(LIST_SFX_CHECK(L), &(L)->list_nilnode)
#endif
#if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
#define list_append(L, N) list_ins_before(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
#define list_prepend(L, N) list_ins_after(LIST_SFX_CHECK(L), N, &(L)->list_nilnode)
#define list_del_first(L) list_delete(LIST_SFX_CHECK(L), list_first(L))
#define list_del_last(L) list_delete(LIST_SFX_CHECK(L), list_last(L))
#endif
/* destination list on the left, source on the right */
void list_extract (list_t *, list_t *, lnode_t *, lnode_t *);
void list_transfer (list_t *, list_t *, lnode_t * first);
void list_merge (list_t *, list_t *, int (const void *, const void *));
void list_sort (list_t *, int (const void *, const void *));
lnode_t *list_find (list_t *, const void *, int (const void *, const void *));
int list_is_sorted (list_t *, int (const void *, const void *));
#ifdef __cplusplus
}
#endif
#endif

269
log.c Normal file
View file

@ -0,0 +1,269 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#include "config.h"
#include "defines.h"
#include "conf.h"
#include "hash.h"
#include "log.h"
#include "dotconf.h"
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
const char* CoreLogFileName="MQServer";
char LogFileNameFormat[MAX_LOGFILENAME]="-%m-%d";
const char *loglevels[10] = {
"CRITICAL",
"ERROR",
"WARNING",
"NOTICE",
"NORMAL",
"INFO",
"DEBUG1",
"DEBUG2",
"DEBUG3",
"INSANE"
};
static char log_buf[BUFSIZE];
static char log_fmttime[TIMEBUFSIZE];
struct logs_ {
FILE *logfile;
char name[BUFSIZE];
char logname[MAXPATH];
unsigned int flush;
} logs_;
hash_t *logs;
/** @brief initialize the logging functions
*/
int
init_logs ()
{
SET_SEGV_LOCATION();
logs = hash_create (-1, 0, 0);
if (!logs) {
printf ("ERROR: Can't initialize log subsystem. Exiting!");
return NS_FAILURE;
}
printf("Logging Subsystem Started\n");
return NS_SUCCESS;
}
/** @brief Occasionally flush log files out
*/
void
close_logs ()
{
hscan_t hs;
hnode_t *hn;
struct logs_ *logentry;
SET_SEGV_LOCATION();
hash_scan_begin (&hs, logs);
while ((hn = hash_scan_next (&hs)) != NULL) {
logentry = hnode_get (hn);
logentry->flush = 0;
#ifdef DEBUG
printf ("Closing Logfile %s (%s)\n", logentry->name, (char *) hnode_getkey (hn));
#endif
if(logentry->logfile)
{
fflush (logentry->logfile);
fclose (logentry->logfile);
logentry->logfile = NULL;
}
hash_scan_delete (logs, hn);
hnode_destroy (hn);
free (logentry);
}
}
void
fini_logs() {
hscan_t hs;
hnode_t *hn;
struct logs_ *logentry;
SET_SEGV_LOCATION();
hash_scan_begin (&hs, logs);
while ((hn = hash_scan_next (&hs)) != NULL) {
logentry = hnode_get (hn);
logentry->flush = 0;
#ifdef DEBUG
printf ("Closing Logfile %s (%s)\n", logentry->name, (char *) hnode_getkey (hn));
#endif
if(logentry->logfile)
{
fflush (logentry->logfile);
fclose (logentry->logfile);
logentry->logfile = NULL;
}
hash_scan_delete (logs, hn);
hnode_destroy (hn);
free (logentry);
}
/* for some reason, the logs are not getting flushed correctly */
hash_destroy(logs);
}
void make_log_filename(char* modname, char *logname)
{
time_t t = time(NULL);
strftime (log_fmttime, TIMEBUFSIZE, LogFileNameFormat, localtime (&t));
// strlwr(modname);
snprintf (logname, MAXPATH, "logs/%s%s.log", modname, log_fmttime);
}
/** @Configurable logging function
*/
void
nlog (int level, int scope, char *fmt, ...)
{
va_list ap;
hnode_t *hn;
struct logs_ *logentry;
if (level <= config.debug) {
/* if scope is > 0, then log to a diff file */
if (scope > 0) {
if (segv_inmodule[0] != 0) {
hn = hash_lookup (logs, segv_inmodule);
} else {
#if 0
nlog (LOG_ERROR, LOG_CORE, "Warning, nlog called with LOG_MOD, but segv_inmodule is blank! Logging to Core");
#endif
hn = hash_lookup (logs, CoreLogFileName);
}
} else {
hn = hash_lookup (logs, CoreLogFileName);
}
if (hn) {
/* we found our log entry */
logentry = hnode_get (hn);
if(!logentry->logfile)
logentry->logfile = fopen (logentry->logname, "a");
} else {
/* log file not found */
if (segv_inmodule[0] == 0 && (scope > 0)) {
#ifdef DEBUG
printf ("segv_inmodule is blank, but scope is for Modules!\n");
#endif
/* bad, but hey ! */
scope = 0;
}
logentry = malloc (sizeof (struct logs_));
strncpy (logentry->name, scope > 0 ? segv_inmodule : CoreLogFileName, BUFSIZE);
make_log_filename(logentry->name, logentry->logname);
logentry->logfile = fopen (logentry->logname, "a");
logentry->flush = 0;
hn = hnode_create (logentry);
hash_insert (logs, hn, logentry->name);
}
#ifdef DEBUG
if (!logentry->logfile) {
printf ("LOG ERROR: %s\n", strerror (errno));
do_exit (NS_EXIT_NORMAL, NULL);
}
#endif
/* we update me.now here, becase some functions might be busy and not call the loop a lot */
me.now = time(NULL);
snprintf (me.strnow, STR_TIME_T_SIZE, "%ld", me.now);
strftime (log_fmttime, TIMEBUFSIZE, "%d/%m/%Y[%H:%M:%S]", localtime (&me.now));
va_start (ap, fmt);
vsnprintf (log_buf, BUFSIZE, fmt, ap);
va_end (ap);
fprintf (logentry->logfile, "(%s) %s %s - %s\n", log_fmttime, loglevels[level - 1], scope > 0 ? segv_inmodule : "CORE", log_buf);
logentry->flush = 1;
#ifndef DEBUG
if (config.foreground)
#endif
printf ("%s %s - %s\n", loglevels[level - 1], scope > 0 ? segv_inmodule : "CORE", log_buf);
}
}
/** rotate logs, called at midnight
*/
void
reset_logs ()
{
hscan_t hs;
hnode_t *hn;
struct logs_ *logentry;
SET_SEGV_LOCATION();
hash_scan_begin (&hs, logs);
while ((hn = hash_scan_next (&hs)) != NULL) {
logentry = hnode_get (hn);
/* If file handle is vald we must have used the log */
if(logentry->logfile) {
if (logentry->flush > 0) {
fflush (logentry->logfile);
}
fclose (logentry->logfile);
logentry->logfile = NULL;
}
logentry->flush = 0;
#ifdef DEBUG
printf ("Closing Logfile %s (%s)\n", logentry->name, (char *) hnode_getkey (hn));
#endif
/* make new file name but do not open until needed to avoid 0 length files*/
make_log_filename(logentry->name, logentry->logname);
}
}
/* this is for printing out details during an assertion failure */
void
nassert_fail (const char *expr, const char *file, const int line, const char *infunk)
{
#ifdef HAVE_BACKTRACE
void *array[50];
size_t size;
char **strings;
size_t i;
/* thanks to gnulibc libary for letting me find this usefull function */
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
#endif
nlog (LOG_CRITICAL, LOG_CORE, "Assertion Failure!!!!!!!!!!!");
nlog (LOG_CRITICAL, LOG_CORE, "Function: %s (%s:%d)", infunk, file, line);
nlog (LOG_CRITICAL, LOG_CORE, "Expression: %s", expr);
#ifdef HAVE_BACKTRACE
for (i = 1; i < size; i++) {
nlog (LOG_CRITICAL, LOG_CORE, "BackTrace(%d): %s", i - 1, strings[i]);
}
#endif
nlog (LOG_CRITICAL, LOG_CORE, "Shutting Down!");
exit (EXIT_FAILURE);
}

105
log.h Normal file
View file

@ -0,0 +1,105 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#ifndef _log_h_
#define _log_h_
/*
* log.h
* dynamic configuration runtime libary
*/
/* define the log levels */
/* critcal crash type notices */
#define LOG_CRITICAL 1
/* something is majorly wrong */
#define LOG_ERROR 2
/* Hey, you should know about this type messages */
#define LOG_WARNING 3
/* did you know messages */
#define LOG_NOTICE 4
/* our normal logging level? */
#define LOG_NORMAL 5
/* lots of info about what we are doing */
#define LOG_INFO 6
/* debug notices about important functions that are going on */
#define LOG_DEBUG1 7
/* more debug notices that are usefull */
#define LOG_DEBUG2 8
/* even more stuff, that would be useless to most normal people */
#define LOG_DEBUG3 9
/* are you insane? */
#define LOG_DEBUG4 10
/* Scope of Logging Defines: */
#define LOG_CORE 0
#define LOG_MOD 1
/* buffer size for new customisable filenames
20 should be more than sufficient */
#define MAX_LOGFILENAME 20
/* this is for the neostats assert replacement. */
/* Version 2.4 and later of GCC define a magical variable _PRETTY_FUNCTION__'
which contains the name of the function currently being defined.
This is broken in G++ before version 2.6.
C9x has a similar variable called __func__, but prefer the GCC one since
it demangles C++ function names. */
#define __NASSERT_FUNCTION __PRETTY_FUNCTION__
/* Not all compilers provide __STRING so define it here if it is unknown */
#ifndef __STRING
#define __STRING(x) #x
#endif /* __STRING */
#ifndef __ASSERT_VOID_CAST
#define __ASSERT_VOID_CAST (void)
#endif
extern void nassert_fail (const char *expr, const char *file, const int line, const char *infunk);
#ifndef NDEBUG
#define nassert(expr) \
(__ASSERT_VOID_CAST ((expr) ? 0 : \
(nassert_fail(__STRING(expr), __FILE__, __LINE__, __NASSERT_FUNCTION), 0)))
#else
#define nassert(expr) (__ASSERT_VOID_CAST (0))
#endif
extern void nlog (int level, int scope, char *fmt, ...) __attribute__((format(printf,3,4))); /* 2=format 3=params */
void close_logs ();
int init_logs ();
void reset_logs ();
void fini_logs();
/* Configurable log filename format string */
extern char LogFileNameFormat[MAX_LOGFILENAME];
#if SQLSRV
void sqlsrvlog(char *logline);
#endif
#endif

496
main.c Normal file
View file

@ -0,0 +1,496 @@
/* MQServer
** Copyright (c) 2004 Justin Hammond
** http://www.dynam.ac/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** MQServer CVS Identification
** $Id$
*/
#include "config.h"
#include <setjmp.h>
#include <stdio.h>
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include "defines.h"
#include "signal.h"
#include "conf.h"
#include "log.h"
#include "sock.h"
#include "dotconf.h"
/*! Date when we were compiled */
const char version_date[] = __DATE__;
/*! Time we were compiled */
const char version_time[] = __TIME__;
char segv_inmodule[SEGV_INMODULE_BUFSIZE];
char segv_location[SEGV_LOCATION_BUFSIZE];
adns_state ads;
void start (void);
static void setup_signals (void);
static int get_options (int argc, char **argv);
static int init_dns ();
/*! have we forked */
int forked = 0;
static int attempts = 0;
jmp_buf sigvbuf;
/** @brief Main Entry point into program
*
* Sets up defaults, and parses the config file.
* Also initializes different parts of NeoStats
*
* @return Exits the program!
*
* @todo Close STDIN etc correctly
*/
int
main (int argc, char *argv[])
{
FILE *fp;
/* initialise version */
strncpy(me.version, MQSERVER_VERSION, VERSIONSIZE);
strncpy(me.versionfull, MQSERVER_VERSION, VERSIONSIZE);
/* get our commandline options */
if(get_options (argc, argv)!=NS_SUCCESS)
return EXIT_FAILURE;
#ifndef DEBUG
/* Change to the working Directory */
if (chdir (NEO_PREFIX) < 0) {
printf ("MQServer Could not change to %s\n", NEO_PREFIX);
printf ("Did you 'make install' after compiling?\n");
printf ("Error Was: %s\n", strerror (errno));
return EXIT_FAILURE;
}
#endif
/* before we do anything, make sure logging is setup */
if(init_logs () != NS_SUCCESS)
return EXIT_FAILURE;
/* our crash trace variables */
SET_SEGV_LOCATION();
CLEAR_SEGV_INMODULE();
/* keep quiet if we are told to :) */
if (!config.quiet) {
printf ("MQServer %s Loading...\n", me.versionfull);
printf ("-----------------------------------------------\n");
printf ("Copyright 2004\n");
printf ("Justin Hammond (justin@dynam.ac)\n");
printf ("-----------------------------------------------\n\n");
}
/* set some defaults before we parse the config file */
me.t_start = time(NULL);
me.now = time(NULL);
snprintf (me.strnow, STR_TIME_T_SIZE, "%ld", (long)me.now);
me.die = 0;
me.local[0] = '\0';
#ifndef DEBUG
me.debug_mode = 0;
#else
me.debug_mode = 1;
#endif
me.r_time = 10;
/* prepare to catch errors */
setup_signals ();
/* init libevent */
event_init();
/* load the config files */
if(ConfLoad () != NS_SUCCESS)
return EXIT_FAILURE;
if (me.die) {
printf ("\n-----> ERROR: Read the README file then edit %s! <-----\n\n",CONFIG_NAME);
nlog (LOG_CRITICAL, LOG_CORE, "Read the README file and edit your %s",CONFIG_NAME);
/* we are exiting the parent, not the program, so just return */
return EXIT_FAILURE;
}
/* initialize the rest of the subsystems */
if(init_dns () != NS_SUCCESS)
return EXIT_FAILURE;
if (init_client_list() != NS_SUCCESS)
return EXIT_FAILURE;
#ifndef DEBUG
/* if we are compiled with debug, or forground switch was specified, DONT FORK */
if (!config.foreground) {
/* fix the double log message problem by closing logs prior to fork() */
close_logs();
forked = fork ();
/* Error check fork() */
if (forked<0) {
perror("fork");
return EXIT_FAILURE; /* fork error */
}
#endif
/* we are the parent */
if (forked>0) {
/* write out our PID */
fp = fopen (PID_FILENAME, "w");
fprintf (fp, "%i", forked);
fclose (fp);
if (!config.quiet) {
printf ("\n");
printf ("MQServer %s Successfully Launched into Background\n", me.versionfull);
printf ("PID: %i - Wrote to %s\n", forked, PID_FILENAME);
}
return EXIT_SUCCESS; /* parent exits */
}
#ifndef DEBUG
/* child (daemon) continues */
/* reopen logs for child */
if(init_logs () != NS_SUCCESS)
return EXIT_FAILURE;
/* detach from parent process */
if (setpgid (0, 0) < 0) {
nlog (LOG_WARNING, LOG_CORE, "setpgid() failed");
}
}
#endif
nlog (LOG_NOTICE, LOG_CORE, "MQServer started (Version %s).", me.versionfull);
/* we are ready to start now Duh! */
start ();
/* We should never reach here but the compiler does not realise and may
complain about not all paths control returning values without the return
Since it should never happen, treat as an error condition! */
return EXIT_FAILURE;
}
/** @brief Process Commandline Options
*
* Processes commandline options
*
* returns 0 on success, -1 on error
*/
static int
get_options (int argc, char **argv)
{
int c;
int dbg;
SET_SEGV_LOCATION();
/* set some defaults first */
#ifdef DEBUG
config.debug = 10;
config.foreground = 1;
#else
config.debug = 5;
config.foreground = 0;
#endif
while ((c = getopt (argc, argv, "hvrd:nqf")) != -1) {
switch (c) {
case 'h':
printf ("MQServer: Usage: \"neostats [options]\"\n");
printf (" -h (Show this screen)\n");
printf (" -v (Show version number)\n");
printf (" -d 1-10 (Enable debugging output 1= lowest, 10 = highest)\n");
printf (" -q (Quiet start - for cron scripts)\n");
printf (" -f (Do not fork into background\n");
return NS_FAILURE;
case 'v':
printf ("MQServer Version %s\n", me.versionfull);
printf ("Compiled: %s at %s\n", version_date, version_time);
printf ("\nMQServer: http://www.neostats.net\n");
return NS_FAILURE;
case 'q':
config.quiet = 1;
break;
case 'd':
dbg = atoi (optarg);
if ((dbg > 10) || (dbg < 1)) {
printf ("Invalid Debug Level %d\n", dbg);
return NS_FAILURE;
}
config.debug = dbg;
break;
case 'f':
config.foreground = 1;
break;
default:
printf ("Unknown command line switch %c\n", optopt);
}
}
return NS_SUCCESS;
}
/** @brief Sigterm Signal handler
*
* Called by the signal handler if we get a SIGTERM
* This shutsdown MQServer and exits
*
* @return Exits the program!
*
* @todo Do a nice shutdown, no thtis crap :)
*/
char msg_sigterm[]="SIGTERM received, shutting down server.";
RETSIGTYPE
serv_die ()
{
#ifdef VALGRIND
exit(NS_SUCCESS);
#else /* VALGRIND */
nlog (LOG_CRITICAL, LOG_CORE, msg_sigterm);
do_exit (NS_EXIT_NORMAL, msg_sigterm);
#endif /* VALGRIND */
}
/** @brief Sighup Signal handler
*
* Called by the signal handler if we get a SIGHUP
* and rehashes the config file.
*
* @return Nothing
*
* @todo Implement a Rehash function. What can we actually rehash?
*/
RETSIGTYPE
conf_rehash ()
{
/* nothing yet */
}
/** @brief Sigsegv Signal handler
*
* This function is called when we get a SEGV
* and will send some debug into to the logs and to IRC
* to help us track down where the problem occured.
* if the platform we are using supports backtrace
* also print out the backtrace.
* if the segv happened inside a module, try to unload the module
* and continue on our merry way :)
*
* @return Nothing
*
*/
#ifndef HAVE_BACKTRACE
static char backtrace_unavailable[]="Backtrace not available on this platform";
#endif
static
void do_backtrace(void)
{
#ifdef HAVE_BACKTRACE
void *array[50];
size_t size;
char **strings;
int i;
nlog (LOG_CRITICAL, LOG_CORE, "Backtrace:");
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
for (i = 1; i < size; i++) {
nlog (LOG_CRITICAL, LOG_CORE, "BackTrace(%d): %s", i - 1, strings[i]);
}
free (strings);
#else
nlog (LOG_CRITICAL, LOG_CORE, backtrace_unavailable);
#endif
}
RETSIGTYPE
serv_segv ()
{
/** if the segv happened while we were inside a module, unload and try to restore
* the stack to where we were before we jumped into the module
* and continue on
*/
if (segv_inmodule[0] != 0) {
nlog (LOG_CRITICAL, LOG_CORE, "------------------------SEGFAULT REPORT-------------------------");
nlog (LOG_CRITICAL, LOG_CORE, "Please view the README for how to submit a bug report");
nlog (LOG_CRITICAL, LOG_CORE, "and include this segfault report in your submission.");
nlog (LOG_CRITICAL, LOG_CORE, "Module: %s", segv_inmodule);
nlog (LOG_CRITICAL, LOG_CORE, "Location: %s", segv_location);
nlog (LOG_CRITICAL, LOG_CORE, "Unloading Module and restoring stacks. Backtrace:");
do_backtrace();
nlog (LOG_CRITICAL, LOG_CORE, "-------------------------END OF REPORT--------------------------");
/* flush the logs out */
close_logs();
longjmp (sigvbuf, -1);
return;
}
/** The segv happened in our core, damn it */
/* Thanks to Stskeeps and Unreal for this stuff :) */
/* Broadcast it out! */
nlog (LOG_CRITICAL, LOG_CORE, "------------------------SEGFAULT REPORT-------------------------");
nlog (LOG_CRITICAL, LOG_CORE, "Please view the README for how to submit a bug report");
nlog (LOG_CRITICAL, LOG_CORE, "and include this segfault report in your submission.");
nlog (LOG_CRITICAL, LOG_CORE, "Location: %s", segv_location);
do_backtrace();
nlog (LOG_CRITICAL, LOG_CORE, "-------------------------END OF REPORT--------------------------");
close_logs();
/* clean up */
do_exit (NS_EXIT_SEGFAULT, NULL);
}
/** @brief Sets up the signal handlers
*
* Sets up the signal handlers for SIGHUP (rehash)
* SIGTERM (die) and SIGSEGV (segv fault)
* and ignore the others (Such as SIGPIPE)
*
* @return Nothing
*
*/
static void
setup_signals (void)
{
struct sigaction act;
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
SET_SEGV_LOCATION();
/* SIGPIPE/SIGALRM */
(void) sigemptyset (&act.sa_mask);
(void) sigaddset (&act.sa_mask, SIGPIPE);
(void) sigaddset (&act.sa_mask, SIGALRM);
(void) sigaction (SIGPIPE, &act, NULL);
(void) sigaction (SIGALRM, &act, NULL);
/* SIGHUP */
act.sa_handler = conf_rehash;
(void) sigemptyset (&act.sa_mask);
(void) sigaddset (&act.sa_mask, SIGHUP);
(void) sigaction (SIGHUP, &act, NULL);
/* SIGTERM/SIGINT */
act.sa_handler = serv_die;
(void) sigaddset (&act.sa_mask, SIGTERM);
(void) sigaction (SIGTERM, &act, NULL);
(void) sigaddset (&act.sa_mask, SIGINT);
(void) sigaction (SIGINT, &act, NULL);
/* SIGSEGV */
act.sa_handler = serv_segv;
(void) sigaddset (&act.sa_mask, SIGSEGV);
(void) sigaction (SIGSEGV, &act, NULL);
(void) signal (SIGHUP, conf_rehash);
(void) signal (SIGTERM, serv_die);
(void) signal (SIGSEGV, serv_segv);
(void) signal (SIGINT, serv_die);
}
/** @brief before exiting call this function. It flushes log files and tidy's up.
*
* Cleans up before exiting
* @parm segv 1 = we are exiting because of a segv fault, 0, we are not.
* if 1, we don't prompt to save data
*/
void
do_exit (NS_EXIT_TYPE exitcode, char* quitmsg)
{
/* Initialise exit code to OK */
int return_code=EXIT_SUCCESS;
SET_SEGV_LOCATION();
switch (exitcode) {
case NS_EXIT_NORMAL:
nlog (LOG_CRITICAL, LOG_CORE, "Normal shut down subsystems");
break;
case NS_EXIT_RELOAD:
nlog (LOG_CRITICAL, LOG_CORE, "Reloading MQServer");
break;
case NS_EXIT_RECONNECT:
nlog (LOG_CRITICAL, LOG_CORE, "Restarting MQServer subsystems");
break;
case NS_EXIT_ERROR:
nlog (LOG_CRITICAL, LOG_CORE, "Exiting due to error");
return_code=EXIT_FAILURE; /* exit code to error */
break;
case NS_EXIT_SEGFAULT:
nlog (LOG_CRITICAL, LOG_CORE, "Shutting down subsystems without saving data due to core");
return_code=EXIT_FAILURE; /* exit code to error */
break;
}
if (exitcode != NS_EXIT_SEGFAULT) {
if (exitcode == NS_EXIT_RECONNECT) {
if(me.r_time>0) {
nlog (LOG_NOTICE, LOG_CORE, "Reconnecting to the server in %d seconds (Attempt %i)", me.r_time, attempts);
sleep (me.r_time);
}
else {
nlog (LOG_NOTICE, LOG_CORE, "Reconnect time is zero, shutting down");
}
}
}
fini_logs();
#if 0
if ((exitcode == NS_EXIT_RECONNECT && me.r_time > 0) || exitcode == NS_EXIT_RELOAD) {
execve ("./mqserver", NULL, NULL);
return_code=EXIT_FAILURE; /* exit code to error */
}
#endif
remove (PID_FILENAME);
exit (return_code);
}
void fatal_error(char* file, int line, char* func, char* error_text)
{
SET_SEGV_LOCATION();
nlog (LOG_CRITICAL, LOG_CORE, "Fatal Error: %s %d %s %s", file, line, func, error_text);
do_exit (NS_EXIT_ERROR, "Fatal Error - check log file");
}
/** @brief sets up DNS subsystem
*
* configures ADNS for use with NeoStats.
*
* @return returns 1 on success, 0 on failure
*/
int
init_dns ()
{
int adnsstart;
SET_SEGV_LOCATION();
#ifndef DEBUG
adnsstart = adns_init (&ads, adns_if_noerrprint | adns_if_noautosys, 0);
#else
adnsstart = adns_init (&ads, adns_if_debug | adns_if_noautosys, 0);
#endif
if (adnsstart) {
printf ("ADNS init failed: %s\n", strerror (adnsstart));
nlog (LOG_CRITICAL, LOG_CORE, "ADNS init failed: %s", strerror (adnsstart));
return NS_FAILURE;
}
return NS_SUCCESS;
}

3
mqserver.cfg Normal file
View file

@ -0,0 +1,3 @@
SERVER_NAME "localhost"
SERVER_PORT 1234

286
sock.c Normal file
View file

@ -0,0 +1,286 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** Portions Copyright (c) 1999 Johnathan George net@lite.net
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "defines.h"
#include "adns.h"
#include "conf.h"
#include "log.h"
#include "dotconf.h"
#include "client.h"
static struct sockaddr_in lsa;
static int dobind;
int servsock;
static int listen_on_port(int port);
/** @brief Connect to a server
*
* also setups the SQL listen socket if defined
*
* @param host to connect to
* @param port on remote host to connect to
*
* @return socket connected to on success
* NS_FAILURE on failure
*/
int
ConnectTo (char *host, int port)
{
int ret;
struct hostent *hp;
struct sockaddr_in sa;
int s;
SET_SEGV_LOCATION();
dobind = 0;
/* bind to a local ip */
memset (&lsa, 0, sizeof (lsa));
if (me.local[0] != 0) {
if ((hp = gethostbyname (me.local)) == NULL) {
nlog (LOG_WARNING, LOG_CORE, "Warning, Couldn't bind to IP address %s", me.local);
} else {
memcpy ((char *) &lsa.sin_addr, hp->h_addr, hp->h_length);
lsa.sin_family = hp->h_addrtype;
dobind = 1;
}
}
if ((hp = gethostbyname (host)) == NULL) {
return NS_FAILURE;
}
if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
free(hp);
return NS_FAILURE;
}
if (dobind > 0) {
if (bind (s, (struct sockaddr *) &lsa, sizeof (lsa)) < 0) {
nlog (LOG_WARNING, LOG_CORE, "bind(): Warning, Couldn't bind to IP address %s", strerror (errno));
}
}
bzero (&sa, sizeof (sa));
sa.sin_family = AF_INET;
sa.sin_port = htons (port);
bcopy (hp->h_addr, (char *) &sa.sin_addr, hp->h_length);
ret=connect (s, (struct sockaddr *) &sa, sizeof (sa));
if (ret< 0) {
close (s);
return NS_FAILURE;
}
return s;
}
void client_activity(int fd, short eventtype, void *arg) {
char buf[BUFSIZE];
mqclient *mqc = arg;
int i;
SET_SEGV_LOCATION();
if (eventtype == EV_READ) {
bzero(buf, BUFSIZE);
i = read(fd, buf, BUFSIZE);
if (i < 1) {
/* error */
nlog(LOG_WARNING, LOG_CORE, "Read Error on %d: %s", fd, strerror(errno));
/* delete sock */
event_del(&mqc->ev);
del_client(mqc);
close(fd);
return;
} else {
printf("%s", buf);
fflush(NULL);
}
}
}
void listen_accept(int fd, short eventtype, void *arg) {
unsigned int al;
struct sockaddr_in client_address;
int l;
mqclient *mqc;
SET_SEGV_LOCATION();
memset (&client_address, 0, al = sizeof (client_address));
l = accept (fd, (struct sockaddr *)&client_address, &al);
if (l < 0) {
nlog(LOG_WARNING, LOG_CORE, "Accept Failed on %d: %s", fd, strerror(errno));
return;
}
mqc = new_client(l);
MQC_SET_STAT_CONNECT(mqc);
mqc->ip = client_address;
strncpy(mqc->host, inet_ntoa(client_address.sin_addr), MAXHOST);
nlog(LOG_DEBUG1, LOG_CORE, "New Connection from %s on fd %d", mqc->host, mqc->fd);
event_set(&mqc->ev, l, EV_READ|EV_PERSIST, client_activity, mqc);
event_add(&mqc->ev, NULL);
}
/** @brief main recv loop
*
* @param none
*
* @return none
*/
void
start ()
{
int servsock;
struct event ev;
SET_SEGV_LOCATION();
servsock = listen_on_port(me.port);
if (servsock < 0) {
do_exit(NS_EXIT_ERROR, "Can't Create Client Port");
return;
}
event_set(&ev, servsock, EV_READ|EV_PERSIST, listen_accept, NULL);
event_add(&ev, NULL);
nlog(LOG_NOTICE, LOG_CORE, "Listening for new connections on port %d: (%d)", me.port, servsock);
while (1) {
SET_SEGV_LOCATION();
event_loop(EVLOOP_ONCE);
if (me.die) {
do_exit(NS_EXIT_NORMAL, "Normal Exit");
}
}
do_exit(NS_EXIT_ERROR, "Exit from Loop");
}
/** @brief get max available sockets
*
* @param none
*
* @return returns the max available socket
*/
int
getmaxsock ()
{
struct rlimit *lim;
int ret;
SET_SEGV_LOCATION();
lim = malloc (sizeof (struct rlimit));
getrlimit (RLIMIT_NOFILE, lim);
ret = lim->rlim_max;
free (lim);
return ret;
}
/** @brief send to socket
*
* @param fmt printf style vaarg list of text to send
*
* @return none
*/
void
sts (const char *buf, const int buflen)
{
int sent;
SET_SEGV_LOCATION();
if (servsock == -1) {
nlog(LOG_WARNING, LOG_CORE, "Not sending to server as we have a invalid socket");
return;
}
sent = write (servsock, buf, buflen);
if (sent == -1) {
nlog (LOG_CRITICAL, LOG_CORE, "Write error: %s", strerror(errno));
do_exit (NS_EXIT_ERROR, NULL);
}
}
/***************************************************************
* listen_on_port(int port): - Open a socket to listen for
* incoming TCP connections on the port given. Return the file
* descriptor if OK, and -1 on any error. The calling routine
* can handle any error condition.
*
* Input: The interger value of the port number to bind to
* Output: The file descriptor of the socket
* Effects: none
***************************************************************/
int
listen_on_port(int port)
{
int srvfd; /* FD for our listen server socket */
struct sockaddr_in srvskt;
int adrlen;
int flags;
SET_SEGV_LOCATION();
adrlen = sizeof(struct sockaddr_in);
(void) memset((void *) &srvskt, 0, (size_t) adrlen);
srvskt.sin_family = AF_INET;
/* bind to the local IP */
if (dobind) {
srvskt.sin_addr = lsa.sin_addr;
} else {
srvskt.sin_addr.s_addr = INADDR_ANY;
}
srvskt.sin_port = htons(me.port);
if ((srvfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
nlog(LOG_CRITICAL,LOG_CORE, "SqlSrv: Unable to get socket for port %d.", port);
return -1;
}
flags = fcntl(srvfd, F_GETFL, 0);
flags |= O_NONBLOCK;
(void) fcntl(srvfd, F_SETFL, flags);
// setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, (char*) 1, sizeof(1));
if (bind(srvfd, (struct sockaddr *) &srvskt, adrlen) < 0)
{
nlog(LOG_CRITICAL, LOG_CORE, "Unable to bind to port %d", port);
return -1;
}
if (listen(srvfd, 1) < 0)
{
nlog(LOG_CRITICAL, LOG_CORE, "Unable to listen on port %d", port);
return -1;
}
return (srvfd);}

37
sock.h Normal file
View file

@ -0,0 +1,37 @@
/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
** Portions Copyright (c) 2000-2001 ^Enigma^
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
#ifndef SOCK_H
#define SOCK_H
int ConnectTo (char * host, int port);
void read_loop (void);
int getmaxsock (void);
void sts (const char *buf, const int buflen);
void start (void);
int check_sql_sock();
#endif

1
version.h Normal file
View file

@ -0,0 +1 @@