diff --git a/configure.ac b/configure.ac index 98439946..9e2bc4a6 100644 --- a/configure.ac +++ b/configure.ac @@ -67,6 +67,7 @@ ARIA2_ARG_WITHOUT([libz]) ARIA2_ARG_WITH([tcmalloc]) ARIA2_ARG_WITH([jemalloc]) ARIA2_ARG_WITHOUT([libssh2]) +ARIA2_ARG_WITHOUT([systemd]) ARIA2_ARG_DISABLE([ssl]) ARIA2_ARG_DISABLE([bittorrent]) @@ -501,6 +502,19 @@ if test "x$with_libssh2" = "xyes"; then fi fi +have_systemd=no +if test "x$with_systemd" = "xyes"; then + PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes], [have_systemd=no]) + if test "x$have_systemd" = "xyes"; then + AC_DEFINE([HAVE_SYSTEMD], [1], [Define to 1 if you have systemd.]) + else + AC_MSG_WARN([$SYSTEMD_PKG_ERRORS]) + if test "x$with_systemd_requested" = "xyes"; then + ARIA2_DEP_NOT_MET([systemd]) + fi + fi +fi + have_libcares=no if test "x$with_libcares" = "xyes"; then PKG_CHECK_MODULES([LIBCARES], [libcares >= 1.16.0], [have_libcares=yes], @@ -644,6 +658,9 @@ AM_CONDITIONAL([HAVE_SQLITE3], [test "x$have_sqlite3" = "xyes"]) # Set conditional for libssh2 AM_CONDITIONAL([HAVE_LIBSSH2], [test "x$have_libssh2" = "xyes"]) +# Set conditional for systemd +AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$have_systemd" = "xyes"]) + case "$host" in *solaris*) save_LIBS=$LIBS @@ -1105,6 +1122,7 @@ LibExpat: $have_libexpat (CFLAGS='$EXPAT_CFLAGS' LIBS='$EXPAT_LIBS') LibCares: $have_libcares (CFLAGS='$LIBCARES_CFLAGS' LIBS='$LIBCARES_LIBS') Zlib: $have_zlib (CFLAGS='$ZLIB_CFLAGS' LIBS='$ZLIB_LIBS') Libssh2: $have_libssh2 (CFLAGS='$LIBSSH2_CFLAGS' LIBS='$LIBSSH2_LIBS') +Systemd: $have_systemd (CFLAGS='$SYSTEMD_CFLAGS' LIBS='$SYSTEMD_LIBS') Tcmalloc: $have_tcmalloc (CFLAGS='$TCMALLOC_CFLAGS' LIBS='$TCMALLOC_LIBS') Jemalloc: $have_jemalloc (CFLAGS='$JEMALLOC_CFLAGS' LIBS='$JEMALLOC_LIBS') Epoll: $have_epoll diff --git a/src/DownloadEngineFactory.cc b/src/DownloadEngineFactory.cc index da45dff1..4ae563d9 100644 --- a/src/DownloadEngineFactory.cc +++ b/src/DownloadEngineFactory.cc @@ -35,6 +35,9 @@ #include "DownloadEngineFactory.h" #include +#ifdef HAVE_SYSTEMD +#include +#endif // HAVE_SYSTEMD #include "Option.h" #include "RequestGroup.h" @@ -205,14 +208,31 @@ std::unique_ptr DownloadEngineFactory::newDownloadEngine( if (secure) { A2_LOG_NOTICE("RPC transport will be encrypted."); } - static int families[] = {AF_INET, AF_INET6}; - size_t familiesLength = op->getAsBool(PREF_DISABLE_IPV6) ? 1 : 2; - for (size_t i = 0; i < familiesLength; ++i) { - auto httpListenCommand = make_unique( - e->newCUID(), e.get(), families[i], secure); - if (httpListenCommand->bindPort(op->getAsInt(PREF_RPC_LISTEN_PORT))) { - e->addCommand(std::move(httpListenCommand)); - ok = true; +#ifdef HAVE_SYSTEMD + int fds = sd_listen_fds(1); + if (0 < fds) { + for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fds; fd++) { + auto httpListenCommand = make_unique( + e->newCUID(), e.get(), fd, secure); + if (httpListenCommand->listen()) { + e->addCommand(std::move(httpListenCommand)); + ok = true; + } + } + } else +#endif // HAVE_SYSTEMD + { + static int families[] = {AF_INET, AF_INET6}; + size_t familiesLength = op->getAsBool(PREF_DISABLE_IPV6) ? 1 : 2; + for (size_t i = 0; i < familiesLength; ++i) { + auto httpListenCommand = make_unique( + e->newCUID(), e.get(), families[i], + op->getAsInt(PREF_RPC_LISTEN_PORT), + secure); + if (httpListenCommand->listen()) { + e->addCommand(std::move(httpListenCommand)); + ok = true; + } } } if (!ok) { diff --git a/src/HttpListenCommand.cc b/src/HttpListenCommand.cc index 7c7794aa..96e2323c 100644 --- a/src/HttpListenCommand.cc +++ b/src/HttpListenCommand.cc @@ -51,8 +51,14 @@ namespace aria2 { HttpListenCommand::HttpListenCommand(cuid_t cuid, DownloadEngine* e, int family, + uint16_t port, bool secure) + : Command(cuid), e_(e), fd_(-1), family_(family), port_(port), secure_(secure) +{ +} + +HttpListenCommand::HttpListenCommand(cuid_t cuid, DownloadEngine* e, int fd, bool secure) - : Command(cuid), e_(e), family_(family), secure_(secure) + : Command(cuid), e_(e), fd_(fd), family_(AF_INET), port_(0), secure_(secure) { } @@ -89,27 +95,34 @@ bool HttpListenCommand::execute() return false; } -bool HttpListenCommand::bindPort(uint16_t port) +bool HttpListenCommand::listen() { if (serverSocket_) { e_->deleteSocketForReadCheck(serverSocket_, this); } serverSocket_ = std::make_shared(); - const int ipv = (family_ == AF_INET) ? 4 : 6; + std::string sListen; try { - int flags = 0; - if (e_->getOption()->getAsBool(PREF_RPC_LISTEN_ALL)) { - flags = AI_PASSIVE; + if (fd_ < 0) { + int flags = 0; + const int ipv = (family_ == AF_INET) ? 4 : 6; + sListen = fmt("TCP/IPv%d port %u", ipv, port_); + if (e_->getOption()->getAsBool(PREF_RPC_LISTEN_ALL)) { + flags = AI_PASSIVE; + } + serverSocket_->bind(nullptr, port_, family_, flags); + } else { + sListen = fmt("file descriptor fd=%d", fd_); + serverSocket_->bindExistingFd(fd_); } - serverSocket_->bind(nullptr, port, family_, flags); serverSocket_->beginListen(); - A2_LOG_INFO(fmt(MSG_LISTENING_PORT, getCuid(), port)); + A2_LOG_INFO(fmt(MSG_LISTENING_RPC, getCuid(), sListen.c_str())); e_->addSocketForReadCheck(serverSocket_, this); - A2_LOG_NOTICE(fmt(_("IPv%d RPC: listening on TCP port %u"), ipv, port)); + A2_LOG_NOTICE(fmt(_("RPC: listening on %s"), sListen.c_str())); return true; } catch (RecoverableException& e) { - A2_LOG_ERROR_EX(fmt("IPv%d RPC: failed to bind TCP port %u", ipv, port), e); + A2_LOG_ERROR_EX(fmt("RPC: failed to listen on %s", sListen.c_str()), e); serverSocket_->closeConnection(); } return false; diff --git a/src/HttpListenCommand.h b/src/HttpListenCommand.h index d63d5b3d..c023f2be 100644 --- a/src/HttpListenCommand.h +++ b/src/HttpListenCommand.h @@ -47,18 +47,22 @@ class SocketCore; class HttpListenCommand : public Command { private: DownloadEngine* e_; + int fd_; int family_; + uint16_t port_; std::shared_ptr serverSocket_; bool secure_; public: - HttpListenCommand(cuid_t cuid, DownloadEngine* e, int family, bool secure); + HttpListenCommand(cuid_t cuid, DownloadEngine* e, int family, uint16_t port, bool secure); + + HttpListenCommand(cuid_t cuid, DownloadEngine* e, int fd, bool secure); virtual ~HttpListenCommand(); virtual bool execute() CXX11_OVERRIDE; - bool bindPort(uint16_t port); + bool listen(); }; } // namespace aria2 diff --git a/src/Makefile.am b/src/Makefile.am index cb6e3b7c..6fc656c2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -712,6 +712,7 @@ AM_CPPFLAGS = \ @LIBGMP_CFLAGS@ \ @LIBGCRYPT_CFLAGS@ \ @LIBSSH2_CFLAGS@ \ + @SYSTEMD_CFLAGS@ \ @LIBCARES_CFLAGS@ \ @WSLAY_CFLAGS@ \ @TCMALLOC_CFLAGS@ \ @@ -735,6 +736,7 @@ EXTLDADD = @ALLOCA@ \ @LIBGMP_LIBS@ \ @LIBGCRYPT_LIBS@ \ @LIBSSH2_LIBS@ \ + @SYSTEMD_LIBS@ \ @LIBCARES_LIBS@ \ @WSLAY_LIBS@ \ @TCMALLOC_LIBS@ \ diff --git a/src/SocketCore.cc b/src/SocketCore.cc index 4e7421bc..6b64d67f 100644 --- a/src/SocketCore.cc +++ b/src/SocketCore.cc @@ -360,6 +360,18 @@ void SocketCore::bind(const struct sockaddr* addr, socklen_t addrlen) sockfd_ = fd; } +void SocketCore::bindExistingFd(int fd) +{ + std::string error; + if (fd < 0) { + error = fmt("Invalid file descriptor fd=%d", fd); + throw DL_ABORT_EX(fmt(EX_SOCKET_BIND, error.c_str())); + } + util::make_fd_cloexec(fd); + applySocketBufferSize(fd); + sockfd_ = fd; +} + void SocketCore::beginListen() { if (listen(sockfd_, 1024) == -1) { diff --git a/src/SocketCore.h b/src/SocketCore.h index 60f33279..2cd7890a 100644 --- a/src/SocketCore.h +++ b/src/SocketCore.h @@ -162,6 +162,11 @@ public: void bind(const char* addrp, uint16_t port, int family, int flags = AI_PASSIVE); + /** + * Bind to an existing file descriptor. + */ + void bindExistingFd(int fd); + /** * Listens form connection on it. * Call bind(uint16_t) before calling this function. diff --git a/src/message.h b/src/message.h index 4705d7fb..f3db6324 100644 --- a/src/message.h +++ b/src/message.h @@ -93,8 +93,8 @@ #define MSG_CONTENT_DISPOSITION_DETECTED \ "CUID#%" PRId64 " - Content-Disposition detected. Use %s as filename" #define MSG_PEER_BANNED "CUID#%" PRId64 " - Peer %s:%d banned." -#define MSG_LISTENING_PORT \ - "CUID#%" PRId64 " - Using port %d for accepting new connections" +#define MSG_LISTENING_RPC \ + "CUID#%" PRId64 " - Using %s for accepting new connections" #define MSG_BIND_FAILURE "CUID#%" PRId64 " - An error occurred while binding port=%d" #define MSG_INCOMING_PEER_CONNECTION \ "CUID#%" PRId64 " - Incoming connection, adding new command CUID#%" PRId64 ""