summaryrefslogtreecommitdiff
path: root/svc/src/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'svc/src/fs.c')
-rw-r--r--svc/src/fs.c241
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);
+}