From: Emil Mikulic Date: Sun, 18 Aug 2013 06:32:41 +0000 (+1000) Subject: Imported Upstream version 3.0.715 X-Git-Url: https://unix4lyfe.org/gitweb/darkstat-debian/commitdiff_plain/a1e8056c92203d02860d719abb1d562453896da8?ds=sidebyside Imported Upstream version 3.0.715 --- a1e8056c92203d02860d719abb1d562453896da8 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..06862ba --- /dev/null +++ b/AUTHORS @@ -0,0 +1,48 @@ +AUTHORS +------- + +- Emil Mikulic + Primary maintainer. + + (please read the manpage before sending me an e-mail about how your + graphs are all blank) + +Big thanks to everyone who helped out, in no particular order: + +- Ben Stewart + Web interface design for v3, import/export code and file format design. + +- Chris Kuethe + Security, cool patches, OpenBSD port maintainer. + +- Bartosz Kuzma + DLT_PPP and DLT_PPP_SERIAL decoding, pkgsrc maintainer. + +- Claudio Leite - DLT_PPP_ETHER decoding. + +- Can Erkin Acar - BIOCSETWF patch. + +- Ingo Bressler - DLT_LINUX_SLL decoding. + +- Dennis Jansen + Motivation for keeping memory use down, cool patches. + +- Anton S. Ustyuzhanin - DLT_RAW decoding. + +- Cristian Rodriguez - SUSE package maintainer. + +- Rene Mayorga - Debian package maintainer. + +- Cedric Delfosse - Debian package maintainer (retired). + +- Damian Lozinski - initial implementation of average KB/s on graphs. + +- Damien Clauzel - launchd config and Mac OS X instructions. + +- Mats Erik Andersson - for doing the IPv6 heavy lifting. + +Cyro W. Corte Real Filho, Jean-Edouard Babin, Leif Terrens, Moritz Grimm, +Andreas Reimann, Colin Phipps, Cheng-Lung Sung, Martin Wilke, Piotr Kalina, +Carlo Florendo, Malte S. Stretz, Dirk Koopman, and others. + +My apologies if I missed anyone - please mail any corrections to me (Emil) diff --git a/COPYING.GPL b/COPYING.GPL new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING.GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..f14816c --- /dev/null +++ b/ChangeLog @@ -0,0 +1,168 @@ +v3.0.715 (January 2012) + - Compatibility fixes for Hurd and Solaris. + - Use link-time optimization and automake-like silent rules. + - Support systems without ifaddrs.h again. + - Continuing fixes for IPv6 support. + - Only update lastseen time for sender, not recipient. + - Implement --local-only: accounting for hosts on the local net. + - Make failure to bind() a socket non-fatal. + - Make failure to get local IP non-fatal. + - Fall back to gethostbyaddr() if getnameinfo() fails. + - Fix detection of IPv4 multicast addresses. + - Fix decoding on OpenBSD DLT_NULL interfaces (e.g. gif(4)) + +v3.0.714 (June 2011) + - IPv6 support! Big ups to Mats Erik Andersson who did most + of this work! + - Allow sort on last-seen, thanks to Dirk Koopman. + - Support multiple bind addresses. + - Add --disable-debug configure flag, thanks to Malte S. Stretz. + - Make it possible to save the DB without resetting it (SIGUSR2). + - Web: Use relative URLs, so darkstat works properly behind mod_proxy, + thanks to Malte S. Stretz. + +v3.0.713 (March 2010) + - Don't require --verbose for pcap_stats. + - Survive interface going down on Linux. + - Support DLT_RAW, implemented by Anton S. Ustyuzhanin. + - Skip accounting for hosts or ports if their max is set to zero. + - Implement --hexdump for troubleshooting. + - Web: Implement --no-lastseen + - Implement --snaplen manual override. + - Fix snaplen problem on recent (1-2 years?) Linux kernels. + - Implement --syslog + - Implement --wait as a NetworkManager workaround. + +(there were no releases made in 2009) + +v3.0.712 (November 2008) + - Web: Add --no-macs option to hide mac addresses. + Thanks Dennis! + - Web: Make tables prettier. + - Host detail view now triggers a DNS lookup. + - Manpage tweaks, also move from section 1 to section 8. + - Track and show how long ago a host was last seen. + Suggested by: Prof A Olowofoyeku (The African Chief) + - Show pcap_stats (like number of packets dropped) in the web + interface and also upon exit. + +v3.0.711 (August 2008) + - Split --debug into --verbose and --no-daemon + - Include launchd config and instructions for running darkstat + on Mac OS X. Contributed by Damien Clauzel. + - Implement PPPoE decoding on ethernet iface. (--pppoe) + - Web: Add automatic reload button. Thanks Dennis! + - Web: Add a graph legend with min/avg/max. + - Web: Remove hashtable stats pages. + +v3.0.708 (May 2008) + + - Implement limiting of number of ports tracked per host, + configurable on the commandline (--ports-max) + - Optionally don't track high ports (--highest-port) + Thanks Dennis! + - Fix rare use-after-free resulting from hosts table reduction. + - Make hosts limit configurable (--hosts-max) + - Option to read from capfile as alternative to live capture + (really only useful for development, benchmarking) + - Add the sniffed interface name to HTML reports. + Thanks Chris! + +v3.0.707 (Sep 2007) + + - Fix silly bug in formatting hex. + - Check for pcap.h in include/pcap/ for old RedHat-a-likes. + - New commandline parser. + - To stay in foreground, pass --debug instead of -d. + - We can now reset all statistics at runtime (send SIGUSR1) + - Make chroot dir configurable on cmdline (--chroot) + - Make privdrop user configurable on cmdline (--user) + - Implement daylog (brings back a v2 feature) + - Import and export hosts and graphs, this brings back a fairly + major v2 feature. Big ups to Ben for doing a lot of the + design and implementation of this feature! + Note that the v3 database format is, by design, incompatible + with the v2 format. + - Report average KB/s in and out on graphs. + Thanks to Damian Lozinski for suggestion and first cut at the + implementation. + - Fix graph rotation when the delay between rotations is big + enough to clear an entire graph. + - Make ip_proto 8 bits wide, to match the IP header. + - Implement pidfile functionality for people who prefer to + handle daemons in this manner. + +v3.0.619 (Apr 2007) + + - Decode DLT_PPP and DLT_PPP_SERIAL on NetBSD, + patch courtesy of Bartosz Kuzma. + - Don't use pcap_setnonblock(), with help from Colin Phipps. + - Reduce the number of syscalls made. + - Answer FAQ about graph axes / labels / scale. + - Fix build on OpenBSD (thanks Chris!) and Solaris. + - Commandline arg (-n) to disable promiscuous mode when + sniffing, thanks to Chris Kuethe for the implementation. + - Commandline arg (-r) to disable DNS resolver. + - Track and report per-host last seen MAC address. + - Move FAQ into manpage. + - Implement display of start time and running time. + - Web: implement sorting the hosts table by in/out/total. + - Web: implement paging through the hosts table. + - Web: implement full view of hosts table. + - Don't die if the capture interface loses its IP address. + - Make daemonize (previously -d) the default, and make -D the + argument to suppress it. + - Commandline arg (-l) to graph traffic entering/leaving the + local network as opposed to just the local IP. v2 had this. + - Allow configure-time override of CHROOT_DIR and PRIVDROP_USER. + - Web: new color scheme. + +v3.0.540 (Aug 2006) + + - Fix build against old libpcap (thanks Claudio) + - Fix build on AIX (thanks Andreas) + - Fix build warnings on NetBSD (thanks Bartosz) + - Deny writes to BPF socket (thanks Can) + - Reverse-resolve IPs less aggressively. + - Free up the DNS queue as we process it. + - Fix dns_reply silliness. + - Web: tweak the look of the top bar. + - Web: update total packets and bytes as part of graph update. + - Decode DLT_LINUX_SLL (ippp0 on Linux), + patch courtesy of Ingo Bressler + +v3.0.524 (Jul 2006) + + - Fix build on NetBSD. + - Fix shutdown on Linux. + - Performance improvements. + - Free the mallocs. + - Work around BPF being immediate on Linux. + This improves performance. + - Drop privileges when we don't need them. Chroot. Generally + be more paranoid. Thanks to Chris Kuethe for patches and + inspiration. + - Daemonize. (run in the background) + - Graphs: Make the entire bar have the same label (instead of + different labels for in/out), add thousands separators for + legibility, include the position/index (i.e. day 22) + - Instead of reducing the hosts_db based on time, do it based on + its size. + - Implement somewhat better handling of time moving backwards - + we assume that real time is monotonic and just renumber the + graph bars. (time is hard) + - Greatly improve IPC with the DNS child, make it more efficient + and much more robust. + - Decode DLT_PPP_ETHER (pppoe0 on OpenBSD), patch courtesy of + Claudio Leite. + +v3.0.471 (Jun 2006) + + First public release of darkstat 3. Almost a complete rewrite + since v2.6. Architecture much improved, better portability and + stability. Approximate feature parity with v2, missing + loading/saving DB. + +v2.6 (Nov 2003) + + End of the line for darkstat 2 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..fb269e6 --- /dev/null +++ b/INSTALL @@ -0,0 +1,47 @@ +Installation instructions +------------------------- + +$ ./configure +$ make +$ make install + + +Quickstart +---------- + +$ darkstat -i eth0 + + +Slightly slower start +--------------------- + +$ man darkstat + + +Packaging +--------- + +The install target respects DESTDIR. If you are packaging darkstat or +installing into a chroot, you can: + +$ make install DESTDIR=/chroot/whatever + + +Portability +----------- + +Builds out of the box on: + + - FreeBSD 7.0 + +Reported to also work on: + + - Solaris (with Sun C 5.8, and libpcap installed) + - Fedora Core (with libpcap-devel installed) + - OpenBSD + - NetBSD + - Mac OS X + - AIX + - Ubuntu (you need build-essential, zlib1g-dev, libpcap-dev) + - Mandrake + - OpenSUSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f7d77fc --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Parts of the darkstat source code are covered by a BSD license (actually +closer to the ISC license template favored by the OpenBSD project). +These are usually the more generalized, reusable parts of the code. + +Other parts of the darkstat code are covered by the GPL. These other +parts are usually not generic code, but are specific to darkstat and its +purpose. + +All of the source code is clearly annotated to show which license covers +which parts. + +Due to the viral nature of the GPL, once linked, the entire darkstat +binary is infected with the GPL. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..27e971d --- /dev/null +++ b/Makefile.in @@ -0,0 +1,151 @@ +# darkstat 3 +# copyright (c) 2001-2011 Emil Mikulic. +# +# You may use, modify and redistribute this file under the terms of the +# GNU General Public License version 2. (see COPYING.GPL) + +CC = @CC@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +INSTALL = @INSTALL@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +HOSTCC ?= $(CC) +HOSTCFLAGS ?= $(CFLAGS) + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +sbindir = @sbindir@ +datarootdir = @datarootdir@ +mandir = @mandir@ + +# Optimizations FIXME: dead code. push into autoconf? +#CPPFLAGS += -D__OPTIMIZE__ + +SRCS = \ +acct.c \ +addr.c \ +bsd.c \ +cap.c \ +conv.c \ +darkstat.c \ +daylog.c \ +db.c \ +decode.c \ +dns.c \ +err.c \ +graph_db.c \ +hosts_db.c \ +hosts_sort.c \ +html.c \ +http.c \ +localip.c \ +ncache.c \ +pidfile.c \ +str.c + +OBJS = $(SRCS:%.c=%.o) + +STATICHS = \ +stylecss.h \ +graphjs.h + +all: darkstat + +darkstat: $(OBJS) + $(AM_V_LINK) + $(AM_V_at)$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LIBS) -o $@ + +.c.o: + $(AM_V_CC) + $(AM_V_at)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +clean: + rm -f darkstat + rm -f $(OBJS) + rm -f $(STATICHS) + rm -f c-ify + +depend: config.status $(STATICHS) + cp Makefile.in Makefile.in.old + sed '/^# Automatically generated dependencies$$/,$$d' \ + Makefile.in + echo "# Automatically generated dependencies" >>Makefile.in + $(CPP) $(CPPFLAGS) -MM $(SRCS) >>Makefile.in + ./config.status + rm -f Makefile.in.old + +show-dep: + @echo $(CPP) $(CPPFLAGS) -MM $(SRCS) + +graphjs.h: static/graph.js + $(AM_V_CIFY) + $(AM_V_at)./c-ify graph_js $@ + +stylecss.h: static/style.css + $(AM_V_CIFY) + $(AM_V_at)./c-ify style_css $@ + +$(STATICHS): c-ify +c-ify: static/c-ify.c + $(AM_V_HOSTCC) + $(AM_V_at)$(HOSTCC) $(HOSTCFLAGS) static/c-ify.c -o $@ + +install: darkstat + $(INSTALL) -d $(DESTDIR)$(sbindir) + $(INSTALL) -m 555 darkstat $(DESTDIR)$(sbindir) + $(INSTALL) -d $(DESTDIR)$(mandir)/man8 + $(INSTALL) -m 444 darkstat.8 $(DESTDIR)$(mandir)/man8 + +.PHONY: all install clean depend show-dep + +# silent-rules +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_LINK = $(am__v_LINK_$(V)) +am__v_LINK_ = $(am__v_LINK_$(AM_DEFAULT_VERBOSITY)) +am__v_LINK_0 = @echo " LINK " $@; +AM_V_HOSTCC = $(am__v_HOSTCC_$(V)) +am__v_HOSTCC_ = $(am__v_HOSTCC_$(AM_DEFAULT_VERBOSITY)) +am__v_HOSTCC_0 = @echo " HOSTCC" $@; +AM_V_CIFY = $(am__v_CIFY_$(V)) +am__v_CIFY_ = $(am__v_CIFY_$(AM_DEFAULT_VERBOSITY)) +am__v_CIFY_0 = @echo " C-IFY " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ + +# Automatically generated dependencies +acct.o: acct.c acct.h decode.h addr.h conv.h daylog.h graph_db.h err.h \ + cdefs.h hosts_db.h localip.h now.h opt.h +addr.o: addr.c addr.h +bsd.o: bsd.c bsd.h config.h cdefs.h +cap.o: cap.c cdefs.h cap.h config.h conv.h decode.h addr.h hosts_db.h \ + localip.h opt.h err.h +conv.o: conv.c conv.h err.h cdefs.h +darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \ + graph_db.h db.h dns.h err.h http.h hosts_db.h addr.h localip.h ncache.h \ + pidfile.h now.h +daylog.o: daylog.c err.h cdefs.h daylog.h graph_db.h str.h now.h +db.o: db.c cdefs.h err.h hosts_db.h addr.h graph_db.h db.h +decode.o: decode.c cdefs.h acct.h cap.h config.h decode.h addr.h err.h \ + opt.h +dns.o: dns.c cdefs.h conv.h decode.h addr.h dns.h err.h hosts_db.h \ + queue.h str.h tree.h bsd.h config.h +err.o: err.c cdefs.h err.h opt.h pidfile.h bsd.h config.h +graph_db.o: graph_db.c cap.h conv.h db.h acct.h err.h cdefs.h str.h \ + html.h graph_db.h now.h opt.h +hosts_db.o: hosts_db.c cdefs.h conv.h decode.h addr.h dns.h err.h \ + hosts_db.h db.h html.h ncache.h now.h opt.h str.h +hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h +html.o: html.c config.h str.h html.h opt.h +http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \ + http.h now.h queue.h str.h stylecss.h graphjs.h +localip.o: localip.c addr.h config.h err.h cdefs.h localip.h bsd.h +ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h +pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h +str.o: str.c conv.h err.h cdefs.h str.h diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..0ec2855 --- /dev/null +++ b/NEWS @@ -0,0 +1,19 @@ +darkstat v3.0.540 and earlier defaulted to running in the foreground +and had a "-d" commandline argument to get darkstat to daemonize, +detach from the controlling terminal, and run in the background. + +Since r540, the default has been inverted. darkstat will daemonize by +default. + +Since r626, this behaviour can be suppressed with the "--debug" +commandline argument, to force darkstat to stay in the foreground for +debugging purposes. + +The "-d" argument is no longer recognized, and will prevent darkstat +from starting, so make sure you adjust any startup scripts you may have. + +After 3.0.708, --debug was split into --verbose and --no-daemon. + +Since v3.0.694, darkstat is able to save its internal database into a +file and reload it on startup. The file format is, by design, +incompatible with the format from darkstat v2. diff --git a/README b/README new file mode 100644 index 0000000..6413832 --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +darkstat is a network statistics gatherer. + +It sniffs packets on a specified interface, accumulates statistics, and +serves them up over HTTP. + +See the "AUTHORS" file for credits, and who to e-mail when things break. +See the "LICENSE" file for an explanation of the source code licensing. +See the "INSTALL" file for installation instructions. +See the "darkstat.8" manual page for usage instructions. + +If your system doesn't have enough copies of the full text of the GNU +General Public License already, we have provided another one in the +"COPYING.GPL" file. diff --git a/acct.c b/acct.c new file mode 100644 index 0000000..62ad076 --- /dev/null +++ b/acct.c @@ -0,0 +1,290 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * acct.c: traffic accounting + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "acct.h" +#include "decode.h" +#include "conv.h" +#include "daylog.h" +#include "err.h" +#include "hosts_db.h" +#include "localip.h" +#include "now.h" +#include "opt.h" + +#define __FAVOR_BSD +#include +#include +#include +#include /* for isdigit */ +#include /* for gai_strerror */ +#include /* for free */ +#include /* for memcpy */ + +uint64_t acct_total_packets = 0, acct_total_bytes = 0; + +static int using_localnet4 = 0, using_localnet6 = 0; +static struct addr localnet4, localmask4, localnet6, localmask6; + +/* Parse the net/mask specification into two IPs or die trying. */ +void +acct_init_localnet(const char *spec) +{ + char **tokens; + unsigned int num_tokens; + int isnum, j, ret; + int pfxlen, octets, remainder; + struct addr localnet, localmask; + + tokens = split('/', spec, &num_tokens); + if (num_tokens != 2) + errx(1, "expecting network/netmask, got \"%s\"", spec); + + if ((ret = str_to_addr(tokens[0], &localnet)) != 0) + errx(1, "couldn't parse \"%s\": %s", tokens[0], gai_strerror(ret)); + + /* Detect a purely numeric argument. */ + isnum = 0; + { + const char *p = tokens[1]; + while (*p != '\0') { + if (isdigit(*p)) { + isnum = 1; + ++p; + continue; + } else { + isnum = 0; + break; + } + } + } + + if (!isnum) { + if ((ret = str_to_addr(tokens[1], &localmask)) != 0) + errx(1, "couldn't parse \"%s\": %s", tokens[1], gai_strerror(ret)); + if (localmask.family != localnet.family) + errx(1, "family mismatch between net and mask"); + } else { + uint8_t frac, *p; + char *endptr; + + localmask.family = localnet.family; + + /* Compute the prefix length. */ + pfxlen = (unsigned int)strtol(tokens[1], &endptr, 10); + + if ((pfxlen < 0) || + ((localnet.family == IPv6) && (pfxlen > 128)) || + ((localnet.family == IPv4) && (pfxlen > 32)) || + (tokens[1][0] == '\0') || + (*endptr != '\0')) + errx(1, "invalid network prefix length \"%s\"", tokens[1]); + + /* Construct the network mask. */ + octets = pfxlen / 8; + remainder = pfxlen % 8; + p = (localnet.family == IPv6) ? (localmask.ip.v6.s6_addr) + : ((uint8_t *) &(localmask.ip.v4)); + + if (localnet.family == IPv6) + memset(p, 0, 16); + else + memset(p, 0, 4); + + for (j = 0; j < octets; ++j) + p[j] = 0xff; + + frac = (uint8_t)(0xff << (8 - remainder)); + if (frac) + p[j] = frac; /* Have contribution for next position. */ + } + + free(tokens[0]); + free(tokens[1]); + free(tokens); + + /* Register the correct netmask and calculate the correct net. */ + addr_mask(&localnet, &localmask); + if (localnet.family == IPv6) { + using_localnet6 = 1; + localnet6 = localnet; + localmask6 = localmask; + } else { + using_localnet4 = 1; + localnet4 = localnet; + localmask4 = localmask; + } + + verbosef("local network address: %s", addr_to_str(&localnet)); + verbosef(" local network mask: %s", addr_to_str(&localmask)); +} + +static int +addr_is_local(const struct addr * const a) +{ + if (a->family == IPv4) { + if (using_localnet4) { + if (addr_inside(a, &localnet4, &localmask4)) + return 1; + } else { + if (addr_equal(a, &localip4)) + return 1; + } + } else { + assert(a->family == IPv6); + if (using_localnet6) { + if (addr_inside(a, &localnet6, &localmask6)) + return 1; + } else { + if (addr_equal(a, &localip6)) + return 1; + } + } + return 0; +} + +/* Account for the given packet summary. */ +void +acct_for(const struct pktsummary * const sm) +{ + struct bucket *hs = NULL, *hd = NULL; + struct bucket *ps, *pd; + int dir_in, dir_out; + +#if 0 /* WANT_CHATTY? */ + printf("%15s > ", addr_to_str(&sm->src)); + printf("%15s ", addr_to_str(&sm->dst)); + printf("len %4d proto %2d", sm->len, sm->proto); + + if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP) + printf(" port %5d : %5d", sm->src_port, sm->dst_port); + if (sm->proto == IPPROTO_TCP) + printf(" %s%s%s%s%s%s", + (sm->tcp_flags & TH_FIN)?"F":"", + (sm->tcp_flags & TH_SYN)?"S":"", + (sm->tcp_flags & TH_RST)?"R":"", + (sm->tcp_flags & TH_PUSH)?"P":"", + (sm->tcp_flags & TH_ACK)?"A":"", + (sm->tcp_flags & TH_URG)?"U":"" + ); + printf("\n"); +#endif + + /* Totals. */ + acct_total_packets++; + acct_total_bytes += sm->len; + + /* Graphs. */ + dir_out = addr_is_local(&(sm->src)); + dir_in = addr_is_local(&(sm->dst)); + + /* Traffic staying within the network isn't counted. */ + if (dir_in == 1 && dir_out == 1) + dir_in = dir_out = 0; + + if (dir_out) { + daylog_acct((uint64_t)sm->len, GRAPH_OUT); + graph_acct((uint64_t)sm->len, GRAPH_OUT); + } + if (dir_in) { + daylog_acct((uint64_t)sm->len, GRAPH_IN); + graph_acct((uint64_t)sm->len, GRAPH_IN); + } + + if (opt_hosts_max == 0) return; /* skip per-host accounting */ + + /* Hosts. */ + hosts_db_reduce(); + if (!opt_want_local_only || addr_is_local(&sm->src)) { + hs = host_get(&(sm->src)); + hs->out += sm->len; + hs->total += sm->len; + memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac)); + hs->u.host.lastseen = now; + } + + if (!opt_want_local_only || addr_is_local(&sm->dst)) { + hd = host_get(&(sm->dst)); + hd->in += sm->len; + hd->total += sm->len; + memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac)); + /* + * Don't update recipient's last seen time, we don't know that + * they received successfully. + */ + } + + /* Protocols. */ + if (sm->proto != IPPROTO_INVALID) { + if (hs) { + ps = host_get_ip_proto(hs, sm->proto); + ps->out += sm->len; + ps->total += sm->len; + } + if (hd) { + pd = host_get_ip_proto(hd, sm->proto); + pd->in += sm->len; + pd->total += sm->len; + } + } + + if (opt_ports_max == 0) return; /* skip ports accounting */ + + /* Ports. */ + switch (sm->proto) { + case IPPROTO_TCP: + if ((sm->src_port <= opt_highest_port) && hs) { + ps = host_get_port_tcp(hs, sm->src_port); + ps->out += sm->len; + ps->total += sm->len; + } + if ((sm->dst_port <= opt_highest_port) && hd) { + pd = host_get_port_tcp(hd, sm->dst_port); + pd->in += sm->len; + pd->total += sm->len; + if (sm->tcp_flags == TH_SYN) + pd->u.port_tcp.syn++; + } + break; + + case IPPROTO_UDP: + if ((sm->src_port <= opt_highest_port) && hs) { + ps = host_get_port_udp(hs, sm->src_port); + ps->out += sm->len; + ps->total += sm->len; + } + if ((sm->dst_port <= opt_highest_port) && hd) { + pd = host_get_port_udp(hd, sm->dst_port); + pd->in += sm->len; + pd->total += sm->len; + } + break; + + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_OSPF: + /* known protocol, don't complain about it */ + break; + + default: + verbosef("unknown IP proto (%04x)", sm->proto); + } +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/acct.h b/acct.h new file mode 100644 index 0000000..3c2828b --- /dev/null +++ b/acct.h @@ -0,0 +1,16 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * acct.h: traffic accounting + */ + +#include + +struct pktsummary; + +extern uint64_t acct_total_packets, acct_total_bytes; + +void acct_init_localnet(const char *spec); +void acct_for(const struct pktsummary * const sm); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/addr.c b/addr.c new file mode 100644 index 0000000..721d5e3 --- /dev/null +++ b/addr.c @@ -0,0 +1,98 @@ +/* darkstat 3 + * copyright (c) 2011 Emil Mikulic. + * + * addr.c: compound IPv4/IPv6 address + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "addr.h" + +#include /* for inet_ntop */ +#include +#include /* for memcmp */ +#include /* for getaddrinfo */ + +int addr_equal(const struct addr * const a, const struct addr * const b) +{ + if (a->family != b->family) + return 0; + if (a->family == IPv4) + return (a->ip.v4 == b->ip.v4); + else { + assert(a->family == IPv6); + return (memcmp(&(a->ip.v6), &(b->ip.v6), sizeof(a->ip.v6)) == 0); + } +} + +static char _addrstrbuf[INET6_ADDRSTRLEN]; +const char *addr_to_str(const struct addr * const a) +{ + if (a->family == IPv4) { + struct in_addr in; + in.s_addr = a->ip.v4; + return (inet_ntoa(in)); + } else { + assert(a->family == IPv6); + inet_ntop(AF_INET6, &(a->ip.v6), _addrstrbuf, sizeof(_addrstrbuf)); + return (_addrstrbuf); + } +} + +int str_to_addr(const char *s, struct addr *a) +{ + struct addrinfo hints, *ai; + int ret; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + + if ((ret = getaddrinfo(s, NULL, &hints, &ai)) != 0) + return (ret); + + if (ai->ai_family == AF_INET) { + a->family = IPv4; + a->ip.v4 = ((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr; + } else if (ai->ai_family == AF_INET6) { + a->family = IPv6; + memcpy(&(a->ip.v6), + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, + sizeof(a->ip.v6)); + } else { + ret = EAI_FAMILY; + } + + freeaddrinfo(ai); + return (ret); +} + +void addr_mask(struct addr *a, const struct addr * const mask) +{ + assert(a->family == mask->family); + if (a->family == IPv4) + a->ip.v4 &= mask->ip.v4; + else { + size_t i; + + assert(a->family == IPv6); + for (i=0; iip.v6.s6_addr); i++) + a->ip.v6.s6_addr[i] &= mask->ip.v6.s6_addr[i]; + } +} + +int addr_inside(const struct addr * const a, + const struct addr * const net, const struct addr * const mask) +{ + struct addr masked; + + assert(a->family == net->family); + assert(a->family == mask->family); + + masked = *a; + addr_mask(&masked, mask); + return (addr_equal(&masked, net)); +} + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/addr.h b/addr.h new file mode 100644 index 0000000..ae7eaa1 --- /dev/null +++ b/addr.h @@ -0,0 +1,35 @@ +/* darkstat 3 + * copyright (c) 2011 Emil Mikulic. + * + * addr.h: compound IPv4/IPv6 address + * (because struct sockaddr_storage stores too much) + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ +#ifndef __DARKSTAT_ADDR_H +#define __DARKSTAT_ADDR_H + +#include /* for in_addr_t, at least on OpenBSD */ +#include /* for AF_INET6 */ +#include /* for in6_addr */ + +struct addr { + union { + in_addr_t v4; + struct in6_addr v6; + } ip; + enum { IPv4 = 4, IPv6 = 6 } family; +}; + +int addr_equal(const struct addr * const a, const struct addr * const b); +const char *addr_to_str(const struct addr * const a); +void addr_mask(struct addr *a, const struct addr * const mask); +int addr_inside(const struct addr * const a, + const struct addr * const net, const struct addr * const mask); + +/* Returns 0 on success, gai_strerror() code otherwise. */ +int str_to_addr(const char *s, struct addr *a); + +#endif +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/bsd.c b/bsd.c new file mode 100644 index 0000000..9a9f156 --- /dev/null +++ b/bsd.c @@ -0,0 +1,131 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * bsd.c: *BSD compatibility. + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bsd.h" +#include "cdefs.h" +#include "config.h" + +#include /* for strlen */ + +/* strlcpy() and strlcat() are derived from: + * + * $OpenBSD: strlcpy.c,v 1.4 + * $FreeBSD: src/lib/libc/string/strlcpy.c,v 1.8 + * + * $OpenBSD: strlcat.c,v 1.2 + * $FreeBSD: src/lib/libc/string/strlcat.c,v 1.10 + * + * under the following license: + * + * Copyright (c) 1998 Todd C. Miller + * 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. + */ + +#ifndef HAVE_STRLCPY +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char * restrict dst, const char * restrict src, const size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (size_t)(s - src - 1); /* count does not include NUL */ +} +#endif + +#ifndef HAVE_STRLCAT +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char * restrict dst, const char * restrict src, const size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = (size_t)(d - dst); + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return (dlen + (size_t)(s - src)); /* count does not include NUL */ +} +#endif + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/bsd.h b/bsd.h new file mode 100644 index 0000000..3426add --- /dev/null +++ b/bsd.h @@ -0,0 +1,28 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * bsd.h: *BSD compatibility. + */ + +#include +#include "config.h" +#ifdef HAVE_BSD_STRING_H +# include +#endif +#ifdef HAVE_BSD_UNISTD_H +# include +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_STRLCAT +size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_SETPROCTITLE +#define setproctitle(fmt, ...) /* no-op */ +#endif + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/cap.c b/cap.c new file mode 100644 index 0000000..d07bf9c --- /dev/null +++ b/cap.c @@ -0,0 +1,413 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * cap.c: interface to libpcap. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "cdefs.h" +#include "cap.h" +#include "config.h" +#include "conv.h" +#include "decode.h" +#include "hosts_db.h" +#include "localip.h" +#include "opt.h" + +#include +#include +#include +#include +#ifdef HAVE_SYS_FILIO_H +# include /* Solaris' FIONBIO hides here */ +#endif +#include +#include "err.h" +#include +#include +#include +#include + +/* The cap process life-cycle: + * + * Init - cap_init() + * Fill fd_set - cap_fd_set() + * Poll - cap_poll() + * Stop - cap_stop() + */ + +/* Globals - only useful within this module. */ +static pcap_t *pcap = NULL; +static int pcap_fd = -1; +static const struct linkhdr *linkhdr = NULL; + +#define CAP_TIMEOUT 500 /* granularity of capture buffer, in milliseconds */ + +/* --------------------------------------------------------------------------- + * Init pcap. Exits on failure. + */ +void +cap_init(const char *device, const char *filter, int promisc) +{ + char errbuf[PCAP_ERRBUF_SIZE], *tmp_device; + int linktype, snaplen, waited; + + /* pcap doesn't like device being const */ + tmp_device = xstrdup(device); + + /* Open packet capture descriptor. */ + waited = 0; + for (;;) { + errbuf[0] = '\0'; /* zero length string */ + pcap = pcap_open_live( + tmp_device, + 1, /* snaplen, irrelevant at this point */ + 0, /* promisc, also irrelevant */ + CAP_TIMEOUT, + errbuf); + if (pcap != NULL) break; /* success! */ + + if ((opt_wait_secs != -1) && strstr(errbuf, "device is not up")) { + if ((opt_wait_secs > 0) && (waited >= opt_wait_secs)) + errx(1, "waited %d secs, giving up: pcap_open_live(): %s", + waited, errbuf); + + verbosef("waited %d secs, interface is not up", waited); + sleep(1); + waited++; + } + else errx(1, "pcap_open_live(): %s", errbuf); + } + + /* Work out the linktype and what snaplen we need. */ + linktype = pcap_datalink(pcap); + verbosef("linktype is %d", linktype); + if ((linktype == DLT_EN10MB) && opt_want_macs) + hosts_db_show_macs = 1; + linkhdr = getlinkhdr(linktype); + if (linkhdr == NULL) + errx(1, "unknown linktype %d", linktype); + if (linkhdr->handler == NULL) + errx(1, "no handler for linktype %d", linktype); + snaplen = getsnaplen(linkhdr); + if (opt_want_pppoe) { + snaplen += PPPOE_HDR_LEN; + if (linktype != DLT_EN10MB) + errx(1, "can't do PPPoE decoding on a non-Ethernet linktype"); + } + verbosef("calculated snaplen minimum %d", snaplen); +#ifdef linux + /* Ubuntu 9.04 has a problem where requesting snaplen <= 60 will + * give us 42 bytes, and we need at least 54 for TCP headers. + * + * Hack to set minimum snaplen to tcpdump's default: + */ + snaplen = MAX(snaplen, 96); +#endif + if (opt_want_snaplen > -1) + snaplen = opt_want_snaplen; + verbosef("using snaplen %d", snaplen); + + /* Close and re-open pcap to use the new snaplen. */ + pcap_close(pcap); + errbuf[0] = '\0'; /* zero length string */ + pcap = pcap_open_live( + tmp_device, + snaplen, + promisc, + CAP_TIMEOUT, + errbuf); + + if (pcap == NULL) + errx(1, "pcap_open_live(): %s", errbuf); + + if (errbuf[0] != '\0') /* not zero length anymore -> warning */ + warnx("pcap_open_live() warning: %s", errbuf); + + free(tmp_device); + + if (promisc) + verbosef("capturing in promiscuous mode"); + else + verbosef("capturing in non-promiscuous mode"); + + /* Set filter expression, if any. */ + if (filter != NULL) + { + struct bpf_program prog; + char *tmp_filter = xstrdup(filter); + if (pcap_compile( + pcap, + &prog, + tmp_filter, + 1, /* optimize */ + 0) /* netmask */ + == -1) + errx(1, "pcap_compile(): %s", pcap_geterr(pcap)); + + if (pcap_setfilter(pcap, &prog) == -1) + errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap)); + + pcap_freecode(&prog); + free(tmp_filter); + } + + pcap_fd = pcap_fileno(pcap); + + /* set non-blocking */ +#ifdef linux + if (pcap_setnonblock(pcap, 1, errbuf) == -1) + errx(1, "pcap_setnonblock(): %s", errbuf); +#else +{ int one = 1; + if (ioctl(pcap_fd, FIONBIO, &one) == -1) + err(1, "ioctl(pcap_fd, FIONBIO)"); } +#endif + +#ifdef BIOCSETWF +{ + /* Deny all writes to the socket */ + struct bpf_insn bpf_wfilter[] = { BPF_STMT(BPF_RET+BPF_K, 0) }; + int wf_len = sizeof(bpf_wfilter) / sizeof(struct bpf_insn); + struct bpf_program pr; + + pr.bf_len = wf_len; + pr.bf_insns = bpf_wfilter; + + if (ioctl(pcap_fd, BIOCSETWF, &pr) == -1) + err(1, "ioctl(pcap_fd, BIOCSETFW)"); + verbosef("filtered out BPF writes"); +} +#endif + +#ifdef BIOCLOCK + /* set "locked" flag (no reset) */ + if (ioctl(pcap_fd, BIOCLOCK) == -1) + err(1, "ioctl(pcap_fd, BIOCLOCK)"); + verbosef("locked down BPF for security"); +#endif +} + +/* + * Set pcap_fd in the given fd_set. + */ +void +cap_fd_set( +#ifdef linux + fd_set *read_set _unused_, + int *max_fd _unused_, + struct timeval *timeout, +#else + fd_set *read_set, + int *max_fd, + struct timeval *timeout _unused_, +#endif + int *need_timeout) +{ + assert(*need_timeout == 0); /* we're first to get a shot at this */ +#ifdef linux + /* + * Linux's BPF is immediate, so don't select() as it will lead to horrible + * performance. Instead, use a timeout for buffering. + */ + *need_timeout = 1; + timeout->tv_sec = 0; + timeout->tv_usec = CAP_TIMEOUT * 1000; /* msec->usec */ +#else + /* We have a BSD-like BPF, we can select() on it. */ + FD_SET(pcap_fd, read_set); + *max_fd = MAX(*max_fd, pcap_fd); +#endif +} + +unsigned int cap_pkts_recv = 0, cap_pkts_drop = 0; + +static void +cap_stats_update(void) +{ + struct pcap_stat ps; + + if (pcap_stats(pcap, &ps) != 0) { + warnx("pcap_stats(): %s", pcap_geterr(pcap)); + return; + } + + cap_pkts_recv = ps.ps_recv; + cap_pkts_drop = ps.ps_drop; +} + +/* + * Print hexdump of received packet. + */ +static void +hexdump(const u_char *buf, const uint32_t len) +{ + uint32_t i, col; + + printf("packet of %u bytes:\n", len); + for (i=0, col=0; ihdrlen) + printf("["); + else if (i+1 == linkhdr->hdrlen + IP_HDR_LEN) + printf("]"); + else printf(" "); + col += 3; + if (col >= 72) { + printf("\n"); + col = 0; + } + } + if (col != 0) printf("\n"); + printf("\n"); +} + +/* + * Callback function for pcap_dispatch() which chains to the decoder specified + * in linkhdr struct. + */ +static void +callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) +{ + if (opt_want_hexdump) hexdump(bytes, h->caplen); + linkhdr->handler(user, h, bytes); +} + +/* + * Process any packets currently in the capture buffer. + */ +void +cap_poll(fd_set *read_set +#ifdef linux + _unused_ +#endif +) +{ + int total, ret; + +#ifndef linux /* We don't use select() on Linux. */ + if (!FD_ISSET(pcap_fd, read_set)) { + verbosef("cap_poll premature"); + return; + } +#endif + + /* + * Once per capture poll, check our IP address. It's used in accounting + * for traffic graphs. + */ + localip_update(); /* FIXME: this might even be too often */ + + total = 0; + for (;;) { +#ifndef NDEBUG + struct timeval t1; + gettimeofday(&t1, NULL); +#endif + ret = pcap_dispatch( + pcap, + -1, /* count, -1 = entire buffer */ + callback, + NULL); /* user */ + + if (ret < 0) { + warnx("pcap_dispatch(): %s", pcap_geterr(pcap)); + return; + } + +#ifndef NDEBUG + { + struct timeval t2; + int td; + + gettimeofday(&t2, NULL); + td = (t2.tv_sec - t1.tv_sec) * 1000000 + t2.tv_usec - t1.tv_usec; + if (td > CAP_TIMEOUT*1000) + warnx("pcap_dispatch blocked for %d usec! (expected <= %d usec)\n", + td, CAP_TIMEOUT*1000); + } +#endif + + /* Despite count = -1, Linux will only dispatch one packet at a time. */ + total += ret; + +#ifdef linux + /* keep looping until we've dispatched all the outstanding packets */ + if (ret == 0) break; +#else + /* we get them all on the first shot */ + break; +#endif + } + cap_stats_update(); +} + +void +cap_stop(void) +{ + pcap_close(pcap); +} + +/* Run through entire capfile. */ +void +cap_from_file(const char *capfile, const char *filter) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + int linktype, ret; + + /* Open packet capture descriptor. */ + errbuf[0] = '\0'; /* zero length string */ + pcap = pcap_open_offline(capfile, errbuf); + + if (pcap == NULL) + errx(1, "pcap_open_offline(): %s", errbuf); + + if (errbuf[0] != '\0') /* not zero length anymore -> warning */ + warnx("pcap_open_offline() warning: %s", errbuf); + + /* Work out the linktype. */ + linktype = pcap_datalink(pcap); + linkhdr = getlinkhdr(linktype); + if (linkhdr == NULL) + errx(1, "unknown linktype %d", linktype); + if (linkhdr->handler == NULL) + errx(1, "no handler for linktype %d", linktype); + if (linktype == DLT_EN10MB) /* FIXME: impossible with capfile? */ + hosts_db_show_macs = 1; + + /* Set filter expression, if any. */ /* FIXME: factor! */ + if (filter != NULL) + { + struct bpf_program prog; + char *tmp_filter = xstrdup(filter); + if (pcap_compile( + pcap, + &prog, + tmp_filter, + 1, /* optimize */ + 0) /* netmask */ + == -1) + errx(1, "pcap_compile(): %s", pcap_geterr(pcap)); + + if (pcap_setfilter(pcap, &prog) == -1) + errx(1, "pcap_setfilter(): %s", pcap_geterr(pcap)); + + pcap_freecode(&prog); + free(tmp_filter); + } + + /* Process file. */ + ret = pcap_dispatch( + pcap, + -1, /* count, -1 = entire buffer */ + callback, + NULL); /* user */ + + if (ret < 0) + errx(1, "pcap_dispatch(): %s", pcap_geterr(pcap)); +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/cap.h b/cap.h new file mode 100644 index 0000000..18f2425 --- /dev/null +++ b/cap.h @@ -0,0 +1,21 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * cap.h: interface to libpcap. + */ + +#include /* OpenBSD needs this before select */ +#include /* FreeBSD 4 needs this for struct timeval */ +#include + +extern unsigned int cap_pkts_recv, cap_pkts_drop; + +void cap_init(const char *device, const char *filter, int promisc); +void cap_fd_set(fd_set *read_set, int *max_fd, + struct timeval *timeout, int *need_timeout); +void cap_poll(fd_set *read_set); +void cap_stop(void); + +void cap_from_file(const char *capfile, const char *filter); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/cdefs.h b/cdefs.h new file mode 100644 index 0000000..a0405eb --- /dev/null +++ b/cdefs.h @@ -0,0 +1,40 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * cdefs.h: compiler-specific defines + * + * This file borrows from FreeBSD's sys/cdefs.h + */ + +#ifdef __GNUC__ +# define _unused_ __attribute__((__unused__)) +# define _noreturn_ __attribute__((__noreturn__)) +# define _printflike_(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg) )) +#else +# define _unused_ +# define _noreturn_ +# define _printflike_(fmtarg, firstvararg) +#endif + +#if __GNUC__ == 2 +# define inline __inline__ +#else +# ifdef __TenDRA__ +# define inline __inline +# endif +#endif + +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901 +#define restrict __restrict +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..09fdf95 --- /dev/null +++ b/config.h.in @@ -0,0 +1,91 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Default chroot directory. */ +#undef CHROOT_DIR + +/* Define to 1 if you have the header file. */ +#undef HAVE_BSD_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_BSD_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_IFADDRS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `pcap' library (-lpcap). */ +#undef HAVE_LIBPCAP + +/* Define to 1 if you have the `z' library (-lz). */ +#undef HAVE_LIBZ + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_ETHER_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PCAP_H + +/* Define to 1 if you have setproctitle(). */ +#undef HAVE_SETPROCTITLE + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have strlcat(). */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have strlcpy(). */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* 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 home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* User to privdrop to. */ +#undef PRIVDROP_USER + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS diff --git a/configure b/configure new file mode 100755 index 0000000..5552f80 --- /dev/null +++ b/configure @@ -0,0 +1,5516 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.68 for darkstat 3.0.715. +# +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='darkstat' +PACKAGE_TARNAME='darkstat' +PACKAGE_VERSION='3.0.715' +PACKAGE_STRING='darkstat 3.0.715' +PACKAGE_BUGREPORT='' +PACKAGE_URL='http://unix4lyfe.org/darkstat/' + +ac_unique_file="darkstat.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +EGREP +GREP +CPP +AM_DEFAULT_VERBOSITY +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_chroot_dir +with_privdrop_user +enable_silent_rules +enable_debug +enable_mad_warnings +with_pcap +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures darkstat 3.0.715 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/darkstat] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of darkstat 3.0.715:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: 'make V=1') + --disable-silent-rules verbose build output (undo: 'make V=0') + --disable-debug turn off debugging code and asserts + --enable-mad-warnings turn on lots of compile-time warnings, these are + GCC-specific and only really useful for development + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-chroot-dir specify the chroot directory (default: /var/empty) + --with-privdrop-user specify which user to drop privileges to (default: + nobody) + --with-pcap=DIR prefix to libpcap installation + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +darkstat home page: . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +darkstat configure 3.0.715 +generated by GNU Autoconf 2.68 + +Copyright (C) 2010 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by darkstat $as_me 3.0.715, which was +generated by GNU Autoconf 2.68. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers config.h" + + +RULE="------------------------------------------------------------" + +# Let user specify CHROOT_DIR, or else autodetect it, or else die. + +# Check whether --with-chroot-dir was given. +if test "${with_chroot_dir+set}" = set; then : + withval=$with_chroot_dir; if test "x$withval" = "xyes"; then + as_fn_error $? "please specify --with-chroot-dir=/path/to/darkstat/chroot" "$LINENO" 5 + fi + _chd="$withval" +else + # Find an "empty" directory to serve as the chroot. + _chd="/var/empty" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_chd" >&5 +$as_echo_n "checking for $_chd... " >&6; } + if test -d $_chd ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found it" >&5 +$as_echo "found it" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not there" >&5 +$as_echo "not there" >&6; } + _chd="/var/lib/empty" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $_chd" >&5 +$as_echo_n "checking for $_chd... " >&6; } + if test -d $_chd ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found it" >&5 +$as_echo "found it" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not there either" >&5 +$as_echo "not there either" >&6; } + as_fn_error $? "please specify --with-chroot-dir=/path/to/darkstat/chroot" "$LINENO" 5 + fi + fi +fi + + +cat >>confdefs.h <<_ACEOF +#define CHROOT_DIR "$_chd" +_ACEOF + + +# Allow configure-time override of PRIVDROP_USER. + +# Check whether --with-privdrop-user was given. +if test "${with_privdrop_user+set}" = set; then : + withval=$with_privdrop_user; _pdu="$withval" +else + _pdu="nobody" +fi + + +cat >>confdefs.h <<_ACEOF +#define PRIVDROP_USER "$_pdu" +_ACEOF + + +# Checks for programs. +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in +no) AM_DEFAULT_VERBOSITY=1;; +*) AM_DEFAULT_VERBOSITY=0;; +esac + + +# Let user disable debugging symbols so we create smaller binaries. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we want debug code" >&5 +$as_echo_n "checking if we want debug code... " >&6; } +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; if test "x$enableval" = "xno" ; then + CFLAGS="$CFLAGS -DNDEBUG -g0" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: nope" >&5 +$as_echo "nope" >&6; } + elif test "x$enableval" = "xyes" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: sure" >&5 +$as_echo "sure" >&6; } + else + CFLAGS="$CFLAGS -g$enableval" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: sure ($enableval)" >&5 +$as_echo "sure ($enableval)" >&6; } + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: sure" >&5 +$as_echo "sure" >&6; } +fi + + +# Augment CFLAGS for fun. +echo "int main(void){return 1;}" > conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler wants a hit off the pipe" >&5 +$as_echo_n "checking if your C compiler wants a hit off the pipe... " >&6; } +save_cflags="$CFLAGS" +CFLAGS="-pipe $CFLAGS" +if (eval $ac_link) 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: sure does" >&5 +$as_echo "sure does" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_cflags" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler has a link-time optimizer" >&5 +$as_echo_n "checking if your C compiler has a link-time optimizer... " >&6; } +if test x$GCC = xyes; then + save_cflags="$CFLAGS" + CFLAGS="-flto $CFLAGS" + if (eval $ac_link) 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: sure does" >&5 +$as_echo "sure does" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_cflags" + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: skipped" >&5 +$as_echo "skipped" >&6; } +fi + +# Check whether --enable-mad-warnings was given. +if test "${enable_mad_warnings+set}" = set; then : + enableval=$enable_mad_warnings; if test "x$enableval" = "xyes" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler can emit certain warnings" >&5 +$as_echo_n "checking if your C compiler can emit certain warnings... " >&6; } + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -fdiagnostics-show-option \ +-Waddress \ +-Waggregate-return \ +-Wall \ +-Wbad-function-cast \ +-Wcast-align \ +-Wcast-qual \ +-Wchar-subscripts \ +-Wcomment \ +-Wdeclaration-after-statement \ +-Wdisabled-optimization \ +-Wextra \ +-Wfloat-equal \ +-Wformat \ +-Wformat=2 \ +-Wformat-nonliteral \ +-Wformat-security \ +-Wformat-y2k \ +-Wimplicit \ +-Wimplicit-function-declaration \ +-Wimplicit-int \ +-Winit-self \ +-Winline \ +-Winvalid-pch \ +-Wmain \ +-Wmissing-braces \ +-Wmissing-declarations \ +-Wmissing-field-initializers \ +-Wmissing-format-attribute \ +-Wmissing-include-dirs \ +-Wmissing-noreturn \ +-Wmissing-prototypes \ +-Wnested-externs \ +-Wnonnull \ +-Wold-style-definition \ +-Wpacked \ +-Wparentheses \ +-Wpointer-arith \ +-Wpointer-sign \ +-Wredundant-decls \ +-Wreturn-type \ +-Wsequence-point \ +-Wshadow \ +-Wsign-compare \ +-Wstrict-aliasing -fstrict-aliasing \ +-Wstrict-overflow=5 -fstrict-overflow \ +-Wstrict-prototypes \ +-Wswitch \ +-Wswitch-default \ +-Wswitch-enum \ +-Wtrigraphs \ +-Wundef \ +-Wuninitialized \ +-Wunknown-pragmas \ +-Wunsafe-loop-optimizations \ +-Wunused \ +-Wunused-function \ +-Wunused-label \ +-Wunused-parameter \ +-Wunused-value \ +-Wunused-variable \ +-Wvariadic-macros \ +-Wvolatile-register-var \ +-Wwrite-strings \ +" +# The above are valid for gcc version 4.2.1. + + if (eval $ac_link) 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your C compiler can emit certain other warnings" >&5 +$as_echo_n "checking if your C compiler can emit certain other warnings... " >&6; } + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS \ +-Warray-bounds \ +-Wclobbered \ +-Wcoverage-mismatch \ +-Wempty-body \ +-Wignored-qualifiers \ +-Wlogical-op \ +-Wmissing-parameter-type \ +-Wold-style-declaration \ +-Wpacked-bitfield-compat \ +-Wsync-nand \ +-Wtype-limits \ +-Wvla \ +" +# The above are valid for gcc version 4.4.3. +# We skip the following entirely: +#-pedantic +#-Wabi +#-Wc++-compat +#-Wconversion +#-Wfatal-errors +#-Wlong-long +#-Wpadded +#-Wsign-conversion +#-Wstack-protector +#-Wsystem-headers +#-Wtraditional +#-Wtraditional-conversion +#-Wunreachable-code + + if (eval $ac_link) 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_cflags" + fi + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_cflags" + fi + fi +fi + + +rm -f conftest.$ac_objext conftest.$ac_ext + + + +# Check for zlib. + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 +$as_echo_n "checking for deflate in -lz... " >&6; } +if ${ac_cv_lib_z_deflate+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char deflate (); +int +main () +{ +return deflate (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_z_deflate=yes +else + ac_cv_lib_z_deflate=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 +$as_echo "$ac_cv_lib_z_deflate" >&6; } +if test "x$ac_cv_lib_z_deflate" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBZ 1 +_ACEOF + + LIBS="-lz $LIBS" + +else + + cat <&5 +$as_echo_n "checking for library containing gethostbyname... " >&6; } +if ${ac_cv_search_gethostbyname+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname (); +int +main () +{ +return gethostbyname (); + ; + return 0; +} +_ACEOF +for ac_lib in '' nsl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_gethostbyname=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_gethostbyname+:} false; then : + break +fi +done +if ${ac_cv_search_gethostbyname+:} false; then : + +else + ac_cv_search_gethostbyname=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 +$as_echo "$ac_cv_search_gethostbyname" >&6; } +ac_res=$ac_cv_search_gethostbyname +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "gethostbyname() not found" "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 +$as_echo_n "checking for library containing socket... " >&6; } +if ${ac_cv_search_socket+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +for ac_lib in '' socket; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_socket=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_socket+:} false; then : + break +fi +done +if ${ac_cv_search_socket+:} false; then : + +else + ac_cv_search_socket=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 +$as_echo "$ac_cv_search_socket" >&6; } +ac_res=$ac_cv_search_socket +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "socket() not found" "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing hstrerror" >&5 +$as_echo_n "checking for library containing hstrerror... " >&6; } +if ${ac_cv_search_hstrerror+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char hstrerror (); +int +main () +{ +return hstrerror (); + ; + return 0; +} +_ACEOF +for ac_lib in '' resolv; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_hstrerror=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_hstrerror+:} false; then : + break +fi +done +if ${ac_cv_search_hstrerror+:} false; then : + +else + ac_cv_search_hstrerror=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_hstrerror" >&5 +$as_echo "$ac_cv_search_hstrerror" >&6; } +ac_res=$ac_cv_search_hstrerror +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "hstrerror() not found" "$LINENO" 5 +fi + + +# Solaris need sys/filio.h for FIONBIO +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in sys/filio.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/filio.h" "ac_cv_header_sys_filio_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_filio_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_FILIO_H 1 +_ACEOF + +fi + +done + + +# Debian GNU/kFreeBSD needs net/if_ether.h for ETH_P_IPV6 +for ac_header in net/if_ether.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "net/if_ether.h" "ac_cv_header_net_if_ether_h" "$ac_includes_default" +if test "x$ac_cv_header_net_if_ether_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NET_IF_ETHER_H 1 +_ACEOF + +fi + +done + + +# This is the modern way. Older systems use the ioctl method. +for ac_header in ifaddrs.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "ifaddrs.h" "ac_cv_header_ifaddrs_h" "$ac_includes_default" +if test "x$ac_cv_header_ifaddrs_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_IFADDRS_H 1 +_ACEOF + +fi + +done + + +# Some OSes (Solaris) need sys/sockio.h for SIOCGIFADDR +for ac_header in sys/sockio.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/sockio.h" "ac_cv_header_sys_sockio_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_sockio_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_SOCKIO_H 1 +_ACEOF + +fi + +done + + +# Check for libpcap + +# Check whether --with-pcap was given. +if test "${with_pcap+set}" = set; then : + withval=$with_pcap; if test "$withval" = yes ; then + as_fn_error $? "must specify a path, as in --with-pcap=DIR" "$LINENO" 5 + fi + if test "$withval" != no ; then + PCAP_HOME="$withval" + fi +fi + + +if test -n "$PCAP_HOME" ; then + LDFLAGS="$LDFLAGS -L$PCAP_HOME/lib" + CPPFLAGS2="$CPPFLAGS -I$PCAP_HOME/include/pcap" + CPPFLAGS="$CPPFLAGS -I$PCAP_HOME/include" +else + CPPFLAGS2="$CPPFLAGS -I/usr/include/pcap" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_loop in -lpcap" >&5 +$as_echo_n "checking for pcap_loop in -lpcap... " >&6; } +if ${ac_cv_lib_pcap_pcap_loop+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpcap $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pcap_loop (); +int +main () +{ +return pcap_loop (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pcap_pcap_loop=yes +else + ac_cv_lib_pcap_pcap_loop=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_loop" >&5 +$as_echo "$ac_cv_lib_pcap_pcap_loop" >&6; } +if test "x$ac_cv_lib_pcap_pcap_loop" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPCAP 1 +_ACEOF + + LIBS="-lpcap $LIBS" + +else + PCAP_BROKEN="yes" +fi + + +if test -z "$PCAP_BROKEN" ; then + for ac_header in pcap.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "pcap.h" "ac_cv_header_pcap_h" "$ac_includes_default" +if test "x$ac_cv_header_pcap_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PCAP_H 1 +_ACEOF + +else + # Couldn't find headers, try include/pcap + CPPFLAGS="$CPPFLAGS2" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking in include/pcap" >&5 +$as_echo "$as_me: checking in include/pcap" >&6;} + unset ac_cv_header_pcap_h + for ac_header in pcap.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "pcap.h" "ac_cv_header_pcap_h" "$ac_includes_default" +if test "x$ac_cv_header_pcap_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PCAP_H 1 +_ACEOF + { $as_echo "$as_me:${as_lineno-$LINENO}: result: I hate you." >&5 +$as_echo "I hate you." >&6; } +else + PCAP_BROKEN="yes" +fi + +done + +fi + +done + +fi + +if test -n "$PCAP_BROKEN" ; then + cat <&5 +$as_echo_n "checking for library containing setproctitle... " >&6; } +if ${ac_cv_search_setproctitle+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char setproctitle (); +int +main () +{ +return setproctitle (); + ; + return 0; +} +_ACEOF +for ac_lib in '' bsd; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_setproctitle=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_setproctitle+:} false; then : + break +fi +done +if ${ac_cv_search_setproctitle+:} false; then : + +else + ac_cv_search_setproctitle=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_setproctitle" >&5 +$as_echo "$ac_cv_search_setproctitle" >&6; } +ac_res=$ac_cv_search_setproctitle +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_SETPROCTITLE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strlcpy" >&5 +$as_echo_n "checking for library containing strlcpy... " >&6; } +if ${ac_cv_search_strlcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strlcpy (); +int +main () +{ +return strlcpy (); + ; + return 0; +} +_ACEOF +for ac_lib in '' bsd; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_strlcpy=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_strlcpy+:} false; then : + break +fi +done +if ${ac_cv_search_strlcpy+:} false; then : + +else + ac_cv_search_strlcpy=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strlcpy" >&5 +$as_echo "$ac_cv_search_strlcpy" >&6; } +ac_res=$ac_cv_search_strlcpy +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_STRLCPY 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strlcat" >&5 +$as_echo_n "checking for library containing strlcat... " >&6; } +if ${ac_cv_search_strlcat+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strlcat (); +int +main () +{ +return strlcat (); + ; + return 0; +} +_ACEOF +for ac_lib in '' bsd; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_strlcat=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_strlcat+:} false; then : + break +fi +done +if ${ac_cv_search_strlcat+:} false; then : + +else + ac_cv_search_strlcat=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strlcat" >&5 +$as_echo "$ac_cv_search_strlcat" >&6; } +ac_res=$ac_cv_search_strlcat +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_STRLCAT 1" >>confdefs.h + +fi + + +for ac_header in bsd/string.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "bsd/string.h" "ac_cv_header_bsd_string_h" "$ac_includes_default" +if test "x$ac_cv_header_bsd_string_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_BSD_STRING_H 1 +_ACEOF + +fi + +done + +for ac_header in bsd/unistd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "bsd/unistd.h" "ac_cv_header_bsd_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_bsd_unistd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_BSD_UNISTD_H 1 +_ACEOF + +fi + +done + + +ac_config_files="$ac_config_files Makefile darkstat.8" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by darkstat $as_me 3.0.715, which was +generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider. +darkstat home page: ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +darkstat config.status 3.0.715 +configured by $0, generated by GNU Autoconf 2.68, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2010 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "darkstat.8") CONFIG_FILES="$CONFIG_FILES darkstat.8" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..15c356a --- /dev/null +++ b/configure.ac @@ -0,0 +1,329 @@ +AC_INIT(darkstat, 3.0.715, , , http://unix4lyfe.org/darkstat/) +AC_CONFIG_SRCDIR([darkstat.c]) +AC_CONFIG_HEADER([config.h]) + +RULE="------------------------------------------------------------" + +# Let user specify CHROOT_DIR, or else autodetect it, or else die. +AC_ARG_WITH(chroot-dir, AS_HELP_STRING([--with-chroot-dir], + [specify the chroot directory (default: /var/empty)]), + [if test "x$withval" = "xyes"; then + AC_MSG_ERROR([please specify --with-chroot-dir=/path/to/darkstat/chroot]) + fi + _chd="$withval"], + [# Find an "empty" directory to serve as the chroot. + _chd="/var/empty" + AC_MSG_CHECKING([for $_chd]) + if test -d $_chd ; then + AC_MSG_RESULT(found it) + else + AC_MSG_RESULT(not there) + _chd="/var/lib/empty" + AC_MSG_CHECKING([for $_chd]) + if test -d $_chd ; then + AC_MSG_RESULT(found it) + else + AC_MSG_RESULT(not there either) + AC_MSG_ERROR([please specify --with-chroot-dir=/path/to/darkstat/chroot]) + fi + fi]) +AC_DEFINE_UNQUOTED(CHROOT_DIR, "$_chd", [Default chroot directory.]) + +# Allow configure-time override of PRIVDROP_USER. +AC_ARG_WITH(privdrop-user, AS_HELP_STRING([--with-privdrop-user], + [specify which user to drop privileges to (default: nobody)]), + [_pdu="$withval"], + [_pdu="nobody"]) +AC_DEFINE_UNQUOTED(PRIVDROP_USER, "$_pdu", [User to privdrop to.]) + +# Checks for programs. +AC_PROG_INSTALL +AC_PROG_CC + +m4_pattern_allow([^AM_DEFAULT_VERBOSITY$]) +AC_ARG_ENABLE([silent-rules], +[ --enable-silent-rules less verbose build output (undo: 'make V=1') + --disable-silent-rules verbose build output (undo: 'make V=0')]) +case $enable_silent_rules in +no) AM_DEFAULT_VERBOSITY=1;; +*) AM_DEFAULT_VERBOSITY=0;; +esac +AC_SUBST([AM_DEFAULT_VERBOSITY]) + +# Let user disable debugging symbols so we create smaller binaries. +AC_MSG_CHECKING(if we want debug code) +AC_ARG_ENABLE(debug, AS_HELP_STRING([--disable-debug], + [turn off debugging code and asserts]), + [if test "x$enableval" = "xno" ; then + CFLAGS="$CFLAGS -DNDEBUG -g0" + AC_MSG_RESULT(nope) + elif test "x$enableval" = "xyes" ; then + AC_MSG_RESULT(sure) + else + CFLAGS="$CFLAGS -g$enableval" + AC_MSG_RESULT(sure ($enableval)) + fi], + [AC_MSG_RESULT(sure)]) + +# Augment CFLAGS for fun. +echo "int main(void){return 1;}" > conftest.$ac_ext + +AC_MSG_CHECKING(if your C compiler wants a hit off the pipe) +save_cflags="$CFLAGS" +CFLAGS="-pipe $CFLAGS" +if (eval $ac_link) 2>/dev/null; then + AC_MSG_RESULT(sure does) +else + AC_MSG_RESULT(no) + CFLAGS="$save_cflags" +fi + +AC_MSG_CHECKING(if your C compiler has a link-time optimizer) +if test x$GCC = xyes; then + save_cflags="$CFLAGS" + CFLAGS="-flto $CFLAGS" + if (eval $ac_link) 2>/dev/null; then + AC_MSG_RESULT(sure does) + else + AC_MSG_RESULT(no) + CFLAGS="$save_cflags" + fi +else + AC_MSG_RESULT(skipped) +fi + +AC_ARG_ENABLE(mad-warnings, AS_HELP_STRING([--enable-mad-warnings], + [turn on lots of compile-time warnings, these are GCC-specific and only + really useful for development]), + [if test "x$enableval" = "xyes" ; then + AC_MSG_CHECKING(if your C compiler can emit certain warnings) + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -fdiagnostics-show-option \ +-Waddress \ +-Waggregate-return \ +-Wall \ +-Wbad-function-cast \ +-Wcast-align \ +-Wcast-qual \ +-Wchar-subscripts \ +-Wcomment \ +-Wdeclaration-after-statement \ +-Wdisabled-optimization \ +-Wextra \ +-Wfloat-equal \ +-Wformat \ +-Wformat=2 \ +-Wformat-nonliteral \ +-Wformat-security \ +-Wformat-y2k \ +-Wimplicit \ +-Wimplicit-function-declaration \ +-Wimplicit-int \ +-Winit-self \ +-Winline \ +-Winvalid-pch \ +-Wmain \ +-Wmissing-braces \ +-Wmissing-declarations \ +-Wmissing-field-initializers \ +-Wmissing-format-attribute \ +-Wmissing-include-dirs \ +-Wmissing-noreturn \ +-Wmissing-prototypes \ +-Wnested-externs \ +-Wnonnull \ +-Wold-style-definition \ +-Wpacked \ +-Wparentheses \ +-Wpointer-arith \ +-Wpointer-sign \ +-Wredundant-decls \ +-Wreturn-type \ +-Wsequence-point \ +-Wshadow \ +-Wsign-compare \ +-Wstrict-aliasing -fstrict-aliasing \ +-Wstrict-overflow=5 -fstrict-overflow \ +-Wstrict-prototypes \ +-Wswitch \ +-Wswitch-default \ +-Wswitch-enum \ +-Wtrigraphs \ +-Wundef \ +-Wuninitialized \ +-Wunknown-pragmas \ +-Wunsafe-loop-optimizations \ +-Wunused \ +-Wunused-function \ +-Wunused-label \ +-Wunused-parameter \ +-Wunused-value \ +-Wunused-variable \ +-Wvariadic-macros \ +-Wvolatile-register-var \ +-Wwrite-strings \ +" +# The above are valid for gcc version 4.2.1. + + if (eval $ac_link) 2>/dev/null; then + AC_MSG_RESULT(yes) + + AC_MSG_CHECKING(if your C compiler can emit certain other warnings) + save_cflags="$CFLAGS" + CFLAGS="$CFLAGS \ +-Warray-bounds \ +-Wclobbered \ +-Wcoverage-mismatch \ +-Wempty-body \ +-Wignored-qualifiers \ +-Wlogical-op \ +-Wmissing-parameter-type \ +-Wold-style-declaration \ +-Wpacked-bitfield-compat \ +-Wsync-nand \ +-Wtype-limits \ +-Wvla \ +" +# The above are valid for gcc version 4.4.3. +# We skip the following entirely: +#-pedantic +#-Wabi +#-Wc++-compat +#-Wconversion +#-Wfatal-errors +#-Wlong-long +#-Wpadded +#-Wsign-conversion +#-Wstack-protector +#-Wsystem-headers +#-Wtraditional +#-Wtraditional-conversion +#-Wunreachable-code + + if (eval $ac_link) 2>/dev/null; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + CFLAGS="$save_cflags" + fi + + else + AC_MSG_RESULT(no) + CFLAGS="$save_cflags" + fi + fi]) + +rm -f conftest.$ac_objext conftest.$ac_ext + + + +# Check for zlib. +AC_CHECK_LIB(z, deflate,, [ + cat < + + + + UserName + root + GroupName + wheel + KeepAlive + + Label + cx.ath.darkstat + Nice + 1 + ProgramArguments + + + --no-daemon + -i + en0 + -b + 127.0.0.1 + + RunAtLoad + + + diff --git a/contrib/darkproxy.php b/contrib/darkproxy.php new file mode 100644 index 0000000..67117ac --- /dev/null +++ b/contrib/darkproxy.php @@ -0,0 +1,30 @@ + +#include +#include +#include "err.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PATH_DEVNULL "/dev/null" + +/* malloc() that exits on failure. */ +void * +xmalloc(const size_t size) +{ + void *ptr = malloc(size); + + if (ptr == NULL) + errx(1, "malloc(): out of memory"); + return (ptr); +} + +/* calloc() that exits on failure. */ +void * +xcalloc(const size_t num, const size_t size) +{ + void *ptr = calloc(num, size); + + if (ptr == NULL) + errx(1, "calloc(): out of memory"); + return (ptr); +} + +/* realloc() that exits on failure. */ +void * +xrealloc(void *original, const size_t size) +{ + void *ptr = realloc(original, size); + + if (ptr == NULL) + errx(1, "realloc(): out of memory"); + return (ptr); +} + +/* strdup() that exits on failure. */ +char * +xstrdup(const char *s) +{ + char *tmp = strdup(s); + + if (tmp == NULL) + errx(1, "strdup(): out of memory"); + return (tmp); +} + +/* --------------------------------------------------------------------------- + * Split string out of src with range [left:right-1] + */ +char * +split_string(const char *src, const size_t left, const size_t right) +{ + char *dest; + assert(left <= right); + assert(left < strlen(src)); /* [left means must be smaller */ + assert(right <= strlen(src)); /* right) means can be equal or smaller */ + + dest = xmalloc(right - left + 1); + memcpy(dest, src+left, right-left); + dest[right-left] = '\0'; + return (dest); +} + +/* --------------------------------------------------------------------------- + * Uppercasify all characters in a string of given length. + */ +void +strntoupper(char *str, const size_t length) +{ + size_t i; + + for (i=0; i 2) + close(fd_null); +} + +/* + * For security, chroot (optionally) and drop privileges. + * Pass a NULL chroot_dir to disable chroot() behaviour. + */ +void +privdrop(const char *chroot_dir, const char *privdrop_user) +{ + struct passwd *pw; + + errno = 0; + pw = getpwnam(privdrop_user); + + if (pw == NULL) { + if (errno == 0) + errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user); + else + err(1, "getpwnam(\"%s\") failed", privdrop_user); + } + if (chroot_dir != NULL) { + tzset(); /* read /etc/localtime before we chroot */ + if (chdir(chroot_dir) == -1) + err(1, "chdir(\"%s\") failed", chroot_dir); + if (chroot(chroot_dir) == -1) + err(1, "chroot(\"%s\") failed", chroot_dir); + verbosef("chrooted into: %s", chroot_dir); + } + if (setgid(pw->pw_gid) == -1) + err(1, "setgid"); + if (setuid(pw->pw_uid) == -1) + err(1, "setuid"); + verbosef("set uid/gid to %d/%d", (int)pw->pw_uid, (int)pw->pw_gid); +} + +/* Make the specified file descriptor non-blocking. */ +void +fd_set_nonblock(const int fd) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + err(1, "fcntl(fd %d) to get flags", fd); + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) + err(1, "fcntl(fd %d) to set O_NONBLOCK", fd); + assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == O_NONBLOCK ); +} + +/* Make the specified file descriptor blocking. */ +void +fd_set_block(const int fd) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + err(1, "fcntl(fd %d) to get flags", fd); + flags &= ~O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) + err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd); + assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == 0 ); +} diff --git a/conv.h b/conv.h new file mode 100644 index 0000000..223ba53 --- /dev/null +++ b/conv.h @@ -0,0 +1,25 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * conv.h: convenience functions. + */ + +#include + +void *xmalloc(const size_t size); +void *xcalloc(const size_t num, const size_t size); +void *xrealloc(void *original, const size_t size); +char *xstrdup(const char *s); +char *split_string(const char *src, const size_t left, const size_t right); +void strntoupper(char *str, const size_t length); +int str_starts_with(const char *haystack, const char *needle); +char**split(const char delimiter, const char *str, unsigned int *num_chunks); +char *qs_get(const char *qs, const char *key); + +void daemonize_start(void); +void daemonize_finish(void); +void privdrop(const char *chroot_dir, const char *privdrop_user); +void fd_set_nonblock(const int fd); +void fd_set_block(const int fd); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/darkstat.8.in b/darkstat.8.in new file mode 100644 index 0000000..e786af3 --- /dev/null +++ b/darkstat.8.in @@ -0,0 +1,453 @@ +.\" +.\" darkstat 3 +.\" Copyright 2001-2011, Emil Mikulic. +.\" +.\" You may use, modify and redistribute this file under the terms of the +.\" GNU General Public License version 2. (see COPYING.GPL) +.\" +.TH darkstat 8 "June 2011" "@PACKAGE_STRING@" +.SH NAME +darkstat \- network statistics gatherer +.\" +.SH SYNOPSIS +.B darkstat +[ +.BI \-i " interface" +] [ +.BI \-r " file" +] [ +.BI \-\-snaplen " bytes" +] [ +.BI \-\-pppoe +] [ +.BI \-\-syslog +] [ +.BI \-\-verbose +] [ +.BI \-\-no\-daemon +] [ +.BI \-\-no\-promisc +] [ +.BI \-\-no\-dns +] [ +.BI \-\-no\-macs +] [ +.BI \-\-no\-lastseen +] [ +.BI \-p " port" +] [ +.BI \-b " bindaddr" +] [ +.BI \-f " filter" +] [ +.BI \-l " network/netmask" +] [ +.BI \-\-local\-only +] [ +.BI \-\-chroot " dir" +] [ +.BI \-\-user " username" +] [ +.BI \-\-daylog " filename" +] [ +.BI \-\-import " filename" +] [ +.BI \-\-export " filename" +] [ +.BI \-\-pidfile " filename" +] [ +.BI \-\-hosts\-max " count" +] [ +.BI \-\-hosts\-keep " count" +] [ +.BI \-\-ports\-max " count" +] [ +.BI \-\-ports\-keep " count" +] [ +.BI \-\-highest\-port " port" +] [ +.BI \-\-wait " secs" +] [ +.BI \-\-hexdump +] +.\" +.SH DESCRIPTION +.I darkstat +is a packet sniffer that runs as a background process, +gathers all sorts of statistics about network usage, +and serves them over HTTP. + +All settings are passed on the commandline. +.\" +.SH OPTIONS +.\" +.TP +.BI \-i " interface" +Capture traffic on the specified network interface. +This is the only mandatory commandline argument. +.\" +.TP +.BI \-r " file" +Instead of capturing live traffic, read it from a +.BR pcap (3) +capture file. +This is only useful for development and benchmarking. +The +.BI \-r +and +.BI \-i +arguments are mutually exclusive. +.\" +.TP +.BI \-\-snaplen " bytes" +How many bytes to capture from the start of each packet. +You should not need to specify this; +\fIdarkstat\fR will calculate it automatically. +.\" +.TP +.BI \-\-pppoe +Don't use this. + +Instead, capture on the tunnel interface that your PPPoE software +provides, for example \fBtun0\fR on \fIFreeBSD\fR, \fBpppoe0\fR on +\fIOpenBSD\fR or \fINetBSD\fR. + +If you really must, you can capture on an Ethernet interface and pass +this argument to have \fIdarkstat\fR decode PPPoE frames and ignore +everything else. +Make sure you also specify your local address with the \fB\-l\fR +argument! +.\" +.TP +.BI \-\-syslog +Errors, warnings, and verbose messages will go to \fBsyslog\fR (facility +daemon, priority debug) instead of \fBstderr\fR. + +On some systems, these messages end up in \fB/var/log/debug\fR +by default. +.\" +.TP +.BI \-\-verbose +Produce more verbose debugging messages. +.\" +.TP +.BI \-\-no\-daemon +Do not detach from the controlling terminal; +stay in the foreground. +.\" +.TP +.BI \-\-no\-promisc +Do not use promiscuous mode to capture. +Note that an interface may already be in promiscuous mode, or may later +enter promiscuous mode, due to circumstances beyond \fIdarkstat\fR's control. +If this is a problem, use \fB\-f\fR to specify an appropriate +.BR bpf (4) +filter. +.\" +.TP +.BI \-\-no\-dns +Do not resolve IPs to host names. +This can significantly reduce memory footprint on small systems +as an extra process is created for DNS resolution. +.\" +.TP +.BI \-\-no\-macs +Do not display MAC addresses in the hosts table. +.\" +.TP +.BI \-\-no\-lastseen +Do not display the last seen time in the hosts table. +.\" +.TP +.BI \-p " port" +Bind the web interface to the specified port. +The default is 667. +.\" +.TP +.BI \-b " bindaddr" +Bind the web interface to the specified address. +The default is to listen on all interfaces. +.\" +.TP +.BI \-f " filter" +Use the specified filter expression when capturing traffic. +The filter syntax is beyond the scope of this manual page; +please refer to the +.BR tcpdump (1) +documentation. +.\" +.TP +.BI \-l " network/netmask" +Define a "local network" according to the network and netmask addresses. +All traffic entering or leaving this network will be graphed, as opposed +to the default behaviour of only graphing traffic to and from the local +host. +.RS + +The rule is that if \fBip_addr & netmask == network\fR, +then that address is considered local. +See the usage example below. +.RE +.\" +.TP +.BI \-\-local\-only +Make the web interface only display hosts on the "local network." +This is intended to be used together with the \fB\-l\fR argument. +.\" +.TP +.BI \-\-chroot " dir" +Force \fIdarkstat\fR to \fBchroot()\fR into the specified directory. +Without this argument, a default directory will be used, which is +determined at build time. +Usually \fI/var/empty\fR or \fI/var/lib/empty\fR. +.RS + +For security reasons, this directory should be empty, and the user that +\fIdarkstat\fR is running as should not have write access to it. + +However, if you wish to use \fB\-\-daylog\fR or \fB\-\-export\fR, +\fIdarkstat\fR will need write access to the chroot. +If you are uncomfortable with the security implications, don't +use any functionality that requires write access. +.RE +.\" +.TP +.BI \-\-user " username" +Force \fIdarkstat\fR to drop privileges to the \fBuid\fR and \fBgid\fR of +the specified user. +Without this argument, a default value will be used, which is set at +build time. +Usually \fBnobody\fR. +.RS + +For security reasons, this should not be \fBroot\fR. +.RE +.\" +.TP +.BI \-\-daylog " filename" +.RS +Log daily traffic statistics into the named file, relative to the +chroot directory. +If you wish to use \fB\-\-daylog\fR, you must first specify a +\fB\-\-chroot\fR directory, and it must be writeable by the +\fIdarkstat\fR user. +A writeable chroot has security implications; if you are uncomfortable +with this, do not use the \fB\-\-daylog\fR functionality. + +If the daylog argument is not specified, no logging is performed. + +The daylog format is: + +localtime|time_t|bytes_in|bytes_out|pkts_in|pkts_outs + +Lines starting with a # are comments stating when logging started and +stopped. +.RE +.\" +.TP +.BI \-\-import " filename" +Upon starting, import a \fIdarkstat\fR database from the named file, +relative to the chroot directory. +If you wish to use \fB\-\-import\fR, you must first specify a +\fB\-\-chroot\fR directory. +If the import is unsuccessful, \fIdarkstat\fR will start with an empty +database. +.\" +.TP +.BI \-\-export " filename" +On shutdown, or upon receiving SIGUSR1 or SIGUSR2, +export the in-memory database +to the named file, relative to the chroot directory. +If you wish to use \fB\-\-export\fR, you must first specify a +\fB\-\-chroot\fR directory, and it must be writeable by the +\fIdarkstat\fR user. +A writeable chroot has security implications - if you are uncomfortable +with this, do not use the \fB\-\-export\fR functionality. +.\" +.TP +.BI \-\-pidfile " filename" +.RS +Creates a file containing the process ID of \fIdarkstat\fR. +This file will be unlinked upon clean shutdown. +As with all pidfiles, if \fIdarkstat\fR dies uncleanly, a stale pidfile +can be left over. + +For example, start \fIdarkstat\fR with: +.IP +darkstat \-i fxp0 \-\-chroot /var/run/darkstat \-\-pidfile darkstat.pid +.PP +And stop with: +.IP +kill `cat /var/run/darkstat/darkstat.pid` +.PP +By default, +.BR kill (1) +will send SIGTERM, which will cause \fIdarkstat\fR to shut down cleanly. +.RE +.\" +.TP +.BI \-\-hosts\-max " count" +The maximum number of hosts that will be kept in the hosts table. +This is used to limit how much accounting data will be kept in memory. +The number of +.BI \-\-hosts\-max +must be greater than +.BI \-\-hosts\-keep +.\" +.TP +.BI \-\-hosts\-keep " count" +When the hosts table hits +.BI \-\-hosts\-max +and traffic is seen from a new host, we clean out the hosts table, +keeping only the top +.BI \-\-hosts\-keep +number of hosts, sorted by total traffic. +.\" +.TP +.BI \-\-ports\-max " count" +The maximum number of ports that will be tracked for each host. +This is used to limit how much accounting data will be kept in memory. +The number of +.BI \-\-ports\-max +must be greater than +.BI \-\-ports\-keep +.\" +.TP +.BI \-\-ports\-keep " count" +When a ports table fills up, this many ports are kept and the rest are +discarded. +.\" +.TP +.BI \-\-highest\-port " port" +Ports that are numerically higher than this will not appear in the +per-host ports tables, although their traffic will still be accounted +for. +This can be used to hide ephemeral ports. +By default, all ports are tracked. +.\" +.TP +.BI \-\-wait " secs" +Don't use this. +It's a hack to help victims of \fINetworkManager\fR and similar systems. +.RS + +You should start \fIdarkstat\fR after the capture interface has come up. +If you can't, specifying the \fB\-\-wait\fR option will make \fIdarkstat\fR +sleep up to the specified number of seconds for the interface to become ready. +Zero means wait indefinitely. +.RE +.\" +.TP +.BI \-\-hexdump +Show hex dumps of received traffic. +This is only for debugging, and implies \fB\-\-verbose\fR and +\fB\-\-no\-daemon\fR. +.\" +.\" -------------------------------------------------------------------- +.SH USAGE EXAMPLES +To gather statistics on the +.I fxp0 +interface: +.IP +darkstat \-i fxp0 +.PP +.\" +We want to account for traffic on the Internet-facing interface, +but only serve web pages to our private local network where we have the +IP address 192.168.0.1: +.IP +darkstat \-i fxp0 \-b 192.168.0.1 +.PP +.\" +We want to serve web pages on the standard HTTP port: +.IP +darkstat \-i fxp0 \-p 80 +.PP +.\" +We are on Optus (cable) and don't want to account for the constant ARP +traffic we are receiving: +.IP +darkstat \-i fxp0 \-f "not arp" +.PP +.\" +We only want to account for SSH traffic: +.IP +darkstat \-i fxp0 \-f "port 22" +.PP +.\" +We don't want to account for traffic between internal IPs: +.IP +darkstat \-i fxp0 \-f "not (src net 192.168.0 and dst net 192.168.0)" +.PP +.\" +(For a full reference on filter syntax, refer to the +.BR tcpdump (1) +manpage) +.PP +.\" +We have a network consisting of a gateway server (192.168.1.1) and a few +workstations (192.168.1.2, 192.168.1.3, etc.) and we want to graph all +traffic entering and leaving the local network, not just the gateway +server (which is running \fIdarkstat\fR): +.IP +darkstat \-i fxp0 \-l 192.168.1.0/255.255.255.0 +.PP +.\" +On some systems, we can't capture on a "decoded" interface but +only on \fInas0\fR which returns PPPoE encapsulated packets. +Do PPPoE decoding, and override the local IP manually since it +cannot be automatically detected. +Note the /32 netmask: +.IP +darkstat \-i nas0 \-\-pppoe \-l 192.168.1.1/255.255.255.255 +.\" +.SH SIGNALS +To shut +.I darkstat +down cleanly, send a SIGTERM or SIGINT signal to the +.I darkstat +parent process. +.PP +Sending the SIGUSR1 signal will cause \fIdarkstat\fR to empty out its +in-memory database. +If an \fB\-\-export\fR file was set, it will first save the database to +file. +Sending SIGUSR2 will save the database without emptying it. +.PP +.\" +.SH FREQUENTLY ASKED QUESTIONS +.SS How many bytes does each bar on the graph represent? +Hover your mouse cursor over a bar and you should get a tooltip +saying exactly how many bytes in and out the bar represents. +.\" +.SS Why aren't there labels / tics / a scale on the graphs? +Because implementing them is hard. +And doing so \fIcorrectly\fR, and in a way that works across all +browsers, looks pretty much impossible. + +I might attempt it some day. +In the meantime, patches would be gladly accepted. +.\" +.SS Why are the graphs blank? All the bars are zero. +The graphs only show traffic in/out of the local host, which is +determined by getting the IP address of the interface you're sniffing +on. + +You can use the \fB\-l\fR argument to override the local address for +accounting purposes. +You can also use it to do accounting for a whole subnet by specifying +an appropriate netmask. +.\" +.SH SEE ALSO +.BR tcpdump (1) +.\" +.SH HISTORY +.I darkstat +was written in 2001, largely as a result of a certain Australian +cable Internet provider introducing a 3GB monthly traffic limit. +.\" +.SH AUTHORS +Emil Mikulic and others. (see the AUTHORS file) +.\" +.SH WEBSITE +@PACKAGE_URL@ diff --git a/darkstat.c b/darkstat.c new file mode 100644 index 0000000..0f4b70d --- /dev/null +++ b/darkstat.c @@ -0,0 +1,518 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * darkstat.c: signals, cmdline parsing, program body. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "acct.h" +#include "cap.h" +#include "cdefs.h" +#include "config.h" +#include "conv.h" +#include "daylog.h" +#include "db.h" +#include "dns.h" +#include "err.h" +#include "http.h" +#include "hosts_db.h" +#include "localip.h" +#include "ncache.h" +#include "pidfile.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "now.h" +time_t now; + +#ifndef INADDR_NONE +# define INADDR_NONE (-1) /* Solaris */ +#endif + +/* --- Signal handling --- */ +static volatile int running = 1; +static void sig_shutdown(int signum _unused_) { running = 0; } + +static volatile int reset_pending = 0, export_pending = 0; +static void sig_reset(int signum _unused_) +{ + reset_pending = 1; + export_pending = 1; +} + +static void sig_export(int signum _unused_) { export_pending = 1; } + +/* --- Commandline parsing --- */ +static unsigned long +parsenum(const char *str, unsigned long max /* 0 for no max */) +{ + unsigned long n; + char *end; + + errno = 0; + n = strtoul(str, &end, 10); + if (*end != '\0') + errx(1, "\"%s\" is not a valid number", str); + if (errno == ERANGE) + errx(1, "\"%s\" is out of range", str); + if ((max != 0) && (n > max)) + errx(1, "\"%s\" is out of range (max %lu)", str, max); + return n; +} + +const char *opt_interface = NULL; +static void cb_interface(const char *arg) { opt_interface = arg; } + +const char *opt_capfile = NULL; +static void cb_capfile(const char *arg) { opt_capfile = arg; } + +int opt_want_snaplen = -1; +static void cb_snaplen(const char *arg) +{ opt_want_snaplen = (int)parsenum(arg, 0); } + +int opt_want_pppoe = 0; +static void cb_pppoe(const char *arg _unused_) { opt_want_pppoe = 1; } + +int opt_want_syslog = 0; +static void cb_syslog(const char *arg _unused_) { opt_want_syslog = 1; } + +int opt_want_verbose = 0; +static void cb_verbose(const char *arg _unused_) { opt_want_verbose = 1; } + +int opt_want_daemonize = 1; +static void cb_no_daemon(const char *arg _unused_) { opt_want_daemonize = 0; } + +int opt_want_promisc = 1; +static void cb_no_promisc(const char *arg _unused_) { opt_want_promisc = 0; } + +int opt_want_dns = 1; +static void cb_no_dns(const char *arg _unused_) { opt_want_dns = 0; } + +int opt_want_macs = 1; +static void cb_no_macs(const char *arg _unused_) { opt_want_macs = 0; } + +int opt_want_lastseen = 1; +static void cb_no_lastseen(const char *arg _unused_) { opt_want_lastseen = 0; } + +unsigned short opt_bindport = 667; +static void cb_port(const char *arg) +{ opt_bindport = (unsigned short)parsenum(arg, 65536); } + +static void cb_bindaddr(const char *arg) { http_add_bindaddr(arg); } + +const char *opt_filter = NULL; +static void cb_filter(const char *arg) { opt_filter = arg; } + +static int is_localnet_specified = 0; +static void cb_local(const char *arg) +{ + acct_init_localnet(arg); + is_localnet_specified = 1; +} + +int opt_want_local_only = 0; +static void cb_local_only(const char *arg _unused_) +{ opt_want_local_only = 1; } + +const char *opt_chroot_dir = NULL; +static void cb_chroot(const char *arg) { opt_chroot_dir = arg; } + +const char *opt_privdrop_user = NULL; +static void cb_user(const char *arg) { opt_privdrop_user = arg; } + +const char *daylog_fn = NULL; +static void cb_daylog(const char *arg) +{ + if (opt_chroot_dir == NULL) + errx(1, "the daylog file is relative to the chroot.\n" + "You must specify a --chroot dir before you can use --daylog."); + else + daylog_fn = arg; +} + +const char *import_fn = NULL; +static void cb_import(const char *arg) +{ + if (opt_chroot_dir == NULL) + errx(1, "the import file is relative to the chroot.\n" + "You must specify a --chroot dir before you can use --import."); + else + import_fn = arg; +} + +const char *export_fn = NULL; +static void cb_export(const char *arg) +{ + if ((opt_chroot_dir == NULL) && (opt_capfile == NULL)) + errx(1, "the export file is relative to the chroot.\n" + "You must specify a --chroot dir before you can use --export."); + else + export_fn = arg; +} + +static const char *pid_fn = NULL; +static void cb_pidfile(const char *arg) +{ + if (opt_chroot_dir == NULL) + errx(1, "the pidfile is relative to the chroot.\n" + "You must specify a --chroot dir before you can use --pidfile."); + else + pid_fn = arg; +} + +unsigned int opt_hosts_max = 1000; +static void cb_hosts_max(const char *arg) +{ opt_hosts_max = parsenum(arg, 0); } + +unsigned int opt_hosts_keep = 500; +static void cb_hosts_keep(const char *arg) +{ opt_hosts_keep = parsenum(arg, 0); } + +unsigned int opt_ports_max = 200; +static void cb_ports_max(const char *arg) +{ opt_ports_max = parsenum(arg, 65536); } + +unsigned int opt_ports_keep = 30; +static void cb_ports_keep(const char *arg) +{ opt_ports_keep = parsenum(arg, 65536); } + +unsigned int opt_highest_port = 65535; +static void cb_highest_port(const char *arg) +{ opt_highest_port = parsenum(arg, 65535); } + +int opt_wait_secs = -1; +static void cb_wait_secs(const char *arg) +{ opt_wait_secs = (int)parsenum(arg, 0); } + +int opt_want_hexdump = 0; +static void cb_hexdump(const char *arg _unused_) +{ opt_want_hexdump = 1; } + +int opt_want_help = 0; +static void cb_help(const char *arg _unused_) +{ opt_want_help = 1; } +static void cb_version(const char *arg _unused_) +{ opt_want_help = -1; } + +/* --- */ + +struct cmdline_arg { + const char *name, *arg_name; /* NULL arg_name means unary */ + void (*callback)(const char *arg); + int num_seen; +}; + +static struct cmdline_arg cmdline_args[] = { + {"-i", "interface", cb_interface, 0}, + {"-r", "file", cb_capfile, 0}, + {"-p", "port", cb_port, 0}, + {"-b", "bindaddr", cb_bindaddr, -1}, + {"-f", "filter", cb_filter, 0}, + {"-l", "network/netmask", cb_local, 0}, + {"--local-only", NULL, cb_local_only, 0}, + {"--snaplen", "bytes", cb_snaplen, 0}, + {"--pppoe", NULL, cb_pppoe, 0}, + {"--syslog", NULL, cb_syslog, 0}, + {"--verbose", NULL, cb_verbose, 0}, + {"--no-daemon", NULL, cb_no_daemon, 0}, + {"--no-promisc", NULL, cb_no_promisc, 0}, + {"--no-dns", NULL, cb_no_dns, 0}, + {"--no-macs", NULL, cb_no_macs, 0}, + {"--no-lastseen", NULL, cb_no_lastseen, 0}, + {"--chroot", "dir", cb_chroot, 0}, + {"--user", "username", cb_user, 0}, + {"--daylog", "filename", cb_daylog, 0}, + {"--import", "filename", cb_import, 0}, + {"--export", "filename", cb_export, 0}, + {"--pidfile", "filename", cb_pidfile, 0}, + {"--hosts-max", "count", cb_hosts_max, 0}, + {"--hosts-keep", "count", cb_hosts_keep, 0}, + {"--ports-max", "count", cb_ports_max, 0}, + {"--ports-keep", "count", cb_ports_keep, 0}, + {"--highest-port", "port", cb_highest_port, 0}, + {"--wait", "secs", cb_wait_secs, 0}, + {"--hexdump", NULL, cb_hexdump, 0}, + {"--version", NULL, cb_version, 0}, + {"--help", NULL, cb_help, 0}, + {NULL, NULL, NULL, 0} +}; + +/* + * We autogenerate the usage statement from the cmdline_args data structure. + */ +static void +usage(void) +{ + static char intro[] = "usage: darkstat "; + char indent[sizeof(intro)]; + struct cmdline_arg *arg; + + printf(PACKAGE_STRING " (using %s)\n", pcap_lib_version()); + if (opt_want_help == -1) return; + + memset(indent, ' ', sizeof(indent)); + indent[0] = indent[sizeof(indent) - 1] = 0; + + printf("\n%s", intro); + for (arg = cmdline_args; arg->name != NULL; arg++) { + printf("%s[ %s%s%s ]\n", + indent, + arg->name, + arg->arg_name != NULL ? " " : "", + arg->arg_name != NULL ? arg->arg_name : ""); + indent[0] = ' '; + } + printf("\n" +"Please refer to the darkstat(8) manual page for further\n" +"documentation and usage examples.\n"); +} + +static void +parse_sub_cmdline(const int argc, char * const *argv) +{ + struct cmdline_arg *arg; + + if (argc == 0) return; + for (arg = cmdline_args; arg->name != NULL; arg++) + if (strcmp(argv[0], arg->name) == 0) { + if ((arg->arg_name != NULL) && (argc == 1)) { + fprintf(stderr, + "error: argument \"%s\" requires parameter \"%s\"\n", + arg->name, arg->arg_name); + usage(); + exit(EXIT_FAILURE); + } + if (arg->num_seen > 0) { + fprintf(stderr, + "error: already specified argument \"%s\"\n", + arg->name); + usage(); + exit(EXIT_FAILURE); + } + + if (arg->num_seen != -1) /* accept more than one */ + arg->num_seen++; + + if (arg->arg_name == NULL) { + arg->callback(NULL); + parse_sub_cmdline(argc-1, argv+1); + } else { + arg->callback(argv[1]); + parse_sub_cmdline(argc-2, argv+2); + } + return; + } + + fprintf(stderr, "error: illegal argument: \"%s\"\n", argv[0]); + usage(); + exit(EXIT_FAILURE); +} + +static void +parse_cmdline(const int argc, char * const *argv) +{ + if (argc < 1) { + /* Not enough args. */ + usage(); + exit(EXIT_FAILURE); + } + + parse_sub_cmdline(argc, argv); + + if (opt_want_help) { + usage(); + exit(EXIT_SUCCESS); + } + + /* start syslogging as early as possible */ + if (opt_want_syslog) openlog("darkstat", LOG_NDELAY | LOG_PID, LOG_DAEMON); + + /* some default values */ + if (opt_chroot_dir == NULL) opt_chroot_dir = CHROOT_DIR; + if (opt_privdrop_user == NULL) opt_privdrop_user = PRIVDROP_USER; + + /* sanity check args */ + if ((opt_interface == NULL) && (opt_capfile == NULL)) + errx(1, "must specify either interface (-i) or capture file (-r)"); + + if ((opt_interface != NULL) && (opt_capfile != NULL)) + errx(1, "can't specify both interface (-i) and capture file (-r)"); + + if ((opt_hosts_max != 0) && (opt_hosts_keep >= opt_hosts_max)) { + opt_hosts_keep = opt_hosts_max / 2; + warnx("reducing --hosts-keep to %u, to be under --hosts-max (%u)", + opt_hosts_keep, opt_hosts_max); + } + verbosef("max %u hosts, cutting down to %u when exceeded", + opt_hosts_max, opt_hosts_keep); + + if ((opt_ports_max != 0) && (opt_ports_keep >= opt_ports_max)) { + opt_ports_keep = opt_ports_max / 2; + warnx("reducing --ports-keep to %u, to be under --ports-max (%u)", + opt_ports_keep, opt_ports_max); + } + verbosef("max %u ports per host, cutting down to %u when exceeded", + opt_ports_max, opt_ports_keep); + + if (opt_want_hexdump && !opt_want_verbose) { + opt_want_verbose = 1; + verbosef("--hexdump implies --verbose"); + } + + if (opt_want_hexdump && opt_want_daemonize) { + opt_want_daemonize = 0; + verbosef("--hexdump implies --no-daemon"); + } + + if (opt_want_local_only && !is_localnet_specified) + verbosef("WARNING: --local-only without -l only matches the local host"); +} + +static void +run_from_capfile(void) +{ + graph_init(); + hosts_db_init(); + cap_from_file(opt_capfile, opt_filter); + cap_stop(); + if (export_fn != NULL) db_export(export_fn); + hosts_db_free(); + graph_free(); +#ifndef PRIu64 +#warning "PRIu64 is not defined, using qu instead" +#define PRIu64 "qu" +#endif + verbosef("Total packets: %"PRIu64", bytes: %"PRIu64, + acct_total_packets, acct_total_bytes); +} + +/* --- Program body --- */ +int +main(int argc, char **argv) +{ + test_64order(); + parse_cmdline(argc-1, argv+1); + + if (opt_capfile) { + /* + * This is very different from a regular run against a network + * interface. + */ + run_from_capfile(); + return 0; + } + + /* must verbosef() before first fork to init lock */ + verbosef("starting up"); + if (pid_fn) pidfile_create(opt_chroot_dir, pid_fn, opt_privdrop_user); + + if (opt_want_daemonize) { + verbosef("daemonizing to run in the background!"); + daemonize_start(); + verbosef("I am the main process"); + } + if (pid_fn) pidfile_write_close(); + + /* do this first as it forks - minimize memory use */ + if (opt_want_dns) dns_init(opt_privdrop_user); + cap_init(opt_interface, opt_filter, opt_want_promisc); /* needs root */ + http_listen(opt_bindport); + ncache_init(); /* must do before chroot() */ + + privdrop(opt_chroot_dir, opt_privdrop_user); + + /* Don't need root privs for these: */ + now = time(NULL); + if (daylog_fn != NULL) daylog_init(daylog_fn); + graph_init(); + hosts_db_init(); + if (import_fn != NULL) db_import(import_fn); + localip_init(opt_interface); + + if (signal(SIGTERM, sig_shutdown) == SIG_ERR) + errx(1, "signal(SIGTERM) failed"); + if (signal(SIGINT, sig_shutdown) == SIG_ERR) + errx(1, "signal(SIGINT) failed"); + if (signal(SIGUSR1, sig_reset) == SIG_ERR) + errx(1, "signal(SIGUSR1) failed"); + if (signal(SIGUSR2, sig_export) == SIG_ERR) + errx(1, "signal(SIGUSR2) failed"); + + verbosef("entering main loop"); + daemonize_finish(); + + while (running) { + int select_ret, max_fd = -1, use_timeout = 0; + struct timeval timeout; + fd_set rs, ws; + + now = time(NULL); + + if (export_pending) { + if (export_fn != NULL) + db_export(export_fn); + export_pending = 0; + } + + if (reset_pending) { + if (export_pending) + continue; /* export before reset */ + hosts_db_reset(); + graph_reset(); + reset_pending = 0; + } + + FD_ZERO(&rs); + FD_ZERO(&ws); + + cap_fd_set(&rs, &max_fd, &timeout, &use_timeout); + http_fd_set(&rs, &ws, &max_fd, &timeout, &use_timeout); + + select_ret = select(max_fd+1, &rs, &ws, NULL, + (use_timeout) ? &timeout : NULL); + + if ((select_ret == 0) && (!use_timeout)) + errx(1, "select() erroneously timed out"); + + if (select_ret == -1) { + if (errno == EINTR) + continue; + else + err(1, "select()"); + } + else { + graph_rotate(); + cap_poll(&rs); + dns_poll(); + http_poll(&rs, &ws); + } + } + + verbosef("shutting down"); + verbosef("pcap stats: %u packets received, %u packets dropped", + cap_pkts_recv, cap_pkts_drop); + http_stop(); + cap_stop(); + dns_stop(); + if (export_fn != NULL) db_export(export_fn); + hosts_db_free(); + graph_free(); + if (daylog_fn != NULL) daylog_free(); + ncache_free(); + if (pid_fn) pidfile_unlink(); + verbosef("shut down"); + return (EXIT_SUCCESS); +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/daylog.c b/daylog.c new file mode 100644 index 0000000..a0b517e --- /dev/null +++ b/daylog.c @@ -0,0 +1,162 @@ +/* darkstat 3 + * copyright (c) 2007-2011 Emil Mikulic. + * + * daylog.c: daily usage log + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */ + +#include +#include +#include +#include +#include +#include + +#include "err.h" +#include "daylog.h" +#include "str.h" +#include "now.h" + +static const char *daylog_fn = NULL; +static time_t today_time, tomorrow_time; +static uint64_t bytes_in, bytes_out, pkts_in, pkts_out; + +#define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */ +static char datebuf[DAYLOG_DATE_LEN]; + +static char * +fmt_date(const time_t when) +{ + time_t tmp = when; + if (strftime(datebuf, DAYLOG_DATE_LEN, + "%Y-%m-%d %H:%M:%S %z", localtime(&tmp) ) == 0) + errx(1, "strftime() failed in fmt_date()"); + return (datebuf); +} + +/* Given some time today, find the first second of tomorrow. */ +static time_t +tomorrow(const time_t today) +{ + time_t tmp = today; + struct tm tm, *lt; + + lt = localtime(&tmp); + memcpy(&tm, lt, sizeof(tm)); + tm.tm_sec = 0; + tm.tm_min = 0; + tm.tm_hour = 0; + tm.tm_mday = lt->tm_mday + 1; /* tomorrow */ + return mktime(&tm); +} + +static int +daylog_open(void) +{ + return open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600); +} + +static void +daylog_emit(void) +{ + int fd = daylog_open(); + + if (fd != -1) { + struct str *buf = str_make(); + char *s; + size_t len; + str_appendf(buf, "%s|%u|%qu|%qu|%qu|%qu\n", + fmt_date(today_time), (unsigned int)today_time, + bytes_in, bytes_out, pkts_in, pkts_out); + str_extract(buf, &len, &s); + + (void)write(fd, s, len); /* ignore write errors */ + close(fd); + free(s); + } +} + +void +daylog_init(const char *filename) +{ + int fd; + struct str *buf; + char *s; + size_t len; + + daylog_fn = filename; + today_time = time(NULL); + tomorrow_time = tomorrow(today_time); + verbosef("today is %u, tomorrow is %u", + (unsigned int)today_time, (unsigned int)tomorrow_time); + bytes_in = bytes_out = pkts_in = pkts_out = 0; + + fd = daylog_open(); + if (fd == -1) + err(1, "couldn't open(\"%s\") for append", filename); + + buf = str_make(); + str_appendf(buf, "# logging started at %s (%u)\n", + fmt_date(today_time), (unsigned int)today_time); + str_extract(buf, &len, &s); + (void)write(fd, s, len); /* ignore write errors */ + close(fd); + free(s); +} + +void daylog_free(void) +{ + int fd; + struct str *buf; + char *s; + size_t len; + + today_time = time(NULL); + + /* Emit what's currently accumulated. */ + daylog_emit(); + + fd = daylog_open(); + if (fd == -1) return; + + buf = str_make(); + str_appendf(buf, "# logging stopped at %s (%u)\n", + fmt_date(today_time), (unsigned int)today_time); + str_extract(buf, &len, &s); + (void)write(fd, s, len); /* ignore write errors */ + close(fd); + free(s); +} + +void +daylog_acct(uint64_t amount, enum graph_dir dir) +{ + if (daylog_fn == NULL) return; /* disabled */ + + /* Check if we need to rotate. */ + if (now >= tomorrow_time) { + daylog_emit(); + + today_time = now; + tomorrow_time = tomorrow(today_time); + bytes_in = bytes_out = pkts_in = pkts_out = 0; + verbosef("rotated daylog, tomorrow = %u", + (unsigned int)tomorrow_time); + } + + /* Accounting. */ + if (dir == GRAPH_IN) { + bytes_in += amount; + pkts_in++; + } else { + assert(dir == GRAPH_OUT); + bytes_out += amount; + pkts_out++; + } +} + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/daylog.h b/daylog.h new file mode 100644 index 0000000..5f67a49 --- /dev/null +++ b/daylog.h @@ -0,0 +1,13 @@ +/* darkstat 3 + * copyright (c) 2007 Emil Mikulic. + * + * daylog.h: daily usage log + */ + +#include "graph_db.h" /* for graph_dir */ + +void daylog_init(const char *filename); +void daylog_free(void); +void daylog_acct(uint64_t amount, enum graph_dir dir); + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/db.c b/db.c new file mode 100644 index 0000000..5a82d90 --- /dev/null +++ b/db.c @@ -0,0 +1,369 @@ +/* darkstat 3 + * + * db.c: load and save in-memory database from/to file + * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#define _GNU_SOURCE 1 /* for O_NOFOLLOW in Linux */ + +#include +#include /* for ntohs() and friends */ +#include +#include +#include +#include + +#include "cdefs.h" +#include "err.h" +#include "hosts_db.h" +#include "graph_db.h" +#include "db.h" + +static const unsigned char export_file_header[] = {0xDA, 0x31, 0x41, 0x59}; +static const unsigned char export_tag_hosts_ver1[] = {0xDA, 'H', 'S', 0x01}; +static const unsigned char export_tag_graph_ver1[] = {0xDA, 'G', 'R', 0x01}; + +#ifndef swap64 +static inline uint64_t +swap64(uint64_t _x) +{ + /* this is __bswap64 from: + * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$ + */ + return ((_x >> 56) | ((_x >> 40) & 0xff00) | ((_x >> 24) & 0xff0000) | + ((_x >> 8) & 0xff000000) | ((_x << 8) & ((uint64_t)0xff << 32)) | + ((_x << 24) & ((uint64_t)0xff << 40)) | + ((_x << 40) & ((uint64_t)0xff << 48)) | ((_x << 56))); +} +#endif + +uint64_t +hton64(const uint64_t ho) +{ + if (ntohs(0x1234) == 0x1234) return ho; + else return swap64(ho); +} + +uint64_t +ntoh64(const uint64_t no) +{ + return hton64(no); +} + +void +test_64order(void) +{ + static const char str[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a }; + uint64_t no, ho; + + assert(sizeof(no) == 8); + memcpy(&no, str, 8); + ho = ntoh64(no); + assert(ho == 8751735851613054314ULL); + assert(hton64(ntoh64(no)) == no); +} + +/* --------------------------------------------------------------------------- + * Read-from-file helpers. They all return 0 on failure, and 1 on success. + */ + +unsigned int +xtell(const int fd) +{ + off_t ofs = lseek(fd, 0, SEEK_CUR); + if (ofs == -1) + err(1, "lseek(0, SEEK_CUR) failed"); + return (unsigned int)ofs; +} + +/* Read bytes from , warn() and return 0 on failure, + * or return 1 for success. + */ +int +readn(const int fd, void *dest, const size_t len) +{ + ssize_t numread; + + numread = read(fd, dest, len); + if (numread == (ssize_t)len) return 1; + + if (numread == -1) + warn("at pos %u: couldn't read %d bytes", xtell(fd), (int)len); + else + warnx("at pos %u: tried to read %d bytes, got %d", + xtell(fd), (int)len, (int)numread); + return 0; +} + +/* Read a byte. */ +int +read8(const int fd, uint8_t *dest) +{ + assert(sizeof(*dest) == 1); + return readn(fd, dest, sizeof(*dest)); +} + +/* Read a byte and compare it to the expected data. + * Returns 0 on failure or mismatch, 1 on success. + */ +int +expect8(const int fd, uint8_t expecting) +{ + uint8_t tmp; + + assert(sizeof(tmp) == 1); + if (!readn(fd, &tmp, sizeof(tmp))) return 0; + if (tmp == expecting) return 1; + + warnx("at pos %u: expecting 0x%02x, got 0x%02x", + xtell(fd)-1, expecting, tmp); + return 0; +} + +/* Read a network order uint16_t from a file + * and store it in host order in memory. + */ +int +read16(const int fd, uint16_t *dest) +{ + uint16_t tmp; + + assert(sizeof(tmp) == 2); + if (!read(fd, &tmp, sizeof(tmp))) return 0; + *dest = ntohs(tmp); + return 1; +} + +/* Read a network order uint32_t from a file + * and store it in host order in memory. + */ +int +read32(const int fd, uint32_t *dest) +{ + uint32_t tmp; + + assert(sizeof(tmp) == 4); + if (!read(fd, &tmp, sizeof(tmp))) return 0; + *dest = ntohl(tmp); + return 1; +} + +/* Read an IPv4 addr from a file. This is for backward compatibility with + * host records version 1 and 2. + */ +int +readaddr_ipv4(const int fd, struct addr *dest) +{ + dest->family = IPv4; + return readn(fd, &(dest->ip.v4), sizeof(dest->ip.v4)); +} + +/* Read a struct addr from a file. Addresses are always stored in network + * order, both in the file and in the host's memory (FIXME: is that right?) + */ +int +readaddr(const int fd, struct addr *dest) +{ + unsigned char family; + + if (!read8(fd, &family)) + return 0; + + if (family == 4) { + dest->family = IPv4; + return readn(fd, &(dest->ip.v4), sizeof(dest->ip.v4)); + } + else if (family == 6) { + dest->family = IPv6; + return readn(fd, dest->ip.v6.s6_addr, sizeof(dest->ip.v6.s6_addr)); + } + else + return 0; /* no address family I ever heard of */ +} + +/* Read a network order uint64_t from a file + * and store it in host order in memory. + */ +int +read64(const int fd, uint64_t *dest) +{ + uint64_t tmp; + + assert(sizeof(tmp) == 8); + if (!read(fd, &tmp, sizeof(tmp))) return 0; + *dest = ntoh64(tmp); + return 1; +} + +/* --------------------------------------------------------------------------- + * Write-to-file helpers. They all return 0 on failure, and 1 on success. + */ + +/* Write bytes to , warn() and return 0 on failure, + * or return 1 for success. + */ +int +writen(const int fd, const void *dest, const size_t len) +{ + ssize_t numwr; + + numwr = write(fd, dest, len); + if (numwr == (ssize_t)len) return 1; + + if (numwr == -1) + warn("couldn't write %d bytes", (int)len); + else + warnx("tried to write %d bytes but wrote %d", + (int)len, (int)numwr); + return 0; +} + +int +write8(const int fd, const uint8_t i) +{ + assert(sizeof(i) == 1); + return writen(fd, &i, sizeof(i)); +} + +/* Given a uint16_t in host order, write it to a file in network order. + */ +int +write16(const int fd, const uint16_t i) +{ + uint16_t tmp = htons(i); + assert(sizeof(tmp) == 2); + return writen(fd, &tmp, sizeof(tmp)); +} + +/* Given a uint32_t in host order, write it to a file in network order. + */ +int +write32(const int fd, const uint32_t i) +{ + uint32_t tmp = htonl(i); + assert(sizeof(tmp) == 4); + return writen(fd, &tmp, sizeof(tmp)); +} + +/* Given a uint64_t in host order, write it to a file in network order. + */ +int +write64(const int fd, const uint64_t i) +{ + uint64_t tmp = hton64(i); + assert(sizeof(tmp) == 8); + return writen(fd, &tmp, sizeof(tmp)); +} + + +/* Write the active address part in a struct addr to a file. + * Addresses are always stored in network order, both in the file and + * in the host's memory (FIXME: is that right?) + */ +int +writeaddr(const int fd, const struct addr *const a) +{ + if (!write8(fd, a->family)) + return 0; + + if (a->family == IPv4) + return writen(fd, &(a->ip.v4), sizeof(a->ip.v4)); + else { + assert(a->family == IPv6); + return writen(fd, a->ip.v6.s6_addr, sizeof(a->ip.v6.s6_addr)); + } +} + +/* --------------------------------------------------------------------------- + * db import/export code follows. + */ + +/* Check that the global file header is correct / supported. */ +int +read_file_header(const int fd, const uint8_t expected[4]) +{ + uint8_t got[4]; + + if (!readn(fd, got, sizeof(got))) return 0; + + /* Check the header data */ + if (memcmp(got, expected, sizeof(got)) != 0) { + warnx("bad header: " + "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x", + expected[0], expected[1], expected[2], expected[3], + got[0], got[1], got[2], got[3]); + return 0; + } + return 1; +} + +/* Returns 0 on failure, 1 on success. */ +static int +db_import_from_fd(const int fd) +{ + if (!read_file_header(fd, export_file_header)) return 0; + if (!read_file_header(fd, export_tag_hosts_ver1)) return 0; + if (!hosts_db_import(fd)) return 0; + if (!read_file_header(fd, export_tag_graph_ver1)) return 0; + if (!graph_import(fd)) return 0; + return 1; +} + +void +db_import(const char *filename) +{ + int fd = open(filename, O_RDONLY | O_NOFOLLOW); + if (fd == -1) { + warn("can't import from \"%s\"", filename); + return; + } + if (!db_import_from_fd(fd)) { + warnx("import failed"); + /* don't stay in an inconsistent state: */ + hosts_db_reset(); + graph_reset(); + } + close(fd); +} + +/* Returns 0 on failure, 1 on success. */ +static int +db_export_to_fd(const int fd) +{ + if (!writen(fd, export_file_header, sizeof(export_file_header))) + return 0; + if (!writen(fd, export_tag_hosts_ver1, sizeof(export_tag_hosts_ver1))) + return 0; + if (!hosts_db_export(fd)) + return 0; + if (!writen(fd, export_tag_graph_ver1, sizeof(export_tag_graph_ver1))) + return 0; + if (!graph_export(fd)) + return 0; + return 1; +} + +void +db_export(const char *filename) +{ + int fd = open(filename, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, 0600); + if (fd == -1) { + warn("can't export to \"%s\"", filename); + return; + } + verbosef("exporting db to file \"%s\"", filename); + if (!db_export_to_fd(fd)) + warnx("export failed"); + else + verbosef("export successful"); + + /* FIXME: should write to another filename and use the rename() syscall to + * atomically update the output file on success + */ + close(fd); +} + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/db.h b/db.h new file mode 100644 index 0000000..5f2eba6 --- /dev/null +++ b/db.h @@ -0,0 +1,40 @@ +/* darkstat 3 + * + * db.h: load and save in-memory database from/to file + * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic. + */ + +#include /* for size_t */ +#include /* for uint64_t */ + +struct addr; + +void db_import(const char *filename); +void db_export(const char *filename); + +/* byteswap */ +uint64_t hton64(const uint64_t ho); +uint64_t ntoh64(const uint64_t no); +void test_64order(void); + +/* read helpers */ +unsigned int xtell(const int fd); +int readn(const int fd, void *dest, const size_t len); +int read8(const int fd, uint8_t *dest); +int expect8(const int fd, uint8_t expecting); +int read16(const int fd, uint16_t *dest); +int read32(const int fd, uint32_t *dest); +int read64(const int fd, uint64_t *dest); +int readaddr_ipv4(const int fd, struct addr *dest); +int readaddr(const int fd, struct addr *dest); +int read_file_header(const int fd, const uint8_t expected[4]); + +/* write helpers */ +int writen(const int fd, const void *dest, const size_t len); +int write8(const int fd, const uint8_t i); +int write16(const int fd, const uint16_t i); +int write32(const int fd, const uint32_t i); +int write64(const int fd, const uint64_t i); +int writeaddr(const int fd, const struct addr *const a); + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/decode.c b/decode.c new file mode 100644 index 0000000..def8c29 --- /dev/null +++ b/decode.c @@ -0,0 +1,472 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * decode.c: packet decoding. + * + * Given a captured packet, decode it and fill out a pktsummary struct which + * will be sent to the accounting code in acct.c + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "cdefs.h" +#include "acct.h" +#include "cap.h" +#include "config.h" +#include "decode.h" +#include "err.h" +#include "opt.h" + +#include +#include +#include +#include +#include +#include +#include /* inet_ntoa() */ + +/* need struct ether_header */ +#if defined(__NetBSD__) || defined(__OpenBSD__) +# include +# include +# include +# include +#else +# ifdef __sun +# include +# define ETHER_HDR_LEN 14 +# else +# ifdef _AIX +# include +# define ETHER_HDR_LEN 14 +# else +# include +# endif +# endif +#endif +#ifndef ETHERTYPE_PPPOE +#define ETHERTYPE_PPPOE 0x8864 +#endif + +#ifndef ETHERTYPE_IPV6 +# ifdef HAVE_NET_IF_ETHER_H +# include /* ETH_P_IPV6 for GNU/kfreebsd */ +# endif +# ifdef ETH_P_IPV6 +# define ETHERTYPE_IPV6 ETH_P_IPV6 +# endif +#endif + +#include /* struct ifreq */ +#include /* n_long */ +#include /* struct ip */ +#include /* struct ip6_hdr */ +#define __FAVOR_BSD +#include /* struct tcphdr */ +#include /* struct udphdr */ + +#ifndef IPV6_VERSION +#define IPV6_VERSION 0x60 +#endif + +#ifndef IPV6_VERSION_MASK +#define IPV6_VERSION_MASK 0xf0 +#endif + +static void decode_ether(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_loop(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_null(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_ppp(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_pppoe(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_pppoe_real(const u_char *pdata, const uint32_t len, + struct pktsummary *sm); +static void decode_linux_sll(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_raw(u_char *, const struct pcap_pkthdr *, + const u_char *); +static void decode_ip(const u_char *pdata, const uint32_t len, + struct pktsummary *sm); +static void decode_ipv6(const u_char *pdata, const uint32_t len, + struct pktsummary *sm); + +/* Link-type header information */ +static const struct linkhdr linkhdrs[] = { + /* linktype hdrlen handler */ + { DLT_EN10MB, ETHER_HDR_LEN, decode_ether }, + { DLT_LOOP, NULL_HDR_LEN, decode_loop }, + { DLT_NULL, NULL_HDR_LEN, decode_null }, + { DLT_PPP, PPP_HDR_LEN, decode_ppp }, +#if defined(__NetBSD__) + { DLT_PPP_SERIAL, PPP_HDR_LEN, decode_ppp }, +#endif + { DLT_FDDI, FDDI_HDR_LEN, NULL }, + { DLT_PPP_ETHER, PPPOE_HDR_LEN, decode_pppoe }, +#ifdef DLT_LINUX_SLL + { DLT_LINUX_SLL, SLL_HDR_LEN, decode_linux_sll }, +#endif + { DLT_RAW, RAW_HDR_LEN, decode_raw }, + { -1, 0, NULL } +}; + +/* + * Returns a pointer to the linkhdr record matching the given linktype, or + * NULL if no matching entry found. + */ +const struct linkhdr * +getlinkhdr(const int linktype) +{ + size_t i; + + for (i=0; linkhdrs[i].linktype != -1; i++) + if (linkhdrs[i].linktype == linktype) + return (&(linkhdrs[i])); + return (NULL); +} + +/* + * Returns the minimum snaplen needed to decode everything up to the TCP/UDP + * packet headers. The IPv6 header is normative. The argument lh is not + * allowed to be NULL. + */ +int +getsnaplen(const struct linkhdr *lh) +{ + return (int)(lh->hdrlen + IPV6_HDR_LEN + MAX(TCP_HDR_LEN, UDP_HDR_LEN)); +} + +/* Decoding functions. */ +static void +decode_ether(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + u_short type; + const struct ether_header *hdr = (const struct ether_header *)pdata; + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + sm.time = pheader->ts.tv_sec; + + if (pheader->caplen < ETHER_HDR_LEN) { + verbosef("ether: packet too short (%u bytes)", pheader->caplen); + return; + } + +#ifdef __sun + memcpy(sm.src_mac, hdr->ether_shost.ether_addr_octet, sizeof(sm.src_mac)); + memcpy(sm.dst_mac, hdr->ether_dhost.ether_addr_octet, sizeof(sm.dst_mac)); +#else + memcpy(sm.src_mac, hdr->ether_shost, sizeof(sm.src_mac)); + memcpy(sm.dst_mac, hdr->ether_dhost, sizeof(sm.dst_mac)); +#endif + + type = ntohs( hdr->ether_type ); + switch (type) { + case ETHERTYPE_IP: + case ETHERTYPE_IPV6: + if (!opt_want_pppoe) { + decode_ip(pdata + ETHER_HDR_LEN, + pheader->caplen - ETHER_HDR_LEN, &sm); + acct_for(&sm); + } else + verbosef("ether: discarded IP packet, expecting PPPoE instead"); + break; + case ETHERTYPE_ARP: + /* known protocol, don't complain about it. */ + break; + case ETHERTYPE_PPPOE: + if (opt_want_pppoe) + decode_pppoe_real(pdata + ETHER_HDR_LEN, + pheader->caplen - ETHER_HDR_LEN, &sm); + else + verbosef("ether: got PPPoE frame: maybe you want --pppoe"); + break; + default: + verbosef("ether: unknown protocol (0x%04x)", type); + } +} + +/* Very similar to decode_null, except on OpenBSD we need to think + * about family endianness. + */ +static void +decode_loop(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + uint32_t family; + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + + if (pheader->caplen < NULL_HDR_LEN) { + verbosef("loop: packet too short (%u bytes)", pheader->caplen); + return; + } + family = *(const uint32_t *)pdata; +#ifdef __OpenBSD__ + family = ntohl(family); +#endif + if (family == AF_INET) { + decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); + } + else if (family == AF_INET6) { + decode_ipv6(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); + } + else + verbosef("loop: unknown family (%x)", family); +} + +static void +decode_null(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + uint32_t family; + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + + if (pheader->caplen < NULL_HDR_LEN) { + verbosef("null: packet too short (%u bytes)", pheader->caplen); + return; + } + family = *(const uint32_t *)pdata; + if (family == AF_INET) { + decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); + } + else if (family == AF_INET6) { + decode_ipv6(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); + } + else + verbosef("null: unknown family (%x)", family); +} + +static void +decode_ppp(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + + if (pheader->caplen < PPPOE_HDR_LEN) { + verbosef("ppp: packet too short (%u bytes)", pheader->caplen); + return; + } + + if (pdata[2] == 0x00 && pdata[3] == 0x21) { + decode_ip(pdata + PPP_HDR_LEN, pheader->caplen - PPP_HDR_LEN, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); + } else + verbosef("non-IP PPP packet; ignoring."); +} + +static void +decode_pppoe(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + sm.time = pheader->ts.tv_sec; + decode_pppoe_real(pdata, pheader->caplen, &sm); +} + +static void +decode_pppoe_real(const u_char *pdata, const uint32_t len, + struct pktsummary *sm) +{ + if (len < PPPOE_HDR_LEN) { + verbosef("pppoe: packet too short (%u bytes)", len); + return; + } + + if (pdata[1] != 0x00) { + verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]); + return; + } + + if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return; /* LCP */ + if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return; /* LQR */ + + if ((pdata[6] == 0x00) && (pdata[7] == 0x21)) { + decode_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm); + acct_for(sm); + } else + verbosef("pppoe: non-IP PPPoE packet (0x%02x%02x); ignoring.", + pdata[6], pdata[7]); +} + +/* very similar to decode_ether ... */ +static void +decode_linux_sll(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + const struct sll_header { + uint16_t packet_type; + uint16_t device_type; + uint16_t addr_length; +#define SLL_MAX_ADDRLEN 8 + uint8_t addr[SLL_MAX_ADDRLEN]; + uint16_t ether_type; + } *hdr = (const struct sll_header *)pdata; + u_short type; + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + + if (pheader->caplen < SLL_HDR_LEN) { + verbosef("linux_sll: packet too short (%u bytes)", pheader->caplen); + return; + } + + type = ntohs( hdr->ether_type ); + switch (type) { + case ETHERTYPE_IP: + case ETHERTYPE_IPV6: + decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); + break; + case ETHERTYPE_ARP: + /* known protocol, don't complain about it. */ + break; + default: + verbosef("linux_sll: unknown protocol (%04x)", type); + } +} + +static void +decode_raw(u_char *user _unused_, + const struct pcap_pkthdr *pheader, + const u_char *pdata) +{ + struct pktsummary sm; + memset(&sm, 0, sizeof(sm)); + + decode_ip(pdata, pheader->caplen, &sm); + sm.time = pheader->ts.tv_sec; + acct_for(&sm); +} + +static void decode_ip_payload(const u_char *pdata, const uint32_t len, + struct pktsummary *sm); + +static void +decode_ip(const u_char *pdata, const uint32_t len, struct pktsummary *sm) +{ + const struct ip *hdr = (const struct ip *)pdata; + + if (hdr->ip_v == 6) { + /* Redirect parsing of IPv6 packets. */ + decode_ipv6(pdata, len, sm); + return; + } + if (len < IP_HDR_LEN) { + verbosef("ip: packet too short (%u bytes)", len); + return; + } + if (hdr->ip_v != 4) { + verbosef("ip: version %d (expecting 4 or 6)", hdr->ip_v); + return; + } + + sm->len = ntohs(hdr->ip_len); + sm->proto = hdr->ip_p; + + sm->src.family = IPv4; + sm->src.ip.v4 = hdr->ip_src.s_addr; + + sm->dst.family = IPv4; + sm->dst.ip.v4 = hdr->ip_dst.s_addr; + + decode_ip_payload(pdata + IP_HDR_LEN, len - IP_HDR_LEN, sm); +} + +static void +decode_ipv6(const u_char *pdata, const uint32_t len, struct pktsummary *sm) +{ + const struct ip6_hdr *hdr = (const struct ip6_hdr *)pdata; + + if (len < IPV6_HDR_LEN) { + verbosef("ipv6: packet too short (%u bytes)", len); + return; + } + + if ((hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + verbosef("ipv6: bad version (%02x, expecting %02x)", + hdr->ip6_vfc & IPV6_VERSION_MASK, + IPV6_VERSION); + return; + } + + sm->len = ntohs(hdr->ip6_plen) + IPV6_HDR_LEN; + sm->proto = hdr->ip6_nxt; + + sm->src.family = IPv6; + memcpy(&sm->src.ip.v6, &hdr->ip6_src, sizeof(sm->src.ip.v6)); + + sm->dst.family = IPv6; + memcpy(&sm->dst.ip.v6, &hdr->ip6_dst, sizeof(sm->dst.ip.v6)); + + decode_ip_payload(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm); +} + +static void +decode_ip_payload(const u_char *pdata, const uint32_t len, + struct pktsummary *sm) +{ + switch (sm->proto) { + case IPPROTO_TCP: { + const struct tcphdr *thdr = (const struct tcphdr *)pdata; + if (len < TCP_HDR_LEN) { + verbosef("tcp: packet too short (%u bytes)", len); + sm->proto = IPPROTO_INVALID; /* don't do accounting! */ + return; + } + sm->src_port = ntohs(thdr->th_sport); + sm->dst_port = ntohs(thdr->th_dport); + sm->tcp_flags = thdr->th_flags & + (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG); + break; + } + + case IPPROTO_UDP: { + const struct udphdr *uhdr = (const struct udphdr *)pdata; + if (len < UDP_HDR_LEN) { + verbosef("udp: packet too short (%u bytes)", len); + sm->proto = IPPROTO_INVALID; /* don't do accounting! */ + return; + } + sm->src_port = ntohs(uhdr->uh_sport); + sm->dst_port = ntohs(uhdr->uh_dport); + break; + } + + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_OSPF: + /* known protocol, don't complain about it */ + break; + + default: + verbosef("ip: unknown protocol %d", sm->proto); + } +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/decode.h b/decode.h new file mode 100644 index 0000000..35cb42a --- /dev/null +++ b/decode.h @@ -0,0 +1,63 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * decode.h: packet decoding. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ +#ifndef __DARKSTAT_DECODE_H +#define __DARKSTAT_DECODE_H + +#include +#include /* n_time */ +#define __USE_GNU 1 +#include /* for */ +#include /* struct ip */ + +#include "addr.h" + +#define PPP_HDR_LEN 4 +#define FDDI_HDR_LEN 21 +#define IP_HDR_LEN sizeof(struct ip) +#define IPV6_HDR_LEN sizeof(struct ip6_hdr) +#define TCP_HDR_LEN sizeof(struct tcphdr) +#define UDP_HDR_LEN sizeof(struct udphdr) +#define NULL_HDR_LEN 4 +#define PPPOE_HDR_LEN 8 +#define SLL_HDR_LEN 16 +#define RAW_HDR_LEN 0 + +#ifndef ETHER_ADDR_LEN +#define ETHER_ADDR_LEN 6 +#endif + +#ifndef IPPROTO_OSPF +#define IPPROTO_OSPF 89 +#endif + +#define IPPROTO_INVALID 254 /* don't do proto accounting */ + +struct linkhdr { + int linktype; + unsigned int hdrlen; + pcap_handler handler; +}; + +const struct linkhdr *getlinkhdr(const int linktype); +int getsnaplen(const struct linkhdr *lh); + +struct pktsummary { + /* Fields are in host byte order (except IPs) */ + struct addr src, dst; + time_t time; + uint16_t len; + uint8_t proto; /* IPPROTO_{TCP, UDP, ICMP} */ + uint8_t tcp_flags; /* only for TCP */ + uint16_t src_port, dst_port; /* only for TCP, UDP */ + uint8_t src_mac[ETHER_ADDR_LEN], + dst_mac[ETHER_ADDR_LEN]; /* only for Ethernet */ +}; + +#endif /* __DARKSTAT_DECODE_H */ +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/dns.c b/dns.c new file mode 100644 index 0000000..e8bdbb4 --- /dev/null +++ b/dns.c @@ -0,0 +1,398 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * dns.c: synchronous DNS in a child process. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "cdefs.h" +#include "conv.h" +#include "decode.h" +#include "dns.h" +#include "err.h" +#include "hosts_db.h" +#include "queue.h" +#include "str.h" +#include "tree.h" +#include "bsd.h" /* for setproctitle, strlcpy */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void dns_main(void) _noreturn_; /* the child process runs this */ + +#define CHILD 0 /* child process uses this socket */ +#define PARENT 1 +static int sock[2]; +static pid_t pid = -1; + +struct dns_reply { + struct addr addr; + int error; /* for gai_strerror(), or 0 if no error */ + char name[256]; /* http://tools.ietf.org/html/rfc1034#section-3.1 */ +}; + +void +dns_init(const char *privdrop_user) +{ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1) + err(1, "socketpair"); + + pid = fork(); + if (pid == -1) + err(1, "fork"); + + if (pid == 0) { + /* We are the child. */ + privdrop(NULL /* don't chroot */, privdrop_user); + close(sock[PARENT]); + sock[PARENT] = -1; + daemonize_finish(); /* drop our copy of the lifeline! */ + if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) + errx(1, "signal(SIGUSR1, ignore) failed"); + dns_main(); + verbosef("fell out of dns_main()"); + exit(0); + } else { + /* We are the parent. */ + close(sock[CHILD]); + sock[CHILD] = -1; + fd_set_nonblock(sock[PARENT]); + verbosef("DNS child has PID %d", pid); + } +} + +void +dns_stop(void) +{ + if (pid == -1) + return; /* no child was started */ + close(sock[PARENT]); + if (kill(pid, SIGINT) == -1) + err(1, "kill"); + verbosef("dns_stop() waiting for child"); + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid"); + verbosef("dns_stop() done waiting for child"); +} + +struct tree_rec { + RB_ENTRY(tree_rec) ptree; + struct addr ip; +}; + +static int +tree_cmp(struct tree_rec *a, struct tree_rec *b) +{ + if (a->ip.family != b->ip.family) + /* Sort IPv4 to the left of IPv6. */ + return ((a->ip.family == IPv4) ? -1 : +1); + + if (a->ip.family == IPv4) + return (memcmp(&a->ip.ip.v4, &b->ip.ip.v4, sizeof(a->ip.ip.v4))); + else { + assert(a->ip.family == IPv6); + return (memcmp(&a->ip.ip.v6, &b->ip.ip.v6, sizeof(a->ip.ip.v6))); + } +} + +static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec); +/* Quiet warnings. */ +static struct tree_rec * tree_t_RB_NEXT(struct tree_rec *elm) + _unused_; +static struct tree_rec * tree_t_RB_MINMAX(struct tree_t *head, int val) + _unused_; +RB_GENERATE(tree_t, tree_rec, ptree, tree_cmp) + +void +dns_queue(const struct addr *const ipaddr) +{ + struct tree_rec *rec; + ssize_t num_w; + + if (pid == -1) + return; /* no child was started - we're not doing any DNS */ + + if ((ipaddr->family != IPv4) && (ipaddr->family != IPv6)) { + verbosef("dns_queue() for unknown family %d", ipaddr->family); + return; + } + + rec = xmalloc(sizeof(*rec)); + memcpy(&rec->ip, ipaddr, sizeof(rec->ip)); + + if (RB_INSERT(tree_t, &ip_tree, rec) != NULL) { + /* Already queued - this happens seldom enough that we don't care about + * the performance hit of needlessly malloc()ing. */ + verbosef("already queued %s", addr_to_str(ipaddr)); + free(rec); + return; + } + + num_w = write(sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */ + if (num_w == 0) + warnx("dns_queue: write: ignoring end of file"); + else if (num_w == -1) + warn("dns_queue: ignoring write error"); + else if (num_w != sizeof(*ipaddr)) + err(1, "dns_queue: wrote %zu instead of %zu", num_w, sizeof(*ipaddr)); +} + +static void +dns_unqueue(const struct addr *const ipaddr) +{ + struct tree_rec tmp, *rec; + + memcpy(&tmp.ip, ipaddr, sizeof(tmp.ip)); + if ((rec = RB_FIND(tree_t, &ip_tree, &tmp)) != NULL) { + RB_REMOVE(tree_t, &ip_tree, rec); + free(rec); + } + else + verbosef("couldn't unqueue %s - not in queue!", addr_to_str(ipaddr)); +} + +/* + * Returns non-zero if result waiting, stores IP and name into given pointers + * (name buffer is allocated by dns_poll) + */ +static int +dns_get_result(struct addr *ipaddr, char **name) +{ + struct dns_reply reply; + ssize_t numread; + + numread = read(sock[PARENT], &reply, sizeof(reply)); + if (numread == -1) { + if (errno == EAGAIN) + return (0); /* no input waiting */ + else + goto error; + } + if (numread == 0) + goto error; /* EOF */ + if (numread != sizeof(reply)) + errx(1, "dns_get_result read got %zu, expected %zu", + numread, sizeof(reply)); + + /* Return successful reply. */ + memcpy(ipaddr, &reply.addr, sizeof(*ipaddr)); + if (reply.error != 0) { + /* Identify common special cases. */ + const char *type = "none"; + + if (reply.addr.family == IPv6) { + if (IN6_IS_ADDR_LINKLOCAL(&reply.addr.ip.v6)) + type = "link-local"; + else if (IN6_IS_ADDR_SITELOCAL(&reply.addr.ip.v6)) + type = "site-local"; + else if (IN6_IS_ADDR_MULTICAST(&reply.addr.ip.v6)) + type = "multicast"; + } else { + assert(reply.addr.family == IPv4); + if (IN_MULTICAST(htonl(reply.addr.ip.v4))) + type = "multicast"; + } + xasprintf(name, "(%s)", type); + } + else /* Correctly resolved name. */ + *name = xstrdup(reply.name); + + dns_unqueue(&reply.addr); + return (1); + +error: + warn("dns_get_result: ignoring read error"); + /* FIXME: re-align to stream? restart dns child? */ + return (0); +} + +void +dns_poll(void) +{ + struct addr ip; + char *name; + + if (pid == -1) + return; /* no child was started - we're not doing any DNS */ + + while (dns_get_result(&ip, &name)) { + /* push into hosts_db */ + struct bucket *b = host_find(&ip); + + if (b == NULL) { + verbosef("resolved %s to %s but it's not in the DB!", + addr_to_str(&ip), name); + return; + } + if (b->u.host.dns != NULL) { + verbosef("resolved %s to %s but it's already in the DB!", + addr_to_str(&ip), name); + return; + } + b->u.host.dns = name; + } +} + +/* ------------------------------------------------------------------------ */ + +struct qitem { + STAILQ_ENTRY(qitem) entries; + struct addr ip; +}; + +STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue); + +static void +enqueue(const struct addr *const ip) +{ + struct qitem *i; + + i = xmalloc(sizeof(*i)); + memcpy(&i->ip, ip, sizeof(i->ip)); + STAILQ_INSERT_TAIL(&queue, i, entries); + verbosef("DNS: enqueued %s", addr_to_str(ip)); +} + +/* Return non-zero and populate pointer if queue isn't empty. */ +static int +dequeue(struct addr *ip) +{ + struct qitem *i; + + i = STAILQ_FIRST(&queue); + if (i == NULL) + return (0); + STAILQ_REMOVE_HEAD(&queue, entries); + memcpy(ip, &i->ip, sizeof(*ip)); + free(i); + verbosef("DNS: dequeued %s", addr_to_str(ip)); + return 1; +} + +static void +xwrite(const int d, const void *buf, const size_t nbytes) +{ + ssize_t ret = write(d, buf, nbytes); + + if (ret == -1) + err(1, "write"); + if (ret != (ssize_t)nbytes) + err(1, "wrote %d bytes instead of all %d bytes", (int)ret, (int)nbytes); +} + +static void +dns_main(void) +{ + struct addr ip; + + setproctitle("DNS child"); + fd_set_nonblock(sock[CHILD]); + verbosef("DNS child entering main DNS loop"); + for (;;) { + int blocking; + + if (STAILQ_EMPTY(&queue)) { + blocking = 1; + fd_set_block(sock[CHILD]); + verbosef("entering blocking read loop"); + } else { + blocking = 0; + fd_set_nonblock(sock[CHILD]); + verbosef("non-blocking poll"); + } + for (;;) { + /* While we have input to process... */ + ssize_t numread = read(sock[CHILD], &ip, sizeof(ip)); + if (numread == 0) + exit(0); /* end of file, nothing more to do here. */ + if (numread == -1) { + if (!blocking && (errno == EAGAIN)) + break; /* ran out of input */ + /* else error */ + err(1, "DNS: read failed"); + } + if (numread != sizeof(ip)) + err(1, "DNS: read got %zu bytes, expecting %zu", + numread, sizeof(ip)); + enqueue(&ip); + if (blocking) { + /* After one blocking read, become non-blocking so that when we + * run out of input we fall through to queue processing. + */ + blocking = 0; + fd_set_nonblock(sock[CHILD]); + } + } + + /* Process queue. */ + if (dequeue(&ip)) { + struct dns_reply reply; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct hostent *he; + char host[NI_MAXHOST]; + int ret, flags; + + reply.addr = ip; + flags = NI_NAMEREQD; +# ifdef NI_IDN + flags |= NI_IDN; +# endif + switch (ip.family) { + case IPv4: + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ip.ip.v4; + ret = getnameinfo((struct sockaddr *) &sin, sizeof(sin), + host, sizeof(host), NULL, 0, flags); + if (ret == EAI_FAMILY) { + verbosef("getnameinfo error %s, trying gethostbyname", + gai_strerror(ret)); + he = gethostbyaddr(&sin.sin_addr.s_addr, + sizeof(sin.sin_addr.s_addr), sin.sin_family); + if (he == NULL) { + ret = EAI_FAIL; + verbosef("gethostbyname error %s", hstrerror(h_errno)); + } else { + ret = 0; + strlcpy(host, he->h_name, sizeof(host)); + } + } + break; + case IPv6: + sin6.sin6_family = AF_INET6; + memcpy(&sin6.sin6_addr, &ip.ip.v6, sizeof(sin6.sin6_addr)); + ret = getnameinfo((struct sockaddr *) &sin6, sizeof(sin6), + host, sizeof(host), NULL, 0, flags); + break; + default: + ret = EAI_FAMILY; + } + + if (ret != 0) { + reply.name[0] = '\0'; + reply.error = ret; + } else { + assert(sizeof(reply.name) > sizeof(char *)); /* not just a ptr */ + strlcpy(reply.name, host, sizeof(reply.name)); + reply.error = 0; + } + fd_set_block(sock[CHILD]); + xwrite(sock[CHILD], &reply, sizeof(reply)); + verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr), + (ret == 0) ? reply.name : gai_strerror(ret)); + } + } +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..eb933fd --- /dev/null +++ b/dns.h @@ -0,0 +1,17 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * dns.h: synchronous DNS in a child process. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +struct addr; + +void dns_init(const char *privdrop_user); +void dns_stop(void); +void dns_queue(const struct addr *const ipaddr); +void dns_poll(void); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/err.c b/err.c new file mode 100644 index 0000000..358e564 --- /dev/null +++ b/err.c @@ -0,0 +1,197 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * err.c: BSD-like err() and warn() functions + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cdefs.h" +#include "err.h" +#include "opt.h" +#include "pidfile.h" +#include "bsd.h" /* for strlcpy */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void to_syslog(const char *type, const int want_err, + const char *format, va_list va) _printflike_(3, 0); + +static void +to_syslog(const char *type, const int want_err, + const char *format, va_list va) +{ + char buf[512]; + size_t pos = 0; + int saved_errno = errno; + + if (type != NULL) { + strlcpy(buf, type, sizeof(buf)); + pos = strlen(buf); + } + vsnprintf(buf+pos, sizeof(buf)-pos, format, va); + if (want_err) { + strlcat(buf, ": ", sizeof(buf)); + strlcat(buf, strerror(saved_errno), sizeof(buf)); + } + syslog(LOG_DEBUG, "%s", buf); +} + +void +err(const int code, const char *format, ...) +{ + va_list va; + + va_start(va, format); + if (opt_want_syslog) + to_syslog("ERROR: ", 1, format, va); + else { + fprintf(stderr, "%5d: error: ", (int)getpid()); + vfprintf(stderr, format, va); + fprintf(stderr, ": %s\n", strerror(errno)); + } + va_end(va); + pidfile_unlink(); + exit(code); +} + +void +errx(const int code, const char *format, ...) +{ + va_list va; + + va_start(va, format); + if (opt_want_syslog) + to_syslog("ERROR: ", 0, format, va); + else { + fprintf(stderr, "%5d: error: ", (int)getpid()); + vfprintf(stderr, format, va); + fprintf(stderr, "\n"); + } + va_end(va); + pidfile_unlink(); + exit(code); +} + +void +warn(const char *format, ...) +{ + va_list va; + + va_start(va, format); + if (opt_want_syslog) + to_syslog("WARNING: ", 1, format, va); + else { + fprintf(stderr, "%5d: warning: ", (int)getpid()); + vfprintf(stderr, format, va); + fprintf(stderr, ": %s\n", strerror(errno)); + } + va_end(va); +} + +void +warnx(const char *format, ...) +{ + va_list va; + + va_start(va, format); + if (opt_want_syslog) + to_syslog("WARNING: ", 0, format, va); + else { + fprintf(stderr, "%5d: warning: ", (int)getpid()); + vfprintf(stderr, format, va); + fprintf(stderr, "\n"); + } + va_end(va); +} + +/* We interlock verbosef() between processes by using a pipe with a single + * byte in it. This pipe must be initialized before the first fork() in order + * to work. Then, verbosef() will block on a read() until it is able to + * retrieve the byte. After doing its business, it will put a byte back into + * the pipe. + * + * This is completely silly and largely unnecessary. + */ +static int inited = 0; +static int lockpipe[2]; + +static void unlock(void); + +static void +initlock(void) +{ + if (pipe(lockpipe) == -1) + err(1, "pipe(lockpipe)"); + inited = 1; + unlock(); +} + +static void +lock(void) +{ + char buf[1]; + + if (!inited) initlock(); + if (read(lockpipe[0], buf, 1) != 1) { + fprintf(stderr, "lock failed!\n"); + pidfile_unlink(); + exit(1); + } +} + +static void +unlock(void) +{ + char c = 0; + + if (write(lockpipe[1], &c, 1) != 1) { + fprintf(stderr, "unlock failed!\n"); + pidfile_unlink(); + exit(1); + } +} + +void +verbosef(const char *format, ...) +{ + va_list va; + + if (!opt_want_verbose) return; + va_start(va, format); + if (opt_want_syslog) + to_syslog(NULL, 0, format, va); + else { + lock(); + fprintf(stderr, "darkstat (%05d): ", (int)getpid()); + vfprintf(stderr, format, va); + fprintf(stderr, "\n"); + unlock(); + } + va_end(va); +} + +void +dverbosef(const char *format _unused_, ...) +{ + /* disabled / do-nothing verbosef */ +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/err.h b/err.h new file mode 100644 index 0000000..842595a --- /dev/null +++ b/err.h @@ -0,0 +1,32 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * err.h: BSD-like err() and warn() functions + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cdefs.h" + +void err(const int code, const char *format, ...) + _noreturn_ _printflike_(2, 3); +void errx(const int code, const char *format, ...) + _noreturn_ _printflike_(2, 3); + +void warn(const char *format, ...) _printflike_(1, 2); +void warnx(const char *format, ...) _printflike_(1, 2); + +void verbosef(const char *format, ...) _printflike_(1, 2); +void dverbosef(const char *format _unused_, ...); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/export-format.txt b/export-format.txt new file mode 100644 index 0000000..ca89f01 --- /dev/null +++ b/export-format.txt @@ -0,0 +1,53 @@ +The darkstat export format was designed by Ben Stewart. +Note that all integers are stored in network order (big-endian). + +FILE HEADER 0xDA314159 darkstat export format + SECTION HEADER 0xDA 'H' 'S' 0x01 hosts_db ver1 + HOST COUNT 0x00000001 1 host follows + For each host: + HOST HEADER 'H' 'S' 'T' 0x03 host ver3 + ADDRESS FAMILY 0x04 Either 4 or 6. + IPv4 ADDR 0x0A010101 IPv4 10.1.1.1 + or for 0x06: + IPv6 ADDR 0x0000 0000 0000 0000 0000 0000 0000 0001 + meaning IPv6 ::1 + MACADDR 0x001122334455 00:11:22:33:44:55 + LASTSEEN 0x0000000048000123 (time_t) 2008-04-12 00:24:03 UTC + HOSTNAME 0x09 "localhost" 9 is the string length + IN 0x0000000000123456 Bytes in: 1193046 + OUT 0x0000000000789ABC Bytes out: 7903932 + PROTOS DATA 'P' start ip proto data + IP PROTO COUNT 0x03 3 ip_proto entries + IP PROTO 0x06 tcp + IN 0x0000000000123456 Bytes in: 1193046 + OUT 0x0000000000789ABC Bytes out: 7903932 + IP PROTO 0x11 udp + IN 0x0000000000000444 Bytes in: 1092 + OUT 0x0000000000000555 Bytes out: 1365 + IP PROTO 0x01 icmp + IN 0x0000000000000001 Bytes in: 1 + OUT 0x0000000000000002 Bytes out: 2 + TCP DATA 'T' start tcp proto data + TCP PROTO COUNT 0x0001 1 tcp_proto entry + PORT 0x0050 http (port 80) + SYN COUNT 0x0000000000000003 SYNs: 3 + IN 0x0000000000000001 Bytes in: 1 + OUT 0x0000000000000002 Bytes out: 2 + UDP DATA 'U' start udp proto data + UDP PROTO COUNT 0x0001 1 udp_proto entry + PORT 0x0045 tftp (port 69) + IN 0x0000000000000001 Bytes in: 1 + OUT 0x0000000000000002 Bytes out: 2 + SECTION HEADER 0xDA 'G' 'R' 0x01 graph_db ver1 + LAST_TIME (time_t as 64-bit uint) + For each of 4 graphs: (60 seconds, 60 minutes, 24 hours, 31 days) + 8 bits - number of bars in this graph + 8 bits - index of last_time bar, in the range [0:n_bars) + For each bar: + 64 bits - bytes in + 64 bits - bytes out + +Host header version 1 is just version 2 without the lastseen time. + +Host header version 2 is just version 3 without the address family +byte (or the possibility of an IPv6 address). diff --git a/graph_db.c b/graph_db.c new file mode 100644 index 0000000..8c72102 --- /dev/null +++ b/graph_db.c @@ -0,0 +1,405 @@ +/* darkstat 3 + * copyright (c) 2006-2011 Emil Mikulic. + * + * graph_db.c: round robin database for graph data + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include + +#include "cap.h" +#include "conv.h" +#include "db.h" +#include "acct.h" +#include "err.h" +#include "str.h" +#include "html.h" +#include "graph_db.h" +#include "now.h" +#include "opt.h" + +#include +#include +#include /* for memcpy() */ + +#define GRAPH_WIDTH "320" +#define GRAPH_HEIGHT "200" + +struct graph { + uint64_t *in, *out; + unsigned int offset; /* i.e. seconds start at 0, days start at 1 */ + unsigned int pos, num_bars; + const char *unit; + unsigned int bar_secs; /* one bar represents seconds */ +}; + +static struct graph + graph_secs = {NULL, NULL, 0, 0, 60, "seconds", 1}, + graph_mins = {NULL, NULL, 0, 0, 60, "minutes", 60}, + graph_hrs = {NULL, NULL, 0, 0, 24, "hours", 3600}, + graph_days = {NULL, NULL, 1, 0, 31, "days", 86400}; + +static struct graph *graph_db[] = { + &graph_secs, &graph_mins, &graph_hrs, &graph_days +}; + +static unsigned int graph_db_size = sizeof(graph_db)/sizeof(*graph_db); + +static time_t start_time, last_time; + +void +graph_init(void) +{ + unsigned int i; + for (i=0; iin = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars); + graph_db[i]->out = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars); + } + start_time = time(NULL); + graph_reset(); +} + +static void +zero_graph(struct graph *g) +{ + memset(g->in, 0, sizeof(uint64_t) * g->num_bars); + memset(g->out, 0, sizeof(uint64_t) * g->num_bars); +} + +void +graph_reset(void) +{ + unsigned int i; + for (i=0; iin); + free(graph_db[i]->out); + } +} + +void +graph_acct(uint64_t amount, enum graph_dir dir) +{ + unsigned int i; + for (i=0; iin[ graph_db[i]->pos ] += amount; break; + case GRAPH_OUT: graph_db[i]->out[ graph_db[i]->pos ] += amount; break; + default: errx(1, "unknown graph_dir in graph_acct: %d", dir); + } +} + +/* Advance a graph: advance the pos, zeroing out bars as we move. */ +static void +advance(struct graph *g, const unsigned int pos) +{ + if (g->pos == pos) + return; /* didn't need to advance */ + do { + g->pos = (g->pos + 1) % g->num_bars; + g->in[g->pos] = g->out[g->pos] = 0; + } while (g->pos != pos); +} + +/* Rotate a graph: rotate all bars so that the bar at the current pos is moved + * to the newly given pos. This is non-destructive. */ +static void +rotate(struct graph *g, const unsigned int pos) +{ + uint64_t *tmp; + unsigned int i, ofs; + size_t size; + + if (pos == g->pos) + return; /* nothing to rotate */ + + size = sizeof(*tmp) * g->num_bars; + tmp = xmalloc(size); + ofs = g->num_bars + pos - g->pos; + + for (i=0; inum_bars; i++) + tmp[ (i+ofs) % g->num_bars ] = g->in[i]; + memcpy(g->in, tmp, size); + + for (i=0; inum_bars; i++) + tmp[ (i+ofs) % g->num_bars ] = g->out[i]; + memcpy(g->out, tmp, size); + + free(tmp); + assert(pos == ( (g->pos + ofs) % g->num_bars )); + g->pos = pos; +} + +static void +graph_resync(const time_t new_time) +{ + struct tm *tm; + /* + * If time went backwards, we assume that real time is continuous and that + * the time adjustment should only affect display. i.e., if we have: + * + * second 15: 12 bytes + * second 16: 345 bytes + * second 17: <-- current pos + * + * and time goes backwards to second 8, we will shift the graph around to + * get: + * + * second 6: 12 bytes + * second 7: 345 bytes + * second 8: <-- current pos + * + * Note that we don't make any corrections for time being stepped forward. + * We rely on graph advancement to happen at the correct real time to + * account for, for example, bandwidth used per day. + */ + assert(new_time < last_time); + + tm = localtime(&new_time); + if (tm->tm_sec == 60) + tm->tm_sec = 59; /* mis-handle leap seconds */ + + rotate(&graph_secs, tm->tm_sec); + rotate(&graph_mins, tm->tm_min); + rotate(&graph_hrs, tm->tm_hour); + rotate(&graph_days, tm->tm_mday - 1); + + last_time = new_time; +} + +void +graph_rotate(void) +{ + time_t t, td; + struct tm *tm; + unsigned int i; + + t = now; + + if (last_time == 0) { + verbosef("first rotate"); + last_time = t; + tm = localtime(&t); + if (tm->tm_sec == 60) + tm->tm_sec = 59; /* mis-handle leap seconds */ + + graph_secs.pos = tm->tm_sec; + graph_mins.pos = tm->tm_min; + graph_hrs.pos = tm->tm_hour; + graph_days.pos = tm->tm_mday - 1; + return; + } + + if (t == last_time) + return; /* superfluous rotate */ + + if (t < last_time) { + verbosef("time went backwards! (from %u to %u, offset is %d)", + (unsigned int)last_time, (unsigned int)t, (int)(t - last_time)); + graph_resync(t); + return; + } + + /* else, normal rotation */ + td = t - last_time; + last_time = t; + tm = localtime(&t); + if (tm->tm_sec == 60) + tm->tm_sec = 59; /* mis-handle leap seconds */ + + /* zero out graphs which have been completely rotated through */ + for (i=0; i= (int)(graph_db[i]->num_bars * graph_db[i]->bar_secs)) + zero_graph(graph_db[i]); + + /* advance the current position, zeroing up to it */ + advance(&graph_secs, tm->tm_sec); + advance(&graph_mins, tm->tm_min); + advance(&graph_hrs, tm->tm_hour); + advance(&graph_days, tm->tm_mday - 1); +} + +/* --------------------------------------------------------------------------- + * Database Import: Grab graphs from a file provided by the caller. + * + * This function will retrieve the data sans the header. We expect the caller + * to have validated the header of the segment, and left the file position at + * the start of the data. + */ +int +graph_import(const int fd) +{ + uint64_t last; + unsigned int i, j; + + if (!read64(fd, &last)) return 0; + last_time = (time_t)last; + + for (i=0; i= num_bars) { + warn("pos is %u, should be < num_bars which is %u", + (unsigned int)pos, (unsigned int)num_bars); + return 0; + } + + if (graph_db[i]->num_bars != num_bars) { + warn("num_bars is %u, expecting %u", + (unsigned int)num_bars, graph_db[i]->num_bars); + return 0; + } + + graph_db[i]->pos = pos; + for (j=0; jin[j]))) return 0; + if (!read64(fd, &(graph_db[i]->out[j]))) return 0; + } + } + + return 1; +} + +/* --------------------------------------------------------------------------- + * Database Export: Dump hosts_db into a file provided by the caller. + * The caller is responsible for writing out the header first. + */ +int +graph_export(const int fd) +{ + unsigned int i, j; + + if (!write64(fd, (uint64_t)last_time)) return 0; + for (i=0; inum_bars)) return 0; + if (!write8(fd, graph_db[i]->pos)) return 0; + + for (j=0; jnum_bars; j++) { + if (!write64(fd, graph_db[i]->in[j])) return 0; + if (!write64(fd, graph_db[i]->out[j])) return 0; + } + } + return 1; +} + +/* --------------------------------------------------------------------------- + * Web interface: front page! + */ +struct str * +html_front_page(void) +{ + struct str *buf, *rf; + unsigned int i; + char start_when[100]; + + buf = str_make(); + html_open(buf, "Graphs", /*path_depth=*/0, /*want_graph_js=*/1); + + str_append(buf, "

