tSingle-threaded plumber that can run "start" rules. Thanks to Caerwyn Jones. - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 3d7e9092a436b707f2160fb869ab68e2a222bc4e
 (DIR) parent 169aba14a4766b3d15695ef27681d0f1d04f8521
 (HTM) Author: rsc <devnull@localhost>
       Date:   Tue, 14 Oct 2003 02:35:00 +0000
       
       Single-threaded plumber that can run "start" rules.
       Thanks to Caerwyn Jones.
       
       Diffstat:
         A include/plumb.h                     |      48 +++++++++++++++++++++++++++++++
         A src/cmd/plumb/Makefile              |      16 ++++++++++++++++
         M src/lib9/Makefile                   |       4 ++--
         A src/lib9/cleanname.c                |      52 +++++++++++++++++++++++++++++++
         A src/lib9/dirstat.c                  |      83 +++++++++++++++++++++++++++++++
         A src/libplumb/Makefile               |      19 +++++++++++++++++++
         A src/libplumb/event.c                |     108 +++++++++++++++++++++++++++++++
         A src/libplumb/mesg.c                 |     403 +++++++++++++++++++++++++++++++
       
       8 files changed, 731 insertions(+), 2 deletions(-)
       ---
 (DIR) diff --git a/include/plumb.h b/include/plumb.h
       t@@ -0,0 +1,48 @@
       +#pragma        lib        "libplumb.a"
       +#pragma        src        "/sys/src/libplumb"
       +
       +/*
       + * Message format:
       + *        source application\n
       + *        destination port\n
       + *        working directory\n
       + *        type\n
       + *        attributes\n
       + *        nbytes\n
       + *        n bytes of data
       + */
       +
       +typedef struct Plumbattr Plumbattr;
       +typedef struct Plumbmsg Plumbmsg;
       +
       +struct Plumbmsg
       +{
       +        char                *src;
       +        char                *dst;
       +        char                *wdir;
       +        char                *type;
       +        Plumbattr        *attr;
       +        int                ndata;
       +        char                *data;
       +};
       +
       +struct Plumbattr
       +{
       +        char                *name;
       +        char                *value;
       +        Plumbattr        *next;
       +};
       +
       +int                        plumbsend(int, Plumbmsg*);
       +Plumbmsg*        plumbrecv(int);
       +char*                plumbpack(Plumbmsg*, int*);
       +Plumbmsg*        plumbunpack(char*, int);
       +Plumbmsg*        plumbunpackpartial(char*, int, int*);
       +char*                plumbpackattr(Plumbattr*);
       +Plumbattr*        plumbunpackattr(char*);
       +Plumbattr*        plumbaddattr(Plumbattr*, Plumbattr*);
       +Plumbattr*        plumbdelattr(Plumbattr*, char*);
       +void                        plumbfree(Plumbmsg*);
       +char*                plumblookup(Plumbattr*, char*);
       +int                        plumbopen(char*, int);
       +int                        eplumb(int, char*);
 (DIR) diff --git a/src/cmd/plumb/Makefile b/src/cmd/plumb/Makefile
       t@@ -0,0 +1,16 @@
       +
       +CFLAGS=-I../../../include
       +LDLIBS=-L../../../lib -lplumb -lbio -lregexp9 -lfmt -l9 -lutf 
       +all:        plumb plumber
       +
       +plumb: plumb.o
       +        cc -o $@ plumb.o $(LDLIBS)
       +
       +plumber: match.o rules.o plumber.o
       +        cc -o $@ match.o rules.o plumber.o $(LDLIBS)
       +
       +clean:
       +        rm -f *.o
       +
       +install: plumb plumber
       +        cp plumb plumber ../../../bin
 (DIR) diff --git a/src/lib9/Makefile b/src/lib9/Makefile
       t@@ -7,6 +7,8 @@ OFILES=\
                _exits.$O\
                argv0.$O\
                await.$O\
       +        cleanname.$O\
       +        dirstat.$O\
                encodefmt.$O\
                errstr.$O\
                exits.$O\
       t@@ -16,12 +18,10 @@ OFILES=\
                lock.$O\
                malloctag.$O\
                mallocz.$O\
       -        netmkaddr.$O\
                nrand.$O\
                qlock.$O\
                readn.$O\
                rendez-$(SYSNAME).$O\
       -        sleep.$O\
                strecpy.$O\
                sysfatal.$O\
                tas-$(OBJTYPE).$O\
 (DIR) diff --git a/src/lib9/cleanname.c b/src/lib9/cleanname.c
       t@@ -0,0 +1,52 @@
       +#include <u.h>
       +#include <libc.h>
       +
       +/*
       + * In place, rewrite name to compress multiple /, eliminate ., and process ..
       + */
       +#define SEP(x)        ((x)=='/' || (x) == 0)
       +char*
       +cleanname(char *name)
       +{
       +        char *p, *q, *dotdot;
       +        int rooted;
       +
       +        rooted = name[0] == '/';
       +
       +        /*
       +         * invariants:
       +         *        p points at beginning of path element we're considering.
       +         *        q points just past the last path element we wrote (no slash).
       +         *        dotdot points just past the point where .. cannot backtrack
       +         *                any further (no slash).
       +         */
       +        p = q = dotdot = name+rooted;
       +        while(*p) {
       +                if(p[0] == '/')        /* null element */
       +                        p++;
       +                else if(p[0] == '.' && SEP(p[1]))
       +                        p += 1;        /* don't count the separator in case it is nul */
       +                else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
       +                        p += 2;
       +                        if(q > dotdot) {        /* can backtrack */
       +                                while(--q > dotdot && *q != '/')
       +                                        ;
       +                        } else if(!rooted) {        /* /.. is / but ./../ is .. */
       +                                if(q != name)
       +                                        *q++ = '/';
       +                                *q++ = '.';
       +                                *q++ = '.';
       +                                dotdot = q;
       +                        }
       +                } else {        /* real path element */
       +                        if(q != name+rooted)
       +                                *q++ = '/';
       +                        while((*q = *p) != '/' && *q != 0)
       +                                p++, q++;
       +                }
       +        }
       +        if(q == name)        /* empty string is really ``.'' */
       +                *q++ = '.';
       +        *q = '\0';
       +        return name;
       +}
 (DIR) diff --git a/src/lib9/dirstat.c b/src/lib9/dirstat.c
       t@@ -0,0 +1,83 @@
       +#include "u.h"
       +#include "libc.h"
       +#include <sys/types.h>
       +#include <sys/stat.h>
       +#include <pwd.h>
       +#include <grp.h>
       +
       +static void
       +statconv(Dir *dir, struct stat *s)
       +{
       +        struct passwd *p;
       +        struct group *g;
       +        ulong q;
       +
       +        p = getpwuid(s->st_uid);
       +        if (p)
       +                strncpy(dir->uid, p->pw_name, NAMELEN);
       +        g = getgrgid(s->st_gid);
       +        if (g)
       +                strncpy(dir->gid, g->gr_name, NAMELEN);
       +        q = 0;
       +        if(S_ISDIR(s->st_mode))
       +                q = CHDIR;
       +        q |= s->st_ino & 0x00FFFFFFUL;
       +        dir->qid.path = q;
       +        dir->qid.vers = s->st_mtime;
       +        dir->mode = (dir->qid.path&CHDIR)|(s->st_mode&0777);
       +        dir->atime = s->st_atime;
       +        dir->mtime = s->st_mtime;
       +        dir->length = s->st_size;
       +        dir->dev = s->st_dev;
       +        dir->type = 'M';
       +        if(S_ISFIFO(s->st_mode))
       +                dir->type = '|';
       +}
       +
       +int
       +dirfstat(int fd, Dir *d)
       +{
       +        struct stat sbuf;
       +
       +        if(fstat(fd, &sbuf) < 0)
       +                return -1;
       +        statconv(d, &sbuf);
       +        return 0;
       +}
       +
       +static char *
       +lelem(char *path)
       +{        
       +        char *pr;
       +
       +        pr = utfrrune(path, '/');
       +        if(pr)
       +                pr++;
       +        else
       +                pr = path;
       +        return pr;
       +}
       +
       +int
       +dirstat(char *f, Dir *d)
       +{
       +        struct stat sbuf;
       +
       +        if(stat(f, &sbuf) < 0)
       +                return -1;
       +        statconv(d, &sbuf);
       +        strncpy(d->name, lelem(f), NAMELEN);
       +        return 0;
       +}
       +
       +int
       +dirfwstat(int fd, Dir *d)
       +{
       +        return -1;
       +}
       +
       +int
       +dirwstat(char *name, Dir *d)
       +{
       +        return -1;
       +}
 (DIR) diff --git a/src/libplumb/Makefile b/src/libplumb/Makefile
       t@@ -0,0 +1,19 @@
       +
       +LIB=../../lib/libplumb.a
       +OFILES=\
       +        mesg.o\
       +
       +HFILES=../../include/plumb.h
       +
       +INCLUDES=-I../../include
       +
       +CFLAGS += $(INCLUDES) -D_POSIX_SOURCE
       +
       +CC=cc
       +
       +$(LIB): $(OFILES)
       +        ar r $(LIB) $(OFILES)
       +
       +clean:
       +        rm -rf $(TARG) $(OFILES)
       +
 (DIR) diff --git a/src/libplumb/event.c b/src/libplumb/event.c
       t@@ -0,0 +1,108 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <draw.h>
       +#include <event.h>
       +#include "plumb.h"
       +
       +typedef struct EQueue EQueue;
       +
       +struct EQueue
       +{
       +        int                id;
       +        char                *buf;
       +        int                nbuf;
       +        EQueue        *next;
       +};
       +
       +static        EQueue        *equeue;
       +static        Lock                eqlock;
       +
       +static
       +int
       +partial(int id, Event *e, uchar *b, int n)
       +{
       +        EQueue *eq, *p;
       +        int nmore;
       +
       +        lock(&eqlock);
       +        for(eq = equeue; eq != nil; eq = eq->next)
       +                if(eq->id == id)
       +                        break;
       +        unlock(&eqlock);
       +        if(eq == nil)
       +                return 0;
       +        /* partial message exists for this id */
       +        eq->buf = realloc(eq->buf, eq->nbuf+n);
       +        if(eq->buf == nil)
       +                drawerror(display, "eplumb: cannot allocate buffer");
       +        memmove(eq->buf+eq->nbuf, b, n);
       +        eq->nbuf += n;
       +        e->v = plumbunpackpartial((char*)eq->buf, eq->nbuf, &nmore);
       +        if(nmore == 0){        /* no more to read in this message */
       +                lock(&eqlock);
       +                if(eq == equeue)
       +                        equeue = eq->next;
       +                else{
       +                        for(p = equeue; p!=nil && p->next!=eq; p = p->next)
       +                                ;
       +                        if(p == nil)
       +                                drawerror(display, "eplumb: bad event queue");
       +                        p->next = eq->next;
       +                }
       +                unlock(&eqlock);
       +                free(eq->buf);
       +                free(eq);
       +        }
       +        return 1;
       +}
       +
       +static
       +void
       +addpartial(int id, char *b, int n)
       +{
       +        EQueue *eq;
       +
       +        eq = malloc(sizeof(EQueue));
       +        if(eq == nil)
       +                return;
       +        eq->id = id;
       +        eq->nbuf = n;
       +        eq->buf = malloc(n);
       +        if(eq->buf == nil){
       +                free(eq);
       +                return;
       +        }
       +        memmove(eq->buf, b, n);
       +        lock(&eqlock);
       +        eq->next = equeue;
       +        equeue = eq;
       +        unlock(&eqlock);
       +}
       +
       +static
       +int
       +plumbevent(int id, Event *e, uchar *b, int n)
       +{
       +        int nmore;
       +
       +        if(partial(id, e, b, n) == 0){
       +                /* no partial message already waiting for this id */
       +                e->v = plumbunpackpartial((char*)b, n, &nmore);
       +                if(nmore > 0)        /* incomplete message */
       +                        addpartial(id, (char*)b, n);
       +        }
       +        if(e->v == nil)
       +                return 0;
       +        return id;
       +}
       +
       +int
       +eplumb(int key, char *port)
       +{
       +        int fd;
       +
       +        fd = plumbopen(port, OREAD|OCEXEC);
       +        if(fd < 0)
       +                return -1;
       +        return estartfn(key, fd, 8192, plumbevent);
       +}
 (DIR) diff --git a/src/libplumb/mesg.c b/src/libplumb/mesg.c
       t@@ -0,0 +1,403 @@
       +#include <u.h>
       +#include <libc.h>
       +#include "plumb.h"
       +
       +static char attrbuf[4096];
       +
       +int
       +plumbopen(char *name, int omode)
       +{
       +        int fd, f;
       +        char *s;
       +        char buf[128];
       +
       +        if(name[0] == '/')
       +                return open(name, omode);
       +        snprint(buf, sizeof buf, "/mnt/plumb/%s", name);
       +        fd = open(buf, omode);
       +        if(fd >= 0)
       +                return fd;
       +        snprint(buf, sizeof buf, "/mnt/term/mnt/plumb/%s", name);
       +        fd = open(buf, omode);
       +        if(fd >= 0)
       +                return fd;
       +        /* try mounting service */
       +        s = getenv("plumbsrv");
       +        if(s == nil)
       +                return -1;
       +        snprint(buf, sizeof buf, "/mnt/plumb/%s", name);
       +        return open(buf, omode);
       +}
       +
       +static int
       +Strlen(char *s)
       +{
       +        if(s == nil)
       +                return 0;
       +        return strlen(s);
       +}
       +
       +static char*
       +Strcpy(char *s, char *t)
       +{
       +        if(t == nil)
       +                return s;
       +        return strcpy(s, t) + strlen(t);
       +}
       +
       +/* quote attribute value, if necessary */
       +static char*
       +quote(char *s)
       +{
       +        char *t;
       +        int c;
       +
       +        if(s == nil){
       +                attrbuf[0] = '\0';
       +                return attrbuf;
       +        }
       +        if(strpbrk(s, " '=\t") == nil)
       +                return s;
       +        t = attrbuf;
       +        *t++ = '\'';
       +        while(t < attrbuf+sizeof attrbuf-2){
       +                c = *s++;
       +                if(c == '\0')
       +                        break;
       +                *t++ = c;
       +                if(c == '\'')
       +                        *t++ = c;
       +        }
       +        *t++ = '\'';
       +        *t = '\0';
       +        return attrbuf;
       +}
       +
       +char*
       +plumbpackattr(Plumbattr *attr)
       +{
       +        int n;
       +        Plumbattr *a;
       +        char *s, *t;
       +
       +        if(attr == nil)
       +                return nil;
       +        n = 0;
       +        for(a=attr; a!=nil; a=a->next)
       +                n += Strlen(a->name) + 1 + Strlen(quote(a->value)) + 1;
       +        s = malloc(n);
       +        if(s == nil)
       +                return nil;
       +        t = s;
       +        *t = '\0';
       +        for(a=attr; a!=nil; a=a->next){
       +                if(t != s)
       +                        *t++ = ' ';
       +                strcpy(t, a->name);
       +                strcat(t, "=");
       +                strcat(t, quote(a->value));
       +                t += strlen(t);
       +        }
       +        if(t > s+n)
       +                abort();
       +        return s;
       +}
       +
       +char*
       +plumblookup(Plumbattr *attr, char *name)
       +{
       +        while(attr){
       +                if(strcmp(attr->name, name) == 0)
       +                        return attr->value;
       +                attr = attr->next;
       +        }
       +        return nil;
       +}
       +
       +char*
       +plumbpack(Plumbmsg *m, int *np)
       +{
       +        int n, ndata;
       +        char *buf, *p, *attr;
       +
       +        ndata = m->ndata;
       +        if(ndata < 0)
       +                ndata = Strlen(m->data);
       +        attr = plumbpackattr(m->attr);
       +        n = Strlen(m->src)+1 + Strlen(m->dst)+1 + Strlen(m->wdir)+1 +
       +                Strlen(m->type)+1 + Strlen(attr)+1 + 16 + ndata;
       +        buf = malloc(n+1);        /* +1 for '\0' */
       +        if(buf == nil){
       +                free(attr);
       +                return nil;
       +        }
       +        p = Strcpy(buf, m->src);
       +        *p++ = '\n';
       +        p = Strcpy(p, m->dst);
       +        *p++ = '\n';
       +        p = Strcpy(p, m->wdir);
       +        *p++ = '\n';
       +        p = Strcpy(p, m->type);
       +        *p++ = '\n';
       +        p = Strcpy(p, attr);
       +        *p++ = '\n';
       +        p += sprint(p, "%d\n", ndata);
       +        memmove(p, m->data, ndata);
       +        *np = (p-buf)+ndata;
       +        buf[*np] = '\0';        /* null terminate just in case */
       +        if(*np >= n+1)
       +                abort();
       +        free(attr);
       +        return buf;
       +}
       +
       +int
       +plumbsend(int fd, Plumbmsg *m)
       +{
       +        char *buf;
       +        int n;
       +
       +        buf = plumbpack(m, &n);
       +        if(buf == nil)
       +                return -1;
       +        n = write(fd, buf, n);
       +        free(buf);
       +        return n;
       +}
       +
       +static int
       +plumbline(char **linep, char *buf, int i, int n, int *bad)
       +{
       +        int starti;
       +        char *p;
       +
       +        if(*bad)
       +                return i;
       +        starti = i;
       +        while(i<n && buf[i]!='\n')
       +                i++;
       +        if(i == n)
       +                *bad = 1;
       +        else{
       +                p = malloc((i-starti) + 1);
       +                if(p == nil)
       +                        *bad = 1;
       +                else{
       +                        memmove(p, buf+starti, i-starti);
       +                        p[i-starti] = '\0';
       +                }
       +                *linep = p;
       +                i++;
       +        }
       +        return i;
       +}
       +
       +void
       +plumbfree(Plumbmsg *m)
       +{
       +        Plumbattr *a, *next;
       +
       +        free(m->src);
       +        free(m->dst);
       +        free(m->wdir);
       +        free(m->type);
       +        for(a=m->attr; a!=nil; a=next){
       +                next = a->next;
       +                free(a->name);
       +                free(a->value);
       +                free(a);
       +        }
       +        free(m->data);
       +        free(m);
       +}
       +
       +Plumbattr*
       +plumbunpackattr(char *p)
       +{
       +        Plumbattr *attr, *prev, *a;
       +        char *q, *v;
       +        int c, quoting;
       +
       +        attr = prev = nil;
       +        while(*p!='\0' && *p!='\n'){
       +                while(*p==' ' || *p=='\t')
       +                        p++;
       +                if(*p == '\0')
       +                        break;
       +                for(q=p; *q!='\0' && *q!='\n' && *q!=' ' && *q!='\t'; q++)
       +                        if(*q == '=')
       +                                break;
       +                if(*q != '=')
       +                        break;        /* malformed attribute */
       +                a = malloc(sizeof(Plumbattr));
       +                if(a == nil)
       +                        break;
       +                a->name = malloc(q-p+1);
       +                if(a->name == nil){
       +                        free(a);
       +                        break;
       +                }
       +                memmove(a->name, p, q-p);
       +                a->name[q-p] = '\0';
       +                /* process quotes in value */
       +                q++;        /* skip '=' */
       +                v = attrbuf;
       +                quoting = 0;
       +                while(*q!='\0' && *q!='\n'){
       +                        if(v >= attrbuf+sizeof attrbuf)
       +                                break;
       +                        c = *q++;
       +                        if(quoting){
       +                                if(c == '\''){
       +                                        if(*q == '\'')
       +                                                q++;
       +                                        else{
       +                                                quoting = 0;
       +                                                continue;
       +                                        }
       +                                }
       +                        }else{
       +                                if(c==' ' || c=='\t')
       +                                        break;
       +                                if(c == '\''){
       +                                        quoting = 1;
       +                                        continue;
       +                                }
       +                        }
       +                        *v++ = c;
       +                }
       +                a->value = malloc(v-attrbuf+1);
       +                if(a->value == nil){
       +                        free(a->name);
       +                        free(a);
       +                        break;
       +                }
       +                memmove(a->value, attrbuf, v-attrbuf);
       +                a->value[v-attrbuf] = '\0';
       +                a->next = nil;
       +                if(prev == nil)
       +                        attr = a;
       +                else
       +                        prev->next = a;
       +                prev = a;
       +                p = q;
       +        }
       +        return attr;
       +}
       +
       +Plumbattr*
       +plumbaddattr(Plumbattr *attr, Plumbattr *new)
       +{
       +        Plumbattr *l;
       +
       +        l = attr;
       +        if(l == nil)
       +                return new;
       +        while(l->next != nil)
       +                l = l->next;
       +        l->next = new;
       +        return attr;
       +}
       +
       +Plumbattr*
       +plumbdelattr(Plumbattr *attr, char *name)
       +{
       +        Plumbattr *l, *prev;
       +
       +        prev = nil;
       +        for(l=attr; l!=nil; l=l->next){
       +                if(strcmp(name, l->name) == 0)
       +                        break;
       +                prev = l;
       +        }
       +        if(l == nil)
       +                return nil;
       +        if(prev)
       +                prev->next = l->next;
       +        else
       +                attr = l->next;
       +        free(l->name);
       +        free(l->value);
       +        free(l);
       +        return attr;
       +}
       +
       +Plumbmsg*
       +plumbunpackpartial(char *buf, int n, int *morep)
       +{
       +        Plumbmsg *m;
       +        int i, bad;
       +        char *ntext, *attr;
       +
       +        m = malloc(sizeof(Plumbmsg));
       +        if(m == nil)
       +                return nil;
       +        memset(m, 0, sizeof(Plumbmsg));
       +        if(morep != nil)
       +                *morep = 0;
       +        bad = 0;
       +        i = plumbline(&m->src, buf, 0, n, &bad);
       +        i = plumbline(&m->dst, buf, i, n, &bad);
       +        i = plumbline(&m->wdir, buf, i, n, &bad);
       +        i = plumbline(&m->type, buf, i, n, &bad);
       +        i = plumbline(&attr, buf, i, n, &bad);
       +        m->attr = plumbunpackattr(attr);
       +        free(attr);
       +        i = plumbline(&ntext, buf, i, n, &bad);
       +        m->ndata = atoi(ntext);
       +        if(m->ndata != n-i){
       +                bad = 1;
       +                if(morep!=nil && m->ndata>n-i)
       +                        *morep = m->ndata - (n-i);
       +        }
       +        free(ntext);
       +        if(!bad){
       +                m->data = malloc(n-i+1);        /* +1 for '\0' */
       +                if(m->data == nil)
       +                        bad = 1;
       +                else{
       +                        memmove(m->data, buf+i, m->ndata);
       +                        m->ndata = n-i;
       +                        /* null-terminate in case it's text */
       +                        m->data[m->ndata] = '\0';
       +                }
       +        }
       +        if(bad){
       +                plumbfree(m);
       +                m = nil;
       +        }
       +        return m;
       +}
       +
       +Plumbmsg*
       +plumbunpack(char *buf, int n)
       +{
       +        return plumbunpackpartial(buf, n, nil);
       +}
       +
       +Plumbmsg*
       +plumbrecv(int fd)
       +{
       +        char *buf;
       +        Plumbmsg *m;
       +        int n, more;
       +
       +        buf = malloc(8192);
       +        if(buf == nil)
       +                return nil;
       +        n = read(fd, buf, 8192);
       +        m = nil;
       +        if(n > 0){
       +                m = plumbunpackpartial(buf, n, &more);
       +                if(m==nil && more>0){
       +                        /* we now know how many more bytes to read for complete message */
       +                        buf = realloc(buf, n+more);
       +                        if(buf == nil)
       +                                return nil;
       +                        if(readn(fd, buf+n, more) == more)
       +                                m = plumbunpackpartial(buf, n+more, nil);
       +                }
       +        }
       +        free(buf);
       +        return m;
       +}