diff options
author | Erik Andersson <erik@packy.se> | 2016-11-22 23:56:43 +0100 |
---|---|---|
committer | Erik Andersson <erik@packy.se> | 2016-11-22 23:56:43 +0100 |
commit | 41954fffc10bfd230f857f57c6871b412d5f2e91 (patch) | |
tree | 010bcf28f294a58b4a22b7276cf615758648bcbe | |
parent | ed374a8dbcdaaf273964293d2805bdd61b148022 (diff) | |
download | ocelot-master.zip ocelot-master.tar.gz ocelot-master.tar.bz2 |
38 files changed, 2895 insertions, 1289 deletions
diff --git a/src/CHANGES b/src/CHANGES index fd67234..97dc028 100644 --- a/src/CHANGES +++ b/src/CHANGES @@ -1,73 +1,88 @@ -Date: Thu Jan 16 - - Get client IP from the x-forwarded-for header if it is provided - -Date: Thu Jan 16 - - Add setting for maximum request size - -Date: Thu Dec 19 - - Read/write full requests even if they are - split into several recv/send calls - -Date: Tue Dec 5 - - Remove most of the dependencies on boost and move all thread-related - calls to the STL components provided in C++11. - -Date: Tue Jun 4 - - Update the db's torrents table when the peer reaper - deletes the last peer on a torrent - -Date: Tue Jun 4 - - Collect some stats and add a method that - prints them in a way that's easy to process - -Date: Sun Jun 3 - - Create a function for adding peers - Make use of the peer iterator for faster deletions - Fix a potential problem where a deleted pointer was being accessed - -Date: Sun Jun 2 - - Separate the sections responsible for output to make it easier to - change http headers if needed - -Date: Tue Mar 19 19:20:21 2013 - - Add --with-tcmalloc option to the configure script to enable Google's - high-performance multi-threaded malloc() implementation - -Date: Wed Feb 6 05:26:58 2013 - - Send special data to search engine crawlers (and other odd clients) in an - attempt to prevent indexing of announce URLs - -Date: Sat Feb 2 18:24:19 2013 - - Use a lighter query to update peer records in xbt_files_users if peer data - has not changed since its previous announcement - -Date: Thu Jan 31 - - Expire multiple tokens in a single request to the web server - -Date: Sat Nov 17 - - Print warning instead of error if someone submits an IPv6 or otherwise - invalid address - -Date: Sun Oct 7 - - Make flush queries slightly less spammy by moving them to the functions - that invoke flush threads. This makes Ocelot print all queue lengths every - <schedule_interval> seconds. It also fixes the problem where two threads - are writing to stdout at the same time - -Date: Fri Oct 5 - - Put primitive types at the beginning of the peer and torrent structs +-- 1.0 (2015-01-26) +NOTE: This version requires the following database change: +ALTER TABLE xbt_files_users DROP PRIMARY KEY, ADD PRIMARY KEY (peer_id,fid,uid) + +Add a 'deleted' flag to user object to stop including removed users in returned peer lists +Add a prefix to the peer list keys for randomization and smaller chance of peer id collisions +Add HTTP Keep-Alive support and show request rate in the tracker stats +Add readonly mode for easier testing +Catch exceptions in the mysql::load_* functions +Configurable max/default numwant parameter +Don't disable binlogs in the peer flush sessions +Don't start a reaper thread if it's already running +Inline peer_is_visible and user functions +Mark nonchanging parameter as const +Mark users with IP = 127.0.0.1 as protected +Read settings from a config file instead of a compiled object +Reload torrent list, user list and client whitelist on SIGUSR1 +Use atomic variables for stats +Use consistent integer widths where it matters +Use std::lock_guard instead of std::unique_lock for mutex + +-- 0.8 (2014-03-27) +Add setting for maximum request size +Get client IP from the x-forwarded-for header if it is provided +Read/write full requests even if they are split into several recv/send calls + +-- 0.7 (2013-12-05) +Add bintohex function to use when printing binary data to stdout +Check if compiler supports -std=c++11 +Collect some stats and add a method that prints them +Create a function for adding peers +Fix a potential problem where a deleted pointer was being accessed +Improve the anti-web crawler code +Make use of the peer iterator for faster deletions +Remove most of the dependencies on boost and move all thread-related calls to the STL components provided in C++11 +Run the reaper routines in a single thread to clean up stdout +Separate the sections responsible for output to make it easier to change http headers if needed +Update the db's torrents table when the peer reaper deletes the last peer on a torrent + +-- 0.6 (2013-03-19) +Add --with-tcmalloc option to the configure script to enable Google's high-performance multi-threaded malloc() implementation +Configurable site path +Expire multiple tokens in a single request to the web server +Less spammy output unless a -v switch is passed when starting Ocelot +Make flush queries slightly less spammy by moving them to the functions that invoke flush threads +Print warning instead of error if someone submits an IPv6 or otherwise invalid address +Remove unused logger class +Send special data to search engine crawlers (and other odd clients) in an attempt to prevent indexing of announce URLs +Use a lighter query to update peer records in xbt_files_users if a peer's data has not changed since its previous announcement + +-- 0.5 (2012-10-08) +Add ability to show reasons why a torrent was deleted on unregistered torrents +Add gzip compression support for announces and scrapes +Autotools build system +Fix incorrect handling of "corrupt" stat +Make Ocelot clear stale peer data out of the db on startup +Track transfer stats from users without leech privs + +-- 0.4 (2012-06-20) +Convert all headers to lowercase +Don't show users to themselves (patch by GrecKo) +Don't write peer updates to the binlog +Search for peer in leecher list if not found in seeders list +Sort response dictionary according to BitTorrent specs + +-- 0.3 (2012-02-03) +Add tokens +Added a logging function +Added DB_LOCK_TIMEOUT constant +Added info command +Added neutral leech +Added transactions and backups around the peer/user queries +Added working makefile +Adding more debug to query outputs +Changed flushing logic to prevent buildups +Clean shutdown +Don't print complete query if an error occurred +Fixed thread visibility +Flush every schedule +More debug output +New DB class with queue +Nicer schedule display + +-- 0.2 alpha (2011-01-11) +Bug fixes + +-- 0.1 alpha (2010-10-27) +First release diff --git a/src/Makefile.am b/src/Makefile.am index 7f0cc04..926b847 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,15 +1,14 @@ ACLOCAL_AMFLAGS = -I m4 bin_PROGRAMS = ocelot -ocelot_SOURCES = config.h db.cpp db.h events.cpp events.h misc_functions.cpp misc_functions.h\ +ocelot_SOURCES = config.cpp config.h db.cpp db.h events.cpp events.h misc_functions.cpp misc_functions.h \ ocelot.cpp ocelot.h report.cpp report.h response.cpp response.h \ schedule.cpp schedule.h site_comm.cpp site_comm.h user.cpp user.h worker.cpp worker.h -nodist_ocelot_SOURCES = config.cpp -AM_CPPFLAGS = -std=c++11 -march=native -O2 -fvisibility=hidden -fvisibility-inlines-hidden -fomit-frame-pointer -fno-ident -pthread -Wall -Wfatal-errors -Wl,O1 -Wl,--as-needed $(BOOST_CPPFLAGS) -I$(MYSQLPP_INC_DIR) -I$(EV_INC_DIR) -I$(MYSQL_C_INC_DIR) -ocelot_LDFLAGS = $(BOOST_IOSTREAMS_LIB) $(BOOST_SYSTEM_LIB) -L$(MYSQLPP_LIB_DIR) -pthread -ocelot_LDADD = $(BOOST_IOSTREAMS_LIB) $(BOOST_SYSTEM_LIB) -lev -lmysqlpp -EXTRA_DIST = CHANGES LICENSE config.cpp.template +AM_CXXFLAGS = -std=c++11 -march=native -O2 -fvisibility=hidden -fvisibility-inlines-hidden -fomit-frame-pointer -fno-ident -Wall -Wfatal-errors $(PTHREAD_CFLAGS) $(BOOST_CPPFLAGS) +ocelot_LDADD = $(PTHREAD_LIBS) $(BOOST_IOSTREAMS_LIB) $(BOOST_SYSTEM_LIB) +AM_LDFLAGS = -Wl,-O1 -Wl,--as-needed +EXTRA_DIST = CHANGES LICENSE README.md ocelot.conf.dist dist-hook: touch ${distdir}/configure patch -p2 -d ${distdir} --no-backup-if-mismatch < ../dist.patch diff --git a/src/Makefile.in b/src/Makefile.in index 6cd2ee8..8a06e06 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -77,21 +87,21 @@ NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ +host_triplet = @host@ bin_PROGRAMS = ocelot$(EXEEXT) subdir = . -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/configure $(am__configure_deps) depcomp compile \ - config.guess config.sub install-sh missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_base.m4 \ $(top_srcdir)/m4/ax_boost_iostreams.m4 \ $(top_srcdir)/m4/ax_boost_system.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ev++.m4 $(top_srcdir)/m4/mysql++.m4 \ - $(top_srcdir)/m4/mysql_loc.m4 $(top_srcdir)/m4/tcmalloc.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/ev++.m4 \ + $(top_srcdir)/m4/mysql++.m4 $(top_srcdir)/m4/mysql_loc.m4 \ + $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d @@ -99,16 +109,14 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) -am_ocelot_OBJECTS = db.$(OBJEXT) events.$(OBJEXT) \ +am_ocelot_OBJECTS = config.$(OBJEXT) db.$(OBJEXT) events.$(OBJEXT) \ misc_functions.$(OBJEXT) ocelot.$(OBJEXT) report.$(OBJEXT) \ response.$(OBJEXT) schedule.$(OBJEXT) site_comm.$(OBJEXT) \ user.$(OBJEXT) worker.$(OBJEXT) -nodist_ocelot_OBJECTS = config.$(OBJEXT) -ocelot_OBJECTS = $(am_ocelot_OBJECTS) $(nodist_ocelot_OBJECTS) +ocelot_OBJECTS = $(am_ocelot_OBJECTS) am__DEPENDENCIES_1 = -ocelot_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -ocelot_LINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(ocelot_LDFLAGS) \ - $(LDFLAGS) -o $@ +ocelot_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -150,7 +158,7 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = -SOURCES = $(ocelot_SOURCES) $(nodist_ocelot_SOURCES) +SOURCES = $(ocelot_SOURCES) DIST_SOURCES = $(ocelot_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -178,6 +186,8 @@ ETAGS = etags CTAGS = ctags CSCOPE = cscope AM_RECURSIVE_TARGETS = cscope +am__DIST_COMMON = $(srcdir)/Makefile.in compile config.guess \ + config.sub depcomp install-sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -250,6 +260,9 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -265,6 +278,7 @@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -277,7 +291,11 @@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ +host = @host@ host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ @@ -302,15 +320,14 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I m4 -ocelot_SOURCES = config.h db.cpp db.h events.cpp events.h misc_functions.cpp misc_functions.h\ +ocelot_SOURCES = config.cpp config.h db.cpp db.h events.cpp events.h misc_functions.cpp misc_functions.h \ ocelot.cpp ocelot.h report.cpp report.h response.cpp response.h \ schedule.cpp schedule.h site_comm.cpp site_comm.h user.cpp user.h worker.cpp worker.h -nodist_ocelot_SOURCES = config.cpp -AM_CPPFLAGS = -std=c++11 -march=native -O2 -fvisibility=hidden -fvisibility-inlines-hidden -fomit-frame-pointer -fno-ident -pthread -Wall -Wfatal-errors -Wl,O1 -Wl,--as-needed $(BOOST_CPPFLAGS) -I$(MYSQLPP_INC_DIR) -I$(EV_INC_DIR) -I$(MYSQL_C_INC_DIR) -ocelot_LDFLAGS = $(BOOST_IOSTREAMS_LIB) $(BOOST_SYSTEM_LIB) -L$(MYSQLPP_LIB_DIR) -pthread -ocelot_LDADD = $(BOOST_IOSTREAMS_LIB) $(BOOST_SYSTEM_LIB) -lev -lmysqlpp -EXTRA_DIST = CHANGES LICENSE config.cpp.template +AM_CXXFLAGS = -std=c++11 -march=native -O2 -fvisibility=hidden -fvisibility-inlines-hidden -fomit-frame-pointer -fno-ident -Wall -Wfatal-errors $(PTHREAD_CFLAGS) $(BOOST_CPPFLAGS) +ocelot_LDADD = $(PTHREAD_LIBS) $(BOOST_IOSTREAMS_LIB) $(BOOST_SYSTEM_LIB) +AM_LDFLAGS = -Wl,-O1 -Wl,--as-needed +EXTRA_DIST = CHANGES LICENSE README.md ocelot.conf.dist all: all-am .SUFFIXES: @@ -330,7 +347,6 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -394,7 +410,7 @@ clean-binPROGRAMS: ocelot$(EXEEXT): $(ocelot_OBJECTS) $(ocelot_DEPENDENCIES) $(EXTRA_ocelot_DEPENDENCIES) @rm -f ocelot$(EXEEXT) - $(AM_V_CXXLD)$(ocelot_LINK) $(ocelot_OBJECTS) $(ocelot_LDADD) $(LIBS) + $(AM_V_CXXLD)$(CXXLINK) $(ocelot_OBJECTS) $(ocelot_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -546,15 +562,15 @@ dist-xz: distdir $(am__post_remove_distdir) dist-tarZ: distdir - @echo WARNING: "Support for shar distribution archives is" \ - "deprecated." >&2 + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir - @echo WARNING: "Support for distribution archives compressed with" \ - "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) @@ -590,17 +606,17 @@ distcheck: dist esac chmod -R a-w $(distdir) chmod u+w $(distdir) - mkdir $(distdir)/_build $(distdir)/_inst + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ - && $(am__cd) $(distdir)/_build \ - && ../configure \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ - --srcdir=.. --prefix="$$dc_install_base" \ + --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ @@ -781,6 +797,8 @@ uninstall-am: uninstall-binPROGRAMS mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS +.PRECIOUS: Makefile + dist-hook: touch ${distdir}/configure patch -p2 -d ${distdir} --no-backup-if-mismatch < ../dist.patch diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..ab1e1cb --- /dev/null +++ b/src/README.md @@ -0,0 +1,45 @@ +# Ocelot + +Ocelot is a BitTorrent tracker written in C++ for the [Gazelle](http://whatcd.github.io/Gazelle/) project. It supports requests over TCP and can only track IPv4 peers. + +## Ocelot Compile-time Dependencies + +* [GCC/G++](http://gcc.gnu.org/) (4.7+ required; 4.8.1+ recommended) +* [Boost](http://www.boost.org/) (1.55.0+ required) +* [libev](http://software.schmorp.de/pkg/libev.html) (required) +* [MySQL++](http://tangentsoft.net/mysql++/) (3.2.0+ required) +* [TCMalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html) (optional, but strongly recommended) + +## Installation + +The [Gazelle installation guides](https://github.com/WhatCD/Gazelle/wiki/Gazelle-installation) include instructions for installing Ocelot as a part of the Gazelle project. + +### Standalone Installation + +* Create the following tables according to the [Gazelle database schema](https://raw.githubusercontent.com/WhatCD/Gazelle/master/gazelle.sql): + - `torrents` + - `users_freeleeches` + - `users_main` + - `xbt_client_whitelist` + - `xbt_files_users` + - `xbt_snatched` + +* Edit `ocelot.conf` to your liking. + +* Build Ocelot: + + ./configure + make + make install + +## Running Ocelot + +### Run-time options: + +* `-c <path/to/ocelot.conf>` - Path to config file. If unspecified, the current working directory is used. +* `-v` - Print queue status every time a flush is initiated. + +### Signals + +* `SIGHUP` - Reload config +* `SIGUSR1` - Reload torrent list, user list and client whitelist diff --git a/src/aclocal.m4 b/src/aclocal.m4 index 7826a8f..15381c1 100644 --- a/src/aclocal.m4 +++ b/src/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.14.1 -*- Autoconf -*- +# generated automatically by aclocal 1.15 -*- Autoconf -*- -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# Copyright (C) 2002-2013 Free Software Foundation, Inc. +# Copyright (C) 2002-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -32,10 +32,10 @@ To do so, use the procedure documented by the package, typically 'autoreconf'.]) # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.14' +[am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.14.1], [], +m4_if([$1], [1.15], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.14.1])dnl +[AM_AUTOMAKE_VERSION([1.15])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -103,15 +103,14 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], -[dnl Rely on autoconf to set up CDPATH properly. -AC_PREREQ([2.50])dnl -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -142,7 +141,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -333,7 +332,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -409,7 +408,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -499,8 +498,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl # <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> AC_SUBST([mkdir_p], ['$(MKDIR_P)']) -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl @@ -573,7 +572,11 @@ to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi -fi]) +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further @@ -602,7 +605,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -613,7 +616,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -if test x"${install_sh}" != xset; then +if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; @@ -623,7 +626,7 @@ if test x"${install_sh}" != xset; then fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# Copyright (C) 2003-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -644,7 +647,7 @@ AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -694,7 +697,7 @@ rm -f confinc confmf # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -733,7 +736,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -762,7 +765,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -809,7 +812,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -828,7 +831,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -909,7 +912,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# Copyright (C) 2009-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -969,7 +972,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -997,7 +1000,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# Copyright (C) 2006-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1016,7 +1019,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# Copyright (C) 2004-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1151,6 +1154,7 @@ m4_include([m4/ax_boost_base.m4]) m4_include([m4/ax_boost_iostreams.m4]) m4_include([m4/ax_boost_system.m4]) m4_include([m4/ax_check_compile_flag.m4]) +m4_include([m4/ax_pthread.m4]) m4_include([m4/ev++.m4]) m4_include([m4/mysql++.m4]) m4_include([m4/mysql_loc.m4]) diff --git a/src/compile b/src/compile index 531136b..a85b723 100755 --- a/src/compile +++ b/src/compile @@ -3,7 +3,7 @@ scriptversion=2012-10-14.11; # UTC -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey <tromey@cygnus.com>. # # This program is free software; you can redistribute it and/or modify diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..e1f8057 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,156 @@ +#include <iostream> +#include <fstream> +#include <string> +#include "config.h" +#include "misc_functions.h" + +confval::confval() { + bool_val = 0; + uint_val = 0; + str_val = ""; + val_type = CONF_NONEXISTENT; +} + +confval::confval(bool value) { + bool_val = value; + val_type = CONF_BOOL; +} + +confval::confval(unsigned int value) { + uint_val = value; + val_type = CONF_UINT; +} + +confval::confval(const char * value) { + str_val = value; + val_type = CONF_STR; +} + +bool confval::get_bool() { + return bool_val; +} + +unsigned int confval::get_uint() { + return uint_val; +} + +std::string confval::get_str() { + return str_val; +} + +void confval::set(const std::string &value) { + if (val_type == CONF_BOOL) { + bool_val = value == "1" || value == "true" || value == "yes"; + } else if (val_type == CONF_UINT) { + uint_val = strtoint32(value); + } else if (val_type == CONF_STR) { + str_val = value; + } +} + +config::config() { + init(); + dummy_setting = new confval(); // Safety value to use if we're accessing nonexistent settings +} + +void config::init() { + // Internal stuff + add("listen_port", 34000u); + add("max_connections", 1024u); + add("max_middlemen", 20000u); + add("max_read_buffer", 4096u); + add("connection_timeout", 10u); + add("keepalive_timeout", 0u); + + // Tracker requests + add("announce_interval", 1800u); + add("max_request_size", 4096u); + add("numwant_limit", 50u); + add("request_log_size", 500u); + + // Timers + add("del_reason_lifetime", 86400u); + add("peers_timeout", 7200u); + add("reap_peers_interval", 1800u); + add("schedule_interval", 3u); + + // MySQL + add("mysql_db", "gazelle"); + add("mysql_host", "localhost"); + add("mysql_username", ""); + add("mysql_password", ""); + + // Site communication + add("site_host", "127.0.0.1"); + add("site_path", ""); + add("site_password", "00000000000000000000000000000000"); + add("report_password", "00000000000000000000000000000000"); + + // Debugging + add("readonly", false); +} + +confval * config::get(const std::string &setting_name) { + const auto setting = settings.find(setting_name); + if (setting == settings.end()) { + std::cout << "WARNING: Unrecognized setting '" << setting_name << "'" << std::endl; + return dummy_setting; + } + return &setting->second; +} + +bool config::get_bool(const std::string &setting_name) { + return get(setting_name)->get_bool(); +} + +unsigned int config::get_uint(const std::string &setting_name) { + return get(setting_name)->get_uint(); +} + +std::string config::get_str(const std::string &setting_name) { + return get(setting_name)->get_str(); +} + +void config::set(const std::string &setting_name, const std::string &value) { + get(setting_name)->set(value); +} + +void config::load(const std::string &conf_file_path, std::istream &conf_file) { + load(conf_file); + add("conf_file_path", conf_file_path.c_str()); +} + +void config::load(std::istream &conf_file) { + std::string line; + while (getline(conf_file, line)) { + size_t pos; + if (line[0] != '#' && (pos = line.find('=')) != std::string::npos) { + std::string key(trim(line.substr(0, pos))); + std::string value(trim(line.substr(pos + 1))); + set(key, value); + } + } +} + +void config::reload() { + const std::string conf_file_path(get_str("conf_file_path")); + std::ifstream conf_file(conf_file_path); + if (conf_file.fail()) { + std::cout << "Config file '" << conf_file_path << "' couldn't be opened" << std::endl; + } else { + init(); + load(conf_file); + } +} + +std::string config::trim(const std::string str) { + size_t ltrim = str.find_first_not_of(" \t"); + if (ltrim == std::string::npos) { + ltrim = 0; + } + size_t rtrim = str.find_last_not_of(" \t"); + if (ltrim != 0 || rtrim != str.length() - 1) { + return str.substr(ltrim, rtrim - ltrim + 1); + } + return str; +} diff --git a/src/config.cpp.template b/src/config.cpp.template deleted file mode 100644 index e1128a4..0000000 --- a/src/config.cpp.template +++ /dev/null @@ -1,32 +0,0 @@ -#include "config.h" - -config::config() { - host = "127.0.0.1"; - port = 34000; - max_connections = 512; - max_read_buffer = 4096; - max_request_size = 4096; - timeout_interval = 20; - schedule_interval = 3; - max_middlemen = 5000; - - announce_interval = 1800; - peers_timeout = 2700; //Announce interval * 1.5 - - reap_peers_interval = 1800; - del_reason_lifetime = 604800; - - // MySQL - mysql_db = "gazelle"; - mysql_host = "127.0.0.1:3306"; - mysql_username = "***"; - mysql_password = "***"; - - // Site communication - site_host = "localhost"; - site_password = "********************************"; // MUST BE 32 CHARS - site_path = ""; // If the site is not running under the domain root - - // Key to use for /report?get=stats and /report?get=user&key=<passkey> requests - report_password = "********************************"; // MUST BE 32 CHARS -} diff --git a/src/config.h b/src/config.h index 97d9d68..ce76ec4 100644 --- a/src/config.h +++ b/src/config.h @@ -2,38 +2,51 @@ #define OCELOT_CONFIG_H #include <string> +#include <map> + +class confval { + private: + bool bool_val; + uint32_t uint_val; + std::string str_val; + enum { + CONF_NONEXISTENT, + CONF_BOOL, + CONF_UINT, + CONF_STR, + } val_type; + public: + confval(); + confval(bool value); + confval(uint32_t value); + confval(const char * value); + uint32_t get_uint(); + bool get_bool(); + std::string get_str(); + void set(const std::string &val); +}; class config { + private: + template <typename T> void add(const std::string &setting_name, T value); + std::string trim(const std::string str); + void init(); + confval * get(const std::string &setting_name); + std::map<std::string, confval> settings; + confval * dummy_setting; public: - std::string host; - unsigned int port; - unsigned int max_connections; - unsigned int max_read_buffer; - unsigned int max_request_size; - unsigned int timeout_interval; - unsigned int schedule_interval; - unsigned int max_middlemen; - - unsigned int announce_interval; - unsigned int peers_timeout; - - unsigned int reap_peers_interval; - unsigned int del_reason_lifetime; - - // MySQL - std::string mysql_db; - std::string mysql_host; - std::string mysql_username; - std::string mysql_password; - - // Site communication - std::string site_host; - std::string site_password; - std::string site_path; - - std::string report_password; - config(); + void load(std::istream &conf_file); + void load(const std::string &conf_file_path, std::istream &conf_file); + void reload(); + bool get_bool(const std::string &setting_name); + uint32_t get_uint(const std::string &setting_name); + std::string get_str(const std::string &setting_name); + void set(const std::string &setting_name, const std::string &value); }; +template <typename T> void config::add(const std::string &setting_name, T value) { + confval setting(value); + settings[setting_name] = setting; +} #endif diff --git a/src/configure b/src/configure index b675d63..a65747b 100755 --- a/src/configure +++ b/src/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for ocelot 0.8. +# Generated by GNU Autoconf 2.69 for ocelot 1.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='ocelot' PACKAGE_TARNAME='ocelot' -PACKAGE_VERSION='0.8' -PACKAGE_STRING='ocelot 0.8' +PACKAGE_VERSION='1.0' +PACKAGE_STRING='ocelot 1.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -622,8 +622,6 @@ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS -EV_INC_DIR -EV_LIB_DIR MYSQLPP_INC_DIR MYSQLPP_LIB_DIR MYSQL_C_LIB_NAME @@ -632,20 +630,30 @@ MYSQL_C_INC_DIR EGREP GREP CPP +EV_INC_DIR +EV_LIB_DIR +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CC +ax_pthread_config +host_os +host_vendor +host_cpu +host BOOST_SYSTEM_LIB build_os build_vendor build_cpu build BOOST_IOSTREAMS_LIB -BOOST_LDFLAGS -BOOST_CPPFLAGS am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE ac_ct_CC CFLAGS CC +BOOST_LDFLAGS +BOOST_CPPFLAGS am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE @@ -737,15 +745,15 @@ with_boost with_boost_libdir with_boost_iostreams with_boost_system +with_ev +with_ev_lib +with_ev_include with_mysql with_mysql_lib with_mysql_include with_mysqlpp with_mysqlpp_lib with_mysqlpp_include -with_ev -with_ev_lib -with_ev_include with_tcmalloc ' ac_precious_vars='build_alias @@ -1300,7 +1308,7 @@ 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 ocelot 0.8 to adapt to many kinds of systems. +\`configure' configures ocelot 1.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1364,12 +1372,13 @@ Program names: System types: --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ocelot 0.8:";; + short | recursive ) echo "Configuration of ocelot 1.0:";; esac cat <<\_ACEOF @@ -1405,6 +1414,10 @@ Optional Packages: use the System library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-system=boost_system-gcc-mt + --with-ev=<path> path containing libev header and library subdirs + --with-ev-lib=<path> directory path of libev library + --with-ev-include=<path> + directory path of libev headers --with-mysql=<path> root directory path of MySQL installation --with-mysql-lib=<path> directory path of MySQL library installation --with-mysql-include=<path> @@ -1414,10 +1427,6 @@ Optional Packages: directory path of MySQL++ library --with-mysqlpp-include=<path> directory path of MySQL++ headers - --with-ev=<path> path containing libev header and library subdirs - --with-ev-lib=<path> directory path of libev library - --with-ev-include=<path> - directory path of libev headers --with-tcmalloc use tcmalloc if it's available [default=yes] Some influential environment variables: @@ -1498,7 +1507,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -ocelot configure 0.8 +ocelot configure 1.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1671,6 +1680,52 @@ fi } # ac_fn_c_try_cpp +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_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_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + 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_cxx_try_link + # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using @@ -1834,7 +1889,7 @@ 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 ocelot $as_me 0.8, which was +It was created by ocelot $as_me 1.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2182,7 +2237,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu -am__api_version='1.14' +am__api_version='1.15' ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do @@ -2383,8 +2438,8 @@ test "$program_suffix" != NONE && ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in @@ -2403,7 +2458,7 @@ else $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi -if test x"${install_sh}" != xset; then +if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; @@ -2697,7 +2752,7 @@ fi # Define the identity of the package. PACKAGE='ocelot' - VERSION='0.8' + VERSION='1.0' # Some tools Automake needs. @@ -2722,8 +2777,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> mkdir_p='$(MKDIR_P)' -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' @@ -2780,6 +2835,7 @@ END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi + ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -3480,6 +3536,353 @@ fi ac_config_files="$ac_config_files Makefile" +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -std=c++11" >&5 +$as_echo_n "checking whether C++ compiler accepts -std=c++11... " >&6; } +if ${ax_cv_check_cxxflags___std_cpp11+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CXXFLAGS + CXXFLAGS="$CXXFLAGS -std=c++11" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_check_cxxflags___std_cpp11=yes +else + ax_cv_check_cxxflags___std_cpp11=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___std_cpp11" >&5 +$as_echo "$ax_cv_check_cxxflags___std_cpp11" >&6; } +if test x"$ax_cv_check_cxxflags___std_cpp11" = xyes; then : + : +else + as_fn_error $? "Compiler does not support -std=c++11" "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -fstack-protector-strong" >&5 +$as_echo_n "checking whether C++ compiler accepts -fstack-protector-strong... " >&6; } +if ${ax_cv_check_cxxflags___fstack_protector_strong+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CXXFLAGS + CXXFLAGS="$CXXFLAGS -fstack-protector-strong" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_check_cxxflags___fstack_protector_strong=yes +else + ax_cv_check_cxxflags___fstack_protector_strong=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___fstack_protector_strong" >&5 +$as_echo "$ax_cv_check_cxxflags___fstack_protector_strong" >&6; } +if test x"$ax_cv_check_cxxflags___fstack_protector_strong" = xyes; then : + CXXFLAGS="-fstack-protector-strong $CXXFLAGS" +else + : +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 --with-boost was given. +if test "${with_boost+set}" = set; then : + withval=$with_boost; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + +else + want_boost="yes" +fi + + + + +# Check whether --with-boost-libdir was given. +if test "${with_boost_libdir+set}" = set; then : + withval=$with_boost_libdir; + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 + fi + +else + ac_boost_lib_path="" + +fi + + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=1.37 + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 +$as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } + succeeded=no + + libsubdirs="lib" + ax_arch=`uname -m` + if test $ax_arch = x86_64 -o $ax_arch = ppc64 -o $ax_arch = s390x -o $ax_arch = sparc64; then + libsubdirs="lib64 lib lib64" + fi + + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <boost/version.hpp> + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + 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 "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 +$as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <boost/version.hpp> + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + 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 + + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation." >&5 +$as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation." >&6;} + else + { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 +$as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} + fi + # execute ACTION-IF-NOT-FOUND (if present): + as_fn_error $? "Need boost >= 1.37" "$LINENO" 5 + else + + + +$as_echo "#define HAVE_BOOST /**/" >>confdefs.h + + # execute ACTION-IF-FOUND (if present): + : + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -4208,307 +4611,6 @@ fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -std=c++11" >&5 -$as_echo_n "checking whether C compiler accepts -std=c++11... " >&6; } -if ${ax_cv_check_cflags___std_cpp11+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -std=c++11" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___std_cpp11=yes -else - ax_cv_check_cflags___std_cpp11=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___std_cpp11" >&5 -$as_echo "$ax_cv_check_cflags___std_cpp11" >&6; } -if test x"$ax_cv_check_cflags___std_cpp11" = xyes; then : - : -else - as_fn_error $? "Compiler does not support -std=c++11" "$LINENO" 5 -fi - - - - -# Check whether --with-boost was given. -if test "${with_boost+set}" = set; then : - withval=$with_boost; - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi - -else - want_boost="yes" -fi - - - - -# Check whether --with-boost-libdir was given. -if test "${with_boost_libdir+set}" = set; then : - withval=$with_boost_libdir; - if test -d "$withval" - then - ac_boost_lib_path="$withval" - else - as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 - fi - -else - ac_boost_lib_path="" - -fi - - -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=1.37 - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 -$as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } - succeeded=no - - libsubdirs="lib" - ax_arch=`uname -m` - if test $ax_arch = x86_64 -o $ax_arch = ppc64 -o $ax_arch = s390x -o $ax_arch = sparc64; then - libsubdirs="lib64 lib lib64" - fi - - if test "$ac_boost_path" != ""; then - BOOST_CPPFLAGS="-I$ac_boost_path/include" - for ac_boost_path_tmp in $libsubdirs; do - if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then - BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" - break - fi - done - elif test "$cross_compiling" != yes; then - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - for libsubdir in $libsubdirs ; do - if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" - break; - fi - done - fi - - if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include <boost/version.hpp> - -int -main () -{ - - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - succeeded=yes - found_system=yes - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - 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 "x$succeeded" != "xyes"; then - _version=0 - if test "$ac_boost_path" != ""; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - fi - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" - done - fi - else - if test "$cross_compiling" != yes; then - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - best_path=$ac_boost_path - fi - done - fi - done - - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "$ac_boost_lib_path" = ""; then - for libsubdir in $libsubdirs ; do - if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$best_path/$libsubdir" - fi - fi - - if test "x$BOOST_ROOT" != "x"; then - for libsubdir in $libsubdirs ; do - if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then - version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` - stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` - stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` - V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 -$as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" - fi - fi - fi - fi - - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include <boost/version.hpp> - -int -main () -{ - - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - succeeded=yes - found_system=yes - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - 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 - - fi - - if test "$succeeded" != "yes" ; then - if test "$_version" = "0" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation." >&5 -$as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation." >&6;} - else - { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 -$as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} - fi - # execute ACTION-IF-NOT-FOUND (if present): - as_fn_error $? "Need boost >= 1.37" "$LINENO" 5 - else - - - -$as_echo "#define HAVE_BOOST /**/" >>confdefs.h - - # execute ACTION-IF-FOUND (if present): - : - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" -fi - - # Check whether --with-boost-iostreams was given. @@ -5021,6 +5123,484 @@ fi LDFLAGS="$LDFLAGS_SAVED" fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + + +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 + +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 +$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } + 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 pthread_join (); +int +main () +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler needs -Werror to reject unknown flags" >&5 +$as_echo_n "checking if compiler needs -Werror to reject unknown flags... " >&6; } +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo(void); +int +main () +{ +foo() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + ax_pthread_extra_flags= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +CFLAGS="$save_CFLAGS" + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +$as_echo_n "checking whether pthreads work without any flags... " >&6; } + ;; + + -*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 +$as_echo_n "checking whether pthreads work with $flag... " >&6; } + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; 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_ax_pthread_config+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ax_pthread_config"; then + ac_cv_prog_ax_pthread_config="$ax_pthread_config" # 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 as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ax_pthread_config="yes" + $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 + + test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" +fi +fi +ax_pthread_config=$ac_cv_prog_ax_pthread_config +if test -n "$ax_pthread_config"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 +$as_echo "$ax_pthread_config" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 +$as_echo_n "checking for the pthreads library -l$flag... " >&6; } + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; } +int +main () +{ +pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +$as_echo_n "checking for joinable pthread attribute... " >&6; } + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> +int +main () +{ +int attr = $attr; return attr /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + attr_name=$attr; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 +$as_echo "$attr_name" >&6; } + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + +cat >>confdefs.h <<_ACEOF +#define PTHREAD_CREATE_JOINABLE $attr_name +_ACEOF + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 +$as_echo_n "checking if more special flags are required for pthreads... " >&6; } + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + # TODO: What about Clang on Solaris? + flag="-mt -D_REENTRANT" + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag" >&5 +$as_echo "$flag" >&6; } + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 +$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } +if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <pthread.h> +int +main () +{ +int i = PTHREAD_PRIO_INHERIT; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_cv_PTHREAD_PRIO_INHERIT=yes +else + ax_cv_PTHREAD_PRIO_INHERIT=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 +$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } + if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then : + +$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h + +fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + case "x/$CC" in #( + x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : + #handle absolute path differently from PATH based program lookup + case "x$CC" in #( + x/*) : + if as_fn_executable_p ${CC}_r; then : + PTHREAD_CC="${CC}_r" +fi ;; #( + *) : + for ac_prog in ${CC}_r +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_PTHREAD_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_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 as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PTHREAD_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 +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + ;; +esac ;; #( + *) : + ;; +esac + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + + + + + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h + + : +else + ax_pthread_ok=no + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "pthread library is required +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 + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -5289,6 +5869,176 @@ $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" + + +# Check whether --with-ev was given. +if test "${with_ev+set}" = set; then : + withval=$with_ev; EV_lib_check="$with_ev/lib64 $with_ev/lib $with_ev/lib64/libev $with_ev/lib/libev" + EV_inc_check="$with_ev/include $with_ev/include/libev" +else + EV_lib_check="/usr/local/libev/lib64 /usr/local/libev/lib /usr/local/lib64/libev /usr/local/lib/libev /opt/libev/lib64 /opt/libev/lib /usr/lib64/libev /usr/lib/libev /usr/local/lib64 /usr/local/lib /usr/lib64 /usr/lib" + EV_inc_check="/usr/local/libev/include /usr/local/include/libev /opt/libev/include /usr/local/include/libev /usr/local/include /usr/include/libev /usr/include" +fi + + +# Check whether --with-ev-lib was given. +if test "${with_ev_lib+set}" = set; then : + withval=$with_ev_lib; EV_lib_check="$with_ev_lib $with_ev_lib/lib64 $with_ev_lib/lib $with_ev_lib/lib64/libev $with_ev_lib/lib/libev" +fi + + +# Check whether --with-ev-include was given. +if test "${with_ev_include+set}" = set; then : + withval=$with_ev_include; EV_inc_check="$with_ev_include $with_ev_include/include $with_ev_include/include/libev" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libev library location" >&5 +$as_echo_n "checking for libev library location... " >&6; } +if ${ac_cv_ev_lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + + for dir in $EV_lib_check + do + if test -d "$dir" && \ + ( test -f "$dir/libev.so" || + test -f "$dir/libev.a" ) + then + ac_cv_ev_lib=$dir + break + fi + done + + if test -z "$ac_cv_ev_lib" + then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "Didn't find the libev library dir in '$EV_lib_check'" "$LINENO" 5 + fi + + case "$ac_cv_ev_lib" in + /* ) + ;; + * ) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + as_fn_error $? "The libev library directory ($ac_cv_ev_lib) must be an absolute path." "$LINENO" 5 + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ev_lib" >&5 +$as_echo "$ac_cv_ev_lib" >&6; } + EV_LIB_DIR=$ac_cv_ev_lib + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libev include path" >&5 +$as_echo_n "checking for libev include path... " >&6; } +if ${ac_cv_ev_inc+:} false; then : + $as_echo_n "(cached) " >&6 +else + + for dir in $EV_inc_check + do + if test -d "$dir" && test -f "$dir/ev++.h" + then + ac_cv_ev_inc=$dir + break + fi + done + + if test -z "$ac_cv_ev_inc" + then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "Didn't find the libev header dir in '$EV_inc_check'" "$LINENO" 5 + fi + + case "$ac_cv_ev_inc" in + /* ) + ;; + * ) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + as_fn_error $? "The libev header directory ($ac_cv_ev_inc) must be an absolute path." "$LINENO" 5 + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ev_inc" >&5 +$as_echo "$ac_cv_ev_inc" >&6; } + EV_INC_DIR=$ac_cv_ev_inc + + + case "$ac_cv_ev_lib" in + /usr/lib) + ;; + *) + LDFLAGS="$LDFLAGS -L${ac_cv_ev_lib}" + ;; + esac + CPPFLAGS="$CPPFLAGS -I${ac_cv_ev_inc}" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking that we can build libev programs" >&5 +$as_echo_n "checking that we can build libev programs... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <ev.h> +#if __GNUC__ && (__GNUC__ < 4 || __GNUC__ == 4 && __GNUC_MINOR__ < 9) && EV_VERSION_MAJOR == 4 && EV_VERSION_MINOR == 18 + bad_gcc_libev_ver +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "bad_gcc_libev_ver" >/dev/null 2>&1; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "GCC versions prior to 4.9 cannot compile libev 4.18" "$LINENO" 5 +fi +rm -f conftest* + + + LIBS="-lev $LIBS" + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ev++.h> +int +main () +{ +ev::io i + ; + return 0; +} + +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; 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; } + { { $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 build libev programs +See \`config.log' for more details" "$LINENO" 5; } + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + 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 ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : @@ -5474,24 +6224,30 @@ $as_echo_n "checking for MySQL library directory... " >&6; } if test -z "$MYSQL_C_LIB_DIR" then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } as_fn_error $? "Didn't find $MYSQL_C_LIB_NAME library in '$MYSQL_lib_check'" "$LINENO" 5 fi case "$MYSQL_C_LIB_DIR" in - /* ) ;; - * ) as_fn_error $? "The MySQL library directory ($MYSQL_C_LIB_DIR) must be an absolute path." "$LINENO" 5 ;; + /* ) + ;; + * ) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + as_fn_error $? "The MySQL library directory ($MYSQL_C_LIB_DIR) must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MYSQL_C_LIB_DIR" >&5 $as_echo "$MYSQL_C_LIB_DIR" >&6; } case "$MYSQL_C_LIB_DIR" in - /usr/lib) - MYSQL_C_LIB_DIR= - ;; - *) - LDFLAGS="$LDFLAGS -L${MYSQL_C_LIB_DIR}" - ;; + /usr/lib) + MYSQL_C_LIB_DIR= + ;; + *) + LDFLAGS="$LDFLAGS -L${MYSQL_C_LIB_DIR}" + ;; esac @@ -5512,31 +6268,36 @@ $as_echo_n "checking for MySQL include directory... " >&6; } if test -z "$MYSQL_C_INC_DIR" then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } as_fn_error $? "Didn't find the MySQL include dir in '$MYSQL_inc_check'" "$LINENO" 5 fi case "$MYSQL_C_INC_DIR" in - /* ) ;; - * ) as_fn_error $? "The MySQL include directory ($MYSQL_C_INC_DIR) must be an absolute path." "$LINENO" 5 ;; + /* ) + ;; + * ) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 +$as_echo "" >&6; } + as_fn_error $? "The MySQL include directory ($MYSQL_C_INC_DIR) must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MYSQL_C_INC_DIR" >&5 $as_echo "$MYSQL_C_INC_DIR" >&6; } - save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS -I${MYSQL_C_INC_DIR}" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can link to MySQL C API library directly" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can link to MySQL C API library directly" >&5 $as_echo_n "checking if we can link to MySQL C API library directly... " >&6; } save_LIBS=$LIBS - LIBS="$LIBS -l$MYSQL_C_LIB_NAME $MYSQLPP_EXTRA_LIBS" + LIBS="-l$MYSQL_C_LIB_NAME $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - #include <mysql.h> +#include <mysql.h> int main () { - mysql_store_result(0); +mysql_store_result(0); ; return 0; } @@ -5545,10 +6306,10 @@ if ac_fn_c_try_link "$LINENO"; 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 "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - LIBS="$save_LIBS" - for ac_header in zlib.h + LIBS="$save_LIBS" + for ac_header in zlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" if test "x$ac_cv_header_zlib_h" = xyes; then : @@ -5599,24 +6360,24 @@ _ACEOF LIBS="-lz $LIBS" else - as_fn_error $? "zlib not found" "$LINENO" 5 + as_fn_error $? "zlib not found" "$LINENO" 5 fi fi done - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether adding -lz will let MySQL C API link succeed" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether adding -lz will let MySQL C API link succeed" >&5 $as_echo_n "checking whether adding -lz will let MySQL C API link succeed... " >&6; } - MYSQLPP_EXTRA_LIBS="$MYSQLPP_EXTRA_LIBS -lz" - LIBS="$save_LIBS -l$MYSQL_C_LIB_NAME $MYSQLPP_EXTRA_LIBS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + LIBS="-l$MYSQL_C_LIB_NAME -lz $save_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - #include <mysql.h> +#include <mysql.h> int main () { - mysql_store_result(0); +mysql_store_result(0); + ; return 0; } @@ -5625,10 +6386,9 @@ if ac_fn_c_try_link "$LINENO"; 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 "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - as_fn_error $? "Unable to link to MySQL client library!" "$LINENO" 5 - + as_fn_error $? "Unable to link to MySQL client library!" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext \ @@ -5637,19 +6397,11 @@ rm -f core conftest.err conftest.$ac_objext \ fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - CPPFLAGS=$save_CPPFLAGS - LIBS=$save_LIBS -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - # Check whether --with-mysqlpp was given. @@ -5693,12 +6445,19 @@ else if test -z "$ac_cv_mysqlpp_lib" then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } as_fn_error $? "Didn't find the MySQL++ library dir in '$MYSQLPP_lib_check'" "$LINENO" 5 fi case "$ac_cv_mysqlpp_lib" in - /* ) ;; - * ) as_fn_error $? "The MySQL++ library directory ($ac_cv_mysqlpp_lib) must be an absolute path." "$LINENO" 5 ;; + /* ) + ;; + * ) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "The MySQL++ library directory ($ac_cv_mysqlpp_lib) must be an absolute path." "$LINENO" 5 + ;; esac fi @@ -5724,12 +6483,19 @@ else if test -z "$ac_cv_mysqlpp_inc" then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } as_fn_error $? "Didn't find the MySQL++ header dir in '$MYSQLPP_inc_check'" "$LINENO" 5 fi case "$ac_cv_mysqlpp_inc" in - /* ) ;; - * ) as_fn_error $? "The MySQL++ header directory ($ac_cv_mysqlpp_inc) must be an absolute path." "$LINENO" 5 ;; + /* ) + ;; + * ) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "The MySQL++ header directory ($ac_cv_mysqlpp_inc) must be an absolute path." "$LINENO" 5 + ;; esac fi @@ -5738,13 +6504,21 @@ $as_echo "$ac_cv_mysqlpp_inc" >&6; } MYSQLPP_INC_DIR=$ac_cv_mysqlpp_inc - LDFLAGS_SAVED="$LDFLAGS" - CPPFLAGS_SAVED="$CPPFLAGS" - case "$ac_cv_mysqlpp_lib" in - /usr/lib) ;; - *) LDFLAGS="$LDFLAGS -L${ac_cv_mysqlpp_lib}" ;; + case "$ac_cv_mysqlpp_lib" in + /usr/lib) + ;; + *) + LDFLAGS="$LDFLAGS -L${ac_cv_mysqlpp_lib}" + ;; esac - CPPFLAGS="$CPPFLAGS -I${ac_cv_mysqlpp_inc} -I${MYSQL_C_INC_DIR}" + CPPFLAGS="$CPPFLAGS -I${ac_cv_mysqlpp_inc}" + LIBS="-lmysqlpp $LIBS" + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + { $as_echo "$as_me:${as_lineno-$LINENO}: checking that we can build MySQL++ programs" >&5 $as_echo_n "checking that we can build MySQL++ programs... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -5754,145 +6528,30 @@ int main () { mysqlpp::Connection c(false) - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - as_fn_error $? "no" "$LINENO" 5 -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - LDFLAGS="$LDFLAGS_SAVED" - CPPFLAGS="$CPPFLAGS_SAVED" - - - -# Check whether --with-ev was given. -if test "${with_ev+set}" = set; then : - withval=$with_ev; EV_lib_check="$with_ev/lib64 $with_ev/lib $with_ev/lib64/libev $with_ev/lib/libev" - EV_inc_check="$with_ev/include $with_ev/include/libev" -else - EV_lib_check="/usr/local/libev/lib64 /usr/local/libev/lib /usr/local/lib64/libev /usr/local/lib/libev /opt/libev/lib64 /opt/libev/lib /usr/lib64/libev /usr/lib/libev /usr/local/lib64 /usr/local/lib /usr/lib64 /usr/lib" - EV_inc_check="/usr/local/libev/include /usr/local/include/libev /opt/libev/include /usr/local/include/libev /usr/local/include /usr/include/libev /usr/include" -fi - - -# Check whether --with-ev-lib was given. -if test "${with_ev_lib+set}" = set; then : - withval=$with_ev_lib; EV_lib_check="$with_ev_lib $with_ev_lib/lib64 $with_ev_lib/lib $with_ev_lib/lib64/libev $with_ev_lib/lib/libev" -fi - - -# Check whether --with-ev-include was given. -if test "${with_ev_include+set}" = set; then : - withval=$with_ev_include; EV_inc_check="$with_ev_include $with_ev_include/include $with_ev_include/include/libev" -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libev library location" >&5 -$as_echo_n "checking for libev library location... " >&6; } -if ${ac_cv_ev_lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - - for dir in $EV_lib_check - do - if test -d "$dir" && \ - ( test -f "$dir/libev.so" || - test -f "$dir/libev.a" ) - then - ac_cv_ev_lib=$dir - break - fi - done - - if test -z "$ac_cv_ev_lib" - then - as_fn_error $? "Didn't find the libev library dir in '$EV_lib_check'" "$LINENO" 5 - fi - - case "$ac_cv_ev_lib" in - /* ) ;; - * ) as_fn_error $? "The libev library directory ($ac_cv_ev_lib) must be an absolute path." "$LINENO" 5 ;; - esac - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ev_lib" >&5 -$as_echo "$ac_cv_ev_lib" >&6; } - EV_LIB_DIR=$ac_cv_ev_lib - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libev include path" >&5 -$as_echo_n "checking for libev include path... " >&6; } -if ${ac_cv_ev_inc+:} false; then : - $as_echo_n "(cached) " >&6 -else - - for dir in $EV_inc_check - do - if test -d "$dir" && test -f "$dir/ev++.h" - then - ac_cv_ev_inc=$dir - break - fi - done - - if test -z "$ac_cv_ev_inc" - then - as_fn_error $? "Didn't find the libev header dir in '$EV_inc_check'" "$LINENO" 5 - fi - - case "$ac_cv_ev_inc" in - /* ) ;; - * ) as_fn_error $? "The libev header directory ($ac_cv_ev_inc) must be an absolute path." "$LINENO" 5 ;; - esac - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_ev_inc" >&5 -$as_echo "$ac_cv_ev_inc" >&6; } - EV_INC_DIR=$ac_cv_ev_inc - - - LDFLAGS_SAVED="$LDFLAGS" - CPPFLAGS_SAVED="$CPPFLAGS" - case "$ac_cv_ev_lib" in - /usr/lib) ;; - *) LDFLAGS="$LDFLAGS -L${ac_cv_ev_lib}" ;; - esac - CPPFLAGS="$CPPFLAGS -I${ac_cv_ev_inc}" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking that we can build libev programs" >&5 -$as_echo_n "checking that we can build libev programs... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <ev++.h> -int -main () -{ -ev::io i ; return 0; } _ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : +if ac_fn_cxx_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else - as_fn_error $? "no" "$LINENO" 5 -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - LDFLAGS="$LDFLAGS_SAVED" - CPPFLAGS="$CPPFLAGS_SAVED" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "Cannot build MySQL++ programs" "$LINENO" 5 -ac_ext=c +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + 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 --with-tcmalloc was given. if test "${with_tcmalloc+set}" = set; then : withval=$with_tcmalloc; @@ -5945,12 +6604,9 @@ _ACEOF LIBS="-ltcmalloc $LIBS" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: tcmalloc libraries not installed" >&5 -$as_echo "$as_me: tcmalloc libraries not installed" >&6;} - fi + fi @@ -6524,7 +7180,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ocelot $as_me 0.8, which was +This file was extended by ocelot $as_me 1.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6581,7 +7237,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -ocelot config.status 0.8 +ocelot config.status 1.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/src/configure.ac b/src/configure.ac index c01d3d4..69c9a6f 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -1,17 +1,18 @@ -AC_INIT(ocelot, 0.8) +AC_INIT(ocelot, 1.0) AM_INIT_AUTOMAKE([1.11 no-define foreign]) AC_PROG_CXX AC_CONFIG_FILES([Makefile]) - +AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-std=c++11], [], [AC_MSG_ERROR([Compiler does not support -std=c++11])]) +AX_CHECK_COMPILE_FLAG([-fstack-protector-strong], [CXXFLAGS="-fstack-protector-strong $CXXFLAGS"]) +AC_LANG_POP([C++]) AX_BOOST_BASE([1.37], [], [AC_MSG_ERROR(Need boost >= 1.37)]) AX_BOOST_IOSTREAMS AX_BOOST_SYSTEM +AX_PTHREAD([], AC_MSG_FAILURE([pthread library is required])) +EV_DEVEL MYSQL_C_API_LOCATION -AC_LANG_PUSH(C++) MYSQLPP_DEVEL -EV_DEVEL -AC_LANG_POP(C++) TCMALLOC AC_OUTPUT @@ -2,32 +2,52 @@ #include "db.h" #include "user.h" #include "misc_functions.h" +#include "config.h" #include <string> #include <iostream> #include <queue> #include <unistd.h> -#include <time.h> +#include <ctime> #include <mutex> #include <thread> +#include <unordered_set> #define DB_LOCK_TIMEOUT 50 -mysql::mysql(std::string mysql_db, std::string mysql_host, std::string username, std::string password) : - db(mysql_db), server(mysql_host), db_user(username), pw(password), - u_active(false), t_active(false), p_active(false), s_active(false), tok_active(false) -{ +mysql::mysql(config * conf) : u_active(false), t_active(false), p_active(false), s_active(false), tok_active(false) { + load_config(conf); + if (mysql_db == "") { + std::cout << "No database selected" << std::endl; + return; + } + try { - conn.connect(mysql_db.c_str(), mysql_host.c_str(), username.c_str(), password.c_str(), 0); + mysqlpp::ReconnectOption reconnect(true); + conn.set_option(&reconnect); + conn.connect(mysql_db.c_str(), mysql_host.c_str(), mysql_username.c_str(), mysql_password.c_str(), 0); } catch (const mysqlpp::Exception &er) { std::cout << "Failed to connect to MySQL (" << er.what() << ')' << std::endl; return; } - std::cout << "Connected to MySQL" << std::endl; - std::cout << "Clearing xbt_files_users and resetting peer counts..."; - std::cout.flush(); - clear_peer_data(); - std::cout << "done" << std::endl; + if (!readonly) { + std::cout << "Clearing xbt_files_users and resetting peer counts..."; + std::cout.flush(); + clear_peer_data(); + std::cout << "done" << std::endl; + } +} + +void mysql::load_config(config * conf) { + mysql_db = conf->get_str("mysql_db"); + mysql_host = conf->get_str("mysql_host"); + mysql_username = conf->get_str("mysql_username"); + mysql_password = conf->get_str("mysql_password"); + readonly = conf->get_bool("readonly"); +} + +void mysql::reload_config(config * conf) { + load_config(conf); } bool mysql::connected() { @@ -45,104 +65,195 @@ void mysql::clear_peer_data() { std::cerr << "Unable to reset seeder and leecher count!" << std::endl; } } catch (const mysqlpp::BadQuery &er) { - std::cerr << "Query error: " << er.what() << " in clear_peer_data" << std::endl; + std::cerr << "Query error in clear_peer_data: " << er.what() << std::endl; } catch (const mysqlpp::Exception &er) { - std::cerr << "Query error: " << er.what() << " in clear_peer_data" << std::endl; + std::cerr << "Query error in clear_peer_data: " << er.what() << std::endl; } } void mysql::load_torrents(torrent_list &torrents) { mysqlpp::Query query = conn.query("SELECT ID, info_hash, freetorrent, Snatched FROM torrents ORDER BY ID;"); - if (mysqlpp::StoreQueryResult res = query.store()) { - mysqlpp::String one("1"); // Hack to get around bug in mysql++3.0.0 - mysqlpp::String two("2"); + try { + mysqlpp::StoreQueryResult res = query.store(); + std::unordered_set<std::string> cur_keys; size_t num_rows = res.num_rows(); + std::lock_guard<std::mutex> tl_lock(torrent_list_mutex); + if (torrents.size() == 0) { + torrents.reserve(num_rows * 1.05); // Reserve 5% extra space to prevent rehashing + } else { + // Create set with all currently known info hashes to remove nonexistent ones later + cur_keys.reserve(torrents.size()); + for (auto const &it: torrents) { + cur_keys.insert(it.first); + } + } for (size_t i = 0; i < num_rows; i++) { std::string info_hash; res[i][1].to_string(info_hash); - - torrent t; - t.id = res[i][0]; - if (res[i][2].compare(one) == 0) { - t.free_torrent = FREE; - } else if (res[i][2].compare(two) == 0) { - t.free_torrent = NEUTRAL; + if (info_hash == "") { + continue; + } + mysqlpp::sql_enum free_torrent(res[i][2]); + + torrent tmp_tor; + auto it = torrents.insert(std::pair<std::string, torrent>(info_hash, tmp_tor)); + torrent &tor = (it.first)->second; + if (it.second) { + tor.id = res[i][0]; + tor.balance = 0; + tor.completed = res[i][3]; + tor.last_selected_seeder = ""; + } else { + tor.tokened_users.clear(); + cur_keys.erase(info_hash); + } + if (free_torrent == "1") { + tor.free_torrent = FREE; + } else if (free_torrent == "2") { + tor.free_torrent = NEUTRAL; } else { - t.free_torrent = NORMAL; + tor.free_torrent = NORMAL; } - t.balance = 0; - t.completed = res[i][3]; - t.last_selected_seeder = ""; - torrents[info_hash] = t; } + for (auto const &info_hash: cur_keys) { + // Remove tracked torrents that weren't found in the database + auto it = torrents.find(info_hash); + if (it != torrents.end()) { + torrent &tor = it->second; + stats.leechers -= tor.leechers.size(); + stats.seeders -= tor.seeders.size(); + for (auto &p: tor.leechers) { + p.second.user->decr_leeching(); + } + for (auto &p: tor.seeders) { + p.second.user->decr_seeding(); + } + torrents.erase(it); + } + } + } catch (const mysqlpp::BadQuery &er) { + std::cerr << "Query error in load_torrents: " << er.what() << std::endl; + return; } + std::cout << "Loaded " << torrents.size() << " torrents" << std::endl; + load_tokens(torrents); } void mysql::load_users(user_list &users) { - mysqlpp::Query query = conn.query("SELECT ID, can_leech, torrent_pass, visible FROM users_main WHERE Enabled='1';"); - if (mysqlpp::StoreQueryResult res = query.store()) { + mysqlpp::Query query = conn.query("SELECT ID, can_leech, torrent_pass, (Visible='0' OR IP='127.0.0.1') AS Protected FROM users_main WHERE Enabled='1';"); + try { + mysqlpp::StoreQueryResult res = query.store(); size_t num_rows = res.num_rows(); + std::unordered_set<std::string> cur_keys; + std::lock_guard<std::mutex> ul_lock(user_list_mutex); + if (users.size() == 0) { + users.reserve(num_rows * 1.05); // Reserve 5% extra space to prevent rehashing + } else { + // Create set with all currently known user keys to remove nonexistent ones later + cur_keys.reserve(users.size()); + for (auto const &it: users) { + cur_keys.insert(it.first); + } + } for (size_t i = 0; i < num_rows; i++) { - std::string passkey; - res[i][2].to_string(passkey); - bool protect_ip = res[i][3].compare("1") != 0; - - user_ptr u(new user(res[i][0], res[i][1], protect_ip)); - users.insert(std::pair<std::string, user_ptr>(passkey, u)); + std::string passkey(res[i][2]); + bool protect_ip = res[i][3]; + user_ptr tmp_user = std::make_shared<user>(res[i][0], res[i][1], protect_ip); + auto it = users.insert(std::pair<std::string, user_ptr>(passkey, tmp_user)); + if (!it.second) { + user_ptr &u = (it.first)->second; + u->set_leechstatus(res[i][1]); + u->set_protected(protect_ip); + u->set_deleted(false); + cur_keys.erase(passkey); + } + } + for (auto const &passkey: cur_keys) { + // Remove users that weren't found in the database + auto it = users.find(passkey); + if (it != users.end()) { + it->second->set_deleted(true); + users.erase(it); + } } + } catch (const mysqlpp::BadQuery &er) { + std::cerr << "Query error in load_users: " << er.what() << std::endl; + return; } + std::cout << "Loaded " << users.size() << " users" << std::endl; } void mysql::load_tokens(torrent_list &torrents) { mysqlpp::Query query = conn.query("SELECT uf.UserID, t.info_hash FROM users_freeleeches AS uf JOIN torrents AS t ON t.ID = uf.TorrentID WHERE uf.Expired = '0';"); - if (mysqlpp::StoreQueryResult res = query.store()) { + int token_count = 0; + try { + mysqlpp::StoreQueryResult res = query.store(); size_t num_rows = res.num_rows(); + std::lock_guard<std::mutex> tl_lock(torrent_list_mutex); for (size_t i = 0; i < num_rows; i++) { std::string info_hash; res[i][1].to_string(info_hash); - torrent_list::iterator it = torrents.find(info_hash); + auto it = torrents.find(info_hash); if (it != torrents.end()) { torrent &tor = it->second; tor.tokened_users.insert(res[i][0]); + ++token_count; } } + } catch (const mysqlpp::BadQuery &er) { + std::cerr << "Query error in load_tokens: " << er.what() << std::endl; + return; } + std::cout << "Loaded " << token_count << " tokens" << std::endl; } void mysql::load_whitelist(std::vector<std::string> &whitelist) { mysqlpp::Query query = conn.query("SELECT peer_id FROM xbt_client_whitelist;"); - if (mysqlpp::StoreQueryResult res = query.store()) { + try { + mysqlpp::StoreQueryResult res = query.store(); size_t num_rows = res.num_rows(); + std::lock_guard<std::mutex> wl_lock(whitelist_mutex); + whitelist.clear(); for (size_t i = 0; i<num_rows; i++) { - whitelist.push_back(res[i][0].c_str()); + std::string peer_id; + res[i][0].to_string(peer_id); + whitelist.push_back(peer_id); } + } catch (const mysqlpp::BadQuery &er) { + std::cerr << "Query error in load_whitelist: " << er.what() << std::endl; + return; + } + if (whitelist.size() == 0) { + std::cout << "Assuming no whitelist desired, disabling" << std::endl; + } else { + std::cout << "Loaded " << whitelist.size() << " clients into the whitelist" << std::endl; } } -void mysql::record_token(std::string &record) { +void mysql::record_token(const std::string &record) { if (update_token_buffer != "") { update_token_buffer += ","; } update_token_buffer += record; } -void mysql::record_user(std::string &record) { +void mysql::record_user(const std::string &record) { if (update_user_buffer != "") { update_user_buffer += ","; } update_user_buffer += record; } -void mysql::record_torrent(std::string &record) { - std::unique_lock<std::mutex> tb_lock(torrent_buffer_lock); +void mysql::record_torrent(const std::string &record) { + std::lock_guard<std::mutex> tb_lock(torrent_buffer_lock); if (update_torrent_buffer != "") { update_torrent_buffer += ","; } update_torrent_buffer += record; } -void mysql::record_peer(std::string &record, std::string &ip, std::string &peer_id, std::string &useragent) { +void mysql::record_peer(const std::string &record, const std::string &ip, const std::string &peer_id, const std::string &useragent) { if (update_heavy_peer_buffer != "") { update_heavy_peer_buffer += ","; } @@ -151,7 +262,7 @@ void mysql::record_peer(std::string &record, std::string &ip, std::string &peer_ update_heavy_peer_buffer += q.str(); } -void mysql::record_peer(std::string &record, std::string &peer_id) { +void mysql::record_peer(const std::string &record, const std::string &peer_id) { if (update_light_peer_buffer != "") { update_light_peer_buffer += ","; } @@ -161,7 +272,7 @@ void mysql::record_peer(std::string &record, std::string &peer_id) { update_light_peer_buffer += q.str(); } -void mysql::record_snatch(std::string &record, std::string &ip) { +void mysql::record_snatch(const std::string &record, const std::string &ip) { if (update_snatch_buffer != "") { update_snatch_buffer += ","; } @@ -183,11 +294,15 @@ void mysql::flush() { } void mysql::flush_users() { + if (readonly) { + update_user_buffer.clear(); + return; + } std::string sql; - std::unique_lock<std::mutex> uq_lock(user_queue_lock); + std::lock_guard<std::mutex> uq_lock(user_queue_lock); size_t qsize = user_queue.size(); if (verbose_flush || qsize > 0) { - std::cout << "User flush queue size: " << qsize << std::endl; + std::cout << "User flush queue size: " << qsize << ", next query length: " << user_queue.front().size() << std::endl; } if (update_user_buffer == "") { return; @@ -203,12 +318,16 @@ void mysql::flush_users() { } void mysql::flush_torrents() { + std::lock_guard<std::mutex> tb_lock(torrent_buffer_lock); + if (readonly) { + update_torrent_buffer.clear(); + return; + } std::string sql; - std::unique_lock<std::mutex> tq_lock(torrent_queue_lock); - std::unique_lock<std::mutex> tb_lock(torrent_buffer_lock); + std::lock_guard<std::mutex> tq_lock(torrent_queue_lock); size_t qsize = torrent_queue.size(); if (verbose_flush || qsize > 0) { - std::cout << "Torrent flush queue size: " << qsize << std::endl; + std::cout << "Torrent flush queue size: " << qsize << ", next query length: " << torrent_queue.front().size() << std::endl; } if (update_torrent_buffer == "") { return; @@ -229,11 +348,15 @@ void mysql::flush_torrents() { } void mysql::flush_snatches() { + if (readonly) { + update_snatch_buffer.clear(); + return; + } std::string sql; - std::unique_lock<std::mutex> sq_lock(snatch_queue_lock); + std::lock_guard<std::mutex> sq_lock(snatch_queue_lock); size_t qsize = snatch_queue.size(); if (verbose_flush || qsize > 0) { - std::cout << "Snatch flush queue size: " << qsize << std::endl; + std::cout << "Snatch flush queue size: " << qsize << ", next query length: " << snatch_queue.front().size() << std::endl; } if (update_snatch_buffer == "" ) { return; @@ -248,11 +371,16 @@ void mysql::flush_snatches() { } void mysql::flush_peers() { + if (readonly) { + update_light_peer_buffer.clear(); + update_heavy_peer_buffer.clear(); + return; + } std::string sql; - std::unique_lock<std::mutex> pq_lock(peer_queue_lock); + std::lock_guard<std::mutex> pq_lock(peer_queue_lock); size_t qsize = peer_queue.size(); if (verbose_flush || qsize > 0) { - std::cout << "Peer flush queue size: " << qsize << std::endl; + std::cout << "Peer flush queue size: " << qsize << ", next query length: " << peer_queue.front().size() << std::endl; } // Nothing to do @@ -260,12 +388,6 @@ void mysql::flush_peers() { return; } - if (qsize == 0) { - sql = "SET session sql_log_bin = 0"; - peer_queue.push(sql); - sql.clear(); - } - if (update_heavy_peer_buffer != "") { // Because xfu inserts are slow and ram is not infinite we need to // limit this queue's size @@ -290,7 +412,7 @@ void mysql::flush_peers() { if (qsize >= 1000) { peer_queue.pop(); } - sql = "INSERT INTO xbt_files_users (fid,timespent,announced,peer_id,mtime) VALUES " + + sql = "INSERT INTO xbt_files_users (uid,fid,timespent,announced,peer_id,mtime) VALUES " + update_light_peer_buffer + " ON DUPLICATE KEY UPDATE upspeed=0, downspeed=0, timespent=VALUES(timespent), " + "announced=VALUES(announced), mtime=VALUES(mtime)"; @@ -306,11 +428,15 @@ void mysql::flush_peers() { } void mysql::flush_tokens() { + if (readonly) { + update_token_buffer.clear(); + return; + } std::string sql; - std::unique_lock<std::mutex> tq_lock(token_queue_lock); + std::lock_guard<std::mutex> tq_lock(token_queue_lock); size_t qsize = token_queue.size(); if (verbose_flush || qsize > 0) { - std::cout << "Token flush queue size: " << qsize << std::endl; + std::cout << "Token flush queue size: " << qsize << ", next query length: " << token_queue.front().size() << std::endl; } if (update_token_buffer == "") { return; @@ -328,7 +454,7 @@ void mysql::flush_tokens() { void mysql::do_flush_users() { u_active = true; try { - mysqlpp::Connection c(db.c_str(), server.c_str(), db_user.c_str(), pw.c_str(), 0); + mysqlpp::Connection c(mysql_db.c_str(), mysql_host.c_str(), mysql_username.c_str(), mysql_password.c_str(), 0); while (user_queue.size() > 0) { try { std::string sql = user_queue.front(); @@ -338,7 +464,7 @@ void mysql::do_flush_users() { sleep(3); continue; } else { - std::unique_lock<std::mutex> uq_lock(user_queue_lock); + std::lock_guard<std::mutex> uq_lock(user_queue_lock); user_queue.pop(); } } @@ -355,8 +481,6 @@ void mysql::do_flush_users() { } catch (const mysqlpp::Exception &er) { std::cerr << "MySQL error in flush_users: " << er.what() << std::endl; - u_active = false; - return; } u_active = false; } @@ -364,7 +488,7 @@ void mysql::do_flush_users() { void mysql::do_flush_torrents() { t_active = true; try { - mysqlpp::Connection c(db.c_str(), server.c_str(), db_user.c_str(), pw.c_str(), 0); + mysqlpp::Connection c(mysql_db.c_str(), mysql_host.c_str(), mysql_username.c_str(), mysql_password.c_str(), 0); while (torrent_queue.size() > 0) { try { std::string sql = torrent_queue.front(); @@ -378,7 +502,7 @@ void mysql::do_flush_torrents() { sleep(3); continue; } else { - std::unique_lock<std::mutex> tq_lock(torrent_queue_lock); + std::lock_guard<std::mutex> tq_lock(torrent_queue_lock); torrent_queue.pop(); } } @@ -395,8 +519,6 @@ void mysql::do_flush_torrents() { } catch (const mysqlpp::Exception &er) { std::cerr << "MySQL error in flush_torrents: " << er.what() << std::endl; - t_active = false; - return; } t_active = false; } @@ -404,7 +526,7 @@ void mysql::do_flush_torrents() { void mysql::do_flush_peers() { p_active = true; try { - mysqlpp::Connection c(db.c_str(), server.c_str(), db_user.c_str(), pw.c_str(), 0); + mysqlpp::Connection c(mysql_db.c_str(), mysql_host.c_str(), mysql_username.c_str(), mysql_password.c_str(), 0); while (peer_queue.size() > 0) { try { std::string sql = peer_queue.front(); @@ -414,7 +536,7 @@ void mysql::do_flush_peers() { sleep(3); continue; } else { - std::unique_lock<std::mutex> pq_lock(peer_queue_lock); + std::lock_guard<std::mutex> pq_lock(peer_queue_lock); peer_queue.pop(); } } @@ -431,8 +553,6 @@ void mysql::do_flush_peers() { } catch (const mysqlpp::Exception &er) { std::cerr << "MySQL error in flush_peers: " << er.what() << std::endl; - p_active = false; - return; } p_active = false; } @@ -440,7 +560,7 @@ void mysql::do_flush_peers() { void mysql::do_flush_snatches() { s_active = true; try { - mysqlpp::Connection c(db.c_str(), server.c_str(), db_user.c_str(), pw.c_str(), 0); + mysqlpp::Connection c(mysql_db.c_str(), mysql_host.c_str(), mysql_username.c_str(), mysql_password.c_str(), 0); while (snatch_queue.size() > 0) { try { std::string sql = snatch_queue.front(); @@ -450,7 +570,7 @@ void mysql::do_flush_snatches() { sleep(3); continue; } else { - std::unique_lock<std::mutex> sq_lock(snatch_queue_lock); + std::lock_guard<std::mutex> sq_lock(snatch_queue_lock); snatch_queue.pop(); } } @@ -467,8 +587,6 @@ void mysql::do_flush_snatches() { } catch (const mysqlpp::Exception &er) { std::cerr << "MySQL error in flush_snatches: " << er.what() << std::endl; - s_active = false; - return; } s_active = false; } @@ -476,7 +594,7 @@ void mysql::do_flush_snatches() { void mysql::do_flush_tokens() { tok_active = true; try { - mysqlpp::Connection c(db.c_str(), server.c_str(), db_user.c_str(), pw.c_str(), 0); + mysqlpp::Connection c(mysql_db.c_str(), mysql_host.c_str(), mysql_username.c_str(), mysql_password.c_str(), 0); while (token_queue.size() > 0) { try { std::string sql = token_queue.front(); @@ -486,7 +604,7 @@ void mysql::do_flush_tokens() { sleep(3); continue; } else { - std::unique_lock<std::mutex> tq_lock(token_queue_lock); + std::lock_guard<std::mutex> tq_lock(token_queue_lock); token_queue.pop(); } } @@ -503,8 +621,6 @@ void mysql::do_flush_tokens() { } catch (const mysqlpp::Exception &er) { std::cerr << "MySQL error in flush_tokens: " << er.what() << std::endl; - tok_active = false; - return; } tok_active = false; } @@ -6,6 +6,7 @@ #include <unordered_map> #include <queue> #include <mutex> +#include "config.h" class mysql { private: @@ -23,8 +24,9 @@ class mysql { std::queue<std::string> snatch_queue; std::queue<std::string> token_queue; - std::string db, server, db_user, pw; + std::string mysql_db, mysql_host, mysql_username, mysql_password; bool u_active, t_active, p_active, s_active, tok_active; + bool readonly; // These locks prevent more than one thread from reading/writing the buffers. // These should be held for the minimum time possible. @@ -35,6 +37,9 @@ class mysql { std::mutex snatch_queue_lock; std::mutex token_queue_lock; + void load_config(config * conf); + void load_tokens(torrent_list &torrents); + void do_flush_users(); void do_flush_torrents(); void do_flush_snatches(); @@ -51,25 +56,27 @@ class mysql { public: bool verbose_flush; - mysql(std::string mysql_db, std::string mysql_host, std::string username, std::string password); + mysql(config * conf); + void reload_config(config * conf); bool connected(); void load_torrents(torrent_list &torrents); void load_users(user_list &users); - void load_tokens(torrent_list &torrents); void load_whitelist(std::vector<std::string> &whitelist); - void record_user(std::string &record); // (id,uploaded_change,downloaded_change) - void record_torrent(std::string &record); // (id,seeders,leechers,snatched_change,balance) - void record_snatch(std::string &record, std::string &ip); // (uid,fid,tstamp) - void record_peer(std::string &record, std::string &ip, std::string &peer_id, std::string &useragent); // (uid,fid,active,peerid,useragent,ip,uploaded,downloaded,upspeed,downspeed,left,timespent,announces,tstamp) - void record_peer(std::string &record, std::string &peer_id); // (fid,peerid,timespent,announces,tstamp) - void record_token(std::string &record); + void record_user(const std::string &record); // (id,uploaded_change,downloaded_change) + void record_torrent(const std::string &record); // (id,seeders,leechers,snatched_change,balance) + void record_snatch(const std::string &record, const std::string &ip); // (uid,fid,tstamp) + void record_peer(const std::string &record, const std::string &ip, const std::string &peer_id, const std::string &useragent); // (uid,fid,active,peerid,useragent,ip,uploaded,downloaded,upspeed,downspeed,left,timespent,announces,tstamp) + void record_peer(const std::string &record, const std::string &peer_id); // (fid,peerid,timespent,announces,tstamp) + void record_token(const std::string &record); void flush(); bool all_clear(); std::mutex torrent_list_mutex; + std::mutex user_list_mutex; + std::mutex whitelist_mutex; }; #pragma GCC visibility pop diff --git a/src/events.cpp b/src/events.cpp index 9d87536..fb3e61e 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -1,12 +1,11 @@ +#include <cerrno> #include "ocelot.h" #include "config.h" #include "db.h" #include "worker.h" -#include "events.h" #include "schedule.h" #include "response.h" -#include <cerrno> -#include <mutex> +#include "events.h" // Define the connection mother (first half) and connection middlemen (second half) @@ -14,68 +13,104 @@ //---------- Connection mother - spawns middlemen and lets them deal with the connection -connection_mother::connection_mother(worker * worker_obj, config * config_obj, mysql * db_obj, site_comm * sc_obj) : work(worker_obj), conf(config_obj), db(db_obj), sc(sc_obj) { - memset(&address, 0, sizeof(address)); - addr_len = sizeof(address); +connection_mother::connection_mother(config * conf, worker * worker_obj, mysql * db_obj, site_comm * sc_obj, schedule * sched) : work(worker_obj), db(db_obj) { + // Handle config stuff first + load_config(conf); - listen_socket = socket(AF_INET, SOCK_STREAM, 0); + listen_socket = create_listen_socket(); - // Stop old sockets from hogging the port - int yes = 1; - if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { - std::cout << "Could not reuse socket" << std::endl; + listen_event.set<connection_mother, &connection_mother::handle_connect>(this); + listen_event.start(listen_socket, ev::READ); + // Create libev timer + schedule_event.set<schedule, &schedule::handle>(sched); + schedule_event.start(sched->schedule_interval, sched->schedule_interval); // After interval, every interval +} + +void connection_mother::load_config(config * conf) { + listen_port = conf->get_uint("listen_port"); + max_connections = conf->get_uint("max_connections"); + max_middlemen = conf->get_uint("max_middlemen"); + connection_timeout = conf->get_uint("connection_timeout"); + keepalive_timeout = conf->get_uint("keepalive_timeout"); + max_read_buffer = conf->get_uint("max_read_buffer"); + max_request_size = conf->get_uint("max_request_size"); +} + +void connection_mother::reload_config(config * conf) { + unsigned int old_listen_port = listen_port; + unsigned int old_max_connections = max_connections; + load_config(conf); + if (old_listen_port != listen_port) { + std::cout << "Changing listen port from " << old_listen_port << " to " << listen_port << std::endl; + int new_listen_socket = create_listen_socket(); + if (new_listen_socket != 0) { + listen_event.stop(); + listen_event.start(new_listen_socket, ev::READ); + close(listen_socket); + listen_socket = new_listen_socket; + } else { + std::cout << "Couldn't create new listen socket when reloading config" << std::endl; + } + } else if (old_max_connections != max_connections) { + listen(listen_socket, max_connections); } +} - // Create libev event loop - ev::io event_loop_watcher; +int connection_mother::create_listen_socket() { + sockaddr_in address; + memset(&address, 0, sizeof(address)); + int new_listen_socket = socket(AF_INET, SOCK_STREAM, 0); - event_loop_watcher.set<connection_mother, &connection_mother::handle_connect>(this); - event_loop_watcher.start(listen_socket, ev::READ); + // Stop old sockets from hogging the port + int yes = 1; + if (setsockopt(new_listen_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + std::cout << "Could not reuse socket: " << strerror(errno) << std::endl; + return 0; + } // Get ready to bind address.sin_family = AF_INET; //address.sin_addr.s_addr = inet_addr(conf->host.c_str()); // htonl(INADDR_ANY) address.sin_addr.s_addr = htonl(INADDR_ANY); - address.sin_port = htons(conf->port); + address.sin_port = htons(listen_port); // Bind - if (bind(listen_socket, (sockaddr *) &address, sizeof(address)) == -1) { - std::cout << "Bind failed " << errno << std::endl; + if (bind(new_listen_socket, (sockaddr *) &address, sizeof(address)) == -1) { + std::cout << "Bind failed: " << strerror(errno) << std::endl; + return 0; } // Listen - if (listen(listen_socket, conf->max_connections) == -1) { - std::cout << "Listen failed" << std::endl; + if (listen(new_listen_socket, max_connections) == -1) { + std::cout << "Listen failed: " << strerror(errno) << std::endl; + return 0; } // Set non-blocking - int flags = fcntl(listen_socket, F_GETFL); + int flags = fcntl(new_listen_socket, F_GETFL); if (flags == -1) { - std::cout << "Could not get socket flags" << std::endl; + std::cout << "Could not get socket flags: " << strerror(errno) << std::endl; + return 0; } - if (fcntl(listen_socket, F_SETFL, flags | O_NONBLOCK) == -1) { - std::cout << "Could not set non-blocking" << std::endl; + if (fcntl(new_listen_socket, F_SETFL, flags | O_NONBLOCK) == -1) { + std::cout << "Could not set non-blocking: " << strerror(errno) << std::endl; + return 0; } - // Create libev timer - schedule timer(this, worker_obj, conf, db, sc); - - schedule_event.set<schedule, &schedule::handle>(&timer); - schedule_event.set(conf->schedule_interval, conf->schedule_interval); // After interval, every interval - schedule_event.start(); + return new_listen_socket; +} - std::cout << "Sockets up, starting event loop!" << std::endl; +void connection_mother::run() { + std::cout << "Sockets up on port " << listen_port << ", starting event loop!" << std::endl; ev_loop(ev_default_loop(0), 0); } - void connection_mother::handle_connect(ev::io &watcher, int events_flags) { // Spawn a new middleman - if (stats.open_connections < conf->max_middlemen) { - std::unique_lock<std::mutex> lock(stats.mutex); + if (stats.open_connections < max_middlemen) { stats.opened_connections++; - lock.unlock(); - new connection_middleman(listen_socket, address, addr_len, work, this, conf); + stats.open_connections++; + new connection_middleman(listen_socket, work, this); } } @@ -92,15 +127,13 @@ connection_mother::~connection_mother() //---------- Connection middlemen - these little guys live until their connection is closed -connection_middleman::connection_middleman(int &listen_socket, sockaddr_in &address, socklen_t &addr_len, worker * new_work, connection_mother * mother_arg, config * config_obj) : - conf(config_obj), mother (mother_arg), work(new_work) { - - connect_sock = accept(listen_socket, (sockaddr *) &address, &addr_len); +connection_middleman::connection_middleman(int &listen_socket, worker * new_work, connection_mother * mother_arg) : + written(0), mother(mother_arg), work(new_work) +{ + connect_sock = accept(listen_socket, NULL, NULL); if (connect_sock == -1) { std::cout << "Accept failed, errno " << errno << ": " << strerror(errno) << std::endl; delete this; - std::unique_lock<std::mutex> lock(stats.mutex); - stats.open_connections++; // destructor decrements open connections return; } @@ -114,10 +147,7 @@ connection_middleman::connection_middleman(int &listen_socket, sockaddr_in &addr } // Get their info - if (getpeername(connect_sock, (sockaddr *) &client_addr, &addr_len) == -1) { - //std::cout << "Could not get client info" << std::endl; - } - request.reserve(conf->max_read_buffer); + request.reserve(mother->max_read_buffer); written = 0; read_event.set<connection_middleman, &connection_middleman::handle_read>(this); @@ -125,47 +155,50 @@ connection_middleman::connection_middleman(int &listen_socket, sockaddr_in &addr // Let the socket timeout in timeout_interval seconds timeout_event.set<connection_middleman, &connection_middleman::handle_timeout>(this); - timeout_event.set(conf->timeout_interval, 0); + timeout_event.set(mother->connection_timeout, mother->keepalive_timeout); timeout_event.start(); - - std::unique_lock<std::mutex> lock(stats.mutex); - stats.open_connections++; } connection_middleman::~connection_middleman() { close(connect_sock); - std::unique_lock<std::mutex> lock(stats.mutex); stats.open_connections--; } // Handler to read data from the socket, called by event loop when socket is readable void connection_middleman::handle_read(ev::io &watcher, int events_flags) { - char buffer[conf->max_read_buffer + 1]; - memset(buffer, 0, conf->max_read_buffer + 1); - int ret = recv(connect_sock, &buffer, conf->max_read_buffer, 0); + char buffer[mother->max_read_buffer + 1]; + memset(buffer, 0, mother->max_read_buffer + 1); + int ret = recv(connect_sock, &buffer, mother->max_read_buffer, 0); if (ret <= 0) { delete this; return; } - std::unique_lock<std::mutex> lock(stats.mutex); stats.bytes_read += ret; - lock.unlock(); request.append(buffer, ret); size_t request_size = request.size(); - if (request_size > conf->max_request_size || (request_size >= 4 && request.compare(request_size - 4, std::string::npos, "\r\n\r\n") == 0)) { + if (request_size > mother->max_request_size || (request_size >= 4 && request.compare(request_size - 4, std::string::npos, "\r\n\r\n") == 0)) { + stats.requests++; read_event.stop(); + client_opts.gzip = false; + client_opts.html = false; + client_opts.http_close = true; - if (request_size > conf->max_request_size) { + if (request_size > mother->max_request_size) { shutdown(connect_sock, SHUT_RD); - response = error("GET string too long"); + response = error("GET string too long", client_opts); } else { char ip[INET_ADDRSTRLEN]; + sockaddr_in client_addr; + socklen_t addr_len = sizeof(client_addr); + getpeername(connect_sock, (sockaddr *) &client_addr, &addr_len); inet_ntop(AF_INET, &(client_addr.sin_addr), ip, INET_ADDRSTRLEN); std::string ip_str = ip; //--- CALL WORKER - response = work->work(request, ip_str); + response = work->work(request, ip_str, client_opts); + request.clear(); + request_size = 0; } // Find out when the socket is writeable. @@ -178,14 +211,22 @@ void connection_middleman::handle_read(ev::io &watcher, int events_flags) { // Handler to write data to the socket, called by event loop when socket is writeable void connection_middleman::handle_write(ev::io &watcher, int events_flags) { int ret = send(connect_sock, response.c_str()+written, response.size()-written, MSG_NOSIGNAL); - written += ret; - std::unique_lock<std::mutex> lock(stats.mutex); + if (ret == -1) { + return; + } stats.bytes_written += ret; - lock.unlock(); + written += ret; if (written == response.size()) { write_event.stop(); - timeout_event.stop(); - delete this; + if (client_opts.http_close) { + timeout_event.stop(); + delete this; + return; + } + timeout_event.again(); + read_event.start(); + response.clear(); + written = 0; } } diff --git a/src/events.h b/src/events.h index d0710ef..c12512e 100644 --- a/src/events.h +++ b/src/events.h @@ -11,7 +11,11 @@ #include <fcntl.h> #include <unistd.h> - +#include "config.h" +#include "worker.h" +#include "schedule.h" +#include "db.h" +#include "site_comm.h" /* TODO find out what these do @@ -47,19 +51,29 @@ THE WORKER // THE MOTHER - Spawns connection middlemen class connection_mother { private: + void load_config(config * conf); + unsigned int listen_port; + unsigned int max_connections; + int listen_socket; - sockaddr_in address; - socklen_t addr_len; worker * work; - config * conf; mysql * db; - site_comm * sc; + ev::io listen_event; ev::timer schedule_event; public: - connection_mother(worker * worker_obj, config * config_obj, mysql * db_obj, site_comm * sc_obj); - void handle_connect(ev::io &watcher, int events_flags); + connection_mother(config * conf, worker * worker_obj, mysql * db_obj, site_comm * sc_obj, schedule * sched_obj); ~connection_mother(); + void reload_config(config * conf); + int create_listen_socket(); + void run(); + void handle_connect(ev::io &watcher, int events_flags); + + unsigned int max_middlemen; + unsigned int connection_timeout; + unsigned int keepalive_timeout; + unsigned int max_read_buffer; + unsigned int max_request_size; }; // THE MIDDLEMAN @@ -68,6 +82,7 @@ class connection_mother { class connection_middleman { private: int connect_sock; + client_opts_t client_opts; unsigned int written; ev::io read_event; ev::io write_event; @@ -75,13 +90,11 @@ class connection_middleman { std::string request; std::string response; - config * conf; connection_mother * mother; worker * work; - sockaddr_in client_addr; public: - connection_middleman(int &listen_socket, sockaddr_in &address, socklen_t &addr_len, worker* work, connection_mother * mother_arg, config * config_obj); + connection_middleman(int &listen_socket, worker* work, connection_mother * mother_arg); ~connection_middleman(); void handle_read(ev::io &watcher, int events_flags); diff --git a/src/m4/ax_pthread.m4 b/src/m4/ax_pthread.m4 new file mode 100644 index 0000000..d383ad5 --- /dev/null +++ b/src/m4/ax_pthread.m4 @@ -0,0 +1,332 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> +# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 21 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], + [AC_MSG_RESULT([yes])], + [ax_pthread_extra_flags= + AC_MSG_RESULT([no])]) +CFLAGS="$save_CFLAGS" + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h> + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>], + [int attr = $attr; return attr /* ; */])], + [attr_name=$attr; break], + []) + done + AC_MSG_RESULT([$attr_name]) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + # TODO: What about Clang on Solaris? + flag="-mt -D_REENTRANT" + fi + ;; + esac + AC_MSG_RESULT([$flag]) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/src/m4/ev++.m4 b/src/m4/ev++.m4 index 573f9da..c81ea09 100644 --- a/src/m4/ev++.m4 +++ b/src/m4/ev++.m4 @@ -47,12 +47,17 @@ AC_DEFUN([EV_DEVEL], if test -z "$ac_cv_ev_lib" then + AC_MSG_RESULT([no]) AC_MSG_ERROR([Didn't find the libev library dir in '$EV_lib_check']) fi case "$ac_cv_ev_lib" in - /* ) ;; - * ) AC_MSG_ERROR([The libev library directory ($ac_cv_ev_lib) must be an absolute path.]) ;; + /* ) + ;; + * ) + AC_MSG_RESULT([]) + AC_MSG_ERROR([The libev library directory ($ac_cv_ev_lib) must be an absolute path.]) + ;; esac ]) AC_SUBST([EV_LIB_DIR],[$ac_cv_ev_lib]) @@ -73,12 +78,17 @@ AC_DEFUN([EV_DEVEL], if test -z "$ac_cv_ev_inc" then + AC_MSG_RESULT([no]) AC_MSG_ERROR([Didn't find the libev header dir in '$EV_inc_check']) fi case "$ac_cv_ev_inc" in - /* ) ;; - * ) AC_MSG_ERROR([The libev header directory ($ac_cv_ev_inc) must be an absolute path.]) ;; + /* ) + ;; + * ) + AC_MSG_RESULT([]) + AC_MSG_ERROR([The libev header directory ($ac_cv_ev_inc) must be an absolute path.]) + ;; esac ]) AC_SUBST([EV_INC_DIR],[$ac_cv_ev_inc]) @@ -87,20 +97,39 @@ AC_DEFUN([EV_DEVEL], dnl Now check that the above checks resulted in -I and -L flags that dnl let us build actual programs against libev. dnl - LDFLAGS_SAVED="$LDFLAGS" - CPPFLAGS_SAVED="$CPPFLAGS" case "$ac_cv_ev_lib" in - /usr/lib) ;; - *) LDFLAGS="$LDFLAGS -L${ac_cv_ev_lib}" ;; + /usr/lib) + ;; + *) + LDFLAGS="$LDFLAGS -L${ac_cv_ev_lib}" + ;; esac CPPFLAGS="$CPPFLAGS -I${ac_cv_ev_inc}" AC_MSG_CHECKING([that we can build libev programs]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([#include <ev++.h>], - [ev::io i])], - AC_MSG_RESULT([yes]), - AC_MSG_ERROR([no])) - LDFLAGS="$LDFLAGS_SAVED" - CPPFLAGS="$CPPFLAGS_SAVED" + AC_EGREP_CPP( + [bad_gcc_libev_ver], + [ +#include <ev.h> +#if __GNUC__ && (__GNUC__ < 4 || __GNUC__ == 4 && __GNUC_MINOR__ < 9) && EV_VERSION_MAJOR == 4 && EV_VERSION_MINOR == 18 + bad_gcc_libev_ver +#endif + ], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([GCC versions prior to 4.9 cannot compile libev 4.18])], + [], + ) + + LIBS="-lev $LIBS" + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [#include <ev++.h>], + [ev::io i]) + ], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_FAILURE([Cannot build libev programs])] + ) + AC_LANG_POP([C++]) ]) dnl End EV_DEVEL diff --git a/src/m4/mysql++.m4 b/src/m4/mysql++.m4 index 2931401..c475fc9 100644 --- a/src/m4/mysql++.m4 +++ b/src/m4/mysql++.m4 @@ -76,12 +76,17 @@ AC_DEFUN([MYSQLPP_DEVEL], if test -z "$ac_cv_mysqlpp_lib" then + AC_MSG_RESULT([no]) AC_MSG_ERROR([Didn't find the MySQL++ library dir in '$MYSQLPP_lib_check']) fi case "$ac_cv_mysqlpp_lib" in - /* ) ;; - * ) AC_MSG_ERROR([The MySQL++ library directory ($ac_cv_mysqlpp_lib) must be an absolute path.]) ;; + /* ) + ;; + * ) + AC_MSG_RESULT([no]) + AC_MSG_ERROR([The MySQL++ library directory ($ac_cv_mysqlpp_lib) must be an absolute path.]) + ;; esac ]) AC_SUBST([MYSQLPP_LIB_DIR],[$ac_cv_mysqlpp_lib]) @@ -102,12 +107,17 @@ AC_DEFUN([MYSQLPP_DEVEL], if test -z "$ac_cv_mysqlpp_inc" then + AC_MSG_RESULT([no]) AC_MSG_ERROR([Didn't find the MySQL++ header dir in '$MYSQLPP_inc_check']) fi case "$ac_cv_mysqlpp_inc" in - /* ) ;; - * ) AC_MSG_ERROR([The MySQL++ header directory ($ac_cv_mysqlpp_inc) must be an absolute path.]) ;; + /* ) + ;; + * ) + AC_MSG_RESULT([no]) + AC_MSG_ERROR([The MySQL++ header directory ($ac_cv_mysqlpp_inc) must be an absolute path.]) + ;; esac ]) AC_SUBST([MYSQLPP_INC_DIR],[$ac_cv_mysqlpp_inc]) @@ -116,20 +126,25 @@ AC_DEFUN([MYSQLPP_DEVEL], dnl Now check that the above checks resulted in -I and -L flags that dnl let us build actual programs against MySQL++. dnl - LDFLAGS_SAVED="$LDFLAGS" - CPPFLAGS_SAVED="$CPPFLAGS" case "$ac_cv_mysqlpp_lib" in - /usr/lib) ;; - *) LDFLAGS="$LDFLAGS -L${ac_cv_mysqlpp_lib}" ;; + /usr/lib) + ;; + *) + LDFLAGS="$LDFLAGS -L${ac_cv_mysqlpp_lib}" + ;; esac - CPPFLAGS="$CPPFLAGS -I${ac_cv_mysqlpp_inc} -I${MYSQL_C_INC_DIR}" + CPPFLAGS="$CPPFLAGS -I${ac_cv_mysqlpp_inc}" + LIBS="-lmysqlpp $LIBS" + AC_LANG_PUSH([C++]) AC_MSG_CHECKING([that we can build MySQL++ programs]) - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([#include <mysql++.h>], - [mysqlpp::Connection c(false)])], - AC_MSG_RESULT([yes]), - AC_MSG_ERROR([no])) - LDFLAGS="$LDFLAGS_SAVED" - CPPFLAGS="$CPPFLAGS_SAVED" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [#include <mysql++.h>], + [mysqlpp::Connection c(false)] + )], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([Cannot build MySQL++ programs])] + ) + AC_LANG_POP([C++]) ]) dnl End MYSQLPP_DEVEL - diff --git a/src/m4/mysql_loc.m4 b/src/m4/mysql_loc.m4 index c06eead..56db7f7 100644 --- a/src/m4/mysql_loc.m4 +++ b/src/m4/mysql_loc.m4 @@ -6,7 +6,7 @@ dnl We define the following configure script flags: dnl dnl --with-mysql: Give prefix for both library and headers, and try dnl to guess subdirectory names for each by tacking common -dnl suffixes on like /lib and /include. +dnl suffixes on like /lib and /include. dnl --with-mysql-lib: Same as --with-mysql, but for library only. dnl --with-mysql-include: Same as --with-mysql, but for headers only. dnl @@ -58,23 +58,27 @@ AC_DEFUN([MYSQL_C_API_LOCATION], if test -z "$MYSQL_C_LIB_DIR" then + AC_MSG_RESULT([no]) AC_MSG_ERROR([Didn't find $MYSQL_C_LIB_NAME library in '$MYSQL_lib_check']) fi case "$MYSQL_C_LIB_DIR" in - /* ) ;; - * ) AC_MSG_ERROR([The MySQL library directory ($MYSQL_C_LIB_DIR) must be an absolute path.]) ;; + /* ) + ;; + * ) + AC_MSG_RESULT([]) + AC_MSG_ERROR([The MySQL library directory ($MYSQL_C_LIB_DIR) must be an absolute path.]) ;; esac AC_MSG_RESULT([$MYSQL_C_LIB_DIR]) case "$MYSQL_C_LIB_DIR" in - /usr/lib) - MYSQL_C_LIB_DIR= - ;; - *) - LDFLAGS="$LDFLAGS -L${MYSQL_C_LIB_DIR}" - ;; + /usr/lib) + MYSQL_C_LIB_DIR= + ;; + *) + LDFLAGS="$LDFLAGS -L${MYSQL_C_LIB_DIR}" + ;; esac @@ -94,47 +98,48 @@ AC_DEFUN([MYSQL_C_API_LOCATION], if test -z "$MYSQL_C_INC_DIR" then + AC_MSG_RESULT([no]) AC_MSG_ERROR([Didn't find the MySQL include dir in '$MYSQL_inc_check']) fi case "$MYSQL_C_INC_DIR" in - /* ) ;; - * ) AC_MSG_ERROR([The MySQL include directory ($MYSQL_C_INC_DIR) must be an absolute path.]) ;; + /* ) + ;; + * ) + AC_MSG_RESULT([]) + AC_MSG_ERROR([The MySQL include directory ($MYSQL_C_INC_DIR) must be an absolute path.]) ;; esac AC_MSG_RESULT([$MYSQL_C_INC_DIR]) - save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS -I${MYSQL_C_INC_DIR}" - AC_MSG_CHECKING([if we can link to MySQL C API library directly]) + AC_MSG_CHECKING([if we can link to MySQL C API library directly]) save_LIBS=$LIBS - LIBS="$LIBS -l$MYSQL_C_LIB_NAME $MYSQLPP_EXTRA_LIBS" - AC_TRY_LINK( - [ #include <mysql.h> ], - [ mysql_store_result(0); ], - AC_MSG_RESULT([yes]), - [ AC_MSG_RESULT([no]) - LIBS="$save_LIBS" - AC_CHECK_HEADERS(zlib.h, AC_CHECK_LIB(z, gzread, [], - [ AC_MSG_ERROR([zlib not found]) ])) - AC_MSG_CHECKING([whether adding -lz will let MySQL C API link succeed]) - MYSQLPP_EXTRA_LIBS="$MYSQLPP_EXTRA_LIBS -lz" - LIBS="$save_LIBS -l$MYSQL_C_LIB_NAME $MYSQLPP_EXTRA_LIBS" - AC_TRY_LINK( - [ #include <mysql.h> ], - [ mysql_store_result(0); ], - AC_MSG_RESULT([yes]), - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([Unable to link to MySQL client library!]) - ] - ) - ]) - CPPFLAGS=$save_CPPFLAGS - LIBS=$save_LIBS + LIBS="-l$MYSQL_C_LIB_NAME $LIBS" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [#include <mysql.h>], + [mysql_store_result(0);])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + LIBS="$save_LIBS" + AC_CHECK_HEADERS(zlib.h, AC_CHECK_LIB(z, gzread, [], + [AC_MSG_ERROR([zlib not found])])) + AC_MSG_CHECKING([whether adding -lz will let MySQL C API link succeed]) + LIBS="-l$MYSQL_C_LIB_NAME -lz $save_LIBS" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [#include <mysql.h>], + [mysql_store_result(0);] + )], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([Unable to link to MySQL client library!])] + )] + ) AC_SUBST(MYSQL_C_INC_DIR) AC_SUBST(MYSQL_C_LIB_DIR) AC_SUBST(MYSQL_C_LIB_NAME) ]) dnl MYSQL_C_API_LOCATION - diff --git a/src/m4/tcmalloc.m4 b/src/m4/tcmalloc.m4 index 44333c5..73d4b4c 100644 --- a/src/m4/tcmalloc.m4 +++ b/src/m4/tcmalloc.m4 @@ -6,7 +6,6 @@ AC_DEFUN([TCMALLOC], [with_tcmalloc=yes]) AS_IF([test "x$with_tcmalloc" != "xno"], - [AC_CHECK_LIB([tcmalloc], [tc_free], [], - [AC_MSG_NOTICE([tcmalloc libraries not installed])] - )]) -]) + [AC_CHECK_LIB([tcmalloc], [tc_free])] + )] +) diff --git a/src/misc_functions.cpp b/src/misc_functions.cpp index 022e6b2..2b616f9 100644 --- a/src/misc_functions.cpp +++ b/src/misc_functions.cpp @@ -2,15 +2,15 @@ #include <iostream> #include <sstream> -long strtolong(const std::string& str) { - std::istringstream stream (str); - long i = 0; +int32_t strtoint32(const std::string& str) { + std::istringstream stream(str); + int32_t i = 0; stream >> i; return i; } -int64_t strtolonglong(const std::string& str) { - std::istringstream stream (str); +int64_t strtoint64(const std::string& str) { + std::istringstream stream(str); int64_t i = 0; stream >> i; return i; @@ -59,17 +59,17 @@ std::string hex_decode(const std::string &in) { std::string bintohex(const std::string &in) { std::string out; - out.reserve(40); size_t length = in.length(); + out.reserve(2*length); for (unsigned int i = 0; i < length; i++) { - unsigned char x = (unsigned char)in[i] >> 4; + unsigned char x = static_cast<unsigned char>((in[i] & 0xF0) >> 4); if (x > 9) { x += 'a' - 10; } else { x += '0'; } out.push_back(x); - x = in[i] & 0xF; + x = in[i] & 0x0F; if (x > 9) { x += 'a' - 10; } else { diff --git a/src/misc_functions.h b/src/misc_functions.h index c9a24d3..fbe164d 100644 --- a/src/misc_functions.h +++ b/src/misc_functions.h @@ -1,12 +1,11 @@ #ifndef MISC_FUNCTIONS__H #define MISC_FUNCTIONS__H #include <string> -#include <cstdlib> -long strtolong(const std::string& str); -int64_t strtolonglong(const std::string& str); + +int32_t strtoint32(const std::string& str); +int64_t strtoint64(const std::string& str); std::string inttostr(int i); std::string hex_decode(const std::string &in); std::string bintohex(const std::string &in); -int timeval_subtract (timeval* result, timeval* x, timeval* y); #endif diff --git a/src/missing b/src/missing index db98974..f62bbae 100755 --- a/src/missing +++ b/src/missing @@ -3,7 +3,7 @@ scriptversion=2013-10-28.13; # UTC -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. # This program is free software; you can redistribute it and/or modify diff --git a/src/ocelot.conf.dist b/src/ocelot.conf.dist new file mode 100644 index 0000000..4f6f567 --- /dev/null +++ b/src/ocelot.conf.dist @@ -0,0 +1,32 @@ +# Ocelot config file +# Lines starting with a # are ignored +# A # anywhere else is treated like any other character + +listen_port = 34000 +max_connections = 128 +max_middlemen = 20000 +max_read_buffer = 4096 +connection_timeout = 10 +# Keepalive is mostly useful if the tracker runs behind reverse proxies +keepalive_timeout = 0 + +announce_interval = 1800 +max_request_size = 4096 +numwant_limit = 50 +request_log_size = 500 + +mysql_host = +mysql_username = +mysql_password = +mysql_db = + +# The passwords must be 32 characters and match the Gazelle config +report_password = 00000000000000000000000000000000 +site_password = 00000000000000000000000000000000 + +peers_timeout = 7200 +del_reason_lifetime = 86400 +reap_peers_interval = 1800 +schedule_interval = 3 + +readonly = false diff --git a/src/ocelot.cpp b/src/ocelot.cpp index 1ca6d49..09a96ab 100644 --- a/src/ocelot.cpp +++ b/src/ocelot.cpp @@ -1,3 +1,7 @@ +#include <fstream> +#include <iostream> +#include <csignal> +#include <thread> #include "ocelot.h" #include "config.h" #include "db.h" @@ -8,13 +12,33 @@ static connection_mother *mother; static worker *work; -struct stats stats; - -static void sig_handler(int sig) -{ - std::cout << "Caught SIGINT/SIGTERM" << std::endl; - if (work->signal(sig)) { - exit(0); +static mysql *db; +static site_comm *sc; +static config *conf; +static schedule *sched; + +struct stats_t stats; + +static void sig_handler(int sig) { + if (sig == SIGINT || sig == SIGTERM) { + std::cout << "Caught SIGINT/SIGTERM" << std::endl; + if (work->shutdown()) { + exit(0); + } + } else if (sig == SIGHUP) { + std::cout << "Reloading config" << std::endl; + std::cout.flush(); + conf->reload(); + db->reload_config(conf); + mother->reload_config(conf); + sc->reload_config(conf); + sched->reload_config(conf); + work->reload_config(conf); + std::cout << "Done reloading config" << std::endl; + } else if (sig == SIGUSR1) { + std::cout << "Reloading from database" << std::endl; + std::thread w_thread(&worker::reload_lists, work); + w_thread.detach(); } } @@ -22,48 +46,55 @@ int main(int argc, char **argv) { // we don't use printf so make cout/cerr a little bit faster std::ios_base::sync_with_stdio(false); - config conf; + conf = new config(); - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); - - bool verbose = false; - for (int i = argc; i > 1; i--) { - if (!strcmp(argv[1], "-v")) { + bool verbose = false, conf_arg = false; + std::string conf_file_path("./ocelot.conf"); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-v")) { verbose = true; + } else if (!strcmp(argv[i], "-c") && i < argc - 1) { + conf_arg = true; + conf_file_path = argv[++i]; + } else { + std::cout << "Usage: " << argv[0] << " [-v] [-c configfile]" << std::endl; + return 0; } } - mysql db(conf.mysql_db, conf.mysql_host, conf.mysql_username, conf.mysql_password); - if (!db.connected()) { - std::cout << "Exiting" << std::endl; - return 0; + std::ifstream conf_file(conf_file_path); + if (conf_file.fail()) { + std::cout << "Using default config because '" << conf_file_path << "' couldn't be opened" << std::endl; + if (!conf_arg) { + std::cout << "Start Ocelot with -c <path> to specify config file if necessary" << std::endl; + } + } else { + conf->load(conf_file_path, conf_file); } - db.verbose_flush = verbose; - site_comm sc(conf); - sc.verbose_flush = verbose; + db = new mysql(conf); - std::vector<std::string> whitelist; - db.load_whitelist(whitelist); - std::cout << "Loaded " << whitelist.size() << " clients into the whitelist" << std::endl; - if (whitelist.size() == 0) { - std::cout << "Assuming no whitelist desired, disabling" << std::endl; + if (!db->connected()) { + std::cout << "Exiting" << std::endl; + return 0; } + db->verbose_flush = verbose; - user_list users_list; - db.load_users(users_list); - std::cout << "Loaded " << users_list.size() << " users" << std::endl; + sc = new site_comm(conf); + sc->verbose_flush = verbose; + user_list users_list; torrent_list torrents_list; - db.load_torrents(torrents_list); - std::cout << "Loaded " << torrents_list.size() << " torrents" << std::endl; - - db.load_tokens(torrents_list); + std::vector<std::string> whitelist; + db->load_users(users_list); + db->load_torrents(torrents_list); + db->load_whitelist(whitelist); stats.open_connections = 0; stats.opened_connections = 0; stats.connection_rate = 0; + stats.requests = 0; + stats.request_rate = 0; stats.leechers = 0; stats.seeders = 0; stats.announcements = 0; @@ -74,10 +105,28 @@ int main(int argc, char **argv) { stats.start_time = time(NULL); // Create worker object, which handles announces and scrapes and all that jazz - work = new worker(torrents_list, users_list, whitelist, &conf, &db, &sc); + work = new worker(conf, torrents_list, users_list, whitelist, db, sc); + + // Create schedule object + sched = new schedule(conf, work, db, sc); // Create connection mother, which binds to its socket and handles the event stuff - mother = new connection_mother(work, &conf, &db, &sc); + mother = new connection_mother(conf, work, db, sc, sched); + + // Add signal handlers now that all objects have been created + struct sigaction handler, ignore; + ignore.sa_handler = SIG_IGN; + handler.sa_handler = sig_handler; + sigemptyset(&handler.sa_mask); + handler.sa_flags = 0; + + sigaction(SIGINT, &handler, NULL); + sigaction(SIGTERM, &handler, NULL); + sigaction(SIGHUP, &handler, NULL); + sigaction(SIGUSR1, &handler, NULL); + sigaction(SIGUSR2, &ignore, NULL); + + mother->run(); return 0; } diff --git a/src/ocelot.h b/src/ocelot.h index 15204d1..0a300a3 100644 --- a/src/ocelot.h +++ b/src/ocelot.h @@ -1,26 +1,29 @@ +#ifndef OCELOT_H +#define OCELOT_H + #include <string> #include <map> #include <vector> #include <unordered_map> #include <set> #include <memory> -#include <mutex> +#include <atomic> -#ifndef OCELOT_H -#define OCELOT_H +typedef uint32_t torid_t; +typedef uint32_t userid_t; class user; typedef std::shared_ptr<user> user_ptr; typedef struct { - unsigned int port; int64_t uploaded; int64_t downloaded; int64_t corrupt; int64_t left; time_t last_announced; time_t first_announced; - unsigned int announces; + uint32_t announces; + uint16_t port; bool visible; bool invalid_ip; user_ptr user; @@ -33,15 +36,15 @@ typedef std::map<std::string, peer> peer_list; enum freetype { NORMAL, FREE, NEUTRAL }; typedef struct { - int id; + torid_t id; + uint32_t completed; int64_t balance; - int completed; freetype free_torrent; time_t last_flushed; peer_list seeders; peer_list leechers; std::string last_selected_seeder; - std::set<int> tokened_users; + std::set<userid_t> tokened_users; } torrent; enum { @@ -75,23 +78,30 @@ typedef struct { time_t time; } del_message; +typedef struct { + bool gzip; + bool html; + bool http_close; +} client_opts_t; + typedef std::unordered_map<std::string, torrent> torrent_list; typedef std::unordered_map<std::string, user_ptr> user_list; typedef std::unordered_map<std::string, std::string> params_type; -struct stats { - std::mutex mutex; - unsigned int open_connections; - uint64_t opened_connections; - uint64_t connection_rate; - unsigned int leechers; - unsigned int seeders; - uint64_t announcements; - uint64_t succ_announcements; - uint64_t scrapes; - uint64_t bytes_read; - uint64_t bytes_written; +struct stats_t { + std::atomic<uint32_t> open_connections; + std::atomic<uint64_t> opened_connections; + std::atomic<uint64_t> connection_rate; + std::atomic<uint32_t> leechers; + std::atomic<uint32_t> seeders; + std::atomic<uint64_t> requests; + std::atomic<uint64_t> request_rate; + std::atomic<uint64_t> announcements; + std::atomic<uint64_t> succ_announcements; + std::atomic<uint64_t> scrapes; + std::atomic<uint64_t> bytes_read; + std::atomic<uint64_t> bytes_written; time_t start_time; }; -extern struct stats stats; +extern struct stats_t stats; #endif diff --git a/src/report.cpp b/src/report.cpp index 97bf753..cd1c1dc 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -7,7 +7,7 @@ #include "response.h" #include "user.h" -std::string report(params_type ¶ms, user_list &users_list) { +std::string report(params_type ¶ms, user_list &users_list, client_opts_t &client_opts) { std::stringstream output; std::string action = params["get"]; if (action == "") { @@ -20,14 +20,16 @@ std::string report(params_type ¶ms, user_list &users_list) { uptime -= up_h * 3600; int up_m = uptime / 60; int up_s = uptime - up_m * 60; - std::string up_ht = up_h <= 9 ? '0' + std::to_string(up_h) : std::to_string(up_h); - std::string up_mt = up_m <= 9 ? '0' + std::to_string(up_m) : std::to_string(up_m); - std::string up_st = up_s <= 9 ? '0' + std::to_string(up_s) : std::to_string(up_s); + std::string up_ht = up_h <= 9 ? '0' + inttostr(up_h) : inttostr(up_h); + std::string up_mt = up_m <= 9 ? '0' + inttostr(up_m) : inttostr(up_m); + std::string up_st = up_s <= 9 ? '0' + inttostr(up_s) : inttostr(up_s); output << "Uptime: " << up_d << " days, " << up_ht << ':' << up_mt << ':' << up_st << '\n' << stats.opened_connections << " connections opened\n" << stats.open_connections << " open connections\n" << stats.connection_rate << " connections/s\n" + << stats.requests << " requests handled\n" + << stats.request_rate << " requests/s\n" << stats.succ_announcements << " successful announcements\n" << (stats.announcements - stats.succ_announcements) << " failed announcements\n" << stats.scrapes << " scrapes\n" @@ -50,5 +52,5 @@ std::string report(params_type ¶ms, user_list &users_list) { output << "Invalid action\n"; } output << "success"; - return response(output.str(), false, false); + return response(output.str(), client_opts); } diff --git a/src/report.h b/src/report.h index d937d28..9a11f83 100644 --- a/src/report.h +++ b/src/report.h @@ -1,4 +1,4 @@ #include <string> #include "ocelot.h" -std::string report(params_type ¶ms, user_list &users_list); +std::string report(params_type ¶ms, user_list &users_list, client_opts_t &client_opts); diff --git a/src/response.cpp b/src/response.cpp index 96c1bc9..abe4363 100644 --- a/src/response.cpp +++ b/src/response.cpp @@ -1,19 +1,18 @@ -#include "response.h" -#include "misc_functions.h" #include <sstream> #include <boost/iostreams/filtering_streambuf.hpp> #include <boost/iostreams/copy.hpp> #include <boost/iostreams/filter/gzip.hpp> +#include "response.h" +#include "misc_functions.h" -std::string response(const std::string &body, bool gzip, bool html) { - const std::string head = response_head(gzip, html); +const std::string response(const std::string &body, client_opts_t &client_opts) { std::string out; bool processed = false; - if (html) { + if (client_opts.html) { out = "<html><head><meta name=\"robots\" content=\"noindex, nofollow\" /></head><body>" + body + "</body></html>"; processed = true; } - if (gzip) { + if (client_opts.gzip) { std::stringstream ss, zss; ss << body; boost::iostreams::filtering_streambuf<boost::iostreams::input> in; @@ -24,26 +23,29 @@ std::string response(const std::string &body, bool gzip, bool html) { processed = true; } if (processed) { - return head + out; + return response_head(out.length(), client_opts) + out; } - return head + body; + return response_head(body.length(), client_opts) + body; } -std::string response_head(bool gzip, bool html) { - const std::string content_type = html ? "text/html" : "text/plain"; +const std::string response_head(size_t content_length, client_opts_t &client_opts) { + const std::string content_type = client_opts.html ? "text/html" : "text/plain"; std::string head = "HTTP/1.1 200 OK\r\nServer: Ocelot 1.0"; head += "\r\nContent-Type: " + content_type; - if (gzip) { + if (client_opts.gzip) { head += "\r\nContent-Encoding: gzip"; } - head += "\r\nConnection: close\r\n\r\n"; + if (client_opts.http_close) { + head += "\r\nConnection: Close"; + } + head += "\r\nContent-Length: " + inttostr(content_length) + "\r\n\r\n"; return head; } -std::string error(const std::string &err) { - return response("d14:failure reason" + inttostr(err.length()) + ':' + err + "12:min intervali5400e8:intervali5400ee", false, false); +const std::string error(const std::string &err, client_opts_t &client_opts) { + return response("d14:failure reason" + inttostr(err.length()) + ':' + err + "12:min intervali5400e8:intervali5400ee", client_opts); } -std::string warning(const std::string &msg) { +const std::string warning(const std::string &msg) { return "15:warning message" + inttostr(msg.length()) + ':' + msg; } diff --git a/src/response.h b/src/response.h index 925a4a5..6f4d6f2 100644 --- a/src/response.h +++ b/src/response.h @@ -1,6 +1,11 @@ +#ifndef RESPONSE_H +#define REPSONSE_H + #include <string> +#include "ocelot.h" -std::string response(const std::string &body, bool gzip, bool html); -std::string response_head(bool gzip, bool html); -std::string error(const std::string &err); -std::string warning(const std::string &msg); +const std::string response(const std::string &body, client_opts_t &client_opts); +const std::string response_head(size_t content_length, client_opts_t &client_opts); +const std::string error(const std::string &err, client_opts_t &client_opts); +const std::string warning(const std::string &msg); +#endif diff --git a/src/schedule.cpp b/src/schedule.cpp index 8073cf1..ef10337 100644 --- a/src/schedule.cpp +++ b/src/schedule.cpp @@ -1,24 +1,37 @@ +#include <iostream> + #include "ocelot.h" #include "config.h" #include "db.h" #include "worker.h" -#include "events.h" #include "schedule.h" - -schedule::schedule(connection_mother * mother_obj, worker* worker_obj, config* conf_obj, mysql * db_obj, site_comm * sc_obj) : mother(mother_obj), work(worker_obj), conf(conf_obj), db(db_obj), sc(sc_obj) { +schedule::schedule(config * conf, worker * worker_obj, mysql * db_obj, site_comm * sc_obj) : work(worker_obj), db(db_obj), sc(sc_obj) { + load_config(conf); counter = 0; last_opened_connections = 0; + last_request_count = 0; + next_reap_peers = reap_peers_interval; +} + +void schedule::load_config(config * conf) { + reap_peers_interval = conf->get_uint("reap_peers_interval"); + schedule_interval = conf->get_uint("schedule_interval"); +} - next_reap_peers = time(NULL) + conf->reap_peers_interval + 40; +void schedule::reload_config(config * conf) { + load_config(conf); } + //---------- Schedule - gets called every schedule_interval seconds void schedule::handle(ev::timer &watcher, int events_flags) { - stats.connection_rate = (stats.opened_connections - last_opened_connections) / conf->schedule_interval; + unsigned int cur_schedule_interval = watcher.repeat; + stats.connection_rate = (stats.opened_connections - last_opened_connections) / cur_schedule_interval; + stats.request_rate = (stats.requests - last_request_count) / cur_schedule_interval; if (counter % 20 == 0) { - std::cout << "Schedule run #" << counter << " - open: " << stats.open_connections << ", opened: " - << stats.opened_connections << ", speed: " - << stats.connection_rate << "/s" << std::endl; + std::cout << stats.open_connections << " open, " + << stats.opened_connections << " connections (" << stats.connection_rate << "/s), " + << stats.requests << " requests (" << stats.request_rate << "/s)" << std::endl; } if (work->get_status() == CLOSING && db->all_clear() && sc->all_clear()) { @@ -27,16 +40,19 @@ void schedule::handle(ev::timer &watcher, int events_flags) { } last_opened_connections = stats.opened_connections; + last_request_count = stats.requests; db->flush(); sc->flush_tokens(); - time_t cur_time = time(NULL); - - if (cur_time > next_reap_peers) { + next_reap_peers -= cur_schedule_interval; + if (next_reap_peers <= 0) { work->start_reaper(); - next_reap_peers = cur_time + conf->reap_peers_interval; + next_reap_peers = reap_peers_interval; } counter++; + if (schedule_interval != cur_schedule_interval) { + watcher.set(schedule_interval, schedule_interval); + } } diff --git a/src/schedule.h b/src/schedule.h index 69d910b..3278e50 100644 --- a/src/schedule.h +++ b/src/schedule.h @@ -1,20 +1,24 @@ -#include <ev++.h> -#include <string> -#include <iostream> +#ifndef SCHEDULE_H +#define SCHEDULE_H +#include <ev++.h> class schedule { private: - connection_mother * mother; + void load_config(config * conf); + + unsigned int reap_peers_interval; worker * work; - config * conf; mysql * db; site_comm * sc; uint64_t last_opened_connections; - int counter; - - time_t next_flush; - time_t next_reap_peers; + uint64_t last_request_count; + unsigned int counter; + int next_reap_peers; public: - schedule(connection_mother * mother_obj, worker * worker_obj, config* conf_obj, mysql * db_obj, site_comm * sc_obj); + schedule(config * conf, worker * worker_obj, mysql * db_obj, site_comm * sc_obj); + void reload_config(config * conf); void handle(ev::timer &watcher, int events_flags); + + unsigned int schedule_interval; }; +#endif diff --git a/src/site_comm.cpp b/src/site_comm.cpp index e414b48..a1ffec6 100644 --- a/src/site_comm.cpp +++ b/src/site_comm.cpp @@ -13,18 +13,26 @@ using boost::asio::ip::tcp; -site_comm::site_comm(config &config) -{ - conf = config; - t_active = false; +site_comm::site_comm(config * conf) : t_active(false) { + load_config(conf); +} + +void site_comm::load_config(config * conf) { + site_host = conf->get_str("site_host"); + site_path = conf->get_str("site_path"); + site_password = conf->get_str("site_password"); + readonly = conf->get_bool("readonly"); +} + +void site_comm::reload_config(config * conf) { + load_config(conf); } bool site_comm::all_clear() { return (token_queue.size() == 0); } -void site_comm::expire_token(int torrent, int user) -{ +void site_comm::expire_token(int torrent, int user) { std::stringstream token_pair; token_pair << user << ':' << torrent; if (expire_token_buffer != "") { @@ -33,15 +41,21 @@ void site_comm::expire_token(int torrent, int user) expire_token_buffer += token_pair.str(); if (expire_token_buffer.length() > 350) { std::cout << "Flushing overloaded token buffer" << std::endl; - std::unique_lock<std::mutex> lock(expire_queue_lock); - token_queue.push(expire_token_buffer); + if (!readonly) { + std::lock_guard<std::mutex> lock(expire_queue_lock); + token_queue.push(expire_token_buffer); + } expire_token_buffer.clear(); } } void site_comm::flush_tokens() { - std::unique_lock<std::mutex> lock(expire_queue_lock); + if (readonly) { + expire_token_buffer.clear(); + return; + } + std::lock_guard<std::mutex> lock(expire_queue_lock); size_t qsize = token_queue.size(); if (verbose_flush || qsize > 0) { std::cout << "Token expire queue size: " << qsize << std::endl; @@ -65,7 +79,7 @@ void site_comm::do_flush_tokens() boost::asio::io_service io_service; tcp::resolver resolver(io_service); - tcp::resolver::query query(conf.site_host, "http"); + tcp::resolver::query query(site_host, "http"); tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); tcp::resolver::iterator end; @@ -81,11 +95,11 @@ void site_comm::do_flush_tokens() boost::asio::streambuf request; std::ostream request_stream(&request); - request_stream << "GET " << conf.site_path << "/tools.php?key=" << conf.site_password; - request_stream << "&type=expiretoken&action=ocelot&tokens=" << token_queue.front() << " HTTP/1.0\r\n"; - request_stream << "Host: " << conf.site_host << "\r\n"; - request_stream << "Accept: */*\r\n"; - request_stream << "Connection: close\r\n\r\n"; + request_stream << "GET " << site_path << "/tools.php?key=" << site_password + << "&type=expiretoken&action=ocelot&tokens=" << token_queue.front() << " HTTP/1.0\r\n" + << "Host: " << site_host << "\r\n" + << "Accept: */*\r\n" + << "Connection: close\r\n\r\n"; boost::asio::write(socket, request); @@ -106,7 +120,7 @@ void site_comm::do_flush_tokens() } if (status_code == 200) { - std::unique_lock<std::mutex> lock(expire_queue_lock); + std::lock_guard<std::mutex> lock(expire_queue_lock); token_queue.pop(); } else { std::cout << "Response returned with status code " << status_code << " when trying to expire a token!" << std::endl;; diff --git a/src/site_comm.h b/src/site_comm.h index cac511a..798154f 100644 --- a/src/site_comm.h +++ b/src/site_comm.h @@ -1,8 +1,5 @@ #ifndef OCELOT_SITE_COMM_H #define OCELOT_SITE_COMM_H -#include <iostream> -#include <istream> -#include <ostream> #include <string> #include <boost/asio.hpp> #include <queue> @@ -14,19 +11,24 @@ using boost::asio::ip::tcp; class site_comm { private: - config conf; + std::string site_host; + std::string site_path; + std::string site_password; std::mutex expire_queue_lock; std::string expire_token_buffer; std::queue<std::string> token_queue; + bool readonly; bool t_active; + void load_config(config * conf); + void do_flush_tokens(); public: bool verbose_flush; - site_comm(config &conf); + site_comm(config * conf); + void reload_config(config * conf); bool all_clear(); void expire_token(int torrent, int user); void flush_tokens(); - void do_flush_tokens(); ~site_comm(); }; #endif diff --git a/src/user.cpp b/src/user.cpp index 6ca156b..d2b606b 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -1,51 +1,6 @@ #include "user.h" -user::user(int uid, bool leech, bool protect) : id(uid), leechstatus(leech), protect_ip(protect) { +user::user(userid_t uid, bool leech, bool protect) : id(uid), deleted(false), leechstatus(leech), protect_ip(protect) { stats.leeching = 0; stats.seeding = 0; } - -int user::get_id() { - return id; -} - -bool user::is_protected() { - return protect_ip; -} - -void user::set_protected(bool status) { - protect_ip = status; -} - -bool user::can_leech() { - return leechstatus; -} - -void user::set_leechstatus(bool status) { - leechstatus = status; -} - -// Stats methods -unsigned int user::get_leeching() { - return stats.leeching; -} - -unsigned int user::get_seeding() { - return stats.seeding; -} - -void user::decr_leeching() { - stats.leeching--; -} - -void user::decr_seeding() { - stats.seeding--; -} - -void user::incr_leeching() { - stats.leeching++; -} - -void user::incr_seeding() { - stats.seeding++; -} @@ -1,23 +1,33 @@ +#ifndef USER_H +#define USER_H + +#include <atomic> +#include "ocelot.h" + class user { private: - int id; + userid_t id; + bool deleted; bool leechstatus; bool protect_ip; struct { - unsigned int leeching; - unsigned int seeding; + std::atomic<uint32_t> leeching; + std::atomic<uint32_t> seeding; } stats; public: - user(int uid, bool leech, bool protect); - int get_id(); - bool is_protected(); - void set_protected(bool status); - bool can_leech(); - void set_leechstatus(bool status); - void decr_leeching(); - void decr_seeding(); - void incr_leeching(); - void incr_seeding(); - unsigned int get_leeching(); - unsigned int get_seeding(); + user(userid_t uid, bool leech, bool protect); + userid_t get_id() { return id; } + bool is_deleted() { return deleted; } + void set_deleted(bool status) { deleted = status; } + bool is_protected() { return protect_ip; } + void set_protected(bool status) { protect_ip = status; } + bool can_leech() { return leechstatus; } + void set_leechstatus(bool status) { leechstatus = status; } + void decr_leeching() { --stats.leeching; } + void decr_seeding() { --stats.seeding; } + void incr_leeching() { ++stats.leeching; } + void incr_seeding() { ++stats.seeding; } + uint32_t get_leeching() { return stats.leeching; } + uint32_t get_seeding() { return stats.seeding; } }; +#endif diff --git a/src/worker.cpp b/src/worker.cpp index eee1c7e..49dbd2c 100644 --- a/src/worker.cpp +++ b/src/worker.cpp @@ -1,7 +1,4 @@ -#include <cmath> -#include <cstdlib> #include <iostream> -#include <fstream> #include <string> #include <map> #include <sstream> @@ -12,9 +9,6 @@ #include <mutex> #include <thread> -#include <netinet/in.h> -#include <arpa/inet.h> - #include "ocelot.h" #include "config.h" #include "db.h" @@ -26,11 +20,35 @@ #include "user.h" //---------- Worker - does stuff with input -worker::worker(torrent_list &torrents, user_list &users, std::vector<std::string> &_whitelist, config * conf_obj, mysql * db_obj, site_comm * sc) : torrents_list(torrents), users_list(users), whitelist(_whitelist), conf(conf_obj), db(db_obj), s_comm(sc) +worker::worker(config * conf_obj, torrent_list &torrents, user_list &users, std::vector<std::string> &_whitelist, mysql * db_obj, site_comm * sc) : + conf(conf_obj), db(db_obj), s_comm(sc), torrents_list(torrents), users_list(users), whitelist(_whitelist), status(OPEN), reaper_active(false) { + load_config(conf); +} + +void worker::load_config(config * conf) { + announce_interval = conf->get_uint("announce_interval"); + del_reason_lifetime = conf->get_uint("del_reason_lifetime"); + peers_timeout = conf->get_uint("peers_timeout"); + numwant_limit = conf->get_uint("numwant_limit"); + keepalive_enabled = conf->get_uint("keepalive_timeout") != 0; + site_password = conf->get_str("site_password"); + report_password = conf->get_str("report_password"); +} + +void worker::reload_config(config * conf) { + load_config(conf); +} + +void worker::reload_lists() { + status = PAUSED; + db->load_torrents(torrents_list); + db->load_users(users_list); + db->load_whitelist(whitelist); status = OPEN; } -bool worker::signal(int sig) { + +bool worker::shutdown() { if (status == OPEN) { status = CLOSING; std::cout << "closing tracker... press Ctrl-C again to terminate" << std::endl; @@ -42,12 +60,13 @@ bool worker::signal(int sig) { return false; } } -std::string worker::work(std::string &input, std::string &ip) { + +std::string worker::work(const std::string &input, std::string &ip, client_opts_t &client_opts) { unsigned int input_length = input.length(); //---------- Parse request - ugly but fast. Using substr exploded. if (input_length < 60) { // Way too short to be anything useful - return error("GET string too short"); + return error("GET string too short", client_opts); } size_t pos = 5; // skip 'GET /' @@ -56,7 +75,7 @@ std::string worker::work(std::string &input, std::string &ip) { std::string passkey; passkey.reserve(32); if (input[37] != '/') { - return error("Malformed announce"); + return error("Malformed announce", client_opts); } for (; pos < 37; pos++) { @@ -71,7 +90,6 @@ std::string worker::work(std::string &input, std::string &ip) { }; action_t action = INVALID; - std::unique_lock<std::mutex> lock(stats.mutex); switch (input[pos]) { case 'a': stats.announcements++; @@ -92,19 +110,11 @@ std::string worker::work(std::string &input, std::string &ip) { pos += 6; break; } - lock.unlock(); if (input[pos] != '?') { // No parameters given. Probably means we're not talking to a torrent client - return response("Nothing to see here", false, true); - } - - if (status != OPEN && action != UPDATE) { - return error("The tracker is temporarily unavailable."); - } - - if (action == INVALID) { - return error("Invalid action"); + client_opts.html = true; + return response("Nothing to see here", client_opts); } // Parse URL params @@ -115,7 +125,7 @@ std::string worker::work(std::string &input, std::string &ip) { std::string value; bool parsing_key = true; // true = key, false = value - pos++; // Skip the '?' + ++pos; // Skip the '?' for (; pos < input_length; ++pos) { if (input[pos] == '=') { parsing_key = false; @@ -139,8 +149,19 @@ std::string worker::work(std::string &input, std::string &ip) { } } } + ++pos; + + if (input.compare(pos, 5, "HTTP/") != 0) { + return error("Malformed HTTP request", client_opts); + } - pos += 10; // skip 'HTTP/1.1' - should probably be +=11, but just in case a client doesn't send \r + std::string http_version; + pos += 5; + while (input[pos] != '\r' && input[pos] != '\n') { + http_version.push_back(input[pos]); + ++pos; + } + ++pos; // skip line break - should probably be += 2, but just in case a client doesn't send \r // Parse headers params_type headers; @@ -171,66 +192,88 @@ std::string worker::work(std::string &input, std::string &ip) { } } + if (keepalive_enabled) { + auto hdr_http_close = headers.find("connection"); + if (hdr_http_close == headers.end()) { + client_opts.http_close = (http_version == "1.0"); + } else { + client_opts.http_close = (hdr_http_close->second != "Keep-Alive"); + } + } else { + client_opts.http_close = true; + } + + if (status != OPEN) { + return error("The tracker is temporarily unavailable.", client_opts); + } + + if (action == INVALID) { + return error("Invalid action", client_opts); + } + if (action == UPDATE) { - if (passkey == conf->site_password) { - return update(params); + if (passkey == site_password) { + return update(params, client_opts); } else { - return error("Authentication failure"); + return error("Authentication failure", client_opts); } } if (action == REPORT) { - if (passkey == conf->report_password) { - return report(params, users_list); + if (passkey == report_password) { + std::lock_guard<std::mutex> ul_lock(db->user_list_mutex); + return report(params, users_list, client_opts); } else { - return error("Authentication failure"); + return error("Authentication failure", client_opts); } } // Either a scrape or an announce - user_list::iterator u = users_list.find(passkey); - if (u == users_list.end()) { - return error("Passkey not found"); + std::unique_lock<std::mutex> ul_lock(db->user_list_mutex); + auto user_it = users_list.find(passkey); + if (user_it == users_list.end()) { + return error("Passkey not found", client_opts); } + user_ptr u = user_it->second; + ul_lock.unlock(); if (action == ANNOUNCE) { - std::unique_lock<std::mutex> tl_lock(db->torrent_list_mutex); // Let's translate the infohash into something nice // info_hash is a url encoded (hex) base 20 number std::string info_hash_decoded = hex_decode(params["info_hash"]); - torrent_list::iterator tor = torrents_list.find(info_hash_decoded); + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); + auto tor = torrents_list.find(info_hash_decoded); if (tor == torrents_list.end()) { - std::unique_lock<std::mutex> dr_lock(del_reasons_lock); + std::lock_guard<std::mutex> dr_lock(del_reasons_lock); auto msg = del_reasons.find(info_hash_decoded); if (msg != del_reasons.end()) { if (msg->second.reason != -1) { - return error("Unregistered torrent: " + get_del_reason(msg->second.reason)); + return error("Unregistered torrent: " + get_del_reason(msg->second.reason), client_opts); } else { - return error("Unregistered torrent"); + return error("Unregistered torrent", client_opts); } } else { - return error("Unregistered torrent"); + return error("Unregistered torrent", client_opts); } } - return announce(tor->second, u->second, params, headers, ip); + return announce(input, tor->second, u, params, headers, ip, client_opts); } else { - return scrape(infohashes, headers); + return scrape(infohashes, headers, client_opts); } } -std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, params_type &headers, std::string &ip) { +std::string worker::announce(const std::string &input, torrent &tor, user_ptr &u, params_type ¶ms, params_type &headers, std::string &ip, client_opts_t &client_opts) { cur_time = time(NULL); if (params["compact"] != "1") { - return error("Your client does not support compact announces"); + return error("Your client does not support compact announces", client_opts); } - bool gzip = false; - int64_t left = std::max((int64_t)0, strtolonglong(params["left"])); - int64_t uploaded = std::max((int64_t)0, strtolonglong(params["uploaded"])); - int64_t downloaded = std::max((int64_t)0, strtolonglong(params["downloaded"])); - int64_t corrupt = std::max((int64_t)0, strtolonglong(params["corrupt"])); + int64_t left = std::max((int64_t)0, strtoint64(params["left"])); + int64_t uploaded = std::max((int64_t)0, strtoint64(params["uploaded"])); + int64_t downloaded = std::max((int64_t)0, strtoint64(params["downloaded"])); + int64_t corrupt = std::max((int64_t)0, strtoint64(params["corrupt"])); int snatched = 0; // This is the value that gets sent to the database on a snatch int active = 1; // This is the value that marks a peer as active/inactive in the database @@ -242,26 +285,37 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par bool peer_changed = false; // Whether or not the peer is new or has changed since the last announcement bool invalid_ip = false; bool inc_l = false, inc_s = false, dec_l = false, dec_s = false; + userid_t userid = u->get_id(); params_type::const_iterator peer_id_iterator = params.find("peer_id"); if (peer_id_iterator == params.end()) { - return error("No peer ID"); + return error("No peer ID", client_opts); + } + const std::string peer_id = hex_decode(peer_id_iterator->second); + if (peer_id.length() != 20) { + return error("Invalid peer ID", client_opts); } - std::string peer_id = peer_id_iterator->second; - peer_id = hex_decode(peer_id); + std::unique_lock<std::mutex> wl_lock(db->whitelist_mutex); if (whitelist.size() > 0) { bool found = false; // Found client in whitelist? for (unsigned int i = 0; i < whitelist.size(); i++) { - if (peer_id.find(whitelist[i]) == 0) { + if (peer_id.compare(0, whitelist[i].length(), whitelist[i]) == 0) { found = true; break; } } if (!found) { - return error("Your client is not on the whitelist"); + return error("Your client is not on the whitelist", client_opts); } } + wl_lock.unlock(); + + std::stringstream peer_key_stream; + peer_key_stream << peer_id[12 + (tor.id & 7)] // "Randomize" the element order in the peer map by prefixing with a peer id byte + << userid // Include user id in the key to lower chance of peer id collisions + << peer_id; + const std::string peer_key(peer_key_stream.str()); if (params["event"] == "completed") { // Don't update <snatched> here as we may decide to use other conditions later on @@ -272,45 +326,44 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par update_torrent = true; active = 0; } - int userid = u->get_id(); peer * p; peer_list::iterator peer_it; // Insert/find the peer in the torrent list if (left > 0) { - peer_it = tor.leechers.find(peer_id); + peer_it = tor.leechers.find(peer_key); if (peer_it == tor.leechers.end()) { // We could search the seed list as well, but the peer reaper will sort things out eventually - peer_it = add_peer(tor.leechers, peer_id); + peer_it = add_peer(tor.leechers, peer_key); inserted = true; inc_l = true; } } else if (completed_torrent) { - peer_it = tor.leechers.find(peer_id); + peer_it = tor.leechers.find(peer_key); if (peer_it == tor.leechers.end()) { - peer_it = tor.seeders.find(peer_id); + peer_it = tor.seeders.find(peer_key); if (peer_it == tor.seeders.end()) { - peer_it = add_peer(tor.seeders, peer_id); + peer_it = add_peer(tor.seeders, peer_key); inserted = true; inc_s = true; } else { completed_torrent = false; } - } else if (tor.seeders.find(peer_id) != tor.seeders.end()) { + } else if (tor.seeders.find(peer_key) != tor.seeders.end()) { // If the peer exists in both peer lists, just decrement the seed count. // Should be cheaper than searching the seed list in the left > 0 case dec_s = true; } } else { - peer_it = tor.seeders.find(peer_id); + peer_it = tor.seeders.find(peer_key); if (peer_it == tor.seeders.end()) { - peer_it = tor.leechers.find(peer_id); + peer_it = tor.leechers.find(peer_key); if (peer_it == tor.leechers.end()) { - peer_it = add_peer(tor.seeders, peer_id); + peer_it = add_peer(tor.seeders, peer_key); inserted = true; } else { p = &peer_it->second; std::pair<peer_list::iterator, bool> insert - = tor.seeders.insert(std::pair<std::string, peer>(peer_id, *p)); + = tor.seeders.insert(std::pair<std::string, peer>(peer_key, *p)); tor.leechers.erase(peer_it); peer_it = insert.first; peer_changed = true; @@ -373,7 +426,7 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par upspeed = uploaded_change / (cur_time - p->last_announced); downspeed = downloaded_change / (cur_time - p->last_announced); } - std::set<int>::iterator sit = tor.tokened_users.find(userid); + auto sit = tor.tokened_users.find(userid); if (tor.free_torrent == NEUTRAL) { downloaded_change = 0; uploaded_change = 0; @@ -415,7 +468,7 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par } } - unsigned int port = strtolong(params["port"]); + uint16_t port = strtoint32(params["port"]) & 0xFFFF; // Generate compact ip/port string if (inserted || port != p->port || ip != p->ip) { p->port = port; @@ -464,18 +517,18 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par } db->record_peer(record_str, record_ip, peer_id, headers["user-agent"]); } else { - record << '(' << tor.id << ',' << (cur_time - p->first_announced) << ',' << p->announces << ','; + record << '(' << userid << ',' << tor.id << ',' << (cur_time - p->first_announced) << ',' << p->announces << ','; std::string record_str = record.str(); db->record_peer(record_str, peer_id); } // Select peers! - unsigned int numwant; + uint32_t numwant; params_type::const_iterator param_numwant = params.find("numwant"); if (param_numwant == params.end()) { - numwant = 50; + numwant = numwant_limit; } else { - numwant = std::min(50l, strtolong(param_numwant->second)); + numwant = std::min((int32_t)numwant_limit, strtoint32(param_numwant->second)); } if (stopped_torrent) { @@ -504,7 +557,7 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par // User is a seeder now! if (!inserted) { std::pair<peer_list::iterator, bool> insert - = tor.seeders.insert(std::pair<std::string, peer>(peer_id, *p)); + = tor.seeders.insert(std::pair<std::string, peer>(peer_key, *p)); tor.leechers.erase(peer_it); peer_it = insert.first; p = &peer_it->second; @@ -555,7 +608,7 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par i = tor.seeders.begin(); } // Don't show users themselves - if (i->second.user->get_id() == userid || !i->second.visible) { + if (i->second.user->is_deleted() || i->second.user->get_id() == userid || !i->second.visible) { ++i; continue; } @@ -569,7 +622,7 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par if (found_peers < numwant && tor.leechers.size() > 1) { for (peer_list::const_iterator i = tor.leechers.begin(); i != tor.leechers.end() && found_peers < numwant; ++i) { // Don't show users themselves or leech disabled users - if (i->second.ip_port == p->ip_port || i->second.user->get_id() == userid || !i->second.visible) { + if (i->second.user->is_deleted() || i->second.ip_port == p->ip_port || i->second.user->get_id() == userid || !i->second.visible) { continue; } found_peers++; @@ -590,10 +643,8 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par } // Update the stats - std::unique_lock<std::mutex> lock(stats.mutex); stats.succ_announcements++; if (dec_l || dec_s || inc_l || inc_s) { - std::unique_lock<std::mutex> us_lock(ustats_lock); if (inc_l) { p->user->incr_leeching(); stats.leechers++; @@ -611,12 +662,10 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par stats.seeders--; } } - lock.unlock(); // Correct the stats for the old user if the peer's user link has changed if (p->user != u) { if (!stopped_torrent) { - std::unique_lock<std::mutex> us_lock(ustats_lock); if (left > 0) { u->incr_leeching(); p->user->decr_leeching(); @@ -648,7 +697,7 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par } if (!u->can_leech() && left > 0) { - return error("Access denied, leeching forbidden"); + return error("Access denied, leeching forbidden", client_opts); } std::string output = "d8:completei"; @@ -659,9 +708,9 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par output += "e10:incompletei"; output += inttostr(tor.leechers.size()); output += "e8:intervali"; - output += inttostr(conf->announce_interval+std::min((size_t)600, tor.seeders.size())); // ensure a more even distribution of announces/second + output += inttostr(announce_interval + std::min((size_t)600, tor.seeders.size())); // ensure a more even distribution of announces/second output += "e12:min intervali"; - output += inttostr(conf->announce_interval); + output += inttostr(announce_interval); output += "e5:peers"; if (peers.length() == 0) { output += "0:"; @@ -680,13 +729,12 @@ std::string worker::announce(torrent &tor, user_ptr &u, params_type ¶ms, par * possibly inflated return size */ /*if (headers["accept-encoding"].find("gzip") != std::string::npos) { - gzip = true; + client_opts.gzip = true; }*/ - return response(output, gzip, false); + return response(output, client_opts); } -std::string worker::scrape(const std::list<std::string> &infohashes, params_type &headers) { - bool gzip = false; +std::string worker::scrape(const std::list<std::string> &infohashes, params_type &headers, client_opts_t &client_opts) { std::string output = "d5:filesd"; for (std::list<std::string>::const_iterator i = infohashes.begin(); i != infohashes.end(); ++i) { std::string infohash = *i; @@ -711,16 +759,17 @@ std::string worker::scrape(const std::list<std::string> &infohashes, params_type } output += "ee"; if (headers["accept-encoding"].find("gzip") != std::string::npos) { - gzip = true; + client_opts.gzip = true; } - return response(output, gzip, false); + return response(output, client_opts); } //TODO: Restrict to local IPs -std::string worker::update(params_type ¶ms) { +std::string worker::update(params_type ¶ms, client_opts_t &client_opts) { if (params["action"] == "change_passkey") { std::string oldpasskey = params["oldpasskey"]; std::string newpasskey = params["newpasskey"]; + std::lock_guard<std::mutex> ul_lock(db->user_list_mutex); auto u = users_list.find(oldpasskey); if (u == users_list.end()) { std::cout << "No user with passkey " << oldpasskey << " exists when attempting to change passkey to " << newpasskey << std::endl; @@ -733,10 +782,11 @@ std::string worker::update(params_type ¶ms) { torrent *t; std::string info_hash = params["info_hash"]; info_hash = hex_decode(info_hash); + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); auto i = torrents_list.find(info_hash); if (i == torrents_list.end()) { t = &torrents_list[info_hash]; - t->id = strtolong(params["id"]); + t->id = strtoint32(params["id"]); t->balance = 0; t->completed = 0; t->last_selected_seeder = ""; @@ -762,6 +812,7 @@ std::string worker::update(params_type ¶ms) { } else { fl = NEUTRAL; } + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); auto torrent_it = torrents_list.find(info_hash); if (torrent_it != torrents_list.end()) { torrent_it->second.free_torrent = fl; @@ -781,6 +832,7 @@ std::string worker::update(params_type ¶ms) { } else { fl = NEUTRAL; } + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); for (unsigned int pos = 0; pos < info_hashes.length(); pos += 20) { std::string info_hash = info_hashes.substr(pos, 20); auto torrent_it = torrents_list.find(info_hash); @@ -794,6 +846,7 @@ std::string worker::update(params_type ¶ms) { } else if (params["action"] == "add_token") { std::string info_hash = hex_decode(params["info_hash"]); int userid = atoi(params["userid"].c_str()); + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); auto torrent_it = torrents_list.find(info_hash); if (torrent_it != torrents_list.end()) { torrent_it->second.tokened_users.insert(userid); @@ -803,6 +856,7 @@ std::string worker::update(params_type ¶ms) { } else if (params["action"] == "remove_token") { std::string info_hash = hex_decode(params["info_hash"]); int userid = atoi(params["userid"].c_str()); + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); auto torrent_it = torrents_list.find(info_hash); if (torrent_it != torrents_list.end()) { torrent_it->second.tokened_users.erase(userid); @@ -817,22 +871,19 @@ std::string worker::update(params_type ¶ms) { if (reason_it != params.end()) { reason = atoi(params["reason"].c_str()); } + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); auto torrent_it = torrents_list.find(info_hash); if (torrent_it != torrents_list.end()) { std::cout << "Deleting torrent " << torrent_it->second.id << " for the reason '" << get_del_reason(reason) << "'" << std::endl; - std::unique_lock<std::mutex> stats_lock(stats.mutex); stats.leechers -= torrent_it->second.leechers.size(); stats.seeders -= torrent_it->second.seeders.size(); - stats_lock.unlock(); - std::unique_lock<std::mutex> us_lock(ustats_lock); - for (auto p = torrent_it->second.leechers.begin(); p != torrent_it->second.leechers.end(); ++p) { - p->second.user->decr_leeching(); + for (auto &p: torrent_it->second.leechers) { + p.second.user->decr_leeching(); } - for (auto p = torrent_it->second.seeders.begin(); p != torrent_it->second.seeders.end(); ++p) { - p->second.user->decr_seeding(); + for (auto &p: torrent_it->second.seeders) { + p.second.user->decr_seeding(); } - us_lock.unlock(); - std::unique_lock<std::mutex> dr_lock(del_reasons_lock); + std::lock_guard<std::mutex> dr_lock(del_reasons_lock); del_message msg; msg.reason = reason; msg.time = time(NULL); @@ -843,31 +894,37 @@ std::string worker::update(params_type ¶ms) { } } else if (params["action"] == "add_user") { std::string passkey = params["passkey"]; - unsigned int userid = strtolong(params["id"]); + userid_t userid = strtoint32(params["id"]); + std::lock_guard<std::mutex> ul_lock(db->user_list_mutex); auto u = users_list.find(passkey); if (u == users_list.end()) { bool protect_ip = params["visible"] == "0"; - user_ptr u(new user(userid, true, protect_ip)); - users_list.insert(std::pair<std::string, user_ptr>(passkey, u)); + user_ptr tmp_user = std::make_shared<user>(userid, true, protect_ip); + users_list.insert(std::pair<std::string, user_ptr>(passkey, tmp_user)); std::cout << "Added user " << passkey << " with id " << userid << std::endl; } else { std::cout << "Tried to add already known user " << passkey << " with id " << userid << std::endl; + u->second->set_deleted(false); } } else if (params["action"] == "remove_user") { std::string passkey = params["passkey"]; + std::lock_guard<std::mutex> ul_lock(db->user_list_mutex); auto u = users_list.find(passkey); if (u != users_list.end()) { std::cout << "Removed user " << passkey << " with id " << u->second->get_id() << std::endl; + u->second->set_deleted(true); users_list.erase(u); } } else if (params["action"] == "remove_users") { // Each passkey is exactly 32 characters long. std::string passkeys = params["passkeys"]; + std::lock_guard<std::mutex> ul_lock(db->user_list_mutex); for (unsigned int pos = 0; pos < passkeys.length(); pos += 32) { std::string passkey = passkeys.substr(pos, 32); auto u = users_list.find(passkey); if (u != users_list.end()) { std::cout << "Removed user " << passkey << std::endl; + u->second->set_deleted(true); users_list.erase(passkey); } } @@ -881,6 +938,7 @@ std::string worker::update(params_type ¶ms) { if (params["visible"] == "0") { protect_ip = true; } + std::lock_guard<std::mutex> ul_lock(db->user_list_mutex); user_list::iterator i = users_list.find(passkey); if (i == users_list.end()) { std::cout << "No user with passkey " << passkey << " found when attempting to change leeching status!" << std::endl; @@ -891,10 +949,12 @@ std::string worker::update(params_type ¶ms) { } } else if (params["action"] == "add_whitelist") { std::string peer_id = params["peer_id"]; + std::lock_guard<std::mutex> wl_lock(db->whitelist_mutex); whitelist.push_back(peer_id); std::cout << "Whitelisted " << peer_id << std::endl; } else if (params["action"] == "remove_whitelist") { std::string peer_id = params["peer_id"]; + std::lock_guard<std::mutex> wl_lock(db->whitelist_mutex); for (unsigned int i = 0; i < whitelist.size(); i++) { if (whitelist[i].compare(peer_id) == 0) { whitelist.erase(whitelist.begin() + i); @@ -905,6 +965,7 @@ std::string worker::update(params_type ¶ms) { } else if (params["action"] == "edit_whitelist") { std::string new_peer_id = params["new_peer_id"]; std::string old_peer_id = params["old_peer_id"]; + std::lock_guard<std::mutex> wl_lock(db->whitelist_mutex); for (unsigned int i = 0; i < whitelist.size(); i++) { if (whitelist[i].compare(old_peer_id) == 0) { whitelist.erase(whitelist.begin() + i); @@ -914,13 +975,15 @@ std::string worker::update(params_type ¶ms) { whitelist.push_back(new_peer_id); std::cout << "Edited whitelist item from " << old_peer_id << " to " << new_peer_id << std::endl; } else if (params["action"] == "update_announce_interval") { - unsigned int interval = strtolong(params["new_announce_interval"]); - conf->announce_interval = interval; - std::cout << "Edited announce interval to " << interval << std::endl; + const std::string interval = params["new_announce_interval"]; + conf->set("announce_interval", interval); + announce_interval = conf->get_uint("announce_interval"); + std::cout << "Edited announce interval to " << announce_interval << std::endl; } else if (params["action"] == "info_torrent") { std::string info_hash_hex = params["info_hash"]; std::string info_hash = hex_decode(info_hash_hex); std::cout << "Info for torrent '" << info_hash_hex << "'" << std::endl; + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); auto torrent_it = torrents_list.find(info_hash); if (torrent_it != torrents_list.end()) { std::cout << "Torrent " << torrent_it->second.id @@ -929,24 +992,27 @@ std::string worker::update(params_type ¶ms) { std::cout << "Failed to find torrent " << info_hash_hex << std::endl; } } - return response("success", false, false); + return response("success", client_opts); } -peer_list::iterator worker::add_peer(peer_list &peer_list, std::string &peer_id) { +peer_list::iterator worker::add_peer(peer_list &peer_list, const std::string &peer_key) { peer new_peer; - std::pair<peer_list::iterator, bool> insert - = peer_list.insert(std::pair<std::string, peer>(peer_id, new_peer)); - return insert.first; + auto it = peer_list.insert(std::pair<std::string, peer>(peer_key, new_peer)); + return it.first; } void worker::start_reaper() { - std::thread thread(&worker::do_start_reaper, this); - thread.detach(); + if (!reaper_active) { + std::thread thread(&worker::do_start_reaper, this); + thread.detach(); + } } void worker::do_start_reaper() { + reaper_active = true; reap_peers(); reap_del_reasons(); + reaper_active = false; } void worker::reap_peers() { @@ -959,12 +1025,10 @@ void worker::reap_peers() { auto p = t->second.leechers.begin(); peer_list::iterator del_p; while (p != t->second.leechers.end()) { - if (p->second.last_announced + conf->peers_timeout < cur_time) { + if (p->second.last_announced + peers_timeout < cur_time) { + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); del_p = p++; - std::unique_lock<std::mutex> us_lock(ustats_lock); del_p->second.user->decr_leeching(); - us_lock.unlock(); - std::unique_lock<std::mutex> tl_lock(db->torrent_list_mutex); t->second.leechers.erase(del_p); reaped_this = true; reaped_l++; @@ -974,12 +1038,10 @@ void worker::reap_peers() { } p = t->second.seeders.begin(); while (p != t->second.seeders.end()) { - if (p->second.last_announced + conf->peers_timeout < cur_time) { + if (p->second.last_announced + peers_timeout < cur_time) { + std::lock_guard<std::mutex> tl_lock(db->torrent_list_mutex); del_p = p++; - std::unique_lock<std::mutex> us_lock(ustats_lock); del_p->second.user->decr_seeding(); - us_lock.unlock(); - std::unique_lock<std::mutex> tl_lock(db->torrent_list_mutex); t->second.seeders.erase(del_p); reaped_this = true; reaped_s++; @@ -996,7 +1058,6 @@ void worker::reap_peers() { } } if (reaped_l || reaped_s) { - std::unique_lock<std::mutex> lock(stats.mutex); stats.leechers -= reaped_l; stats.seeders -= reaped_s; } @@ -1006,13 +1067,13 @@ void worker::reap_peers() { void worker::reap_del_reasons() { std::cout << "Starting del reason reaper" << std::endl; - time_t max_time = time(NULL) - conf->del_reason_lifetime; + time_t max_time = time(NULL) - del_reason_lifetime; auto it = del_reasons.begin(); unsigned int reaped = 0; for (; it != del_reasons.end(); ) { if (it->second.time <= max_time) { auto del_it = it++; - std::unique_lock<std::mutex> dr_lock(del_reasons_lock); + std::lock_guard<std::mutex> dr_lock(del_reasons_lock); del_reasons.erase(del_it); reaped++; continue; diff --git a/src/worker.h b/src/worker.h index 5eb1987..63f8973 100644 --- a/src/worker.h +++ b/src/worker.h @@ -1,12 +1,13 @@ +#ifndef WORKER_H +#define WORKER_H + #include <string> #include <vector> #include <list> #include <unordered_map> -#include <netinet/in.h> -#include <arpa/inet.h> #include <iostream> -#include <fstream> #include <mutex> +#include <ctime> #include "site_comm.h" #include "ocelot.h" @@ -14,35 +15,47 @@ enum tracker_status { OPEN, PAUSED, CLOSING }; // tracker status class worker { private: - torrent_list torrents_list; - user_list users_list; - std::vector<std::string> whitelist; - std::unordered_map<std::string, del_message> del_reasons; config * conf; mysql * db; + site_comm * s_comm; + torrent_list &torrents_list; + user_list &users_list; + std::vector<std::string> &whitelist; + std::unordered_map<std::string, del_message> del_reasons; tracker_status status; + bool reaper_active; time_t cur_time; - site_comm * s_comm; + + unsigned int announce_interval; + unsigned int del_reason_lifetime; + unsigned int peers_timeout; + unsigned int numwant_limit; + bool keepalive_enabled; + std::string site_password; + std::string report_password; std::mutex del_reasons_lock; - std::mutex ustats_lock; + void load_config(config * conf); void do_start_reaper(); void reap_peers(); void reap_del_reasons(); std::string get_del_reason(int code); - peer_list::iterator add_peer(peer_list &peer_list, std::string &peer_id); - bool peer_is_visible(user_ptr &u, peer *p); + peer_list::iterator add_peer(peer_list &peer_list, const std::string &peer_id); + inline bool peer_is_visible(user_ptr &u, peer *p); public: - worker(torrent_list &torrents, user_list &users, std::vector<std::string> &_whitelist, config * conf_obj, mysql * db_obj, site_comm * sc); - std::string work(std::string &input, std::string &ip); - std::string announce(torrent &tor, user_ptr &u, params_type ¶ms, params_type &headers, std::string &ip); - std::string scrape(const std::list<std::string> &infohashes, params_type &headers); - std::string update(params_type ¶ms); + worker(config * conf_obj, torrent_list &torrents, user_list &users, std::vector<std::string> &_whitelist, mysql * db_obj, site_comm * sc); + void reload_config(config * conf); + std::string work(const std::string &input, std::string &ip, client_opts_t &client_opts); + std::string announce(const std::string &input, torrent &tor, user_ptr &u, params_type ¶ms, params_type &headers, std::string &ip, client_opts_t &client_opts); + std::string scrape(const std::list<std::string> &infohashes, params_type &headers, client_opts_t &client_opts); + std::string update(params_type ¶ms, client_opts_t &client_opts); - bool signal(int sig); + void reload_lists(); + bool shutdown(); tracker_status get_status() { return status; } void start_reaper(); }; +#endif |