\n"); + str_append(buf, "Running for "); + rf = length_of_time(now - start_time); + /* FIXME: use a more monotonic clock perhaps? */ + str_appendstr(buf, rf); + str_free(rf); + str_append(buf, ""); + + if (strftime(start_when, sizeof(start_when), + "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_time)) != 0) + str_appendf(buf, ", since %s", start_when); + + str_appendf(buf,".
\n" + "Total %'qu bytes, " + "in %'qu packets. " + "(%'u captured, " + "%'u dropped)
\n" + "

\n", + acct_total_bytes, + acct_total_packets, + cap_pkts_recv, cap_pkts_drop); + + str_append(buf, + "
\n" + "Graphs require JavaScript.\n" + "\n" + "
\n" + ); + + html_close(buf); + return (buf); +} + +/* --------------------------------------------------------------------------- + * Web interface: graphs.xml + */ +struct str * +xml_graphs(void) +{ + unsigned int i, j; + struct str *buf = str_make(), *rf; + + str_appendf(buf, "\n"); + + for (i=0; i\n", g->unit); + j = g->pos; + do { + j = (j + 1) % g->num_bars; + /* */ + str_appendf(buf, "\n", + g->offset + j, g->in[j], g->out[j]); + } while (j != g->pos); + str_appendf(buf, "\n", g->unit); + } + str_append(buf, "\n"); + return (buf); +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/graph_db.h b/graph_db.h new file mode 100644 index 0000000..24db11b --- /dev/null +++ b/graph_db.h @@ -0,0 +1,30 @@ +/* darkstat 3 + * copyright (c) 2006-2011 Emil Mikulic. + * + * graph_db.h: round robin database for graph data + */ +#ifndef __DARKSTAT_GRAPH_DB_H +#define __DARKSTAT_GRAPH_DB_H + +#include /* for uint64_t on Linux and OS X */ + +enum graph_dir { + MIN_GRAPH_DIR = 1, + GRAPH_IN = 1, + GRAPH_OUT = 2, + MAX_GRAPH_DIR = 2 +}; + +void graph_init(void); +void graph_reset(void); +void graph_free(void); +void graph_acct(uint64_t amount, enum graph_dir dir); +void graph_rotate(void); +int graph_import(const int fd); +int graph_export(const int fd); + +struct str *html_front_page(void); +struct str *xml_graphs(void); + +#endif +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/graphjs.h b/graphjs.h new file mode 100644 index 0000000..4d9cee9 --- /dev/null +++ b/graphjs.h @@ -0,0 +1,283 @@ +/* this file was automatically generated */ +static char graph_js[] = +"/* darkstat 3\n" +" * copyright (c) 2006-2008 Emil Mikulic.\n" +" *\n" +" * graph.js: graph renderer\n" +" *\n" +" * You may use, modify and redistribute this file under the terms of the\n" +" * GNU General Public License version 2. (see COPYING.GPL)\n" +" *\n" +" * At some point, this script worked correctly in:\n" +" * - Firefox 1.5.0.4, 2.0.0.1, 3.0\n" +" * - IE 6.0\n" +" * - Opera 8.53, 9.50\n" +" * - Konqueror 3.5.9, 4.0.80, 4.0.83\n" +" *\n" +" * Consumer needs to supply the following variables:\n" +" * - graph_width\n" +" * - graph_height\n" +" * - bar_gap\n" +" *\n" +" * - graphs [ {id, name, title, bar_secs} ]\n" +" * - graphs_uri\n" +" *\n" +" * - window.onload = graphs_init\n" +" */\n" +"\n" +"function killChildren(elem) {\n" +" while (elem.childNodes.length > 0)\n" +" elem.removeChild( elem.childNodes.item(0) );\n" +"}\n" +"\n" +"function setClass(elem, c) {\n" +" elem.setAttribute(\"class\", c);\n" +" elem.setAttribute(\"className\", c); /* for MSIE */\n" +"}\n" +"\n" +"function setStyle(elem, s) {\n" +" elem.setAttribute(\"style\", s);\n" +" elem.style.cssText = s; /* for MSIE */\n" +"}\n" +"\n" +"function makeElemClass(e, c) {\n" +" var r = document.createElement(e);\n" +" setClass(r, c);\n" +" return r;\n" +"}\n" +"\n" +"function makeClear() {\n" +" var r = document.createElement(\"div\");\n" +" setStyle(r, \"clear:both\");\n" +" return r;\n" +"}\n" +"\n" +"function thousands(n) {\n" +" var s = String(n);\n" +" var out = \"\";\n" +" while (s.length > 3) {\n" +" out = \",\" + s.substr(s.length - 3, 3) + out;\n" +" s = s.substr(0, s.length - 3);\n" +" }\n" +" return s+out;\n" +"}\n" +"\n" +"function fkbps(bps) {\n" +" bps /= 1024;\n" +" return bps.toFixed(1);\n" +"}\n" +"\n" +"function kbps(bps) {\n" +" bps /= 1024;\n" +" if (bps < 1) return bps.toPrecision(2);\n" +" else return bps.toFixed(1);\n" +"}\n" +"\n" +"function min(a,b) { return (ab)?a:b; }\n" +"\n" +"var xh, autoreload=false;\n" +"\n" +"function graphs_init() {\n" +" var gr = document.getElementById(\"graphs\");\n" +"\n" +" /* update message */\n" +" var msg = document.createElement(\"div\");\n" +" msg.appendChild(document.createTextNode(\"Graphs are being loaded...\"));\n" +" msg.appendChild(document.createElement(\"br\"));\n" +" msg.appendChild(document.createElement(\"br\"));\n" +" killChildren(gr);\n" +" gr.appendChild(msg);\n" +" graphs.msg = msg;\n" +"\n" +" for (var i=0; i4G? */\n" +" if (b_total > total_max)\n" +" total_max = b_total;\n" +" data.push( [b_pos, b_in, b_out] );\n" +" }\n" +"\n" +" var igraph = makeElemClass(\"div\", \"graph\"); // inner graph\n" +" setStyle(igraph,\n" +" \"width:\"+graph_width+\"px; \"+\n" +" \"height:\"+graph_height+\"px; \"+\n" +" \"position:relative;\");\n" +"\n" +" var nbars = data.length;\n" +" var b_width = (graph_width - bar_gap * (nbars-1)) / nbars;\n" +" var next_xofs = 0;\n" +"\n" +" var min_i = 0, min_o = 0,\n" +" max_i = 0, max_o = 0,\n" +" tot_i = 0, tot_o = 0;\n" +"\n" +" for (var i=0; i0) { if (min_i == 0) min_i = b_i; else min_i = min(min_i, b_i); }\n" +" max_i = max(max_i, b_i);\n" +" tot_i += b_i;\n" +"\n" +" if (b_o>0) { if (min_o == 0) min_o = b_o; else min_o = min(min_o, b_o); }\n" +" max_o = max(max_o, b_o);\n" +" tot_o += b_o;\n" +"\n" +" var xofs = next_xofs;\n" +"\n" +" next_xofs = Math.round((b_width + bar_gap) * (i+1));\n" +" var curr_w = next_xofs - xofs - bar_gap;\n" +"\n" +" var h_i = Math.round( b_i * graph_height / total_max );\n" +" var h_o = Math.round( b_o * graph_height / total_max );\n" +"\n" +" var label = b_p+\": \"+\n" +" thousands(b_i)+\" bytes in, \"+\n" +" thousands(b_o)+\" bytes out | \"+\n" +" kbps(b_i/bar_secs)+\" KB/s in, \"+\n" +" kbps(b_o/bar_secs)+\" KB/s out\";\n" +"\n" +" addBar(igraph, label, \"bar_in\", curr_w, h_i, xofs, 0);\n" +" addBar(igraph, label, \"bar_out\", curr_w, h_o, xofs, h_i);\n" +" }\n" +"\n" +" function legendRow(dir_str, minb, avgb, maxb) {\n" +" function makeTD(c, str) {\n" +" var r = makeElemClass(\"td\", c);\n" +" r.appendChild(document.createTextNode(str));\n" +" return r;\n" +" }\n" +" function addToRow(row, type_str, bytes, trail) {\n" +" row.appendChild( makeTD(\"type\", type_str) );\n" +" row.appendChild( makeTD(\"rate\", fkbps(bytes/bar_secs)+\" KB/s\"+trail) );\n" +" }\n" +" var row = document.createElement(\"tr\");\n" +" row.appendChild( makeTD(\"dir\", dir_str) );\n" +" var cell = makeElemClass(\"td\", \"swatch\");\n" +" var swatch = makeElemClass(\"div\", \"bar_\"+dir_str);\n" +" setStyle(swatch, \"width:6px; height:6px;\");\n" +" cell.appendChild(swatch);\n" +" row.appendChild(cell);\n" +" addToRow(row, \"min:\", minb, \",\");\n" +" addToRow(row, \"avg:\", avgb, \",\");\n" +" addToRow(row, \"max:\", maxb, \"\");\n" +" return row;\n" +" }\n" +"\n" +" var glegend = makeElemClass(\"div\", \"legend\");\n" +" var avg_i = tot_i / nbars,\n" +" avg_o = tot_o / nbars;\n" +" var tbl = document.createElement(\"table\");\n" +" var tb = document.createElement(\"tbody\"); /* for MSIE */\n" +" tb.appendChild( legendRow(\"in\", min_i, avg_i, max_i) );\n" +" tb.appendChild( legendRow(\"out\", min_o, avg_o, max_o) );\n" +" tbl.appendChild(tb);\n" +" glegend.appendChild(tbl);\n" +" setStyle(glegend, \"width:\"+graph_width+\"px;\");\n" +"\n" +" var gtitle = makeElemClass(\"div\", \"graphtitle\");\n" +" setStyle(gtitle, \"width:\"+graph_width+\"px;\");\n" +" gtitle.appendChild(document.createTextNode(title));\n" +"\n" +" killChildren(graph);\n" +" graph.appendChild(igraph);\n" +" graph.appendChild(glegend);\n" +" graph.appendChild(gtitle);\n" +"}\n"; +static const size_t graph_js_len = sizeof(graph_js) - 1; diff --git a/hosts_db.c b/hosts_db.c new file mode 100644 index 0000000..754dacc --- /dev/null +++ b/hosts_db.c @@ -0,0 +1,1473 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * hosts_db.c: database of hosts, ports, protocols. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "cdefs.h" +#include "conv.h" +#include "decode.h" +#include "dns.h" +#include "err.h" +#include "hosts_db.h" +#include "db.h" +#include "html.h" +#include "ncache.h" +#include "now.h" +#include "opt.h" +#include "str.h" + +#include /* struct addrinfo */ +#include +#include +#include +#include +#include /* memset(), strcmp() */ +#include + +int hosts_db_show_macs = 0; + +/* FIXME: specify somewhere more sane/tunable */ +#define MAX_ENTRIES 30 /* in an HTML table rendered from a hashtable */ + +typedef uint32_t (hash_func_t)(const struct hashtable *, const void *); +typedef void (free_func_t)(struct bucket *); +typedef const void * (key_func_t)(const struct bucket *); +typedef int (find_func_t)(const struct bucket *, const void *); +typedef struct bucket * (make_func_t)(const void *); +typedef void (format_cols_func_t)(struct str *); +typedef void (format_row_func_t)(struct str *, const struct bucket *, + const char *); + +struct hashtable { + uint8_t bits; /* size of hashtable in bits */ + uint32_t size, mask; + uint32_t count, count_max, count_keep; /* items in table */ + uint32_t coeff; /* coefficient for Fibonacci hashing */ + struct bucket **table; + + struct { + uint64_t inserts, searches, deletions, rehashes; + } stats; + + hash_func_t *hash_func; + /* returns hash value of given key (passed as void*) */ + + free_func_t *free_func; + /* free of bucket payload */ + + key_func_t *key_func; + /* returns pointer to key of bucket (to pass to hash_func) */ + + find_func_t *find_func; + /* returns true if given bucket matches key (passed as void*) */ + + make_func_t *make_func; + /* returns bucket containing new record with key (passed as void*) */ + + format_cols_func_t *format_cols_func; + /* append table columns to str */ + + format_row_func_t *format_row_func; + /* format record and append to str */ +}; + +static void hashtable_reduce(struct hashtable *ht); +static void hashtable_free(struct hashtable *h); + +#define HOST_BITS 1 /* initial size of hosts table */ +#define PORT_BITS 1 /* initial size of ports tables */ +#define PROTO_BITS 1 /* initial size of proto table */ + +/* We only use one hosts_db hashtable and this is it. */ +static struct hashtable *hosts_db = NULL; + +/* phi^-1 (reciprocal of golden ratio) = (sqrt(5) - 1) / 2 */ +static const double phi_1 = + 0.61803398874989490252573887119069695472717285156250; + +/* Co-prime of u, using phi^-1 */ +inline static uint32_t +coprime(const uint32_t u) +{ + return ( (uint32_t)( (double)(u) * phi_1 ) | 1U ); +} + +/* + * This is the "recommended" IPv4 hash function, as seen in FreeBSD's + * src/sys/netinet/tcp_hostcache.c 1.1 + */ +inline static uint32_t +ipv4_hash(const struct addr *const a) +{ + uint32_t ip = a->ip.v4; + return ( (ip) ^ ((ip) >> 7) ^ ((ip) >> 17) ); +} + +#ifndef s6_addr32 +# ifdef sun +/* + * http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/netinet/in.h#130 + */ +# define s6_addr32 _S6_un._S6_u32 +# else +/* Covers OpenBSD and FreeBSD. The macro __USE_GNU has + * taken care of GNU/Linux and GNU/kfreebsd. */ +# define s6_addr32 __u6_addr.__u6_addr32 +# endif +#endif + +/* + * This is the IPv6 hash function used by FreeBSD in the same file as above, + * svn rev 122922. + */ +inline static uint32_t +ipv6_hash(const struct addr *const a) +{ + const struct in6_addr *const ip6 = &(a->ip.v6); + return ( ip6->s6_addr32[0] ^ ip6->s6_addr32[1] ^ + ip6->s6_addr32[2] ^ ip6->s6_addr32[3] ); +} + +/* --------------------------------------------------------------------------- + * hash_func collection + */ +static uint32_t +hash_func_host(const struct hashtable *h _unused_, const void *key) +{ + const struct addr *a = key; + if (a->family == IPv4) + return (ipv4_hash(a)); + else { + assert(a->family == IPv6); + return (ipv6_hash(a)); + } +} + +#define CASTKEY(type) (*((const type *)key)) + +static uint32_t +hash_func_short(const struct hashtable *h, const void *key) +{ + return (CASTKEY(uint16_t) * h->coeff); +} + +static uint32_t +hash_func_byte(const struct hashtable *h, const void *key) +{ + return (CASTKEY(uint8_t) * h->coeff); +} + +/* --------------------------------------------------------------------------- + * key_func collection + */ + +static const void * +key_func_host(const struct bucket *b) +{ + return &(b->u.host.addr); +} + +static const void * +key_func_port_tcp(const struct bucket *b) +{ + return &(b->u.port_tcp.port); +} + +static const void * +key_func_port_udp(const struct bucket *b) +{ + return &(b->u.port_udp.port); +} + +static const void * +key_func_ip_proto(const struct bucket *b) +{ + return &(b->u.ip_proto.proto); +} + +/* --------------------------------------------------------------------------- + * find_func collection + */ + +static int +find_func_host(const struct bucket *b, const void *key) +{ + return (addr_equal(key, &(b->u.host.addr))); +} + +static int +find_func_port_tcp(const struct bucket *b, const void *key) +{ + return (b->u.port_tcp.port == CASTKEY(uint16_t)); +} + +static int +find_func_port_udp(const struct bucket *b, const void *key) +{ + return (b->u.port_udp.port == CASTKEY(uint16_t)); +} + +static int +find_func_ip_proto(const struct bucket *b, const void *key) +{ + return (b->u.ip_proto.proto == CASTKEY(uint8_t)); +} + +/* --------------------------------------------------------------------------- + * make_func collection + */ + +#define MAKE_BUCKET(name_bucket, name_content, type) struct { \ + struct bucket *next; \ + uint64_t in, out, total; \ + union { struct type t; } u; } _custom_bucket; \ + struct bucket *name_bucket = xcalloc(1, sizeof(_custom_bucket)); \ + struct type *name_content = &(name_bucket->u.type); \ + name_bucket->next = NULL; \ + name_bucket->in = name_bucket->out = name_bucket->total = 0; + +static struct bucket * +make_func_host(const void *key) +{ + MAKE_BUCKET(b, h, host); + h->addr = CASTKEY(struct addr); + h->dns = NULL; + h->lastseen = 0; + memset(&h->mac_addr, 0, sizeof(h->mac_addr)); + h->ports_tcp = NULL; + h->ports_udp = NULL; + h->ip_protos = NULL; + return (b); +} + +static void +free_func_host(struct bucket *b) +{ + struct host *h = &(b->u.host); + if (h->dns != NULL) free(h->dns); + hashtable_free(h->ports_tcp); + hashtable_free(h->ports_udp); + hashtable_free(h->ip_protos); +} + +static struct bucket * +make_func_port_tcp(const void *key) +{ + MAKE_BUCKET(b, p, port_tcp); + p->port = CASTKEY(uint16_t); + p->syn = 0; + return (b); +} + +static struct bucket * +make_func_port_udp(const void *key) +{ + MAKE_BUCKET(b, p, port_udp); + p->port = CASTKEY(uint16_t); + return (b); +} + +static struct bucket * +make_func_ip_proto(const void *key) +{ + MAKE_BUCKET(b, p, ip_proto); + p->proto = CASTKEY(uint8_t); + return (b); +} + +static void +free_func_simple(struct bucket *b _unused_) +{ + /* nop */ +} + +/* --------------------------------------------------------------------------- + * format_func collection (ordered by struct) + */ + +static void +format_cols_host(struct str *buf) +{ + /* FIXME: don't clobber parts of the query string + * specifically "full" and "start" + * when setting sort direction + */ + str_append(buf, + "\n" + "\n" + " \n" + " \n"); + if (hosts_db_show_macs) str_append(buf, + " \n"); + str_append(buf, + " \n" + " \n" + " \n"); + if (opt_want_lastseen) str_append(buf, + " \n"); + str_append(buf, + "\n"); +} + +static void +format_row_host(struct str *buf, const struct bucket *b, + const char *css_class) +{ + const char *ip = addr_to_str(&(b->u.host.addr)); + + str_appendf(buf, + "\n" + " \n" + " \n", + css_class, + ip, ip, + (b->u.host.dns == NULL) ? "" : b->u.host.dns); + + if (hosts_db_show_macs) + str_appendf(buf, + " \n", + b->u.host.mac_addr[0], + b->u.host.mac_addr[1], + b->u.host.mac_addr[2], + b->u.host.mac_addr[3], + b->u.host.mac_addr[4], + b->u.host.mac_addr[5]); + + str_appendf(buf, + " \n" + " \n" + " \n", + b->in, b->out, b->total); + + if (opt_want_lastseen) { + time_t last_t = b->u.host.lastseen; + struct str *last_str = NULL; + + if ((now >= last_t) && (last_t > 0)) + last_str = length_of_time(now - last_t); + + str_append(buf, + " "); + } + + str_appendf(buf, + "\n"); + + /* Only resolve hosts "on demand" */ + if (b->u.host.dns == NULL) + dns_queue(&(b->u.host.addr)); +} + +static void +format_cols_port_tcp(struct str *buf) +{ + str_append(buf, + "
IPHostnameMAC AddressInOutTotalLast seen
%s%s%x:%x:%x:%x:%x:%x%'qu%'qu%'qu"); + if (last_str == NULL) { + if (last_t == 0) + str_append(buf, "(never)"); + else + str_append(buf, "(clock error)"); + } else { + str_appendstr(buf, last_str); + str_free(last_str); + } + str_append(buf, + "
\n" + "\n" + " \n" + ); +} + +static void +format_row_port_tcp(struct str *buf, const struct bucket *b, + const char *css_class) +{ + const struct port_tcp *p = &(b->u.port_tcp); + + str_appendf(buf, + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + css_class, + p->port, getservtcp(p->port), b->in, b->out, b->total, p->syn + ); +} + +static void +format_cols_port_udp(struct str *buf) +{ + str_append(buf, + "
Port\n" + " Service\n" + " In\n" + " Out\n" + " Total\n" + " SYNs\n" + "
%u%s%'qu%'qu%'qu%'qu
\n" + "\n" + " \n" + ); +} + +static void +format_row_port_udp(struct str *buf, const struct bucket *b, + const char *css_class) +{ + const struct port_udp *p = &(b->u.port_udp); + + str_appendf(buf, + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + css_class, + p->port, getservudp(p->port), b->in, b->out, b->total + ); +} + +static void +format_cols_ip_proto(struct str *buf) +{ + str_append(buf, + "
Port\n" + " Service\n" + " In\n" + " Out\n" + " Total\n" + "
%u%s%'qu%'qu%'qu
\n" + "\n" + " \n" + ); +} + +static void +format_row_ip_proto(struct str *buf, const struct bucket *b, + const char *css_class) +{ + const struct ip_proto *p = &(b->u.ip_proto); + + str_appendf(buf, + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n", + css_class, + p->proto, getproto(p->proto), + b->in, b->out, b->total + ); +} + +/* --------------------------------------------------------------------------- + * Initialise a hashtable. + */ +static struct hashtable * +hashtable_make(const uint8_t bits, + const unsigned int count_max, + const unsigned int count_keep, + hash_func_t *hash_func, + free_func_t *free_func, + key_func_t *key_func, + find_func_t *find_func, + make_func_t *make_func, + format_cols_func_t *format_cols_func, + format_row_func_t *format_row_func) +{ + struct hashtable *hash; + assert(bits > 0); + + hash = xmalloc(sizeof(*hash)); + hash->bits = bits; + hash->count_max = count_max; + hash->count_keep = count_keep; + hash->size = 1U << bits; + hash->mask = hash->size - 1; + hash->coeff = coprime(hash->size); + hash->hash_func = hash_func; + hash->free_func = free_func; + hash->key_func = key_func; + hash->find_func = find_func; + hash->make_func = make_func; + hash->format_cols_func = format_cols_func; + hash->format_row_func = format_row_func; + hash->count = 0; + hash->table = xcalloc(hash->size, sizeof(*hash->table)); + memset(&(hash->stats), 0, sizeof(hash->stats)); + return (hash); +} + +/* --------------------------------------------------------------------------- + * Initialise global hosts_db. + */ +void +hosts_db_init(void) +{ + assert(hosts_db == NULL); + hosts_db = hashtable_make(HOST_BITS, opt_hosts_max, opt_hosts_keep, + hash_func_host, free_func_host, key_func_host, find_func_host, + make_func_host, format_cols_host, format_row_host); +} + +static void +hashtable_rehash(struct hashtable *h, const uint8_t bits) +{ + struct bucket **old_table, **new_table; + uint32_t i, old_size; + assert(h != NULL); + assert(bits > 0); + + h->stats.rehashes++; + old_size = h->size; + old_table = h->table; + + h->bits = bits; + h->size = 1U << bits; + h->mask = h->size - 1; + h->coeff = coprime(h->size); + new_table = xcalloc(h->size, sizeof(*new_table)); + + for (i=0; ihash_func(h, h->key_func(b)) & h->mask; + next = b->next; + b->next = new_table[pos]; + new_table[pos] = b; + b = next; + } + } + + free(h->table); + h->table = new_table; +} + +static void +hashtable_insert(struct hashtable *h, struct bucket *b) +{ + uint32_t pos; + assert(h != NULL); + assert(b != NULL); + assert(b->next == NULL); + + /* Rehash on 80% occupancy */ + if ((h->count > h->size) || + ((h->size - h->count) < h->size / 5)) + hashtable_rehash(h, h->bits+1); + + pos = h->hash_func(h, h->key_func(b)) & h->mask; + if (h->table[pos] == NULL) + h->table[pos] = b; + else { + /* Insert at top of chain. */ + b->next = h->table[pos]; + h->table[pos] = b; + } + h->count++; + h->stats.inserts++; +} + +/* Return bucket matching key, or NULL if no such entry. */ +static struct bucket * +hashtable_search(struct hashtable *h, const void *key) +{ + uint32_t pos; + struct bucket *b; + + h->stats.searches++; + pos = h->hash_func(h, key) & h->mask; + b = h->table[pos]; + while (b != NULL) { + if (h->find_func(b, key)) + return (b); + else + b = b->next; + } + return (NULL); +} + +typedef enum { NO_REDUCE = 0, ALLOW_REDUCE = 1 } reduce_bool; +/* Search for a key. If it's not there, make and insert a bucket for it. */ +static struct bucket * +hashtable_find_or_insert(struct hashtable *h, const void *key, + const reduce_bool allow_reduce) +{ + struct bucket *b = hashtable_search(h, key); + + if (b == NULL) { + /* Not found, so insert after checking occupancy. */ + if (allow_reduce && (h->count >= h->count_max)) + hashtable_reduce(h); + b = h->make_func(key); + hashtable_insert(h, b); + } + return (b); +} + +/* + * Frees the hashtable and the buckets. The contents are assumed to be + * "simple" -- i.e. no "destructor" action is required beyond simply freeing + * the bucket. + */ +static void +hashtable_free(struct hashtable *h) +{ + uint32_t i; + + if (h == NULL) + return; + for (i=0; isize; i++) { + struct bucket *tmp, *b = h->table[i]; + while (b != NULL) { + tmp = b; + b = b->next; + h->free_func(tmp); + free(tmp); + } + } + free(h->table); + free(h); +} + +/* --------------------------------------------------------------------------- + * Return existing host or insert a new one. + */ +struct bucket * +host_get(const struct addr *const a) +{ + return (hashtable_find_or_insert(hosts_db, a, NO_REDUCE)); +} + +/* --------------------------------------------------------------------------- + * Find host, returns NULL if not in DB. + */ +struct bucket * +host_find(const struct addr *const a) +{ + return (hashtable_search(hosts_db, a)); +} + +/* --------------------------------------------------------------------------- + * Find host, returns NULL if not in DB. + */ +static struct bucket * +host_search(const char *ipstr) +{ + struct addr a; + struct addrinfo hints, *ai; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(ipstr, NULL, &hints, &ai)) + return (NULL); /* invalid addr */ + + if (ai->ai_family == AF_INET) { + a.family = IPv4; + a.ip.v4 = ((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr; + } + else if (ai->ai_family == AF_INET6) { + a.family = IPv6; + memcpy(&(a.ip.v6), + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, + sizeof(a.ip.v6)); + } else { + freeaddrinfo(ai); + return (NULL); /* unknown family */ + } + freeaddrinfo(ai); + + verbosef("search(%s) turned into %s", ipstr, addr_to_str(&a)); + return (hashtable_search(hosts_db, &a)); +} + +/* --------------------------------------------------------------------------- + * Reduce a hashtable to the top entries. + */ +static void +hashtable_reduce(struct hashtable *ht) +{ + uint32_t i, pos, rmd; + const struct bucket **table; + uint64_t cutoff; + + assert(ht->count_keep < ht->count); + + /* Fill table with pointers to buckets in hashtable. */ + table = xcalloc(ht->count, sizeof(*table)); + for (pos=0, i=0; isize; i++) { + struct bucket *b = ht->table[i]; + while (b != NULL) { + table[pos++] = b; + b = b->next; + } + } + assert(pos == ht->count); + qsort_buckets(table, ht->count, 0, ht->count_keep, TOTAL); + cutoff = table[ht->count_keep]->total; + free(table); + + /* Remove all elements with total <= cutoff. */ + rmd = 0; + for (i=0; isize; i++) { + struct bucket *last = NULL, *next, *b = ht->table[i]; + while (b != NULL) { + next = b->next; + if (b->total <= cutoff) { + /* Remove this one. */ + ht->free_func(b); + free(b); + if (last == NULL) + ht->table[i] = next; + else + last->next = next; + rmd++; + ht->count--; + } else { + last = b; + } + b = next; + } + } + verbosef("hashtable_reduce: removed %u buckets, left %u", + rmd, ht->count); + hashtable_rehash(ht, ht->bits); /* is this needed? */ +} + +/* Reduce hosts_db if needed. */ +void hosts_db_reduce(void) +{ + if (hosts_db->count >= hosts_db->count_max) + hashtable_reduce(hosts_db); +} + +/* --------------------------------------------------------------------------- + * Reset hosts_db to empty. + */ +void +hosts_db_reset(void) +{ + unsigned int i; + + for (i=0; isize; i++) { + struct bucket *next, *b = hosts_db->table[i]; + while (b != NULL) { + next = b->next; + hosts_db->free_func(b); + free(b); + b = next; + } + hosts_db->table[i] = NULL; + } + verbosef("hosts_db reset to empty, freed %u hosts", hosts_db->count); + hosts_db->count = 0; +} + +/* --------------------------------------------------------------------------- + * Deallocate hosts_db. + */ +void hosts_db_free(void) +{ + uint32_t i; + + assert(hosts_db != NULL); + for (i=0; isize; i++) { + struct bucket *tmp, *b = hosts_db->table[i]; + while (b != NULL) { + tmp = b; + b = b->next; + hosts_db->free_func(tmp); + free(tmp); + } + } + free(hosts_db->table); + free(hosts_db); + hosts_db = NULL; +} + +/* --------------------------------------------------------------------------- + * Find or create a port_tcp inside a host. + */ +struct bucket * +host_get_port_tcp(struct bucket *host, const uint16_t port) +{ + struct host *h = &host->u.host; + assert(h != NULL); + if (h->ports_tcp == NULL) + h->ports_tcp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep, + hash_func_short, free_func_simple, key_func_port_tcp, + find_func_port_tcp, make_func_port_tcp, + format_cols_port_tcp, format_row_port_tcp); + return (hashtable_find_or_insert(h->ports_tcp, &port, ALLOW_REDUCE)); +} + +/* --------------------------------------------------------------------------- + * Find or create a port_udp inside a host. + */ +struct bucket * +host_get_port_udp(struct bucket *host, const uint16_t port) +{ + struct host *h = &host->u.host; + assert(h != NULL); + if (h->ports_udp == NULL) + h->ports_udp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep, + hash_func_short, free_func_simple, key_func_port_udp, + find_func_port_udp, make_func_port_udp, + format_cols_port_udp, format_row_port_udp); + return (hashtable_find_or_insert(h->ports_udp, &port, ALLOW_REDUCE)); +} + +/* --------------------------------------------------------------------------- + * Find or create an ip_proto inside a host. + */ +struct bucket * +host_get_ip_proto(struct bucket *host, const uint8_t proto) +{ + struct host *h = &host->u.host; + static const unsigned int PROTOS_MAX = 512, PROTOS_KEEP = 256; + assert(h != NULL); + if (h->ip_protos == NULL) + h->ip_protos = hashtable_make(PROTO_BITS, PROTOS_MAX, PROTOS_KEEP, + hash_func_byte, free_func_simple, key_func_ip_proto, + find_func_ip_proto, make_func_ip_proto, + format_cols_ip_proto, format_row_ip_proto); + return (hashtable_find_or_insert(h->ip_protos, &proto, ALLOW_REDUCE)); +} + +static struct str *html_hosts_main(const char *qs); +static struct str *html_hosts_detail(const char *ip); + +/* --------------------------------------------------------------------------- + * Web interface: delegate the /hosts/ space. + */ +struct str * +html_hosts(const char *uri, const char *query) +{ + unsigned int i, num_elems; + char **elem = split('/', uri, &num_elems); + struct str *buf = NULL; + + assert(num_elems >= 1); + assert(strcmp(elem[0], "hosts") == 0); + + if (num_elems == 1) + /* /hosts/ */ + buf = html_hosts_main(query); + else if (num_elems == 2) + /* /hosts// */ + buf = html_hosts_detail(elem[1]); + + for (i=0; icount == 0)) { + str_append(buf, "

The table is empty.

\n"); + return; + } + + /* Fill table with pointers to buckets in hashtable. */ + table = xcalloc(ht->count, sizeof(*table)); + for (pos=0, i=0; isize; i++) { + struct bucket *b = ht->table[i]; + while (b != NULL) { + table[pos++] = b; + b = b->next; + } + } + assert(pos == ht->count); + + if (full) { + /* full report overrides start and end */ + start = 0; + end = ht->count; + } else + end = MIN(ht->count, (uint32_t)start+MAX_ENTRIES); + + str_appendf(buf, "(%u-%u of %u)
\n", start+1, end, ht->count); + qsort_buckets(table, ht->count, start, end, sort); + ht->format_cols_func(buf); + + for (i=start; iformat_row_func(buf, table[i], alt ? "alt1" : "alt2"); + alt = !alt; /* alternate class for table rows */ + } + free(table); + str_append(buf, "
#\n" + " Protocol\n" + " In\n" + " Out\n" + " Total\n" + "
%u%s%'qu%'qu%'qu
\n"); +} + +/* --------------------------------------------------------------------------- + * Web interface: sorted table of hosts. + */ +static struct str * +html_hosts_main(const char *qs) +{ + struct str *buf = str_make(); + char *qs_start, *qs_sort, *qs_full, *ep; + const char *sortstr; + int start, full = 0; + enum sort_dir sort; + + /* parse query string */ + qs_start = qs_get(qs, "start"); + qs_sort = qs_get(qs, "sort"); + qs_full = qs_get(qs, "full"); + if (qs_full != NULL) { + full = 1; + free(qs_full); + } + + /* validate sort */ + if (qs_sort == NULL) sort = TOTAL; + else if (strcmp(qs_sort, "total") == 0) sort = TOTAL; + else if (strcmp(qs_sort, "in") == 0) sort = IN; + else if (strcmp(qs_sort, "out") == 0) sort = OUT; + else if (strcmp(qs_sort, "lastseen") == 0) sort = LASTSEEN; + else { + str_append(buf, "Error: invalid value for \"sort\".\n"); + goto done; + } + + /* parse start */ + if (qs_start == NULL) + start = 0; + else { + start = (int)strtoul(qs_start, &ep, 10); + if (*ep != '\0') { + str_append(buf, "Error: \"start\" is not a number.\n"); + goto done; + } + if ((errno == ERANGE) || + (start < 0) || (start >= (int)hosts_db->count)) { + str_append(buf, "Error: \"start\" is out of bounds.\n"); + goto done; + } + } + +#define PREV "<<< prev page" +#define NEXT "next page >>>" +#define FULL "full table" + + html_open(buf, "Hosts", /*path_depth=*/1, /*want_graph_js=*/0); + format_table(buf, hosts_db, start, sort, full); + + /* */ + sortstr = qs_sort; + if (sortstr == NULL) sortstr = "total"; + if (start > 0) { + int prev = start - MAX_ENTRIES; + if (prev < 0) + prev = 0; + str_appendf(buf, "" PREV "", + prev, sortstr); + } else + str_append(buf, PREV); + + if (full) + str_append(buf, " | " FULL); + else + str_appendf(buf, " | " FULL "", + sortstr); + + if (start+MAX_ENTRIES < (int)hosts_db->count) + str_appendf(buf, " | " NEXT "", + start+MAX_ENTRIES, sortstr); + else + str_append(buf, " | " NEXT); + + str_append(buf, "
\n"); + + html_close(buf); +done: + if (qs_start != NULL) free(qs_start); + if (qs_sort != NULL) free(qs_sort); + return buf; +#undef PREV +#undef NEXT +#undef FULL +} + +/* --------------------------------------------------------------------------- + * Web interface: detailed view of a single host. + */ +static struct str * +html_hosts_detail(const char *ip) +{ + struct bucket *h; + struct str *buf, *ls_len; + char ls_when[100]; + const char *canonical; + time_t ls; + + h = host_search(ip); + if (h == NULL) + return (NULL); /* no such host */ + + canonical = addr_to_str(&(h->u.host.addr)); + + /* Overview. */ + buf = str_make(); + html_open(buf, ip, /*path_depth=*/2, /*want_graph_js=*/0); + if (strcmp(ip, canonical) != 0) + str_appendf(buf, "(canonically %s)\n", canonical); + str_appendf(buf, + "

