ref: 8541821b582ddccd33adb3c38f29257e5c239bb6
parent: dcecd5ef3ee96776d833d1c457c6cd7ba5fb8a78
author: halfwit <michaelmisch1985@gmail.com>
date: Wed Oct 11 14:35:12 PDT 2023
Initial brainstorming, first pass
--- a/README.md
+++ b/README.md
@@ -1,14 +1,94 @@
-# registry
-Inferno's registry, done in a more plan9-like manner
+# Registry
+This is an interpretation of the Inferno Registry, for Plan9-like systems
+
+## Configuration
+
+Update your ipnet in /lib/ndb/local
```
-mk all && mk install
+ipnet=mynetwork ip=192.168.1.0 ipmask=255.255.255.0
+ ipgw=192.168.1.1
+ dns=1.1.1.1
+ auth=authy
+ registry=authy <---
+ fs=servy
+ cpu=crunchy
```
-## Bugs/Gotchas
-The registry must be in the same namespace as cs to be able to translate addresses, be sure to set up your namespaces accordingly!
+This is used by ndb/registry to find your network regfs
-## Future
-There is work towards having ndb/registry act in resolver mode, but it hasn't been completely ironed out.
-The basic gist would be, you run a main registry for your entire network, and add an entry into your ipnet tuple for `registry=thatip`
-Then you start the rest of your ndb/registry sessions with -s, which uses the main to resolve any query; but also allows resolution from any other arbitrary registry server you add to your ndb.
+Add the following to your /cfg/$sysname/cpurc, where $sysname matches what you entered above.
+
+```
+# Assuming you add a "registry" port mapping
+aux/svcfs -m /mnt/services /adm/services
+aux/listen1 -t tcp!*!registry /bin/exportfs -r /mnt/services
+```
+
+## aux/svcfs
+
+Usage: `aux/svcfs [-r] [-m mtpt] servicesfile`
+
+svcfs will periodically check a service is still alive with a gradual backoff, capping off at hourly.
+svcfs manages the contents of a file, /adm/services, which it will read in on startup
+It serves up on /mnt/services, making a new directory creates a new service,
+The dir contains many of the following files:
+ - addr
+ - status (ok/down)
+ - uptime
+ - description
+ - fd0/fd1 (?)
+
+Services may be read by anyone, but can only be modified by the owner. Request must come from users in the same authdom.
+
+## ndb/registry
+
+Usage: `ndb/registry [-r] [-s srvname]`
+- `-r` do not parse /cfg/$sysname/registry
+- `-s` Alternate address for Registry server
+
+Registry connects to a `svcfs`, by default checking for an entry in your local ipnet=.
+It parses `/cfg/$sysname/registry`, an ndb-formatted list of local services.
+
+```
+## /cfg/mysystem/registry
+
+# Local-only service, this is not written to the svcfs
+service=myservice
+ addr=tcp!myserver!19293
+ description='My local-only service'
+ local=true
+
+# Network-shared service, this is written to the svcfs
+service=mysharedservice
+ addr=tcp!myserver!19294
+ description='My shared service'
+```
+
+In addition to the above style of service tuples, we could also handle local pseudo-services:
+
+```
+service='!g'
+ addr=local!/bin/gcli
+ description='Search Google from the command line'
+ local=true
+
+service=plumber
+ addr=local!/srv/plumb
+ description='Local plumber instance'
+ local=true
+```
+
+The point of which is more for bookkeeping, populating menus in an automated way, etc
+
+## ndb/regquery
+Usage: `nbd/regquery [-m mtpt] [-a] [query]`
+
+Connects to `mtpt`, by default at `/mnt/registry` and issues a search for the given query. If no value is passed in, all entries will be returned.
+
+- `-a` only returns services with a status of `ok`, and/or local address services.
+
+Searches are for partial matches, searching for `"speaker"` will return `"outside-speaker-front"` and `"living-room-speaker"`, for example.
+
+## ndb/regdebug
+Like regquery, but issues queries directly to the given svcfs
\ No newline at end of file
--- /dev/null
+++ b/lib/reglookup.c
@@ -1,0 +1,213 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ndb.h>
+#include "dns.h"
+
+static Ndb *db;
+static QLock dblock;
+
+int
+openregistry(void)
+{
+ if(db != nil)
+ return 0;
+
+ db = ndbopen(dbfile);
+ return db!=nil ? 0: -1;
+}
+
+static void
+attach(Svc* svc, int persist)
+{
+ svc->perm = !!persist;
+
+ if(registry != nil){
+ svc->next = registry;
+ }
+
+ svc->next = registry;
+ registry = svc;
+}
+
+static char*
+detach(char *dial)
+{
+ Svc *c, *last = 0;
+ char buf[Maxdial]; /* trns is capped at 16, port 8 */
+
+ for(c = registry; c; c = c->next){
+ snprint(buf, Maxdial, "%s!%s!%s", c->trns, c->host, c->port);
+ if(strcmp(buf, dial)==0){
+ if(last == 0)
+ registry = c->next;
+ else
+ last->next = c->next;
+ free(c);
+ return 0;
+ }
+ last = c;
+ }
+
+ return "found no matching service";
+}
+
+static void
+host2svc(Svc *svc, char *dial)
+{
+ int n;
+
+ /*
+ * entry host=tcp!mything!9fs
+ * for now, tokenize but we should allow short strings
+ */
+ n = strcspn(dial, "!");
+ if(n < 1)
+ strcpy(svc->trns, "tcp");
+ else
+ strecpy(svc->trns, svc->trns+n+1, dial);
+ dial = dial + n + 1;
+
+ n = strcspn(dial, "!");
+ strecpy(svc->host, svc->host+n+1, dial);
+
+ dial = dial + n + 1;
+ if(sizeof(dial) < 1)
+ strcpy(svc->port, "9fs");
+ else if(sizeof(dial) > 8)
+ /* If this starts happening, we should bump the number */
+ strecpy(svc->port, svc->port + 8, dial);
+ else
+ strcpy(svc->port, dial);
+}
+
+static void
+dbtuple2cache(Ndbtuple *t, int persist)
+{
+ Ndbtuple *et, *nt;
+ Svc *svc;
+
+
+ for(et = t; et; et = et->entry)
+ if(strncmp(et->attr, "serv", 4)==0){
+ svc = emalloc(sizeof(*svc));
+ host2svc(svc, et->val);
+ for(nt = et->entry; nt; nt = nt->entry)
+ if(strcmp(nt->attr, "label")==0)
+ strecpy(svc->labl, svc->labl+Maxmdns, nt->val);
+ else if(strcmp(nt->attr, "auth")==0)
+ strecpy(svc->auth, svc->auth+Maxauth, nt->val);
+ else if(strcmp(nt->attr, "mtpt")==0)
+ strecpy(svc->mtpt, svc->mtpt+Maxpath, nt->val);
+ attach(svc, persist);
+ };
+}
+
+static void
+dbfile2cache(Ndb *db)
+{
+ Ndbtuple *t;
+
+ if(debug)
+ reglog("reading %s", db->file);
+ Bseek(&db->b, 0, 0);
+ while(t = ndbparse(db)){
+ dbtuple2cache(t, 1);
+ ndbfree(t);
+ }
+
+
+}
+
+Svc*
+rstr2svc(char *entry)
+{
+ Svc *svc;
+ char *args[7];
+
+ int i, n;
+
+ n = tokenize(entry, args, 7);
+
+ svc = emalloc(sizeof(*svc));
+ host2svc(svc, estrdup(args[0]));
+
+ for(i = 1; i < n - 1; i++)
+ if(strcmp(args[i], "label")==0)
+ strecpy(svc->labl, svc->labl+Maxmdns, args[++i]);
+ else if(strcmp(args[i], "auth")==0)
+ strecpy(svc->auth, svc->auth+Maxauth, args[++i]);
+ else if(strcmp(args[i], "mtpt")==0)
+ strecpy(svc->mtpt, svc->mtpt+Maxpath, args[++i]);
+
+ return svc;
+}
+
+char*
+rstr2cache(char *entry, int persist)
+{
+ Svc *svc;
+
+ svc = rstr2svc(entry);
+ attach(svc, persist);
+ return 0;
+}
+
+char*
+rstrdtch(char *svc)
+{
+ return detach(svc);
+}
+
+/* e.g. update tcp!foo!9fs label newlabel */
+char*
+rstrupdt(char *entry)
+{
+ Svc *c, *svc = 0;
+ char *args[7], buf[Maxdial];
+ int i, n;
+
+ n = tokenize(entry, args, 7);
+
+ /* Find our service */
+ for(c = registry; c; c = c->next){
+ snprint(buf, Maxdial, "%s!%s!%s", c->trns, c->host, c->port);
+ if(strcmp(buf, args[0])==0){
+ svc = c;
+ break;
+ }
+ }
+
+ if(svc == 0)
+ return "found no matching service";
+
+ for(i = 1; i < n - 1; i++)
+ if(strcmp(args[i], "label")==0)
+ strecpy(svc->labl, svc->labl+Maxmdns, args[++i]);
+ else if(strcmp(args[i], "auth")==0)
+ strecpy(svc->auth, svc->auth+Maxauth, args[++i]);
+ else if(strcmp(args[i], "mtpt")==0)
+ strecpy(svc->mtpt, svc->mtpt+Maxpath, args[++i]);
+
+ return 0;
+}
+
+void
+reg2cache(void)
+{
+ Ndb *ndb;
+
+ qlock(&dblock);
+ if(openregistry() < 0){
+ qunlock(&dblock);
+ return;
+ }
+
+ if(debug)
+ syslog(0, logfile, "building cache from db");
+
+ for(ndb = db; ndb; ndb = ndb->next)
+ dbfile2cache(ndb);
+
+ qunlock(&dblock);
+}
--- /dev/null
+++ b/ndb/registry.c
@@ -1,0 +1,967 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <bio.h>
+#include <ip.h>
+#include "dns.h"
+
+enum
+{
+ Maxrequest= 1024,
+ Maxreply= 8192,
+ Maxrecords= 192,
+ Maxfdata= 8192,
+
+ Qdir= 0,
+ Qreg= 1,
+};
+
+typedef struct Mfile Mfile;
+typedef struct Job Job;
+typedef struct Records Records;
+
+struct Mfile
+{
+ Mfile *next;
+
+ char *user;
+ Qid qid;
+ int fid;
+ int bare;
+
+ char reply[Maxreply];
+ ushort rr[Maxrecords]; /* offset of record */
+ ushort nrr; /* number of records */
+};
+
+/*
+ * active requests
+ */
+struct Job
+{
+ Job *next;
+ int flushed;
+ Fcall request;
+ Fcall reply;
+};
+Lock joblock;
+Job *joblist;
+
+struct {
+ Lock;
+ Mfile *inuse; /* active mfile's */
+} mfalloc;
+
+Svc *registry;
+int vers;
+int debug;
+char *dbfile = "/lib/ndb/registry";
+char *reguser;
+char mtpt[Maxpath];
+int rfd[Maxremote];
+int mfd[2];
+char *logfile = "registry";
+
+void rversion(Job*);
+void rflush(Job*);
+void rattach(Job*, Mfile*);
+char* rwalk(Job*, Mfile*);
+void ropen(Job*, Mfile*);
+void rcreate(Job*, Mfile*);
+void rread(Job*, Mfile*);
+void rwrite(Job*, Mfile*);
+void rclunk(Job*, Mfile*);
+void rremove(Job*, Mfile*);
+void rstat(Job*, Mfile*);
+void rwstat(Job*, Mfile*);
+void rauth(Job*);
+void mountinit(char*, char*);
+void setext(char*, int, char*);
+void io(void);
+
+static char* resolve(char*, ...);
+static char* addsvc(char*);
+static char* rmsvc(char*);
+static char* updatesvc(char*);
+static void refresh(void);
+static void regdump(char*);
+static void sendmsg(Job*, char*);
+
+static int scanfmt(Fmt*);
+static int srvfmt(Fmt*);
+static int dumpfmt(Fmt*);
+
+static char* query(Job*, Mfile*, char*, int);
+static char* resolvequery(Job*, Mfile*, char*, int);
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-xrd] [-f ndb-file]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char* argv[])
+{
+ char servefile[Maxpath], ext[Maxpath];
+ Dir *dir;
+ ext[0] = 0;
+
+ setnetmtpt(mtpt, sizeof mtpt, nil);
+
+ ARGBEGIN{
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ dbfile = EARGF(usage());
+ break;
+ case 'x':
+ setnetmtpt(mtpt, sizeof mtpt, EARGF(usage()));
+ setext(ext, sizeof ext, mtpt);
+ break;
+ } ARGEND;
+ if(argc != 0)
+ usage();
+
+
+
+ rfork(RFREND|RFNOTEG);
+
+ fmtinstall('F', fcallfmt);
+ fmtinstall('G', srvfmt);
+ fmtinstall('N', scanfmt);
+ fmtinstall('D', dumpfmt);
+
+ reglog("starting registry on %s", mtpt);
+
+ if(openregistry())
+ sysfatal("unable to open db file");
+
+ reguser = estrdup(getuser());
+ seprint(servefile, servefile+Maxpath, "#s/registry%s", ext);
+
+ dir = dirstat(servefile);
+ if (dir)
+ sysfatal("%s exists; another registry instance is running", servefile);
+ free(dir);
+
+ mountinit(servefile, mtpt);
+ reg2cache();
+ io();
+
+ _exits(0);
+}
+
+void
+setext(char *ext, int n, char *p)
+{
+ int i, c;
+
+ n--;
+ for(i = 0; i < n; i++){
+ c = p[i];
+ if(c == 0)
+ break;
+ if(c == '/')
+ c = '_';
+ ext[i] = c;
+ }
+ ext[i] = 0;
+}
+
+void
+mountinit(char *service, char *mtpt)
+{
+ int f;
+ int p[2];
+ char buf[32];
+
+ if(pipe(p) < 0)
+ sysfatal("pipe failed: %r");
+
+ /*
+ * make a /srv/registry
+ */
+ if((f = create(service, OWRITE|ORCLOSE, 0666)) < 0)
+ sysfatal("create %s failed: %r", service);
+ snprint(buf, sizeof buf, "%d", p[1]);
+ if(write(f, buf, strlen(buf)) != strlen(buf))
+ sysfatal("write %s failed: %r", service);
+
+ /* copy namespace to avoid a deadlock */
+ switch(rfork(RFFDG|RFPROC|RFNAMEG)){
+ case 0: /* child: start main proc */
+ close(p[1]);
+ procsetname("%s", mtpt);
+ break;
+ case -1:
+ sysfatal("fork failed: %r");
+ default: /* parent: make /srv/registry, mount it, exit */
+ close(p[0]);
+
+ /*
+ * put ourselves into the file system
+ */
+ if(mount(p[1], -1, mtpt, MAFTER, "") < 0)
+ fprint(2, "registry mount failed: %r\n");
+ _exits(0);
+ }
+ mfd[0] = mfd[1] = p[0];
+}
+
+Mfile*
+newfid(int fid, int needunused)
+{
+ Mfile *mf;
+
+ lock(&mfalloc);
+ for(mf = mfalloc.inuse; mf != nil; mf = mf->next)
+ if(mf->fid == fid){
+ unlock(&mfalloc);
+ if(needunused)
+ return nil;
+ return mf;
+ }
+ mf = emalloc(sizeof(*mf));
+ mf->fid = fid;
+ mf->qid.vers = vers;
+ mf->qid.type = QTDIR;
+ mf->qid.path = 0LL;
+ mf->user = estrdup(reguser);
+ mf->next = mfalloc.inuse;
+ mfalloc.inuse = mf;
+ mf->bare = 1;
+ unlock(&mfalloc);
+ return mf;
+}
+
+void
+freefid(Mfile *mf)
+{
+ Mfile **l;
+
+ lock(&mfalloc);
+ for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next)
+ if(*l == mf){
+ *l = mf->next;
+ free(mf->user);
+ memset(mf, 0, sizeof *mf); /* cause trouble */
+ free(mf);
+ unlock(&mfalloc);
+ return;
+ }
+ unlock(&mfalloc);
+ sysfatal("freeing unused fid");
+}
+
+Mfile*
+copyfid(Mfile *mf, int fid)
+{
+ Mfile *nmf;
+
+ nmf = newfid(fid, 1);
+ if(nmf == nil)
+ return nil;
+ nmf->fid = fid;
+ free(nmf->user);
+ nmf->user = estrdup(mf->user);
+ nmf->qid.type = mf->qid.type;
+ nmf->qid.path = mf->qid.path;
+ nmf->qid.vers = vers++;
+ return nmf;
+}
+
+Job*
+newjob(void)
+{
+ Job *job;
+
+ job = emalloc(sizeof *job);
+ lock(&joblock);
+ job->next = joblist;
+ joblist = job;
+ job->request.tag = -1;
+ unlock(&joblock);
+ return job;
+}
+
+void
+freejob(Job *job)
+{
+ Job **l;
+
+ lock(&joblock);
+ for(l = &joblist; *l; l = &(*l)->next)
+ if(*l == job){
+ *l = job->next;
+ memset(job, 0, sizeof *job); /* cause trouble */
+ free(job);
+ break;
+ }
+ unlock(&joblock);
+}
+
+void
+flushjob(int tag)
+{
+ Job *job;
+
+ lock(&joblock);
+ for(job = joblist; job; job = job->next)
+ if(job->request.tag == tag && job->request.type != Tflush){
+ job->flushed = 1;
+ break;
+ }
+ unlock(&joblock);
+}
+
+void
+io(void)
+{
+ long n;
+ Mfile *mf;
+ uchar mdata[IOHDRSZ + Maxfdata];
+ Job *job;
+
+ while((n = read9pmsg(mfd[0], mdata, sizeof mdata)) != 0){
+ if(n < 0){
+ syslog(1, logfile, "error reading 9P from %s: %r", mtpt);
+ break;
+ }
+
+ job = newjob();
+ if(convM2S(mdata, n, &job->request) != n){
+ reglog("format error %ux %ux %ux %ux %ux",
+ mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
+ freejob(job);
+ break;
+ }
+ mf = newfid(job->request.fid, 0);
+ if(debug)
+ reglog("%F", &job->request);
+
+ switch(job->request.type){
+ default:
+ warning("unknown request type %d", job->request.type);
+ break;
+ case Tversion:
+ rversion(job);
+ break;
+ case Tauth:
+ rauth(job);
+ break;
+ case Tflush:
+ rflush(job);
+ break;
+ case Tattach:
+ rattach(job, mf);
+ break;
+ case Twalk:
+ rwalk(job, mf);
+ break;
+ case Topen:
+ ropen(job, mf);
+ break;
+ case Tcreate:
+ rcreate(job, mf);
+ break;
+ case Tread:
+ rread(job, mf);
+ break;
+ case Twrite:
+ rwrite(job, mf);
+ break;
+ case Tclunk:
+ rclunk(job, mf);
+ break;
+ case Tremove:
+ rremove(job, mf);
+ break;
+ case Tstat:
+ rstat(job, mf);
+ break;
+ case Twstat:
+ rwstat(job, mf);
+ break;
+ }
+
+ freejob(job);
+ }
+}
+
+void
+rversion(Job *job)
+{
+ if(job->request.msize > IOHDRSZ + Maxfdata)
+ job->reply.msize = IOHDRSZ + Maxfdata;
+ else
+ job->reply.msize = job->request.msize;
+ job->reply.version = "9P2000";
+ if(strncmp(job->request.version, "9P", 2) != 0)
+ job->reply.version = "unknown";
+ sendmsg(job, nil);
+}
+
+void
+rauth(Job *job)
+{
+ sendmsg(job, "registry: authentication not required");
+}
+
+void
+rflush(Job *job)
+{
+ flushjob(job->request.oldtag);
+ sendmsg(job, 0);
+}
+
+void
+rattach(Job *job, Mfile *mf)
+{
+ if(mf->user != nil)
+ free(mf->user);
+ mf->user = estrdup(job->request.uname);
+ mf->qid.vers = vers++;
+ mf->qid.type = QTDIR;
+ mf->qid.path = 0LL;
+ job->reply.qid = mf->qid;
+ sendmsg(job, 0);
+}
+
+char*
+rwalk(Job *job, Mfile *mf)
+{
+ int i, nelems;
+ char *err;
+ char **elems;
+ Mfile *nmf;
+ Qid qid;
+
+ err = 0;
+ nmf = nil;
+ elems = job->request.wname;
+ nelems = job->request.nwname;
+ job->reply.nwqid = 0;
+
+ if(job->request.newfid != job->request.fid){
+ /* clone fid */
+ nmf = copyfid(mf, job->request.newfid);
+ if(nmf == nil){
+ err = "clone bad newfid";
+ goto send;
+ }
+ mf = nmf;
+ }
+ /* else nmf will be nil */
+
+ qid = mf->qid;
+ if(nelems > 0){
+ /* walk fid */
+ for(i=0; i<nelems && i<MAXWELEM; i++){
+ if((qid.type & QTDIR) == 0){
+ err = "not a directory";
+ break;
+ }
+ if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
+ qid.type = QTDIR;
+ qid.path = Qdir;
+ Found:
+ job->reply.wqid[i] = qid;
+ job->reply.nwqid++;
+ continue;
+ }
+ if(strcmp(elems[i], "registry") == 0){
+ qid.type = QTFILE;
+ qid.path = Qreg;
+ goto Found;
+ }
+ err = "file does not exist";
+ break;
+ }
+ }
+
+ send:
+ if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
+ freefid(nmf);
+ if(err == nil)
+ mf->qid = qid;
+ sendmsg(job, err);
+ return err;
+}
+
+void
+ropen(Job *job, Mfile *mf)
+{
+ int mode;
+ char *err;
+
+ err = 0;
+ mode = job->request.mode;
+ if(mf->qid.type & QTDIR)
+ if(mode)
+ err = "permission denied";
+ job->reply.qid = mf->qid;
+ job->reply.iounit = 0;
+ sendmsg(job, err);
+}
+
+void
+rcreate(Job *job, Mfile *mf)
+{
+ USED(mf);
+ sendmsg(job, "creation permission denied");
+}
+
+void rclunk(Job *job, Mfile *mf)
+{
+ freefid(mf);
+ sendmsg(job, 0);
+}
+
+void
+rremove(Job *job, Mfile *mf)
+{
+ USED(mf);
+ sendmsg(job, "remove permission denied");
+}
+
+void
+rread(Job *job, Mfile *mf)
+{
+ int i, n;
+ long clock;
+ ulong cnt;
+ vlong off;
+ char *err;
+ uchar buf[Maxfdata];
+ Dir dir;
+
+ n = 0;
+ err = nil;
+ off = job->request.offset;
+ cnt = job->request.count;
+ *buf = '\0';
+ job->reply.data = (char*)buf;
+ if(mf->qid.type & QTDIR){
+ clock = time(nil);
+ if(off == 0){
+ memset(&dir, 0, sizeof dir);
+ dir.name = "registry";
+ dir.qid.type = QTFILE;
+ dir.qid.vers = vers;
+ dir.qid.path = Qreg;
+ dir.mode = 0666;
+ dir.length = 0;
+ dir.uid = dir.gid = dir.muid = mf->user;
+ dir.atime = dir.mtime = clock;
+ n = convD2M(&dir, buf, sizeof buf);
+ }
+ } else if (off < 0)
+ err = "negative read offset";
+ else {
+ if(mf->bare)
+ query(job, mf, "all", 0);
+ for(i = 1; i < mf->nrr; i++)
+ if(mf->rr[i] > off)
+ break;
+ if(i <= mf->nrr){
+ if(off + cnt > mf->rr[i])
+ n = mf->rr[i] - off;
+ else
+ n = cnt;
+ assert(n >= 0);
+ job->reply.data = mf->reply + off;
+ }
+ }
+ job->reply.count = n;
+ sendmsg(job, err);
+}
+
+void
+rwrite(Job *job, Mfile *mf)
+{
+ int send, pipe2rc;
+ ulong cnt;
+ char *err, *atype;
+ char errbuf[ERRMAX];
+
+ err = nil;
+ cnt = job->request.count;
+ send = 1;
+ if(mf->qid.type & QTDIR)
+ err = "can't write directory";
+ else if (job->request.offset != 0)
+ err = "writing at non-zero offset";
+ else if (cnt >= Maxrequest)
+ err = "request too long";
+ else
+ send = 0;
+ if(send)
+ goto send;
+
+ job->request.data[cnt] = 0;
+ if(cnt > 0 && job->request.data[cnt-1] == '\n')
+ job->request.data[cnt-1] = 0;
+
+ if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, reguser) != 0)
+ goto query; /* We don't want remote clients to modify our local */
+
+ /*
+ * special commands
+ */
+ send = 1;
+ if(strcmp(job->request.data, "debug")==0)
+ debug ^= 1;
+ else if(strcmp(job->request.data, "dump")==0)
+ regdump("/lib/ndb/regdump");
+ else if (strcmp(job->request.data, "refresh")==0)
+ refresh();
+ else if (strncmp(job->request.data, "add ", 4)==0)
+ err = addsvc(job->request.data + 4);
+ else if (strncmp(job->request.data, "rm ", 3)==0)
+ err = rmsvc(job->request.data + 3);
+ else if (strncmp(job->request.data, "update ", 7)==0)
+ err = updatesvc(job->request.data + 7);
+ else
+ send = 0;
+ if (send)
+ goto send;
+
+query:
+ /*
+ * kill previous reply
+ */
+ mf->nrr = 0;
+ mf->rr[0] = 0;
+ pipe2rc = 0;
+
+ atype = strchr(job->request.data, ' ');
+ if(atype == 0){
+ snprint(errbuf, sizeof errbuf, "illegal request %s", job->request.data);
+ err = errbuf;
+ goto send;
+ } else
+ *atype++ = 0;
+
+ if(strcmp(atype, "svc") == 0)
+ pipe2rc++;
+ else if(strcmp(atype, "scan") != 0){
+ snprint(errbuf, sizeof errbuf, "unknown query %s", atype);
+ err = errbuf;
+ goto send;
+ }
+
+ err = query(job, mf,job->request.data, pipe2rc);
+send:
+ mf->bare = 0;
+ job->reply.count = cnt;
+ sendmsg(job, err);
+}
+
+void
+rstat(Job *job, Mfile *mf)
+{
+ Dir dir;
+ uchar buf[IOHDRSZ+Maxfdata];
+
+ memset(&dir, 0, sizeof dir);
+ if(mf->qid.type & QTDIR){
+ dir.name = ".";
+ dir.mode = DMDIR|0555;
+ }else{
+ dir.name = "registry";
+ dir.mode = 0666;
+ }
+ dir.qid = mf->qid;
+ dir.length = 0;
+ dir.uid = dir.gid = dir.muid = mf->user;
+ dir.atime = dir.mtime = time(nil);
+ job->reply.nstat = convD2M(&dir, buf, sizeof buf);
+ job->reply.stat = buf;
+ sendmsg(job, 0);
+}
+
+void
+rwstat(Job *job, Mfile *mf)
+{
+ USED(mf);
+ sendmsg(job, "wstat permission denied");
+}
+
+static char *
+resolvequery(Job *job, Mfile *mf, char *p, int pipe2rc)
+{
+ int match, i;
+ int n;
+ Svc *c;
+
+ char cmd[256];
+ char buf[8192+1];
+
+ lock(&joblock);
+ if(!job->flushed){
+ match = n = 0;
+ mf->nrr = 0;
+
+ snprint(cmd, sizeof(cmd), "%s %s", p, ((pipe2rc)?"svc":"scan"));
+
+ for(i = 0; i < Maxremote && !match && rfd[i] > 1; i++){
+ seek(rfd[i], 0, 0);
+ write(rfd[i], cmd, sizeof cmd);
+
+ seek(rfd[i], 0, 0);
+ while(read(rfd[i], buf, sizeof(buf)-1) > 0){
+ match = 1;
+ c = rstr2svc(buf);
+ mf->rr[mf->nrr++] = n;
+ if(pipe2rc)
+ n += snprint(mf->reply+n, Maxreply-n, "%G", c);
+ else
+ n += snprint(mf->reply+n, Maxreply-n, "%N", c);
+ free(c);
+ }
+ }
+ }
+ unlock(&joblock);
+
+ return 0;
+}
+
+static char *
+query(Job *job, Mfile *mf, char *p, int pipe2rc)
+{
+ int n;
+
+ Svc *c;
+ lock(&joblock);
+ if(!job->flushed){
+ n = 0;
+ mf->nrr = 0;
+ for(c = registry; c && n < Maxreply; c = c->next)
+ if((strncmp(p, c->labl, strlen(p))==0) || (strcmp(p, "all")==0)){
+ mf->rr[mf->nrr++] = n;
+ if(pipe2rc)
+ n += snprint(mf->reply+n, Maxreply-n, "%G", c);
+ else
+ n += snprint(mf->reply+n, Maxreply-n, "%N", c);
+ }
+ mf->rr[mf->nrr] = n;
+ }
+ unlock(&joblock);
+ return nil;
+}
+
+static void
+sendmsg(Job *job, char *err)
+{
+ int n;
+ uchar mdata[IOHDRSZ+Maxfdata];
+ char ename[ERRMAX];
+
+ if(err){
+ job->reply.type = Rerror;
+ snprint(ename, sizeof ename, "registry: %s", err);
+ job->reply.ename = ename;
+ }else
+ job->reply.type = job->request.type+1;
+ job->reply.tag = job->request.tag;
+ n = convS2M(&job->reply, mdata, sizeof mdata);
+ if(n == 0){
+ warning("sendmsg convS2M of %F returns 0", &job->reply);
+ abort();
+ }
+ lock(&joblock);
+ if(job->flushed == 0)
+ if(write(mfd[1], mdata, n)!=n)
+ sysfatal("mount write");
+ unlock(&joblock);
+ if(debug)
+ reglog("%F %d", &job->reply, n);
+}
+
+static void
+regdump(char *file)
+{
+ Svc *rp;
+ int fd;
+
+ fd = create(file, OWRITE, 0666);
+ if(fd < 0)
+ return;
+ lock(&mfalloc);
+ for(rp = registry; rp; rp = rp->next)
+ fprint(fd, "%D\n\n", rp);
+ unlock(&mfalloc);
+ close(fd);
+}
+
+static void
+refresh(void)
+{
+ Svc *c;
+ char dial[Maxdial];
+
+ for(c = registry; c; c = c->next){
+ /* Don't remove the ones we've added since startup */
+ if(!c->perm)
+ continue;
+ snprint(dial, Maxdial, "%s!%s!%s", c->trns, c->host, c->port);
+ rmsvc(dial);
+ /* Reset so we don't have messy loops */
+ c = registry;
+ }
+ reg2cache();
+}
+
+static char *
+resolve(char *cmd, ...)
+{
+ int n;
+ char fullcmd[256];
+ char buf[8192+1];
+ va_list arg;
+
+ va_start(arg, cmd);
+ vseprint(fullcmd, fullcmd+sizeof(fullcmd), cmd, arg);
+ va_end(arg);
+
+ /* We only operate on our local rfd */
+ seek(rfd[0], 0, 0);
+ write(rfd[0], fullcmd, sizeof fullcmd);
+
+ seek(rfd[0], 0, 0);
+ while((n = read(rfd[0], buf, sizeof(buf)-1)) > 0){
+ buf[n++] = '\n';
+ write(1, buf, n);
+ }
+ return buf;
+}
+
+static char *
+addsvc(char *args)
+{
+ if(debug)
+ reglog("Adding entry: %s", args);
+
+ return rstr2cache(args, 0);
+}
+
+static char *
+rmsvc(char *args)
+{
+ if(debug)
+ reglog("Removing entry: %s", args);
+ return rstrdtch(args);
+}
+
+static char *
+updatesvc(char *args)
+{
+ if(debug)
+ reglog("Updating entry: %s", args);
+ return rstrupdt(args);
+}
+
+void
+warning(char *fmt, ...)
+{
+ char regerr[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(regerr, regerr+sizeof(regerr), fmt, arg);
+ va_end(arg);
+ syslog(1, logfile, regerr);
+}
+
+void
+reglog(char *fmt, ...)
+{
+ char regerr[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(regerr, regerr+sizeof(regerr), fmt, arg);
+ va_end(arg);
+ syslog(0, logfile, regerr);
+}
+
+void*
+emalloc(int size)
+{
+ void *x;
+
+ x = malloc(size);
+ if(x == nil)
+ sysfatal("out of memory");
+ memset(x, 0, size);
+ return x;
+}
+
+char*
+estrdup(char *s)
+{
+ int size;
+ char *p;
+
+ size = strlen(s);
+ p = malloc(size+1);
+ if(p == nil)
+ sysfatal("out of memory");
+ memmove(p, s, size);
+ p[size] = 0;
+ return p;
+}
+
+static int
+srvfmt(Fmt *f)
+{
+ Svc *r;
+ char mf[Maxpath+1], auth[7];
+
+ r = va_arg(f->args, Svc*);
+ mf[0] = 0;
+ auth[0] = 0;
+
+ if(strcmp(r->mtpt, "")!= 0)
+ snprint(mf, sizeof(r->mtpt)+1, " %s", r->mtpt);
+
+ if(strcmp(r->auth, "none")==0)
+ snprint(auth, 4, "srv");
+ else
+ snprint(auth, 7, "srvtls");
+
+ return fmtprint(f, "%s!%s!%s\n",
+ r->trns, r->host, r->port);
+}
+
+static int
+scanfmt(Fmt *f)
+{
+ Svc *r;
+ char mf[Maxpath+6]; /* pad for our tuple attrs */
+
+ mf[0] = 0;
+ r = va_arg(f->args, Svc*);
+ if(strcmp(r->mtpt, "")!=0)
+ snprint(mf, sizeof(r->mtpt)+6, " mtpt=%s", r->mtpt);
+ return fmtprint(f, "service=%s!%s!%s label='%s' auth=%s%s\n",
+ r->trns, r->host, r->port, r->labl, r->auth, mf);
+}
+
+static int
+dumpfmt(Fmt *f)
+{
+ Svc *r;
+ char mf[Maxpath+7]; /* pad for our tuple attrs */
+
+ r = va_arg(f->args, Svc*);
+ if(r->mtpt != 0)
+ snprint(mf, sizeof(r->mtpt) + 7, "\n\tmtpt=%s", r->mtpt);
+ return fmtprint(f, "service=%s!%s!%s\n\tlabel=%s\n\tauth=%s%s",
+ r->trns, r->host, r->port, r->labl, r->auth, mf);
+}
+
--- /dev/null
+++ b/ndb/regquery.c
@@ -1,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <ndb.h>
+#include "dns.h"
+#include "ip.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: regquery [-s] [-f registry] query\n");
+ exits("usage");
+}
+
+static void
+queryregistry(int fd, char *line, int n)
+{
+ char buf[8192+1];
+
+ seek(fd, 0, 0);
+ write(fd, line, n);
+
+ seek(fd, 0, 0);
+ while((n = read(fd, buf, sizeof(buf)-1)) > 0)
+ write(1, buf, n);
+}
+
+static void
+query(int fd, char *q, int pipe2rc)
+{
+ char arg[260];
+
+ if(strlen(q) > 255)
+ sysfatal("query too long");
+
+ sprint(arg, "%s %s", q, (pipe2rc) ? "svc":"scan");
+ queryregistry(fd, arg, sizeof(arg));
+}
+
+void
+main(int argc, char *argv[])
+{
+ int fd, pipe2rc = 0;
+ char *rst = "/net/registry";
+
+ ARGBEGIN {
+ case 's':
+ pipe2rc++;
+ break;
+ case 'f':
+ rst = EARGF(usage());
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if(argc != 1)
+ usage();
+
+ fd = open(rst, ORDWR);
+ if(fd < 0)
+ sysfatal("can't open %s: %r", rst);
+
+ query(fd, argv[0], pipe2rc);
+ exits(0);
+}
--- a/registry.c
+++ /dev/null
@@ -1,995 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <auth.h>
-#include <fcall.h>
-#include <bio.h>
-#include <ip.h>
-#include "dns.h"
-
-enum
-{
- Maxrequest= 1024,
- Maxreply= 8192,
- Maxrecords= 192,
- Maxfdata= 8192,
-
- Qdir= 0,
- Qreg= 1,
-};
-
-typedef struct Mfile Mfile;
-typedef struct Job Job;
-typedef struct Records Records;
-
-struct Mfile
-{
- Mfile *next;
-
- char *user;
- Qid qid;
- int fid;
- int bare;
-
- char reply[Maxreply];
- ushort rr[Maxrecords]; /* offset of record */
- ushort nrr; /* number of records */
-};
-
-/*
- * active requests
- */
-struct Job
-{
- Job *next;
- int flushed;
- Fcall request;
- Fcall reply;
-};
-Lock joblock;
-Job *joblist;
-
-struct {
- Lock;
- Mfile *inuse; /* active mfile's */
-} mfalloc;
-
-Svc *registry;
-int vers;
-int resolver;
-int debug;
-char *dbfile = "/lib/ndb/registry";
-char *reguser;
-char mtpt[Maxpath];
-int rfd[Maxremote];
-int mfd[2];
-char *logfile = "registry";
-
-void rversion(Job*);
-void rflush(Job*);
-void rattach(Job*, Mfile*);
-char* rwalk(Job*, Mfile*);
-void ropen(Job*, Mfile*);
-void rcreate(Job*, Mfile*);
-void rread(Job*, Mfile*);
-void rwrite(Job*, Mfile*);
-void rclunk(Job*, Mfile*);
-void rremove(Job*, Mfile*);
-void rstat(Job*, Mfile*);
-void rwstat(Job*, Mfile*);
-void rauth(Job*);
-void mountinit(char*, char*);
-void setext(char*, int, char*);
-void io(void);
-
-static char* resolve(char*, ...);
-static char* addsvc(char*);
-static char* rmsvc(char*);
-static char* updatesvc(char*);
-static void refresh(void);
-static void regdump(char*);
-static void sendmsg(Job*, char*);
-
-static int scanfmt(Fmt*);
-static int srvfmt(Fmt*);
-static int dumpfmt(Fmt*);
-
-static char* query(Job*, Mfile*, char*, int);
-static char* resolvequery(Job*, Mfile*, char*, int);
-
-void
-usage(void)
-{
- fprint(2, "usage: %s [-xrd] [-f ndb-file]\n", argv0);
- exits("usage");
-}
-
-void
-main(int argc, char* argv[])
-{
- char servefile[Maxpath], ext[Maxpath];
- Dir *dir;
- ext[0] = 0;
-
- setnetmtpt(mtpt, sizeof mtpt, nil);
-
- ARGBEGIN{
- case 'r':
- resolver = 1;
- break;
- case 'd':
- debug = 1;
- break;
- case 'f':
- dbfile = EARGF(usage());
- break;
- case 'x':
- setnetmtpt(mtpt, sizeof mtpt, EARGF(usage()));
- setext(ext, sizeof ext, mtpt);
- break;
- } ARGEND;
- if(argc != 0)
- usage();
-
-
-
- rfork(RFREND|RFNOTEG);
-
- fmtinstall('F', fcallfmt);
- fmtinstall('G', srvfmt);
- fmtinstall('N', scanfmt);
- fmtinstall('D', dumpfmt);
-
- reglog("starting registry on %s", mtpt);
-
- if(! resolver && openregistry())
- sysfatal("unable to open db file");
-
- reguser = estrdup(getuser());
- seprint(servefile, servefile+Maxpath, "#s/registry%s", ext);
-
- dir = dirstat(servefile);
- if (dir)
- sysfatal("%s exists; another registry instance is running", servefile);
- free(dir);
-
-
- mountinit(servefile, mtpt);
-
- if(resolver)
- regconnect();
- else
- reg2cache();
- io();
-
- _exits(0);
-}
-
-void
-setext(char *ext, int n, char *p)
-{
- int i, c;
-
- n--;
- for(i = 0; i < n; i++){
- c = p[i];
- if(c == 0)
- break;
- if(c == '/')
- c = '_';
- ext[i] = c;
- }
- ext[i] = 0;
-}
-
-void
-mountinit(char *service, char *mtpt)
-{
- int f;
- int p[2];
- char buf[32];
-
- if(pipe(p) < 0)
- sysfatal("pipe failed: %r");
-
- /*
- * make a /srv/registry
- */
- if((f = create(service, OWRITE|ORCLOSE, 0666)) < 0)
- sysfatal("create %s failed: %r", service);
- snprint(buf, sizeof buf, "%d", p[1]);
- if(write(f, buf, strlen(buf)) != strlen(buf))
- sysfatal("write %s failed: %r", service);
-
- /* copy namespace to avoid a deadlock */
- switch(rfork(RFFDG|RFPROC|RFNAMEG)){
- case 0: /* child: start main proc */
- close(p[1]);
- procsetname("%s", mtpt);
- break;
- case -1:
- sysfatal("fork failed: %r");
- default: /* parent: make /srv/registry, mount it, exit */
- close(p[0]);
-
- /*
- * put ourselves into the file system
- */
- if(mount(p[1], -1, mtpt, MAFTER, "") < 0)
- fprint(2, "registry mount failed: %r\n");
- _exits(0);
- }
- mfd[0] = mfd[1] = p[0];
-}
-
-Mfile*
-newfid(int fid, int needunused)
-{
- Mfile *mf;
-
- lock(&mfalloc);
- for(mf = mfalloc.inuse; mf != nil; mf = mf->next)
- if(mf->fid == fid){
- unlock(&mfalloc);
- if(needunused)
- return nil;
- return mf;
- }
- mf = emalloc(sizeof(*mf));
- mf->fid = fid;
- mf->qid.vers = vers;
- mf->qid.type = QTDIR;
- mf->qid.path = 0LL;
- mf->user = estrdup(reguser);
- mf->next = mfalloc.inuse;
- mfalloc.inuse = mf;
- mf->bare = 1;
- unlock(&mfalloc);
- return mf;
-}
-
-void
-freefid(Mfile *mf)
-{
- Mfile **l;
-
- lock(&mfalloc);
- for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next)
- if(*l == mf){
- *l = mf->next;
- free(mf->user);
- memset(mf, 0, sizeof *mf); /* cause trouble */
- free(mf);
- unlock(&mfalloc);
- return;
- }
- unlock(&mfalloc);
- sysfatal("freeing unused fid");
-}
-
-Mfile*
-copyfid(Mfile *mf, int fid)
-{
- Mfile *nmf;
-
- nmf = newfid(fid, 1);
- if(nmf == nil)
- return nil;
- nmf->fid = fid;
- free(nmf->user);
- nmf->user = estrdup(mf->user);
- nmf->qid.type = mf->qid.type;
- nmf->qid.path = mf->qid.path;
- nmf->qid.vers = vers++;
- return nmf;
-}
-
-Job*
-newjob(void)
-{
- Job *job;
-
- job = emalloc(sizeof *job);
- lock(&joblock);
- job->next = joblist;
- joblist = job;
- job->request.tag = -1;
- unlock(&joblock);
- return job;
-}
-
-void
-freejob(Job *job)
-{
- Job **l;
-
- lock(&joblock);
- for(l = &joblist; *l; l = &(*l)->next)
- if(*l == job){
- *l = job->next;
- memset(job, 0, sizeof *job); /* cause trouble */
- free(job);
- break;
- }
- unlock(&joblock);
-}
-
-void
-flushjob(int tag)
-{
- Job *job;
-
- lock(&joblock);
- for(job = joblist; job; job = job->next)
- if(job->request.tag == tag && job->request.type != Tflush){
- job->flushed = 1;
- break;
- }
- unlock(&joblock);
-}
-
-void
-io(void)
-{
- long n;
- Mfile *mf;
- uchar mdata[IOHDRSZ + Maxfdata];
- Job *job;
-
- while((n = read9pmsg(mfd[0], mdata, sizeof mdata)) != 0){
- if(n < 0){
- syslog(1, logfile, "error reading 9P from %s: %r", mtpt);
- break;
- }
-
- job = newjob();
- if(convM2S(mdata, n, &job->request) != n){
- reglog("format error %ux %ux %ux %ux %ux",
- mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
- freejob(job);
- break;
- }
- mf = newfid(job->request.fid, 0);
- if(debug)
- reglog("%F", &job->request);
-
- switch(job->request.type){
- default:
- warning("unknown request type %d", job->request.type);
- break;
- case Tversion:
- rversion(job);
- break;
- case Tauth:
- rauth(job);
- break;
- case Tflush:
- rflush(job);
- break;
- case Tattach:
- rattach(job, mf);
- break;
- case Twalk:
- rwalk(job, mf);
- break;
- case Topen:
- ropen(job, mf);
- break;
- case Tcreate:
- rcreate(job, mf);
- break;
- case Tread:
- rread(job, mf);
- break;
- case Twrite:
- rwrite(job, mf);
- break;
- case Tclunk:
- rclunk(job, mf);
- break;
- case Tremove:
- rremove(job, mf);
- break;
- case Tstat:
- rstat(job, mf);
- break;
- case Twstat:
- rwstat(job, mf);
- break;
- }
-
- freejob(job);
- }
-}
-
-void
-rversion(Job *job)
-{
- if(job->request.msize > IOHDRSZ + Maxfdata)
- job->reply.msize = IOHDRSZ + Maxfdata;
- else
- job->reply.msize = job->request.msize;
- job->reply.version = "9P2000";
- if(strncmp(job->request.version, "9P", 2) != 0)
- job->reply.version = "unknown";
- sendmsg(job, nil);
-}
-
-void
-rauth(Job *job)
-{
- sendmsg(job, "registry: authentication not required");
-}
-
-void
-rflush(Job *job)
-{
- flushjob(job->request.oldtag);
- sendmsg(job, 0);
-}
-
-void
-rattach(Job *job, Mfile *mf)
-{
- if(mf->user != nil)
- free(mf->user);
- mf->user = estrdup(job->request.uname);
- mf->qid.vers = vers++;
- mf->qid.type = QTDIR;
- mf->qid.path = 0LL;
- job->reply.qid = mf->qid;
- sendmsg(job, 0);
-}
-
-char*
-rwalk(Job *job, Mfile *mf)
-{
- int i, nelems;
- char *err;
- char **elems;
- Mfile *nmf;
- Qid qid;
-
- err = 0;
- nmf = nil;
- elems = job->request.wname;
- nelems = job->request.nwname;
- job->reply.nwqid = 0;
-
- if(job->request.newfid != job->request.fid){
- /* clone fid */
- nmf = copyfid(mf, job->request.newfid);
- if(nmf == nil){
- err = "clone bad newfid";
- goto send;
- }
- mf = nmf;
- }
- /* else nmf will be nil */
-
- qid = mf->qid;
- if(nelems > 0){
- /* walk fid */
- for(i=0; i<nelems && i<MAXWELEM; i++){
- if((qid.type & QTDIR) == 0){
- err = "not a directory";
- break;
- }
- if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
- qid.type = QTDIR;
- qid.path = Qdir;
- Found:
- job->reply.wqid[i] = qid;
- job->reply.nwqid++;
- continue;
- }
- if(strcmp(elems[i], "registry") == 0){
- qid.type = QTFILE;
- qid.path = Qreg;
- goto Found;
- }
- err = "file does not exist";
- break;
- }
- }
-
- send:
- if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
- freefid(nmf);
- if(err == nil)
- mf->qid = qid;
- sendmsg(job, err);
- return err;
-}
-
-void
-ropen(Job *job, Mfile *mf)
-{
- int mode;
- char *err;
-
- err = 0;
- mode = job->request.mode;
- if(mf->qid.type & QTDIR)
- if(mode)
- err = "permission denied";
- job->reply.qid = mf->qid;
- job->reply.iounit = 0;
- sendmsg(job, err);
-}
-
-void
-rcreate(Job *job, Mfile *mf)
-{
- USED(mf);
- sendmsg(job, "creation permission denied");
-}
-
-void rclunk(Job *job, Mfile *mf)
-{
- freefid(mf);
- sendmsg(job, 0);
-}
-
-void
-rremove(Job *job, Mfile *mf)
-{
- USED(mf);
- sendmsg(job, "remove permission denied");
-}
-
-void
-rread(Job *job, Mfile *mf)
-{
- int i, n;
- long clock;
- ulong cnt;
- vlong off;
- char *err;
- uchar buf[Maxfdata];
- Dir dir;
-
- n = 0;
- err = nil;
- off = job->request.offset;
- cnt = job->request.count;
- *buf = '\0';
- job->reply.data = (char*)buf;
- if(mf->qid.type & QTDIR){
- clock = time(nil);
- if(off == 0){
- memset(&dir, 0, sizeof dir);
- dir.name = "registry";
- dir.qid.type = QTFILE;
- dir.qid.vers = vers;
- dir.qid.path = Qreg;
- dir.mode = 0666;
- dir.length = 0;
- dir.uid = dir.gid = dir.muid = mf->user;
- dir.atime = dir.mtime = clock;
- n = convD2M(&dir, buf, sizeof buf);
- }
- } else if (off < 0)
- err = "negative read offset";
- else {
- if(mf->bare)
- query(job, mf, "all", 0);
- for(i = 1; i < mf->nrr; i++)
- if(mf->rr[i] > off)
- break;
- if(i <= mf->nrr){
- if(off + cnt > mf->rr[i])
- n = mf->rr[i] - off;
- else
- n = cnt;
- assert(n >= 0);
- job->reply.data = mf->reply + off;
- }
- }
- job->reply.count = n;
- sendmsg(job, err);
-}
-
-void
-rwrite(Job *job, Mfile *mf)
-{
- int send, pipe2rc;
- ulong cnt;
- char *err, *atype;
- char errbuf[ERRMAX];
-
- err = nil;
- cnt = job->request.count;
- send = 1;
- if(mf->qid.type & QTDIR)
- err = "can't write directory";
- else if (job->request.offset != 0)
- err = "writing at non-zero offset";
- else if (cnt >= Maxrequest)
- err = "request too long";
- else
- send = 0;
- if(send)
- goto send;
-
- job->request.data[cnt] = 0;
- if(cnt > 0 && job->request.data[cnt-1] == '\n')
- job->request.data[cnt-1] = 0;
-
- if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, reguser) != 0)
- goto query; /* We don't want remote clients to modify our local */
-
- /*
- * special commands
- */
- send = 1;
- if(strcmp(job->request.data, "debug")==0)
- debug ^= 1;
- else if(strcmp(job->request.data, "dump")==0)
- regdump("/lib/ndb/regdump");
- else if (strcmp(job->request.data, "refresh")==0)
- refresh();
- else if (strncmp(job->request.data, "add ", 4)==0)
- err = addsvc(job->request.data + 4);
- else if (strncmp(job->request.data, "rm ", 3)==0)
- err = rmsvc(job->request.data + 3);
- else if (strncmp(job->request.data, "update ", 7)==0)
- err = updatesvc(job->request.data + 7);
- else
- send = 0;
- if (send)
- goto send;
-
-query:
- /*
- * kill previous reply
- */
- mf->nrr = 0;
- mf->rr[0] = 0;
- pipe2rc = 0;
-
- atype = strchr(job->request.data, ' ');
- if(atype == 0){
- snprint(errbuf, sizeof errbuf, "illegal request %s", job->request.data);
- err = errbuf;
- goto send;
- } else
- *atype++ = 0;
-
- if(strcmp(atype, "svc") == 0)
- pipe2rc++;
- else if(strcmp(atype, "scan") != 0){
- snprint(errbuf, sizeof errbuf, "unknown query %s", atype);
- err = errbuf;
- goto send;
- }
-
- if(resolver){
- resolvequery(job, mf, job->request.data, pipe2rc);
- goto send;
- }
- err = query(job, mf,job->request.data, pipe2rc);
-send:
- mf->bare = 0;
- job->reply.count = cnt;
- sendmsg(job, err);
-}
-
-void
-rstat(Job *job, Mfile *mf)
-{
- Dir dir;
- uchar buf[IOHDRSZ+Maxfdata];
-
- memset(&dir, 0, sizeof dir);
- if(mf->qid.type & QTDIR){
- dir.name = ".";
- dir.mode = DMDIR|0555;
- }else{
- dir.name = "registry";
- dir.mode = 0666;
- }
- dir.qid = mf->qid;
- dir.length = 0;
- dir.uid = dir.gid = dir.muid = mf->user;
- dir.atime = dir.mtime = time(nil);
- job->reply.nstat = convD2M(&dir, buf, sizeof buf);
- job->reply.stat = buf;
- sendmsg(job, 0);
-}
-
-void
-rwstat(Job *job, Mfile *mf)
-{
- USED(mf);
- sendmsg(job, "wstat permission denied");
-}
-
-static char *
-resolvequery(Job *job, Mfile *mf, char *p, int pipe2rc)
-{
- int match, i;
- int n;
- Svc *c;
-
- char cmd[256];
- char buf[8192+1];
-
- lock(&joblock);
- if(!job->flushed){
- match = n = 0;
- mf->nrr = 0;
-
- snprint(cmd, sizeof(cmd), "%s %s", p, ((pipe2rc)?"svc":"scan"));
-
- for(i = 0; i < Maxremote && !match && rfd[i] > 1; i++){
- seek(rfd[i], 0, 0);
- write(rfd[i], cmd, sizeof cmd);
-
- seek(rfd[i], 0, 0);
- while(read(rfd[i], buf, sizeof(buf)-1) > 0){
- match = 1;
- c = rstr2svc(buf);
- mf->rr[mf->nrr++] = n;
- if(pipe2rc)
- n += snprint(mf->reply+n, Maxreply-n, "%G", c);
- else
- n += snprint(mf->reply+n, Maxreply-n, "%N", c);
- free(c);
- }
- }
- }
- unlock(&joblock);
-
- return 0;
-}
-
-static char *
-query(Job *job, Mfile *mf, char *p, int pipe2rc)
-{
- int n;
-
- Svc *c;
- lock(&joblock);
- if(!job->flushed){
- n = 0;
- mf->nrr = 0;
- for(c = registry; c && n < Maxreply; c = c->next)
- if((strncmp(p, c->labl, strlen(p))==0) || (strcmp(p, "all")==0)){
- mf->rr[mf->nrr++] = n;
- if(pipe2rc)
- n += snprint(mf->reply+n, Maxreply-n, "%G", c);
- else
- n += snprint(mf->reply+n, Maxreply-n, "%N", c);
- }
- mf->rr[mf->nrr] = n;
- }
- unlock(&joblock);
- return nil;
-}
-
-static void
-sendmsg(Job *job, char *err)
-{
- int n;
- uchar mdata[IOHDRSZ+Maxfdata];
- char ename[ERRMAX];
-
- if(err){
- job->reply.type = Rerror;
- snprint(ename, sizeof ename, "registry: %s", err);
- job->reply.ename = ename;
- }else
- job->reply.type = job->request.type+1;
- job->reply.tag = job->request.tag;
- n = convS2M(&job->reply, mdata, sizeof mdata);
- if(n == 0){
- warning("sendmsg convS2M of %F returns 0", &job->reply);
- abort();
- }
- lock(&joblock);
- if(job->flushed == 0)
- if(write(mfd[1], mdata, n)!=n)
- sysfatal("mount write");
- unlock(&joblock);
- if(debug)
- reglog("%F %d", &job->reply, n);
-}
-
-static void
-regdump(char *file)
-{
- Svc *rp;
- int fd;
-
- if(resolver){
- resolve("dump");
- return;
- }
-
- fd = create(file, OWRITE, 0666);
- if(fd < 0)
- return;
- lock(&mfalloc);
- for(rp = registry; rp; rp = rp->next)
- fprint(fd, "%D\n\n", rp);
- unlock(&mfalloc);
- close(fd);
-}
-
-static void
-refresh(void)
-{
- Svc *c;
- char dial[Maxdial];
-
- if(resolver){
- resolve("refresh");
- return;
- }
-
- for(c = registry; c; c = c->next){
- /* Don't remove the ones we've added since startup */
- if(!c->perm)
- continue;
- snprint(dial, Maxdial, "%s!%s!%s", c->trns, c->host, c->port);
- rmsvc(dial);
- /* Reset so we don't have messy loops */
- c = registry;
- }
- reg2cache();
-}
-
-static char *
-resolve(char *cmd, ...)
-{
- int n;
- char fullcmd[256];
- char buf[8192+1];
- va_list arg;
-
- va_start(arg, cmd);
- vseprint(fullcmd, fullcmd+sizeof(fullcmd), cmd, arg);
- va_end(arg);
-
- /* We only operate on our local rfd */
- seek(rfd[0], 0, 0);
- write(rfd[0], fullcmd, sizeof fullcmd);
-
- seek(rfd[0], 0, 0);
- while((n = read(rfd[0], buf, sizeof(buf)-1)) > 0){
- buf[n++] = '\n';
- write(1, buf, n);
- }
- return buf;
-}
-
-static char *
-addsvc(char *args)
-{
- if(debug)
- reglog("Adding entry: %s", args);
- if(resolver)
- return resolve("add", args);
- return rstr2cache(args, 0);
-}
-
-static char *
-rmsvc(char *args)
-{
- if(debug)
- reglog("Removing entry: %s", args);
- if(resolver)
- return resolve("rm", args);
- return rstrdtch(args);
-}
-
-static char *
-updatesvc(char *args)
-{
- if(debug)
- reglog("Updating entry: %s", args);
- if(resolver)
- return resolve("update", args);
- return rstrupdt(args);
-}
-
-void
-warning(char *fmt, ...)
-{
- char regerr[256];
- va_list arg;
-
- va_start(arg, fmt);
- vseprint(regerr, regerr+sizeof(regerr), fmt, arg);
- va_end(arg);
- syslog(1, logfile, regerr);
-}
-
-void
-reglog(char *fmt, ...)
-{
- char regerr[256];
- va_list arg;
-
- va_start(arg, fmt);
- vseprint(regerr, regerr+sizeof(regerr), fmt, arg);
- va_end(arg);
- syslog(0, logfile, regerr);
-}
-
-void*
-emalloc(int size)
-{
- void *x;
-
- x = malloc(size);
- if(x == nil)
- sysfatal("out of memory");
- memset(x, 0, size);
- return x;
-}
-
-char*
-estrdup(char *s)
-{
- int size;
- char *p;
-
- size = strlen(s);
- p = malloc(size+1);
- if(p == nil)
- sysfatal("out of memory");
- memmove(p, s, size);
- p[size] = 0;
- return p;
-}
-
-static int
-srvfmt(Fmt *f)
-{
- Svc *r;
- char mf[Maxpath+1], auth[7];
-
- r = va_arg(f->args, Svc*);
- mf[0] = 0;
- auth[0] = 0;
-
- if(strcmp(r->mtpt, "")!= 0)
- snprint(mf, sizeof(r->mtpt)+1, " %s", r->mtpt);
-
- if(strcmp(r->auth, "none")==0)
- snprint(auth, 4, "srv");
- else
- snprint(auth, 7, "srvtls");
-
- return fmtprint(f, "%s!%s!%s\n",
- r->trns, r->host, r->port);
-}
-
-static int
-scanfmt(Fmt *f)
-{
- Svc *r;
- char mf[Maxpath+6]; /* pad for our tuple attrs */
-
- mf[0] = 0;
- r = va_arg(f->args, Svc*);
- if(strcmp(r->mtpt, "")!=0)
- snprint(mf, sizeof(r->mtpt)+6, " mtpt=%s", r->mtpt);
- return fmtprint(f, "service=%s!%s!%s label='%s' auth=%s%s\n",
- r->trns, r->host, r->port, r->labl, r->auth, mf);
-}
-
-static int
-dumpfmt(Fmt *f)
-{
- Svc *r;
- char mf[Maxpath+7]; /* pad for our tuple attrs */
-
- r = va_arg(f->args, Svc*);
- if(r->mtpt != 0)
- snprint(mf, sizeof(r->mtpt) + 7, "\n\tmtpt=%s", r->mtpt);
- return fmtprint(f, "service=%s!%s!%s\n\tlabel=%s\n\tauth=%s%s",
- r->trns, r->host, r->port, r->labl, r->auth, mf);
-}
-
--- a/reglookup.c
+++ /dev/null
@@ -1,251 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <ndb.h>
-#include "dns.h"
-
-static Ndb *db;
-static QLock dblock;
-
-int
-openregistry(void)
-{
- if(db != nil)
- return 0;
-
- db = ndbopen(dbfile);
- return db!=nil ? 0: -1;
-}
-
-static void
-attach(Svc* svc, int persist)
-{
- svc->perm = !!persist;
-
- if(registry != nil){
- svc->next = registry;
- }
-
- svc->next = registry;
- registry = svc;
-}
-
-static char*
-detach(char *dial)
-{
- Svc *c, *last = 0;
- char buf[Maxdial]; /* trns is capped at 16, port 8 */
-
- for(c = registry; c; c = c->next){
- snprint(buf, Maxdial, "%s!%s!%s", c->trns, c->host, c->port);
- if(strcmp(buf, dial)==0){
- if(last == 0)
- registry = c->next;
- else
- last->next = c->next;
- free(c);
- return 0;
- }
- last = c;
- }
-
- return "found no matching service";
-}
-
-static void
-host2svc(Svc *svc, char *dial)
-{
- int n;
-
- /*
- * entry host=tcp!mything!9fs
- * for now, tokenize but we should allow short strings
- */
- n = strcspn(dial, "!");
- if(n < 1)
- strcpy(svc->trns, "tcp");
- else
- strecpy(svc->trns, svc->trns+n+1, dial);
- dial = dial + n + 1;
-
- n = strcspn(dial, "!");
- strecpy(svc->host, svc->host+n+1, dial);
-
- dial = dial + n + 1;
- if(sizeof(dial) < 1)
- strcpy(svc->port, "9fs");
- else if(sizeof(dial) > 8)
- /* If this starts happening, we should bump the number */
- strecpy(svc->port, svc->port + 8, dial);
- else
- strcpy(svc->port, dial);
-}
-
-static void
-dbtuple2cache(Ndbtuple *t, int persist)
-{
- Ndbtuple *et, *nt;
- Svc *svc;
-
-
- for(et = t; et; et = et->entry)
- if(strncmp(et->attr, "serv", 4)==0){
- svc = emalloc(sizeof(*svc));
- host2svc(svc, et->val);
- for(nt = et->entry; nt; nt = nt->entry)
- if(strcmp(nt->attr, "label")==0)
- strecpy(svc->labl, svc->labl+Maxmdns, nt->val);
- else if(strcmp(nt->attr, "auth")==0)
- strecpy(svc->auth, svc->auth+Maxauth, nt->val);
- else if(strcmp(nt->attr, "mtpt")==0)
- strecpy(svc->mtpt, svc->mtpt+Maxpath, nt->val);
- attach(svc, persist);
- };
-}
-
-static void
-dbfile2cache(Ndb *db)
-{
- Ndbtuple *t;
-
- if(debug)
- reglog("reading %s", db->file);
- Bseek(&db->b, 0, 0);
- while(t = ndbparse(db)){
- dbtuple2cache(t, 1);
- ndbfree(t);
- }
-
-
-}
-
-Svc*
-rstr2svc(char *entry)
-{
- Svc *svc;
- char *args[7];
-
- int i, n;
-
- n = tokenize(entry, args, 7);
-
- svc = emalloc(sizeof(*svc));
- host2svc(svc, estrdup(args[0]));
-
- for(i = 1; i < n - 1; i++)
- if(strcmp(args[i], "label")==0)
- strecpy(svc->labl, svc->labl+Maxmdns, args[++i]);
- else if(strcmp(args[i], "auth")==0)
- strecpy(svc->auth, svc->auth+Maxauth, args[++i]);
- else if(strcmp(args[i], "mtpt")==0)
- strecpy(svc->mtpt, svc->mtpt+Maxpath, args[++i]);
-
- return svc;
-}
-
-char*
-rstr2cache(char *entry, int persist)
-{
- Svc *svc;
-
- svc = rstr2svc(entry);
- attach(svc, persist);
- return 0;
-}
-
-char*
-rstrdtch(char *svc)
-{
- return detach(svc);
-}
-
-/* e.g. update tcp!foo!9fs label newlabel */
-char*
-rstrupdt(char *entry)
-{
- Svc *c, *svc = 0;
- char *args[7], buf[Maxdial];
- int i, n;
-
- n = tokenize(entry, args, 7);
-
- /* Find our service */
- for(c = registry; c; c = c->next){
- snprint(buf, Maxdial, "%s!%s!%s", c->trns, c->host, c->port);
- if(strcmp(buf, args[0])==0){
- svc = c;
- break;
- }
- }
-
- if(svc == 0)
- return "found no matching service";
-
- for(i = 1; i < n - 1; i++)
- if(strcmp(args[i], "label")==0)
- strecpy(svc->labl, svc->labl+Maxmdns, args[++i]);
- else if(strcmp(args[i], "auth")==0)
- strecpy(svc->auth, svc->auth+Maxauth, args[++i]);
- else if(strcmp(args[i], "mtpt")==0)
- strecpy(svc->mtpt, svc->mtpt+Maxpath, args[++i]);
-
- return 0;
-}
-
-void
-regconnect(void)
-{
- Ndb *adb;
- Ndbtuple *t;
- char *host, *list[1];
- int rfdn, fd;
-
- rfdn = 0;
- adb = ndbopen(0);
-
- /* Start with us */
- host = getenv("sysname");
- list[0] = "registry";
- t = ndbipinfo(adb, "sys", host, list, 1);
- if(t->val != nil){
- fd = dial(t->val, 0, 0, 0);
- if(fd >= 0)
- rfd[rfdn++] = fd;
- }
- ndbfree(t);
-
- /* Here we want to find our other registry= tuples in the ndb
- * I don't claim to know the best way to do this. registry= entries
- * in an ipnet= is the solution i see best fitting, but for now
- * this will be marked as TODO and left alone
- * the goal will be, however, to iterate through upstream resolvers
- * which act as lists for services one may add, like the 9ants grid
- * registry, as a good example; and when a local resolve misses, it
- * bubbles up to the next resolver, etc, until we exhaust the list
- * or find a match
- */
-
- if(rfdn < 1)
- sysfatal("unable to dial any registry server");
- rfd[rfdn] = 0;
-}
-
-void
-reg2cache(void)
-{
- Ndb *ndb;
-
- qlock(&dblock);
- if(openregistry() < 0){
- qunlock(&dblock);
- return;
- }
-
- if(debug)
- syslog(0, logfile, "building cache from db");
-
- for(ndb = db; ndb; ndb = ndb->next)
- dbfile2cache(ndb);
-
- qunlock(&dblock);
-}
--- a/regquery.c
+++ /dev/null
@@ -1,67 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <ctype.h>
-#include <ndb.h>
-#include "dns.h"
-#include "ip.h"
-
-void
-usage(void)
-{
- fprint(2, "usage: regquery [-s] [-f registry] query\n");
- exits("usage");
-}
-
-static void
-queryregistry(int fd, char *line, int n)
-{
- char buf[8192+1];
-
- seek(fd, 0, 0);
- write(fd, line, n);
-
- seek(fd, 0, 0);
- while((n = read(fd, buf, sizeof(buf)-1)) > 0)
- write(1, buf, n);
-}
-
-static void
-query(int fd, char *q, int pipe2rc)
-{
- char arg[260];
-
- if(strlen(q) > 255)
- sysfatal("query too long");
-
- sprint(arg, "%s %s", q, (pipe2rc) ? "svc":"scan");
- queryregistry(fd, arg, sizeof(arg));
-}
-
-void
-main(int argc, char *argv[])
-{
- int fd, pipe2rc = 0;
- char *rst = "/net/registry";
-
- ARGBEGIN {
- case 's':
- pipe2rc++;
- break;
- case 'f':
- rst = EARGF(usage());
- break;
- default:
- usage();
- } ARGEND;
-
- if(argc != 1)
- usage();
-
- fd = open(rst, ORDWR);
- if(fd < 0)
- sysfatal("can't open %s: %r", rst);
-
- query(fd, argv[0], pipe2rc);
- exits(0);
-}