4 * use at your own risk.
18 void sendmail(char *content, char *subject, char *msg);
19 #define TIME "[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+"
23 "([^ ]+ \\([0-9,]+-[0-9,]+\\))"
24 "|( copy [0-9,]+-[0-9,]+ (data|hole|directory|tail))"
25 "|( sha1 [0-9,]+-[0-9,]+)"
26 "|([^ ]+: [0-9,]+ used mirrored)"
27 "|([^ \\-]+-[^ \\-]+( mirrored| sealed| empty)+)"
33 "([^ ]+: unsealed [0-9,]+ bytes)"
40 LogSize = 4*1024*1024 // TODO: make smaller
45 typedef struct Mirror Mirror;
52 typedef struct Conf Conf;
68 typedef struct Job Job;
98 fprint(2, "usage: mgr [-s] [-b bin/venti/] venti.conf\n");
103 rdconf(char *file, Conf *conf)
105 char *s, *line, *flds[10];
109 if(readifile(&f, file) < 0)
111 memset(conf, 0, sizeof *conf);
121 i = getfields(s, flds, nelem(flds), 1, " \t\r");
122 if(i <= 0 || strcmp(flds[0], "mgr") != 0) {
124 }else if(i == 4 && strcmp(flds[1], "mirror") == 0) {
125 if(conf->nmirror%64 == 0)
126 conf->mirror = vtrealloc(conf->mirror, (conf->nmirror+64)*sizeof(conf->mirror[0]));
127 conf->mirror[conf->nmirror].src = vtstrdup(flds[2]);
128 conf->mirror[conf->nmirror].dst = vtstrdup(flds[3]);
130 }else if(i == 3 && strcmp(flds[1], "mirrorfreq") == 0) {
131 conf->mirrorfreq = atoi(flds[2]);
132 }else if(i == 3 && strcmp(flds[1], "verify") == 0) {
133 if(conf->nverify%64 == 0)
134 conf->verify = vtrealloc(conf->verify, (conf->nverify+64)*sizeof(conf->verify[0]));
135 conf->verify[conf->nverify++] = vtstrdup(flds[2]);
136 }else if(i == 3 && strcmp(flds[1], "verifyfreq") == 0) {
137 conf->verifyfreq = atoi(flds[2]);
138 }else if(i == 3 && strcmp(flds[1], "httpaddr") == 0){
140 seterr(EAdmin, "duplicate httpaddr lines in configuration file %s", file);
143 conf->httpaddr = estrdup(flds[2]);
144 }else if(i == 3 && strcmp(flds[1], "webroot") == 0){
146 seterr(EAdmin, "duplicate webroot lines in configuration file %s", file);
149 conf->webroot = estrdup(flds[2]);
150 }else if(i == 3 && strcmp(flds[1], "smtp") == 0) {
152 seterr(EAdmin, "duplicate smtp lines in configuration file %s", file);
155 conf->smtp = estrdup(flds[2]);
156 }else if(i == 3 && strcmp(flds[1], "mailfrom") == 0) {
158 seterr(EAdmin, "duplicate mailfrom lines in configuration file %s", file);
161 conf->mailfrom = estrdup(flds[2]);
162 }else if(i == 3 && strcmp(flds[1], "mailto") == 0) {
164 seterr(EAdmin, "duplicate mailto lines in configuration file %s", file);
167 conf->mailto = estrdup(flds[2]);
169 seterr(EAdmin, "illegal line '%s' in configuration file %s", line, file);
192 for(i=0; i<l->nchunk; i++) {
193 if(++c == l->chunk+l->nchunk)
195 memmove(p, c->p, c->wp - c->p);
203 typedef struct HttpObj HttpObj;
205 static int fromwebdir(HConnect*);
215 char name[ObjNameSize];
219 static HttpObj objs[MaxObjs];
220 static void httpproc(void*);
227 c = mallocz(sizeof(HConnect), 1);
229 sysfatal("out of memory");
232 c->hstop = c->header;
239 if(hparseheaders(c, 0) < 0)
241 if(strcmp(c->req.meth, "GET") != 0
242 && strcmp(c->req.meth, "HEAD") != 0)
243 return hunallowed(c, "GET, HEAD");
244 if(c->head.expectother || c->head.expectcont)
245 return hfail(c, HExpectFail, nil);
250 hsettype(HConnect *c, char *type)
262 hprint(hout, "Content-type: %s\r\n", type);
264 hprint(hout, "Transfer-Encoding: chunked\r\n");
265 hprint(hout, "\r\n");
276 hsethtml(HConnect *c)
278 return hsettype(c, "text/html; charset=utf-8");
282 hsettext(HConnect *c)
284 return hsettype(c, "text/plain; charset=utf-8");
288 hnotfound(HConnect *c)
295 return hfail(c, HNotFound, c->req.uri);
299 xloglist(HConnect *c)
301 if(hsettype(c, "text/html") < 0)
303 vtloghlist(&c->hout);
309 strpcmp(const void *va, const void *vb)
311 return strcmp(*(char**)va, *(char**)vb);
320 hprint(h, "<html><head>\n");
321 hprint(h, "<title>Venti Server Logs</title>\n");
322 hprint(h, "</head><body>\n");
323 hprint(h, "<b>Venti Server Logs</b>\n<p>\n");
326 qsort(p, n, sizeof(p[0]), strpcmp);
328 hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]);
330 hprint(h, "</body></html>\n");
334 vtloghdump(Hio *h, VtLog *l)
340 name = l ? l->name : "<nil>";
342 hprint(h, "<html><head>\n");
343 hprint(h, "<title>Venti Server Log: %s</title>\n", name);
344 hprint(h, "</head><body>\n");
345 hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name);
349 for(i=0; i<l->nchunk; i++){
350 if(++c == l->chunk+l->nchunk)
352 hwrite(h, c->p, c->wp-c->p);
355 hprint(h, "</body></html>\n");
360 hargstr(HConnect *c, char *name, char *def)
364 for(p=c->req.searchpairs; p; p=p->next)
365 if(strcmp(p->s, name) == 0)
376 name = hargstr(c, "log", "");
379 l = vtlogopen(name, 0);
382 if(hsettype(c, "text/html") < 0){
386 vtloghdump(&c->hout, l);
393 httpdproc(void *vaddress)
396 char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
400 ctl = announce(address, dir);
402 sysfatal("announce %s: %r", address);
406 if(0) print("announce ctl %d dir %s\n", ctl, dir);
409 * wait for a call (or an error)
411 nctl = listen(dir, ndir);
412 if(0) print("httpd listen %d %s...\n", nctl, ndir);
414 fprint(2, "mgr: httpd can't listen on %s: %r\n", address);
418 data = accept(ctl, ndir);
419 if(0) print("httpd accept %d...\n", data);
421 fprint(2, "mgr: httpd accept: %r\n");
425 if(0) print("httpd close nctl %d\n", nctl);
428 hinit(&c->hin, data, Hread);
429 hinit(&c->hout, data, Hwrite);
444 * No timeout because the signal appears to hit every
447 if(hparsereq(c, 0) < 0)
450 for(i = 0; i < MaxObjs && objs[i].name[0]; i++){
451 n = strlen(objs[i].name);
452 if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0)
453 || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){
454 ok = (*objs[i].f)(c);
474 httpdobj(char *name, int (*f)(HConnect*))
478 if(name == nil || strlen(name) >= ObjNameSize)
480 for(i = 0; i < MaxObjs; i++){
481 if(objs[i].name[0] == '\0'){
482 strcpy(objs[i].name, name);
486 if(strcmp(objs[i].name, name) == 0)
497 ".html", "text/html",
498 ".txt", "text/plain",
506 fromwebdir(HConnect *c)
508 char buf[4096], *p, *ext, *type;
509 int i, fd, n, defaulted;
512 if(conf.webroot == nil || strstr(c->req.uri, ".."))
514 snprint(buf, sizeof buf-20, "%s/%s", conf.webroot, c->req.uri+1);
517 if((fd = open(buf, OREAD)) < 0)
527 strcat(buf, "/index.html");
537 type = "application/octet-stream";
538 for(i=0; exttab[i].ext; i++){
540 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){
541 type = exttab[i].type;
545 if(hsettype(c, type) < 0){
549 while((n = read(fd, buf, sizeof buf)) > 0)
550 if(hwrite(&c->hout, buf, n) < 0)
558 hmanager(HConnect *c)
572 hprint(hout, "<html><head><title>venti mgr status</title></head>\n");
573 hprint(hout, "<body><h2>venti mgr status</h2>\n");
575 for(i=0; i<njob; i++) {
579 hprint(hout, "----/--/-- --:--:--");
581 hprint(hout, "%+T", (long)(j->runstart + time0));
582 hprint(hout, " %s", j->name);
585 hprint(hout, " (running)");
586 } else if(!j->newok) {
587 hprint(hout, " <font color=\"#cc0000\">(FAILED)</font>");
590 hprint(hout, "</b>\n");
591 hprint(hout, "<font size=-1><pre>\n");
594 for(k=0; k<l->nchunk; k++){
595 if(++ch == l->chunk+l->nchunk)
597 hwrite(hout, ch->p, ch->wp-ch->p);
599 hprint(hout, "</pre></font>\n");
602 hprint(hout, "</body></html>\n");
621 while((n = read(fd, buf, 512-1)) > 0) {
624 vtlogprint(l, "%s", buf);
642 if((fd[0] = open("/dev/null", ORDWR)) < 0) {
643 vtlogprint(errlog, "%T open /dev/null: %r\n");
647 vtlogprint(errlog, "%T pipe: %r\n");
653 j->oldlog = j->newlog;
656 for(i=0; i<l->nchunk; i++)
657 l->chunk[i].wp = l->chunk[i].p;
665 j->pid = threadspawn(fd, j->argv[0], j->argv);
667 vtlogprint(errlog, "%T exec %s: %r\n", j->argv[0]);
672 // fd[0], fd[1], fd[2] are closed now
679 getline(Resub *text, Resub *line)
683 if(text->sp >= text->ep)
686 p = memchr(text->sp, '\n', text->ep - text->sp);
698 verifyok(char *output)
703 text.ep = output+strlen(output);
704 while(getline(&text, &line) >= 0) {
706 memset(&m, 0, sizeof m);
707 if(!regexec(verifyprog, line.sp, nil, 0))
715 mirrorok(char *output)
720 text.ep = output+strlen(output);
721 while(getline(&text, &line) >= 0) {
723 memset(&m, 0, sizeof m);
724 if(!regexec(mirrorprog, line.sp, nil, 0))
738 memset(j, 0, sizeof *j);
741 while((p = va_arg(arg, char*)) != nil) {
743 if(i >= nelem(j->argv))
744 sysfatal("job argv size too small");
747 j->oldlog = vtlogopen(smprint("log%ld.0", j-job), LogSize);
748 j->newlog = vtlogopen(smprint("log%ld.1", j-job), LogSize);
760 for(;; sleep(1000)) {
761 for(i=0; i<njob; i++) {
762 now = time(0) - time0;
764 if(j->pid > 0 || j->newok == -1) {
766 if(now - j->runstart > 2*j->freq) {
767 //TODO: log slow running j
771 if((j->nrun > 0 && now - j->runend > j->freq)
772 || (j->nrun == 0 && now > (vlong)(j->offset*j->freq))) {
792 for(i=0; i<njob; i++) {
794 if(j->pid == w->pid) {
796 j->runend = time(0) - time0;
805 threadmain(int argc, char **argv)
815 bin = unsharp("#9/bin/venti");
822 bin = EARGF(usage());
833 if(rdconf(argv[0], &conf) < 0)
834 sysfatal("reading config: %r");
835 if(conf.httpaddr == nil)
836 sysfatal("config has no httpaddr");
837 if(conf.smtp != nil && conf.mailfrom == nil)
838 sysfatal("config has smtp but no mailfrom");
839 if(conf.smtp != nil && conf.mailto == nil)
840 sysfatal("config has smtp but no mailto");
841 if((mirrorprog = regcomp(mirrorregexp)) == nil)
842 sysfatal("mirrorregexp did not complete");
843 if((verifyprog = regcomp(verifyregexp)) == nil)
844 sysfatal("verifyregexp did not complete");
845 if(conf.nverify > 0 && conf.verifyfreq == 0)
846 sysfatal("config has no verifyfreq");
847 if(conf.nmirror > 0 && conf.mirrorfreq == 0)
848 sysfatal("config has no mirrorfreq");
851 // sendmail("startup", "mgr is starting\n");
853 logbuf = vtmalloc(LogSize+1); // +1 for NUL
855 errlog = vtlogopen("errors", LogSize);
856 job = vtmalloc((conf.nmirror+conf.nverify)*sizeof job[0]);
857 prog = smprint("%s/mirrorarenas", bin);
858 for(i=0; i<conf.nmirror; i++) {
859 // job: /bin/venti/mirrorarenas -v src dst
862 mkjob(j, prog, "-v", conf.mirror[i].src, conf.mirror[i].dst, nil);
863 j->name = smprint("mirror %s %s", conf.mirror[i].src, conf.mirror[i].dst);
865 j->freq = conf.mirrorfreq; // 4 hours // TODO: put in config
866 j->offset = (double)i/conf.nmirror;
869 prog = smprint("%s/verifyarena", bin);
870 for(i=0; i<conf.nverify; i++) {
871 // job: /bin/venti/verifyarena -b 64M -s 1000 -v arena
874 mkjob(j, prog, "-b64M", "-s1000", conf.verify[i], nil);
875 j->name = smprint("verify %s", conf.verify[i]);
877 j->freq = conf.verifyfreq;
878 j->offset = (double)i/conf.nverify;
881 httpdobj("/mgr", hmanager);
882 httpdobj("/log", xlog);
883 vtproc(httpdproc, conf.httpaddr);
884 vtproc(waitproc, threadwaitchan());
888 vtproc(manager, nil);
893 qp(Biobuf *b, char *p)
914 if(33 <= *p && *p <= 126 && *p != '=') {
920 if(*p == ' ' || *p == '\t') {
926 Bprint(b, "=%02X", (uchar)*p);
933 smtpread(Biobuf *b, int code)
938 while((p = Brdstr(b, '\n', 1)) != nil) {
939 n = strtol(p, &q, 10);
940 if(n == 0 || q != p+3) {
942 vtlogprint(errlog, "sending mail: %s\n", p);
962 sendmail(char *content, char *subject, char *msg)
967 if((fd = dial(conf.smtp, 0, 0, 0)) < 0) {
968 vtlogprint(errlog, "dial %s: %r\n", conf.smtp);
971 bin = vtmalloc(sizeof *bin);
972 bout = vtmalloc(sizeof *bout);
973 Binit(bin, fd, OREAD);
974 Binit(bout, fd, OWRITE);
975 if(smtpread(bin, 220) < 0){
983 Bprint(bout, "HELO venti-mgr\n");
985 if(smtpread(bin, 250) < 0)
988 Bprint(bout, "MAIL FROM:<%s>\n", conf.mailfrom);
990 if(smtpread(bin, 250) < 0)
993 Bprint(bout, "RCPT TO:<%s>\n", conf.mailfrom);
995 if(smtpread(bin, 250) < 0)
998 Bprint(bout, "DATA\n");
1000 if(smtpread(bin, 354) < 0)
1003 Bprint(bout, "From: \"venti mgr\" <%s>\n", conf.mailfrom);
1004 Bprint(bout, "To: <%s>\n", conf.mailto);
1005 Bprint(bout, "Subject: %s\n", subject);
1006 Bprint(bout, "MIME-Version: 1.0\n");
1007 Bprint(bout, "Content-Type: %s; charset=\"UTF-8\"\n", content);
1008 Bprint(bout, "Content-Transfer-Encoding: quoted-printable\n");
1009 Bprint(bout, "Message-ID: %08lux%08lux@venti.swtch.com\n", fastrand(), fastrand());
1012 Bprint(bout, ".\n");
1014 if(smtpread(bin, 250) < 0)
1017 Bprint(bout, "QUIT\n");