monotone

monotone Mtn Source Tree

Root/contrib/usher.cc

1// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
2// Timothy Brownawell <tbrownaw@gmail.com>
3// GPL v2
4//
5// This is an "usher" to allow multiple monotone servers to work from
6// the same port. It asks the client what it wants to sync,
7// and then looks up the matching server in a table. It then forwards
8// the connection to that server. All servers using the same usher need
9// to have the same server key.
10//
11// This requires cooperation from the client, which means it only works
12// for recent (0.23 or later) clients. In order to match against hostnames
13// a post-0.23 client is needed (0.23 clients can only be matched against
14// their include pattern).
15//
16// Usage: usher [-l address[:port]] [-a address:port] [-p pidfile] <server-file>
17//
18// options:
19// -m the monotone command, defaults to "mtn"
20// -l address and port to listen on, defaults to 0.0.0.0:4691
21// -a address and port to listen for admin commands
22// -p a file (deleted on program exit) to record the pid of the usher in
23// <server-file> a file that looks like
24// userpass username password
25// logdir directory
26//
27// server monotone
28// host localhost
29// pattern net.venge.monotone
30// remote 66.96.28.3:4691
31//
32// server local
33// host 127.0.0.1
34// pattern *
35// local -d /usr/local/src/managed/mt.db~ *
36//
37// or in general, one block of one or more lines of
38// userpass <username> <password>
39// logdir <directory>
40// followed by any number of blocks of a
41// server <name>
42// line followed by one or more
43// host <hostname>
44// lines and/or one or more
45// pattern <pattern>
46// lines, and one of
47// remote <address:port>
48// local <arguments>
49// , with blocks separated by blank lines
50//
51// "userpass" lines specify who is allowed to use the administrative port.
52//
53// A request to server "hostname" will be directed to the
54// server at <ip-address>:<port-number>, if that stem is marked as remote,
55// and to a local server managed by the usher, started with the given
56// arguments ("mtn serve --bind=something <server arguments>"), if it is
57// marked as local.
58// Note that "hostname" has to be an initial substring of who the client asked
59// to connect to, but does not have to match exactly. This means that you don't
60// have to know in advance whether clients will be asking for
61// <host> or <host>:<port> .
62//
63//
64// Admin commands
65//
66// If the -a option is given, the usher will listen for administrative
67// connections on that port. The connecting client gives commands of the form
68// COMMAND [arguments] <newline>
69// , and after any command except USERPASS the usher will send a reply and
70// close the connection. The reply will always end with a newline.
71//
72// USERPASS username password
73// Required before any other command, so random people can't do bad things.
74// If incorrect, the connection will be closed immediately.
75//
76// STATUS [servername]
77// Get the status of a server, as named by the "server" lines in the
78// config file. If a server is specified, the result will be one of:
79// REMOTE - this is a remote server without active connections
80// ACTIVE n - this server currently has n active connections
81// WAITING - this (local) server is running, but has no connections
82// SLEEPING - this (local) server is not running, but is available
83// STOPPING n - this (local) server has been asked to stop, but still has
84// n active connections. It will not accept further connections.
85// STOPPED - this (local) server has been stopped, and will not accept
86// connections. The server process is not running.
87// SHUTTINGDOWN - the usher has been shut down, no servers are accepting
88// connections.
89// SHUTDOWN - the usher has been shut down, all connections have been closed,
90// and all local server processes have been stopped.
91// If no server is specified, the repsonse will be SHUTTINGDOWN, SHUTDOWN,
92// WAITING, or ACTIVE (with n being the total number of open connections,
93// across all servers).
94//
95// STOP servername
96// Prevent the given local server from receiving further connections, and stop
97// it once all connections are closed. The result will be the new status of
98// that server: ACTIVE local servers become STOPPING, and WAITING and SLEEPING
99// servers become STOPPED. Servers in other states are not affected.
100//
101// START servername
102// Allow a stopped or stopping server to receive connections again. The result
103// will be the new status of that server. (A server in the "STOPPING" state
104// becomes ACTIVE, and a STOPPED server becomes SLEEPING. A server in some
105// other state is not affected.)
106//
107// LIST [state]
108// Returns a space-separated list of all servers. If a state is given, only
109// list the servers that are in that state.
110//
111// SHUTDOWN
112// Do not accept new connections for any servers, local or remote. Returns "ok".
113//
114// STARTUP
115// Begin accepting connections again after a SHUTDOWN. Returns "ok".
116//
117// CONNECTIONS
118// Returns the number of connections currently open.
119//
120// RELOAD
121// Reload the config file (same as sending SIGHUP). The reply will be "ok",
122// and will not be given until the config file has been reloaded.
123//
124
125#define NDEBUG
126#define BOOST_DISABLE_THREADS
127#define BOOST_MULTI_INDEX_DISABLE_SERIALIZATION
128
129#include <stdlib.h>
130#include <stdio.h>
131#include <unistd.h>
132#include <sys/time.h>
133#include <sys/types.h>
134#include <sys/stat.h>
135#include <fcntl.h>
136#include <sys/wait.h>
137#include <string.h>
138#include <signal.h>
139#include <sys/socket.h>
140#include <netinet/in.h>
141#include <arpa/inet.h>
142#include <errno.h>
143#include <time.h>
144#include <locale.h>
145
146#include <string>
147#include <list>
148#include <iostream>
149#include <sstream>
150#include <fstream>
151#include <vector>
152#include <set>
153#include <map>
154
155#include <boost/lexical_cast.hpp>
156#include <boost/shared_ptr.hpp>
157
158using std::vector;
159using std::max;
160using std::string;
161using std::list;
162using std::set;
163using std::map;
164using boost::lexical_cast;
165using boost::shared_ptr;
166using std::cerr;
167using std::pair;
168using std::make_pair;
169
170// defaults, overridden by command line
171int listenport = 4691;
172string listenaddr = "0.0.0.0";
173string monotone = "mtn";
174string logdir = ".";
175
176// keep local servers around for this many seconds after the last
177// client disconnects from them (only accurate to ~10 seconds)
178int const server_idle_timeout = 60;
179
180// ranges that dynamic (local) servers can be put on
181int const minport = 15000;
182int const maxport = 65000;
183int const minaddr[] = {127, 0, 1, 1};
184int const maxaddr[] = {127, 254, 254, 254};
185int currport = 0;
186int curraddr[] = {0, 0, 0, 0};
187
188char const netsync_version = 6;
189
190string const greeting = " Hello! This is the monotone usher at localhost. What would you like?";
191
192string const notfound = "!Sorry, I don't know where to find that.";
193
194string const disabled = "!Sorry, this usher is not currently accepting connections.";
195
196string const srvdisabled = "!Sorry, that server is currently disabled.";
197
198struct errstr
199{
200 std::string name;
201 int err;
202 errstr(std::string const & s): name(s), err(0) {}
203 errstr(std::string const & s, int e): name(s), err(e) {}
204};
205
206int tosserr(int ret, std::string const & name)
207{
208 if (ret == -1)
209 throw errstr(name, errno);
210 if (ret < 0)
211 throw errstr(name, ret);
212 return ret;
213}
214
215// packet format is:
216// byte version
217// byte cmd {100 if we send, 101 if we receive}
218// uleb128 {size of everything after this}
219// uleb128 {size of string}
220// string
221// {
222// uleb128 {size of string}
223// string
224// } // only present if we're receiving
225
226// uleb128 is
227// byte 0x80 | <low 7 bits>
228// byte 0x80 | <next 7 bits>
229// ...
230// byte 0xff & <remaining bits>
231//
232// the high bit says that this byte is not the last
233
234void make_packet(std::string const & msg, char * & pkt, int & size)
235{
236 size = msg.size();
237 char const * txt = msg.c_str();
238 char header[6];
239 header[0] = netsync_version;
240 header[1] = 100;
241 int headersize;
242 if (size >= 128) {
243 header[2] = 0x80 | (0x7f & (char)(size+2));
244 header[3] = (char)((size+2)>>7);
245 header[4] = 0x80 | (0x7f & (char)(size));
246 header[5] = (char)(size>>7);
247 headersize = 6;
248 } else if (size >= 127) {
249 header[2] = 0x80 | (0x7f & (char)(size+1));
250 header[3] = (char)((size+1)>>7);
251 header[4] = (char)(size);
252 headersize = 5;
253 } else {
254 header[2] = (char)(size+1);
255 header[3] = (char)(size);
256 headersize = 4;
257 }
258 pkt = new char[headersize + size];
259 memcpy(pkt, header, headersize);
260 memcpy(pkt + headersize, txt, size);
261 size += headersize;
262}
263
264struct buffer
265{
266 static int const buf_size = 2048;
267 static int const buf_reset_size = 1024;
268 char * ptr;
269 int readpos;
270 int writepos;
271 buffer(): readpos(0), writepos(0)
272 {
273 ptr = new char[buf_size];
274 }
275 ~buffer(){delete[] ptr;}
276 buffer(buffer const & b)
277 {
278 ptr = new char[buf_size];
279 memcpy(ptr, b.ptr, buf_size);
280 readpos = b.readpos;
281 writepos = b.writepos;
282 }
283 bool canread(){return writepos > readpos;}
284 bool canwrite(){return writepos < buf_size;}
285 void getread(char *& p, int & n)
286 {
287 p = ptr + readpos;
288 n = writepos - readpos;
289 }
290 void getwrite(char *& p, int & n)
291 {
292 p = ptr + writepos;
293 n = buf_size - writepos;
294 }
295 void fixread(int n)
296 {
297 if (n < 0) throw errstr("negative read\n", 0);
298 readpos += n;
299 if (readpos == writepos) {
300 readpos = writepos = 0;
301 } else if (readpos > buf_reset_size) {
302 memcpy(ptr, ptr+readpos, writepos-readpos);
303 writepos -= readpos;
304 readpos = 0;
305 }
306 }
307 void fixwrite(int n)
308 {
309 if (n < 0)
310 throw errstr("negative write\n", 0);
311 writepos += n;
312 }
313};
314
315struct sock
316{
317 int *s;
318 static set<int*> all_socks;
319 operator int()
320 {
321 if (!s)
322 return -1;
323 else
324 return s[0];
325 }
326 sock(int ss)
327 {
328 s = new int[2];
329 s[0] = ss;
330 s[1] = 1;
331 all_socks.insert(s);
332 }
333 sock(sock const & ss)
334 {
335 s = ss.s;
336 if (s)
337 s[1]++;
338 }
339 void deref()
340 {
341 if (s && !(--s[1])) {
342 try {
343 close();
344 } catch(errstr & e) {
345 // if you want it to throw errors, call close manually
346 }
347 delete[] s;
348 all_socks.erase(all_socks.find(s));
349 }
350 s = 0;
351 }
352 ~sock()
353 {
354 deref();
355 }
356 sock const & operator=(int ss)
357 {
358 deref();
359 s = new int[2];
360 all_socks.insert(s);
361 s[0]=ss;
362 return *this;
363 }
364 sock const & operator=(sock const & ss)
365 {
366 deref();
367 s = ss.s;
368 if (s)
369 ++s[1];
370 return *this;
371 }
372 void close()
373 {
374 if (!s || s[0] == -1)
375 return;
376 shutdown(s[0], SHUT_RDWR);
377 while (::close(s[0]) < 0) {
378 if (errno == EIO)
379 throw errstr("close failed", errno);
380 if (errno != EINTR)
381 break;
382 }
383 s[0]=-1;
384 }
385 static void close_all_socks()
386 {
387 for (set<int*>::iterator i = all_socks.begin(); i != all_socks.end(); ++i) {
388 while (::close((*i)[0]) < 0)
389 if (errno != EINTR)
390 break;
391 }
392 }
393 bool read_to(buffer & buf)
394 {
395 if (!s)
396 return false;
397 char *p;
398 int n;
399 buf.getwrite(p, n);
400 n = read(s[0], p, n);
401 if (n < 1) {
402 close();
403 return false;
404 } else
405 buf.fixwrite(n);
406 return true;
407 }
408 bool write_from(buffer & buf)
409 {
410 if (!s)
411 return false;
412 char *p;
413 int n;
414 buf.getread(p, n);
415 n = write(s[0], p, n);
416 if (n < 1) {
417 close();
418 return false;
419 } else
420 buf.fixread(n);
421 return true;
422 }
423};
424set<int*> sock::all_socks;
425
426bool check_address_empty(string const & addr, int port)
427{
428 sock s = tosserr(socket(AF_INET, SOCK_STREAM, 0), "socket()");
429 int yes = 1;
430 tosserr(setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
431 &yes, sizeof(yes)), "setsockopt");
432 sockaddr_in a;
433 memset (&a, 0, sizeof (a));
434 if (!inet_aton(addr.c_str(), (in_addr *) &a.sin_addr.s_addr))
435 throw errstr("bad ip address format", 0);
436 a.sin_port = htons(port);
437 a.sin_family = AF_INET;
438 int r = bind(s, (sockaddr *) &a, sizeof(a));
439 s.close();
440 return r == 0;
441}
442
443void find_addr(string & addr, int & port)
444{
445 if (currport == 0) {
446 currport = minport-1;
447 for(int i = 0; i < 4; ++i)
448 curraddr[i] = minaddr[i];
449 }
450 do {
451 // get the next address in our list
452 if (++currport > maxport) {
453 currport = minport;
454 for (int i = 0; i < 4; ++i) {
455 if (++curraddr[i] <= maxaddr[i])
456 break;
457 curraddr[i] = minaddr[i];
458 }
459 }
460 port = currport;
461 addr = lexical_cast<string>(curraddr[0]) + "." +
462 lexical_cast<string>(curraddr[1]) + "." +
463 lexical_cast<string>(curraddr[2]) + "." +
464 lexical_cast<string>(curraddr[3]);
465 } while (!check_address_empty(addr, port));
466}
467
468sock start(string addr, int port)
469{
470 sock s = tosserr(socket(AF_INET, SOCK_STREAM, 0), "socket()");
471 int yes = 1;
472 tosserr(setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
473 &yes, sizeof(yes)), "setsockopt");
474 sockaddr_in a;
475 memset (&a, 0, sizeof (a));
476 if (!inet_aton(addr.c_str(), (in_addr *) &a.sin_addr.s_addr))
477 throw errstr("bad ip address format", 0);
478 a.sin_port = htons(port);
479 a.sin_family = AF_INET;
480 tosserr(bind(s, (sockaddr *) &a, sizeof(a)), "bind");
481 cerr<<"bound to "<<addr<<":"<<port<<"\n";
482 listen(s, 10);
483 return s;
484}
485
486sock make_outgoing(int port, string const & address)
487{
488 sock s = tosserr(socket(AF_INET, SOCK_STREAM, 0), "socket()");
489
490 struct sockaddr_in a;
491 memset(&a, 0, sizeof(a));
492 a.sin_family = AF_INET;
493 a.sin_port = htons(port);
494
495 if (!inet_aton(address.c_str(), (in_addr *) &a.sin_addr.s_addr))
496 throw errstr("bad ip address format", 0);
497
498 tosserr(connect(s, (sockaddr *) &a, sizeof (a)), "connect()");
499 return s;
500}
501
502int fork_server(const string & name, vector<string> const & args)
503{
504 string logfile = logdir + "/" + name + ".log";
505 int err[2];
506 if ((err[1]=open(logfile.c_str(),O_CREAT|O_APPEND,0644)) < 0)
507 return false;
508 if ((err[0]=open(logfile.c_str(),O_RDONLY)) < 0)
509 {
510 close(err[1]);
511 return false;
512 }
513 int pid = fork();
514 if (pid == -1) {
515 close(err[0]);
516 close(err[1]);
517 cerr<<"Failed to fork server.\n";
518 return false;
519 } else if (pid == 0) {
520 close(err[0]);
521 close(0);
522 close(1);
523 close(2);
524 sock::close_all_socks();
525 if (dup2(err[1], 1) < 0) {
526 exit(1);
527 }
528 close(err[1]);
529 if (dup2(1, 2) < 0) {
530 exit(1);
531 }
532
533 char ** a = new char*[args.size()+1];
534 for (unsigned int i = 0; i < args.size(); ++i) {
535 a[i] = new char[args[i].size()+1];
536 memcpy(a[i], args[i].c_str(), args[i].size()+1);
537 }
538 a[args.size()] = 0;
539
540 // Must set C locale, because usher interprets the output from
541 // monotone itself, and only deals with english!
542 setlocale(LC_ALL,"C");
543 setlocale(LC_MESSAGES,"C");
544 setlocale(LC_NUMERIC,"C");
545 setenv("LC_ALL","C",1);
546
547 execvp(a[0], a);
548 perror("execvp failed");
549 exit(1);
550 } else {
551 close(err[1]);
552 char head[256];
553 int got = 0;
554 int r = 0;
555 bool line = false;
556 // the first line output on the server's stderr will be either
557 // "monotone: beginning service on <interface> : <port>" or
558 // "monotone: network error: bind(2) error: Address already in use"
559 do {
560 r = read(err[0], head + got, 256 - got);
561 if (r)
562 cerr<<"Read '"<<string(head+got, r)<<"'\n";
563 if (r > 0) {
564 for (int i = 0; i < r && !line; ++i)
565 if (head[got+i] == '\n')
566 line = true;
567 got += r;
568 }
569 } while(r >= 0 && !line && got < 256);
570 close(err[0]);
571 head[got] = 0;
572 if (string(head).find("beginning service") != string::npos)
573 return pid;
574 kill(pid, SIGKILL);
575 do {r = waitpid(pid, 0, 0);} while (r==-1 && errno == EINTR);
576 return -1;
577 }
578}
579
580bool connections_allowed = true;
581int total_connections = 0;
582
583struct serverstate
584{
585 enum ss {remote, active, waiting, sleeping, stopping,
586 stopped, shuttingdown, shutdown, unknown};
587 ss state;
588 int num;
589 serverstate(): state(unknown), num(0) {}
590 serverstate const & operator=(string const & s)
591 {
592 if (s == "REMOTE")
593 state = remote;
594 else if (s == "ACTIVE")
595 state = active;
596 else if (s == "WAITING")
597 state = waiting;
598 else if (s == "SLEEPING")
599 state = sleeping;
600 else if (s == "STOPPING")
601 state = stopping;
602 else if (s == "STOPPED")
603 state = stopped;
604 else if (s == "SHUTTINGDOWN")
605 state = shuttingdown;
606 else if (s == "SHUTDOWN")
607 state = shutdown;
608 return *this;
609 }
610 bool operator==(string const & s)
611 {
612 serverstate foo;
613 foo = s;
614 return foo.state == state;
615 }
616};
617std::ostream & operator<<(std::ostream & os, serverstate const & ss)
618{
619 switch (ss.state) {
620 case serverstate::remote:
621 os<<"REMOTE";
622 break;
623 case serverstate::active:
624 os<<"ACTIVE "<<ss.num;
625 break;
626 case serverstate::waiting:
627 os<<"WAITING";
628 break;
629 case serverstate::sleeping:
630 os<<"SLEEPING";
631 break;
632 case serverstate::stopping:
633 os<<"STOPPING "<<ss.num;
634 break;
635 case serverstate::stopped:
636 os<<"STOPPED";
637 break;
638 case serverstate::shuttingdown:
639 os<<"SHUTTINGDOWN "<<ss.num;
640 break;
641 case serverstate::shutdown:
642 os<<"SHUTDOWN";
643 case serverstate::unknown:
644 break;
645 }
646 return os;
647}
648
649struct server
650{
651 bool enabled;
652 list<map<string, shared_ptr<server> >::iterator> by_host, by_pat;
653 map<string, shared_ptr<server> >::iterator by_name;
654 static map<string, shared_ptr<server> > servers_by_host;
655 static map<string, shared_ptr<server> > servers_by_pattern;
656 static map<string, shared_ptr<server> > servers_by_name;
657 static set<shared_ptr<server> > live_servers;
658 bool local;
659 int pid;
660 string arguments;
661 string addr;
662 int port;
663 int connection_count;
664 int last_conn_time;
665 server() : enabled(true), local(false), pid(-1), port(0),
666 connection_count(0), last_conn_time(0)
667 {
668 }
669 ~server()
670 {
671 yeskill();
672 }
673 serverstate get_state()
674 {
675 serverstate ss;
676 ss.num = connection_count;
677 if (!connections_allowed) {
678 if (!total_connections)
679 ss.state = serverstate::shutdown;
680 else
681 ss.state = serverstate::shuttingdown;
682 } else if (connection_count) {
683 if (enabled)
684 ss.state = serverstate::active;
685 else
686 ss.state = serverstate::stopping;
687 } else if (!local)
688 ss.state = serverstate::remote;
689 else if (!enabled)
690 ss.state = serverstate::stopped;
691 else if (pid == -1)
692 ss.state = serverstate::sleeping;
693 else
694 ss.state = serverstate::waiting;
695 return ss;
696 }
697 void delist()
698 {
699 vector<string> foo;
700 set_hosts(foo);
701 set_patterns(foo);
702 servers_by_name.erase(by_name);
703 by_name = 0;
704 }
705 void rename(string const & n)
706 {
707 shared_ptr<server> me = by_name->second;
708 servers_by_name.erase(by_name);
709 by_name = servers_by_name.insert(make_pair(n, me)).first;
710 }
711 void set_hosts(vector<string> const & h)
712 {
713 shared_ptr<server> me = by_name->second;
714 map<string, shared_ptr<server> >::iterator c;
715 for (list<map<string, shared_ptr<server> >::iterator>::iterator
716 i = by_host.begin(); i != by_host.end(); ++i)
717 servers_by_host.erase(*i);
718 by_host.clear();
719 for (vector<string>::const_iterator i = h.begin(); i != h.end(); ++i) {
720 c = servers_by_host.find(*i);
721 if (c != servers_by_host.end()) {
722 cerr << "Removing duplicate for hostname " << *i << " in:\n "
723 list<map<string, shared_ptr<server> >::iterator>::iterator j;
724 bool first = true;
725 for (j = c->second->by_host.begin(); j != c->second->by_host.end();)
726 {
727 list<map<string, shared_ptr<server> >::iterator>::iterator j_saved
728 = j;
729 ++j;
730 cerr << (first ? "" : ", ") << (*j_saved)->second->by_name->first;
731 first = false;
732 if ((*j_saved)->first == *i)
733 {
734 servers_by_host.erase(*j_saved);
735 c->second->by_host.erase(j_saved);
736 }
737 }
738 cerr << "\n ... because it appeared in " << by_name->first << '\n';
739 }
740 c = servers_by_host.insert(make_pair(*i, me)).first;
741 by_host.push_back(c);
742 }
743 }
744 void set_patterns(vector<string> const & p)
745 {
746 shared_ptr<server> me = by_name->second;
747 map<string, shared_ptr<server> >::iterator c;
748 for (list<map<string, shared_ptr<server> >::iterator>::iterator
749 i = by_pat.begin(); i != by_pat.end(); ++i)
750 servers_by_pattern.erase(*i);
751 by_pat.clear();
752 for (vector<string>::const_iterator i = p.begin(); i != p.end(); ++i) {
753 c = servers_by_pattern.find(*i);
754 if (c != servers_by_pattern.end()) {
755 cerr << "Removing duplicate for pattern " << *i << " in:\n ";
756 list<map<string, shared_ptr<server> >::iterator>::iterator j;
757 bool first = true;
758 for (j = c->second->by_pat.begin(); j != c->second->by_pat.end(); ++j)
759 {
760 list<map<string, shared_ptr<server> >::iterator>::iterator j_saved
761 = j;
762 ++j;
763 cerr << (first ? "" : ", ") << (*j_saved)->second->by_name->first;
764 first = false;
765 if ((*j_saved)->first == *i)
766 {
767 servers_by_pattern.erase(*j_saved);
768 c->second->by_pat.erase(j_saved);
769 }
770 }
771 cerr << "\n ... because it appeared in " << by_name->first << '\n';
772 }
773 c = servers_by_pattern.insert(make_pair(*i, me)).first;
774 by_pat.push_back(c);
775 }
776 }
777 sock connect()
778 {
779 if (!connections_allowed)
780 throw errstr("all servers disabled");
781 if (!enabled)
782 throw errstr("server disabled");
783 if (local && pid == -1) {
784 // server needs to be started
785 // we'll try 3 times, since there's a delay between our checking that
786 // a port's available and the server taking it
787 for (int i = 0; i < 3 && pid == -1; ++i) {
788 if (i > 0 || port == 0)
789 find_addr(addr, port);
790 vector<string> args;
791 args.push_back(monotone);
792 args.push_back("serve");
793 args.push_back("--ticker=dot");
794 args.push_back("--bind=" + addr + ":" + lexical_cast<string>(port));
795 unsigned int n = 0, m = 0;
796 n = arguments.find_first_not_of(" \t");
797 while (n != string::npos && m != string::npos) {
798 m = arguments.find_first_of(" ", n);
799 args.push_back(arguments.substr(n, m-n));
800 n = arguments.find_first_not_of(" ", m);
801 }
802 pid = fork_server(by_name->first,args);
803 }
804 }
805 sock s = make_outgoing(port, addr);
806 if (local && !connection_count) {
807 live_servers.insert(by_name->second);
808 }
809 ++connection_count;
810 ++total_connections;
811 return s;
812 }
813 void disconnect()
814 {
815 --total_connections;
816 if (--connection_count || !local)
817 return;
818 last_conn_time = time(0);
819 maybekill();
820 }
821 void maybekill()
822 {
823 if (!local)
824 return;
825 if (pid == -1)
826 return;
827 int difftime = time(0) - last_conn_time;
828 if (!connection_count
829 && (difftime > server_idle_timeout || !connections_allowed))
830 yeskill();
831 else if (waitpid(pid, 0, WNOHANG) > 0) {
832 pid = -1;
833 port = 0;
834 }
835 }
836 void yeskill()
837 {
838 if (local && pid != -1) {
839 kill(pid, SIGTERM);
840 int r;
841 do {r = waitpid(pid, 0, 0);} while (r==-1 && errno == EINTR);
842 pid = -1;
843 port = 0;
844 live_servers.erase(live_servers.find(by_name->second));
845 }
846 }
847 string name()
848 {
849 if (local && port == 0)
850 return "dynamic local server";
851 else
852 return addr + ":" + lexical_cast<string>(port);
853 }
854};
855
856map<string, shared_ptr<server> > server::servers_by_host;
857map<string, shared_ptr<server> > server::servers_by_pattern;
858map<string, shared_ptr<server> > server::servers_by_name;
859set<shared_ptr<server> > server::live_servers;
860
861string getline(std::istream & in)
862{
863 string out;
864 char buf[256];
865 do {
866 in.getline(buf, 256);
867 int got = in.gcount()-1;
868 if (got > 0)
869 out.append(buf, got);
870 } while (in.fail() && !in.eof());
871 return out;
872}
873
874string read_server_record(std::istream & in)
875{
876 // server foobar
877 // hostname foobar.com
878 // hostname mtn.foobar.com
879 // pattern com.foobar
880 // remote 127.5.6.7:80
881 //
882 // server myproj
883 // hostname localhost
884 // local -d foo.db *
885 vector<string> hosts, patterns;
886 string name, desc;
887 bool local(false);
888 string line = getline(in);
889 while (!line.empty()) {
890 // server foobar
891 // ^ ^ ^
892 // a b c
893 int a = line.find_first_not_of(" \t");
894 int b = line.find_first_of(" \t", a);
895 int c = line.find_first_not_of(" \t", b);
896 string cmd = line.substr(a, b-a);
897 string arg = line.substr(c);
898 if (cmd == "server")
899 name = arg;
900 else if (cmd == "local") {
901 local = true;
902 desc = arg;
903 } else if (cmd == "remote") {
904 local = false;
905 desc = arg;
906 } else if (cmd == "host")
907 hosts.push_back(arg);
908 else if (cmd == "pattern")
909 patterns.push_back(arg);
910 else
911 cerr << "Unrecognised directive " << cmd << ", skipping line...\n";
912
913 line = getline(in);
914 }
915 if (name.empty())
916 return string();
917 shared_ptr<server> srv;
918 map<string, shared_ptr<server> >::iterator
919 i = server::servers_by_name.find(name);
920 if (i != server::servers_by_name.end()) {
921 if (local && i->second->local && i->second->arguments == desc)
922 srv = i->second;
923 else
924 srv = shared_ptr<server>(new server);
925 i->second->delist();
926 } else
927 srv = shared_ptr<server>(new server);
928 srv->by_name = server::servers_by_name.insert(make_pair(name, srv)).first;
929 srv->set_hosts(hosts);
930 srv->set_patterns(patterns);
931 if (local) {
932 srv->local = true;
933 srv->arguments = desc;
934 } else {
935 srv->local = false;
936 unsigned int c = desc.find(":");
937 if (c != desc.npos) {
938 srv->addr = desc.substr(0, c);
939 srv->port = lexical_cast<int>(desc.substr(c+1));
940 } else {
941 srv->addr = desc;
942 srv->port = 4691;
943 }
944 }
945 return name;
946}
947
948shared_ptr<server> get_server(string const & srv, string const & pat)
949{
950 map<string, shared_ptr<server> >::iterator i;
951 for (i = server::servers_by_host.begin();
952 i != server::servers_by_host.end(); ++i) {
953 if (srv.find(i->first) == 0) {
954 return i->second;
955 }
956 }
957 for (i = server::servers_by_pattern.begin();
958 i != server::servers_by_pattern.end(); ++i) {
959 if (pat.find(i->first) == 0) {
960 return i->second;
961 }
962 }
963 std::cerr<<"no server found for '"<<pat<<"' at '"<<srv<<"'\n";
964 return shared_ptr<server>();
965}
966
967shared_ptr<server> get_server(string const & name)
968{
969 map<string, shared_ptr<server> >::iterator i;
970 for (i = server::servers_by_name.begin();
971 i != server::servers_by_name.end(); ++i) {
972 if (name == i->first) {
973 return i->second;
974 }
975 }
976 return shared_ptr<server>();
977}
978
979void kill_old_servers()
980{
981 set<shared_ptr<server> >::iterator i;
982 for (i = server::live_servers.begin(); i != server::live_servers.end(); ++i) {
983 (*i)->maybekill();
984 }
985}
986
987int extract_uleb128(char *p, int maxsize, int & out)
988{
989 out = 0;
990 int b = 0;
991 unsigned char got;
992 do {
993 if (b == maxsize)
994 return -1;
995 got = p[b];
996 out += ((int)(p[b] & 0x7f))<<(b*7);
997 ++b;
998 } while ((unsigned int)(b*7) < sizeof(int)*8-1 && (got & 0x80));
999 return b;
1000}
1001
1002int extract_vstr(char *p, int maxsize, string & out)
1003{
1004 int size;
1005 out.clear();
1006 int chars = extract_uleb128(p, maxsize, size);
1007 if (chars == -1 || chars + size > maxsize) {
1008 return -1;
1009 }
1010 out.append(p+chars, size);
1011 return chars+size;
1012}
1013
1014bool extract_reply(buffer & buf, string & host, string & pat)
1015{
1016 char *p;
1017 int n, s;
1018 buf.getread(p, n);
1019 if (n < 4) return false;
1020 p += 2; // first 2 bytes are header
1021 n -= 2;
1022 // extract size, and make sure we have the entire packet
1023 int pos = extract_uleb128(p, n, s);
1024 if (pos == -1 || n < s+pos) {
1025 return false;
1026 }
1027 // extract host vstr
1028 int num = extract_vstr(p+pos, n-pos, host);
1029 if (num == -1) {
1030 return false;
1031 }
1032 pos += num;
1033 // extract pattern vstr
1034 num = extract_vstr(p+pos, n-pos, pat);
1035 if (num == -1) {
1036 cerr<<"old-style reply.\n";
1037 pat = host;
1038 host.clear();
1039 return true;
1040 }
1041 pos += num;
1042 buf.fixread(pos+2);
1043 return true;
1044}
1045
1046struct channel
1047{
1048 static int counter;
1049 int num;
1050 sock cli;
1051 sock srv;
1052 bool have_routed;
1053 bool no_server;
1054 buffer cbuf;
1055 buffer sbuf;
1056 shared_ptr<server> who;
1057 channel(sock & c): num(++counter),
1058 cli(c), srv(-1),
1059 have_routed(false), no_server(false)
1060 {
1061 char * dat;
1062 int size;
1063 make_packet(greeting, dat, size);
1064 char *p;
1065 int n;
1066 sbuf.getwrite(p, n);
1067 if (n < size) size = n;
1068 memcpy(p, dat, size);
1069 sbuf.fixwrite(size);
1070 delete[] dat;
1071
1072 cli.write_from(sbuf);
1073 }
1074 ~channel()
1075 {
1076 if (who && !no_server)
1077 who->disconnect();
1078 }
1079 bool is_finished()
1080 {
1081 return (cli == -1) && (srv == -1);
1082 }
1083 void add_to_select(int & maxfd, fd_set & rd, fd_set & wr, fd_set & er)
1084 {
1085 int c = cli;
1086 int s = srv;
1087
1088 if (c > 0) {
1089 FD_SET(c, &er);
1090 if (cbuf.canwrite())
1091 FD_SET(c, &rd);
1092 if (sbuf.canread())
1093 FD_SET(c, &wr);
1094 maxfd = max(maxfd, c);
1095 }
1096 if (s > 0) {
1097 FD_SET(s, &er);
1098 if (sbuf.canwrite())
1099 FD_SET(s, &rd);
1100 if (cbuf.canread())
1101 FD_SET(s, &wr);
1102 maxfd = max(maxfd, s);
1103 }
1104 }
1105 bool process_selected(fd_set & rd, fd_set & wr, fd_set & er)
1106 {
1107 int c = cli;
1108 int s = srv;
1109/* NB: read oob data before normal reads */
1110 if (c > 0 && FD_ISSET(c, &er)) {
1111 char d;
1112 errno = 0;
1113 if (recv(c, &d, 1, MSG_OOB) < 1)
1114 cli.close(), c = -1;
1115 else
1116 send(s, &d, 1, MSG_OOB);
1117 }
1118 if (s > 0 && FD_ISSET(s, &er)) {
1119 char d;
1120 errno = 0;
1121 if (recv(s, &d, 1, MSG_OOB) < 1)
1122 srv.close(), s = -1;
1123 else
1124 send(c, &d, 1, MSG_OOB);
1125 }
1126
1127 char *p=0;
1128 int n;
1129
1130 if (c > 0 && FD_ISSET(c, &rd)) {
1131 if (!cli.read_to(cbuf)) c = -1;
1132 if (!have_routed) {
1133 string reply_srv, reply_pat;
1134 if (extract_reply(cbuf, reply_srv, reply_pat)) {
1135 who = get_server(reply_srv, reply_pat);
1136 if (who && who->enabled) {
1137 try {
1138 srv = who->connect();
1139 have_routed = true;
1140 s = srv;
1141 } catch (errstr & e) {
1142 cerr<<"cannot contact server "<<who->name()<<"\n";
1143 no_server = true;
1144 }
1145 } else {
1146 char * dat;
1147 int size;
1148 sbuf.getwrite(p, n);
1149 if (who)
1150 make_packet(srvdisabled, dat, size);
1151 else
1152 make_packet(notfound, dat, size);
1153 if (n < size) size = n;
1154 memcpy(p, dat, size);
1155 sbuf.fixwrite(size);
1156 delete[] dat;
1157 no_server = true;
1158 }
1159 }
1160 }
1161 }
1162 if (s > 0 && FD_ISSET(s, &rd)) {
1163 if (!srv.read_to(sbuf)) s = -1;
1164 }
1165
1166 if (c > 0 && FD_ISSET(c, &wr)) {
1167 if (!cli.write_from(sbuf)) c = -1;
1168 }
1169 if (s > 0 && FD_ISSET(s, &wr)) {
1170 if (!srv.write_from(cbuf)) s = -1;
1171 }
1172
1173 // close sockets we have nothing more to send to
1174 if (c < 0 && !cbuf.canread()) {
1175 srv.close(), s = -1;
1176 }
1177 if ((no_server || have_routed && s < 0) && !sbuf.canread()) {
1178 cli.close(), c = -1;
1179 }
1180 return true;
1181 }
1182};
1183int channel::counter = 0;
1184
1185bool reload_pending;
1186map<string, string> admins;
1187string conffile;
1188
1189void reload_conffile(string const & file)
1190{
1191 reload_pending = false;
1192 cerr<<"Reloading config file...\n";
1193 std::ifstream cf(file.c_str());
1194
1195 string line = getline(cf);
1196 while (!line.empty()) {
1197 std::istringstream iss(line);
1198 string a, b, c;
1199 iss>>a;
1200 if (a == "userpass")
1201 {
1202 iss>>b>>c;
1203 admins.insert(make_pair(b, c));
1204 }
1205 else if (a == "logdir")
1206 {
1207 iss>>b;
1208 logdir = b;
1209 }
1210 else
1211 cerr << "Unrecognised directive " << a << ", skipping line...\n";
1212 line = getline(cf);
1213 }
1214
1215 set<string> names;
1216 while(cf) {
1217 string n = read_server_record(cf);
1218 if(!n.empty())
1219 names.insert(n);
1220 }
1221 for (map<string, shared_ptr<server> >::iterator
1222 i = server::servers_by_name.begin();
1223 i != server::servers_by_name.end(); ++i) {
1224 if (names.find(i->first) == names.end())
1225 i->second->delist();
1226 }
1227 cerr<<"Servers:\n";
1228 for (map<string, shared_ptr<server> >::iterator
1229 i = server::servers_by_name.begin();
1230 i != server::servers_by_name.end(); ++i) {
1231 cerr<<"\t"<<i->first<<"\n";
1232 list<map<string, shared_ptr<server> >::iterator>::iterator j;
1233 for (j = i->second->by_host.begin(); j != i->second->by_host.end(); ++j)
1234 cerr<<"\t\tHost: '"<<(*j)->first<<"'\n";
1235 for (j = i->second->by_pat.begin(); j != i->second->by_pat.end(); ++j)
1236 cerr<<"\t\tPattern: '"<<(*j)->first<<"'\n";
1237 }
1238 cerr<<"Reload complete.\n";
1239}
1240void sched_reload(int sig)
1241{
1242 reload_pending = true;
1243}
1244
1245struct administrator
1246{
1247 sock port;
1248 struct cstate
1249 {
1250 bool auth;
1251 bool rdone;
1252 string buf;
1253 cstate(): auth(false), rdone(false) {}
1254 };
1255 list<pair<cstate, sock> > conns;
1256 administrator(): port(-1)
1257 {}
1258 bool process(cstate & cs)
1259 {
1260 unsigned int n = cs.buf.find("\n");
1261 if (n == cs.buf.npos)
1262 return true;
1263 string l = cs.buf.substr(0, n);
1264 cs.buf.erase(0, n+1);
1265 std::istringstream iss(l);
1266 string cmd;
1267 iss>>cmd;
1268 if (cmd == "USERPASS") {
1269 string user, pass;
1270 iss>>user>>pass;
1271 map<string, string>::iterator i = admins.find(user);
1272 if (i == admins.end() || i->second != pass) {
1273 cerr<<"Failed admin login.\n";
1274 return false;
1275 } else {
1276 if (cs.auth == true)
1277 return false;
1278 cs.auth = true;
1279 return process(cs);
1280 }
1281 } else if (cmd == "STATUS") {
1282 string srv;
1283 iss>>srv;
1284 std::ostringstream oss;
1285 if (srv.empty()) {
1286 serverstate ss;
1287 ss.num = total_connections;
1288 if (connections_allowed) {
1289 if (total_connections)
1290 ss.state = serverstate::active;
1291 else
1292 ss.state = serverstate::waiting;
1293 } else {
1294 if (total_connections)
1295 ss.state = serverstate::shuttingdown;
1296 else
1297 ss.state = serverstate::shutdown;
1298 }
1299 oss<<ss<<"\n";
1300 } else {
1301 map<string, shared_ptr<server> >::iterator i;
1302 i = server::servers_by_name.find(srv);
1303 if (i != server::servers_by_name.end())
1304 oss<<i->second->get_state()<<"\n";
1305 else
1306 oss<<"No such server.\n";
1307 }
1308 cs.buf = oss.str();
1309 } else if (cmd == "START") {
1310 string srv;
1311 iss>>srv;
1312 std::ostringstream oss;
1313 map<string, shared_ptr<server> >::iterator i;
1314 i = server::servers_by_name.find(srv);
1315 if (i != server::servers_by_name.end()) {
1316 i->second->enabled = true;
1317 oss<<i->second->get_state()<<"\n";
1318 } else
1319 oss<<"No such server.\n";
1320 cs.buf = oss.str();
1321 } else if (cmd == "STOP") {
1322 string srv;
1323 iss>>srv;
1324 std::ostringstream oss;
1325 map<string, shared_ptr<server> >::iterator i;
1326 i = server::servers_by_name.find(srv);
1327 if (i != server::servers_by_name.end()) {
1328 i->second->enabled = false;
1329 i->second->maybekill();
1330 oss<<i->second->get_state()<<"\n";
1331 } else
1332 oss<<"No such server.\n";
1333 cs.buf = oss.str();
1334 } else if (cmd == "LIST") {
1335 string state;
1336 iss>>state;
1337 map<string, shared_ptr<server> >::iterator i;
1338 for (i = server::servers_by_name.begin();
1339 i != server::servers_by_name.end(); ++i) {
1340 if (state.empty() || i->second->get_state() == state)
1341 cs.buf += (cs.buf.empty()?"":" ") + i->first;
1342 }
1343 cs.buf += "\n";
1344 } else if (cmd == "SHUTDOWN") {
1345 connections_allowed = false;
1346 kill_old_servers();
1347 cs.buf = "ok\n";
1348 } else if (cmd == "CONNECTIONS") {
1349 cs.buf = lexical_cast<string>(total_connections) + "\n";
1350 } else if (cmd == "RELOAD") {
1351 reload_conffile(conffile);
1352 cs.buf = "ok\n";
1353 } else if (cmd == "STARTUP") {
1354 connections_allowed = true;
1355 cs.buf = "ok\n";
1356 } else {
1357 return true;
1358 }
1359 cs.rdone = true;
1360 return true;
1361 }
1362 void initialize(string const & ap)
1363 {
1364 try {
1365 int c = ap.find(":");
1366 string a = ap.substr(0, c);
1367 int p = lexical_cast<int>(ap.substr(c+1));
1368 port = start(a, p);
1369 } catch (errstr & s) {
1370 cerr<<"Could not initialize admin port: "<<s.name<<"\n";
1371 return;
1372 }
1373 }
1374 void add_to_select(int & maxfd, fd_set & rd, fd_set & wr, fd_set & er)
1375 {
1376 if (int(port) == -1)
1377 return;
1378 FD_SET (port, &rd);
1379 maxfd = max(maxfd, int(port));
1380 for (list<pair<cstate, sock> >::iterator i = conns.begin();
1381 i != conns.end(); ++i) {
1382 int c = i->second;
1383 if (!i->first.rdone)
1384 FD_SET(c, &rd);
1385 else
1386 FD_SET(c, &wr);
1387 maxfd = max(maxfd, int(c));
1388 }
1389 }
1390 void process_selected(fd_set & rd, fd_set & wr, fd_set & er)
1391 {
1392 if (int(port) == -1)
1393 return;
1394 if (FD_ISSET(port, &rd)) {
1395 try {
1396 struct sockaddr_in addr;
1397 unsigned int l = sizeof(addr);
1398 memset(&addr, 0, l);
1399 sock nc = tosserr(accept(port, (struct sockaddr *)
1400 &addr, &l), "accept()");
1401 conns.push_back(make_pair(cstate(), nc));
1402 } catch(errstr & s) {
1403 cerr<<"During new admin connection: "<<s.name<<"\n";
1404 }
1405 }
1406 list<list<pair<cstate, sock> >::iterator> del;
1407 for (list<pair<cstate, sock> >::iterator i = conns.begin();
1408 i != conns.end(); ++i) {
1409 int c = i->second;
1410 if (c <= 0) {
1411// cerr<<"Bad socket.\n";
1412 del.push_back(i);
1413 } else if (FD_ISSET(c, &rd)) {
1414 char buf[120];
1415 int n;
1416 n = read(c, buf, 120);
1417 if (n < 1) {
1418 // cerr<<"Read failed.\n";
1419 del.push_back(i);
1420 }
1421 i->first.buf.append(buf, n);
1422 if (!process(i->first)) {
1423// cerr<<"Closing connection...\n";
1424// i->second.close();
1425 del.push_back(i);
1426 }
1427 }
1428 else if (FD_ISSET(c, &wr)) {
1429 int n = write(c, i->first.buf.c_str(), i->first.buf.size());
1430 if (n < 1) {
1431// cerr<<"Write failed.\n";
1432 del.push_back(i);
1433 } else {
1434 i->first.buf.erase(0, n);
1435 if (i->first.buf.empty() && i->first.rdone) {
1436// cerr<<"Done.\n";
1437 del.push_back(i);
1438 }
1439 }
1440 }
1441 }
1442 for (list<list<pair<cstate, sock> >::iterator>::iterator i = del.begin();
1443 i != del.end(); ++i) {
1444 conns.erase(*i);
1445 }
1446 }
1447};
1448
1449struct pidfile
1450{
1451 string filename;
1452 void initialize(string const & file)
1453 {
1454 filename = file;
1455 std::ofstream ofs(filename.c_str());
1456 ofs<<getpid();
1457 }
1458 ~pidfile()
1459 {
1460 if (!filename.empty())
1461 unlink(filename.c_str());
1462 }
1463};
1464
1465bool done;
1466void sig_end(int sig)
1467{
1468 done = true;
1469}
1470
1471int main (int argc, char **argv)
1472{
1473 pidfile pf;
1474 administrator admin;
1475 {
1476 int i;
1477 for (i = 1; i < argc; ++i) {
1478 if (string(argv[i]) == "-l") {
1479 string lp(argv[++i]);
1480 unsigned int c = lp.find(":");
1481 listenaddr = lp.substr(0, c);
1482 if (c != lp.npos)
1483 listenport = lexical_cast<int>(lp.substr(c+1));
1484 } else if (string(argv[i]) == "-m")
1485 monotone = argv[++i];
1486 else if (string(argv[i]) == "-a")
1487 admin.initialize(argv[++i]);
1488 else if (string(argv[i]) == "-p")
1489 pf.initialize(argv[++i]);
1490 else
1491 conffile = argv[i];
1492 }
1493 if (conffile.empty() || i != argc) {
1494 cerr<<"Usage:\n";
1495 cerr<<"\tusher [-l addr[:port]] [-a addr:port] <config-file>\n";
1496 exit (1);
1497 }
1498 }
1499 reload_conffile(conffile);
1500
1501
1502 struct sigaction sa, sa_old;
1503 sa.sa_handler = &sched_reload;
1504 sigemptyset(&sa.sa_mask);
1505 sa.sa_flags = 0;
1506 while(sigaction(SIGHUP, &sa, &sa_old) == -1 && errno == EINTR);
1507 sa.sa_handler = SIG_IGN;
1508 while(sigaction(SIGPIPE, &sa, &sa_old) == -1 && errno == EINTR);
1509 sa.sa_handler = sig_end;
1510 while(sigaction(SIGTERM, &sa, &sa_old) == -1 && errno == EINTR);
1511 while(sigaction(SIGINT, &sa, &sa_old) == -1 && errno == EINTR);
1512
1513 sock h(-1);
1514 try {
1515 h = start(listenaddr, listenport);
1516 } catch (errstr & s) {
1517 std::cerr<<"Error while opening socket: "<<s.name<<"\n";
1518 exit (1);
1519 }
1520
1521 std::list<channel> channels;
1522
1523 done = false;
1524 while (!done) {
1525 fd_set rd, wr, er;
1526 FD_ZERO (&rd);
1527 FD_ZERO (&wr);
1528 FD_ZERO (&er);
1529 FD_SET (h, &rd);
1530 int nfds = h;
1531 channel *newchan = 0;
1532
1533 for (std::list<channel>::iterator i = channels.begin();
1534 i != channels.end(); ++i)
1535 i->add_to_select(nfds, rd, wr, er);
1536
1537 admin.add_to_select(nfds, rd, wr, er);
1538
1539 timeval timeout;
1540 timeout.tv_sec = 10;
1541 timeout.tv_usec = 0;
1542 int r = select(nfds+1, &rd, &wr, &er, &timeout);
1543
1544 if (r < 0 && errno != EINTR) {
1545 perror ("select()");
1546 exit (1);
1547 }
1548 if (done)
1549 return 0;
1550 if (FD_ISSET(h, &rd)) {
1551 try {
1552 struct sockaddr_in client_address;
1553 unsigned int l = sizeof(client_address);
1554 memset(&client_address, 0, l);
1555 sock cli = tosserr(accept(h, (struct sockaddr *)
1556 &client_address, &l), "accept()");
1557 if (connections_allowed)
1558 newchan = new channel(cli);
1559 else {
1560 char * dat;
1561 int size;
1562 make_packet(disabled, dat, size);
1563 write(cli, dat, size);
1564 delete[] dat;
1565 }
1566 } catch(errstr & s) {
1567 cerr<<"During new connection: "<<s.name<<"\n";
1568 }
1569 }
1570 std::list<std::list<channel>::iterator> finished;
1571 for (std::list<channel>::iterator i = channels.begin();
1572 i != channels.end(); ++i) {
1573 try {
1574 i->process_selected(rd, wr, er);
1575 if (i->is_finished())
1576 finished.push_back(i);
1577 } catch (errstr & e) {
1578 finished.push_back(i);
1579 cerr<<"Error proccessing connection "<<i->num<<": "<<e.name<<"\n";
1580 }
1581 }
1582 for (std::list<std::list<channel>::iterator>::iterator i = finished.begin();
1583 i != finished.end(); ++i)
1584 channels.erase(*i);
1585 if (newchan) {
1586 channels.push_back(*newchan);
1587 delete newchan;
1588 newchan = 0;
1589 }
1590 kill_old_servers();
1591 if (reload_pending)
1592 reload_conffile(conffile);
1593
1594 admin.process_selected(rd, wr, er);
1595 }
1596 return 0;
1597}

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status