# 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. # Note also that with execute_capture_nothrow there is no way to check if the # command failed. # Example usage: d.set_custom4=$execute_capture=handler.sh Index: rtorrent/src/rpc/exec_file.cc =================================================================== --- rtorrent/src/rpc/exec_file.cc (revision 1087) +++ rtorrent/src/rpc/exec_file.cc (working copy) @@ -36,6 +36,7 @@ #include "config.h" +#include #include #include #include @@ -51,7 +52,7 @@ // 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 @@ 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 @@ _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 @@ *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); } Index: rtorrent/src/rpc/exec_file.h =================================================================== --- rtorrent/src/rpc/exec_file.h (revision 1087) +++ rtorrent/src/rpc/exec_file.h (working copy) @@ -48,18 +48,20 @@ 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; }; } Index: rtorrent/src/command_local.cc =================================================================== --- rtorrent/src/command_local.cc (revision 1087) +++ rtorrent/src/command_local.cc (working copy) @@ -204,6 +204,8 @@ 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));