# # patch "ChangeLog" # from [1922f0f586ad5eee0e79c1955441b9df3d0f6ded] # to [adc66ae3d08a555a7c42f431e3c4d56e602176c0] # # patch "contrib/usher.cc" # from [c67d693036174bc4a591c02202c65e6b8521fc59] # to [454acd6ae3513b0f2925177c0a72f5a14e99fc30] # # patch "netcmd.cc" # from [38e6b3a85549ed7c7e5f109623d9ea1b802fc340] # to [7b2a66cff2f716be765f9536dab1734e8fc1b0b4] # # patch "netcmd.hh" # from [87007edfa58287e177df2f3246b28eaa5c5f6146] # to [104f746aaddbc57fbf388486c6247c8c48eb6cf8] # # patch "netsync.cc" # from [3b956db0420ef07f1020e8faa8d3f872abb740d9] # to [750ebec30f4cb560e17fcb14adbc601fdd73224f] # ======================================================================== --- ChangeLog 1922f0f586ad5eee0e79c1955441b9df3d0f6ded +++ ChangeLog adc66ae3d08a555a7c42f431e3c4d56e602176c0 @@ -1,5 +1,13 @@ 2005-10-18 Timothy Brownawell + * netsync.cc netcmd.{cc,hh}: usher_reply_cmd now has both who we + connected to *and* what pattern we asked for + * contrib/usher.cc: new file format, allow to key servers on either + hostname or pattern, hostname checked first + reload config file on SIGHUP + +2005-10-18 Timothy Brownawell + * netsync.cc (session::process_usher_cmd): reply with who we connected to (hostname or hostname:port), not what pattern we asked for. * contrib/usher.cc: Update comment. ======================================================================== --- contrib/usher.cc c67d693036174bc4a591c02202c65e6b8521fc59 +++ contrib/usher.cc 454acd6ae3513b0f2925177c0a72f5a14e99fc30 @@ -14,14 +14,27 @@ // // is the address to listen on // is the local port to listen on -// is a file containing lines of -// hostname remote ip-address port-number -// hostname local +// is a file that looks like +// server monotone +// host localhost +// pattern net.venge.monotone +// remote 66.96.28.3:5253 +// +// server local +// host 127.0.0.1 +// pattern * +// local -d /usr/local/src/managed/mt.db~ * // -// Example server-file: -// localhost local -d /usr/local/src/project.db * -// venge.net remote 66.96.28.3 5253 -// company.com:5200 remote 192.168.4.5 5200 +// or in general, blocks of a +// server +// line followed by one or more +// host +// lines and/or one or more +// pattern +// lines, and one of +// remote +// local +// , with blocks separated by blank lines // // A request to server "hostname" will be directed to the // server at :, if that stem is marked as remote, @@ -53,16 +66,21 @@ #include #include #include +#include #include +#include using std::vector; using std::max; using std::string; using std::list; using std::set; +using std::map; using boost::lexical_cast; +using boost::shared_ptr; using std::cerr; +using std::make_pair; // defaults, overridden by command line int listenport = 5253; @@ -107,8 +125,12 @@ // byte version // byte cmd {100 if we send, 101 if we receive} // uleb128 {size of everything after this} -// uleb128 {size of everything after this} +// uleb128 {size of string} // string +// { +// uleb128 {size of string} +// string +// } // only present if we're receiving // uleb128 is // byte 0x80 | @@ -260,7 +282,6 @@ static void close_all_socks() { for (set::iterator i = all_socks.begin(); i != all_socks.end(); ++i) { -// shutdown((*i)[0], SHUT_RDWR); while (::close((*i)[0]) < 0) if (errno != EINTR) break; @@ -435,6 +456,12 @@ struct server { + list >::iterator> by_host, by_pat; + map >::iterator by_name; + static map > servers_by_host; + static map > servers_by_pattern; + static map > servers_by_name; + static set > live_servers; bool local; int pid; string arguments; @@ -450,6 +477,64 @@ { yeskill(); } + void delist() + { + vector foo; + set_hosts(foo); + set_patterns(foo); + servers_by_name.erase(by_name); + by_name = 0; + } + void rename(string const & n) + { + shared_ptr me = by_name->second; + servers_by_name.erase(by_name); + by_name = servers_by_name.insert(make_pair(n, me)).first; + } + void set_hosts(vector const & h) + { + shared_ptr me = by_name->second; + map >::iterator c; + for (list >::iterator>::iterator + i = by_host.begin(); i != by_host.end(); ++i) + servers_by_host.erase(*i); + by_host.clear(); + for (vector::const_iterator i = h.begin(); i != h.end(); ++i) { + c = servers_by_host.find(*i); + if (c != servers_by_host.end()) { + list >::iterator>::iterator j; + for (j = c->second->by_host.begin(); j != c->second->by_host.end(); ++j) + if ((*j)->first == *i) { + servers_by_host.erase(*j); + c->second->by_host.erase(j); + } + } + c = servers_by_host.insert(make_pair(*i, me)).first; + by_host.push_back(c); + } + } + void set_patterns(vector const & p) + { + shared_ptr me = by_name->second; + map >::iterator c; + for (list >::iterator>::iterator + i = by_pat.begin(); i != by_pat.end(); ++i) + servers_by_pattern.erase(*i); + by_pat.clear(); + for (vector::const_iterator i = p.begin(); i != p.end(); ++i) { + c = servers_by_pattern.find(*i); + if (c != servers_by_pattern.end()) { + list >::iterator>::iterator j; + for (j = c->second->by_pat.begin(); j != c->second->by_pat.end(); ++j) + if ((*j)->first == *i) { + servers_by_pattern.erase(*j); + c->second->by_pat.erase(j); + } + } + c = servers_by_pattern.insert(make_pair(*i, me)).first; + by_pat.push_back(c); + } + } sock connect() { if (local && pid == -1) { @@ -485,6 +570,8 @@ } void maybekill() { + if (!local) + return; int difftime = time(0) - last_conn_time; if (difftime > server_idle_timeout && !connection_count) yeskill(); @@ -512,61 +599,173 @@ } }; -struct record +map > server::servers_by_host; +map > server::servers_by_pattern; +map > server::servers_by_name; +set > server::live_servers; + +string getline(std::istream & in) { - std::string stem; - server srv; -}; + string out; + char buf[256]; + do { + in.getline(buf, 256); + int got = in.gcount()-1; + if (got > 0) + out.append(buf, got); + } while (in.fail() && !in.eof()); + return out; +} -list servers; +string read_server_record(std::istream & in) +{ + // server foobar + // hostname foobar.com + // hostname mtn.foobar.com + // pattern com.foobar + // remote 127.5.6.7:80 + // + // server myproj + // hostname localhost + // local -d foo.db * + vector hosts, patterns; + string name, desc; + bool local(false); + string line = getline(in); + while (!line.empty()) { + // server foobar + // ^ ^ ^ + // a b c + int a = line.find_first_not_of(" \t"); + int b = line.find_first_of(" \t", a); + int c = line.find_first_not_of(" \t", b); + string cmd = line.substr(a, b-a); + string arg = line.substr(c); + if (cmd == "server") + name = arg; + else if (cmd == "local") { + local = true; + desc = arg; + } else if (cmd == "remote") { + local = false; + desc = arg; + } else if (cmd == "host") + hosts.push_back(arg); + else if (cmd == "pattern") + patterns.push_back(arg); + line = getline(in); + } + if (name.empty()) + return string(); + shared_ptr srv(new server); + map >::iterator + i = server::servers_by_name.find(name); + if (i != server::servers_by_name.end()) + i->second->delist(); + srv->by_name = server::servers_by_name.insert(make_pair(name, srv)).first; + srv->set_hosts(hosts); + srv->set_patterns(patterns); + if (local) { + srv->local = true; + srv->arguments = desc; + } else { + srv->local = false; + int c = desc.find(":"); + if (c != desc.npos) { + srv->addr = desc.substr(0, c); + srv->port = lexical_cast(desc.substr(c+1)); + } else { + srv->addr = desc; + srv->port = 5253; + } + } + return name; +} -server * get_server(std::string const & stem) +shared_ptr get_server(string const & srv, string const & pat) { - std::list::iterator i; - for (i = servers.begin(); i != servers.end(); ++i) { - if (stem.find(i->stem) == 0) - break; + map >::iterator i; + for (i = server::servers_by_host.begin(); + i != server::servers_by_host.end(); ++i) { + if (srv.find(i->first) == 0) { + return i->second; + } } - if (i == servers.end()) { - std::cerr<<"no server found for "<first) == 0) { + return i->second; + } } - cerr<<"found server "<stem<<" for "<srv; + std::cerr<<"no server found for '"<(); } void kill_old_servers() { - std::list::iterator i; - for (i = servers.begin(); i != servers.end(); ++i) { - i->srv.maybekill(); + set >::iterator i; + for (i = server::live_servers.begin(); i != server::live_servers.end(); ++i) { + (*i)->maybekill(); } } -bool extract_reply(buffer & buf, std::string & out) +int extract_uleb128(char *p, int maxsize, int & out) { + out = 0; + int b = 0; + unsigned char got; + do { + if (b == maxsize) + return -1; + got = p[b]; + out += ((int)(p[b] & 0x7f))<<(b*7); + ++b; + } while (b*7 < sizeof(int)*8-1 && (got & 0x80)); + return b; +} + +int extract_vstr(char *p, int maxsize, string & out) +{ + int size; + out.clear(); + int chars = extract_uleb128(p, maxsize, size); + if (chars == -1 || chars + size > maxsize) { + return -1; + } + out.append(p+chars, size); + return chars+size; +} + +bool extract_reply(buffer & buf, string & host, string & pat) +{ char *p; - int n; + int n, s; buf.getread(p, n); if (n < 4) return false; - int b = 2; - unsigned int psize = p[b]; - ++b; - if (psize >=128) { - psize = psize - 128 + ((unsigned int)(p[b])<<7); - ++b; + p += 2; // first 2 bytes are header + n -= 2; + // extract size, and make sure we have the entire packet + int pos = extract_uleb128(p, n, s); + if (pos == -1 || n < s+pos) { + return false; } - if (n < b+psize) return false; - unsigned int size = p[b]; - ++b; - if (size >=128) { - size = size - 128 + ((unsigned int)(p[b])<<7); - ++b; + // extract host vstr + int num = extract_vstr(p+pos, n-pos, host); + if (num == -1) { + return false; } - if (n < b+size) return false; - out.clear(); - out.append(p+b, size); - buf.fixread(b + size); + pos += num; + // extract pattern vstr + num = extract_vstr(p+pos, n-pos, pat); + if (num == -1) { + cerr<<"old-style reply.\n"; + pat = host; + host.clear(); + return true; + } + pos += num; + buf.fixread(pos+2); + return true; } struct channel @@ -579,10 +778,10 @@ bool no_server; buffer cbuf; buffer sbuf; - server * who; + shared_ptr who; channel(sock & c): num(++counter), cli(c), srv(-1), - have_routed(false), no_server(false), who(0) + have_routed(false), no_server(false) { char * dat; int size; @@ -656,9 +855,9 @@ if (c > 0 && FD_ISSET(c, &rd)) { if (!cli.read_to(cbuf)) c = -1; if (!have_routed) { - std::string reply; - if (extract_reply(cbuf, reply)) { - who = get_server(reply); + string reply_srv, reply_pat; + if (extract_reply(cbuf, reply_srv, reply_pat)) { + who = get_server(reply_srv, reply_pat); if (who) { try { srv = who->connect(); @@ -704,10 +903,48 @@ }; int channel::counter = 0; +bool reload_pending; + +void reload_conffile(string const & file) +{ + reload_pending = false; + cerr<<"Reloading config file...\n"; + std::ifstream cf(file.c_str()); + set names; + int pos = 0; + while(cf) { + string n = read_server_record(cf); + if(!n.empty()) + names.insert(n); + } + for (map >::iterator + i = server::servers_by_name.begin(); + i != server::servers_by_name.end(); ++i) { + if (names.find(i->first) == names.end()) + i->second->delist(); + } + cerr<<"Servers:\n"; + for (map >::iterator + i = server::servers_by_name.begin(); + i != server::servers_by_name.end(); ++i) { + cerr<<"\t"<first<<"\n"; + list >::iterator>::iterator j; + for (j = i->second->by_host.begin(); j != i->second->by_host.end(); ++j) + cerr<<"\t\tHost: '"<<(*j)->first<<"'\n"; + for (j = i->second->by_pat.begin(); j != i->second->by_pat.end(); ++j) + cerr<<"\t\tPattern: '"<<(*j)->first<<"'\n"; + } + cerr<<"Reload complete.\n"; +} +void sched_reload(int sig) +{ + reload_pending = true; +} + int main (int argc, char **argv) { + string conffile; { - char const * conffile = 0; int i; for (i = 1; i < argc; ++i) { if (string(argv[i]) == "-a") @@ -717,41 +954,24 @@ else conffile = argv[i]; } - if (conffile == 0 || i != argc) { + if (conffile.empty() || i != argc) { cerr<<"Usage:\n"; cerr<<"\tusher [-a ] [-p ] \n"; exit (1); } - std::ifstream cf(conffile); - int pos = 0; - while(cf) { - string stem, type; - cf>>stem; - cf>>type; - if (!cf) - break; - record rec; - rec.stem = stem; - if (type == "local") { - rec.srv.local = true; - rec.srv.arguments.clear(); - while(cf.peek() != '\n') - rec.srv.arguments += cf.get(); - } else if (type == "remote") { - rec.srv.local = false; - cf>>rec.srv.addr; - cf>>rec.srv.port; - } else { - cerr<<"Error parsing "<