From 3c670a7add3ebfdf3bea59a8f677e44f11cb1076 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Wed, 13 May 2009 09:39:00 +0000 Subject: [PATCH] * Add support for any number of custom download values identified by string keys. d.set_custom=key,value d.get_custom=key (returns "" if not set) d.get_custom_throw=key (returns error if not set) * With this patch, rtorrent will detect and complain about .torrent files with broken bencode representation (e.g. where the order of dictionary keys is not lexicographic). * Choose a different poll type using the RTORRENT_POLL env. variable (if it's implemented), probably only useful as RTORRENT_POLL=select. * Add the commands execute_capture and execute_capture_nothrow that work like their other counterparts but return the OUTPUT (stdout) of the given command. Note that the output is not modified in any way, in particular a trailing newline is NOT removed. To work around this, have the command write its output without it e.g. using echo -n. Also note that with execute_capture_nothrow there is no way to check if the command failed. Example usage: d.set_custom4=$execute_capture=handler.sh * When logging peer communication errors, show IP and peer ID to help debugging. All patches above by Josef Drexler. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1088 e378c898-3ddf-0310-93e7-cc216c733640 --- src/command_download.cc | 40 +++++++++++++++++++++++++++++ src/command_local.cc | 2 ++ src/core/manager.cc | 18 ++++++++++++- src/main.cc | 4 +-- src/rpc/exec_file.cc | 56 +++++++++++++++++++++++++++++++++++------ src/rpc/exec_file.h | 4 ++- 6 files changed, 113 insertions(+), 11 deletions(-) diff --git a/src/command_download.cc b/src/command_download.cc index 88e0515c..b9816c42 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -252,6 +252,42 @@ retrieve_d_local_id_html(core::Download* download) { return torrent::Object(rak::copy_escape_html(hashString->begin(), hashString->end())); } +torrent::Object +apply_d_custom(core::Download* download, const torrent::Object& rawArgs) { + torrent::Object::list_const_iterator itr = rawArgs.as_list().begin(); + if (itr == rawArgs.as_list().end()) + throw torrent::bencode_error("Missing key argument."); + + const std::string& key = itr->as_string(); + if (++itr == rawArgs.as_list().end()) + throw torrent::bencode_error("Missing value argument."); + + download->bencode()->get_key("rtorrent"). + insert_preserve_copy("custom", torrent::Object::create_map()).first->second. + insert_key(key, itr->as_string()); + return torrent::Object(); +} + +torrent::Object +retrieve_d_custom(core::Download* download, const std::string& key) { + try { + return download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key); + + } catch (torrent::bencode_error& e) { + return std::string(); + } +} + +torrent::Object +retrieve_d_custom_throw(core::Download* download, const std::string& key) { + try { + return download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key); + + } catch (torrent::bencode_error& e) { + throw torrent::input_error("No such custom value."); + } +} + // Just a helper function atm. torrent::Object cmd_d_initialize_logs(core::Download* download) { @@ -560,6 +596,10 @@ initialize_command_download() { ADD_CD_VARIABLE_STRING_PUBLIC("custom4", "rtorrent", "custom4"); ADD_CD_VARIABLE_STRING_PUBLIC("custom5", "rtorrent", "custom5"); + ADD_CD_SLOT_PUBLIC("d.set_custom", call_list, rak::ptr_fn(&apply_d_custom), "i:", ""); + ADD_CD_SLOT_PUBLIC("d.get_custom", call_string, rpc::object_string_fn(std::ptr_fun(&retrieve_d_custom)), "s:s", ""); + ADD_CD_SLOT_PUBLIC("d.get_custom_throw", call_string, rpc::object_string_fn(std::ptr_fun(&retrieve_d_custom_throw)), "s:s", ""); + // 0 - stopped // 1 - started ADD_CD_VARIABLE_VALUE("state", "rtorrent", "state"); diff --git a/src/command_local.cc b/src/command_local.cc index 828736e5..4e2befd4 100644 --- a/src/command_local.cc +++ b/src/command_local.cc @@ -204,6 +204,8 @@ initialize_command_local() { ADD_COMMAND_LIST("execute_nothrow", rak::bind2_mem_fn(&rpc::execFile, &rpc::ExecFile::execute_object, rpc::ExecFile::flag_expand_tilde)); ADD_COMMAND_LIST("execute_raw", rak::bind2_mem_fn(&rpc::execFile, &rpc::ExecFile::execute_object, rpc::ExecFile::flag_throw)); ADD_COMMAND_LIST("execute_raw_nothrow", rak::bind2_mem_fn(&rpc::execFile, &rpc::ExecFile::execute_object, 0)); + ADD_COMMAND_LIST("execute_capture", rak::bind2_mem_fn(&rpc::execFile, &rpc::ExecFile::execute_object, rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture)); + ADD_COMMAND_LIST("execute_capture_nothrow", rak::bind2_mem_fn(&rpc::execFile, &rpc::ExecFile::execute_object, rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture)); ADD_COMMAND_STRING_UN("execute_log", std::ptr_fun(&apply_execute_log)); diff --git a/src/core/manager.cc b/src/core/manager.cc index e1d7a9f5..41991d28 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -187,7 +187,23 @@ Manager::set_hashing_view(View* v) { void Manager::initialize_first() { - if ((m_pollManager = PollManagerEPoll::create(sysconf(_SC_OPEN_MAX))) != NULL) + const char* poll = getenv("RTORRENT_POLL"); + if (poll != NULL) { + if (!strcmp(poll, "epoll")) + m_pollManager = PollManagerEPoll::create(sysconf(_SC_OPEN_MAX)); + else if (!strcmp(poll, "kqueue")) + m_pollManager = PollManagerKQueue::create(sysconf(_SC_OPEN_MAX)); + else if (!strcmp(poll, "select")) + m_pollManager = PollManagerSelect::create(sysconf(_SC_OPEN_MAX)); + + if (m_pollManager == NULL) + m_logImportant.push_front(std::string("Cannot enable '") + poll + "' based polling."); + } + + if (m_pollManager != NULL) + m_logImportant.push_front(std::string("Using '") + poll + "' based polling."); + + else if ((m_pollManager = PollManagerEPoll::create(sysconf(_SC_OPEN_MAX))) != NULL) m_logImportant.push_front("Using 'epoll' based polling."); else if ((m_pollManager = PollManagerKQueue::create(sysconf(_SC_OPEN_MAX))) != NULL) diff --git a/src/main.cc b/src/main.cc index f6304a51..02951848 100644 --- a/src/main.cc +++ b/src/main.cc @@ -199,8 +199,8 @@ main(int argc, char** argv) { "system.method.set_key = event.download.inserted_new, 1_prepare, \"branch=d.get_state=,view.set_visible=started,view.set_visible=stopped ;d.save_session=\"\n" "system.method.set_key = event.download.inserted_session, 1_prepare, \"branch=d.get_state=,view.set_visible=started,view.set_visible=stopped\"\n" - "system.method.set_key = event.download.erased, 0_download_list, ui.unfocus_download=\n" - "system.method.set_key = event.download.erased, 9_delete_tied, d.delete_tied=\n" + "system.method.set_key = event.download.erased, !_download_list, ui.unfocus_download=\n" + "system.method.set_key = event.download.erased, ~_delete_tied, d.delete_tied=\n" "system.method.insert = ratio.enable, simple|static|const,group.seeding.ratio.enable=\n" "system.method.insert = ratio.disable,simple|static|const,group.seeding.ratio.disable=\n" diff --git a/src/rpc/exec_file.cc b/src/rpc/exec_file.cc index bd5593d2..b2dd8acf 100644 --- a/src/rpc/exec_file.cc +++ b/src/rpc/exec_file.cc @@ -36,6 +36,7 @@ #include "config.h" +#include #include #include #include @@ -51,7 +52,7 @@ namespace rpc { // Close m_logFd. int -ExecFile::execute(const char* file, char* const* argv) { +ExecFile::execute(const char* file, char* const* argv, int flags) { // Write the execued command and its parameters to the log fd. if (m_logFd != -1) { for (char* const* itr = argv; *itr != NULL; itr++) { @@ -66,20 +67,38 @@ ExecFile::execute(const char* file, char* const* argv) { write(m_logFd, "\n---\n", sizeof("\n---\n")); } + int pipeFd[2]; + + if ((flags & flag_capture) && pipe(pipeFd)) + throw torrent::input_error("ExecFile::execute(...) Pipe creation failed."); + pid_t childPid = fork(); if (childPid == -1) throw torrent::input_error("ExecFile::execute(...) Fork failed."); if (childPid == 0) { - ::close(0); - ::close(1); - ::close(2); + int devNull = open("/dev/null", O_RDWR); + if (devNull != -1) + dup2(devNull, 0); + else + ::close(0); - if (m_logFd != -1) { + if (flags & flag_capture) + dup2(pipeFd[1], 1); + else if (m_logFd != -1) dup2(m_logFd, 1); + else if (devNull != -1) + dup2(devNull, 1); + else + ::close(1); + + if (m_logFd != -1) dup2(m_logFd, 2); - } + else if (devNull != -1) + dup2(devNull, 2); + else + ::close(2); // Close all fd's. for (int i = 3, last = sysconf(_SC_OPEN_MAX); i != last; i++) @@ -90,6 +109,26 @@ ExecFile::execute(const char* file, char* const* argv) { _exit(result); } else { + if (flags & flag_capture) { + m_capture = std::string(); + ::close(pipeFd[1]); + + char buffer[4096]; + ssize_t length; + + do { + length = read(pipeFd[0], buffer, sizeof(buffer)); + + if (length > 0) + m_capture += std::string(buffer, length); + } while (length > 0); + + if (m_logFd != -1) { + write(m_logFd, "Captured output:\n", sizeof("Captured output:\n")); + write(m_logFd, m_capture.data(), m_capture.length()); + } + } + int status; int wpid = waitpid(childPid, &status, 0); @@ -143,11 +182,14 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) { *argsCurrent = NULL; - int status = execute(argsBuffer[0], argsBuffer); + int status = execute(argsBuffer[0], argsBuffer, flags); if ((flags & flag_throw) && status != 0) throw torrent::input_error("Bad return code."); + if (flags & flag_capture) + return m_capture; + return torrent::Object((int64_t)status); } diff --git a/src/rpc/exec_file.h b/src/rpc/exec_file.h index d7ccb113..803af318 100644 --- a/src/rpc/exec_file.h +++ b/src/rpc/exec_file.h @@ -48,18 +48,20 @@ public: static const int flag_expand_tilde = 0x1; static const int flag_throw = 0x2; + static const int flag_capture = 0x4; ExecFile() : m_logFd(-1) {} int log_fd() const { return m_logFd; } void set_log_fd(int fd) { m_logFd = fd; } - int execute(const char* file, char* const* argv); + int execute(const char* file, char* const* argv, int flags); torrent::Object execute_object(const torrent::Object& rawArgs, int flags); private: int m_logFd; + std::string m_capture; }; }