shithub: svcfs

Download patch

ref: d41d7a57904b372d1be5521f07be41aacb200817
parent: 16e9e2ac9e1fce7ac68b4401ed39893f74f13516
author: Michael Misch <michaelmisch1985@gmail.com>
date: Tue Jun 11 09:36:00 EDT 2024

Use our alternative approach which ends up much cleaner, and avoids necessary namespace awkwardness

--- a/TODO
+++ b/TODO
@@ -1,18 +1,34 @@
-/mnt/altid/clone
-/mnt/altid/stats
-/mnt/altid/[0-9]
-/mnt/altid/[0-9]/feed
-/mnt/altid/[0-9]/...
+ Commands:
+ - markup, will need to import the state machine parser to pre-parse the output into just text
+ - tabs could be a command?
 
-/mnt/altid/service
-/mnt/altid/service/clone
-/mnt/altid/service/[0-9]
+Post a descriptor to /srv for each given service, that a client can connect into
+alt/fs sname, then in that namespace run the service binary 
+It mounts to /mnt/alt, which is what the service reads and writes to, then the client will do the other side
 
-Can read stats for list of servers, etc
-Clone, get your n back
-/mnt/altid/1/ctl, write which service to connect to
-the fs populates with that, you can read in tabs to see what buffers are available
+if(isInitial) could be set to true on spinup, then we attach and flag appropriately; then negate isInitial so we can always assume a client attach after
 
-Commands:
- - markup, will need to import the state machine parser to pre-parse the output into just text
- - tabs could be a command?
\ No newline at end of file
+ - Do a Srv + postmountsrv in main, then Srv + postsrv or listensrv in client.c which is fired off after attach from a service and initialization is complete
+Client
+
+```
+#!/bin/rc
+
+# Example /bin/rc/service/tcp18111
+mount '#s/libera' /mnt/irc
+exec /bin/exportfs -r /mnt/irc
+```
+
+Connects with aname = desired buffer, or gets just whichever is first (usually a `server`, but just in added-order)
+ - if no buffers exist, exit error. We don't support hung states!
+
+Service
+
+`/mnt/alt` is served by the alt/fs
+- service will write the service name to `/mnt/alt/clone`
+	- If a service exists in /srv by that name, an error will be returned. rm the service first!
+	- If the name is not valid filechars, an error will be returned (this is used in /srv, after all)
+- the service reads and writes to the returned fd until it closes, signalling completion
+
+
+Add a stats/info file to /mnt/alt? something anyways.
--- a/alt.h
+++ b/alt.h
@@ -1,16 +1,15 @@
-#define	CLIENTID(c)	((int)(((Client*)(c)) - client))
-
 typedef struct Buffer Buffer;
 typedef struct Notify Notify;
-typedef struct Service Service;
-typedef struct Client Client;
 
 struct Buffer
 {
 	char	title[256];
 	char	status[256];
-	char	feed[256];
 	char	*aside;
+	int	fd;
+	Notify	*notify;
+	// callback function from server for processing input
+	Buffer	*next;
 };
 
 struct Notify
@@ -19,28 +18,6 @@
 	Notify	*next;
 };
 
-struct Service
-{
-	Buffer	*buffer;
-	Notify	*notifications;
-	//input callback function here
-	char	*name;
-	Service	*next;
-};
-
-struct Client
-{
-	Buffer	*current;
-	int	showmarkdown;
-	int	ref;
-};
-
-Client* newclient(void);
-void freeclient(Client*);
-char* clientctl(Client*, char*, char*);
-Service* newservice(void);
-void freeservice(Service*);
-char* servicectl(Service*, char*, char*);
 void* emalloc(int);
 char* estrdup(char*);
 
@@ -47,10 +24,29 @@
 char *mtpt;
 char *srvpt;
 char *user;
-long time0;
 char *logdir;
-Client client[256];
-int nclient;
-Service service[64];
-int nservice;
 int debug;
