plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

rules.c (13776B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <regexp.h>
      5 #include <thread.h>
      6 #include <ctype.h>
      7 #include <plumb.h>
      8 #include "plumber.h"
      9 
     10 typedef struct Input Input;
     11 typedef struct Var Var;
     12 
     13 struct Input
     14 {
     15 	char		*file;		/* name of file */
     16 	Biobuf	*fd;		/* input buffer, if from real file */
     17 	uchar	*s;		/* input string, if from /mnt/plumb/rules */
     18 	uchar	*end;	/* end of input string */
     19 	int		lineno;
     20 	Input	*next;	/* file to read after EOF on this one */
     21 };
     22 
     23 struct Var
     24 {
     25 	char	*name;
     26 	char	*value;
     27 	char *qvalue;
     28 };
     29 
     30 static int		parsing;
     31 static int		nvars;
     32 static Var		*vars;
     33 static Input	*input;
     34 
     35 static char 	ebuf[4096];
     36 
     37 char *badports[] =
     38 {
     39 	".",
     40 	"..",
     41 	"send",
     42 	nil
     43 };
     44 
     45 char *objects[] =
     46 {
     47 	"arg",
     48 	"attr",
     49 	"data",
     50 	"dst",
     51 	"plumb",
     52 	"src",
     53 	"type",
     54 	"wdir",
     55 	nil
     56 };
     57 
     58 char *verbs[] =
     59 {
     60 	"add",
     61 	"client",
     62 	"delete",
     63 	"is",
     64 	"isdir",
     65 	"isfile",
     66 	"matches",
     67 	"set",
     68 	"start",
     69 	"to",
     70 	nil
     71 };
     72 
     73 static void
     74 printinputstackrev(Input *in)
     75 {
     76 	if(in == nil)
     77 		return;
     78 	printinputstackrev(in->next);
     79 	fprint(2, "%s:%d: ", in->file, in->lineno);
     80 }
     81 
     82 void
     83 printinputstack(void)
     84 {
     85 	printinputstackrev(input);
     86 }
     87 
     88 static void
     89 pushinput(char *name, int fd, uchar *str)
     90 {
     91 	Input *in;
     92 	int depth;
     93 
     94 	depth = 0;
     95 	for(in=input; in; in=in->next)
     96 		if(depth++ >= 10)	/* prevent deep C stack in plumber and bad include structure */
     97 			parseerror("include stack too deep; max 10");
     98 
     99 	in = emalloc(sizeof(Input));
    100 	in->file = estrdup(name);
    101 	in->next = input;
    102 	input = in;
    103 	if(str)
    104 		in->s = str;
    105 	else{
    106 		in->fd = emalloc(sizeof(Biobuf));
    107 		if(Binit(in->fd, fd, OREAD) < 0)
    108 			parseerror("can't initialize Bio for rules file: %r");
    109 	}
    110 
    111 }
    112 
    113 int
    114 popinput(void)
    115 {
    116 	Input *in;
    117 
    118 	in = input;
    119 	if(in == nil)
    120 		return 0;
    121 	input = in->next;
    122 	if(in->fd){
    123 		Bterm(in->fd);
    124 		free(in->fd);
    125 	}
    126 	free(in);
    127 	return 1;
    128 }
    129 
    130 static int
    131 getc(void)
    132 {
    133 	if(input == nil)
    134 		return Beof;
    135 	if(input->fd)
    136 		return Bgetc(input->fd);
    137 	if(input->s < input->end)
    138 		return *(input->s)++;
    139 	return -1;
    140 }
    141 
    142 char*
    143 getline(void)
    144 {
    145 	static int n = 0;
    146 	static char *s /*, *incl*/;
    147 	int c, i;
    148 
    149 	i = 0;
    150 	for(;;){
    151 		c = getc();
    152 		if(c < 0)
    153 			return nil;
    154 		if(i == n){
    155 			n += 100;
    156 			s = erealloc(s, n);
    157 		}
    158 		if(c<0 || c=='\0' || c=='\n')
    159 			break;
    160 		s[i++] = c;
    161 	}
    162 	s[i] = '\0';
    163 	return s;
    164 }
    165 
    166 int
    167 lookup(char *s, char *tab[])
    168 {
    169 	int i;
    170 
    171 	for(i=0; tab[i]!=nil; i++)
    172 		if(strcmp(s, tab[i])==0)
    173 			return i;
    174 	return -1;
    175 }
    176 
    177 Var*
    178 lookupvariable(char *s, int n)
    179 {
    180 	int i;
    181 
    182 	for(i=0; i<nvars; i++)
    183 		if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0)
    184 			return vars+i;
    185 	return nil;
    186 }
    187 
    188 char*
    189 variable(char *s, int n)
    190 {
    191 	Var *var;
    192 
    193 	var = lookupvariable(s, n);
    194 	if(var)
    195 		return var->qvalue;
    196 	return nil;
    197 }
    198 
    199 void
    200 setvariable(char  *s, int n, char *val, char *qval)
    201 {
    202 	Var *var;
    203 
    204 	var = lookupvariable(s, n);
    205 	if(var){
    206 		free(var->value);
    207 		free(var->qvalue);
    208 	}else{
    209 		vars = erealloc(vars, (nvars+1)*sizeof(Var));
    210 		var = vars+nvars++;
    211 		var->name = emalloc(n+1);
    212 		memmove(var->name, s, n);
    213 	}
    214 	var->value = estrdup(val);
    215 	var->qvalue = estrdup(qval);
    216 }
    217 
    218 static char*
    219 scanvarname(char *s)
    220 {
    221 	if(isalpha((uchar)*s) || *s=='_')
    222 		do
    223 			s++;
    224 		while(isalnum((uchar)*s) || *s=='_');
    225 	return s;
    226 }
    227 
    228 static char*
    229 nonnil(char *s)
    230 {
    231 	if(s == nil)
    232 		return "";
    233 	return s;
    234 }
    235 
    236 static char*
    237 filename(Exec *e, char *name)
    238 {
    239 	static char *buf;	/* rock to hold value so we don't leak the strings */
    240 
    241 	free(buf);
    242 	/* if name is defined, used it */
    243 	if(name!=nil && name[0]!='\0'){
    244 		buf = estrdup(name);
    245 		return cleanname(buf);
    246 	}
    247 	/* if data is an absolute file name, or wdir is empty, use it */
    248 	if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){
    249 		buf = estrdup(e->msg->data);
    250 		return cleanname(buf);
    251 	}
    252 	buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1);
    253 	sprint(buf, "%s/%s", e->msg->wdir, e->msg->data);
    254 	return cleanname(buf);
    255 }
    256 
    257 char*
    258 dollar(Exec *e, char *s, int *namelen)
    259 {
    260 	int n;
    261 	ulong m;
    262 	char *t;
    263 	static char *abuf;
    264 
    265 	if(e!=nil && '0'<=s[0] && s[0]<='9'){
    266 		m = strtoul(s, &t, 10);
    267 		*namelen = t-s;
    268 		if(t==s || m>=NMATCHSUBEXP)
    269 			return "";
    270 		return nonnil(e->match[m]);
    271 	}
    272 
    273 	n = scanvarname(s)-s;
    274 	*namelen = n;
    275 	if(n == 0)
    276 		return nil;
    277 
    278 	if(e != nil){
    279 		if(n == 3){
    280 			if(memcmp(s, "src", 3) == 0)
    281 				return nonnil(e->msg->src);
    282 			if(memcmp(s, "dst", 3) == 0)
    283 				return nonnil(e->msg->dst);
    284 			if(memcmp(s, "dir", 3) == 0)
    285 				return filename(e, e->dir);
    286 		}
    287 		if(n == 4){
    288 			if(memcmp(s, "attr", 4) == 0){
    289 				free(abuf);
    290 				abuf = plumbpackattr(e->msg->attr);
    291 				return nonnil(abuf);
    292 			}
    293 			if(memcmp(s, "data", 4) == 0)
    294 				return nonnil(e->msg->data);
    295 			if(memcmp(s, "file", 4) == 0)
    296 				return filename(e, e->file);
    297 			if(memcmp(s, "type", 4) == 0)
    298 				return nonnil(e->msg->type);
    299 			if(memcmp(s, "wdir", 3) == 0)
    300 				return nonnil(e->msg->wdir);
    301 		}
    302 	}
    303 
    304 	return variable(s, n);
    305 }
    306 
    307 static void
    308 ruleerror(char *msg)
    309 {
    310 	if(parsing){
    311 		parsing = 0;
    312 		parseerror("%s", msg);
    313 	}
    314 	error("%s", msg);
    315 }
    316 
    317 /* expand one blank-terminated string, processing quotes and $ signs */
    318 char*
    319 expand(Exec *e, char *s, char **ends)
    320 {
    321 	char *p, *ep, *val;
    322 	int namelen, vallen, quoting, inputleft;
    323 
    324 	p = ebuf;
    325 	ep = ebuf+sizeof ebuf-1;
    326 	quoting = 0;
    327 	for(;;){
    328 		inputleft = (*s!='\0' && (quoting || (*s!=' ' && *s!='\t')));
    329 		if(!inputleft || p==ep)
    330 			break;
    331 		if(*s == '\''){
    332 			s++;
    333 			if(!quoting)
    334 				quoting = 1;
    335 			else if(*s == '\''){
    336 				*p++ = '\'';
    337 				s++;
    338 			}else
    339 				quoting = 0;
    340 			continue;
    341 		}
    342 		if(quoting || *s!='$'){
    343 			*p++ = *s++;
    344 			continue;
    345 		}
    346 		s++;
    347 		val = dollar(e, s, &namelen);
    348 		if(val == nil){
    349 			*p++ = '$';
    350 			continue;
    351 		}
    352 		vallen = strlen(val);
    353 		if(ep-p < vallen)
    354 			break;
    355 		strcpy(p, val);
    356 		p += vallen;
    357 		s += namelen;
    358 	}
    359 	if(inputleft)
    360 		ruleerror("expanded string too long");
    361 	else if(quoting)
    362 		ruleerror("runaway quoted string literal");
    363 	if(ends)
    364 		*ends = s;
    365 	*p = '\0';
    366 	return ebuf;
    367 }
    368 
    369 void
    370 regerror(char *msg)
    371 {
    372 	ruleerror(msg);
    373 }
    374 
    375 void
    376 parserule(Rule *r)
    377 {
    378 	r->qarg = estrdup(expand(nil, r->arg, nil));
    379 	switch(r->obj){
    380 	case OArg:
    381 	case OAttr:
    382 	case OData:
    383 	case ODst:
    384 	case OType:
    385 	case OWdir:
    386 	case OSrc:
    387 		if(r->verb==VClient || r->verb==VStart || r->verb==VTo)
    388 			parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
    389 		if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete))
    390 			parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
    391 		if(r->verb == VMatches){
    392 			r->regex = regcomp(r->qarg);
    393 			return;
    394 		}
    395 		break;
    396 	case OPlumb:
    397 		if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo)
    398 			parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
    399 		break;
    400 	}
    401 }
    402 
    403 int
    404 assignment(char *p)
    405 {
    406 	char *var, *qval;
    407 	int n;
    408 
    409 	var = p;
    410 	p = scanvarname(p);
    411 	n = p-var;
    412 	if(n == 0)
    413 		return 0;
    414 	while(*p==' ' || *p=='\t')
    415 		p++;
    416 	if(*p++ != '=')
    417 		return 0;
    418 	while(*p==' ' || *p=='\t')
    419 		p++;
    420 	qval = expand(nil, p, nil);
    421 	setvariable(var, n, p, qval);
    422 	return 1;
    423 }
    424 
    425 int
    426 include(char *s)
    427 {
    428 	char *t, *args[3], buf[128];
    429 	int n, fd;
    430 
    431 	if(strncmp(s, "include", 7) != 0)
    432 		return 0;
    433 	/* either an include or an error */
    434 	n = tokenize(s, args, nelem(args));
    435 	if(n < 2)
    436 		goto Err;
    437 	if(strcmp(args[0], "include") != 0)
    438 		goto Err;
    439 	if(args[1][0] == '#')
    440 		goto Err;
    441 	if(n>2 && args[2][0] != '#')
    442 		goto Err;
    443 	t = args[1];
    444 	fd = open(t, OREAD);
    445 	if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
    446 		snprint(buf, sizeof buf, "#9/plumb/%s", t);
    447 		t = unsharp(buf);
    448 		fd = open(t, OREAD);
    449 	}
    450 	if(fd < 0)
    451 		parseerror("can't open %s for inclusion", t);
    452 	pushinput(t, fd, nil);
    453 	return 1;
    454 
    455     Err:
    456 	parseerror("malformed include statement");
    457 	return 0;
    458 }
    459 
    460 Rule*
    461 readrule(int *eof)
    462 {
    463 	Rule *rp;
    464 	char *line, *p;
    465 	char *word;
    466 
    467 Top:
    468 	line = getline();
    469 	if(line == nil){
    470 		/*
    471 		 * if input is from string, and bytes remain (input->end is within string),
    472 		 * morerules() will pop input and save remaining data.  otherwise pop
    473 		 * the stack here, and if there's more input, keep reading.
    474 		 */
    475 		if((input!=nil && input->end==nil) && popinput())
    476 			goto Top;
    477 		*eof = 1;
    478 		return nil;
    479 	}
    480 	input->lineno++;
    481 
    482 	for(p=line; *p==' ' || *p=='\t'; p++)
    483 		;
    484 	if(*p=='\0' || *p=='#')	/* empty or comment line */
    485 		return nil;
    486 
    487 	if(include(p))
    488 		goto Top;
    489 
    490 	if(assignment(p))
    491 		return nil;
    492 
    493 	rp = emalloc(sizeof(Rule));
    494 
    495 	/* object */
    496 	for(word=p; *p!=' ' && *p!='\t'; p++)
    497 		if(*p == '\0')
    498 			parseerror("malformed rule");
    499 	*p++ = '\0';
    500 	rp->obj = lookup(word, objects);
    501 	if(rp->obj < 0){
    502 		if(strcmp(word, "kind") == 0)	/* backwards compatibility */
    503 			rp->obj = OType;
    504 		else
    505 			parseerror("unknown object %s", word);
    506 	}
    507 
    508 	/* verb */
    509 	while(*p==' ' || *p=='\t')
    510 		p++;
    511 	for(word=p; *p!=' ' && *p!='\t'; p++)
    512 		if(*p == '\0')
    513 			parseerror("malformed rule");
    514 	*p++ = '\0';
    515 	rp->verb = lookup(word, verbs);
    516 	if(rp->verb < 0)
    517 		parseerror("unknown verb %s", word);
    518 
    519 	/* argument */
    520 	while(*p==' ' || *p=='\t')
    521 		p++;
    522 	if(*p == '\0')
    523 		parseerror("malformed rule");
    524 	rp->arg = estrdup(p);
    525 
    526 	parserule(rp);
    527 
    528 	return rp;
    529 }
    530 
    531 void
    532 freerule(Rule *r)
    533 {
    534 	free(r->arg);
    535 	free(r->qarg);
    536 	free(r->regex);
    537 }
    538 
    539 void
    540 freerules(Rule **r)
    541 {
    542 	while(*r)
    543 		freerule(*r++);
    544 }
    545 
    546 void
    547 freeruleset(Ruleset *rs)
    548 {
    549 	freerules(rs->pat);
    550 	free(rs->pat);
    551 	freerules(rs->act);
    552 	free(rs->act);
    553 	free(rs->port);
    554 	free(rs);
    555 }
    556 
    557 Ruleset*
    558 readruleset(void)
    559 {
    560 	Ruleset *rs;
    561 	Rule *r;
    562 	int eof, inrule, i, ncmd;
    563 	char *plan9root;
    564 
    565 	plan9root = get9root();
    566 	if(plan9root)
    567 		setvariable("plan9", 5, plan9root, plan9root);
    568 
    569    Again:
    570 	eof = 0;
    571 	rs = emalloc(sizeof(Ruleset));
    572 	rs->pat = emalloc(sizeof(Rule*));
    573 	rs->act = emalloc(sizeof(Rule*));
    574 	inrule = 0;
    575 	ncmd = 0;
    576 	for(;;){
    577 		r = readrule(&eof);
    578 		if(eof)
    579 			break;
    580 		if(r==nil){
    581 			if(inrule)
    582 				break;
    583 			continue;
    584 		}
    585 		inrule = 1;
    586 		switch(r->obj){
    587 		case OArg:
    588 		case OAttr:
    589 		case OData:
    590 		case ODst:
    591 		case OType:
    592 		case OWdir:
    593 		case OSrc:
    594 			rs->npat++;
    595 			rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
    596 			rs->pat[rs->npat-1] = r;
    597 			rs->pat[rs->npat] = nil;
    598 			break;
    599 		case OPlumb:
    600 			rs->nact++;
    601 			rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
    602 			rs->act[rs->nact-1] = r;
    603 			rs->act[rs->nact] = nil;
    604 			if(r->verb == VTo){
    605 				if(rs->npat>0 && rs->port != nil)	/* npat==0 implies port declaration */
    606 					parseerror("too many ports");
    607 				if(lookup(r->qarg, badports) >= 0)
    608 					parseerror("illegal port name %s", r->qarg);
    609 				rs->port = estrdup(r->qarg);
    610 			}else
    611 				ncmd++;	/* start or client rule */
    612 			break;
    613 		}
    614 	}
    615 	if(ncmd > 1){
    616 		freeruleset(rs);
    617 		parseerror("ruleset has more than one client or start action");
    618 	}
    619 	if(rs->npat>0 && rs->nact>0)
    620 		return rs;
    621 	if(rs->npat==0 && rs->nact==0){
    622 		freeruleset(rs);
    623 		return nil;
    624 	}
    625 	if(rs->nact==0 || rs->port==nil){
    626 		freeruleset(rs);
    627 		parseerror("ruleset must have patterns and actions");
    628 		return nil;
    629 	}
    630 
    631 	/* declare ports */
    632 	for(i=0; i<rs->nact; i++)
    633 		if(rs->act[i]->verb != VTo){
    634 			freeruleset(rs);
    635 			parseerror("ruleset must have actions");
    636 			return nil;
    637 		}
    638 	for(i=0; i<rs->nact; i++)
    639 		addport(rs->act[i]->qarg);
    640 	freeruleset(rs);
    641 	goto Again;
    642 }
    643 
    644 Ruleset**
    645 readrules(char *name, int fd)
    646 {
    647 	Ruleset *rs, **rules;
    648 	int n;
    649 
    650 	parsing = 1;
    651 	pushinput(name, fd, nil);
    652 	rules = emalloc(sizeof(Ruleset*));
    653 	for(n=0; (rs=readruleset())!=nil; n++){
    654 		rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
    655 		rules[n] = rs;
    656 		rules[n+1] = nil;
    657 	}
    658 	popinput();
    659 	parsing = 0;
    660 	return rules;
    661 }
    662 
    663 char*
    664 concat(char *s, char *t)
    665 {
    666 	if(t == nil)
    667 		return s;
    668 	if(s == nil)
    669 		s = estrdup(t);
    670 	else{
    671 		s = erealloc(s, strlen(s)+strlen(t)+1);
    672 		strcat(s, t);
    673 	}
    674 	return s;
    675 }
    676 
    677 char*
    678 printpat(Rule *r)
    679 {
    680 	char *s;
    681 
    682 	s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
    683 	sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
    684 	return s;
    685 }
    686 
    687 char*
    688 printvar(Var *v)
    689 {
    690 	char *s;
    691 
    692 	s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
    693 	sprint(s, "%s=%s\n\n", v->name, v->value);
    694 	return s;
    695 }
    696 
    697 char*
    698 printrule(Ruleset *r)
    699 {
    700 	int i;
    701 	char *s;
    702 
    703 	s = nil;
    704 	for(i=0; i<r->npat; i++)
    705 		s = concat(s, printpat(r->pat[i]));
    706 	for(i=0; i<r->nact; i++)
    707 		s = concat(s, printpat(r->act[i]));
    708 	s = concat(s, "\n");
    709 	return s;
    710 }
    711 
    712 char*
    713 printport(char *port)
    714 {
    715 	char *s;
    716 
    717 	s = nil;
    718 	s = concat(s, "plumb to ");
    719 	s = concat(s, port);
    720 	s = concat(s, "\n");
    721 	return s;
    722 }
    723 
    724 char*
    725 printrules(void)
    726 {
    727 	int i;
    728 	char *s;
    729 
    730 	s = nil;
    731 	for(i=0; i<nvars; i++)
    732 		s = concat(s, printvar(&vars[i]));
    733 	for(i=0; i<nports; i++)
    734 		s = concat(s, printport(ports[i]));
    735 	s = concat(s, "\n");
    736 	for(i=0; rules[i]; i++)
    737 		s = concat(s, printrule(rules[i]));
    738 	return s;
    739 }
    740 
    741 char*
    742 stringof(char *s, int n)
    743 {
    744 	char *t;
    745 
    746 	t = emalloc(n+1);
    747 	memmove(t, s, n);
    748 	return t;
    749 }
    750 
    751 uchar*
    752 morerules(uchar *text, int done)
    753 {
    754 	int n;
    755 	Ruleset *rs;
    756 	uchar *otext, *s, *endofrule;
    757 
    758 	pushinput("<rules input>", -1, text);
    759 	if(done)
    760 		input->end = text+strlen((char*)text);
    761 	else{
    762 		/*
    763 		 * Help user by sending any full rules to parser so any parse errors will
    764 		 * occur on write rather than close. A heuristic will do: blank line ends rule.
    765 		 */
    766 		endofrule = nil;
    767 		for(s=text; *s!='\0'; s++)
    768 			if(*s=='\n' && *(s+1)=='\n')
    769 				endofrule = s+2;
    770 		if(endofrule == nil)
    771 			return text;
    772 		input->end = endofrule;
    773 	}
    774 	for(n=0; rules[n]; n++)
    775 		;
    776 	while((rs=readruleset()) != nil){
    777 		rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
    778 		rules[n++] = rs;
    779 		rules[n] = nil;
    780 	}
    781 	otext =text;
    782 	if(input == nil)
    783 		text = (uchar*)estrdup("");
    784 	else
    785 		text = (uchar*)estrdup((char*)input->end);
    786 	popinput();
    787 	free(otext);
    788 	return text;
    789 }
    790 
    791 char*
    792 writerules(char *s, int n)
    793 {
    794 	static uchar *text;
    795 	char *tmp;
    796 
    797 	free(lasterror);
    798 	lasterror = nil;
    799 	parsing = 1;
    800 	if(setjmp(parsejmp) == 0){
    801 		tmp = stringof(s, n);
    802 		text = (uchar*)concat((char*)text, tmp);
    803 		free(tmp);
    804 		text = morerules(text, n==0);
    805 	}
    806 	if(s == nil){
    807 		free(text);
    808 		text = nil;
    809 	}
    810 	parsing = 0;
    811 	makeports(rules);
    812 	return lasterror;
    813 }