1022 lines
18 KiB
C
1022 lines
18 KiB
C
/*
|
|
* mirror manager.
|
|
* a work in progress.
|
|
* use at your own risk.
|
|
*/
|
|
|
|
#include "stdinc.h"
|
|
#include <regexp.h>
|
|
#include <bio.h>
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
|
|
#ifdef PLAN9PORT
|
|
#define sp s.sp
|
|
#define ep e.ep
|
|
#endif
|
|
|
|
void sendmail(char *content, char *subject, char *msg);
|
|
#define TIME "[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+"
|
|
|
|
char *mirrorregexp =
|
|
"^" TIME " ("
|
|
"([^ ]+ \\([0-9,]+-[0-9,]+\\))"
|
|
"|( copy [0-9,]+-[0-9,]+ (data|hole|directory|tail))"
|
|
"|( sha1 [0-9,]+-[0-9,]+)"
|
|
"|([^ ]+: [0-9,]+ used mirrored)"
|
|
"|([^ \\-]+-[^ \\-]+( mirrored| sealed| empty)+)"
|
|
")$";
|
|
Reprog *mirrorprog;
|
|
|
|
char *verifyregexp =
|
|
"^" TIME " ("
|
|
"([^ ]+: unsealed [0-9,]+ bytes)"
|
|
")$";
|
|
Reprog *verifyprog;
|
|
|
|
#undef pipe
|
|
enum
|
|
{
|
|
LogSize = 4*1024*1024 // TODO: make smaller
|
|
};
|
|
|
|
VtLog *errlog;
|
|
|
|
typedef struct Mirror Mirror;
|
|
struct Mirror
|
|
{
|
|
char *src;
|
|
char *dst;
|
|
};
|
|
|
|
typedef struct Conf Conf;
|
|
struct Conf
|
|
{
|
|
Mirror *mirror;
|
|
int nmirror;
|
|
char **verify;
|
|
int nverify;
|
|
char *httpaddr;
|
|
char *webroot;
|
|
char *smtp;
|
|
char *mailfrom;
|
|
char *mailto;
|
|
int mirrorfreq;
|
|
int verifyfreq;
|
|
};
|
|
|
|
typedef struct Job Job;
|
|
struct Job
|
|
{
|
|
char *name;
|
|
QLock lk;
|
|
char *argv[10];
|
|
int oldok;
|
|
int newok;
|
|
VtLog *oldlog;
|
|
VtLog *newlog;
|
|
int pid;
|
|
int pipe;
|
|
int nrun;
|
|
vlong freq;
|
|
vlong runstart;
|
|
vlong runend;
|
|
double offset;
|
|
int (*ok)(char*);
|
|
};
|
|
|
|
Job *job;
|
|
int njob;
|
|
char *bin;
|
|
|
|
vlong time0;
|
|
Conf conf;
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: mgr [-s] [-b bin/venti/] venti.conf\n");
|
|
threadexitsall(0);
|
|
}
|
|
|
|
int
|
|
rdconf(char *file, Conf *conf)
|
|
{
|
|
char *s, *line, *flds[10];
|
|
int i, ok;
|
|
IFile f;
|
|
|
|
if(readifile(&f, file) < 0)
|
|
return -1;
|
|
memset(conf, 0, sizeof *conf);
|
|
ok = -1;
|
|
line = nil;
|
|
for(;;){
|
|
s = ifileline(&f);
|
|
if(s == nil){
|
|
ok = 0;
|
|
break;
|
|
}
|
|
line = estrdup(s);
|
|
i = getfields(s, flds, nelem(flds), 1, " \t\r");
|
|
if(i <= 0 || strcmp(flds[0], "mgr") != 0) {
|
|
/* do nothing */
|
|
}else if(i == 4 && strcmp(flds[1], "mirror") == 0) {
|
|
if(conf->nmirror%64 == 0)
|
|
conf->mirror = vtrealloc(conf->mirror, (conf->nmirror+64)*sizeof(conf->mirror[0]));
|
|
conf->mirror[conf->nmirror].src = vtstrdup(flds[2]);
|
|
conf->mirror[conf->nmirror].dst = vtstrdup(flds[3]);
|
|
conf->nmirror++;
|
|
}else if(i == 3 && strcmp(flds[1], "mirrorfreq") == 0) {
|
|
conf->mirrorfreq = atoi(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "verify") == 0) {
|
|
if(conf->nverify%64 == 0)
|
|
conf->verify = vtrealloc(conf->verify, (conf->nverify+64)*sizeof(conf->verify[0]));
|
|
conf->verify[conf->nverify++] = vtstrdup(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "verifyfreq") == 0) {
|
|
conf->verifyfreq = atoi(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "httpaddr") == 0){
|
|
if(conf->httpaddr){
|
|
seterr(EAdmin, "duplicate httpaddr lines in configuration file %s", file);
|
|
break;
|
|
}
|
|
conf->httpaddr = estrdup(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "webroot") == 0){
|
|
if(conf->webroot){
|
|
seterr(EAdmin, "duplicate webroot lines in configuration file %s", file);
|
|
break;
|
|
}
|
|
conf->webroot = estrdup(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "smtp") == 0) {
|
|
if(conf->smtp){
|
|
seterr(EAdmin, "duplicate smtp lines in configuration file %s", file);
|
|
break;
|
|
}
|
|
conf->smtp = estrdup(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "mailfrom") == 0) {
|
|
if(conf->mailfrom){
|
|
seterr(EAdmin, "duplicate mailfrom lines in configuration file %s", file);
|
|
break;
|
|
}
|
|
conf->mailfrom = estrdup(flds[2]);
|
|
}else if(i == 3 && strcmp(flds[1], "mailto") == 0) {
|
|
if(conf->mailto){
|
|
seterr(EAdmin, "duplicate mailto lines in configuration file %s", file);
|
|
break;
|
|
}
|
|
conf->mailto = estrdup(flds[2]);
|
|
}else{
|
|
seterr(EAdmin, "illegal line '%s' in configuration file %s", line, file);
|
|
break;
|
|
}
|
|
free(line);
|
|
line = nil;
|
|
}
|
|
free(line);
|
|
freeifile(&f);
|
|
return ok;
|
|
}
|
|
|
|
static QLock loglk;
|
|
static char *logbuf;
|
|
|
|
char*
|
|
logtext(VtLog *l)
|
|
{
|
|
int i;
|
|
char *p;
|
|
VtLogChunk *c;
|
|
|
|
p = logbuf;
|
|
c = l->w;
|
|
for(i=0; i<l->nchunk; i++) {
|
|
if(++c == l->chunk+l->nchunk)
|
|
c = l->chunk;
|
|
memmove(p, c->p, c->wp - c->p);
|
|
p += c->wp - c->p;
|
|
}
|
|
*p = 0;
|
|
return logbuf;
|
|
}
|
|
|
|
|
|
typedef struct HttpObj HttpObj;
|
|
|
|
static int fromwebdir(HConnect*);
|
|
|
|
enum
|
|
{
|
|
ObjNameSize = 64,
|
|
MaxObjs = 64
|
|
};
|
|
|
|
struct HttpObj
|
|
{
|
|
char name[ObjNameSize];
|
|
int (*f)(HConnect*);
|
|
};
|
|
|
|
static HttpObj objs[MaxObjs];
|
|
static void httpproc(void*);
|
|
|
|
static HConnect*
|
|
mkconnect(void)
|
|
{
|
|
HConnect *c;
|
|
|
|
c = mallocz(sizeof(HConnect), 1);
|
|
if(c == nil)
|
|
sysfatal("out of memory");
|
|
c->replog = nil;
|
|
c->hpos = c->header;
|
|
c->hstop = c->header;
|
|
return c;
|
|
}
|
|
|
|
static int
|
|
preq(HConnect *c)
|
|
{
|
|
if(hparseheaders(c, 0) < 0)
|
|
return -1;
|
|
if(strcmp(c->req.meth, "GET") != 0
|
|
&& strcmp(c->req.meth, "HEAD") != 0)
|
|
return hunallowed(c, "GET, HEAD");
|
|
if(c->head.expectother || c->head.expectcont)
|
|
return hfail(c, HExpectFail, nil);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
hsettype(HConnect *c, char *type)
|
|
{
|
|
Hio *hout;
|
|
int r;
|
|
|
|
r = preq(c);
|
|
if(r < 0)
|
|
return r;
|
|
|
|
hout = &c->hout;
|
|
if(c->req.vermaj){
|
|
hokheaders(c);
|
|
hprint(hout, "Content-type: %s\r\n", type);
|
|
if(http11(c))
|
|
hprint(hout, "Transfer-Encoding: chunked\r\n");
|
|
hprint(hout, "\r\n");
|
|
}
|
|
|
|
if(http11(c))
|
|
hxferenc(hout, 1);
|
|
else
|
|
c->head.closeit = 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
hsethtml(HConnect *c)
|
|
{
|
|
return hsettype(c, "text/html; charset=utf-8");
|
|
}
|
|
|
|
int
|
|
hsettext(HConnect *c)
|
|
{
|
|
return hsettype(c, "text/plain; charset=utf-8");
|
|
}
|
|
|
|
int
|
|
hnotfound(HConnect *c)
|
|
{
|
|
int r;
|
|
|
|
r = preq(c);
|
|
if(r < 0)
|
|
return r;
|
|
return hfail(c, HNotFound, c->req.uri);
|
|
}
|
|
|
|
static int
|
|
xloglist(HConnect *c)
|
|
{
|
|
if(hsettype(c, "text/html") < 0)
|
|
return -1;
|
|
vtloghlist(&c->hout);
|
|
hflush(&c->hout);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
strpcmp(const void *va, const void *vb)
|
|
{
|
|
return strcmp(*(char**)va, *(char**)vb);
|
|
}
|
|
|
|
void
|
|
vtloghlist(Hio *h)
|
|
{
|
|
char **p;
|
|
int i, n;
|
|
|
|
hprint(h, "<html><head>\n");
|
|
hprint(h, "<title>Venti Server Logs</title>\n");
|
|
hprint(h, "</head><body>\n");
|
|
hprint(h, "<b>Venti Server Logs</b>\n<p>\n");
|
|
|
|
p = vtlognames(&n);
|
|
qsort(p, n, sizeof(p[0]), strpcmp);
|
|
for(i=0; i<n; i++)
|
|
hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]);
|
|
vtfree(p);
|
|
hprint(h, "</body></html>\n");
|
|
}
|
|
|
|
void
|
|
vtloghdump(Hio *h, VtLog *l)
|
|
{
|
|
int i;
|
|
VtLogChunk *c;
|
|
char *name;
|
|
|
|
name = l ? l->name : "<nil>";
|
|
|
|
hprint(h, "<html><head>\n");
|
|
hprint(h, "<title>Venti Server Log: %s</title>\n", name);
|
|
hprint(h, "</head><body>\n");
|
|
hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name);
|
|
|
|
if(l){
|
|
c = l->w;
|
|
for(i=0; i<l->nchunk; i++){
|
|
if(++c == l->chunk+l->nchunk)
|
|
c = l->chunk;
|
|
hwrite(h, c->p, c->wp-c->p);
|
|
}
|
|
}
|
|
hprint(h, "</body></html>\n");
|
|
}
|
|
|
|
|
|
char*
|
|
hargstr(HConnect *c, char *name, char *def)
|
|
{
|
|
HSPairs *p;
|
|
|
|
for(p=c->req.searchpairs; p; p=p->next)
|
|
if(strcmp(p->s, name) == 0)
|
|
return p->t;
|
|
return def;
|
|
}
|
|
|
|
static int
|
|
xlog(HConnect *c)
|
|
{
|
|
char *name;
|
|
VtLog *l;
|
|
|
|
name = hargstr(c, "log", "");
|
|
if(!name[0])
|
|
return xloglist(c);
|
|
l = vtlogopen(name, 0);
|
|
if(l == nil)
|
|
return hnotfound(c);
|
|
if(hsettype(c, "text/html") < 0){
|
|
vtlogclose(l);
|
|
return -1;
|
|
}
|
|
vtloghdump(&c->hout, l);
|
|
vtlogclose(l);
|
|
hflush(&c->hout);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
httpdproc(void *vaddress)
|
|
{
|
|
HConnect *c;
|
|
char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
|
|
int ctl, nctl, data;
|
|
|
|
address = vaddress;
|
|
ctl = announce(address, dir);
|
|
if(ctl < 0){
|
|
sysfatal("announce %s: %r", address);
|
|
return;
|
|
}
|
|
|
|
if(0) print("announce ctl %d dir %s\n", ctl, dir);
|
|
for(;;){
|
|
/*
|
|
* wait for a call (or an error)
|
|
*/
|
|
nctl = listen(dir, ndir);
|
|
if(0) print("httpd listen %d %s...\n", nctl, ndir);
|
|
if(nctl < 0){
|
|
fprint(2, "mgr: httpd can't listen on %s: %r\n", address);
|
|
return;
|
|
}
|
|
|
|
data = accept(ctl, ndir);
|
|
if(0) print("httpd accept %d...\n", data);
|
|
if(data < 0){
|
|
fprint(2, "mgr: httpd accept: %r\n");
|
|
close(nctl);
|
|
continue;
|
|
}
|
|
if(0) print("httpd close nctl %d\n", nctl);
|
|
close(nctl);
|
|
c = mkconnect();
|
|
hinit(&c->hin, data, Hread);
|
|
hinit(&c->hout, data, Hwrite);
|
|
vtproc(httpproc, c);
|
|
}
|
|
}
|
|
|
|
static void
|
|
httpproc(void *v)
|
|
{
|
|
HConnect *c;
|
|
int ok, i, n;
|
|
|
|
c = v;
|
|
|
|
for(;;){
|
|
/*
|
|
* No timeout because the signal appears to hit every
|
|
* proc, not just us.
|
|
*/
|
|
if(hparsereq(c, 0) < 0)
|
|
break;
|
|
|
|
for(i = 0; i < MaxObjs && objs[i].name[0]; i++){
|
|
n = strlen(objs[i].name);
|
|
if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0)
|
|
|| (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){
|
|
ok = (*objs[i].f)(c);
|
|
goto found;
|
|
}
|
|
}
|
|
ok = fromwebdir(c);
|
|
found:
|
|
hflush(&c->hout);
|
|
if(c->head.closeit)
|
|
ok = -1;
|
|
hreqcleanup(c);
|
|
|
|
if(ok < 0)
|
|
break;
|
|
}
|
|
hreqcleanup(c);
|
|
close(c->hin.fd);
|
|
free(c);
|
|
}
|
|
|
|
static int
|
|
httpdobj(char *name, int (*f)(HConnect*))
|
|
{
|
|
int i;
|
|
|
|
if(name == nil || strlen(name) >= ObjNameSize)
|
|
return -1;
|
|
for(i = 0; i < MaxObjs; i++){
|
|
if(objs[i].name[0] == '\0'){
|
|
strcpy(objs[i].name, name);
|
|
objs[i].f = f;
|
|
return 0;
|
|
}
|
|
if(strcmp(objs[i].name, name) == 0)
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
struct {
|
|
char *ext;
|
|
char *type;
|
|
} exttab[] = {
|
|
".html", "text/html",
|
|
".txt", "text/plain",
|
|
".xml", "text/xml",
|
|
".png", "image/png",
|
|
".gif", "image/gif",
|
|
0
|
|
};
|
|
|
|
static int
|
|
fromwebdir(HConnect *c)
|
|
{
|
|
char buf[4096], *p, *ext, *type;
|
|
int i, fd, n, defaulted;
|
|
Dir *d;
|
|
|
|
if(conf.webroot == nil || strstr(c->req.uri, ".."))
|
|
return hnotfound(c);
|
|
snprint(buf, sizeof buf-20, "%s/%s", conf.webroot, c->req.uri+1);
|
|
defaulted = 0;
|
|
reopen:
|
|
if((fd = open(buf, OREAD)) < 0)
|
|
return hnotfound(c);
|
|
d = dirfstat(fd);
|
|
if(d == nil){
|
|
close(fd);
|
|
return hnotfound(c);
|
|
}
|
|
if(d->mode&DMDIR){
|
|
if(!defaulted){
|
|
defaulted = 1;
|
|
strcat(buf, "/index.html");
|
|
free(d);
|
|
close(fd);
|
|
goto reopen;
|
|
}
|
|
free(d);
|
|
return hnotfound(c);
|
|
}
|
|
free(d);
|
|
p = buf+strlen(buf);
|
|
type = "application/octet-stream";
|
|
for(i=0; exttab[i].ext; i++){
|
|
ext = exttab[i].ext;
|
|
if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){
|
|
type = exttab[i].type;
|
|
break;
|
|
}
|
|
}
|
|
if(hsettype(c, type) < 0){
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
while((n = read(fd, buf, sizeof buf)) > 0)
|
|
if(hwrite(&c->hout, buf, n) < 0)
|
|
break;
|
|
close(fd);
|
|
hflush(&c->hout);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hmanager(HConnect *c)
|
|
{
|
|
Hio *hout;
|
|
int r;
|
|
int i, k;
|
|
Job *j;
|
|
VtLog *l;
|
|
VtLogChunk *ch;
|
|
|
|
r = hsethtml(c);
|
|
if(r < 0)
|
|
return r;
|
|
|
|
hout = &c->hout;
|
|
hprint(hout, "<html><head><title>venti mgr status</title></head>\n");
|
|
hprint(hout, "<body><h2>venti mgr status</h2>\n");
|
|
|
|
for(i=0; i<njob; i++) {
|
|
j = &job[i];
|
|
hprint(hout, "<b>");
|
|
if(j->nrun == 0)
|
|
hprint(hout, "----/--/-- --:--:--");
|
|
else
|
|
hprint(hout, "%+T", (long)(j->runstart + time0));
|
|
hprint(hout, " %s", j->name);
|
|
if(j->nrun > 0) {
|
|
if(j->newok == -1) {
|
|
hprint(hout, " (running)");
|
|
} else if(!j->newok) {
|
|
hprint(hout, " <font color=\"#cc0000\">(FAILED)</font>");
|
|
}
|
|
}
|
|
hprint(hout, "</b>\n");
|
|
hprint(hout, "<font size=-1><pre>\n");
|
|
l = j->newlog;
|
|
ch = l->w;
|
|
for(k=0; k<l->nchunk; k++){
|
|
if(++ch == l->chunk+l->nchunk)
|
|
ch = l->chunk;
|
|
hwrite(hout, ch->p, ch->wp-ch->p);
|
|
}
|
|
hprint(hout, "</pre></font>\n");
|
|
hprint(hout, "\n");
|
|
}
|
|
hprint(hout, "</body></html>\n");
|
|
hflush(hout);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
piper(void *v)
|
|
{
|
|
Job *j;
|
|
char buf[512];
|
|
VtLog *l;
|
|
int n;
|
|
int fd;
|
|
char *p;
|
|
int ok;
|
|
|
|
j = v;
|
|
fd = j->pipe;
|
|
l = j->newlog;
|
|
while((n = read(fd, buf, 512-1)) > 0) {
|
|
buf[n] = 0;
|
|
if(l != nil)
|
|
vtlogprint(l, "%s", buf);
|
|
}
|
|
qlock(&loglk);
|
|
p = logtext(l);
|
|
ok = j->ok(p);
|
|
qunlock(&loglk);
|
|
j->newok = ok;
|
|
close(fd);
|
|
}
|
|
|
|
void
|
|
kickjob(Job *j)
|
|
{
|
|
int i;
|
|
int fd[3];
|
|
int p[2];
|
|
VtLog *l;
|
|
|
|
if((fd[0] = open("/dev/null", ORDWR)) < 0) {
|
|
vtlogprint(errlog, "%T open /dev/null: %r\n");
|
|
return;
|
|
}
|
|
if(pipe(p) < 0) {
|
|
vtlogprint(errlog, "%T pipe: %r\n");
|
|
close(fd[0]);
|
|
return;
|
|
}
|
|
qlock(&j->lk);
|
|
l = j->oldlog;
|
|
j->oldlog = j->newlog;
|
|
j->newlog = l;
|
|
qlock(&l->lk);
|
|
for(i=0; i<l->nchunk; i++)
|
|
l->chunk[i].wp = l->chunk[i].p;
|
|
qunlock(&l->lk);
|
|
j->oldok = j->newok;
|
|
j->newok = -1;
|
|
qunlock(&j->lk);
|
|
|
|
fd[1] = p[1];
|
|
fd[2] = p[1];
|
|
j->pid = threadspawn(fd, j->argv[0], j->argv);
|
|
if(j->pid < 0) {
|
|
vtlogprint(errlog, "%T exec %s: %r\n", j->argv[0]);
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
close(p[0]);
|
|
}
|
|
// fd[0], fd[1], fd[2] are closed now
|
|
j->pipe = p[0];
|
|
j->nrun++;
|
|
vtproc(piper, j);
|
|
}
|
|
|
|
int
|
|
getline(Resub *text, Resub *line)
|
|
{
|
|
char *p;
|
|
|
|
if(text->sp >= text->ep)
|
|
return -1;
|
|
line->sp = text->sp;
|
|
p = memchr(text->sp, '\n', text->ep - text->sp);
|
|
if(p == nil) {
|
|
line->ep = text->ep;
|
|
text->sp = text->ep;
|
|
} else {
|
|
line->ep = p;
|
|
text->sp = p+1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
verifyok(char *output)
|
|
{
|
|
Resub text, line, m;
|
|
|
|
text.sp = output;
|
|
text.ep = output+strlen(output);
|
|
while(getline(&text, &line) >= 0) {
|
|
*line.ep = 0;
|
|
memset(&m, 0, sizeof m);
|
|
if(!regexec(verifyprog, line.sp, nil, 0))
|
|
return 0;
|
|
*line.ep = '\n';
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
mirrorok(char *output)
|
|
{
|
|
Resub text, line, m;
|
|
|
|
text.sp = output;
|
|
text.ep = output+strlen(output);
|
|
while(getline(&text, &line) >= 0) {
|
|
*line.ep = 0;
|
|
memset(&m, 0, sizeof m);
|
|
if(!regexec(mirrorprog, line.sp, nil, 0))
|
|
return 0;
|
|
*line.ep = '\n';
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
mkjob(Job *j, ...)
|
|
{
|
|
int i;
|
|
char *p;
|
|
va_list arg;
|
|
|
|
memset(j, 0, sizeof *j);
|
|
i = 0;
|
|
va_start(arg, j);
|
|
while((p = va_arg(arg, char*)) != nil) {
|
|
j->argv[i++] = p;
|
|
if(i >= nelem(j->argv))
|
|
sysfatal("job argv size too small");
|
|
}
|
|
j->argv[i] = nil;
|
|
j->oldlog = vtlogopen(smprint("log%ld.0", j-job), LogSize);
|
|
j->newlog = vtlogopen(smprint("log%ld.1", j-job), LogSize);
|
|
va_end(arg);
|
|
}
|
|
|
|
void
|
|
manager(void *v)
|
|
{
|
|
int i;
|
|
Job *j;
|
|
vlong now;
|
|
|
|
USED(v);
|
|
for(;; sleep(1000)) {
|
|
for(i=0; i<njob; i++) {
|
|
now = time(0) - time0;
|
|
j = &job[i];
|
|
if(j->pid > 0 || j->newok == -1) {
|
|
// still running
|
|
if(now - j->runstart > 2*j->freq) {
|
|
//TODO: log slow running j
|
|
}
|
|
continue;
|
|
}
|
|
if((j->nrun > 0 && now - j->runend > j->freq)
|
|
|| (j->nrun == 0 && now > (vlong)(j->offset*j->freq))) {
|
|
j->runstart = now;
|
|
j->runend = 0;
|
|
kickjob(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
waitproc(void *v)
|
|
{
|
|
Channel *c;
|
|
Waitmsg *w;
|
|
int i;
|
|
Job *j;
|
|
|
|
c = v;
|
|
for(;;) {
|
|
w = recvp(c);
|
|
for(i=0; i<njob; i++) {
|
|
j = &job[i];
|
|
if(j->pid == w->pid) {
|
|
j->pid = 0;
|
|
j->runend = time(0) - time0;
|
|
break;
|
|
}
|
|
}
|
|
free(w);
|
|
}
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char **argv)
|
|
{
|
|
int i;
|
|
int nofork;
|
|
char *prog;
|
|
Job *j;
|
|
|
|
ventilogging = 1;
|
|
ventifmtinstall();
|
|
#ifdef PLAN9PORT
|
|
bin = unsharp("#9/bin/venti");
|
|
#else
|
|
bin = "/bin/venti";
|
|
#endif
|
|
nofork = 0;
|
|
ARGBEGIN{
|
|
case 'b':
|
|
bin = EARGF(usage());
|
|
break;
|
|
case 's':
|
|
nofork = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
if(argc != 1)
|
|
usage();
|
|
if(rdconf(argv[0], &conf) < 0)
|
|
sysfatal("reading config: %r");
|
|
if(conf.httpaddr == nil)
|
|
sysfatal("config has no httpaddr");
|
|
if(conf.smtp != nil && conf.mailfrom == nil)
|
|
sysfatal("config has smtp but no mailfrom");
|
|
if(conf.smtp != nil && conf.mailto == nil)
|
|
sysfatal("config has smtp but no mailto");
|
|
if((mirrorprog = regcomp(mirrorregexp)) == nil)
|
|
sysfatal("mirrorregexp did not complete");
|
|
if((verifyprog = regcomp(verifyregexp)) == nil)
|
|
sysfatal("verifyregexp did not complete");
|
|
if(conf.nverify > 0 && conf.verifyfreq == 0)
|
|
sysfatal("config has no verifyfreq");
|
|
if(conf.nmirror > 0 && conf.mirrorfreq == 0)
|
|
sysfatal("config has no mirrorfreq");
|
|
|
|
time0 = time(0);
|
|
// sendmail("startup", "mgr is starting\n");
|
|
|
|
logbuf = vtmalloc(LogSize+1); // +1 for NUL
|
|
|
|
errlog = vtlogopen("errors", LogSize);
|
|
job = vtmalloc((conf.nmirror+conf.nverify)*sizeof job[0]);
|
|
prog = smprint("%s/mirrorarenas", bin);
|
|
for(i=0; i<conf.nmirror; i++) {
|
|
// job: /bin/venti/mirrorarenas -v src dst
|
|
// filter output
|
|
j = &job[njob++];
|
|
mkjob(j, prog, "-v", conf.mirror[i].src, conf.mirror[i].dst, nil);
|
|
j->name = smprint("mirror %s %s", conf.mirror[i].src, conf.mirror[i].dst);
|
|
j->ok = mirrorok;
|
|
j->freq = conf.mirrorfreq; // 4 hours // TODO: put in config
|
|
j->offset = (double)i/conf.nmirror;
|
|
}
|
|
|
|
prog = smprint("%s/verifyarena", bin);
|
|
for(i=0; i<conf.nverify; i++) {
|
|
// job: /bin/venti/verifyarena -b 64M -s 1000 -v arena
|
|
// filter output
|
|
j = &job[njob++];
|
|
mkjob(j, prog, "-b64M", "-s1000", conf.verify[i], nil);
|
|
j->name = smprint("verify %s", conf.verify[i]);
|
|
j->ok = verifyok;
|
|
j->freq = conf.verifyfreq;
|
|
j->offset = (double)i/conf.nverify;
|
|
}
|
|
|
|
httpdobj("/mgr", hmanager);
|
|
httpdobj("/log", xlog);
|
|
vtproc(httpdproc, conf.httpaddr);
|
|
vtproc(waitproc, threadwaitchan());
|
|
if(nofork)
|
|
manager(nil);
|
|
else
|
|
vtproc(manager, nil);
|
|
}
|
|
|
|
|
|
void
|
|
qp(Biobuf *b, char *p)
|
|
{
|
|
int n, nspace;
|
|
|
|
nspace = 0;
|
|
n = 0;
|
|
for(; *p; p++) {
|
|
if(*p == '\n') {
|
|
if(nspace > 0) {
|
|
nspace = 0;
|
|
Bprint(b, "=\n");
|
|
}
|
|
Bputc(b, '\n');
|
|
n = 0;
|
|
continue;
|
|
}
|
|
if(n > 70) {
|
|
Bprint(b, "=\n");
|
|
nspace = 0;
|
|
continue;
|
|
}
|
|
if(33 <= *p && *p <= 126 && *p != '=') {
|
|
Bputc(b, *p);
|
|
n++;
|
|
nspace = 0;
|
|
continue;
|
|
}
|
|
if(*p == ' ' || *p == '\t') {
|
|
Bputc(b, *p);
|
|
n++;
|
|
nspace++;
|
|
continue;
|
|
}
|
|
Bprint(b, "=%02X", (uchar)*p);
|
|
n += 3;
|
|
nspace = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
smtpread(Biobuf *b, int code)
|
|
{
|
|
char *p, *q;
|
|
int n;
|
|
|
|
while((p = Brdstr(b, '\n', 1)) != nil) {
|
|
n = strtol(p, &q, 10);
|
|
if(n == 0 || q != p+3) {
|
|
error:
|
|
vtlogprint(errlog, "sending mail: %s\n", p);
|
|
free(p);
|
|
return -1;
|
|
}
|
|
if(*q == ' ') {
|
|
if(n == code) {
|
|
free(p);
|
|
return 0;
|
|
}
|
|
goto error;
|
|
}
|
|
if(*q != '-') {
|
|
goto error;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void
|
|
sendmail(char *content, char *subject, char *msg)
|
|
{
|
|
int fd;
|
|
Biobuf *bin, *bout;
|
|
|
|
if((fd = dial(conf.smtp, 0, 0, 0)) < 0) {
|
|
vtlogprint(errlog, "dial %s: %r\n", conf.smtp);
|
|
return;
|
|
}
|
|
bin = vtmalloc(sizeof *bin);
|
|
bout = vtmalloc(sizeof *bout);
|
|
Binit(bin, fd, OREAD);
|
|
Binit(bout, fd, OWRITE);
|
|
if(smtpread(bin, 220) < 0){
|
|
error:
|
|
close(fd);
|
|
Bterm(bin);
|
|
Bterm(bout);
|
|
return;
|
|
}
|
|
|
|
Bprint(bout, "HELO venti-mgr\n");
|
|
Bflush(bout);
|
|
if(smtpread(bin, 250) < 0)
|
|
goto error;
|
|
|
|
Bprint(bout, "MAIL FROM:<%s>\n", conf.mailfrom);
|
|
Bflush(bout);
|
|
if(smtpread(bin, 250) < 0)
|
|
goto error;
|
|
|
|
Bprint(bout, "RCPT TO:<%s>\n", conf.mailfrom);
|
|
Bflush(bout);
|
|
if(smtpread(bin, 250) < 0)
|
|
goto error;
|
|
|
|
Bprint(bout, "DATA\n");
|
|
Bflush(bout);
|
|
if(smtpread(bin, 354) < 0)
|
|
goto error;
|
|
|
|
Bprint(bout, "From: \"venti mgr\" <%s>\n", conf.mailfrom);
|
|
Bprint(bout, "To: <%s>\n", conf.mailto);
|
|
Bprint(bout, "Subject: %s\n", subject);
|
|
Bprint(bout, "MIME-Version: 1.0\n");
|
|
Bprint(bout, "Content-Type: %s; charset=\"UTF-8\"\n", content);
|
|
Bprint(bout, "Content-Transfer-Encoding: quoted-printable\n");
|
|
Bprint(bout, "Message-ID: %08lux%08lux@venti.swtch.com\n", fastrand(), fastrand());
|
|
Bprint(bout, "\n");
|
|
qp(bout, msg);
|
|
Bprint(bout, ".\n");
|
|
Bflush(bout);
|
|
if(smtpread(bin, 250) < 0)
|
|
goto error;
|
|
|
|
Bprint(bout, "QUIT\n");
|
|
Bflush(bout);
|
|
Bterm(bin);
|
|
Bterm(bout);
|
|
close(fd);
|
|
}
|