+
+void clattach(Req*);
+void clstat(Req*);
+char *clwalk1(Fid*, char*, Qid*);
+char *clclone(Fid*, Fid*);
+void clopen(Req*);
+void clread(Req*);
+void clwrite(Req*);
+void clflush(Req*);
+void cldestroyfid(Fid*);
+void clstart(Srv*);
+void clend(Srv*);
+
+void svcattach(Req*);
+void svcstat(Req*);
+char *svcwalk1(Fid*, char*, Qid*);
+char *svcclone(Fid*, Fid*);
+void svcopen(Req*);
+void svcread(Req*);
+void svcwrite(Req*);
+void svcflush(Req*);
+void svcdestroyfid(Fid*);
+void svcstart(Srv*);
+void svcend(Srv*);
--- a/client.c
+++ b/client.c
@@ -6,7 +6,53 @@
 
 #include "alt.h"
 
-Client*
+enum {
+	Qcroot,
+		Qtitle,
+		Qstatus,
+		Qaside,
+		Qfeed,
+		Qinput,
+		Qnotify,
+		Qctl,
+	Qmax,
+};
+
+static char *cltab[] = {
+	"/",
+		"title",
+		"status",
+		"aside",
+		"feed",
+		"input",
+		"notify",
+		"ctl",
+	nil,
+};
+
+typedef struct Clfid Clfid;
+typedef struct Client Client;
+
+struct Clfid
+{
+	int	level;
+	Client	*cl;
+};
+
+struct Client
+{
+	Ref;
+
+	Buffer	*current;
+	int	showmarkdown;
+};
+
+static Client client[256];
+static Buffer *root;
+static int nclient;
+static int time0;
+
+static Client*
 newclient(void)
 {
 	Client *cl;
@@ -28,7 +74,7 @@
 	return cl;
 }
 
-void
+static void
 freeclient(Client *cl)
 {
 	if(cl == nil || cl->ref == 0)
@@ -40,16 +86,180 @@
 	memset(cl, 0, sizeof(*cl));
 }
 
+static void
+clmkqid(Qid *q, int level, void *aux)
+{
+	q->type = 0;
+	q->vers = 0;
+	if(level == Qcroot)
+		q->type = QTDIR;
+	else
+		q->path = (level<<24) | (((uintptr)aux ^ time0) & 0x00ffffff);
+}
+
+static void
+clmkdir(Dir *d, int level, void *aux)
+{
+	memset(d, 0, sizeof(*d));
+	clmkqid(&d->qid, level, aux);
+	d->mode = 0444;
+	d->atime = d->mtime = time(0);
+	d->uid = estrdup(user);
+	d->gid = estrdup(user);
+	d->muid = estrdup(user);
+	if(d->qid.type & QTDIR)
+		d->mode |= DMDIR | 0111;
+	switch(level){
+	case Qctl:
+		d->mode = 0666;
+	default:
+		d->name = estrdup(cltab[level]);
+	}
+}
+
+void
+clattach(Req *r)
+{
+	Clfid *f;
+
+	// TODO: We want to actually use the aname here
+	if(r->ifcall.aname && r->ifcall.aname[0]){
+		print(r->ifcall.aname);
+		// TODO: Check and establish buffer, abort if it's not available
+	} else {
+		// TODO: If we have any valid buffers, we take the top of the list here
+	}
+	f = emalloc(sizeof(*f));
+	f->level = Qcroot;
+	clmkqid(&r->fid->qid, f->level, nil);
+	r->ofcall.qid = r->fid->qid;
+	r->fid->aux = f;
+	respond(r, nil);
+}
+
+
+
+void
+clstat(Req *r)
+{
+	Clfid *f;
+
+	f = r->fid->aux;
+	clmkdir(&r->d, f->level, f->cl);
+	respond(r, nil);
+}
+
+
 char*
