mirror of
https://github.com/WhatCD/Ocelot.git
synced 2026-01-16 19:05:03 -05:00
Ocelot 1.0
This commit is contained in:
23
CHANGES
23
CHANGES
@@ -1,3 +1,24 @@
|
||||
-- 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
|
||||
@@ -22,7 +43,6 @@ 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
|
||||
Make Ocelot clear stale peer data out of the db on startup
|
||||
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
|
||||
@@ -40,7 +60,6 @@ Track transfer stats from users without leech privs
|
||||
Convert all headers to lowercase
|
||||
Don't show users to themselves (patch by GrecKo)
|
||||
Don't write peer updates to the binlog
|
||||
Fix incorrect handling of "corrupt" stat
|
||||
Search for peer in leecher list if not found in seeders list
|
||||
Sort response dictionary according to BitTorrent specs
|
||||
|
||||
|
||||
11
Makefile.am
11
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)
|
||||
ocelot_LDFLAGS = -pthread $(BOOST_LDFLAGS)
|
||||
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
|
||||
|
||||
82
Makefile.in
82
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
|
||||
|
||||
45
README.md
Normal file
45
README.md
Normal file
@@ -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
|
||||
66
aclocal.m4
vendored
66
aclocal.m4
vendored
@@ -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])
|
||||
|
||||
2
compile
2
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
|
||||
|
||||
156
config.cpp
Normal file
156
config.cpp
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
73
config.h
73
config.h
@@ -2,38 +2,51 @@
|
||||
#define OCELOT_CONFIG_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class config {
|
||||
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:
|
||||
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();
|
||||
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:
|
||||
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
|
||||
|
||||
13
configure.ac
13
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
|
||||
MYSQL_C_API_LOCATION
|
||||
AC_LANG_PUSH(C++)
|
||||
MYSQLPP_DEVEL
|
||||
AX_PTHREAD([], AC_MSG_FAILURE([pthread library is required]))
|
||||
EV_DEVEL
|
||||
AC_LANG_POP(C++)
|
||||
MYSQL_C_API_LOCATION
|
||||
MYSQLPP_DEVEL
|
||||
TCMALLOC
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
290
db.cpp
290
db.cpp
@@ -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;
|
||||
} else {
|
||||
t.free_torrent = NORMAL;
|
||||
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 {
|
||||
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();
|
||||
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::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]);
|
||||
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;
|
||||
}
|
||||
|
||||
25
db.h
25
db.h
@@ -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
|
||||
|
||||
171
events.cpp
171
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();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int connection_mother::create_listen_socket() {
|
||||
sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
int new_listen_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
// Create libev event loop
|
||||
ev::io event_loop_watcher;
|
||||
|
||||
event_loop_watcher.set<connection_mother, &connection_mother::handle_connect>(this);
|
||||
event_loop_watcher.start(listen_socket, ev::READ);
|
||||
|
||||
// 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);
|
||||
return new_listen_socket;
|
||||
}
|
||||
|
||||
schedule_event.set<schedule, &schedule::handle>(&timer);
|
||||
schedule_event.set(conf->schedule_interval, conf->schedule_interval); // After interval, every interval
|
||||
schedule_event.start();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
events.h
33
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);
|
||||
|
||||
332
m4/ax_pthread.m4
Normal file
332
m4/ax_pthread.m4
Normal file
@@ -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
|
||||
55
m4/ev++.m4
55
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])
|
||||
@@ -88,15 +98,38 @@ AC_DEFUN([EV_DEVEL],
|
||||
dnl let us build actual programs against libev.
|
||||
dnl
|
||||
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]))
|
||||
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
|
||||
|
||||
|
||||
@@ -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])
|
||||
@@ -117,15 +127,24 @@ AC_DEFUN([MYSQLPP_DEVEL],
|
||||
dnl let us build actual programs against MySQL++.
|
||||
dnl
|
||||
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]))
|
||||
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
|
||||
|
||||
|
||||
@@ -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,45 +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])
|
||||
|
||||
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!])
|
||||
]
|
||||
)
|
||||
])
|
||||
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
|
||||
|
||||
|
||||
@@ -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])]
|
||||
)]
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
2
missing
2
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
|
||||
|
||||
32
ocelot.conf.dist
Normal file
32
ocelot.conf.dist
Normal file
@@ -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
|
||||
117
ocelot.cpp
117
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 mysql *db;
|
||||
static site_comm *sc;
|
||||
static config *conf;
|
||||
static schedule *sched;
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
std::cout << "Caught SIGINT/SIGTERM" << std::endl;
|
||||
if (work->signal(sig)) {
|
||||
exit(0);
|
||||
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::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 = new mysql(conf);
|
||||
|
||||
if (!db->connected()) {
|
||||
std::cout << "Exiting" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
db.verbose_flush = verbose;
|
||||
db->verbose_flush = verbose;
|
||||
|
||||
site_comm sc(conf);
|
||||
sc.verbose_flush = verbose;
|
||||
|
||||
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;
|
||||
}
|
||||
sc = new site_comm(conf);
|
||||
sc->verbose_flush = verbose;
|
||||
|
||||
user_list users_list;
|
||||
db.load_users(users_list);
|
||||
std::cout << "Loaded " << users_list.size() << " users" << std::endl;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
52
ocelot.h
52
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
|
||||
|
||||
12
report.cpp
12
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);
|
||||
}
|
||||
|
||||
2
report.h
2
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);
|
||||
|
||||
32
response.cpp
32
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;
|
||||
}
|
||||
|
||||
15
response.h
15
response.h
@@ -1,6 +1,11 @@
|
||||
#include <string>
|
||||
#ifndef RESPONSE_H
|
||||
#define REPSONSE_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);
|
||||
#include <string>
|
||||
#include "ocelot.h"
|
||||
|
||||
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
|
||||
|
||||
42
schedule.cpp
42
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;
|
||||
|
||||
next_reap_peers = time(NULL) + conf->reap_peers_interval + 40;
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
24
schedule.h
24
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
|
||||
|
||||
@@ -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;;
|
||||
|
||||
14
site_comm.h
14
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
|
||||
|
||||
47
user.cpp
47
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++;
|
||||
}
|
||||
|
||||
40
user.h
40
user.h
@@ -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
|
||||
|
||||
305
worker.cpp
305
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;
|
||||
|
||||
pos += 10; // skip 'HTTP/1.1' - should probably be +=11, but just in case a client doesn't send \r
|
||||
if (input.compare(pos, 5, "HTTP/") != 0) {
|
||||
return error("Malformed HTTP request", client_opts);
|
||||
}
|
||||
|
||||
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 (action == UPDATE) {
|
||||
if (passkey == conf->site_password) {
|
||||
return update(params);
|
||||
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 {
|
||||
return error("Authentication failure");
|
||||
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 == site_password) {
|
||||
return update(params, client_opts);
|
||||
} else {
|
||||
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;
|
||||
|
||||
49
worker.h
49
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;
|
||||
tracker_status status;
|
||||
time_t cur_time;
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user