* Fix the use of strlcpy.

* Allow lists as arguments in commands by using '{' and '}'. The list
will be recursed and all '$' will be called, while '~' will only be
expanded when in the first element in the list. E.g "execute =
touch,{~/tmp/,$get_client_version=}"

* Added rak::path_expand that works on char buffers.

* Fixed RequestList::calculate_pipe_size so it doesn't request too
many pieces from fast peers.

* Added 'execute_log' for logging the result of calls to 'execute'.


git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@952 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
rakshasa
2007-08-12 13:41:50 +00:00
parent 781e1ca7dc
commit 788b496241
9 changed files with 207 additions and 78 deletions
+30 -1
View File
@@ -58,6 +58,31 @@ path_expand(const std::string& path) {
return home + path.substr(1);
}
// Don't inline this...
//
// Same strlcpy as found in *bsd.
inline size_t
strlcpy(char *dest, const char *src, size_t size) {
size_t n = size;
const char* first = src;
if (n != 0) {
while (--n != 0)
if ((*dest++ = *src++) == '\0')
break;
}
if (n == 0) {
if (size != 0)
*dest = '\0';
while (*src++)
;
}
return src - first - 1;
}
inline char*
path_expand(const char* src, char* first, char* last) {
if (*src == '~') {
@@ -67,10 +92,14 @@ path_expand(const char* src, char* first, char* last) {
return first;
first += strlcpy(first, home, std::distance(first, last));
if (first > last)
return last;
src++;
}
return first + strlcpy(first, src, std::distance(first, last));
return std::min(first + strlcpy(first, src, std::distance(first, last)), last);
}
}
+1 -1
View File
@@ -349,7 +349,7 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
else
if eval xmlrpc-c-config --version 2>/dev/null >/dev/null; then
CXXFLAGS="$CXXFLAGS `xmlrpc-c-config --cflags server-util`"
LIBS="$LIBS `xmlrpc-c-config --libs server-util` -lxmlrpc_server"
LIBS="$LIBS -lxmlrpc_server `xmlrpc-c-config --libs server-util`"
AC_TRY_LINK(
[ #include <xmlrpc-c/server.h>
+27
View File
@@ -36,7 +36,9 @@
#include "config.h"
#include <fcntl.h>
#include <functional>
#include <rak/path.h>
#include <torrent/torrent.h>
#include <torrent/chunk_manager.h>
@@ -53,6 +55,29 @@
typedef torrent::ChunkManager CM_t;
torrent::Object
apply_execute_log(const torrent::Object& rawArgs) {
if (rpc::execFile.log_fd() != -1) {
::close(rpc::execFile.log_fd());
rpc::execFile.set_log_fd(-1);
}
if (rawArgs.is_string() && !rawArgs.as_string().empty()) {
int logFd = open(rak::path_expand(rawArgs.as_string()).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
if (logFd < 0)
throw torrent::input_error("Could not open execute log file.");
rpc::execFile.set_log_fd(logFd);
control->core()->push_log("Opened execute log file.");
} else {
control->core()->push_log("Closed execute log file.");
}
return torrent::Object();
}
void
initialize_command_local() {
torrent::ChunkManager* chunkManager = torrent::chunk_manager();
@@ -87,4 +112,6 @@ 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_STRING_UN("execute_log", std::ptr_fun(&apply_execute_log));
}
+1 -18
View File
@@ -111,24 +111,7 @@ apply_view_list(const torrent::Object&) {
torrent::Object
apply_print(const torrent::Object& rawArgs) {
char buffer[1024];
char* current = buffer;
for (torrent::Object::list_type::const_iterator itr = rawArgs.as_list().begin(), last = rawArgs.as_list().end(); itr != last; itr++) {
switch (itr->type()) {
case torrent::Object::TYPE_STRING:
{
int len = std::min<int>(itr->as_string().size(), buffer + 1024 - current);
std::memcpy(current, itr->as_string().c_str(), len + 1);
current += len;
break;
}
case torrent::Object::TYPE_VALUE:
default:
current += snprintf(current, buffer + 1024 - current, "%lli", itr->as_value());
break;
}
}
rpc::print_object(buffer, buffer + 1024, &rawArgs, 0);
control->core()->push_log(buffer);
return torrent::Object();
+41 -23
View File
@@ -36,25 +36,52 @@
#include "config.h"
#include <string>
#include <unistd.h>
#include <rak/path.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "exec_file.h"
#include "parse.h"
namespace rpc {
// Close m_logFd.
int
ExecFile::execute(const char* file, char* const* argv) {
// Write the execued command and its parameters to the log fd.
if (m_logFd != -1) {
for (char* const* itr = argv; *itr != NULL; itr++) {
if (itr == argv)
write(m_logFd, "\n---\n", sizeof("\n---\n"));
else
write(m_logFd, " ", 1);
write(m_logFd, *itr, std::strlen(*itr));
}
write(m_logFd, "\n---\n", sizeof("\n---\n"));
}
pid_t childPid = fork();
if (childPid == -1)
throw torrent::input_error("ExecFile::execute(...) Fork failed.");
if (childPid == 0) {
::close(0);
::close(1);
::close(2);
if (m_logFd != -1) {
dup2(m_logFd, 1);
dup2(m_logFd, 2);
}
// Close all fd's.
for (int i = 0, last = sysconf(_SC_OPEN_MAX); i != last; i++)
for (int i = 3, last = sysconf(_SC_OPEN_MAX); i != last; i++)
::close(i);
int result = execvp(file, argv);
@@ -68,6 +95,12 @@ ExecFile::execute(const char* file, char* const* argv) {
throw torrent::internal_error("ExecFile::execute(...) waitpid failed.");
// Check return value?
if (m_logFd) {
if (status == 0)
write(m_logFd, "\n--- Success ---\n", sizeof("\n--- Success ---\n"));
else
write(m_logFd, "\n--- Error ---\n", sizeof("\n--- Error ---\n"));
}
return status;
}
@@ -91,35 +124,20 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
if (argsCurrent == argsBuffer + max_args - 1)
throw torrent::input_error("Too many arguments.");
switch (itr->type()) {
case torrent::Object::TYPE_STRING:
{
const std::string& str = itr->as_string();
if (itr->is_string() && (!(flags & flag_expand_tilde) || *itr->as_string().c_str() != '~')) {
*argsCurrent = const_cast<char*>(itr->as_string().c_str());
if ((flags & flag_expand_tilde) && *str.c_str() == '~') {
*argsCurrent = valueCurrent;
valueCurrent = rak::path_expand(str.c_str(), valueCurrent, valueBuffer + buffer_size) + 1;
} else {
*argsCurrent = const_cast<char*>(str.c_str());
}
break;
}
case torrent::Object::TYPE_VALUE:
} else {
*argsCurrent = valueCurrent;
valueCurrent = print_object(valueCurrent, valueBuffer + buffer_size, &*itr, flags) + 1;
valueCurrent += snprintf(valueCurrent, valueBuffer + buffer_size - valueCurrent, "%lli", itr->as_value()) + 1;
break;
default:
throw torrent::input_error("Invalid type.");
}
if (valueCurrent >= valueBuffer + buffer_size)
throw torrent::input_error("Overflowed execute arg buffer.");
}
}
*argsCurrent = NULL;
// Check if we overflowed the valueBuffer.
int status = execute(argsBuffer[0], argsBuffer);
if ((flags & flag_throw) && status != 0)
+8 -3
View File
@@ -46,15 +46,20 @@ public:
static const unsigned int max_args = 128;
static const unsigned int buffer_size = 4096;
static const int flag_throw = 0x1;
static const int flag_expand_tilde = 0x2;
static const int flag_expand_tilde = 0x1;
static const int flag_throw = 0x2;
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);
torrent::Object execute_object(const torrent::Object& rawArgs, int flags);
private:
int m_logFd;
};
}
+71 -14
View File
@@ -37,6 +37,7 @@
#include "config.h"
#include <locale>
#include <rak/path.h>
#include <torrent/exceptions.h>
#include "parse.h"
@@ -60,7 +61,7 @@ parse_skip_wspace(const char* first) {
}
const char*
parse_string(const char* first, const char* last, std::string* dest) {
parse_string(const char* first, const char* last, std::string* dest, bool (*delim)(const char)) {
if (first == last)
return first;
@@ -75,7 +76,7 @@ parse_string(const char* first, const char* last, std::string* dest) {
return ++first;
} else {
if (parse_is_seperator(*first) || std::isspace(*first))
if (delim(*first))
return first;
}
@@ -165,19 +166,39 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
return last;
}
// Somewhat ugly...
const char*
parse_list(const char* first, const char* last, torrent::Object* dest) {
parse_object(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
if (*first == '{') {
*dest = torrent::Object(torrent::Object::TYPE_LIST);
first = parse_list(first + 1, last, dest, &parse_is_delim_list);
first = parse_skip_wspace(first, last);
if (first == last || *first != '}')
throw torrent::input_error("Could not find closing '}'.");
return ++first;
} else {
*dest = std::string();
return parse_string(first, last, &dest->as_string(), delim);
}
}
const char*
parse_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
if (!dest->is_list())
throw torrent::internal_error("parse_list(...) !dest->is_list().");
while (true) {
std::string str;
torrent::Object tmp;
first = parse_skip_wspace(first, last);
first = parse_string(first, last, &str);
first = parse_object(first, last, &tmp, delim);
first = parse_skip_wspace(first, last);
dest->as_list().push_back(str);
dest->as_list().push_back(tmp);
if (first == last || !parse_is_seperator(*first))
break;
@@ -190,20 +211,16 @@ parse_list(const char* first, const char* last, torrent::Object* dest) {
const char*
parse_whole_list(const char* first, const char* last, torrent::Object* dest) {
std::string str;
first = parse_skip_wspace(first, last);
first = parse_string(first, last, &str);
first = parse_object(first, last, dest);
first = parse_skip_wspace(first, last);
if (first != last && parse_is_seperator(*first)) {
*dest = torrent::Object(torrent::Object::TYPE_LIST);
torrent::Object tmp = torrent::Object(torrent::Object::TYPE_LIST);
tmp.swap(*dest);
dest->as_list().push_back(str);
dest->as_list().push_back(tmp);
first = parse_list(++first, last, dest);
} else {
*dest = str;
}
return first;
@@ -332,4 +349,44 @@ convert_to_value_nothrow(const torrent::Object& src, int64_t* value, int base, i
return true;
}
char*
print_object(char* first, char* last, const torrent::Object* src, int flags) {
switch (src->type()) {
case torrent::Object::TYPE_STRING:
{
const std::string& str = src->as_string();
if ((flags & print_expand_tilde) && *str.c_str() == '~') {
return rak::path_expand(str.c_str(), first, last);
} else {
if (first == last)
return first;
size_t n = std::min<size_t>(str.size(), std::distance(first, last) - 1);
std::memcpy(first, str.c_str(), n);
*(first += n) = '\0';
return first;
}
}
case torrent::Object::TYPE_VALUE:
return std::max(first + snprintf(first, std::distance(first, last), "%lli", src->as_value()), last);
case torrent::Object::TYPE_LIST:
for (torrent::Object::list_type::const_iterator itr = src->as_list().begin(), itrEnd = src->as_list().end(); itr != itrEnd; itr++) {
first = print_object(first, last, &*itr, flags);
// Don't expand tilde after the first element in the list.
flags &= ~print_expand_tilde;
}
return first;
default:
throw torrent::input_error("Invalid type.");
}
}
}
+10 -2
View File
@@ -54,10 +54,13 @@ inline bool parse_is_escape(const char c) { return c == '\\'; }
inline bool parse_is_seperator(const char c) { return c == ','; }
inline bool parse_is_space(const char c) { return c == ' ' || c == '\t'; }
inline bool parse_is_delim_default(const char c) { return parse_is_seperator(c) || std::isspace(c); }
inline bool parse_is_delim_list(const char c) { return parse_is_seperator(c) || c == '}' || std::isspace(c); }
const char* parse_skip_wspace(const char* first);
const char* parse_skip_wspace(const char* first, const char* last);
const char* parse_string(const char* first, const char* last, std::string* dest);
const char* parse_string(const char* first, const char* last, std::string* dest, bool (*delim)(const char) = &parse_is_delim_default);
void parse_whole_string(const char* first, const char* last, std::string* dest);
const char* parse_value(const char* src, int64_t* value, int base = 0, int unit = 1);
@@ -66,7 +69,8 @@ const char* parse_value_nothrow(const char* src, int64_t* value, int base = 0, i
void parse_whole_value(const char* src, int64_t* value, int base = 0, int unit = 1);
bool parse_whole_value_nothrow(const char* src, int64_t* value, int base = 0, int unit = 1);
const char* parse_list(const char* first, const char* last, torrent::Object* dest);
const char* parse_object(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
const char* parse_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
const char* parse_whole_list(const char* first, const char* last, torrent::Object* dest);
std::string convert_list_to_string(const torrent::Object& src);
@@ -84,6 +88,10 @@ convert_to_single_argument(const torrent::Object& args) {
return args;
}
static const int print_expand_tilde = 0x1;
char* print_object(char* first, char* last, const torrent::Object* src, int flags);
}
#endif
+18 -16
View File
@@ -77,6 +77,23 @@ parse_count_escaped(const char* first, const char* last) {
return escaped;
}
// Replace any strings starting with '$' with the result of the
// result of the command.
//
// Find a better name.
void
parse_command_execute(core::Download* download, torrent::Object* object) {
if (object->is_list()) {
for (torrent::Object::list_type::iterator itr = object->as_list().begin(), last = object->as_list().end(); itr != last; itr++)
parse_command_execute(download, &*itr);
} else if (*object->as_string().c_str() == '$') {
const std::string& str = object->as_string();
*object = parse_command_d_single(download, str.c_str() + 1, str.c_str() + str.size());
}
}
// Set 'download' to NULL to call the generic functions, thus reusing
// the code below for both cases.
std::pair<torrent::Object, const char*>
@@ -110,22 +127,7 @@ parse_command(core::Download* download, const char* first, const char* last) {
// Replace any strings starting with '$' with the result of the
// following command.
if (args.is_list()) {
for (torrent::Object::list_type::iterator itr = args.as_list().begin(), last = args.as_list().end(); itr != last; itr++) {
if (!itr->is_string())
continue;
const std::string& str = itr->as_string();
if (*str.c_str() == '$')
*itr = parse_command_d_single(download, str.c_str() + 1, str.c_str() + str.size());
}
} else if (*args.as_string().c_str() == '$') {
const std::string& str = args.as_string();
args = parse_command_d_single(download, str.c_str() + 1, str.c_str() + str.size());
}
parse_command_execute(download, &args);
return std::make_pair(commands.call_command_d(key.c_str(), download, args), first);
}