-clientctl(Client *cl, char *ctl, char *arg)
+clwalk1(Fid *fid, char *name, Qid *qid)
 {
-	USED(cl);
-	print("Command in: %s\nArgs in: %s\n", ctl, arg);
-	if(strcmp(ctl, "buffer") == 0){
+	Clfid *f;
+	int i;
 
-	} else{
-		// Clients should be polling for commands and input alike
-		
+	if(!(fid->qid.type&QTDIR))
+		return "walk in non-directory";
+
+	f = fid->aux;
+	for(i=f->level+1; i < nelem(cltab); i++){
+		if(cltab[i]){
+			if(strcmp(name, cltab[i]) == 0)
+				break;
+		}
 	}
+	if(i >= nelem(cltab))
+		return "directory entry not found";
+	f->level = i;
+	clmkqid(qid, f->level, f->cl);
+	fid->qid = *qid;
+	return nil;	
+}
+
+char *
+clclone(Fid *oldfid, Fid *newfid)
+{
+	Clfid *f, *o;
+
+	o = oldfid->aux;
+	if(o == nil)
+		return "bad fid";
+	f = emalloc(sizeof(*f));
+	memmove(f, o, sizeof(*f));
+	if(f->cl)
+		incref(f->cl);
+	newfid->aux = f;
 	return nil;
+}
+
+void
+clopen(Req *r)
+{
+	respond(r, nil);
+	// Use feed for fid, with offset as well.
+}
+
+static int
+rootgen(int i, Dir *d, void *aux)
+{
+	i += Qcroot+1;
+	if(i < Qmax){
+		clmkdir(d, i, aux);
+		return 0;
+	}
+	return -1;
+}
+
+void
+clread(Req *r)
+{
+	Clfid *f;
+
+	f = r->fid->aux;
+	switch(f->level){
+	case Qcroot:
+		dirread9p(r, rootgen, f->cl);
+		respond(r, nil);
+		return;
+	//case Qfeed:
+	}
+	respond(r, "not implemented");
+}
+
+void
+clwrite(Req *r)
+{
+	// TODO: Input, ctl
+	respond(r, nil);
+}
+
+void
+clflush(Req *r)
+{
+	respond(r, nil);
+}
+
+void
+cldestroyfid(Fid *fid)
+{
+	Clfid *f;
+
+	if(f = fid->aux){
+		//fid->aux = nil;
+		//if(f->cl)
+		//	freeclient(f->cl);
+	}
+	free(f);
+}
+
+void
+clstart(Srv *s)
+{
+	root = (Buffer*)s->aux;
+	time0 = time(0);
+}
+
+void
+clend(Srv*)
+{
+	postnote(PNGROUP, getpid(), "shutdown");
+	exits(nil);
 }
--- a/fs.c
+++ b/fs.c
@@ -7,60 +7,6 @@
 
 #include "alt.h"
 
-// TODO: Start turning off output if we aren't connected to anything
-
-enum {
-	Qroot,
-		Qclone,
-		Qclients,
-			Qctl,
-			Qtitle,
-			Qstatus,
-			Qfeed,
-			Qaside,
-			Qnotify,
-			Qtabs,
-			Qinput,
-		Qservices,
-			Qsclone,
-			Qservice,
-				Qsctl,
-				Qsinput,
-		Qmax,
-};
-
-static char *nametab[] = {
-	"/",
-		"clone",
-		nil,
-			"ctl",
-			"title",
-			"status",
-			"feed",
-			"aside",
-			"notify",
-			"tabs",
-			"input",
-		"services",
-			"clone",
-			nil,
-				"ctl",
-				"input",
-		nil,
-};
-
-typedef struct Altfid Altfid;
-struct Altfid
-{
-	int	level;
-	Client	*client;
-	Service	*service;
-	int	fd;
-	vlong	foffset;
-};
-
-static char *whitespace = "\t\r\n";
-
 void*
 emalloc(int n)
 {
@@ -79,447 +25,19 @@
 	return s;
 }
 
-static void*
-wfaux(Altfid *f)
+Srv svcfs = 
 {
-	if(f->level < Qclients)
-		return nil;
-	else if(f->level < Qservices)
-		return f->client;
-	return f->service;
-}
-
-static void
-fsmkqid(Qid *q, int level, void *aux)
-{
-
-	q->type = 0;
-	q->vers = 0;
-	switch(level){
-	case Qroot:
-	case Qclients:
-	case Qservices:
-	case Qservice:
-		q->type = QTDIR;
-	default:
-		q->path = (level<<24) | (((uintptr)aux ^ time0) & 0x00ffffff);
-	}
-}
-
-static void
-fsmkdir(Dir *d, int level, void *aux)
-{
-	Service *sv;
-	char buf[1024];
-
-	memset(d, 0, sizeof(*d));
-	fsmkqid(&d->qid, level, aux);
-	d->mode = 0444;
-	d->atime = d->mtime = time0;
-	d->uid = estrdup(user);
-	d->gid = estrdup(user);
-	d->muid = estrdup(user);
-	if(d->qid.type & QTDIR)
-		d->mode |= DMDIR | 0111;
-	switch(level){
-	case Qclients:
-		snprint(buf, sizeof(buf), "%d", CLIENTID(aux));
-		d->name = estrdup(buf);
-		break;
-	case Qservice:
-		sv = (Service*)aux;
-		d->name = sv->name;
-		break;
-	case Qctl:
-	case Qsctl:
-	case Qclone:
-	case Qsclone:
-		d->mode = 0666;
-		if(0){
-	case Qinput:
-		d->mode = 0222;
-		}
-	default:
-		d->name = estrdup(nametab[level]);
-	}
-}
-
-static void
-fsattach(Req *r)
-{
-	Altfid *f;
-
-	if(r->ifcall.aname && r->ifcall.aname[0]){
-		respond(r, "invalid attach specifier");
-		return;
-	}
-	f = emalloc(sizeof(*f));
-	f->level = Qroot;
-	fsmkqid(&r->fid->qid, f->level, wfaux(f));
-	r->ofcall.qid = r->fid->qid;
-	r->fid->aux = f;
-	respond(r, nil);
-}
-
-static void
-fsstat(Req *r)
-{
-	Altfid *f;
-
-	f = r->fid->aux;
-	fsmkdir(&r->d, f->level, wfaux(f));
-	respond(r, nil);
-}
-
-static char*
-fswalk1(Fid *fid, char *name, Qid *qid)
-{
-	Altfid *f;
-	int i, j;
-	i = 0;
-
-	if(!(fid->qid.type&QTDIR))
-		return "walk in non-directory";
-
-	f = fid->aux;
-	if(strcmp(name, "..")==0){
-		switch(f->level){
-		case Qroot:
-			break;
-		case Qclients:
-			freeclient(f->client);
-			f->client = nil;
-			break;
-		case Qservices:
-			f->level = Qroot;
-			break;
-		default:
-			if(f->level > Qservices)
-				f->level = Qservices;
-			else
-				f->level = Qclients;
-		}
-	} else if(strcmp(name, "services")==0){
-		i = Qservices;
-		goto Out;
-	} else {
-		if(nservice){
-			for(j=0; j < nservice; j++){
-				if(strcmp(name, service[j].name) == 0){
-					f->service = &service[j];
-					i = Qservice;
-					break;
-				}
-			}
-		} else {
-			for(i = f->level+1; i < nelem(nametab); i++){
-				if(nametab[i]){
-					if(strcmp(name, nametab[i]) == 0)
-						goto Out;
-					// anything else?
-				}
-				if(i == Qclients){
-					j = atoi(name);
-					if(j >= 0 && j < nclient){
-						f->client = &client[j];
-						incref(f->client);
-						goto Out;
-					}
-				}
-			}
-		}
-Out:
-		if(i >= nelem(nametab))
-			return "directory entry not found";
-		f->level = i;
-	}
-	fsmkqid(qid, f->level, wfaux(f));
-	fid->qid = *qid;
-	return nil;
-}
-
-static char*
-fsclone(Fid *oldfid, Fid *newfid)
-{
-	Altfid *f, *o;
-	o = oldfid->aux;
-	if(o == nil)
-		return "bad fid";
-	f = emalloc(sizeof(*f));
-	memmove(f, o, sizeof(*f));
-	if(f->client)
-		incref(f->client);
-	newfid->aux = f;
-	return nil;
-}
-
-static void
-fsopen(Req *r)
-{
-	Altfid *f;
-	Client *cl;
-	Service *svc;
-	char buf[256];
-
-	// Switch and create on clones, etc
-	f = r->fid->aux;
-	cl = f->client;
-	svc = f->service;
-	USED(svc);
-	switch(f->level){
-	case Qclone:
-		if((cl = newclient()) == nil){
-			respond(r, "no more clients");
-			return;
-		}
-		f->level = Qctl;
-		f->client = cl;
-		fsmkqid(&r->fid->qid, f->level, wfaux(f));
-		r->ofcall.qid = r->fid->qid;
-		break;
-	case Qsclone:
-		if((svc = newservice()) == nil){
-			respond(r, "no more services");
-		}
-		f->level = Qsctl;
-		f->service = svc;
-		fsmkqid(&r->fid->qid, f->level, wfaux(f));
-		r->ofcall.qid = r->fid->qid;
-		break;
-	case Qfeed:
-		if(cl->current){
-			snprint(buf, sizeof(buf), "%s/%s", logdir, cl->current->feed);
-			print("%s\n", buf);
-			f->fd = open(buf, 0644);
-			f->foffset = 0;
-		}
-	}
-	respond(r, nil);
-}
-
-static int
-rootgen(int i, Dir *d, void *)
-{
-	i += Qroot+1;
-	if(i < Qclients){
-		fsmkdir(d, i, 0);
-		return 0;
-	}
-	i -= Qclients;
-	if(i < nclient){
-		fsmkdir(d, Qclients, &client[i]);
-		return 0;
-	}
-	// Final entry is just our services dir
-	if(i == nclient){
-		fsmkdir(d, Qservices, 0);
-		return 0;
-	}
-	return -1;
-}
-
-static int
-servicesgen(int i, Dir *d, void *)
-{
-	i += Qservices + 1;
-	if(i < Qservice){
-		fsmkdir(d, i, 0);
-		return 0;
-	}
-	i -= Qservices + 2;
-	if(i < nservice){
-		fsmkdir(d, Qservice, &service[i]);
-		return 0;
-	}
-	return -1;
-}
-
-static int
-clientgen(int i, Dir *d, void *aux)
-{
-	// TODO: Mask the unusable files if we have no current buffer
-	i += Qclients+1;
-	if(i > Qinput)
-		return -1;
-	fsmkdir(d, i, aux);
-	return 0;
-}
-
-static int
-servicegen(int i, Dir *d, void *aux)
-{
-	i += Qservice+1;
-print("%d %d\n", i, Qmax);
-	if(i >= Qmax)
-		return -1;
-	fsmkdir(d, i, aux);
-	return 0;
-}
-
-static void
-fsread(Req *r)
-{
-	char buf[1024];
-	Altfid *f;
-	Client *cl;
-	Service *svc;
-
-	f = r->fid->aux;
-	cl = f->client;
-	svc = f->service;
-	
-	if(f->level > Qctl && f->level < Qservices && !cl->current){
-		respond(r, "no current buffer selected");
-		return;
-	}
-
-	switch(f->level){
-	case Qroot:
-print("Root\n");
-		dirread9p(r, rootgen, nil);
-		respond(r, nil);
-		return;
-	case Qclients:
-print("Clients\n");
-		dirread9p(r, clientgen, nil);
-		respond(r, nil);
-		return;
-	case Qservices:
-print("Services\n");
-		dirread9p(r, servicesgen, nil);
-		respond(r, nil);
-		return;
-	case Qservice:
-print("Service\n");
-		dirread9p(r, servicegen, nil);
-		respond(r, nil);
-		return;
-	case Qtitle:
-		snprint(buf, sizeof(buf), "%s\n", cl->current->title);
-	String:
-		readstr(r, buf);
-		respond(r, nil);
-		return;
-	case Qctl: 
-		snprint(buf, sizeof(buf), "%d\n", CLIENTID(f->client));
-		goto String;
-	case Qstatus:
-		snprint(buf, sizeof(buf), "%s\n", cl->current->status);
-		goto String;
-	case Qaside:
-		snprint(buf, sizeof(buf), "%s\n", cl->current->aside);
-		goto String;
-	case Qsctl:
-		snprint(buf, sizeof(buf), "%s\n", svc->name);
-		goto String;
-	case Qfeed:
-		pread(f->fd, buf, sizeof(buf), f->foffset);
-		goto String;
-	case Qsinput:
-		// forward any pending input from client
-		// TODO: Channel for input?
-		break;
-	case Qnotify:
-		// TODO: notify fmt %N, install at start
-		//snprint(buf, sizeof(buf), "%N\n", svc->notify);
-		break;
-	case Qtabs:
-		// TODO: tabs fmt %T, install at start
-		//snprint(buf, sizeof(buf), "%T\n", svc);
-		goto String;
-		
-	}
-	respond(r, "not implemented");
-}
-
-static void
-fswrite(Req *r)
-{
-	int n;
-	Altfid *f;
-	char *s, *t;
-
-	f = r->fid->aux;
-	switch(f->level){
-	case Qsctl:
-	case Qctl:
-		n = r->ofcall.count = r->ifcall.count;
-		s = emalloc(n+1);
-		memmove(s, r->ifcall.data, n);
-		while(n > 0 && strchr("\r\n", s[n-1]))
-			n--;
-		s[n] = 0;
-		// TODO: We don't use any of this in any meaningful way, remove t from calls
-		t = s;
-		while(*t && strchr(whitespace, *t)==0)
-			t++;
-		while(*t && strchr(whitespace, *t))
-			*t++ = 0;
-		if(f->level == Qctl)
-			t = clientctl(f->client, s, t);
-		else
-			t = servicectl(f->service, s, t);
-		free(s);
-		respond(r, t);
-		return;
-	case Qinput:
-		// TODO: User wrote a string to us, forward to server (cb?)
-		//f->svc->callback(r->ifcall.data, r->ifcall.count);
-		return;
-	}
-	respond(r, "not implemented");
-}
-
-static void
-fsflush(Req *r)
-{
-	respond(r, nil);
-}
-
-static void
-fsdestroyfid(Fid *fid)
-{
-	Altfid *f;
-
-	if(f = fid->aux){
-		fid->aux = nil;
-		if(f->client)
-			freeclient(f->client);
-		// TODO: uncomment so services hold open an FD to show their livelihood
-		//if(f->service)
-		//	freeservice(f->service);
-		free(f);
-	}	
-}
-
-static void
-fsstart(Srv*)
-{
-	/* Overwrite if we have one, force a reconnect of everything */
-	if(mtpt != nil)
-		unmount(nil, mtpt);
-}
-
-static void
-fsend(Srv*)
-{
-	postnote(PNGROUP, getpid(), "shutdown");
-	exits(nil);
-}
-
-Srv fs = 
-{
-	.start=fsstart,
-	.attach=fsattach,
-	.stat=fsstat,
-	.walk1=fswalk1,
-	.clone=fsclone,
-	.open=fsopen,
-	.read=fsread,
-	.write=fswrite,
-	.flush=fsflush,
-	.destroyfid=fsdestroyfid,
-	.end=fsend,
+	.start=svcstart,
+	.attach=svcattach,
+	.stat=svcstat,
+	.walk1=svcwalk1,
+	.clone=svcclone,
+	.open=svcopen,
+	.read=svcread,
+	.write=svcwrite,
+	.flush=svcflush,
+	.destroyfid=svcdestroyfid,
+	.end=svcend,
 };
 
 void
@@ -530,7 +48,7 @@
 }
 
 void
