diff options
| author | Calvin <calvinm@kissingerassoc.com> | 2026-08-16 20:52:12 -0400 |
|---|---|---|
| committer | Calvin <calvinm@kissingerassoc.com> | 2026-08-16 20:52:12 -0400 |
| commit | 147b0ca1e4e00f66abb47fff3c543b853033f609 (patch) | |
| tree | a388172fc16723c139deff1e45a74c3d1c567cc0 /svc/src/fs.c | |
Initial commit: svc supervisor, design docs, session transcript
Existing work moved from /storage/vms/9front/svc, previously unversioned.
Object files and linked binaries excluded via .gitignore.
Diffstat (limited to 'svc/src/fs.c')
| -rw-r--r-- | svc/src/fs.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/svc/src/fs.c b/svc/src/fs.c new file mode 100644 index 0000000..63150fb --- /dev/null +++ b/svc/src/fs.c @@ -0,0 +1,241 @@ +/* + * 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 <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#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 <name> start a service\n" + "stop <name> stop a service\n" + "restart <name> 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 <service>, 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); +} |
