From 68502367ec01862e8a3f828f1a89e9a71a9081cf Mon Sep 17 00:00:00 2001 From: rakshasa Date: Sat, 4 Feb 2006 19:18:41 +0000 Subject: [PATCH] * Don't show ETA on finished and stopped torrents. * Added percentage completed to active unfinished torrents. * Re-enabled different seeding and leeching priorities. * Make a temporary container in AvailableList::insert(AddressList*) when doing std::set_differernce. AvailableList's std::vector would resize and cause invalidated iterators to be used. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@630 e378c898-3ddf-0310-93e7-cc216c733640 --- doc/rtorrent.1.xml | 75 ++++++++++++++++++++++++--------- src/control.cc | 3 +- src/core/curl_get.cc | 4 +- src/core/download.cc | 17 +++++--- src/core/download_factory.cc | 1 + src/core/download_list.cc | 24 ++++++++--- src/core/download_list.h | 36 ++++++++-------- src/core/download_store.cc | 2 +- src/core/manager.cc | 5 --- src/display/utils.cc | 26 +++++++++--- src/display/utils.h | 1 + src/display/window_peer_info.cc | 5 ++- src/main.cc | 16 ------- src/option_handler_rules.cc | 3 +- src/ui/download_list.cc | 2 +- src/utils/lockfile.cc | 42 ++++++++++++++++-- src/utils/lockfile.h | 13 +++++- 17 files changed, 184 insertions(+), 91 deletions(-) diff --git a/doc/rtorrent.1.xml b/doc/rtorrent.1.xml index ec1b8c5a..a84201e1 100644 --- a/doc/rtorrent.1.xml +++ b/doc/rtorrent.1.xml @@ -139,6 +139,21 @@ + + ^O + + Change the destination directory of the download. The + torrent must be closed. + + + + + + | - + + Change the priority of the download. + + + backspace @@ -155,6 +170,13 @@ + + ^P + + Call commands or change settings. + + + @@ -424,6 +446,11 @@ seconds, starting from start. An start of zero calls it immediately. Currently command is forwarded to the option handler. +start and interval may +optionally use a time format, dd:hh:mm:ss. F.ex to +start a task every day at 18:00, use +18:00:00. + @@ -455,9 +482,8 @@ to the filename provided. remove_untied = -Stop or remove the torrents that are tied to filenames that has been -deleted, the association is then cleared. Don't use -remove_untied as it crashes the client. +Stop or remove the torrents that are tied to filenames that have been +deleted, the association is then cleared. @@ -530,23 +556,34 @@ deleted, the association is then cleared. Don't use - - - - - - - - - + + working_directory = directory + - - - - - - - +Changes the working directory of the process using +chdir. + + + + + + session_on_completion = yes + + +Controls whetever the session torrent is updated when a torrent +finishes. By default on. + + + + + + session_lock = yes + + +Controls whetever a lock file is created in the session directory. + + + diff --git a/src/control.cc b/src/control.cc index ba209955..df98ec34 100644 --- a/src/control.cc +++ b/src/control.cc @@ -53,6 +53,7 @@ Control::Control() : m_shutdownReceived(false), + m_shutdownQuick(false), m_ui(new ui::Root()), m_core(new core::Manager()), @@ -92,8 +93,6 @@ Control::initialize() { display::Window::slot_unschedule(rak::make_mem_fun(m_display, &display::Manager::unschedule)); display::Window::slot_adjust(rak::make_mem_fun(m_display, &display::Manager::adjust_layout)); -// m_core->get_poll_manager()->signal_interrupted().connect(sigc::mem_fun(*m_inputStdin, &input::InputEvent::event_read)); -// m_core->get_poll_manager()->signal_interrupted().connect(sigc::ptr_fun(display::Canvas::do_update)); m_core->get_poll_manager()->get_http_stack()->set_user_agent(std::string(PACKAGE "/" VERSION "/") + torrent::version()); m_core->initialize_second(); diff --git a/src/core/curl_get.cc b/src/core/curl_get.cc index d3c9ea67..e4539b70 100644 --- a/src/core/curl_get.cc +++ b/src/core/curl_get.cc @@ -152,9 +152,7 @@ CurlGet::perform(CURLMsg* msg) { else m_signalFailed.emit(curl_easy_strerror(msg->data.result)); - if (m_handle != NULL) - throw std::logic_error("CurlGet::perform finished but the object wasn't closed."); - + // Do nothing below after emitting the signals. } } diff --git a/src/core/download.cc b/src/core/download.cc index 31d8983d..bbb53ad9 100644 --- a/src/core/download.cc +++ b/src/core/download.cc @@ -97,14 +97,14 @@ Download::~Download() { void Download::start() { - if (is_done()) { + if (is_done()) m_download.set_connection_type(string_to_connection_type(m_variables.get("connection_seed").as_string())); - torrent::download_set_priority(m_download, 2); - } else { + else m_download.set_connection_type(string_to_connection_type(m_variables.get("connection_leech").as_string())); - torrent::download_set_priority(m_download, 4); - } + // Update the priority to ensure it has the correct + // seeding/unfinished modifiers. + set_priority(priority()); m_download.start(); } @@ -128,7 +128,12 @@ Download::set_priority(uint32_t p) { if (p >= 4) throw torrent::input_error("Priority out of range."); - torrent::download_set_priority(m_download, p * p); + // Seeding torrents get half the priority of unfinished torrents. + if (!is_done()) + torrent::download_set_priority(m_download, p * p * 2); + else + torrent::download_set_priority(m_download, p * p); + get_bencode().get_key("rtorrent").insert_key("priority", (int64_t)p); } diff --git a/src/core/download_factory.cc b/src/core/download_factory.cc index 360ceeba..b0455bf2 100644 --- a/src/core/download_factory.cc +++ b/src/core/download_factory.cc @@ -49,6 +49,7 @@ #include "globals.h" #include "manager.h" +#include "download.h" #include "download_factory.h" namespace core { diff --git a/src/core/download_list.cc b/src/core/download_list.cc index d211e327..f61161d4 100644 --- a/src/core/download_list.cc +++ b/src/core/download_list.cc @@ -47,6 +47,16 @@ namespace core { +struct download_list_call { + download_list_call(Download* d) : m_download(d) {} + + void operator () (const DownloadList::SlotMap::value_type& s) { + s.second(m_download); + } + + Download* m_download; +}; + DownloadList::iterator DownloadList::insert(std::istream* str) { torrent::Download d = torrent::download_add(str); @@ -54,14 +64,14 @@ DownloadList::insert(std::istream* str) { iterator itr = Base::insert(end(), new Download(d)); (*itr)->get_download().signal_download_done(sigc::bind(sigc::mem_fun(*this, &DownloadList::finished), *itr)); - m_slotMapInsert.for_each(*itr); + std::for_each(m_slotMapInsert.begin(), m_slotMapInsert.end(), download_list_call(*itr)); return itr; } DownloadList::iterator DownloadList::erase(iterator itr) { - m_slotMapErase.for_each(*itr); + std::for_each(m_slotMapErase.begin(), m_slotMapErase.end(), download_list_call(*itr)); torrent::download_remove((*itr)->get_download()); delete *itr; @@ -74,7 +84,7 @@ DownloadList::open(Download* d) { if (d->get_download().is_open()) return; - m_slotMapOpen.for_each(d); + std::for_each(m_slotMapOpen.begin(), m_slotMapOpen.end(), download_list_call(d)); } void @@ -83,7 +93,7 @@ DownloadList::close(Download* d) { return; stop(d); - m_slotMapClose.for_each(d); + std::for_each(m_slotMapClose.begin(), m_slotMapClose.end(), download_list_call(d)); } void @@ -93,7 +103,7 @@ DownloadList::start(Download* d) { return; open(d); - m_slotMapStart.for_each(d); + std::for_each(m_slotMapStart.begin(), m_slotMapStart.end(), download_list_call(d)); } void @@ -101,7 +111,7 @@ DownloadList::stop(Download* d) { if (!d->get_download().is_active()) return; - m_slotMapStop.for_each(d); + std::for_each(m_slotMapStop.begin(), m_slotMapStop.end(), download_list_call(d)); } void @@ -113,7 +123,7 @@ DownloadList::clear() { void DownloadList::finished(Download* d) { - m_slotMapFinished.for_each(d); + std::for_each(m_slotMapFinished.begin(), m_slotMapFinished.end(), download_list_call(d)); } } diff --git a/src/core/download_list.h b/src/core/download_list.h index 12637679..431a0460 100644 --- a/src/core/download_list.h +++ b/src/core/download_list.h @@ -39,8 +39,9 @@ #include #include - -#include "download_slot_map.h" +#include +#include +#include namespace core { @@ -52,7 +53,8 @@ class Download; class DownloadList : private std::list { public: - typedef std::list Base; + typedef std::list Base; + typedef std::map > SlotMap; using Base::iterator; using Base::const_iterator; @@ -80,28 +82,28 @@ public: void start(Download* d); void stop(Download* d); - DownloadSlotMap& slot_map_insert() { return m_slotMapInsert; } - DownloadSlotMap& slot_map_erase() { return m_slotMapErase; } - DownloadSlotMap& slot_map_open() { return m_slotMapOpen; } - DownloadSlotMap& slot_map_close() { return m_slotMapClose; } - DownloadSlotMap& slot_map_start() { return m_slotMapStart; } - DownloadSlotMap& slot_map_stop() { return m_slotMapStop; } + SlotMap& slot_map_insert() { return m_slotMapInsert; } + SlotMap& slot_map_erase() { return m_slotMapErase; } + SlotMap& slot_map_open() { return m_slotMapOpen; } + SlotMap& slot_map_close() { return m_slotMapClose; } + SlotMap& slot_map_start() { return m_slotMapStart; } + SlotMap& slot_map_stop() { return m_slotMapStop; } - DownloadSlotMap& slot_map_finished() { return m_slotMapFinished; } + SlotMap& slot_map_finished() { return m_slotMapFinished; } private: void clear(); void finished(Download* d); - DownloadSlotMap m_slotMapInsert; - DownloadSlotMap m_slotMapErase; - DownloadSlotMap m_slotMapOpen; - DownloadSlotMap m_slotMapClose; - DownloadSlotMap m_slotMapStart; - DownloadSlotMap m_slotMapStop; + SlotMap m_slotMapInsert; + SlotMap m_slotMapErase; + SlotMap m_slotMapOpen; + SlotMap m_slotMapClose; + SlotMap m_slotMapStart; + SlotMap m_slotMapStop; - DownloadSlotMap m_slotMapFinished; + SlotMap m_slotMapFinished; }; } diff --git a/src/core/download_store.cc b/src/core/download_store.cc index 29a8fb5b..0e3ed0d7 100644 --- a/src/core/download_store.cc +++ b/src/core/download_store.cc @@ -65,7 +65,7 @@ DownloadStore::enable(bool lock) { m_lockfile.set_path(std::string()); if (!m_lockfile.try_lock()) - throw torrent::input_error("Could not lock session directory: \"" + m_path + "\"."); + throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", held by \"" + m_lockfile.locked_by() + "\"."); } void diff --git a/src/core/manager.cc b/src/core/manager.cc index dfa30a61..9fb6d14c 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -67,11 +67,6 @@ connect_signal_network_log(Download* d, torrent::Download::SlotString s) { d->get_download().signal_network_log(s); } -// static void -// connect_signal_tracker_log(Download* d, torrent::Download::SlotString s) { -// d->get_download().signal_tracker_failed(s); -// } - static void connect_signal_storage_log(Download* d, torrent::Download::SlotString s) { d->get_download().signal_storage_error(s); diff --git a/src/display/utils.cc b/src/display/utils.cc index 7453f18d..db0b276c 100644 --- a/src/display/utils.cc +++ b/src/display/utils.cc @@ -164,12 +164,20 @@ print_download_info(char* first, char* last, core::Download* d) { (double)d->get_download().bytes_done() / (double)(1 << 20), (double)d->get_download().bytes_total() / (double)(1 << 20)); - first = print_buffer(first, last, " Rate: %5.1f / %5.1f KB Uploaded: %7.1f MB ", + first = print_buffer(first, last, " Rate: %5.1f / %5.1f KB Uploaded: %7.1f MB", (double)d->get_download().up_rate()->rate() / (1 << 10), (double)d->get_download().down_rate()->rate() / (1 << 10), (double)d->get_download().up_rate()->total() / (1 << 20)); - first = print_download_time_left(first, last, d); + if (d->get_download().is_active() && !d->is_done()) { + first = print_buffer(first, last, " "); + first = print_download_percentage_done(first, last, d); + + first = print_buffer(first, last, " "); + first = print_download_time_left(first, last, d); + } else { + first = print_buffer(first, last, " "); + } if (d->priority() != 2) first = print_buffer(first, last, " [%s]", core::Download::priority_to_string(d->priority())); @@ -210,10 +218,9 @@ print_download_status(char* first, char* last, core::Download* d) { char* print_download_time_left(char* first, char* last, core::Download* d) { - uint32_t rate; + uint32_t rate = d->get_download().down_rate()->rate(); - if (!d->get_download().is_active() || - (rate = d->get_download().down_rate()->rate()) < 512) + if (rate < 512) return print_buffer(first, last, "--:--:--"); time_t remaining = (d->get_download().bytes_total() - d->get_download().bytes_done()) / (rate & ~(uint32_t)(512 - 1)); @@ -221,6 +228,15 @@ print_download_time_left(char* first, char* last, core::Download* d) { return print_ddhhmm(first, last, remaining); } +char* +print_download_percentage_done(char* first, char* last, core::Download* d) { + if (!d->is_open() || d->is_done()) + //return print_buffer(first, last, "[--%%]"); + return print_buffer(first, last, " "); + else + return print_buffer(first, last, "[%2u%%]", (d->get_download().chunks_done() * 100) / d->get_download().chunks_total()); +} + char* print_status_info(char* first, char* last) { if (torrent::up_throttle() == 0) diff --git a/src/display/utils.h b/src/display/utils.h index 37f555af..b25c1d39 100644 --- a/src/display/utils.h +++ b/src/display/utils.h @@ -66,6 +66,7 @@ char* print_download_title(char* first, char* last, core::Download* d); char* print_download_info(char* first, char* last, core::Download* d); char* print_download_status(char* first, char* last, core::Download* d); char* print_download_time_left(char* first, char* last, core::Download* d); +char* print_download_percentage_done(char* first, char* last, core::Download* d); char* print_entry_tags(char* first, char* last); char* print_entry_file(char* first, char* last, const torrent::Entry& entry); diff --git a/src/display/window_peer_info.cc b/src/display/window_peer_info.cc index fceea724..a08ae093 100644 --- a/src/display/window_peer_info.cc +++ b/src/display/window_peer_info.cc @@ -83,9 +83,10 @@ WindowPeerInfo::redraw() { m_download->variables()->get("connection_current").as_string().c_str(), m_download->variables()->get("connection_seed").as_string().c_str(), m_download->variables()->get("connection_leech").as_string().c_str()); + m_canvas->print(0, y++, "Priority: %u", torrent::download_priority(m_download->get_download())); - m_canvas->print(0, y++, "Directory: %s", m_download->variable_string("directory").c_str()); - m_canvas->print(0, y++, "Tied to file: %s", m_download->variable_string("tied_to_file").c_str()); + m_canvas->print(0, y++, "Directory: %s", m_download->variable_string("directory").c_str()); + m_canvas->print(0, y++, "Tied to file: %s", m_download->variable_string("tied_to_file").c_str()); y++; diff --git a/src/main.cc b/src/main.cc index ee52ddfc..92e49b9e 100644 --- a/src/main.cc +++ b/src/main.cc @@ -249,22 +249,6 @@ do_panic(int signum) { exit(-1); } -void -receive_tracker_dump(std::istream* s) { - std::stringstream filename; - filename << "./tracker_dump." << rak::timer::current().seconds(); - - std::fstream out(filename.str().c_str(), std::ios::out | std::ios::trunc); - - if (!out.is_open()) - return; - - s->seekg(0); - - std::copy(std::istream_iterator(*s), std::istream_iterator(), - std::ostream_iterator(out)); -} - void print_help() { std::cout << "Rakshasa's BitTorrent client version " VERSION "." << std::endl; diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index c4caaf4b..e0abf598 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -47,6 +47,7 @@ #include #include +#include "core/download.h" #include "core/manager.h" #include "ui/root.h" #include "utils/directory.h" @@ -159,7 +160,7 @@ initialize_option_handler(Control* c) { variables->insert("use_udp_trackers", new utils::VariableAny("yes")); variables->insert("port_random", new utils::VariableAny("yes")); variables->insert("session", new utils::VariableSlotString<>(NULL, rak::mem_fn(&control->core()->download_store(), &core::DownloadStore::set_path))); - variables->insert("session_lock", new utils::VariableAny("yes")); + variables->insert("session_lock", new utils::VariableAny("no")); variables->insert("session_on_completion", new utils::VariableAny("yes")); variables->insert("connection_leech", new utils::VariableAny("leech")); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 25335f29..7d2b7b08 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -124,7 +124,7 @@ DownloadList::activate() { m_control->input()->push_front(m_bindings); - m_control->core()->download_list().slot_map_erase().insert("0_download_list", sigc::mem_fun(this, &DownloadList::receive_download_erased)); + m_control->core()->download_list().slot_map_erase()["0_download_list"] = sigc::mem_fun(this, &DownloadList::receive_download_erased); activate_display(DISPLAY_DOWNLOAD_LIST); } diff --git a/src/utils/lockfile.cc b/src/utils/lockfile.cc index e93590fd..42428b71 100644 --- a/src/utils/lockfile.cc +++ b/src/utils/lockfile.cc @@ -36,9 +36,12 @@ #include "config.h" +#include +#include +#include +#include #include #include -#include #include "lockfile.h" @@ -47,29 +50,60 @@ namespace utils { bool Lockfile::try_lock() { if (m_path.empty()) { - m_id = "foo"; + m_locked = true; return true; } // Just do a simple locking for now that isn't safe for network // devices. - int fd = ::open(m_path.c_str(), O_RDWR | O_CREAT | O_EXCL); + int fd = ::open(m_path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0444); if (fd == -1) return false; - m_id = "foo"; + char buf[256]; + int pos = ::gethostname(buf, 255); + + if (pos == 0) { + ::snprintf(buf + std::strlen(buf), 255, ":+%i\n", ::getpid()); + ::write(fd, buf, std::strlen(buf)); + } + ::close(fd); + m_locked = true; return true; } bool Lockfile::unlock() { + m_locked = false; + if (m_path.empty()) return true; else return ::unlink(m_path.c_str()) != -1; } +struct lockfile_valid_char { + bool operator () (char c) { + return !std::isgraph(c); + } +}; + +std::string +Lockfile::locked_by() const { + int fd = ::open(m_path.c_str(), O_RDONLY); + + if (fd < 0) + return ""; + + char buf[256]; + int pos = read(fd, buf, 255); + + ::close(fd); + + return std::string(buf, std::find_if(buf, buf + std::max(pos, 0), lockfile_valid_char())); +} + } diff --git a/src/utils/lockfile.h b/src/utils/lockfile.h index 83e4e5ce..d0218ff5 100644 --- a/src/utils/lockfile.h +++ b/src/utils/lockfile.h @@ -34,6 +34,12 @@ // Skomakerveien 33 // 3185 Skoppum, NORWAY +// A simple, and not guaranteed atomic, lockfile implementation. It +// saves the hostname and pid in the lock file, which may be accessed +// by Lockfile::locked_by(). If the path is an empty string then no +// lockfile will be created when Lockfile::try_lock() is called, still +// it will set the locked state of the Lockfile instance. + #ifndef RTORRENT_UTILS_LOCKFILE_H #define RTORRENT_UTILS_LOCKFILE_H @@ -43,8 +49,9 @@ namespace utils { class Lockfile { public: + Lockfile() : m_locked(false) {} - bool is_locked() const { return !m_id.empty(); } + bool is_locked() const { return m_locked; } // If the path is empty no lock file will be created, although // is_locked() will return true. @@ -54,9 +61,11 @@ public: const std::string& path() const { return m_path; } void set_path(const std::string& path) { m_path = path; } + std::string locked_by() const; + private: std::string m_path; - std::string m_id; + bool m_locked; }; }