-main(int argc, char *argv[])
+threadmain(int argc, char *argv[])
 {
 	// We could use quotefmtinstall here
 	// add in tabs at very least
@@ -537,7 +55,6 @@
 	user = getuser();
 	mtpt = "/mnt/alt";
 	logdir = "/tmp/alt";
-	time0 = time(0);
 
 	ARGBEGIN {
 	case 'D': 
@@ -546,9 +63,6 @@
 	case 'm':
 		mtpt = EARGF(usage());
 		break;
-	case 's':
-		srvpt = EARGF(usage());
-		break;
 	case 'l':
 		logdir = EARGF(usage());
 		break;
@@ -562,6 +76,6 @@
 	argv0 = "alt/fs";
 
 	create(logdir, OREAD, DMDIR | 0755);
-	postmountsrv(&fs, srvpt, mtpt, MCREATE);
+	threadpostmountsrv(&svcfs, nil, mtpt, MCREATE);
 	exits(nil); 
 }
--- a/service.c
+++ b/service.c
@@ -6,24 +6,85 @@
 
 #include "alt.h"
 
-Service*
+#define SERVICEID(c)	((int)(((Service*)(c)) - service))
+
+enum {
+	Qsroot,
+		Qclone,
+		Qservices,
+			Qctl,
+	Qmax,
+};
+
+static char *svctab[] = {
+	"/",
+		"clone",
+		nil,
+			"ctl",
+	nil,
+};
+
+Srv clfs =
+{
+	.start=clstart,
+	.attach=clattach,
+	.stat=clstat,
+	.walk1=clwalk1,
+	.clone=clclone,
+	.open=clopen,
+	.read=clread,
+	.write=clwrite,
+	.flush=clflush,
+	.destroyfid=cldestroyfid,
+	.end=clend,
+};
+
+typedef struct Svcfid Svcfid;
+typedef struct Service Service;
+
+struct Svcfid
+{	int	level;
+	Service 	*svc;
+	// Who knows
+};
+
+struct Service
+{
+	Ref;
+
+	Buffer	*base;
+	char	*name;
+	int	isInitialized;
+	int	childpid;
+};
+
+Service service[64];
+int nservice;
+
+static Service*
 newservice(void)
 {
-	Service *sv;
-	char buf[1024];
-	
-	sv = &service[nservice];
-	nservice++;
+	Service *svc;
+	int i;
 
-	sv->buffer = nil;
-	sv->notifications = nil;
-	snprint(buf, sizeof(buf), "default");
-	sv->name = estrdup(buf);
+	for(i = 0; i < nservice; i++)
+		if(service[i].ref == 0)
+			break;
+	if(i >= nelem(service))
+		return nil;
+	if(i == nservice)
+		nservice++;
+	svc = &service[i];
+	svc->ref++;
+
+	// TODO: Instantiate a proper base
+	svc->base = nil;
+	svc->isInitialized = 0;
 	
-	return sv;
+	return svc;
 }
 
-void
+static void
 freeservice(Service *s)
 {
 	if(s == nil)
@@ -31,14 +92,283 @@
 	memset(s, 0, sizeof(*s));
 }
 
+static void*
+wfaux(Svcfid *f)
+{
+	if(f->level < Qservices)
+		return nil;
+	return f->svc;
+}
+
+static void
+svcmkqid(Qid *q, int level, void *)
+{
+	q->type = 0;
+	q->vers = 0;
+	switch(level){
+	case Qsroot:
+	case Qservices:
+		q->type = QTDIR;
+	default:
+		;
+	}
+}
+
+static void
+svcmkdir(Dir *d, int level, void *aux)
+{
+	char buf[1024];
+
+	memset(d, 0, sizeof(*d));
+	svcmkqid(&d->qid, level, aux);
+	d->mode = 0444;
+	d->atime = d->mtime = time(0);
+	d->uid = estrdup(user);
+	d->gid = estrdup(user);
+	d->muid = estrdup(user);
+	if(d->qid.type & QTDIR)
+		d->mode |= DMDIR | 0111;
+	switch(level){
+	case Qservices:
+		snprint(buf, sizeof(buf), "%d", SERVICEID(aux));
+		d->name = estrdup(buf);
+		break;
+	case Qctl:
+	case Qclone:
+		d->mode = 0666;
+	default:
+		d->name = estrdup(svctab[level]);
+	}
+}
+
+void
+svcattach(Req *r)
+{
+	Svcfid *f;
+
+	// No anames
+	if(r->ifcall.aname && r->ifcall.aname[0]){
+		respond(r, "invalid attach specifier");
+		return;
+	}
+	f = emalloc(sizeof(*f));
+	f->level = Qsroot;
+	svcmkqid(&r->fid->qid, f->level, wfaux(f));
+	r->ofcall.qid = r->fid->qid;
+	r->fid->aux = f;
+	respond(r, nil);
+}
+
+void
+svcstat(Req *r)
+{
+	Svcfid *f;
+
+	f = r->fid->aux;
+	svcmkdir(&r->d, f->level, wfaux(f));
+	respond(r, nil);
+}
+
 char*
-servicectl(Service *svc, char *ctl, char *arg)
+svcwalk1(Fid *fid, char *name, Qid *qid)
 {
-	char *target, *buffer;
-	USED(svc, ctl, arg);
-	//ctl is like title:##meskarune
-	target = strstr(":",  ctl);
-	buffer = strstr(nil, ctl);
-	print("%s and %s\n", buffer, target);
+	Svcfid *f;
+	int i, j;
+
+	if(!(fid->qid.type&QTDIR))
+		return "walk in non-directory";
+
+	f = fid->aux;
+	if(strcmp(name, "..")==0){
+		switch(f->level){
+		case Qsroot:
+			break;
+		case Qservices:
+			f->level = Qsroot;
+			break;
+		default:
+			f->level = Qservices;
+		}
+	} else {
+		for(i = f->level+1; i < nelem(svctab); i++){
+			if(svctab[i])
+				if(strcmp(name, svctab[i]) == 0)
+					goto Out;
+			if(i == Qservices){
+				j = atoi(name);
+				if(j >= 0 && j < nservice){
+					f->svc = &service[j];
+					incref(f->svc);
+					goto Out;
+				}
+			}
+		}
+Out:
+		if(i >= nelem(svctab))
+			return "directory entry not found";
+		f->level = i;
+	}
+	svcmkqid(qid, f->level, wfaux(f));
+	fid->qid = *qid;
 	return nil;
+}
+
+char *
+svcclone(Fid *oldfid, Fid *newfid)
+{
+	Svcfid *f, *o;
+	o = oldfid->aux;
+	if(o == nil)
+		return "bad fid";
+	f = emalloc(sizeof(*f));
+	memmove(f, o, sizeof(*f));
+	if(f->svc)
+		incref(f->svc);
+	newfid->aux = f;
+	return nil;
+}
+
+void
+svcopen(Req *r)
+{
+	Svcfid *f;
+	Service *svc;
+
+	f = r->fid->aux;
+	if(f->level == Qclone){
+		if((svc = newservice()) == nil){
+			respond(r, "no more services");
+			return;
+		}
+		f->level = Qctl;
+		f->svc = svc;
+		svcmkqid(&r->fid->qid, f->level, wfaux(f));
+		r->ofcall.qid = r->fid->qid;
+	}
+	respond(r, nil);
+}
+
+static int
+rootgen(int i, Dir *d, void *)
+{
+	i += Qsroot+1;
+	if(i < Qservices){
+		svcmkdir(d, i, 0);
+		return 0;
+	}
+	i -= Qservices;
+	if(i < nservice){
+		svcmkdir(d, Qservices, &service[i]);
+		return 0;
+	}
+	return -1;
+}
+
+static int
+servicegen(int i, Dir *d, void *aux)
+{
+	i += Qservices+1;
+	if(i >= Qmax)
+		return -1;
+	svcmkdir(d, i, aux);
+	return 0;
+}
+
+void
+svcread(Req *r)
+{
+	char buf[1024];
+	Svcfid *f;
+
+	f = r->fid->aux;
+
+	switch(f->level){
+	case Qsroot:
+		dirread9p(r, rootgen, nil);
+		respond(r, nil);
+		return;
+	case Qservices:
+		dirread9p(r, servicegen, nil);
+		respond(r, nil);
+		return;
+	case Qctl:
+		snprint(buf, sizeof(buf), "%d\n", SERVICEID(f->svc));
+		readstr(r, buf);
+		respond(r, nil);
+		return;
+	}
+	respond(r, "not implemented");
+}
+
+void
+svcwrite(Req *r)
+{
+	int n;
+	char *s;
+	Svcfid *f;
+
+	f = r->fid->aux;
+
+	if(f->level == Qctl){
+		n = r->ofcall.count = r->ifcall.count;
+		s = emalloc(n+1);
+		memmove(s, r->ifcall.data, n);
+		s[n] = 0;
+		if(n > 0 && s[n-1] == '\n')
+			s[n-1] = 0;
+		if(f->svc->isInitialized){
+			print("Command: %s\n", s);
+			
+			respond(r, nil);
+		} else {
+			f->svc->name = estrdup(s);
+			// TODO: Buffers will have an input cb from the server
+			clfs.aux = f->svc->base;
+			f->svc->childpid = threadpostsrv(&clfs, s);
+			if(f->svc->childpid >= 0){
+				f->svc->isInitialized++;
+				respond(r, nil);
+			} else 
+				respond(r, "Unable to post to srv");
+		}
+		free(s);
+		return;
+	}
+	respond(r, "not implemented");
+}
+
+void
+svcflush(Req *r)
+{
+	respond(r, nil);
+}
+
+void
+svcdestroyfid(Fid *fid)
+{
+	Svcfid *f;
+
+	if(f = fid->aux){
+		//if(f->svc && f->svc->childpid)
+		//	postnote(PNGROUP, f->svc->childpid, "shutdown");
+		// TODO: Uncomment this after we are good to go, this is our keepalive roughly
+		//fid->aux = nil;
+		//if(f->svc)
+		//	freeservice(f->svc);
+	}
+	free(f);
+}
+
+void
+svcstart(Srv*)
+{
+	if(mtpt != nil)
+		unmount(nil, mtpt);
+}
+
+void
+svcend(Srv*)
+{
+	postnote(PNGROUP, getpid(), "shutdown");
+	threadexitsall(nil);
 }
--