URI:
       tmgr.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmgr.c (18765B)
       ---
            1 /*
            2  * mirror manager.
            3  * a work in progress.
            4  * use at your own risk.
            5  */
            6 
            7 #include "stdinc.h"
            8 #include <regexp.h>
            9 #include <bio.h>
           10 #include "dat.h"
           11 #include "fns.h"
           12 
           13 #ifdef PLAN9PORT
           14 #define sp s.sp
           15 #define ep e.ep
           16 #endif
           17 
           18 void sendmail(char *content, char *subject, char *msg);
           19 #define TIME "[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+"
           20 
           21 char *mirrorregexp =
           22         "^" TIME " ("
           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)+)"
           28         ")$";
           29 Reprog *mirrorprog;
           30 
           31 char *verifyregexp =
           32         "^" TIME " ("
           33                 "([^ ]+: unsealed [0-9,]+ bytes)"
           34         ")$";
           35 Reprog *verifyprog;
           36 
           37 #undef pipe
           38 enum
           39 {
           40         LogSize = 4*1024*1024        // TODO: make smaller
           41 };
           42 
           43 VtLog *errlog;
           44 
           45 typedef struct Mirror Mirror;
           46 struct Mirror
           47 {
           48         char *src;
           49         char *dst;
           50 };
           51 
           52 typedef struct Conf Conf;
           53 struct Conf
           54 {
           55         Mirror *mirror;
           56         int nmirror;
           57         char **verify;
           58         int nverify;
           59         char *httpaddr;
           60         char *webroot;
           61         char *smtp;
           62         char *mailfrom;
           63         char *mailto;
           64         int mirrorfreq;
           65         int verifyfreq;
           66 };
           67 
           68 typedef struct Job Job;
           69 struct Job
           70 {
           71         char *name;
           72         QLock lk;
           73         char *argv[10];
           74         int oldok;
           75         int newok;
           76         VtLog *oldlog;
           77         VtLog *newlog;
           78         int pid;
           79         int pipe;
           80         int nrun;
           81         vlong freq;
           82         vlong runstart;
           83         vlong runend;
           84         double offset;
           85         int (*ok)(char*);
           86 };
           87 
           88 Job *job;
           89 int njob;
           90 char *bin;
           91 
           92 vlong time0;
           93 Conf conf;
           94 
           95 void
           96 usage(void)
           97 {
           98         fprint(2, "usage: mgr [-s] [-b bin/venti/] venti.conf\n");
           99         threadexitsall(0);
          100 }
          101 
          102 int
          103 rdconf(char *file, Conf *conf)
          104 {
          105         char *s, *line, *flds[10];
          106         int i, ok;
          107         IFile f;
          108 
          109         if(readifile(&f, file) < 0)
          110                 return -1;
          111         memset(conf, 0, sizeof *conf);
          112         ok = -1;
          113         line = nil;
          114         for(;;){
          115                 s = ifileline(&f);
          116                 if(s == nil){
          117                         ok = 0;
          118                         break;
          119                 }
          120                 line = estrdup(s);
          121                 i = getfields(s, flds, nelem(flds), 1, " \t\r");
          122                 if(i <= 0 || strcmp(flds[0], "mgr") != 0) {
          123                         /* do nothing */
          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]);
          129                         conf->nmirror++;
          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){
          139                         if(conf->httpaddr){
          140                                 seterr(EAdmin, "duplicate httpaddr lines in configuration file %s", file);
          141                                 break;
          142                         }
          143                         conf->httpaddr = estrdup(flds[2]);
          144                 }else if(i == 3 && strcmp(flds[1], "webroot") == 0){
          145                         if(conf->webroot){
          146                                 seterr(EAdmin, "duplicate webroot lines in configuration file %s", file);
          147                                 break;
          148                         }
          149                         conf->webroot = estrdup(flds[2]);
          150                 }else if(i == 3 && strcmp(flds[1], "smtp") == 0) {
          151                         if(conf->smtp){
          152                                 seterr(EAdmin, "duplicate smtp lines in configuration file %s", file);
          153                                 break;
          154                         }
          155                         conf->smtp = estrdup(flds[2]);
          156                 }else if(i == 3 && strcmp(flds[1], "mailfrom") == 0) {
          157                         if(conf->mailfrom){
          158                                 seterr(EAdmin, "duplicate mailfrom lines in configuration file %s", file);
          159                                 break;
          160                         }
          161                         conf->mailfrom = estrdup(flds[2]);
          162                 }else if(i == 3 && strcmp(flds[1], "mailto") == 0) {
          163                         if(conf->mailto){
          164                                 seterr(EAdmin, "duplicate mailto lines in configuration file %s", file);
          165                                 break;
          166                         }
          167                         conf->mailto = estrdup(flds[2]);
          168                 }else{
          169                         seterr(EAdmin, "illegal line '%s' in configuration file %s", line, file);
          170                         break;
          171                 }
          172                 free(line);
          173                 line = nil;
          174         }
          175         free(line);
          176         freeifile(&f);
          177         return ok;
          178 }
          179 
          180 static QLock loglk;
          181 static char *logbuf;
          182 
          183 char*
          184 logtext(VtLog *l)
          185 {
          186         int i;
          187         char *p;
          188         VtLogChunk *c;
          189 
          190         p = logbuf;
          191         c = l->w;
          192         for(i=0; i<l->nchunk; i++) {
          193                 if(++c == l->chunk+l->nchunk)
          194                         c = l->chunk;
          195                 memmove(p, c->p, c->wp - c->p);
          196                 p += c->wp - c->p;
          197         }
          198         *p = 0;
          199         return logbuf;
          200 }
          201 
          202 
          203 typedef struct HttpObj        HttpObj;
          204 
          205 static int fromwebdir(HConnect*);
          206 
          207 enum
          208 {
          209         ObjNameSize        = 64,
          210         MaxObjs                = 64
          211 };
          212 
          213 struct HttpObj
          214 {
          215         char        name[ObjNameSize];
          216         int        (*f)(HConnect*);
          217 };
          218 
          219 static HttpObj        objs[MaxObjs];
          220 static void httpproc(void*);
          221 
          222 static HConnect*
          223 mkconnect(void)
          224 {
          225         HConnect *c;
          226 
          227         c = mallocz(sizeof(HConnect), 1);
          228         if(c == nil)
          229                 sysfatal("out of memory");
          230         c->replog = nil;
          231         c->hpos = c->header;
          232         c->hstop = c->header;
          233         return c;
          234 }
          235 
          236 static int
          237 preq(HConnect *c)
          238 {
          239         if(hparseheaders(c, 0) < 0)
          240                 return -1;
          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);
          246         return 0;
          247 }
          248 
          249 int
          250 hsettype(HConnect *c, char *type)
          251 {
          252         Hio *hout;
          253         int r;
          254 
          255         r = preq(c);
          256         if(r < 0)
          257                 return r;
          258 
          259         hout = &c->hout;
          260         if(c->req.vermaj){
          261                 hokheaders(c);
          262                 hprint(hout, "Content-type: %s\r\n", type);
          263                 if(http11(c))
          264                         hprint(hout, "Transfer-Encoding: chunked\r\n");
          265                 hprint(hout, "\r\n");
          266         }
          267 
          268         if(http11(c))
          269                 hxferenc(hout, 1);
          270         else
          271                 c->head.closeit = 1;
          272         return 0;
          273 }
          274 
          275 int
          276 hsethtml(HConnect *c)
          277 {
          278         return hsettype(c, "text/html; charset=utf-8");
          279 }
          280 
          281 int
          282 hsettext(HConnect *c)
          283 {
          284         return hsettype(c, "text/plain; charset=utf-8");
          285 }
          286 
          287 int
          288 hnotfound(HConnect *c)
          289 {
          290         int r;
          291 
          292         r = preq(c);
          293         if(r < 0)
          294                 return r;
          295         return hfail(c, HNotFound, c->req.uri);
          296 }
          297 
          298 static int
          299 xloglist(HConnect *c)
          300 {
          301         if(hsettype(c, "text/html") < 0)
          302                 return -1;
          303         vtloghlist(&c->hout);
          304         hflush(&c->hout);
          305         return 0;
          306 }
          307 
          308 static int
          309 strpcmp(const void *va, const void *vb)
          310 {
          311         return strcmp(*(char**)va, *(char**)vb);
          312 }
          313 
          314 void
          315 vtloghlist(Hio *h)
          316 {
          317         char **p;
          318         int i, n;
          319 
          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");
          324 
          325         p = vtlognames(&n);
          326         qsort(p, n, sizeof(p[0]), strpcmp);
          327         for(i=0; i<n; i++)
          328                 hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]);
          329         vtfree(p);
          330         hprint(h, "</body></html>\n");
          331 }
          332 
          333 void
          334 vtloghdump(Hio *h, VtLog *l)
          335 {
          336         int i;
          337         VtLogChunk *c;
          338         char *name;
          339 
          340         name = l ? l->name : "&lt;nil&gt;";
          341 
          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);
          346 
          347         if(l){
          348                 c = l->w;
          349                 for(i=0; i<l->nchunk; i++){
          350                         if(++c == l->chunk+l->nchunk)
          351                                 c = l->chunk;
          352                         hwrite(h, c->p, c->wp-c->p);
          353                 }
          354         }
          355         hprint(h, "</body></html>\n");
          356 }
          357 
          358 
          359 char*
          360 hargstr(HConnect *c, char *name, char *def)
          361 {
          362         HSPairs *p;
          363 
          364         for(p=c->req.searchpairs; p; p=p->next)
          365                 if(strcmp(p->s, name) == 0)
          366                         return p->t;
          367         return def;
          368 }
          369 
          370 static int
          371 xlog(HConnect *c)
          372 {
          373         char *name;
          374         VtLog *l;
          375 
          376         name = hargstr(c, "log", "");
          377         if(!name[0])
          378                 return xloglist(c);
          379         l = vtlogopen(name, 0);
          380         if(l == nil)
          381                 return hnotfound(c);
          382         if(hsettype(c, "text/html") < 0){
          383                 vtlogclose(l);
          384                 return -1;
          385         }
          386         vtloghdump(&c->hout, l);
          387         vtlogclose(l);
          388         hflush(&c->hout);
          389         return 0;
          390 }
          391 
          392 static void
          393 httpdproc(void *vaddress)
          394 {
          395         HConnect *c;
          396         char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
          397         int ctl, nctl, data;
          398 
          399         address = vaddress;
          400         ctl = announce(address, dir);
          401         if(ctl < 0){
          402                 sysfatal("announce %s: %r", address);
          403                 return;
          404         }
          405 
          406         if(0) print("announce ctl %d dir %s\n", ctl, dir);
          407         for(;;){
          408                 /*
          409                  *  wait for a call (or an error)
          410                  */
          411                 nctl = listen(dir, ndir);
          412                 if(0) print("httpd listen %d %s...\n", nctl, ndir);
          413                 if(nctl < 0){
          414                         fprint(2, "mgr: httpd can't listen on %s: %r\n", address);
          415                         return;
          416                 }
          417 
          418                 data = accept(ctl, ndir);
          419                 if(0) print("httpd accept %d...\n", data);
          420                 if(data < 0){
          421                         fprint(2, "mgr: httpd accept: %r\n");
          422                         close(nctl);
          423                         continue;
          424                 }
          425                 if(0) print("httpd close nctl %d\n", nctl);
          426                 close(nctl);
          427                 c = mkconnect();
          428                 hinit(&c->hin, data, Hread);
          429                 hinit(&c->hout, data, Hwrite);
          430                 vtproc(httpproc, c);
          431         }
          432 }
          433 
          434 static void
          435 httpproc(void *v)
          436 {
          437         HConnect *c;
          438         int ok, i, n;
          439 
          440         c = v;
          441 
          442         for(;;){
          443                 /*
          444                  * No timeout because the signal appears to hit every
          445                  * proc, not just us.
          446                  */
          447                 if(hparsereq(c, 0) < 0)
          448                         break;
          449 
          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);
          455                                 goto found;
          456                         }
          457                 }
          458                 ok = fromwebdir(c);
          459         found:
          460                 hflush(&c->hout);
          461                 if(c->head.closeit)
          462                         ok = -1;
          463                 hreqcleanup(c);
          464 
          465                 if(ok < 0)
          466                         break;
          467         }
          468         hreqcleanup(c);
          469         close(c->hin.fd);
          470         free(c);
          471 }
          472 
          473 static int
          474 httpdobj(char *name, int (*f)(HConnect*))
          475 {
          476         int i;
          477 
          478         if(name == nil || strlen(name) >= ObjNameSize)
          479                 return -1;
          480         for(i = 0; i < MaxObjs; i++){
          481                 if(objs[i].name[0] == '\0'){
          482                         strcpy(objs[i].name, name);
          483                         objs[i].f = f;
          484                         return 0;
          485                 }
          486                 if(strcmp(objs[i].name, name) == 0)
          487                         return -1;
          488         }
          489         return -1;
          490 }
          491 
          492 
          493 struct {
          494         char *ext;
          495         char *type;
          496 } exttab[] = {
          497         ".html",        "text/html",
          498         ".txt",        "text/plain",
          499         ".xml",        "text/xml",
          500         ".png",        "image/png",
          501         ".gif",        "image/gif",
          502         0
          503 };
          504 
          505 static int
          506 fromwebdir(HConnect *c)
          507 {
          508         char buf[4096], *p, *ext, *type;
          509         int i, fd, n, defaulted;
          510         Dir *d;
          511 
          512         if(conf.webroot == nil || strstr(c->req.uri, ".."))
          513                 return hnotfound(c);
          514         snprint(buf, sizeof buf-20, "%s/%s", conf.webroot, c->req.uri+1);
          515         defaulted = 0;
          516 reopen:
          517         if((fd = open(buf, OREAD)) < 0)
          518                 return hnotfound(c);
          519         d = dirfstat(fd);
          520         if(d == nil){
          521                 close(fd);
          522                 return hnotfound(c);
          523         }
          524         if(d->mode&DMDIR){
          525                 if(!defaulted){
          526                         defaulted = 1;
          527                         strcat(buf, "/index.html");
          528                         free(d);
          529                         close(fd);
          530                         goto reopen;
          531                 }
          532                 free(d);
          533                 return hnotfound(c);
          534         }
          535         free(d);
          536         p = buf+strlen(buf);
          537         type = "application/octet-stream";
          538         for(i=0; exttab[i].ext; i++){
          539                 ext = exttab[i].ext;
          540                 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){
          541                         type = exttab[i].type;
          542                         break;
          543                 }
          544         }
          545         if(hsettype(c, type) < 0){
          546                 close(fd);
          547                 return 0;
          548         }
          549         while((n = read(fd, buf, sizeof buf)) > 0)
          550                 if(hwrite(&c->hout, buf, n) < 0)
          551                         break;
          552         close(fd);
          553         hflush(&c->hout);
          554         return 0;
          555 }
          556 
          557 static int
          558 hmanager(HConnect *c)
          559 {
          560         Hio *hout;
          561         int r;
          562         int i, k;
          563         Job *j;
          564         VtLog *l;
          565         VtLogChunk *ch;
          566 
          567         r = hsethtml(c);
          568         if(r < 0)
          569                 return r;
          570 
          571         hout = &c->hout;
          572         hprint(hout, "<html><head><title>venti mgr status</title></head>\n");
          573         hprint(hout, "<body><h2>venti mgr status</h2>\n");
          574 
          575         for(i=0; i<njob; i++) {
          576                 j = &job[i];
          577                 hprint(hout, "<b>");
          578                 if(j->nrun == 0)
          579                         hprint(hout, "----/--/-- --:--:--");
          580                 else
          581                         hprint(hout, "%+T", (long)(j->runstart + time0));
          582                 hprint(hout, " %s", j->name);
          583                 if(j->nrun > 0) {
          584                         if(j->newok == -1) {
          585                                 hprint(hout, " (running)");
          586                         } else if(!j->newok) {
          587                                 hprint(hout, " <font color=\"#cc0000\">(FAILED)</font>");
          588                         }
          589                 }
          590                 hprint(hout, "</b>\n");
          591                 hprint(hout, "<font size=-1><pre>\n");
          592                 l = j->newlog;
          593                 ch = l->w;
          594                 for(k=0; k<l->nchunk; k++){
          595                         if(++ch == l->chunk+l->nchunk)
          596                                 ch = l->chunk;
          597                         hwrite(hout, ch->p, ch->wp-ch->p);
          598                 }
          599                 hprint(hout, "</pre></font>\n");
          600                 hprint(hout, "\n");
          601         }
          602         hprint(hout, "</body></html>\n");
          603         hflush(hout);
          604         return 0;
          605 }
          606 
          607 void
          608 piper(void *v)
          609 {
          610         Job *j;
          611         char buf[512];
          612         VtLog *l;
          613         int n;
          614         int fd;
          615         char *p;
          616         int ok;
          617 
          618         j = v;
          619         fd = j->pipe;
          620         l = j->newlog;
          621         while((n = read(fd, buf, 512-1)) > 0) {
          622                 buf[n] = 0;
          623                 if(l != nil)
          624                         vtlogprint(l, "%s", buf);
          625         }
          626         qlock(&loglk);
          627         p = logtext(l);
          628         ok = j->ok(p);
          629         qunlock(&loglk);
          630         j->newok = ok;
          631         close(fd);
          632 }
          633 
          634 void
          635 kickjob(Job *j)
          636 {
          637         int i;
          638         int fd[3];
          639         int p[2];
          640         VtLog *l;
          641 
          642         if((fd[0] = open("/dev/null", ORDWR)) < 0) {
          643                 vtlogprint(errlog, "%T open /dev/null: %r\n");
          644                 return;
          645         }
          646         if(pipe(p) < 0) {
          647                 vtlogprint(errlog, "%T pipe: %r\n");
          648                 close(fd[0]);
          649                 return;
          650         }
          651         qlock(&j->lk);
          652         l = j->oldlog;
          653         j->oldlog = j->newlog;
          654         j->newlog = l;
          655         qlock(&l->lk);
          656         for(i=0; i<l->nchunk; i++)
          657                 l->chunk[i].wp = l->chunk[i].p;
          658         qunlock(&l->lk);
          659         j->oldok = j->newok;
          660         j->newok = -1;
          661         qunlock(&j->lk);
          662 
          663         fd[1] = p[1];
          664         fd[2] = p[1];
          665         j->pid = threadspawn(fd, j->argv[0], j->argv);
          666         if(j->pid < 0) {
          667                 vtlogprint(errlog, "%T exec %s: %r\n", j->argv[0]);
          668                 close(fd[0]);
          669                 close(fd[1]);
          670                 close(p[0]);
          671         }
          672         // fd[0], fd[1], fd[2] are closed now
          673         j->pipe = p[0];
          674         j->nrun++;
          675         vtproc(piper, j);
          676 }
          677 
          678 int
          679 getline(Resub *text, Resub *line)
          680 {
          681         char *p;
          682 
          683         if(text->sp >= text->ep)
          684                 return -1;
          685         line->sp = text->sp;
          686         p = memchr(text->sp, '\n', text->ep - text->sp);
          687         if(p == nil) {
          688                 line->ep = text->ep;
          689                 text->sp = text->ep;
          690         } else {
          691                 line->ep = p;
          692                 text->sp = p+1;
          693         }
          694         return 0;
          695 }
          696 
          697 int
          698 verifyok(char *output)
          699 {
          700         Resub text, line, m;
          701 
          702         text.sp = output;
          703         text.ep = output+strlen(output);
          704         while(getline(&text, &line) >= 0) {
          705                 *line.ep = 0;
          706                 memset(&m, 0, sizeof m);
          707                 if(!regexec(verifyprog, line.sp, nil, 0))
          708                         return 0;
          709                 *line.ep = '\n';
          710         }
          711         return 1;
          712 }
          713 
          714 int
          715 mirrorok(char *output)
          716 {
          717         Resub text, line, m;
          718 
          719         text.sp = output;
          720         text.ep = output+strlen(output);
          721         while(getline(&text, &line) >= 0) {
          722                 *line.ep = 0;
          723                 memset(&m, 0, sizeof m);
          724                 if(!regexec(mirrorprog, line.sp, nil, 0))
          725                         return 0;
          726                 *line.ep = '\n';
          727         }
          728         return 1;
          729 }
          730 
          731 void
          732 mkjob(Job *j, ...)
          733 {
          734         int i;
          735         char *p;
          736         va_list arg;
          737 
          738         memset(j, 0, sizeof *j);
          739         i = 0;
          740         va_start(arg, j);
          741         while((p = va_arg(arg, char*)) != nil) {
          742                 j->argv[i++] = p;
          743                 if(i >= nelem(j->argv))
          744                         sysfatal("job argv size too small");
          745         }
          746         j->argv[i] = nil;
          747         j->oldlog = vtlogopen(smprint("log%ld.0", j-job), LogSize);
          748         j->newlog = vtlogopen(smprint("log%ld.1", j-job), LogSize);
          749         va_end(arg);
          750 }
          751 
          752 void
          753 manager(void *v)
          754 {
          755         int i;
          756         Job *j;
          757         vlong now;
          758 
          759         USED(v);
          760         for(;; sleep(1000)) {
          761                 for(i=0; i<njob; i++) {
          762                         now = time(0) - time0;
          763                         j = &job[i];
          764                         if(j->pid > 0 || j->newok == -1) {
          765                                 // still running
          766                                 if(now - j->runstart > 2*j->freq) {
          767                                         //TODO: log slow running j
          768                                 }
          769                                 continue;
          770                         }
          771                         if((j->nrun > 0 && now - j->runend > j->freq)
          772                         || (j->nrun == 0 && now > (vlong)(j->offset*j->freq))) {
          773                                 j->runstart = now;
          774                                 j->runend = 0;
          775                                 kickjob(j);
          776                         }
          777                 }
          778         }
          779 }
          780 
          781 void
          782 waitproc(void *v)
          783 {
          784         Channel *c;
          785         Waitmsg *w;
          786         int i;
          787         Job *j;
          788 
          789         c = v;
          790         for(;;) {
          791                 w = recvp(c);
          792                 for(i=0; i<njob; i++) {
          793                         j = &job[i];
          794                         if(j->pid == w->pid) {
          795                                 j->pid = 0;
          796                                 j->runend = time(0) - time0;
          797                                 break;
          798                         }
          799                 }
          800                 free(w);
          801         }
          802 }
          803 
          804 void
          805 threadmain(int argc, char **argv)
          806 {
          807         int i;
          808         int nofork;
          809         char *prog;
          810         Job *j;
          811 
          812         ventilogging = 1;
          813         ventifmtinstall();
          814 #ifdef PLAN9PORT
          815         bin = unsharp("#9/bin/venti");
          816 #else
          817         bin = "/bin/venti";
          818 #endif
          819         nofork = 0;
          820         ARGBEGIN{
          821         case 'b':
          822                 bin = EARGF(usage());
          823                 break;
          824         case 's':
          825                 nofork = 1;
          826                 break;
          827         default:
          828                 usage();
          829         }ARGEND
          830 
          831         if(argc != 1)
          832                 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");
          849 
          850         time0 = time(0);
          851 //        sendmail("startup", "mgr is starting\n");
          852 
          853         logbuf = vtmalloc(LogSize+1);        // +1 for NUL
          854 
          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
          860                 // filter output
          861                 j = &job[njob++];
          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);
          864                 j->ok = mirrorok;
          865                 j->freq = conf.mirrorfreq;        // 4 hours        // TODO: put in config
          866                 j->offset = (double)i/conf.nmirror;
          867         }
          868 
          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
          872                 // filter output
          873                 j = &job[njob++];
          874                 mkjob(j, prog, "-b64M", "-s1000", conf.verify[i], nil);
          875                 j->name = smprint("verify %s", conf.verify[i]);
          876                 j->ok = verifyok;
          877                 j->freq = conf.verifyfreq;
          878                 j->offset = (double)i/conf.nverify;
          879         }
          880 
          881         httpdobj("/mgr", hmanager);
          882         httpdobj("/log", xlog);
          883         vtproc(httpdproc, conf.httpaddr);
          884         vtproc(waitproc, threadwaitchan());
          885         if(nofork)
          886                 manager(nil);
          887         else
          888                 vtproc(manager, nil);
          889 }
          890 
          891 
          892 void
          893 qp(Biobuf *b, char *p)
          894 {
          895         int n, nspace;
          896 
          897         nspace = 0;
          898         n = 0;
          899         for(; *p; p++) {
          900                 if(*p == '\n') {
          901                         if(nspace > 0) {
          902                                 nspace = 0;
          903                                 Bprint(b, "=\n");
          904                         }
          905                         Bputc(b, '\n');
          906                         n = 0;
          907                         continue;
          908                 }
          909                 if(n > 70) {
          910                         Bprint(b, "=\n");
          911                         nspace = 0;
          912                         continue;
          913                 }
          914                 if(33 <= *p && *p <= 126 && *p != '=') {
          915                         Bputc(b, *p);
          916                         n++;
          917                         nspace = 0;
          918                         continue;
          919                 }
          920                 if(*p == ' ' || *p == '\t') {
          921                         Bputc(b, *p);
          922                         n++;
          923                         nspace++;
          924                         continue;
          925                 }
          926                 Bprint(b, "=%02X", (uchar)*p);
          927                 n += 3;
          928                 nspace = 0;
          929         }
          930 }
          931 
          932 int
          933 smtpread(Biobuf *b, int code)
          934 {
          935         char *p, *q;
          936         int n;
          937 
          938         while((p = Brdstr(b, '\n', 1)) != nil) {
          939                 n = strtol(p, &q, 10);
          940                 if(n == 0 || q != p+3) {
          941                 error:
          942                         vtlogprint(errlog, "sending mail: %s\n", p);
          943                         free(p);
          944                         return -1;
          945                 }
          946                 if(*q == ' ') {
          947                         if(n == code) {
          948                                 free(p);
          949                                 return 0;
          950                         }
          951                         goto error;
          952                 }
          953                 if(*q != '-') {
          954                         goto error;
          955                 }
          956         }
          957         return -1;
          958 }
          959 
          960 
          961 void
          962 sendmail(char *content, char *subject, char *msg)
          963 {
          964         int fd;
          965         Biobuf *bin, *bout;
          966 
          967         if((fd = dial(conf.smtp, 0, 0, 0)) < 0) {
          968                 vtlogprint(errlog, "dial %s: %r\n", conf.smtp);
          969                 return;
          970         }
          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){
          976         error:
          977                 close(fd);
          978                 Bterm(bin);
          979                 Bterm(bout);
          980                 return;
          981         }
          982 
          983         Bprint(bout, "HELO venti-mgr\n");
          984         Bflush(bout);
          985         if(smtpread(bin, 250) < 0)
          986                 goto error;
          987 
          988         Bprint(bout, "MAIL FROM:<%s>\n", conf.mailfrom);
          989         Bflush(bout);
          990         if(smtpread(bin, 250) < 0)
          991                 goto error;
          992 
          993         Bprint(bout, "RCPT TO:<%s>\n", conf.mailfrom);
          994         Bflush(bout);
          995         if(smtpread(bin, 250) < 0)
          996                 goto error;
          997 
          998         Bprint(bout, "DATA\n");
          999         Bflush(bout);
         1000         if(smtpread(bin, 354) < 0)
         1001                 goto error;
         1002 
         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());
         1010         Bprint(bout, "\n");
         1011         qp(bout, msg);
         1012         Bprint(bout, ".\n");
         1013         Bflush(bout);
         1014         if(smtpread(bin, 250) < 0)
         1015                 goto error;
         1016 
         1017         Bprint(bout, "QUIT\n");
         1018         Bflush(bout);
         1019         Bterm(bin);
         1020         Bterm(bout);
         1021         close(fd);
         1022 }