\n" + "Hostname: %s
\n", + (h->u.host.dns == NULL)?"(resolving...)":h->u.host.dns); + + /* Resolve host "on demand" */ + if (h->u.host.dns == NULL) + dns_queue(&(h->u.host.addr)); + + if (hosts_db_show_macs) + str_appendf(buf, + "MAC Address: " + "%x:%x:%x:%x:%x:%x
\n", + h->u.host.mac_addr[0], + h->u.host.mac_addr[1], + h->u.host.mac_addr[2], + h->u.host.mac_addr[3], + h->u.host.mac_addr[4], + h->u.host.mac_addr[5]); + + str_append(buf, + "

\n" + "

\n" + "Last seen: "); + + ls = h->u.host.lastseen; + if (strftime(ls_when, sizeof(ls_when), + "%Y-%m-%d %H:%M:%S %Z%z", localtime(&ls)) != 0) + str_append(buf, ls_when); + + if (h->u.host.lastseen <= now) { + ls_len = length_of_time(now - h->u.host.lastseen); + str_append(buf, " ("); + str_appendstr(buf, ls_len); + str_free(ls_len); + str_append(buf, " ago)"); + } else { + str_append(buf, " (in the future, possible clock problem)"); + } + + str_appendf(buf, + "

\n" + "

