diff --git a/Makefile.am b/Makefile.am index 2140fb31..d269f251 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,7 @@ EXTRA_DIST= \ rak/algorithm.h \ rak/error_number.h \ rak/file_stat.h \ + rak/fs_stat.h \ rak/functional.h \ rak/functional_fun.h \ rak/path.h \ diff --git a/configure.ac b/configure.ac index 9163414e..1d7fe457 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,8 @@ TORRENT_OTFD() TORRENT_WITHOUT_VARIABLE_FDSET() TORRENT_WITHOUT_NCURSESW() +TORRENT_WITHOUT_STATVFS() +TORRENT_WITHOUT_STATFS() PKG_CHECK_MODULES(STUFF, sigc++-2.0 libtorrent >= 0.9.0, CXXFLAGS="$CXXFLAGS $STUFF_CFLAGS $CURL_CFLAGS"; diff --git a/doc/rtorrent.1.xml b/doc/rtorrent.1.xml index 74e1385c..1eb5faba 100644 --- a/doc/rtorrent.1.xml +++ b/doc/rtorrent.1.xml @@ -575,6 +575,17 @@ the event will not trigger more than once. + + close_low_diskspace = space + + +Close any active torrents on filesystems with less than +space diskspace left. Use with +schedule. + + + + load = file load_start = file diff --git a/doc/rtorrent.rc b/doc/rtorrent.rc index df0c0acd..42843f1d 100644 --- a/doc/rtorrent.rc +++ b/doc/rtorrent.rc @@ -26,6 +26,9 @@ #schedule = watch_directory,5,5,load_start=./watch/*.torrent #schedule = untied_directory,5,5,stop_untied= +# Close torrents when diskspace is low. +#schedule = untied_directory,5,60,close_low_diskspace=100M + # The ip address reported to the tracker. #ip = 127.0.0.1 #ip = rakshasa.no diff --git a/rak/fs_stat.h b/rak/fs_stat.h new file mode 100644 index 00000000..44e4978e --- /dev/null +++ b/rak/fs_stat.h @@ -0,0 +1,84 @@ +// rak - Rakshasa's toolbox +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#ifndef RAK_FS_STAT_H +#define RAK_FS_STAT_H + +#include +#include + +#include + +#if HAVE_SYS_VFS_H +#include +#endif +#if HAVE_SYS_STATVFS_H +#include +#endif +#if HAVE_SYS_STATFS_H +#include +#endif +#if HAVE_SYS_PARAM_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#endif + +namespace rak { + +class fs_stat { +public: + + typedef FS_STAT_SIZE_TYPE blocksize_type; + typedef FS_STAT_COUNT_TYPE blockcount_type; + typedef FS_STAT_STRUCT fs_stat_type; + + bool update(int fd) { return FS_STAT_FD; } + bool update(const char* fn) { return FS_STAT_FN; } + bool update(const std::string& filename) { return update(filename.c_str()); } + + blocksize_type blocksize() { return m_stat.f_bsize; } + blockcount_type blocks_avail() { return m_stat.f_bavail; } + int64_t bytes_avail() { return (int64_t) m_stat.f_bsize * m_stat.f_bavail; } + +private: + fs_stat_type m_stat; +}; + +} + +#endif diff --git a/scripts/checks.m4 b/scripts/checks.m4 index bb2a4485..70a15ab2 100644 --- a/scripts/checks.m4 +++ b/scripts/checks.m4 @@ -201,3 +201,119 @@ AC_DEFUN([TORRENT_WITHOUT_NCURSESW], [ ) ]) ]) + +AC_DEFUN([TORRENT_CHECK_STATVFS], [ + AC_CHECK_HEADERS(sys/vfs.h sys/statvfs.h sys/statfs.h) + + AC_MSG_CHECKING(for statvfs) + + AC_TRY_LINK( + [ + #if HAVE_SYS_VFS_H + #include + #endif + #if HAVE_SYS_STATVFS_H + #include + #endif + #if HAVE_SYS_STATFS_H + #include + #endif + ],[ + struct statvfs s; fsblkcnt_t c; + statvfs("", &s); + fstatvfs(0, &s); + ], + [ + AC_DEFINE(FS_STAT_FD, [fstatvfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd) + AC_DEFINE(FS_STAT_FN, [statvfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename) + AC_DEFINE(FS_STAT_STRUCT, [struct statvfs], Type of second argument to statfs function) + AC_DEFINE(FS_STAT_SIZE_TYPE, [unsigned long], Type of block size member in stat struct) + AC_DEFINE(FS_STAT_COUNT_TYPE, [fsblkcnt_t], Type of block count member in stat struct) + AC_MSG_RESULT(ok) + have_stat_vfs=yes + ], + [ + AC_MSG_RESULT(no) + have_stat_vfs=no + ]) +]) + +AC_DEFUN([TORRENT_CHECK_STATFS], [ + AC_CHECK_HEADERS(sys/statfs.h sys/param.h sys/mount.h) + + AC_MSG_CHECKING(for statfs) + + AC_TRY_LINK( + [ + #if HAVE_SYS_STATFS_H + #include + #endif + #if HAVE_SYS_PARAM_H + #include + #endif + #if HAVE_SYS_MOUNT_H + #include + #endif + ],[ + struct statfs s; + statfs("", &s); + fstatfs(0, &s); + ], + [ + AC_DEFINE(FS_STAT_FD, [fstatfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd) + AC_DEFINE(FS_STAT_FN, [statfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename) + AC_DEFINE(FS_STAT_STRUCT, [struct statfs], Type of second argument to statfs function) + AC_DEFINE(FS_STAT_SIZE_TYPE, [long], Type of block size member in stat struct) + AC_DEFINE(FS_STAT_COUNT_TYPE, [long], Type of block count member in stat struct) + AC_MSG_RESULT(ok) + ], + [ + AC_MSG_RESULT(no) + ]) +]) + +AC_DEFUN([TORRENT_DISABLED_STATFS], [ + AC_DEFINE(FS_STAT_FD, [(errno = ENOSYS) == 0], Function to determine filesystem stats from fd) + AC_DEFINE(FS_STAT_FN, [(errno = ENOSYS) == 0], Function to determine filesystem stats from filename) + AC_DEFINE(FS_STAT_STRUCT, [struct {blocksize_type f_bsize; blockcount_type f_bavail;}], Type of second argument to statfs function) + AC_DEFINE(FS_STAT_SIZE_TYPE, [int], Type of block size member in stat struct) + AC_DEFINE(FS_STAT_COUNT_TYPE, [int], Type of block count member in stat struct) + AC_MSG_RESULT(No filesystem stats available) +]) + +AC_DEFUN([TORRENT_WITHOUT_STATVFS], [ + AC_ARG_WITH(statvfs, + [ --without-statvfs Don't try to use statvfs to find free diskspace.], + [ + if test "$withval" = "yes"; then + TORRENT_CHECK_STATVFS + else + have_stat_vfs=no + fi + ], + [ + TORRENT_CHECK_STATVFS + ]) +]) + +AC_DEFUN([TORRENT_WITHOUT_STATFS], [ + AC_ARG_WITH(statfs, + [ --without-statfs Don't try to use statfs to find free diskspace.], + [ + if test "$have_stat_vfs" = "no"; then + if test "$withval" = "yes"; then + TORRENT_CHECK_STATFS + else + TORRENT_DISABLED_STATFS + fi + fi + ], + [ + if test "$have_stat_vfs" = "no"; then + TORRENT_CHECK_STATFS + if test "$have_stat_vfs" = "no"; then + TORRENT_DISABLED_STATFS + fi + fi + ]) +]) diff --git a/src/core/download.h b/src/core/download.h index b30e257f..c1f3f447 100644 --- a/src/core/download.h +++ b/src/core/download.h @@ -66,6 +66,7 @@ public: bool is_open() const { return m_download.is_open(); } bool is_active() const { return m_download.is_active(); } bool is_done() const { return m_download.chunks_done() == m_download.chunks_total(); } + bool is_downloading() const { return is_active() && !is_done(); } // FIXME: Fixed a bug in libtorrent that caused is_hash_checked to // return true when the torrent is closed. Remove this redundant diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index c71ef13a..1aeb7ecd 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -41,12 +41,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -156,6 +159,28 @@ apply_remove_untied(Control* m) { } } +void +apply_close_low_diskspace(Control* m, int64_t arg) { + core::Manager::DListItr itr = m->core()->download_list()->begin(); + + while ((itr = std::find_if(itr, m->core()->download_list()->end(), + rak::equal(true, std::mem_fun(&core::Download::is_downloading)))) + != m->core()->download_list()->end()) { + rak::fs_stat stat; + std::string path = (*itr)->file_list()->root_dir() + (*itr)->file_list()->get(0).path()->as_string(); + + if (!stat.update(path)) { + m->core()->push_log(std::string("Cannot read free diskspace: ") + strerror(errno) + " for " + path); + + } else if (stat.bytes_avail() < arg) { + m->core()->download_list()->close(*itr); + (*itr)->set_message(std::string("Low diskspace")); + } + + ++itr; + } +} + void apply_encoding_list(__UNUSED Control* m, const std::string& arg) { torrent::encoding_list()->push_back(arg); @@ -407,6 +432,7 @@ initialize_option_handler(Control* c) { variables->insert("stop_untied", new utils::VariableVoidSlot(rak::bind_ptr_fn(&apply_stop_untied, c))); variables->insert("close_untied", new utils::VariableVoidSlot(rak::bind_ptr_fn(&apply_close_untied, c))); variables->insert("remove_untied", new utils::VariableVoidSlot(rak::bind_ptr_fn(&apply_remove_untied, c))); + variables->insert("close_low_diskspace", new utils::VariableValueSlot(rak::value_fn(int64_t()), rak::bind_ptr_fn(&apply_close_low_diskspace, c))); variables->insert("enable_trackers", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_enable_trackers, c))); variables->insert("encoding_list", new utils::VariableStringSlot(rak::value_fn(std::string()), rak::bind_ptr_fn(&apply_encoding_list, c))); diff --git a/src/utils/variable_map.cc b/src/utils/variable_map.cc index 28033067..3a1ad75e 100644 --- a/src/utils/variable_map.cc +++ b/src/utils/variable_map.cc @@ -175,7 +175,7 @@ bool VariableMap::process_file(const std::string& path) { std::fstream file(rak::path_expand(path).c_str(), std::ios::in); - if (!file.good()) + if (!file.is_open()) return false; int lineNumber = 0;