# # # patch "doc/documentation.html" # from [aa098f958289d699d949361a2fed998ae33a1b70] # to [f528926a99202aff93837351b22cb1a5bafe90b5] # # patch "src/administrator.cc" # from [ce537034af9af4b4985bdf5b52c44b3a22714317] # to [362b1109b89aa9d16be22abced7eefaba1b58daa] # # patch "src/server_manager.cc" # from [862a39f860372d2de7d695319406bbccf18c1f1f] # to [344abfa7ee91434fe5c70dae6fb0ada63e1416f1] # # patch "src/server_manager.hh" # from [c02ae903a25dd2ccc52cd6a0cb641469806996f6] # to [fc8ad29b28f82cae3b26955995afdad8f1276a5a] # # patch "test/run-tests.sh" # from [507fb6602261afd32a84594028a6fba8b6ca2e0f] # to [09b15b7cc7258cd670214c487462e5a1103f31f9] # # patch "test/test1/script.txt" # from [18da33dc8b0860cda8241385b62e5604c3300b1c] # to [ad04e6ce794f64fd89e01d15aa56a32c038dc0e7] # ============================================================ --- src/administrator.cc ce537034af9af4b4985bdf5b52c44b3a22714317 +++ src/administrator.cc 362b1109b89aa9d16be22abced7eefaba1b58daa @@ -68,6 +68,80 @@ administrator::administrator(server_mana reload_conffile(); } +namespace { + // parse quoted options + vector parse_cmd_line(string const & cmd_line) + { + vector args; + + bool have_arg = false; + string arg; + enum quote_type { q_none, q_single, q_double }; + quote_type quote = q_none; + for (string::const_iterator i = cmd_line.begin(); i != cmd_line.end(); ++i) { + switch(quote) { + case q_none: + if (std::isspace(*i)) { + if (have_arg) { + args.push_back(arg); + arg.clear(); + } + have_arg = false; + } else { + have_arg = true; + switch (*i) { + case '"': + quote = q_double; + break; + case '\'': + quote = q_single; + break; + case '\\': + ++i; + if (i != cmd_line.end()) { + if (*i == 'n') + arg += '\n'; + else + arg += *i; + } + break; + default: + arg += *i; + } + } + break; + case q_single: + if (*i == '\'') + quote = q_none; + else { + arg += *i; + } + break; + case q_double: + if (*i == '"') + quote = q_none; + else if (*i == '\\') { + ++i; + if (i != cmd_line.end()) { + if (*i == 'n') + arg += '\n'; + else + arg += *i; + } + } else { + arg += *i; + } + break; + } + } + if (have_arg) { + args.push_back(arg); + } + + return args; + } +} + bool administrator::process(connection & cs) { @@ -235,6 +309,46 @@ administrator::process(connection & cs) } else if (cmd == "STARTUP") { manager.allow_connections(true); cs.outbound.put_string("ok\n"); + } else if (cmd == "ADD_SERVER") { + string name, type; + iss >> name >> type; + if (type == "local") { + string local_args; + std::getline(iss, local_args); + vector const arguments = parse_cmd_line(local_args); + if (arguments.empty()) + cs.outbound.put_string("you need at least one argument for a local server\n"); + else { + bool ok = manager.create_local_server(name, arguments); + if (ok) + cs.outbound.put_string("ok\n"); + else + cs.outbound.put_string("failed to create server\n"); + } + } else if (type == "remote") { + string addr; + int port; + iss >> addr >> port; + if (!iss) + cs.outbound.put_string("could not read address or port\n"); + else { + bool ok = manager.create_remote_server(name, addr, port); + if (ok) + cs.outbound.put_string("ok\n"); + else + cs.outbound.put_string("failed to create server\n"); + } + } else { + cs.outbound.put_string("invalid server type\n"); + } + } else if (cmd == "REMOVE_SERVER") { + string name; + iss >> name; + if (manager.remove_server(name)) { + cs.outbound.put_string("ok\n"); + } else { + cs.outbound.put_string("not found\n"); + } } else if (cmd == "SCRIPT") { do { string name; @@ -266,79 +380,19 @@ administrator::process(connection & cs) close(0); vector args = script_iter->second; + string cmd_line; std::getline(iss, cmd_line); - { - bool have_arg = false; - string arg; - enum quote_type { q_none, q_single, q_double }; - quote_type quote = q_none; - for (string::iterator i = cmd_line.begin(); i != cmd_line.end(); ++i) { - switch(quote) { - case q_none: - if (std::isspace(*i)) { - if (have_arg) { - args.push_back(arg); - arg.clear(); - } - have_arg = false; - } else { - have_arg = true; - switch (*i) { - case '"': - quote = q_double; - break; - case '\'': - quote = q_single; - break; - case '\\': - ++i; - if (i != cmd_line.end()) { - if (*i == 'n') - arg += '\n'; - else - arg += *i; - } - break; - default: - arg += *i; - } - } - break; - case q_single: - if (*i == '\'') - quote = q_none; - else { - arg += *i; - } - break; - case q_double: - if (*i == '"') - quote = q_none; - else if (*i == '\\') { - ++i; - if (i != cmd_line.end()) { - if (*i == 'n') - arg += '\n'; - else - arg += *i; - } - } else { - arg += *i; - } - break; - } - } - if (have_arg) { - args.push_back(arg); - } - } + vector extra_args = parse_cmd_line(cmd_line); + std::copy(extra_args.begin(), extra_args.end(), back_inserter(args)); + char ** argv = new char*[args.size() + 1]; for (unsigned int i = 0; i < args.size(); ++i) { argv[i] = new char[args[i].size()+1]; memcpy(argv[i], args[i].c_str(), args[i].size()+1); } argv[args.size()] = 0; + execvp(argv[0], argv); perror("execvp failed"); exit(126); ============================================================ --- src/server_manager.cc 862a39f860372d2de7d695319406bbccf18c1f1f +++ src/server_manager.cc 344abfa7ee91434fe5c70dae6fb0ada63e1416f1 @@ -97,6 +97,40 @@ server_manager::delist_server(shared_ptr } } +bool +server_manager::create_remote_server(string const & name, + string const & addr, int port) +{ + if (by_name.find(name) != by_name.end()) + return false; + shared_ptr srv(new server(*this)); + srv->local = false; + srv->addr = addr; + srv->port = port; + srv->me = srv; + serverdata sd; + sd.name = name; + by_name.insert(make_pair(name, srv)); + servers.insert(make_pair(srv, sd)); + return true; +} +bool +server_manager::create_local_server(string const & name, + vector const & args) +{ + if (by_name.find(name) != by_name.end()) + return false; + shared_ptr srv(new server(*this)); + srv->local = true; + srv->arguments = args; + srv->me = srv; + serverdata sd; + sd.name = name; + by_name.insert(make_pair(name, srv)); + servers.insert(make_pair(srv, sd)); + return true; +} + void server_manager::reload_servers() { @@ -156,12 +190,19 @@ server_manager::reload_servers() for (set::iterator j = all.begin(); j != all.end(); ++j) { - map >::iterator i = by_name.find(*j); - if (i != by_name.end()) - delist_server(i->second); + remove_server(*j); } } +bool server_manager::remove_server(string const & name) +{ + map >::iterator i = by_name.find(name); + if (i == by_name.end()) + return false; + delist_server(i->second); + return true; +} + namespace { shared_ptr find_by_prefix(server_manager::prefix_map const & servers, ============================================================ --- src/server_manager.hh c02ae903a25dd2ccc52cd6a0cb641469806996f6 +++ src/server_manager.hh fc8ad29b28f82cae3b26955995afdad8f1276a5a @@ -71,7 +71,11 @@ public: void allow_connections(bool allow=true); string start_stop_server(string const &srv, bool start); void kill_server_now(string const &srv); + void reload_servers(); + bool remove_server(string const & name); + bool create_remote_server(string const & name, string const & addr, int port); + bool create_local_server(string const & name, vector const & args); private: shared_ptr find_server(string peername, string const &pattern); ============================================================ --- test/run-tests.sh 507fb6602261afd32a84594028a6fba8b6ca2e0f +++ test/run-tests.sh 09b15b7cc7258cd670214c487462e5a1103f31f9 @@ -45,11 +45,14 @@ client() { client() { local what=$1 local database=$2 - local pattern="$3" + local uri="$3" + uri="$(echo $uri | sed 's/HOST/127.0.0.1:8691/')" [ -e $database.mtn ] || $mtn db init -d $database.mtn # see usher.conf.head for address - $mtn --root=. -d $database.mtn $what 127.0.0.1:8691 $pattern & - CLIENTS="$CLIENTS $!" + $mtn --root=. -d $database.mtn $what "$uri" & + local mypid=$! + CLIENTS="$CLIENTS $mypid" + eval CLIENT_${mypid}='"$uri"' } sync() { @@ -81,6 +84,11 @@ check_match() { fi } +check_cmd() { + local ret="$(msg_usher "$@")" + test "$ret" = "ok" +} + script() { local name="$1" local args="$2" @@ -159,7 +167,7 @@ for test_name in $(ls $SRCDIR/test/); do for c in $CLIENTS; do echo "Waiting for $c..." if ! wait $c; then - echo "Client died horribly." + echo "Client died horribly: " "$(eval echo '$'CLIENT_$c)" OK=false fi done ============================================================ --- test/test1/script.txt 18da33dc8b0860cda8241385b62e5604c3300b1c +++ test/test1/script.txt ad04e6ce794f64fd89e01d15aa56a32c038dc0e7 @@ -1,10 +1,10 @@ serve example 127.0.0.1:25436 -multipull 3 net.prjek.separate -multipull 3 net.prjek.{fnord,foobar} -multipull 3 org.example +multipull 3 mtn://HOST/prjek-s?net.prjek.separate +multipull 3 'mtn://HOST/prjek?net.prjek.{fnord,foobar}' +multipull 3 mtn://HOST/example?org.example -sync user1 net.prjek'*' +sync user1 mtn://HOST/prjek?net.prjek'*' # check_match @@ -39,3 +39,15 @@ # script script fooscript 'a "b c" d' 'foo\nxyzzy x z a b c d\nbar\n\n0 exited\n' + + +# add / remove server + +check_cmd REMOVE_SERVER prjek-s +check_match - net.prjek.separate prjek +check_match mtn://HOST/prjek-s - - + +check_cmd ADD_SERVER someserver remote 127.0.0.1 25436 +sync user1 mtn://HOST/someserver?'*' +check_cmd ADD_SERVER otherserver local "-d" "user1.mtn" "--confdir=../confdir" +multipull 2 mtn://HOST/otherserver?'*' ============================================================ --- doc/documentation.html aa098f958289d699d949361a2fed998ae33a1b70 +++ doc/documentation.html f528926a99202aff93837351b22cb1a5bafe90b5 @@ -79,14 +79,20 @@ will be given before any arguments given

