Initial Checking of MessageQueue Server
This commit is contained in:
parent
26b50de4c8
commit
452fffb460
88 changed files with 40535 additions and 0 deletions
87
.gitattributes
vendored
87
.gitattributes
vendored
|
@ -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
147
Makefile.in
Normal 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
4243
aclocal.m4
vendored
Normal file
File diff suppressed because it is too large
Load diff
43
adns/Makefile
Normal file
43
adns/Makefile
Normal 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
873
adns/adns.h
Normal 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
231
adns/check.c
Normal 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
106
adns/config.h
Normal 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
105
adns/config.h.in
Normal 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
75
adns/dlist.h
Normal 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
938
adns/event.c
Normal 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
445
adns/general.c
Normal 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
770
adns/internal.h
Normal 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
332
adns/parse.c
Normal 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
684
adns/query.c
Normal 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
497
adns/reply.c
Normal 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
825
adns/setup.c
Normal 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
330
adns/transmit.c
Normal 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
67
adns/tvarith.h
Normal 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
1281
adns/types.c
Normal file
File diff suppressed because it is too large
Load diff
101
client.c
Normal file
101
client.c
Normal 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
72
client.h
Normal 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
157
conf.c
Normal 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
54
conf.h
Normal 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
1371
config.guess
vendored
Executable file
File diff suppressed because it is too large
Load diff
140
config.h.in
Normal file
140
config.h.in
Normal 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
1362
config.sub
vendored
Executable file
File diff suppressed because it is too large
Load diff
150
configure.in
Normal file
150
configure.in
Normal 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
225
defines.h
Normal 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
|
||||
|
128
dict.h
Normal file
128
dict.h
Normal 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
453
dotconf.c
Normal 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
81
dotconf.h
Normal 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
898
hash.c
Normal 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
261
hash.h
Normal 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
251
install-sh
Executable 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
33
libevent/Makefile.am
Normal 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
523
libevent/Makefile.in
Normal 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
64
libevent/acconfig.h
Normal 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
190
libevent/aclocal.m4
vendored
Normal 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
220
libevent/buffer.c
Normal 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
46
libevent/compat/err.h
Normal 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
163
libevent/compat/sys/_time.h
Normal 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
488
libevent/compat/sys/queue.h
Normal 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
677
libevent/compat/sys/tree.h
Normal 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
191
libevent/config.h.in
Normal 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
4731
libevent/configure
vendored
Executable file
File diff suppressed because it is too large
Load diff
253
libevent/configure.in
Normal file
253
libevent/configure.in
Normal 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
335
libevent/epoll.c
Normal 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
52
libevent/epoll_sub.c
Normal 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
97
libevent/err.c
Normal 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
355
libevent/evbuffer.c
Normal 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
444
libevent/event.3
Normal 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
630
libevent/event.c
Normal 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
249
libevent/event.h
Normal 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
37
libevent/evsignal.h
Normal 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
238
libevent/install-sh
Executable 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
378
libevent/kqueue.c
Normal 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
198
libevent/missing
Executable 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
40
libevent/mkinstalldirs
Executable 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
245
libevent/poll.c
Normal 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
434
libevent/rtsig.c
Normal 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);
|
||||
}
|
13
libevent/sample/Makefile.am
Normal file
13
libevent/sample/Makefile.am
Normal 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
287
libevent/sample/Makefile.in
Normal 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:
|
135
libevent/sample/event-test.c
Normal file
135
libevent/sample/event-test.c
Normal 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);
|
||||
}
|
||||
|
57
libevent/sample/signal-test.c
Normal file
57
libevent/sample/signal-test.c
Normal 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);
|
||||
}
|
||||
|
63
libevent/sample/time-test.c
Normal file
63
libevent/sample/time-test.c
Normal 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
245
libevent/select.c
Normal 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
162
libevent/signal.c
Normal 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
1
libevent/stamp-h.in
Normal file
|
@ -0,0 +1 @@
|
|||
timestamp
|
23
libevent/test/Makefile.am
Normal file
23
libevent/test/Makefile.am
Normal 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
324
libevent/test/Makefile.in
Normal 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
181
libevent/test/bench.c
Normal 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
518
libevent/test/regress.c
Normal 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
68
libevent/test/test-eof.c
Normal 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
27
libevent/test/test-init.c
Normal 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
68
libevent/test/test-time.c
Normal 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
68
libevent/test/test-weof.c
Normal 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
77
libevent/test/test.sh
Executable 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
848
list.c
Normal 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
179
list.h
Normal 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
269
log.c
Normal 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
105
log.h
Normal 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
496
main.c
Normal 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
3
mqserver.cfg
Normal file
|
@ -0,0 +1,3 @@
|
|||
SERVER_NAME "localhost"
|
||||
SERVER_PORT 1234
|
||||
|
286
sock.c
Normal file
286
sock.c
Normal 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
37
sock.h
Normal 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
1
version.h
Normal file
|
@ -0,0 +1 @@
|
|||
|
Reference in a new issue