\n" + " In: %'qu
\n" + " Out: %'qu
\n" + " Total: %'qu
\n" + "

\n", + h->in, h->out, h->total); + + str_append(buf, "

TCP ports

\n"); + format_table(buf, h->u.host.ports_tcp, 0,TOTAL,0); + + str_append(buf, "

UDP ports

\n"); + format_table(buf, h->u.host.ports_udp, 0,TOTAL,0); + + str_append(buf, "

IP protocols

\n"); + format_table(buf, h->u.host.ip_protos, 0,TOTAL,0); + + html_close(buf); + return (buf); +} + +/* --------------------------------------------------------------------------- + * Database import and export code: + * Initially written and contributed by Ben Stewart. + * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic. + */ +static int hosts_db_export_ip(const struct hashtable *h, const int fd); +static int hosts_db_export_tcp(const struct hashtable *h, const int fd); +static int hosts_db_export_udp(const struct hashtable *h, const int fd); + +static const char + export_proto_ip = 'P', + export_proto_tcp = 'T', + export_proto_udp = 'U'; + +static const unsigned char + export_tag_host_ver1[] = {'H', 'S', 'T', 0x01}, + export_tag_host_ver2[] = {'H', 'S', 'T', 0x02}, + export_tag_host_ver3[] = {'H', 'S', 'T', 0x03}; + +/* --------------------------------------------------------------------------- + * Load a host's ip_proto table from a file. + * Returns 0 on failure, 1 on success. + */ +static int +hosts_db_import_ip(const int fd, struct bucket *host) +{ + uint8_t count, i; + + if (!expect8(fd, export_proto_ip)) return 0; + if (!read8(fd, &count)) return 0; + + for (i=0; iin = in; + b->out = out; + b->total = in + out; + assert(b->u.ip_proto.proto == proto); /* should be done by make fn */ + } + return 1; +} + +/* --------------------------------------------------------------------------- + * Load a host's port_tcp table from a file. + * Returns 0 on failure, 1 on success. + */ +static int +hosts_db_import_tcp(const int fd, struct bucket *host) +{ + uint16_t count, i; + + if (!expect8(fd, export_proto_tcp)) return 0; + if (!read16(fd, &count)) return 0; + + for (i=0; iin = in; + b->out = out; + b->total = in + out; + assert(b->u.port_tcp.port == port); /* done by make_func_port_tcp */ + b->u.port_tcp.syn = syn; + } + return 1; +} + +/* --------------------------------------------------------------------------- + * Load a host's port_tcp table from a file. + * Returns 0 on failure, 1 on success. + */ +static int +hosts_db_import_udp(const int fd, struct bucket *host) +{ + uint16_t count, i; + + if (!expect8(fd, export_proto_udp)) return 0; + if (!read16(fd, &count)) return 0; + + for (i=0; iin = in; + b->out = out; + b->total = in + out; + assert(b->u.port_udp.port == port); /* done by make_func */ + } + return 1; +} + +/* --------------------------------------------------------------------------- + * Load all hosts from a file. + * Returns 0 on failure, 1 on success. + */ +static int +hosts_db_import_host(const int fd) +{ + struct bucket *host; + struct addr a; + uint8_t hostname_len; + uint64_t in, out; + unsigned int pos = xtell(fd); + char hdr[4]; + int ver = 0; + + if (!readn(fd, hdr, sizeof(hdr))) return 0; + if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0) + ver = 3; + else if (memcmp(hdr, export_tag_host_ver2, sizeof(hdr)) == 0) + ver = 2; + else if (memcmp(hdr, export_tag_host_ver1, sizeof(hdr)) == 0) + ver = 1; + else { + warnx("bad host header: %02x%02x%02x%02x", + hdr[0], hdr[1], hdr[2], hdr[3]); + return 0; + } + + if (ver == 3) { + if (!readaddr(fd, &a)) + return 0; + } else { + assert((ver == 1) || (ver == 2)); + if (!readaddr_ipv4(fd, &a)) + return 0; + } + verbosef("at file pos %u, importing host %s", pos, addr_to_str(&a)); + host = host_get(&a); + assert(addr_equal(&(host->u.host.addr), &a)); + + if (ver > 1) { + uint64_t t; + if (!read64(fd, &t)) return 0; + host->u.host.lastseen = (time_t)t; + } + + assert(sizeof(host->u.host.mac_addr) == 6); + if (!readn(fd, host->u.host.mac_addr, sizeof(host->u.host.mac_addr))) + return 0; + + /* HOSTNAME */ + assert(host->u.host.dns == NULL); /* make fn? */ + if (!read8(fd, &hostname_len)) return 0; + if (hostname_len > 0) { + host->u.host.dns = xmalloc(hostname_len + 1); + host->u.host.dns[0] = '\0'; + + /* At this point, the hostname is attached to a host which is in our + * hosts_db, so if we bail out due to an import error, this pointer + * isn't lost and leaked, it can be cleaned up in hosts_db_{free,reset} + */ + + if (!readn(fd, host->u.host.dns, hostname_len)) return 0; + host->u.host.dns[hostname_len] = '\0'; + } + + if (!read64(fd, &in)) return 0; + if (!read64(fd, &out)) return 0; + + host->in = in; + host->out = out; + host->total = in + out; + + /* Host's port and proto subtables: */ + if (!hosts_db_import_ip(fd, host)) return 0; + if (!hosts_db_import_tcp(fd, host)) return 0; + if (!hosts_db_import_udp(fd, host)) return 0; + return 1; +} + +/* --------------------------------------------------------------------------- + * Database Import: Grab hosts_db from a file provided by the caller. + * + * This function will retrieve the data sans the header. We expect the caller + * to have validated the header of the hosts_db segment, and left the file + * sitting at the start of the data. + */ +int hosts_db_import(const int fd) +{ + uint32_t host_count, i; + + if (!read32(fd, &host_count)) return 0; + + for (i=0; icount)) return 0; + + for (i = 0; isize; i++) + for (b = hosts_db->table[i]; b != NULL; b = b->next) { + /* For each host: */ + if (!writen(fd, export_tag_host_ver3, sizeof(export_tag_host_ver3))) + return 0; + + if (!writeaddr(fd, &(b->u.host.addr))) return 0; + + if (!write64(fd, (uint64_t)(b->u.host.lastseen))) return 0; + + assert(sizeof(b->u.host.mac_addr) == 6); + if (!writen(fd, b->u.host.mac_addr, sizeof(b->u.host.mac_addr))) + return 0; + + /* HOSTNAME */ + if (b->u.host.dns == NULL) { + if (!write8(fd, 0)) return 0; + } else { + int dnslen = strlen(b->u.host.dns); + + if (dnslen > 255) { + warnx("found a very long hostname: \"%s\"\n" + "wasn't expecting one longer than 255 chars (this one is %d)", + b->u.host.dns, dnslen); + dnslen = 255; + } + + if (!write8(fd, (uint8_t)dnslen)) return 0; + if (!writen(fd, b->u.host.dns, dnslen)) return 0; + } + + if (!write64(fd, b->in)) return 0; + if (!write64(fd, b->out)) return 0; + + if (!hosts_db_export_ip(b->u.host.ip_protos, fd)) return 0; + if (!hosts_db_export_tcp(b->u.host.ports_tcp, fd)) return 0; + if (!hosts_db_export_udp(b->u.host.ports_udp, fd)) return 0; + } + return 1; +} + +/* --------------------------------------------------------------------------- + * Dump the ip_proto table of a host. + */ +static int +hosts_db_export_ip(const struct hashtable *h, const int fd) +{ + uint32_t i, written = 0; + struct bucket *b; + + /* IP DATA */ + if (!write8(fd, export_proto_ip)) return 0; + + /* If no data, write a IP Proto count of 0 and we're done. */ + if (h == NULL) { + if (!write8(fd, 0)) return 0; + return 1; + } + + assert(h->count < 256); + if (!write8(fd, (uint8_t)h->count)) return 0; + + for (i = 0; isize; i++) + for (b = h->table[i]; b != NULL; b = b->next) { + /* For each ip_proto bucket: */ + + if (!write8(fd, b->u.ip_proto.proto)) return 0; + if (!write64(fd, b->in)) return 0; + if (!write64(fd, b->out)) return 0; + written++; + } + assert(written == h->count); + return 1; +} + +/* --------------------------------------------------------------------------- + * Dump the port_tcp table of a host. + */ +static int +hosts_db_export_tcp(const struct hashtable *h, const int fd) +{ + struct bucket *b; + uint32_t i, written = 0; + + /* TCP DATA */ + if (!write8(fd, export_proto_tcp)) return 0; + + /* If no data, write a count of 0 and we're done. */ + if (h == NULL) { + if (!write16(fd, 0)) return 0; + return 1; + } + + assert(h->count < 65536); + if (!write16(fd, (uint16_t)h->count)) return 0; + + for (i = 0; isize; i++) + for (b = h->table[i]; b != NULL; b = b->next) { + if (!write16(fd, b->u.port_tcp.port)) return 0; + if (!write64(fd, b->u.port_tcp.syn)) return 0; + if (!write64(fd, b->in)) return 0; + if (!write64(fd, b->out)) return 0; + written++; + } + assert(written == h->count); + return 1; +} + +/* --------------------------------------------------------------------------- + * Dump the port_udp table of a host. + */ +static int +hosts_db_export_udp(const struct hashtable *h, const int fd) +{ + struct bucket *b; + uint32_t i, written = 0; + + /* UDP DATA */ + if (!write8(fd, export_proto_udp)) return 0; + + /* If no data, write a count of 0 and we're done. */ + if (h == NULL) { + if (!write16(fd, 0)) return 0; + return 1; + } + + assert(h->count < 65536); + if (!write16(fd, (uint16_t)h->count)) return 0; + + for (i = 0; isize; i++) + for (b = h->table[i]; b != NULL; b = b->next) { + if (!write16(fd, b->u.port_udp.port)) return 0; + if (!write64(fd, b->in)) return 0; + if (!write64(fd, b->out)) return 0; + written++; + } + assert(written == h->count); + return 1; +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/hosts_db.h b/hosts_db.h new file mode 100644 index 0000000..f2f389a --- /dev/null +++ b/hosts_db.h @@ -0,0 +1,75 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * hosts_db.h: database of hosts, ports, protocols. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ +#ifndef __DARKSTAT_HOSTS_DB_H +#define __DARKSTAT_HOSTS_DB_H + +#include /* for time_t and uint64_t (esp on FreeBSD) */ + +#include "addr.h" + +struct hashtable; + +struct host { + struct addr addr; + char *dns; + uint8_t mac_addr[6]; + time_t lastseen; + struct hashtable *ports_tcp, *ports_udp, *ip_protos; +}; + +struct port_tcp { + uint16_t port; + uint64_t syn; +}; + +struct port_udp { + uint16_t port; +}; + +struct ip_proto { + uint8_t proto; +}; + +struct bucket { + struct bucket *next; + uint64_t in, out, total; + union { + struct host host; + struct port_tcp port_tcp; + struct port_udp port_udp; + struct ip_proto ip_proto; + } u; +}; + +enum sort_dir { IN, OUT, TOTAL, LASTSEEN }; + +extern int hosts_db_show_macs; + +void hosts_db_init(void); +void hosts_db_reduce(void); +void hosts_db_reset(void); +void hosts_db_free(void); +int hosts_db_import(const int fd); +int hosts_db_export(const int fd); + +struct bucket *host_find(const struct addr *const a); /* can return NULL */ +struct bucket *host_get(const struct addr *const a); +struct bucket *host_get_port_tcp(struct bucket *host, const uint16_t port); +struct bucket *host_get_port_udp(struct bucket *host, const uint16_t port); +struct bucket *host_get_ip_proto(struct bucket *host, const uint8_t proto); + +/* Web pages. */ +struct str *html_hosts(const char *uri, const char *query); + +/* From hosts_sort */ +void qsort_buckets(const struct bucket **a, size_t n, + size_t left, size_t right, const enum sort_dir d); + +#endif /* __DARKSTAT_HOSTS_DB_H */ +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/hosts_sort.c b/hosts_sort.c new file mode 100644 index 0000000..d17a21f --- /dev/null +++ b/hosts_sort.c @@ -0,0 +1,206 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * hosts_sort.c: quicksort a table of buckets. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "cdefs.h" +#include "err.h" +#include "hosts_db.h" + +/* --------------------------------------------------------------------------- + * comparator for sorting (biggest first) + */ +static int +cmp(const struct bucket * const *x, const struct bucket * const *y, + const enum sort_dir dir) +{ + uint64_t a, b; + + switch (dir) { + case IN: + a = (*x)->in; + b = (*y)->in; + break; + case OUT: + a = (*x)->out; + b = (*y)->out; + break; + case TOTAL: + a = (*x)->total; + b = (*y)->total; + break; + case LASTSEEN: + a = (*x)->u.host.lastseen; + b = (*y)->u.host.lastseen; + break; + default: + errx(1, "cmp: unknown direction: %d", dir); + } + + if (a < b) return (1); + else if (a > b) return (-1); + else return (0); +} + +/* + * The quicksort code is derived from FreeBSD's + * src/lib/libc/stdlib/qsort.c v1.12 + */ + +/*- + * Copyright (c) 1992, 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + */ + +static void +vecswap(const struct bucket **pi, const struct bucket **pj, int n) +{ + if (n <= 0) + return; + + do { + const struct bucket *t = *pi; + *pi++ = *pj; + *pj++ = t; + } while (--n > 0); +} + +#define swap(a, b) { \ + const struct bucket *t = *(const struct bucket **)(a); \ + *(const struct bucket **)(a) = *(const struct bucket **)(b); \ + *(const struct bucket **)(b) = t; \ +} + +static const struct bucket ** +med3(const struct bucket **a, + const struct bucket **b, + const struct bucket **c, + const enum sort_dir dir) +{ + return (cmp(a, b, dir) < 0) + ? (cmp(b, c, dir) < 0 ? b : (cmp(a, c, dir) < 0 ? c : a )) + : (cmp(b, c, dir) > 0 ? b : (cmp(a, c, dir) < 0 ? a : c )); +} + +/* Partial sort - only sort elements in the range [left:right] */ +void +qsort_buckets(const struct bucket **a, size_t n, + size_t left, size_t right, + const enum sort_dir dir) +{ + const struct bucket **pa, **pb, **pc, **pd, **pl, **pm, **pn; + int d, r, swap_cnt; + +loop: + swap_cnt = 0; + if (n < 7) { + for (pm = a+1; pm < a+n; pm++) + for (pl = pm; + (pl > a) && (cmp(pl-1, pl, dir) > 0); + pl--) + swap(pl, pl-1); + return; + } + pm = a + (n / 2); + if (n > 7) { + pl = a; + pn = a + (n - 1); + if (n > 40) { + d = (n / 8); + pl = med3(pl, pl + d, pl + 2 * d, dir); + pm = med3(pm - d, pm, pm + d, dir); + pn = med3(pn - 2 * d, pn - d, pn, dir); + } + pm = med3(pl, pm, pn, dir); + } + swap(a, pm); + pa = pb = a + 1; + + pc = pd = a + (n - 1); + for (;;) { + while (pb <= pc && (r = cmp(pb, a, dir)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa++; + } + pb++; + } + while (pb <= pc && (r = cmp(pc, a, dir)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd--; + } + pc--; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb++; + pc--; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = a + 1; pm < a+n; pm++) + for (pl = pm; + (pl > a) && (cmp(pl-1, pl, dir) > 0); + pl--) + swap(pl, pl-1); + return; + } + + pn = a + n; + r = MIN(pa - a, pb - pa); + vecswap(a, pb - r, r); + r = MIN(pd - pc, pn - pd - 1); + vecswap(pb, pn - r, r); + if (((r = pb - pa) > 1) && ((unsigned)r >= left)) + qsort_buckets(a, r, left, right, dir); + if (((r = pd - pc) > 1) && (n - r <= right)) { + /* Iterate rather than recurse to save stack space */ + if (n - r > left) + left = 0; + else + left -= n - r; + right -= n - r; + a += n - r; + n = r; + goto loop; + } +/* qsort(pn - r, r, cmp);*/ +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/html.c b/html.c new file mode 100644 index 0000000..2d2b7f7 --- /dev/null +++ b/html.c @@ -0,0 +1,70 @@ +/* darkstat 3 + * + * html.c: HTML header/footer templating for web interface. + * copyright (c) 2006 Ben Stewart. + * copyright (c) 2010 Malte S. Stretz. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "config.h" +#include "str.h" +#include "html.h" +#include "opt.h" + +#include + +static const char *relpaths[] = { + ".", + "..", + "../.." +}; + +void html_open(struct str *buf, const char *title, + const unsigned int path_depth, const int want_graph_js) +{ + const char *root; + assert(path_depth < (sizeof(relpaths)/sizeof(*relpaths))); + root = relpaths[path_depth]; + + str_appendf(buf, + "\n" + "\n" + "\n" + "%s (darkstat3 %s)\n" + "\n" + "\n" + "\n" + , title, opt_interface, root); + + if (want_graph_js) + str_appendf(buf, + "\n" + , root); + + str_appendf(buf, + "\n" + "\n" + "
\n" + "
    " /* no whitespace (newlines) in list */ + "
  • " PACKAGE_STRING "
  • " + "
  • graphs
  • " + "
  • hosts
  • " + "
  • homepage
  • " + "
\n" + "
\n" + "
\n" + "

%s

\n" + , root, root, title); +} + +void html_close(struct str *buf) +{ + str_append(buf, + "
\n" + "\n" + "\n"); +} + +/* vim:set ts=4 sw=4 tw=78 expandtab: */ diff --git a/html.h b/html.h new file mode 100644 index 0000000..b024cc5 --- /dev/null +++ b/html.h @@ -0,0 +1,14 @@ +/* darkstat 3 + * + * html.h: HTML header/footer templating for web interface. + * copyright (c) 2006 Ben Stewart. + * copyright (c) 2010 Malte S. Stretz. + */ + +struct str; + +void html_open(struct str *buf, const char *title, + const unsigned int path_depth, const int want_graph_js); +void html_close(struct str *buf); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/http.c b/http.c new file mode 100644 index 0000000..2835e8e --- /dev/null +++ b/http.c @@ -0,0 +1,1135 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * http.c: embedded webserver. + * This borrows a lot of code from darkhttpd. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "cdefs.h" +#include "config.h" +#include "conv.h" +#include "err.h" +#include "graph_db.h" +#include "hosts_db.h" +#include "http.h" +#include "now.h" +#include "queue.h" +#include "str.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char mime_type_xml[] = "text/xml"; +static const char mime_type_html[] = "text/html; charset=us-ascii"; +static const char mime_type_css[] = "text/css"; +static const char mime_type_js[] = "text/javascript"; +static const char encoding_identity[] = "identity"; +static const char encoding_gzip[] = "gzip"; + +static const char server[] = PACKAGE_NAME "/" PACKAGE_VERSION; +static int idletime = 60; +#define MAX_REQUEST_LENGTH 4000 + +static int *insocks = NULL; +static unsigned int insock_num = 0; + +struct connection { + LIST_ENTRY(connection) entries; + + int socket; + struct sockaddr_storage client; + time_t last_active; + enum { + RECV_REQUEST, /* receiving request */ + SEND_HEADER_AND_REPLY, /* try to send header+reply together */ + SEND_HEADER, /* sending generated header */ + SEND_REPLY, /* sending reply */ + DONE /* conn closed, need to remove from queue */ + } state; + + /* char request[request_length+1] is null-terminated */ + char *request; + size_t request_length; + int accept_gzip; + + /* request fields */ + char *method, *uri, *query; /* query can be NULL */ + + char *header; + const char *mime_type, *encoding, *header_extra; + size_t header_length, header_sent; + int header_dont_free, header_only, http_code; + + char *reply; + int reply_dont_free; + size_t reply_length, reply_sent; + + unsigned int total_sent; /* header + body = total, for logging */ +}; + +static LIST_HEAD(conn_list_head, connection) connlist = + LIST_HEAD_INITIALIZER(conn_list_head); + +struct bindaddr_entry { + STAILQ_ENTRY(bindaddr_entry) entries; + const char *s; +}; +static STAILQ_HEAD(bindaddrs_head, bindaddr_entry) bindaddrs = + STAILQ_HEAD_INITIALIZER(bindaddrs); + +/* --------------------------------------------------------------------------- + * Decode URL by converting %XX (where XX are hexadecimal digits) to the + * character it represents. Don't forget to free the return value. + */ +static char *urldecode(const char *url) +{ + size_t i, len = strlen(url); + char *out = xmalloc(len+1); + int pos; + + for (i=0, pos=0; i= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \ + ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \ + ((hex)-'0') ) + + out[pos++] = HEX_TO_DIGIT(url[i+1]) * 16 + + HEX_TO_DIGIT(url[i+2]); + i += 2; + + #undef HEX_TO_DIGIT + } + else + { + /* straight copy */ + out[pos++] = url[i]; + } + } + out[pos] = 0; +#if 0 + /* don't really need to realloc here - it's probably a performance hit */ + out = xrealloc(out, strlen(out)+1); /* dealloc what we don't need */ +#endif + return (out); +} + + + +/* --------------------------------------------------------------------------- + * Consolidate slashes in-place by shifting parts of the string over repeated + * slashes. + */ +static void consolidate_slashes(char *s) +{ + size_t left = 0, right = 0; + int saw_slash = 0; + + assert(s != NULL); + + while (s[right] != '\0') + { + if (saw_slash) + { + if (s[right] == '/') right++; + else + { + saw_slash = 0; + s[left++] = s[right++]; + } + } + else + { + if (s[right] == '/') saw_slash++; + s[left++] = s[right++]; + } + } + s[left] = '\0'; +} + + + +/* --------------------------------------------------------------------------- + * Resolve /./ and /../ in a URI, returing a new, safe URI, or NULL if the URI + * is invalid/unsafe. Returned buffer needs to be deallocated. + */ +static char *make_safe_uri(char *uri) +{ + char **elem, *out; + unsigned int slashes = 0, elements = 0; + size_t urilen, i, j, pos; + + assert(uri != NULL); + if (uri[0] != '/') + return (NULL); + consolidate_slashes(uri); + urilen = strlen(uri); + + /* count the slashes */ + for (i=0, slashes=0; isocket = -1; + memset(&conn->client, 0, sizeof(conn->client)); + conn->last_active = now; + conn->request = NULL; + conn->request_length = 0; + conn->accept_gzip = 0; + conn->method = NULL; + conn->uri = NULL; + conn->query = NULL; + conn->header = NULL; + conn->mime_type = NULL; + conn->encoding = NULL; + conn->header_extra = ""; + conn->header_length = 0; + conn->header_sent = 0; + conn->header_dont_free = 0; + conn->header_only = 0; + conn->http_code = 0; + conn->reply = NULL; + conn->reply_dont_free = 0; + conn->reply_length = 0; + conn->reply_sent = 0; + conn->total_sent = 0; + + /* Make it harmless so it gets garbage-collected if it should, for some + * reason, fail to be correctly filled out. + */ + conn->state = DONE; + + return (conn); +} + + + +/* --------------------------------------------------------------------------- + * Accept a connection from sockin and add it to the connection queue. + */ +static void accept_connection(const int sockin) +{ + struct sockaddr_storage addrin; + socklen_t sin_size; + struct connection *conn; + char ipaddr[INET6_ADDRSTRLEN], portstr[12]; + int sock; + + sin_size = (socklen_t)sizeof(addrin); + sock = accept(sockin, (struct sockaddr *)&addrin, &sin_size); + if (sock == -1) + { + if (errno == ECONNABORTED || errno == EINTR) + { + verbosef("accept() failed: %s", strerror(errno)); + return; + } + /* else */ err(1, "accept()"); + } + + fd_set_nonblock(sock); + + /* allocate and initialise struct connection */ + conn = new_connection(); + conn->socket = sock; + conn->state = RECV_REQUEST; + memcpy(&conn->client, &addrin, sizeof(conn->client)); + LIST_INSERT_HEAD(&connlist, conn, entries); + + getnameinfo((struct sockaddr *) &addrin, sin_size, + ipaddr, sizeof(ipaddr), portstr, sizeof(portstr), + NI_NUMERICHOST | NI_NUMERICSERV); + verbosef("accepted connection from %s:%s", ipaddr, portstr); +} + + + +/* --------------------------------------------------------------------------- + * Log a connection, then cleanly deallocate its internals. + */ +static void free_connection(struct connection *conn) +{ + dverbosef("free_connection(%d)", conn->socket); + if (conn->socket != -1) + close(conn->socket); + free(conn->request); + free(conn->method); + free(conn->uri); + free(conn->query); + if (!conn->header_dont_free) + free(conn->header); + if (!conn->reply_dont_free) + free(conn->reply); +} + + + +/* --------------------------------------------------------------------------- + * Format [when] as an RFC1123 date, stored in the specified buffer. The same + * buffer is returned for convenience. + */ +#define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */ +static char *rfc1123_date(char *dest, const time_t when) +{ + time_t tmp = when; + if (strftime(dest, DATE_LEN, + "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp) ) == 0) + errx(1, "strftime() failed [%s]", dest); + return (dest); +} + +static void generate_header(struct connection *conn, + const int code, const char *text) +{ + char date[DATE_LEN]; + + assert(conn->header == NULL); + assert(conn->mime_type != NULL); + if (conn->encoding == NULL) + conn->encoding = encoding_identity; + + verbosef("http: %d %s (%s: %d bytes)", code, text, + conn->encoding, conn->reply_length); + conn->header_length = xasprintf(&(conn->header), + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Server: %s\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Type: %s\r\n" + "Content-Length: %d\r\n" + "Content-Encoding: %s\r\n" + "X-Robots-Tag: noindex, noarchive\r\n" + "%s" + "\r\n" + , + code, text, + rfc1123_date(date, now), server, + conn->mime_type, conn->reply_length, conn->encoding, + conn->header_extra); + conn->http_code = code; +} + + + +/* --------------------------------------------------------------------------- + * A default reply for any (erroneous) occasion. + */ +static void default_reply(struct connection *conn, + const int errcode, const char *errname, const char *format, ...) +{ + char *reason; + va_list va; + + va_start(va, format); + xvasprintf(&reason, format, va); + va_end(va); + + conn->reply_length = xasprintf(&(conn->reply), + "%d %s\n" + "

%s

\n" /* errname */ + "%s\n" /* reason */ + "
\n" + "Generated by %s" + "\n", + errcode, errname, errname, reason, server); + free(reason); + + /* forget any dangling metadata */ + conn->mime_type = mime_type_html; + conn->encoding = encoding_identity; + + generate_header(conn, errcode, errname); +} + + + +/* --------------------------------------------------------------------------- + * Parses a single HTTP request field. Returns string from end of [field] to + * first \r, \n or end of request string. Returns NULL if [field] can't be + * matched. + * + * You need to remember to deallocate the result. + * example: parse_field(conn, "Referer: "); + */ +static char *parse_field(const struct connection *conn, const char *field) +{ + size_t bound1, bound2; + char *pos; + + /* find start */ + pos = strstr(conn->request, field); + if (pos == NULL) + return (NULL); + bound1 = pos - conn->request + strlen(field); + + /* find end */ + for (bound2 = bound1; + conn->request[bound2] != '\r' && + bound2 < conn->request_length; bound2++) + ; + + /* copy to buffer */ + return (split_string(conn->request, bound1, bound2)); +} + + + +/* --------------------------------------------------------------------------- + * Parse an HTTP request like "GET /hosts/?sort=in HTTP/1.1" to get the method + * (GET), the uri (/hosts/), the query (sort=in) and whether the UA will + * accept gzip encoding. Remember to deallocate all these buffers. Query + * can be NULL. The method will be returned in uppercase. + */ +static int parse_request(struct connection *conn) +{ + size_t bound1, bound2, mid; + char *accept_enc; + + /* parse method */ + for (bound1 = 0; bound1 < conn->request_length && + conn->request[bound1] != ' '; bound1++) + ; + + conn->method = split_string(conn->request, 0, bound1); + strntoupper(conn->method, bound1); + + /* parse uri */ + for (; bound1 < conn->request_length && + conn->request[bound1] == ' '; bound1++) + ; + + if (bound1 == conn->request_length) + return (0); /* fail */ + + for (bound2=bound1+1; bound2 < conn->request_length && + conn->request[bound2] != ' ' && + conn->request[bound2] != '\r'; bound2++) + ; + + /* find query string */ + for (mid=bound1; midrequest[mid] != '?'; mid++) + ; + + if (conn->request[mid] == '?') { + conn->query = split_string(conn->request, mid+1, bound2); + bound2 = mid; + } + + conn->uri = split_string(conn->request, bound1, bound2); + + /* parse important fields */ + accept_enc = parse_field(conn, "Accept-Encoding: "); + if (accept_enc != NULL) { + if (strstr(accept_enc, "gzip") != NULL) + conn->accept_gzip = 1; + free(accept_enc); + } + return (1); +} + +/* FIXME: maybe we need a smarter way of doing static pages: */ + +/* --------------------------------------------------------------------------- + * Web interface: static stylesheet. + */ +static void +static_style_css(struct connection *conn) +{ +#include "stylecss.h" + + conn->reply = style_css; + conn->reply_length = style_css_len; + conn->reply_dont_free = 1; + conn->mime_type = mime_type_css; +} + +/* --------------------------------------------------------------------------- + * Web interface: static JavaScript. + */ +static void +static_graph_js(struct connection *conn) +{ +#include "graphjs.h" + + conn->reply = graph_js; + conn->reply_length = graph_js_len; + conn->reply_dont_free = 1; + conn->mime_type = mime_type_js; +} + +/* --------------------------------------------------------------------------- + * gzip a reply, if requested and possible. Don't bother with a minimum + * length requirement, I've never seen a page fail to compress. + */ +static void +process_gzip(struct connection *conn) +{ + char *buf; + size_t len; + z_stream zs; + + if (!conn->accept_gzip) + return; + + buf = xmalloc(conn->reply_length); + len = conn->reply_length; + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + + if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, + 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */ + 8 /* default */, + Z_DEFAULT_STRATEGY) != Z_OK) + return; + + zs.avail_in = conn->reply_length; + zs.next_in = (unsigned char *)conn->reply; + + zs.avail_out = conn->reply_length; + zs.next_out = (unsigned char *)buf; + + if (deflate(&zs, Z_FINISH) != Z_STREAM_END) { + deflateEnd(&zs); + free(buf); + verbosef("failed to compress %u bytes", (unsigned int)len); + return; + } + + if (conn->reply_dont_free) + conn->reply_dont_free = 0; + else + free(conn->reply); + conn->reply = buf; + conn->reply_length -= zs.avail_out; + conn->encoding = encoding_gzip; + deflateEnd(&zs); +} + +/* --------------------------------------------------------------------------- + * Process a GET/HEAD request + */ +static void process_get(struct connection *conn) +{ + char *decoded_url, *safe_url; + + verbosef("http: %s \"%s\" %s", conn->method, conn->uri, + (conn->query == NULL)?"":conn->query); + + /* work out path of file being requested */ + decoded_url = urldecode(conn->uri); + + /* make sure it's safe */ + safe_url = make_safe_uri(decoded_url); + free(decoded_url); + if (safe_url == NULL) + { + default_reply(conn, 400, "Bad Request", + "You requested an invalid URI: %s", conn->uri); + return; + } + + if (strcmp(safe_url, "/") == 0) { + struct str *buf = html_front_page(); + str_extract(buf, &(conn->reply_length), &(conn->reply)); + conn->mime_type = mime_type_html; + } + else if (str_starts_with(safe_url, "/hosts/")) { + /* FIXME here - make this saner */ + struct str *buf = html_hosts(safe_url, conn->query); + if (buf == NULL) { + default_reply(conn, 404, "Not Found", + "The page you requested could not be found."); + free(safe_url); + return; + } + str_extract(buf, &(conn->reply_length), &(conn->reply)); + conn->mime_type = mime_type_html; + } + else if (str_starts_with(safe_url, "/graphs.xml")) { + struct str *buf = xml_graphs(); + str_extract(buf, &(conn->reply_length), &(conn->reply)); + conn->mime_type = mime_type_xml; + /* hack around Opera caching the XML */ + conn->header_extra = "Pragma: no-cache\r\n"; + } + else if (strcmp(safe_url, "/style.css") == 0) + static_style_css(conn); + else if (strcmp(safe_url, "/graph.js") == 0) + static_graph_js(conn); + else { + default_reply(conn, 404, "Not Found", + "The page you requested could not be found."); + free(safe_url); + return; + } + free(safe_url); + + process_gzip(conn); + assert(conn->mime_type != NULL); + generate_header(conn, 200, "OK"); +} + + + +/* --------------------------------------------------------------------------- + * Process a request: build the header and reply, advance state. + */ +static void process_request(struct connection *conn) +{ + if (!parse_request(conn)) + { + default_reply(conn, 400, "Bad Request", + "You sent a request that the server couldn't understand."); + } + else if (strcmp(conn->method, "GET") == 0) + { + process_get(conn); + } + else if (strcmp(conn->method, "HEAD") == 0) + { + process_get(conn); + conn->header_only = 1; + } + else + { + default_reply(conn, 501, "Not Implemented", + "The method you specified (%s) is not implemented.", + conn->method); + } + + /* advance state */ + if (conn->header_only) + conn->state = SEND_HEADER; + else + conn->state = SEND_HEADER_AND_REPLY; +} + + + +/* --------------------------------------------------------------------------- + * Receiving request. + */ +static void poll_recv_request(struct connection *conn) +{ + char buf[65536]; + ssize_t recvd; + + recvd = recv(conn->socket, buf, sizeof(buf), 0); + dverbosef("poll_recv_request(%d) got %d bytes", conn->socket, (int)recvd); + if (recvd <= 0) + { + if (recvd == -1) + verbosef("recv(%d) error: %s", conn->socket, strerror(errno)); + conn->state = DONE; + return; + } + conn->last_active = now; + + /* append to conn->request */ + conn->request = xrealloc(conn->request, conn->request_length+recvd+1); + memcpy(conn->request+conn->request_length, buf, (size_t)recvd); + conn->request_length += recvd; + conn->request[conn->request_length] = 0; + + /* die if it's too long */ + if (conn->request_length > MAX_REQUEST_LENGTH) + { + default_reply(conn, 413, "Request Entity Too Large", + "Your request was dropped because it was too long."); + conn->state = SEND_HEADER; + return; + } + + /* process request if we have all of it */ + if (conn->request_length > 4 && + memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0) + { + process_request(conn); + + /* request not needed anymore */ + free(conn->request); + conn->request = NULL; /* important: don't free it again later */ + } +} + + + +/* --------------------------------------------------------------------------- + * Try to send header and [a part of the] reply in one packet. + */ +static void poll_send_header_and_reply(struct connection *conn) +{ + ssize_t sent; + struct iovec iov[2]; + + assert(!conn->header_only); + assert(conn->reply_length > 0); + assert(conn->header_sent == 0); + + assert(conn->reply_sent == 0); + + /* Fill out iovec */ + iov[0].iov_base = conn->header; + iov[0].iov_len = conn->header_length; + + iov[1].iov_base = conn->reply; + iov[1].iov_len = conn->reply_length; + + sent = writev(conn->socket, iov, 2); + conn->last_active = now; + + /* handle any errors (-1) or closure (0) in send() */ + if (sent < 1) { + if (sent == -1) + verbosef("writev(%d) error: %s", conn->socket, strerror(errno)); + conn->state = DONE; + return; + } + + /* Figure out what we've sent. */ + conn->total_sent += (unsigned int)sent; + if (sent < (ssize_t)conn->header_length) { + verbosef("partially sent header"); + conn->header_sent = sent; + conn->state = SEND_HEADER; + return; + } + /* else */ + conn->header_sent = conn->header_length; + sent -= conn->header_length; + + if (sent < (ssize_t)conn->reply_length) { + verbosef("partially sent reply"); + conn->reply_sent += sent; + conn->state = SEND_REPLY; + return; + } + /* else */ + conn->reply_sent = conn->reply_length; + conn->state = DONE; +} + +/* --------------------------------------------------------------------------- + * Sending header. Assumes conn->header is not NULL. + */ +static void poll_send_header(struct connection *conn) +{ + ssize_t sent; + + sent = send(conn->socket, conn->header + conn->header_sent, + conn->header_length - conn->header_sent, 0); + conn->last_active = now; + dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent); + + /* handle any errors (-1) or closure (0) in send() */ + if (sent < 1) + { + if (sent == -1) + verbosef("send(%d) error: %s", conn->socket, strerror(errno)); + conn->state = DONE; + return; + } + conn->header_sent += (unsigned int)sent; + conn->total_sent += (unsigned int)sent; + + /* check if we're done sending */ + if (conn->header_sent == conn->header_length) + { + if (conn->header_only) + conn->state = DONE; + else + conn->state = SEND_REPLY; + } +} + + + +/* --------------------------------------------------------------------------- + * Sending reply. + */ +static void poll_send_reply(struct connection *conn) +{ + ssize_t sent; + + sent = send(conn->socket, + conn->reply + conn->reply_sent, + conn->reply_length - conn->reply_sent, 0); + conn->last_active = now; + dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d", + conn->socket, (int)sent, + (int)conn->reply_sent, + (int)(conn->reply_sent + sent - 1), + (int)conn->reply_length); + + /* handle any errors (-1) or closure (0) in send() */ + if (sent < 1) + { + if (sent == -1) + verbosef("send(%d) error: %s", conn->socket, strerror(errno)); + else if (sent == 0) + verbosef("send(%d) closure", conn->socket); + conn->state = DONE; + return; + } + conn->reply_sent += (unsigned int)sent; + conn->total_sent += (unsigned int)sent; + + /* check if we're done sending */ + if (conn->reply_sent == conn->reply_length) conn->state = DONE; +} + +/* Use getaddrinfo to figure out what type of socket to create and + * what to bind it to. "bindaddr" can be NULL. Remember to freeaddrinfo() + * the result. + */ +static struct addrinfo *get_bind_addr( + const char *bindaddr, const unsigned short bindport) +{ + struct addrinfo hints, *ai; + char portstr[6]; + int ret; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + snprintf(portstr, sizeof(portstr), "%u", bindport); + if ((ret = getaddrinfo(bindaddr, portstr, &hints, &ai))) + err(1, "getaddrinfo(%s, %s) failed: %s", + bindaddr ? bindaddr : "NULL", portstr, gai_strerror(ret)); + if (ai == NULL) + err(1, "getaddrinfo() returned NULL pointer"); + return ai; +} + +void http_add_bindaddr(const char *bindaddr) +{ + struct bindaddr_entry *ent; + + ent = xmalloc(sizeof(*ent)); + ent->s = bindaddr; + STAILQ_INSERT_TAIL(&bindaddrs, ent, entries); +} + +static void http_listen_one(struct addrinfo *ai, + const unsigned short bindport) +{ + char ipaddr[INET6_ADDRSTRLEN]; + int sockin, sockopt, ret; + + /* format address into ipaddr string */ + if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr, + sizeof(ipaddr), NULL, 0, NI_NUMERICHOST)) != 0) + err(1, "getnameinfo failed: %s", gai_strerror(ret)); + + /* create incoming socket */ + if ((sockin = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol)) == -1) + err(1, "http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed", + ipaddr, (unsigned int)bindport, + ai->ai_family, + (ai->ai_family == AF_INET6) ? "AF_INET6" : + (ai->ai_family == AF_INET) ? "AF_INET" : + "?", + ai->ai_socktype, ai->ai_protocol); + + /* reuse address */ + sockopt = 1; + if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR, + &sockopt, sizeof(sockopt)) == -1) + err(1, "can't set SO_REUSEADDR"); + +#ifdef IPV6_V6ONLY + /* explicitly disallow IPv4 mapped addresses since OpenBSD doesn't allow + * dual stack sockets under any circumstances + */ + if (ai->ai_family == AF_INET6) { + sockopt = 1; + if (setsockopt(sockin, IPPROTO_IPV6, IPV6_V6ONLY, + &sockopt, sizeof(sockopt)) == -1) + err(1, "can't set IPV6_V6ONLY"); + } +#endif + + /* bind socket */ + if (bind(sockin, ai->ai_addr, ai->ai_addrlen) == -1) { + warn("bind(\"%s\") failed", ipaddr); + return; + } + + /* listen on socket */ + if (listen(sockin, -1) == -1) + err(1, "listen() failed"); + + verbosef("listening on http://%s%s%s:%u/", + (ai->ai_family == AF_INET6) ? "[" : "", + ipaddr, + (ai->ai_family == AF_INET6) ? "]" : "", + bindport); + + /* add to insocks */ + insocks = xrealloc(insocks, sizeof(*insocks) * (insock_num + 1)); + insocks[insock_num++] = sockin; +} + +/* Initialize the http sockets and listen on them. */ +void http_listen(const unsigned short bindport) +{ + /* If the user didn't specify any bind addresses, add a NULL. + * This will become a wildcard. + */ + if (STAILQ_EMPTY(&bindaddrs)) + http_add_bindaddr(NULL); + + /* Listen on every specified interface. */ + while (!STAILQ_EMPTY(&bindaddrs)) { + struct bindaddr_entry *bindaddr = STAILQ_FIRST(&bindaddrs); + struct addrinfo *ai, *ais = get_bind_addr(bindaddr->s, bindport); + + /* There could be multiple addresses returned, handle them all. */ + for (ai = ais; ai; ai = ai->ai_next) + http_listen_one(ai, bindport); + + freeaddrinfo(ais); + + STAILQ_REMOVE_HEAD(&bindaddrs, entries); + free(bindaddr); + } + + if (insocks == 0) + errx(1, "was not able to bind any ports for http interface"); + + /* ignore SIGPIPE */ + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + err(1, "can't ignore SIGPIPE"); +} + + + +/* --------------------------------------------------------------------------- + * Set recv/send fd_sets and calculate timeout length. + */ +void +http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd, + struct timeval *timeout, int *need_timeout) +{ + struct connection *conn, *next; + int minidle = idletime + 1; + unsigned int i; + + #define MAX_FD_SET(sock, fdset) do { \ + FD_SET(sock, fdset); *max_fd = MAX(*max_fd, sock); } while(0) + + for (i=0; ilast_active; + + /* Time out dead connections. */ + if (idlefor >= idletime) { + char ipaddr[INET6_ADDRSTRLEN]; + /* FIXME: this is too late on FreeBSD, socket is invalid */ + int ret = getnameinfo((struct sockaddr *)&conn->client, + sizeof(conn->client), ipaddr, sizeof(ipaddr), + NULL, 0, NI_NUMERICHOST); + if (ret == 0) + verbosef("http socket timeout from %s (fd %d)", + ipaddr, conn->socket); + else + warn("http socket timeout: getnameinfo error: %s", + gai_strerror(ret)); + conn->state = DONE; + } + + /* Connections that need a timeout. */ + if (conn->state != DONE) + minidle = MIN(minidle, (idletime - idlefor)); + + switch (conn->state) + { + case DONE: + /* clean out stale connection */ + LIST_REMOVE(conn, entries); + free_connection(conn); + free(conn); + break; + + case RECV_REQUEST: + MAX_FD_SET(conn->socket, recv_set); + break; + + case SEND_HEADER_AND_REPLY: + case SEND_HEADER: + case SEND_REPLY: + MAX_FD_SET(conn->socket, send_set); + break; + + default: errx(1, "invalid state"); + } + } + #undef MAX_FD_SET + + /* Only set timeout if cap hasn't already. */ + if ((*need_timeout == 0) && (minidle <= idletime)) { + *need_timeout = 1; + timeout->tv_sec = minidle; + timeout->tv_usec = 0; + } +} + + + +/* --------------------------------------------------------------------------- + * poll connections that select() says need attention + */ +void http_poll(fd_set *recv_set, fd_set *send_set) +{ + struct connection *conn; + unsigned int i; + + for (i=0; istate) + { + case RECV_REQUEST: + if (FD_ISSET(conn->socket, recv_set)) poll_recv_request(conn); + break; + + case SEND_HEADER_AND_REPLY: + if (FD_ISSET(conn->socket, send_set)) poll_send_header_and_reply(conn); + break; + + case SEND_HEADER: + if (FD_ISSET(conn->socket, send_set)) poll_send_header(conn); + break; + + case SEND_REPLY: + if (FD_ISSET(conn->socket, send_set)) poll_send_reply(conn); + break; + + case DONE: /* fallthrough */ + default: errx(1, "invalid state"); + } +} + +void http_stop(void) { + struct connection *conn; + unsigned int i; + + /* Close listening sockets. */ + for (i=0; i +#include +#include + +void http_add_bindaddr(const char *bindaddr); +void http_listen(const unsigned short bindport); +void http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd, + struct timeval *timeout, int *need_timeout); +void http_poll(fd_set *read_set, fd_set *write_set); +void http_stop(void); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..4d4a951 --- /dev/null +++ b/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# 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}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # 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 $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/localip.c b/localip.c new file mode 100644 index 0000000..0c15409 --- /dev/null +++ b/localip.c @@ -0,0 +1,154 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * localip.c: determine local IP of our capture interface + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "addr.h" +#include "config.h" /* for HAVE_IFADDRS_H */ +#include "err.h" +#include "localip.h" +#include "bsd.h" /* for strlcpy */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_IFADDRS_H +# include +#else +# ifdef HAVE_SYS_SOCKIO_H +# include /* for SIOCGIFADDR, especially on Solaris */ +# endif +# include +#endif + +static const char *iface = NULL; +struct addr localip4, localip6; +static struct addr last_localip4, last_localip6; + +void +localip_init(const char *interface) +{ + iface = interface; + + /* defaults */ + localip4.family = IPv4; + localip4.ip.v4 = 0; + + localip6.family = IPv6; + memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6)); + + last_localip4 = localip4; + last_localip6 = localip6; + + /* initial update */ + localip_update(); +} + +static void +localip_update_helper(void) +{ + /* defaults */ + localip4.family = IPv4; + localip4.ip.v4 = 0; + + localip6.family = IPv6; + memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6)); + + if (iface == NULL) + return; /* reading from capfile */ + +#ifdef HAVE_IFADDRS_H + { + int got_v4 = 0, got_v6 = 0; + struct ifaddrs *ifas, *ifa; + + if (getifaddrs(&ifas) < 0) { + warn("can't getifaddrs() on interface \"%s\"", iface); + return; + } + + for (ifa = ifas; ifa; ifa = ifa->ifa_next) { + if (got_v4 && got_v6) + break; /* Task is already complete. */ + + if (strncmp(ifa->ifa_name, iface, IFNAMSIZ)) + continue; /* Wrong interface. */ + + if (!ifa->ifa_addr) + continue; /* This can be NULL, e.g. for ppp0. */ + + /* The first IPv4 name is always functional. */ + if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4) + { + /* Good IPv4 address. */ + localip4.ip.v4 = + ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + got_v4 = 1; + continue; + } + + /* IPv6 needs some obvious exceptions. */ + if ( ifa->ifa_addr->sa_family == AF_INET6 ) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr; + + if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr)) + || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) ) + continue; + + /* Only standard IPv6 can reach this point. */ + memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6)); + got_v6 = 1; + } + } + + freeifaddrs(ifas); + + if (!got_v4) + warnx("can't get own IPv4 address on interface \"%s\"", iface); + } +#else /* don't HAVE_IFADDRS_H */ + { + int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + struct ifreq ifr; + struct sockaddr sa; + + strlcpy(ifr.ifr_name, iface, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) { + if (errno == EADDRNOTAVAIL) { + verbosef("lost local IP"); + } else + warn("can't get own IP address on interface \"%s\"", iface); + } else { + /* success! */ + sa = ifr.ifr_addr; + localip4.ip.v4 = ((struct sockaddr_in*)&sa)->sin_addr.s_addr; + } + close(tmp); + } +#endif +} + +void +localip_update(void) +{ + localip_update_helper(); + + if (!addr_equal(&last_localip4, &localip4)) { + verbosef("%s ip4 update: %s", iface, addr_to_str(&localip4)); + last_localip4 = localip4; + } + if (!addr_equal(&last_localip6, &localip6)) { + verbosef("%s ip6 update: %s", iface, addr_to_str(&localip6)); + last_localip6 = localip6; + } +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/localip.h b/localip.h new file mode 100644 index 0000000..21231b6 --- /dev/null +++ b/localip.h @@ -0,0 +1,15 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * localip.h: determine local IP of our capture interface + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +extern struct addr localip4, localip6; + +void localip_init(const char *interface); +void localip_update(void); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/ncache.c b/ncache.c new file mode 100644 index 0000000..96e052f --- /dev/null +++ b/ncache.c @@ -0,0 +1,144 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * ncache.c: cache of protocol and service names. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +#include "conv.h" +#include "err.h" +#include "ncache.h" +#include "tree.h" +#include "bsd.h" /* for strlcpy */ + +#include /* ntohs */ +#include +#include +#include + +struct name_rec { + RB_ENTRY(name_rec) ptree; + int num; + char *name; +}; + +static int +rec_cmp(struct name_rec *a, struct name_rec *b) +{ + if (a->num < b->num) return (-1); else + if (a->num > b->num) return (+1); else + return (0); +} + +RB_HEAD(nc_tree, name_rec); +RB_GENERATE(nc_tree, name_rec, ptree, rec_cmp) + +static struct nc_tree + t_proto = RB_INITIALIZER(&name_rec), + t_servtcp = RB_INITIALIZER(&name_rec), + t_servudp = RB_INITIALIZER(&name_rec); + +static void +add_rec(struct nc_tree *tree, const int num, const char *name) +{ + struct name_rec *e, *r = xmalloc(sizeof(*r)); + + r->num = num; + e = RB_INSERT(nc_tree, tree, r); + + if (e != NULL) { + size_t newlen; + + /* record exists: append service name, free record */ + newlen = strlen(e->name) + strlen(name) + 2; + e->name = xrealloc(e->name, newlen); + strlcat(e->name, " ", newlen); + strlcat(e->name, name, newlen); + free(r); + } + else { + /* record added: fill out name field */ + r->name = xstrdup(name); + } +} + +void +ncache_init(void) +{ + struct protoent *pe; + struct servent *se; + int count, ctcp, cudp; + + count = 0; + setprotoent(0); + while ((pe = getprotoent()) != NULL) { + add_rec(&t_proto, pe->p_proto, pe->p_name); + count++; + } + endprotoent(); + verbosef("loaded %d protos", count); + + count = ctcp = cudp = 0; + setservent(0); + while ((se = getservent()) != NULL) { + if (strcmp(se->s_proto, "tcp") == 0) { + add_rec(&t_servtcp, ntohs(se->s_port), se->s_name); + ctcp++; + } + else if (strcmp(se->s_proto, "udp") == 0) { + add_rec(&t_servudp, ntohs(se->s_port), se->s_name); + cudp++; + } + count++; + } + endservent(); + verbosef("loaded %d tcp and %d udp servs, from total %d", + ctcp, cudp, count); +} + +static void +tree_free(struct nc_tree *tree) +{ + struct name_rec *curr, *next; + + for (curr = RB_MIN(nc_tree, tree); curr != NULL; curr = next) { + next = RB_NEXT(nc_tree, tree, curr); + RB_REMOVE(nc_tree, tree, curr); + free(curr->name); + free(curr); + } +} + +void +ncache_free(void) +{ + tree_free(&t_proto); + tree_free(&t_servtcp); + tree_free(&t_servudp); +} + +#define FIND(tree,n) { \ + struct name_rec r, *f; \ + r.num = n; \ + f = RB_FIND(nc_tree, &tree, &r); \ + if (f == NULL) \ + return (""); \ + else \ + return (f->name); \ +} + +const char * +getproto(const int proto) +FIND(t_proto, proto) + +const char * +getservtcp(const int port) +FIND(t_servtcp, port) + +const char * +getservudp(const int port) +FIND(t_servudp, port) + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/ncache.h b/ncache.h new file mode 100644 index 0000000..bf99615 --- /dev/null +++ b/ncache.h @@ -0,0 +1,16 @@ +/* darkstat 3 + * copyright (c) 2001-2006 Emil Mikulic. + * + * ncache.h: cache of protocol and service names. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +void ncache_init(void); +void ncache_free(void); +const char *getproto(const int proto); +const char *getservtcp(const int port); +const char *getservudp(const int port); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/now.h b/now.h new file mode 100644 index 0000000..2dd8e4a --- /dev/null +++ b/now.h @@ -0,0 +1,9 @@ +/* darkstat 3 + * copyright (c) 2001-2006 Emil Mikulic. + * + * now.h: a cache of the current time + * This lets us avoid superfluous gettimeofday() syscalls. + */ +#include + +extern time_t now; /* updated in the event loop in darkstat.c */ diff --git a/opt.h b/opt.h new file mode 100644 index 0000000..5d75cf3 --- /dev/null +++ b/opt.h @@ -0,0 +1,43 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * opt.h: global options + */ + +/* + * Capture options. + */ +extern int opt_want_pppoe; +extern int opt_want_macs; +extern int opt_want_hexdump; +extern int opt_want_snaplen; +extern int opt_wait_secs; + +/* + * Error/logging options. + */ +extern int opt_want_verbose; +extern int opt_want_syslog; + +/* + * Accounting options. + */ +extern unsigned int opt_highest_port; +extern int opt_want_local_only; + +/* + * Hosts table reduction - when the number of entries is about to exceed + * , we reduce the table to the top entries. + */ +extern unsigned int opt_hosts_max; +extern unsigned int opt_hosts_keep; +extern unsigned int opt_ports_max; +extern unsigned int opt_ports_keep; + +/* + * Hosts output options. + */ +extern int opt_want_lastseen; +extern const char *opt_interface; + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/pidfile.c b/pidfile.c new file mode 100644 index 0000000..044c574 --- /dev/null +++ b/pidfile.c @@ -0,0 +1,91 @@ +/* darkstat 3 + * copyright (c) 2007-2011 Emil Mikulic. + * + * pidfile.h: pidfile manglement + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "err.h" +#include "str.h" +#include "pidfile.h" + +#include +#include +#include +#include +#include + +static int pidfd = -1; +static const char *pidname = NULL; + +void +pidfile_create(const char *chroot_dir, const char *filename, + const char *privdrop_user) +{ + struct passwd *pw; + + if (pidfd != -1) + errx(1, "pidfile already created"); + + errno = 0; + pw = getpwnam(privdrop_user); + + if (pw == NULL) { + if (errno == 0) + errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user); + else + err(1, "getpwnam(\"%s\") failed", privdrop_user); + } + + if (chdir(chroot_dir) == -1) + err(1, "chdir(\"%s\") failed", chroot_dir); + pidname = filename; + pidfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600); + if (pidfd == -1) + err(1, "couldn't not create pidfile"); + if (chown(filename, pw->pw_uid, pw->pw_gid) == -1) + err(1, "couldn't chown pidfile"); +} + +void +pidfile_write_close(void) +{ + struct str *s; + size_t len; + char *buf; + + if (pidfd == -1) + errx(1, "cannot write pidfile: not created"); + + s = str_make(); + str_appendf(s, "%u\n", (unsigned int)getpid()); + str_extract(s, &len, &buf); + + if (write(pidfd, buf, len) != (int)len) + err(1, "couldn't write to pidfile"); + free(buf); + if (close(pidfd) == -1) + warn("problem closing pidfile"); +} + +void +pidfile_unlink(void) +{ + if (pidname == NULL) + return; /* pidfile wasn't created */ + if (unlink(pidname) == -1) + warn("problem unlinking pidfile"); +} + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/pidfile.h b/pidfile.h new file mode 100644 index 0000000..7ec1ca8 --- /dev/null +++ b/pidfile.h @@ -0,0 +1,12 @@ +/* darkstat 3 + * copyright (c) 2007 Emil Mikulic. + * + * pidfile.h: pidfile manglement + */ + +void pidfile_create(const char *chroot_dir, const char *filename, + const char *privdrop_user); +void pidfile_write_close(void); +void pidfile_unlink(void); + +/* vim:set ts=3 sw=3 tw=78 et: */ diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..95a1276 --- /dev/null +++ b/queue.h @@ -0,0 +1,128 @@ +/* This is a stripped down version of FreeBSD's + * src/sys/sys/queue.h,v 1.60.2.1 + * + * The original file's license: + * + * 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. + * 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. + */ + +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#ifdef STAILQ_INSERT_TAIL +#undef STAILQ_INSERT_TAIL +#endif + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#ifdef STAILQ_REMOVE_HEAD +#undef STAILQ_REMOVE_HEAD +#endif + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#undef LIST_HEAD +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#undef LIST_HEAD_INITIALIZER +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#undef LIST_ENTRY +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#undef LIST_FIRST +#define LIST_FIRST(head) ((head)->lh_first) + +#undef LIST_FOREACH +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#undef LIST_FOREACH_SAFE +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#undef LIST_INIT +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#undef LIST_INSERT_HEAD +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#undef LIST_NEXT +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#undef LIST_REMOVE +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) diff --git a/static/c-ify.c b/static/c-ify.c new file mode 100644 index 0000000..908a274 --- /dev/null +++ b/static/c-ify.c @@ -0,0 +1,32 @@ +/* DON'T LOOK AT MY FACE! MY HIDEOUS FACE! */ +#include +#include +int +main(int argc, char **argv) +{ + int c, eol; + if (argc != 2) { + fprintf(stderr, "usage: %s name outfile.h\n", + argv[0]); + exit(EXIT_FAILURE); + } + printf("/* this file was automatically generated */\n" + "static char %s[] =", argv[1]); + eol = 1; + while ((c = getchar()) != EOF) { + if (eol) { + printf("\n\""); + eol = 0; + } + switch (c) { + case '\n': printf("\\n\""); eol = 1; break; + case '"': printf("\\\""); break; + case '\\': printf("\\\\"); break; + default: putchar(c); + } + } + printf(";\n" + "static const size_t %s_len = sizeof(%s) - 1;\n", + argv[1], argv[1]); + return (0); +} diff --git a/static/graph.js b/static/graph.js new file mode 100644 index 0000000..6a90bdc --- /dev/null +++ b/static/graph.js @@ -0,0 +1,280 @@ +/* darkstat 3 + * copyright (c) 2006-2008 Emil Mikulic. + * + * graph.js: graph renderer + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + * + * At some point, this script worked correctly in: + * - Firefox 1.5.0.4, 2.0.0.1, 3.0 + * - IE 6.0 + * - Opera 8.53, 9.50 + * - Konqueror 3.5.9, 4.0.80, 4.0.83 + * + * Consumer needs to supply the following variables: + * - graph_width + * - graph_height + * - bar_gap + * + * - graphs [ {id, name, title, bar_secs} ] + * - graphs_uri + * + * - window.onload = graphs_init + */ + +function killChildren(elem) { + while (elem.childNodes.length > 0) + elem.removeChild( elem.childNodes.item(0) ); +} + +function setClass(elem, c) { + elem.setAttribute("class", c); + elem.setAttribute("className", c); /* for MSIE */ +} + +function setStyle(elem, s) { + elem.setAttribute("style", s); + elem.style.cssText = s; /* for MSIE */ +} + +function makeElemClass(e, c) { + var r = document.createElement(e); + setClass(r, c); + return r; +} + +function makeClear() { + var r = document.createElement("div"); + setStyle(r, "clear:both"); + return r; +} + +function thousands(n) { + var s = String(n); + var out = ""; + while (s.length > 3) { + out = "," + s.substr(s.length - 3, 3) + out; + s = s.substr(0, s.length - 3); + } + return s+out; +} + +function fkbps(bps) { + bps /= 1024; + return bps.toFixed(1); +} + +function kbps(bps) { + bps /= 1024; + if (bps < 1) return bps.toPrecision(2); + else return bps.toFixed(1); +} + +function min(a,b) { return (ab)?a:b; } + +var xh, autoreload=false; + +function graphs_init() { + var gr = document.getElementById("graphs"); + + /* update message */ + var msg = document.createElement("div"); + msg.appendChild(document.createTextNode("Graphs are being loaded...")); + msg.appendChild(document.createElement("br")); + msg.appendChild(document.createElement("br")); + killChildren(gr); + gr.appendChild(msg); + graphs.msg = msg; + + for (var i=0; i4G? */ + if (b_total > total_max) + total_max = b_total; + data.push( [b_pos, b_in, b_out] ); + } + + var igraph = makeElemClass("div", "graph"); // inner graph + setStyle(igraph, + "width:"+graph_width+"px; "+ + "height:"+graph_height+"px; "+ + "position:relative;"); + + var nbars = data.length; + var b_width = (graph_width - bar_gap * (nbars-1)) / nbars; + var next_xofs = 0; + + var min_i = 0, min_o = 0, + max_i = 0, max_o = 0, + tot_i = 0, tot_o = 0; + + for (var i=0; i0) { if (min_i == 0) min_i = b_i; else min_i = min(min_i, b_i); } + max_i = max(max_i, b_i); + tot_i += b_i; + + if (b_o>0) { if (min_o == 0) min_o = b_o; else min_o = min(min_o, b_o); } + max_o = max(max_o, b_o); + tot_o += b_o; + + var xofs = next_xofs; + + next_xofs = Math.round((b_width + bar_gap) * (i+1)); + var curr_w = next_xofs - xofs - bar_gap; + + var h_i = Math.round( b_i * graph_height / total_max ); + var h_o = Math.round( b_o * graph_height / total_max ); + + var label = b_p+": "+ + thousands(b_i)+" bytes in, "+ + thousands(b_o)+" bytes out | "+ + kbps(b_i/bar_secs)+" KB/s in, "+ + kbps(b_o/bar_secs)+" KB/s out"; + + addBar(igraph, label, "bar_in", curr_w, h_i, xofs, 0); + addBar(igraph, label, "bar_out", curr_w, h_o, xofs, h_i); + } + + function legendRow(dir_str, minb, avgb, maxb) { + function makeTD(c, str) { + var r = makeElemClass("td", c); + r.appendChild(document.createTextNode(str)); + return r; + } + function addToRow(row, type_str, bytes, trail) { + row.appendChild( makeTD("type", type_str) ); + row.appendChild( makeTD("rate", fkbps(bytes/bar_secs)+" KB/s"+trail) ); + } + var row = document.createElement("tr"); + row.appendChild( makeTD("dir", dir_str) ); + var cell = makeElemClass("td", "swatch"); + var swatch = makeElemClass("div", "bar_"+dir_str); + setStyle(swatch, "width:6px; height:6px;"); + cell.appendChild(swatch); + row.appendChild(cell); + addToRow(row, "min:", minb, ","); + addToRow(row, "avg:", avgb, ","); + addToRow(row, "max:", maxb, ""); + return row; + } + + var glegend = makeElemClass("div", "legend"); + var avg_i = tot_i / nbars, + avg_o = tot_o / nbars; + var tbl = document.createElement("table"); + var tb = document.createElement("tbody"); /* for MSIE */ + tb.appendChild( legendRow("in", min_i, avg_i, max_i) ); + tb.appendChild( legendRow("out", min_o, avg_o, max_o) ); + tbl.appendChild(tb); + glegend.appendChild(tbl); + setStyle(glegend, "width:"+graph_width+"px;"); + + var gtitle = makeElemClass("div", "graphtitle"); + setStyle(gtitle, "width:"+graph_width+"px;"); + gtitle.appendChild(document.createTextNode(title)); + + killChildren(graph); + graph.appendChild(igraph); + graph.appendChild(glegend); + graph.appendChild(gtitle); +} diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..06b1b9a --- /dev/null +++ b/static/style.css @@ -0,0 +1,67 @@ +/* darkstat 3 + * + * style.css: CSS stylesheet for web interface. + * copyright (c) 2006 Ben Stewart. + * colors broken in 2007 by Emil Mikulic. + * + * You may use, modify and redistribute this file under the terms of the + * GNU General Public License version 2. (see COPYING.GPL) + */ + +body { background-color: #fff; z-index: 0; } +.content { z-index: 1; + position: absolute; top:15px; left:10px; } +div.menu { z-index: 2; + position: absolute; top:0; left:0; + width: 100%; background-color: #789; + border-bottom: 1px solid black; + font-size:11px; } +ul.menu { list-style: none; margin:0; padding:2px 0 3px 0; } +ul.menu li { list-style: none; display: inline; margin:0; + padding:2px 0 3px 0; + border-right:1px solid white; } +ul.menu li.label + { padding-left:10px; padding-right:10px; color:#000; + text-shadow: 0px 1px 0px #9ab; } +ul.menu li a { color: white; text-decoration: none; + text-shadow: 0px 1px 0px #456; + border-bottom: none; padding:2px 15px 3px 15px; } +ul.menu li a:hover + { background-color: #9ab; } +h1, h2, h3, h4, h5, h6 + { margin-top:10px; margin-bottom:5px; color: #000000; + font-family: Arial, sans-serif; font-weight:bold; } +.pageheader { border-bottom: 2px dotted black; } +table { border-collapse: collapse; } +td, th { border:1px solid #C0C0C0; padding:1px 5px 1px 5px; } +td.num { text-align:right; } +th { background-color:#EFEFEF; font-weight: bold; + padding-top:2px; padding-bottom:2px; } +th a { color:black; border-bottom:1px dotted; } +tr.alt1 { background:#FFFFFF; } +tr.alt2 { background:#FAFAFA; } +tr.alt1:hover, tr.alt2:hover { background:#EFEFEF; } +body, td, th, p, input, textarea + { font-family: Tahoma, Verdana, sans-serif; + font-size: small; } +tt { font-family: Courier New, monospace; + font-size: small; } +a:hover { border-bottom: 1px dotted #666; } +a { text-decoration: none; color: #666; } +div.outergraph { float:left; margin-right:10px; margin-bottom:20px; } +div.graph { border: 1px solid black; } +div.graphtitle { text-align:center; font-weight:bold; } +div.bar_in { background: #678; } +div.bar_out { background: #abc; } + +#graph_reload,#graph_autoreload { border:1px solid black; + padding:2px 10px 2px 10px; margin-left:5px; color:black; } + +#graph_reload:hover,#graph_autoreload:hover { background:#9ab; color:black; } + +div.legend table { margin-left:auto; margin-right:auto; /* center */ + border:0; } +div.legend td { border:0; padding:0 0.2em 0 0.2em; font-size:11px; + color:#444; } +div.legend td.dir { text-align:right; } +div.legend td.rate { text-align:right; white-space: nowrap; } diff --git a/str.c b/str.c new file mode 100644 index 0000000..8092666 --- /dev/null +++ b/str.c @@ -0,0 +1,357 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * str.c: string buffer with pool-based reallocation + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include /* for uint32_t on Linux and OS X */ + +#include "conv.h" +#include "err.h" +#include "str.h" + +#define INITIAL_LEN 1024 + +struct str { + char *buf; + size_t len, pool; +}; + +struct str * +str_make(void) +{ + struct str *s = xmalloc(sizeof(*s)); + s->len = 0; + s->pool = INITIAL_LEN; + s->buf = xmalloc(s->pool); + return (s); +} + +void +str_free(struct str *s) +{ + free(s->buf); + free(s); +} + +/* + * Extract struct str into buffer and length, freeing the struct in the + * process. + */ +void +str_extract(struct str *s, size_t *len, char **str) +{ + *len = s->len; + *str = s->buf; + free(s); +} + +void +str_appendn(struct str *buf, const char *s, const size_t len) +{ + if (buf->pool < buf->len + len) { + /* pool has dried up */ + while (buf->pool < buf->len + len) + buf->pool *= 2; + buf->buf = xrealloc(buf->buf, buf->pool); + } + memcpy(buf->buf + buf->len, s, len); + buf->len += len; +} + +void +str_appendstr(struct str *buf, const struct str *s) +{ + str_appendn(buf, s->buf, s->len); +} + +#ifndef str_append +void +str_append(struct str *buf, const char *s) +{ + str_appendn(buf, s, strlen(s)); +} +#endif + +/* + * Apparently, some wacky locales use periods, or another character that isn't + * a comma, to separate thousands. If you are afflicted by such a locale, + * change this macro: + */ +#define COMMA ',' + +/* 2^32 = 4,294,967,296 (10 digits, 13 chars) */ +#define I32_MAXLEN 13 + +/* 2^64 = 18,446,744,073,709,551,616 (20 digits, 26 chars) */ +#define I64_MAXLEN 26 + +static void +str_append_u32(struct str *s, const uint32_t i, const int mod_sep) +{ + char out[I32_MAXLEN]; + int pos, len; + uint32_t rem, next; + + if (i == 0) { + str_append(s, "0"); + return; + } + + pos = sizeof(out)-1; + len = 0; + rem = i; + + while (rem > 0) { + assert(pos >= 0); + next = rem / 10; + rem = rem - next * 10; + assert(rem < 10); + out[pos] = '0' + rem; + pos--; + len++; + rem = next; + if (mod_sep && (rem > 0) && (len > 0) && (len % 3 == 0)) { + out[pos] = COMMA; + pos--; + } + } + str_appendn(s, out+pos+1, sizeof(out)-1-pos); +} + +static void +str_append_i32(struct str *s, int32_t i, const int mod_sep) +{ + if (i < 0) { + str_append(s, "-"); + i = -i; + } + str_append_u32(s, (uint32_t)i, mod_sep); +} + +static void +str_append_u64(struct str *s, const uint64_t i, const int mod_sep) +{ + char out[I64_MAXLEN]; + int pos, len; + uint64_t rem, next; + uint32_t rem32, next32; + + if (i == 0) { + str_append(s, "0"); + return; + } + + pos = sizeof(out)-1; + len = 0; + rem = i; + + while (rem >= 4294967295U) { + assert(pos >= 0); + next = rem / 10; + rem = rem - next * 10; + assert(rem < 10); + out[pos] = '0' + rem; + pos--; + len++; + rem = next; + if (mod_sep && (rem > 0) && (len > 0) && (len % 3 == 0)) { + out[pos] = COMMA; + pos--; + } + } + + /* + * Stick to 32-bit math when we can as it's faster on 32-bit platforms. + * FIXME: a tunable way to switch this off? + */ + rem32 = (uint32_t)rem; + while (rem32 > 0) { + assert(pos >= 0); + next32 = rem32 / 10; + rem32 = rem32 - next32 * 10; + assert(rem32 < 10); + out[pos] = '0' + rem32; + pos--; + len++; + rem32 = next32; + if (mod_sep && (rem32 > 0) && (len > 0) && (len % 3 == 0)) { + out[pos] = COMMA; + pos--; + } + } + str_appendn(s, out+pos+1, sizeof(out)-1-pos); +} + +static void +str_append_i64(struct str *s, int64_t i, const int mod_sep) +{ + if (i < 0) { + str_append(s, "-"); + i = -i; + } + str_append_u64(s, (uint64_t)i, mod_sep); +} + +static void +str_append_hex8(struct str *s, const uint8_t b) +{ + char out[2]; + static const char hexset[] = "0123456789abcdef"; + + out[0] = hexset[ ((b >> 4) & 15) ]; + out[1] = hexset[ (b & 15) ]; + str_appendn(s, out, 2); +} + +/* accepted formats: %s %d %u %x + * accepted modifiers: q and ' + * + * %x is equivalent to %02x and expects a uint8_t + */ +static void +str_vappendf(struct str *s, const char *format, va_list va) +{ + size_t pos, len; + len = strlen(format); + + for (pos=0; pos 0) + str_appendn(s, format+span_start, span_len); + + if (format[pos] == '%') { + int mod_quad = 0, mod_sep = 0; + char *arg_str; +FORMAT: + pos++; + switch (format[pos]) { + case '%': + str_append(s, "%"); + break; + case 'q': + mod_quad = 1; + goto FORMAT; + case '\'': + mod_sep = 1; + goto FORMAT; + case 's': + arg_str = va_arg(va, char*); + str_append(s, arg_str); + /* str_append can be a macro! passing it va_arg can result in + * va_arg being called twice + */ + break; + case 'd': + if (mod_quad) + str_append_i64(s, va_arg(va, int64_t), mod_sep); + else + str_append_i32(s, (int32_t)va_arg(va, int), mod_sep); + break; + case 'u': + if (mod_quad) + str_append_u64(s, va_arg(va, uint64_t), mod_sep); + else + str_append_u32(s, (uint32_t)va_arg(va, unsigned int), mod_sep); + break; + case 'x': + str_append_hex8(s, (uint8_t)va_arg(va, int)); + break; + default: + errx(1, "format string is \"%s\", unknown format '%c' at %u", + format, format[pos], (unsigned int)pos); + } + } + } +} + +void +str_appendf(struct str *s, const char *format, ...) +{ + va_list va; + va_start(va, format); + str_vappendf(s, format, va); + va_end(va); +} + +size_t +xvasprintf(char **result, const char *format, va_list va) +{ + size_t len; + struct str *s = str_make(); + str_vappendf(s, format, va); + str_appendn(s, "", 1); /* "" still contains \0 */ + str_extract(s, &len, result); + return (len-1); +} + +size_t +xasprintf(char **result, const char *format, ...) +{ + va_list va; + size_t ret; + va_start(va, format); + ret = xvasprintf(result, format, va); + va_end(va); + return (ret); +} + +/* + * Format a length of time in seconds to "n days, n hrs, n mins, n secs". + * Returns a newly allocated str. + */ +struct str * +length_of_time(const time_t t) +{ + struct str *buf = str_make(); + int secs = t % 60; + int mins = (t / 60) % 60; + int hours = (t / 3600) % 24; + int days = t / 86400; + + int show_zeroes = 0; + + if (days > 0) { + str_appendf(buf, "%d %s", days, (days==1)?"day":"days"); + show_zeroes = 1; + } + + if (show_zeroes || (hours > 0)) { + if (show_zeroes) str_append(buf, ", "); + str_appendf(buf, "%d %s", hours, (hours==1)?"hr":"hrs"); + show_zeroes = 1; + } + + if (show_zeroes || (mins > 0)) { + if (show_zeroes) str_append(buf, ", "); + str_appendf(buf, "%d %s", mins, (mins==1)?"min":"mins"); + show_zeroes = 1; + } + + if (show_zeroes) str_append(buf, ", "); + str_appendf(buf, "%d %s", secs, (secs==1)?"sec":"secs"); + + return buf; +} + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/str.h b/str.h new file mode 100644 index 0000000..da8a2dc --- /dev/null +++ b/str.h @@ -0,0 +1,47 @@ +/* darkstat 3 + * copyright (c) 2001-2011 Emil Mikulic. + * + * str.h: string buffer with pool-based reallocation + * + * Permission to use, copy, modify, and distribute this file for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* Note: the contents are 8-bit clean and not zero terminated! */ + +struct str; + +struct str *str_make(void); +void str_free(struct str *s); +void str_extract(struct str *buf, size_t *len, char **str); +void str_appendn(struct str *buf, const char *s, const size_t len); +void str_appendstr(struct str *buf, const struct str *s); + +#ifdef __GNUC__ +/* amusing efficiency hack */ +#include +#define str_append(buf, s) str_appendn(buf, s, \ + (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) ) +#else +void str_append(struct str *buf, const char *s); +#endif + +size_t xvasprintf(char **result, const char *format, va_list va); +size_t xasprintf(char **result, const char *format, ...); +void str_appendf(struct str *buf, const char *format, ...); + +struct str *length_of_time(const time_t t); + +/* vim:set ts=3 sw=3 tw=78 expandtab: */ diff --git a/stylecss.h b/stylecss.h new file mode 100644 index 0000000..2bf6b59 --- /dev/null +++ b/stylecss.h @@ -0,0 +1,70 @@ +/* this file was automatically generated */ +static char style_css[] = +"/* darkstat 3\n" +" *\n" +" * style.css: CSS stylesheet for web interface.\n" +" * copyright (c) 2006 Ben Stewart.\n" +" * colors broken in 2007 by Emil Mikulic.\n" +" *\n" +" * You may use, modify and redistribute this file under the terms of the\n" +" * GNU General Public License version 2. (see COPYING.GPL)\n" +" */\n" +"\n" +"body { background-color: #fff; z-index: 0; }\n" +".content { z-index: 1;\n" +" position: absolute; top:15px; left:10px; }\n" +"div.menu { z-index: 2;\n" +" position: absolute; top:0; left:0;\n" +" width: 100%; background-color: #789;\n" +" border-bottom: 1px solid black;\n" +" font-size:11px; }\n" +"ul.menu { list-style: none; margin:0; padding:2px 0 3px 0; }\n" +"ul.menu li { list-style: none; display: inline; margin:0;\n" +" padding:2px 0 3px 0;\n" +" border-right:1px solid white; }\n" +"ul.menu li.label\n" +" { padding-left:10px; padding-right:10px; color:#000;\n" +" text-shadow: 0px 1px 0px #9ab; }\n" +"ul.menu li a { color: white; text-decoration: none;\n" +" text-shadow: 0px 1px 0px #456;\n" +" border-bottom: none; padding:2px 15px 3px 15px; }\n" +"ul.menu li a:hover\n" +" { background-color: #9ab; }\n" +"h1, h2, h3, h4, h5, h6\n" +" { margin-top:10px; margin-bottom:5px; color: #000000;\n" +" font-family: Arial, sans-serif; font-weight:bold; }\n" +".pageheader { border-bottom: 2px dotted black; }\n" +"table { border-collapse: collapse; }\n" +"td, th { border:1px solid #C0C0C0; padding:1px 5px 1px 5px; }\n" +"td.num { text-align:right; }\n" +"th { background-color:#EFEFEF; font-weight: bold;\n" +" padding-top:2px; padding-bottom:2px; }\n" +"th a { color:black; border-bottom:1px dotted; }\n" +"tr.alt1 { background:#FFFFFF; }\n" +"tr.alt2 { background:#FAFAFA; }\n" +"tr.alt1:hover, tr.alt2:hover { background:#EFEFEF; }\n" +"body, td, th, p, input, textarea\n" +" { font-family: Tahoma, Verdana, sans-serif;\n" +" font-size: small; }\n" +"tt { font-family: Courier New, monospace;\n" +" font-size: small; }\n" +"a:hover { border-bottom: 1px dotted #666; }\n" +"a { text-decoration: none; color: #666; }\n" +"div.outergraph { float:left; margin-right:10px; margin-bottom:20px; }\n" +"div.graph { border: 1px solid black; }\n" +"div.graphtitle { text-align:center; font-weight:bold; }\n" +"div.bar_in { background: #678; }\n" +"div.bar_out { background: #abc; }\n" +"\n" +"#graph_reload,#graph_autoreload { border:1px solid black;\n" +" padding:2px 10px 2px 10px; margin-left:5px; color:black; }\n" +"\n" +"#graph_reload:hover,#graph_autoreload:hover { background:#9ab; color:black; }\n" +"\n" +"div.legend table { margin-left:auto; margin-right:auto; /* center */\n" +" border:0; }\n" +"div.legend td { border:0; padding:0 0.2em 0 0.2em; font-size:11px;\n" +" color:#444; }\n" +"div.legend td.dir { text-align:right; }\n" +"div.legend td.rate { text-align:right; white-space: nowrap; }\n"; +static const size_t style_css_len = sizeof(style_css) - 1; diff --git a/tree.h b/tree.h new file mode 100644 index 0000000..f533547 --- /dev/null +++ b/tree.h @@ -0,0 +1,394 @@ +/* This is a cut down version of FreeBSD's + * src/sys/sys/tree.h,v 1.5 + * + * Also tagged + * $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ + * $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ + * + * The original file's license: + * + * Copyright 2002 Niels Provos + * 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. + */ + +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) { NULL } + +#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_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 (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_AUGMENT(x) do {} while (0) + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + 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 (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + 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 (/*CONSTCOND*/ 0) + +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + 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; \ +} \ + \ +attr 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)) \ + != NULL) \ + 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)) \ + != NULL) \ + 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; \ +} \ + \ +attr 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)) != NULL) \ + 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)) != NULL); \ + } \ + 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 */ \ +attr 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 */ \ +attr 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); \ +} \ + \ +/* ARGSUSED */ \ +attr 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); \ +} \ + \ +attr 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_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)