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;