/* * svcfs - the control filesystem init serves. * * This runs in a proc of its own, sharing memory with the supervisor, * so everything it touches is under statelock. It never starts or * stops anything itself: a service has to be a child of the supervisor * for the supervisor to wait for it, so a write here is validated now * and queued for the supervisor to carry out on its next tick. */ #include #include #include #include #include <9p.h> #include "dat.h" enum { Qctl, Qstatus, Qargs, Qpid, Qlog, }; typedef struct Fref Fref; struct Fref { int type; Svc *svc; /* nil for the root ctl and status */ }; static char *rootctltext = "start start a service\n" "stop stop a service\n" "restart stop it, then start it\n" "reset stop every service, then start them again\n"; static char *svcctltext = "start\n" "stop\n" "restart\n"; static Fref* fref(int type, Svc *s) { Fref *f; f = mallocz(sizeof *f, 1); if(f == nil) sysfatal("out of memory"); f->type = type; f->svc = s; return f; } static void fsread(Req *r) { Fref *f; char *s; char buf[64]; int fd, n; char path[512]; f = r->fid->file->aux; if(f == nil){ respond(r, "not a readable file"); return; } switch(f->type){ case Qctl: readstr(r, f->svc == nil ? rootctltext : svcctltext); break; case Qstatus: qlock(&statelock); s = f->svc == nil ? allstatus() : statusline(f->svc); qunlock(&statelock); if(s == nil){ respond(r, "out of memory"); return; } readstr(r, s); free(s); break; case Qargs: qlock(&statelock); s = smprint("%s", f->svc->exec); qunlock(&statelock); if(s == nil){ respond(r, "out of memory"); return; } readstr(r, s); free(s); break; case Qpid: qlock(&statelock); if(f->svc->pid == 0) buf[0] = '\0'; else snprint(buf, sizeof buf, "%d\n", f->svc->pid); qunlock(&statelock); readstr(r, buf); break; case Qlog: snprint(path, sizeof path, "%s/%s", logdir, f->svc->name); fd = open(path, OREAD); if(fd < 0){ /* no output yet is not an error */ r->ofcall.count = 0; break; } seek(fd, r->ifcall.offset, 0); n = read(fd, r->ofcall.data, r->ifcall.count); close(fd); if(n < 0){ responderror(r); return; } r->ofcall.count = n; break; default: respond(r, "not a readable file"); return; } respond(r, nil); } static void fswrite(Req *r) { Fref *f; char *buf, *err; char *fld[3]; int nf; f = r->fid->file->aux; if(f == nil || f->type != Qctl){ respond(r, "not a writable file"); return; } buf = mallocz(r->ifcall.count+1, 1); if(buf == nil){ respond(r, "out of memory"); return; } memmove(buf, r->ifcall.data, r->ifcall.count); buf[r->ifcall.count] = '\0'; nf = tokenize(buf, fld, nelem(fld)); if(nf == 0){ free(buf); respond(r, "no command"); return; } if(f->svc != nil){ /* * A per-service ctl takes no name: which file you wrote to * already said which service you meant. */ if(nf != 1){ free(buf); respond(r, "this ctl takes no arguments; " "try start, stop or restart"); return; } err = queuecmd(fld[0], f->svc->name); }else if(strcmp(fld[0], "reset") == 0) err = queuecmd("reset", nil); else if(nf != 2){ free(buf); respond(r, "usage: start, stop or restart , or reset"); return; }else err = queuecmd(fld[0], fld[1]); free(buf); if(err != nil){ respond(r, err); return; } r->ofcall.count = r->ifcall.count; respond(r, nil); } static Srv fs = { .read= fsread, .write= fswrite, }; void startfs(char *srvname, char *mtpt) { Svc *s; File *d; Tree *t; char *me; /* * Files belong to whoever is running us, so that the host owner * can write the ctl files. They are world readable: reading a * ctl file is how you find out what it accepts, which is no use * if only one user may do it. Writing is what wants restricting. */ me = getuser(); if(me == nil) me = "none"; t = alloctree(me, me, DMDIR|0555, nil); if(t == nil) sysfatal("alloctree: %r"); fs.tree = t; createfile(t->root, "ctl", me, 0644, fref(Qctl, nil)); createfile(t->root, "status", me, 0444, fref(Qstatus, nil)); for(s = svcs; s != nil; s = s->next){ d = createfile(t->root, s->name, me, DMDIR|0555, nil); if(d == nil){ fprint(2, "init: svcfs: create %s: %r\n", s->name); continue; } createfile(d, "ctl", me, 0644, fref(Qctl, s)); createfile(d, "status", me, 0444, fref(Qstatus, s)); createfile(d, "args", me, 0444, fref(Qargs, s)); createfile(d, "pid", me, 0444, fref(Qpid, s)); createfile(d, "log", me, 0444, fref(Qlog, s)); } postmountsrv(&fs, srvname, mtpt, MREPL); }