# # # patch "src/administrator.cc" # from [72123cb2f5117a814feeef5b81ecd2042486fd9b] # to [1b15e642e907e823949d4f29e68bee03c3872d78] # # patch "src/administrator.hh" # from [9939b5ded272f98a384f1d4fe2df0c151d43c0a7] # to [80100a85bb6fad55b37272f583cc423592c7d9ef] # # patch "src/buffer.cc" # from [0b2c8452c5928af7218866309c67780c228c7825] # to [4240b3b4df67428d5bbb899c32d3de083561b64a] # # patch "src/buffer.hh" # from [253447ed2dde50e7acc17e9f9151abf1716fadba] # to [c0ade86b5284cde081ff807ab770442601ceddf0] # # patch "test/run-tests.sh" # from [5999d6d9d8ef5fa21c06d9aa0979eef3c18c1958] # to [1eb46a921473826f1797551748752e69fa63b039] # ============================================================ --- src/administrator.cc 72123cb2f5117a814feeef5b81ecd2042486fd9b +++ src/administrator.cc 1b15e642e907e823949d4f29e68bee03c3872d78 @@ -47,8 +47,10 @@ namespace defaults map > defaults = get_defaults(); } -administrator::cstate::cstate() - : auth(false), rdone(false) +administrator::connection::connection(sock & s) + : authenticated(false), + read_done(false), + response_done(false), the_sock(s) { } @@ -59,16 +61,17 @@ bool } bool -administrator::process(cstate & cs) +administrator::process(connection & cs) { - string::size_type n = cs.buf.find("\n"); - if (n == cs.buf.npos) + string l; + if (!cs.inbound.get_string(l)) return true; - string l = cs.buf.substr(0, n); - cs.buf.erase(0, n+1); + if (cs.authenticated) + cs.read_done = true; std::istringstream iss(l); string cmd; iss>>cmd; + bool set_response = true; if (cmd == "USERPASS") { string user, pass; iss>>user>>pass; @@ -77,13 +80,13 @@ administrator::process(cstate & cs) cerr<<"Failed admin login.\n"; return false; } else { - if (cs.auth == true) + if (cs.authenticated == true) return false; - cs.auth = true; + cs.authenticated = true; return process(cs); } - } else if (!cs.auth) { - cs.buf = "You must log in first.\n"; + } else if (!cs.authenticated) { + cs.outbound.put_string("You must log in first.\n"); } else if (cmd == "MATCH") { string host; iss>>host; @@ -91,9 +94,9 @@ administrator::process(cstate & cs) iss>>pattern; try { string const &name = manager.lookup_server_name(host, pattern); - cs.buf = "OK: " + name + "\n"; + cs.outbound.put_string("OK: " + name + "\n"); } catch (std::exception & e) { - cs.buf = string("ERROR: ") + e.what() + "\n"; + cs.outbound.put_string(string("ERROR: ") + e.what() + "\n"); } } else if (cmd == "STATUS") { string srv; @@ -124,7 +127,7 @@ administrator::process(cstate & cs) oss<>srv; @@ -161,7 +164,7 @@ administrator::process(cstate & cs) if (oss.str().empty()) oss<<"none"; oss<<"\n"; - cs.buf = oss.str(); + cs.outbound.put_string(oss.str()); } else if (cmd == "START") { string srv; iss>>srv; @@ -174,7 +177,7 @@ administrator::process(cstate & cs) { oss<>srv; @@ -187,46 +190,48 @@ administrator::process(cstate & cs) { oss<>srv; try { manager.kill_server_now(srv); - cs.buf = "ok\n"; + cs.outbound.put_string("ok\n"); } catch (std::exception &e) { - cs.buf = e.what(); - cs.buf += "\n"; + cs.outbound.put_string(e.what() + string("\n")); } } else if (cmd == "LIST") { string state; iss>>state; set servers = manager.list_servers(state); + bool first = true; for (set::iterator i = servers.begin(); i != servers.end(); ++i) { - cs.buf += (cs.buf.empty()?"":" ") + *i; + cs.outbound.put_string((first?"":" ") + *i); + first = false; } - cs.buf += "\n"; + cs.outbound.put_string("\n"); } else if (cmd == "SHUTDOWN") { manager.allow_connections(false); manager.kill_old_servers(); - cs.buf = "ok\n"; + cs.outbound.put_string("ok\n"); } else if (cmd == "CONNECTIONS") { - cs.buf = lexical_cast(manager.get_total_connections()) + "\n"; + cs.outbound.put_string(lexical_cast(manager.get_total_connections()) + "\n"); } else if (cmd == "RELOAD") { reload_conffile(); - cs.buf = "ok\n"; + cs.outbound.put_string("ok\n"); } else if (cmd == "STARTUP") { manager.allow_connections(true); - cs.buf = "ok\n"; + cs.outbound.put_string("ok\n"); } else { - cs.buf = "unknown command\n"; + cs.outbound.put_string("unknown command\n"); } - cs.rdone = true; + if (set_response) + cs.response_done = true; return true; } @@ -270,14 +275,20 @@ administrator::add_to_select(int & maxfd return; FD_SET (port, &rd); maxfd = max(maxfd, int(port)); - for (list >::iterator i = conns.begin(); + for (list::iterator i = conns.begin(); i != conns.end(); ++i) { - int c = i->second; - if (!i->first.rdone) + int c = i->the_sock; + bool added; + if (!i->read_done && i->inbound.canwrite()) { FD_SET(c, &rd); - else + added = true; + } + if (i->outbound.canread()) { FD_SET(c, &wr); - maxfd = max(maxfd, int(c)); + added = true; + } + if (added) + maxfd = max(maxfd, int(c)); } } @@ -286,51 +297,45 @@ administrator::process_selected(fd_set & { if (int(port) == -1) return; + if (FD_ISSET(port, &rd)) { try { sock nc = port.accept(); - conns.push_back(make_pair(cstate(), nc)); + conns.push_back(connection(nc)); } catch(std::exception & e) { cerr<<"During new admin connection: "< >::iterator> del; - for (list >::iterator i = conns.begin(); + + list::iterator> del; + for (list::iterator i = conns.begin(); i != conns.end(); ++i) { - int c = i->second; - if (c <= 0) { -// cerr<<"Bad socket.\n"; + sock & connsock = i->the_sock; + if (connsock <= 0) { del.push_back(i); - } else if (FD_ISSET(c, &rd)) { - char buf[120]; - int n; - n = read(c, buf, 120); - if (n < 1) { -// cerr<<"Read failed.\n"; + continue; + } + + if (FD_ISSET(connsock, &rd)) { + if (!connsock.read_to(i->inbound)) { + del.push_back(i); + } else if (!process(*i)) { del.push_back(i); } - i->first.buf.append(buf, n); - if (!process(i->first)) { -// cerr<<"Closing connection...\n"; -// i->second.close(); - del.push_back(i); - } } - else if (FD_ISSET(c, &wr)) { - int n = write(c, i->first.buf.c_str(), i->first.buf.size()); - if (n < 1) { -// cerr<<"Write failed.\n"; - del.push_back(i); - } else { - i->first.buf.erase(0, n); - if (i->first.buf.empty() && i->first.rdone) { -// cerr<<"Done.\n"; - del.push_back(i); - } + + if (FD_ISSET(connsock, &wr)) { + if (!connsock.write_from(i->outbound)) { + del.push_back(i); + continue; } + if (!i->outbound.canwrite() && i->response_done) { + del.push_back(i); + } } } - for (list >::iterator>::iterator i = del.begin(); + + for (list::iterator>::iterator i = del.begin(); i != del.end(); ++i) { conns.erase(*i); } ============================================================ --- src/administrator.hh 9939b5ded272f98a384f1d4fe2df0c151d43c0a7 +++ src/administrator.hh 80100a85bb6fad55b37272f583cc423592c7d9ef @@ -10,8 +10,9 @@ #ifndef __ADMINISTRATOR_HH_ #define __ADMINISTRATOR_HH_ -#include "sock.hh" +#include "buffer.hh" #include "server_manager.hh" +#include "sock.hh" #include using std::list; @@ -28,20 +29,23 @@ struct administrator { sock port; sock serverport; - struct cstate + struct connection { - bool auth; - bool rdone; - string buf; - cstate(); + bool authenticated; + bool read_done; + bool response_done; + buffer outbound; + buffer inbound; + sock the_sock; + connection(sock & s); }; - list > conns; + list conns; map admins; map > scripts; server_manager &manager; string conffile; administrator(server_manager &sm, string const &cf); - bool process(cstate & cs); + bool process(connection & cs); void initialize(string const & ap, string const & sp); void add_to_select(int & maxfd, fd_set & rd, fd_set & wr, fd_set & er); void process_selected(fd_set & rd, fd_set & wr, fd_set & er); ============================================================ --- src/buffer.cc 0b2c8452c5928af7218866309c67780c228c7825 +++ src/buffer.cc 4240b3b4df67428d5bbb899c32d3de083561b64a @@ -80,3 +80,35 @@ buffer::fixwrite(int n) throw std::logic_error("negative write"); writepos += n; } + +bool +buffer::put_string(std::string const & s) +{ + int const len = s.size(); + char *pos; + int avail; + getwrite(pos, avail); + if (avail < len) + return false; + char const * dat = s.c_str(); + memcpy(pos, dat, len); + fixwrite(len); + return true; +} + +bool +buffer::get_string(std::string & s, char delim) +{ + char * pos; + int avail; + getread(pos, avail); + char * end = pos + avail; + for (char * i = pos; i < end; ++i) { + if (*i == delim) { + s.assign(pos, i); + fixread(i - pos + 1); + return true; + } + } + return false; +} ============================================================ --- src/buffer.hh 253447ed2dde50e7acc17e9f9151abf1716fadba +++ src/buffer.hh c0ade86b5284cde081ff807ab770442601ceddf0 @@ -10,6 +10,9 @@ #ifndef __BUFFER_HH_ #define __BUFFER_HH_ +// why is there no ? +#include + struct buffer { static int const buf_size = 1024 * 64; @@ -26,6 +29,11 @@ struct buffer void getwrite(char *& p, int & n); void fixread(int n); void fixwrite(int n); + + // return false and do nothing if there's not enough space + bool put_string(std::string const & s); + // return false and do nothing if delim isn't found + bool get_string(std::string & s, char delim = '\n'); }; #endif ============================================================ --- test/run-tests.sh 5999d6d9d8ef5fa21c06d9aa0979eef3c18c1958 +++ test/run-tests.sh 1eb46a921473826f1797551748752e69fa63b039 @@ -29,8 +29,11 @@ msg_usher() { mtn="mtn --confdir=$TESTDIR/confdir --ticker=none" msg_usher() { + MSG_USHER_SLEEP=$(expr $RANDOM % 2); # see usher.conf.head for address - { echo "USERPASS user pass"; echo "$@"; } | socat tcp:127.0.0.1:23345 stdio + { echo "USERPASS user pass"; + sleep $MSG_USHER_SLEEP; + echo "$@"; } | socat tcp:127.0.0.1:23345 stdio } serve() { @@ -55,7 +58,7 @@ check_match() { if ! [ "$wanted_server" = "-" ]; then wanted_server="OK: $wanted_server" fi - local got_server=$(msg_usher MATCH $host $pattern) + local got_server=$(msg_usher MATCH "$host" "$pattern") if [ ! -n "$got_server" -o "${got_server:0:5}" = "ERROR" ]; then got_server=- fi