/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <bio.h>#include <regexp.h>#include <thread.h>#include <ctype.h>#include <plumb.h>#include "plumber.h" typedef struct Input Input;typedef struct Var Var; struct Input{ char *file; /* name of file */ Biobuf *fd; /* input buffer, if from real file */ uchar *s; /* input string, if from /mnt/plumb/rules */ uchar *end; /* end of input string */ int lineno; Input *next; /* file to read after EOF on this one */}; struct Var{ char *name; char *value; char *qvalue;}; static int parsing;static int nvars;static Var *vars;static Input *input; static char ebuf[4096]; char *badports[] ={ ".", "..", "send", nil}; char *objects[] ={ "arg", "attr", "data", "dst", "plumb", "src", "type", "wdir", nil}; char *verbs[] ={ "add", "client", "delete", "is", "isdir", "isfile", "matches", "set", "start", "to", nil}; static voidprintinputstackrev(Input *in){ if(in == nil) return; printinputstackrev(in->next); fprint(2, "%s:%d: ", in->file, in->lineno);} voidprintinputstack(void){ printinputstackrev(input);} static voidpushinput(char *name, int fd, uchar *str){ Input *in; int depth; depth = 0; for(in=input; in; in=in->next) if(depth++ >= 10) /* prevent deep C stack in plumber and bad include structure */ parseerror("include stack too deep; max 10"); in = emalloc(sizeof(Input)); in->file = estrdup(name); in->next = input; input = in; if(str) in->s = str; else{ in->fd = emalloc(sizeof(Biobuf)); if(Binit(in->fd, fd, OREAD) < 0) parseerror("can't initialize Bio for rules file: %r"); } } intpopinput(void){ Input *in; in = input; if(in == nil) return 0; input = in->next; if(in->fd){ Bterm(in->fd); free(in->fd); } free(in->file); free(in); return 1;} intgetc(void){ if(input == nil) return Beof; if(input->fd) return Bgetc(input->fd); if(input->s < input->end) return *(input->s)++; return -1;} char*getline(void){ static int n = 0; static char *s, *incl; int c, i; i = 0; for(;;){ c = getc(); if(c < 0) return nil; if(i == n){ n += 100; s = erealloc(s, n); } if(c<0 || c=='\0' || c=='\n') break; s[i++] = c; } s[i] = '\0'; return s;} intlookup(char *s, char *tab[]){ int i; for(i=0; tab[i]!=nil; i++) if(strcmp(s, tab[i])==0) return i; return -1;} Var*lookupvariable(char *s, int n){ int i; for(i=0; i<nvars; i++) if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0) return vars+i; return nil;} char*variable(char *s, int n){ Var *var; var = lookupvariable(s, n); if(var) return var->qvalue; return nil;} voidsetvariable(char *s, int n, char *val, char *qval){ Var *var; var = lookupvariable(s, n); if(var){ free(var->value); free(var->qvalue); }else{ vars = erealloc(vars, (nvars+1)*sizeof(Var)); var = vars+nvars++; var->name = emalloc(n+1); memmove(var->name, s, n); } var->value = estrdup(val); var->qvalue = estrdup(qval);} static char*nonnil(char *s){ if(s == nil) return ""; return s;} static char*filename(Exec *e, char *name){ static char *buf; /* rock to hold value so we don't leak the strings */ free(buf); /* if name is defined, used it */ if(name!=nil && name[0]!='\0'){ buf = estrdup(name); return cleanname(buf); } /* if data is an absolute file name, or wdir is empty, use it */ if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){ buf = estrdup(e->msg->data); return cleanname(buf); } buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1); sprint(buf, "%s/%s", e->msg->wdir, e->msg->data); return cleanname(buf);} char*dollar(Exec *e, char *s, int *namelen){ int n; static char *abuf; char *t; *namelen = 1; if(e!=nil && '0'<=s[0] && s[0]<='9') return nonnil(e->match[s[0]-'0']); for(t=s; isalnum(*t); t++) ; n = t-s; *namelen = n; if(e != nil){ if(n == 3){ if(memcmp(s, "src", 3) == 0) return nonnil(e->msg->src); if(memcmp(s, "dst", 3) == 0) return nonnil(e->msg->dst); if(memcmp(s, "dir", 3) == 0) return filename(e, e->dir); } if(n == 4){ if(memcmp(s, "attr", 4) == 0){ free(abuf); abuf = plumbpackattr(e->msg->attr); return nonnil(abuf); } if(memcmp(s, "data", 4) == 0) return nonnil(e->msg->data); if(memcmp(s, "file", 4) == 0) return filename(e, e->file); if(memcmp(s, "type", 4) == 0) return nonnil(e->msg->type); if(memcmp(s, "wdir", 3) == 0) return nonnil(e->msg->wdir); } } return variable(s, n);} /* expand one blank-terminated string, processing quotes and $ signs */char*expand(Exec *e, char *s, char **ends){ char *p, *ep, *val; int namelen, quoting; p = ebuf; ep = ebuf+sizeof ebuf-1; quoting = 0; while(p<ep && *s!='\0' && (quoting || (*s!=' ' && *s!='\t'))){ if(*s == '\''){ s++; if(!quoting) quoting = 1; else if(*s == '\''){ *p++ = '\''; s++; }else quoting = 0; continue; } if(quoting || *s!='$'){ *p++ = *s++; continue; } s++; val = dollar(e, s, &namelen); if(val == nil){ *p++ = '$'; continue; } if(ep-p < strlen(val)) return "string-too-long"; strcpy(p, val); p += strlen(val); s += namelen; } if(ends) *ends = s; *p = '\0'; return ebuf;} voidregerror(char *msg){ if(parsing){ parsing = 0; parseerror("%s", msg); } error("%s", msg);} voidparserule(Rule *r){ r->qarg = estrdup(expand(nil, r->arg, nil)); switch(r->obj){ case OArg: case OAttr: case OData: case ODst: case OType: case OWdir: case OSrc: if(r->verb==VClient || r->verb==VStart || r->verb==VTo) parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]); if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete)) parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]); if(r->verb == VMatches){ r->regex = regcomp(r->qarg); return; } break; case OPlumb: if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo) parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]); break; }} intassignment(char *p){ char *var, *qval; int n; if(!isalpha(p[0])) return 0; for(var=p; isalnum(*p); p++) ; n = p-var; while(*p==' ' || *p=='\t') p++; if(*p++ != '=') return 0; while(*p==' ' || *p=='\t') p++; qval = expand(nil, p, nil); setvariable(var, n, p, qval); return 1;} intinclude(char *s){ char *t, *args[3], buf[128]; int n, fd; if(strncmp(s, "include", 7) != 0) return 0; /* either an include or an error */ n = tokenize(s, args, nelem(args)); if(n < 2) goto Err; if(strcmp(args[0], "include") != 0) goto Err; if(args[1][0] == '#') goto Err; if(n>2 && args[2][0] != '#') goto Err; t = args[1]; fd = open(t, OREAD); if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){ snprint(buf, sizeof buf, "/sys/lib/plumb/%s", t); t = buf; fd = open(t, OREAD); } if(fd < 0) parseerror("can't open %s for inclusion", t); pushinput(t, fd, nil); return 1; Err: parseerror("malformed include statement"); return 0;} Rule*readrule(int *eof){ Rule *rp; char *line, *p; char *word; Top: line = getline(); if(line == nil){ /* * if input is from string, and bytes remain (input->end is within string), * morerules() will pop input and save remaining data. otherwise pop * the stack here, and if there's more input, keep reading. */ if((input!=nil && input->end==nil) && popinput()) goto Top; *eof = 1; return nil; } input->lineno++; for(p=line; *p==' ' || *p=='\t'; p++) ; if(*p=='\0' || *p=='#') /* empty or comment line */ return nil; if(include(p)) goto Top; if(assignment(p)) return nil; rp = emalloc(sizeof(Rule)); /* object */ for(word=p; *p!=' ' && *p!='\t'; p++) if(*p == '\0') parseerror("malformed rule"); *p++ = '\0'; rp->obj = lookup(word, objects); if(rp->obj < 0){ if(strcmp(word, "kind") == 0) /* backwards compatibility */ rp->obj = OType; else parseerror("unknown object %s", word); } /* verb */ while(*p==' ' || *p=='\t') p++; for(word=p; *p!=' ' && *p!='\t'; p++) if(*p == '\0') parseerror("malformed rule"); *p++ = '\0'; rp->verb = lookup(word, verbs); if(rp->verb < 0) parseerror("unknown verb %s", word); /* argument */Lines 1–500 of 791. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h> #include <libc.h> #include <bio.h> #include <regexp.h> #include <thread.h> #include <ctype.h> #include <plumb.h> #include "plumber.h" typedef struct Input Input; typedef struct Var Var; struct Input { char *file; /* name of file */ Biobuf *fd; /* input buffer, if from real file */ uchar *s; /* input string, if from /mnt/plumb/rules */ uchar *end; /* end of input string */ int lineno; Input *next; /* file to read after EOF on this one */ }; struct Var { char *name; char *value; char *qvalue; }; static int parsing; static int nvars; static Var *vars; static Input *input; static char ebuf[4096]; char *badports[] = { ".", "..", "send", nil }; char *objects[] = { "arg", "attr", "data", "dst", "plumb", "src", "type", "wdir", nil }; char *verbs[] = { "add", "client", "delete", "is", "isdir", "isfile", "matches", "set", "start", "to", nil }; static void printinputstackrev(Input *in) { if(in == nil) return; printinputstackrev(in->next); fprint(2, "%s:%d: ", in->file, in->lineno); } void printinputstack(void) { printinputstackrev(input); } static void pushinput(char *name, int fd, uchar *str) { Input *in; int depth; depth = 0; for(in=input; in; in=in->next) if(depth++ >= 10) /* prevent deep C stack in plumber and bad include structure */ parseerror("include stack too deep; max 10"); in = emalloc(sizeof(Input)); in->file = estrdup(name); in->next = input; input = in; if(str) in->s = str; else{ in->fd = emalloc(sizeof(Biobuf)); if(Binit(in->fd, fd, OREAD) < 0) parseerror("can't initialize Bio for rules file: %r"); } } int popinput(void) { Input *in; in = input; if(in == nil) return 0; input = in->next; if(in->fd){ Bterm(in->fd); free(in->fd); } free(in->file); free(in); return 1; } int getc(void) { if(input == nil) return Beof; if(input->fd) return Bgetc(input->fd); if(input->s < input->end) return *(input->s)++; return -1; } char* getline(void) { static int n = 0; static char *s, *incl; int c, i; i = 0; for(;;){ c = getc(); if(c < 0) return nil; if(i == n){ n += 100; s = erealloc(s, n); } if(c<0 || c=='\0' || c=='\n') break; s[i++] = c; } s[i] = '\0'; return s; } int lookup(char *s, char *tab[]) { int i; for(i=0; tab[i]!=nil; i++) if(strcmp(s, tab[i])==0) return i; return -1; } Var* lookupvariable(char *s, int n) { int i; for(i=0; i<nvars; i++) if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0) return vars+i; return nil; } char* variable(char *s, int n) { Var *var; var = lookupvariable(s, n); if(var) return var->qvalue; return nil; } void setvariable(char *s, int n, char *val, char *qval) { Var *var; var = lookupvariable(s, n); if(var){ free(var->value); free(var->qvalue); }else{ vars = erealloc(vars, (nvars+1)*sizeof(Var)); var = vars+nvars++; var->name = emalloc(n+1); memmove(var->name, s, n); } var->value = estrdup(val); var->qvalue = estrdup(qval); } static char* nonnil(char *s) { if(s == nil) return ""; return s; } static char* filename(Exec *e, char *name) { static char *buf; /* rock to hold value so we don't leak the strings */ free(buf); /* if name is defined, used it */ if(name!=nil && name[0]!='\0'){ buf = estrdup(name); return cleanname(buf); } /* if data is an absolute file name, or wdir is empty, use it */ if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){ buf = estrdup(e->msg->data); return cleanname(buf); } buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1); sprint(buf, "%s/%s", e->msg->wdir, e->msg->data); return cleanname(buf); } char* dollar(Exec *e, char *s, int *namelen) { int n; static char *abuf; char *t; *namelen = 1; if(e!=nil && '0'<=s[0] && s[0]<='9') return nonnil(e->match[s[0]-'0']); for(t=s; isalnum(*t); t++) ; n = t-s; *namelen = n; if(e != nil){ if(n == 3){ if(memcmp(s, "src", 3) == 0) return nonnil(e->msg->src); if(memcmp(s, "dst", 3) == 0) return nonnil(e->msg->dst); if(memcmp(s, "dir", 3) == 0) return filename(e, e->dir); } if(n == 4){ if(memcmp(s, "attr", 4) == 0){ free(abuf); abuf = plumbpackattr(e->msg->attr); return nonnil(abuf); } if(memcmp(s, "data", 4) == 0) return nonnil(e->msg->data); if(memcmp(s, "file", 4) == 0) return filename(e, e->file); if(memcmp(s, "type", 4) == 0) return nonnil(e->msg->type); if(memcmp(s, "wdir", 3) == 0) return nonnil(e->msg->wdir); } } return variable(s, n); } /* expand one blank-terminated string, processing quotes and $ signs */ char* expand(Exec *e, char *s, char **ends) { char *p, *ep, *val; int namelen, quoting; p = ebuf; ep = ebuf+sizeof ebuf-1; quoting = 0; while(p<ep && *s!='\0' && (quoting || (*s!=' ' && *s!='\t'))){ if(*s == '\''){ s++; if(!quoting) quoting = 1; else if(*s == '\''){ *p++ = '\''; s++; }else quoting = 0; continue; } if(quoting || *s!='$'){ *p++ = *s++; continue; } s++; val = dollar(e, s, &namelen); if(val == nil){ *p++ = '$'; continue; } if(ep-p < strlen(val)) return "string-too-long"; strcpy(p, val); p += strlen(val); s += namelen; } if(ends) *ends = s; *p = '\0'; return ebuf; } void regerror(char *msg) { if(parsing){ parsing = 0; parseerror("%s", msg); } error("%s", msg); } void parserule(Rule *r) { r->qarg = estrdup(expand(nil, r->arg, nil)); switch(r->obj){ case OArg: case OAttr: case OData: case ODst: case OType: case OWdir: case OSrc: if(r->verb==VClient || r->verb==VStart || r->verb==VTo) parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]); if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete)) parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]); if(r->verb == VMatches){ r->regex = regcomp(r->qarg); return; } break; case OPlumb: if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo) parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]); break; } } int assignment(char *p) { char *var, *qval; int n; if(!isalpha(p[0])) return 0; for(var=p; isalnum(*p); p++) ; n = p-var; while(*p==' ' || *p=='\t') p++; if(*p++ != '=') return 0; while(*p==' ' || *p=='\t') p++; qval = expand(nil, p, nil); setvariable(var, n, p, qval); return 1; } int include(char *s) { char *t, *args[3], buf[128]; int n, fd; if(strncmp(s, "include", 7) != 0) return 0; /* either an include or an error */ n = tokenize(s, args, nelem(args)); if(n < 2) goto Err; if(strcmp(args[0], "include") != 0) goto Err; if(args[1][0] == '#') goto Err; if(n>2 && args[2][0] != '#') goto Err; t = args[1]; fd = open(t, OREAD); if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){ snprint(buf, sizeof buf, "/sys/lib/plumb/%s", t); t = buf; fd = open(t, OREAD); } if(fd < 0) parseerror("can't open %s for inclusion", t); pushinput(t, fd, nil); return 1; Err: parseerror("malformed include statement"); return 0; } Rule* readrule(int *eof) { Rule *rp; char *line, *p; char *word; Top: line = getline(); if(line == nil){ /* * if input is from string, and bytes remain (input->end is within string), * morerules() will pop input and save remaining data. otherwise pop * the stack here, and if there's more input, keep reading. */ if((input!=nil && input->end==nil) && popinput()) goto Top; *eof = 1; return nil; } input->lineno++; for(p=line; *p==' ' || *p=='\t'; p++) ; if(*p=='\0' || *p=='#') /* empty or comment line */ return nil; if(include(p)) goto Top; if(assignment(p)) return nil; rp = emalloc(sizeof(Rule)); /* object */ for(word=p; *p!=' ' && *p!='\t'; p++) if(*p == '\0') parseerror("malformed rule"); *p++ = '\0'; rp->obj = lookup(word, objects); if(rp->obj < 0){ if(strcmp(word, "kind") == 0) /* backwards compatibility */ rp->obj = OType; else parseerror("unknown object %s", word); } /* verb */ while(*p==' ' || *p=='\t') p++; for(word=p; *p!=' ' && *p!='\t'; p++) if(*p == '\0') parseerror("malformed rule"); *p++ = '\0'; rp->verb = lookup(word, verbs); if(rp->verb < 0) parseerror("unknown verb %s", word); /* argument */