Per-server directives

server "name"
-
Define a new server with the given name. Clients that are version 0.48 or later can connect to a specific server with mtn://host.com/servername .
+
Define a new server with the given name. Clients that are version 0.48 or later can connect to a specific server with mtn://host.com/servername . If you only expect clients 0.48 or newer, this is the preferred dispatch mechanism.
host "hostname-prefix"
-
This server will match connections made to a hostname with the given prefix.
+
This server will match +connections made to a hostname with the given prefix. If you expect +clients older than 0.48 and control  your DNS, this is the +preferred dispatch mechanism.
+
pattern "pattern-prefix"
-
This server will match connections where the user's include -pattern has the given prefix. If you control your DNS, it is better to -use host "hostname-prefix" instead.
+
This server will match +connections where the user's include +pattern has the given prefix. If you don't control your DNS, this is +the only dispatch mechanism that you can use with clients older than +0.48.
remote "address:port"
Specifies a remote server to forward connections to.
@@ -220,8 +226,22 @@ inside single quotes. You cannot use lit Arguments with spaces can be quoted with single or double quotes, and the "\n", "\'", "\"", and "\\" escape sequences are recognized except inside single quotes. You cannot use literal newlines even inside -quotes, but must use "\n" if you want a newline character.
+quotes, but must use "\n" if you want a newline character. Returns the +output (combined stdout and stderr) of the script, followed by a +newline, the exit status and a comment, and another newline.
+
REMOVE_SERVER servername
+
Remove the named server, as if by removing it from the config +file and giving the "RELOAD" command. Returns "ok", or "not found" if +the named server doesn't exist.
+
+
ADD_SERVER name { remote | local } args
+
+
Add a server with the given name and startup arguments (or name and address for remote servers). +"args" is either "hostname port" for remote servers, or "arg 'arg' ..." +for local servers. Quotes are treated the same as for RUN. Returns +"ok", or an error message.
+