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
printinputstackrev(Input * in)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
printinputstack(void)83 printinputstack(void)
84 {
85 	printinputstackrev(input);
86 }
87 
88 static void
pushinput(char * name,int fd,uchar * str)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
popinput(void)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
getc(void)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*
getline(void)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
lookup(char * s,char * tab[])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*
lookupvariable(char * s,int n)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*
variable(char * s,int n)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
setvariable(char * s,int n,char * val,char * qval)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*
nonnil(char * s)219 nonnil(char *s)
220 {
221 	if(s == nil)
222 		return "";
223 	return s;
224 }
225 
226 static char*
filename(Exec * e,char * name)227 filename(Exec *e, char *name)
228 {
229 	static char *buf;	/* rock to hold value so we don't leak the strings */
230 
231 	free(buf);
232 	/* if name is defined, used it */
233 	if(name!=nil && name[0]!='\0'){
234 		buf = estrdup(name);
235 		return cleanname(buf);
236 	}
237 	/* if data is an absolute file name, or wdir is empty, use it */
238 	if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){
239 		buf = estrdup(e->msg->data);
240 		return cleanname(buf);
241 	}
242 	buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1);
243 	sprint(buf, "%s/%s", e->msg->wdir, e->msg->data);
244 	return cleanname(buf);
245 }
246 
247 char*
dollar(Exec * e,char * s,int * namelen)248 dollar(Exec *e, char *s, int *namelen)
249 {
250 	int n;
251 	static char *abuf;
252 	char *t;
253 
254 	*namelen = 1;
255 	if(e!=nil && '0'<=s[0] && s[0]<='9')
256 		return nonnil(e->match[s[0]-'0']);
257 
258 	for(t=s; isalnum((uchar)*t); t++)
259 		;
260 	n = t-s;
261 	*namelen = n;
262 
263 	if(e != nil){
264 		if(n == 3){
265 			if(memcmp(s, "src", 3) == 0)
266 				return nonnil(e->msg->src);
267 			if(memcmp(s, "dst", 3) == 0)
268 				return nonnil(e->msg->dst);
269 			if(memcmp(s, "dir", 3) == 0)
270 				return filename(e, e->dir);
271 		}
272 		if(n == 4){
273 			if(memcmp(s, "attr", 4) == 0){
274 				free(abuf);
275 				abuf = plumbpackattr(e->msg->attr);
276 				return nonnil(abuf);
277 			}
278 			if(memcmp(s, "data", 4) == 0)
279 				return nonnil(e->msg->data);
280 			if(memcmp(s, "file", 4) == 0)
281 				return filename(e, e->file);
282 			if(memcmp(s, "type", 4) == 0)
283 				return nonnil(e->msg->type);
284 			if(memcmp(s, "wdir", 3) == 0)
285 				return nonnil(e->msg->wdir);
286 		}
287 	}
288 
289 	return variable(s, n);
290 }
291 
292 /* expand one blank-terminated string, processing quotes and $ signs */
293 char*
expand(Exec * e,char * s,char ** ends)294 expand(Exec *e, char *s, char **ends)
295 {
296 	char *p, *ep, *val;
297 	int namelen, quoting;
298 
299 	p = ebuf;
300 	ep = ebuf+sizeof ebuf-1;
301 	quoting = 0;
302 	while(p<ep && *s!='\0' && (quoting || (*s!=' ' && *s!='\t'))){
303 		if(*s == '\''){
304 			s++;
305 			if(!quoting)
306 				quoting = 1;
307 			else  if(*s == '\''){
308 				*p++ = '\'';
309 				s++;
310 			}else
311 				quoting = 0;
312 			continue;
313 		}
314 		if(quoting || *s!='$'){
315 			*p++ = *s++;
316 			continue;
317 		}
318 		s++;
319 		val = dollar(e, s, &namelen);
320 		if(val == nil){
321 			*p++ = '$';
322 			continue;
323 		}
324 		if(ep-p < strlen(val))
325 			return "string-too-long";
326 		strcpy(p, val);
327 		p += strlen(val);
328 		s += namelen;
329 	}
330 	if(ends)
331 		*ends = s;
332 	*p = '\0';
333 	return ebuf;
334 }
335 
336 void
regerror(char * msg)337 regerror(char *msg)
338 {
339 	if(parsing){
340 		parsing = 0;
341 		parseerror("%s", msg);
342 	}
343 	error("%s", msg);
344 }
345 
346 void
parserule(Rule * r)347 parserule(Rule *r)
348 {
349 	r->qarg = estrdup(expand(nil, r->arg, nil));
350 	switch(r->obj){
351 	case OArg:
352 	case OAttr:
353 	case OData:
354 	case ODst:
355 	case OType:
356 	case OWdir:
357 	case OSrc:
358 		if(r->verb==VClient || r->verb==VStart || r->verb==VTo)
359 			parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
360 		if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete))
361 			parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
362 		if(r->verb == VMatches){
363 			r->regex = regcomp(r->qarg);
364 			return;
365 		}
366 		break;
367 	case OPlumb:
368 		if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo)
369 			parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
370 		break;
371 	}
372 }
373 
374 int
assignment(char * p)375 assignment(char *p)
376 {
377 	char *var, *qval;
378 	int n;
379 
380 	if(!isalpha((uchar)p[0]))
381 		return 0;
382 	for(var=p; isalnum((uchar)*p); p++)
383 		;
384 	n = p-var;
385 	while(*p==' ' || *p=='\t')
386 			p++;
387 	if(*p++ != '=')
388 		return 0;
389 	while(*p==' ' || *p=='\t')
390 			p++;
391 	qval = expand(nil, p, nil);
392 	setvariable(var, n, p, qval);
393 	return 1;
394 }
395 
396 int
include(char * s)397 include(char *s)
398 {
399 	char *t, *args[3], buf[128];
400 	int n, fd;
401 
402 	if(strncmp(s, "include", 7) != 0)
403 		return 0;
404 	/* either an include or an error */
405 	n = tokenize(s, args, nelem(args));
406 	if(n < 2)
407 		goto Err;
408 	if(strcmp(args[0], "include") != 0)
409 		goto Err;
410 	if(args[1][0] == '#')
411 		goto Err;
412 	if(n>2 && args[2][0] != '#')
413 		goto Err;
414 	t = args[1];
415 	fd = open(t, OREAD);
416 	if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
417 		snprint(buf, sizeof buf, "#9/plumb/%s", t);
418 		t = unsharp(buf);
419 		fd = open(t, OREAD);
420 	}
421 	if(fd < 0)
422 		parseerror("can't open %s for inclusion", t);
423 	pushinput(t, fd, nil);
424 	return 1;
425 
426     Err:
427 	parseerror("malformed include statement");
428 	return 0;
429 }
430 
431 Rule*
readrule(int * eof)432 readrule(int *eof)
433 {
434 	Rule *rp;
435 	char *line, *p;
436 	char *word;
437 
438 Top:
439 	line = getline();
440 	if(line == nil){
441 		/*
442 		 * if input is from string, and bytes remain (input->end is within string),
443 		 * morerules() will pop input and save remaining data.  otherwise pop
444 		 * the stack here, and if there's more input, keep reading.
445 		 */
446 		if((input!=nil && input->end==nil) && popinput())
447 			goto Top;
448 		*eof = 1;
449 		return nil;
450 	}
451 	input->lineno++;
452 
453 	for(p=line; *p==' ' || *p=='\t'; p++)
454 		;
455 	if(*p=='\0' || *p=='#')	/* empty or comment line */
456 		return nil;
457 
458 	if(include(p))
459 		goto Top;
460 
461 	if(assignment(p))
462 		return nil;
463 
464 	rp = emalloc(sizeof(Rule));
465 
466 	/* object */
467 	for(word=p; *p!=' ' && *p!='\t'; p++)
468 		if(*p == '\0')
469 			parseerror("malformed rule");
470 	*p++ = '\0';
471 	rp->obj = lookup(word, objects);
472 	if(rp->obj < 0){
473 		if(strcmp(word, "kind") == 0)	/* backwards compatibility */
474 			rp->obj = OType;
475 		else
476 			parseerror("unknown object %s", word);
477 	}
478 
479 	/* verb */
480 	while(*p==' ' || *p=='\t')
481 		p++;
482 	for(word=p; *p!=' ' && *p!='\t'; p++)
483 		if(*p == '\0')
484 			parseerror("malformed rule");
485 	*p++ = '\0';
486 	rp->verb = lookup(word, verbs);
487 	if(rp->verb < 0)
488 		parseerror("unknown verb %s", word);
489 
490 	/* argument */
491 	while(*p==' ' || *p=='\t')
492 		p++;
493 	if(*p == '\0')
494 		parseerror("malformed rule");
495 	rp->arg = estrdup(p);
496 
497 	parserule(rp);
498 
499 	return rp;
500 }
501 
502 void
freerule(Rule * r)503 freerule(Rule *r)
504 {
505 	free(r->arg);
506 	free(r->qarg);
507 	free(r->regex);
508 }
509 
510 void
freerules(Rule ** r)511 freerules(Rule **r)
512 {
513 	while(*r)
514 		freerule(*r++);
515 }
516 
517 void
freeruleset(Ruleset * rs)518 freeruleset(Ruleset *rs)
519 {
520 	freerules(rs->pat);
521 	free(rs->pat);
522 	freerules(rs->act);
523 	free(rs->act);
524 	free(rs->port);
525 	free(rs);
526 }
527 
528 Ruleset*
readruleset(void)529 readruleset(void)
530 {
531 	Ruleset *rs;
532 	Rule *r;
533 	int eof, inrule, i, ncmd;
534 	char *plan9root;
535 
536 	plan9root = get9root();
537 	if(plan9root)
538 		setvariable("plan9", 5, plan9root, plan9root);
539 
540    Again:
541 	eof = 0;
542 	rs = emalloc(sizeof(Ruleset));
543 	rs->pat = emalloc(sizeof(Rule*));
544 	rs->act = emalloc(sizeof(Rule*));
545 	inrule = 0;
546 	ncmd = 0;
547 	for(;;){
548 		r = readrule(&eof);
549 		if(eof)
550 			break;
551 		if(r==nil){
552 			if(inrule)
553 				break;
554 			continue;
555 		}
556 		inrule = 1;
557 		switch(r->obj){
558 		case OArg:
559 		case OAttr:
560 		case OData:
561 		case ODst:
562 		case OType:
563 		case OWdir:
564 		case OSrc:
565 			rs->npat++;
566 			rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
567 			rs->pat[rs->npat-1] = r;
568 			rs->pat[rs->npat] = nil;
569 			break;
570 		case OPlumb:
571 			rs->nact++;
572 			rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
573 			rs->act[rs->nact-1] = r;
574 			rs->act[rs->nact] = nil;
575 			if(r->verb == VTo){
576 				if(rs->npat>0 && rs->port != nil)	/* npat==0 implies port declaration */
577 					parseerror("too many ports");
578 				if(lookup(r->qarg, badports) >= 0)
579 					parseerror("illegal port name %s", r->qarg);
580 				rs->port = estrdup(r->qarg);
581 			}else
582 				ncmd++;	/* start or client rule */
583 			break;
584 		}
585 	}
586 	if(ncmd > 1){
587 		freeruleset(rs);
588 		parseerror("ruleset has more than one client or start action");
589 	}
590 	if(rs->npat>0 && rs->nact>0)
591 		return rs;
592 	if(rs->npat==0 && rs->nact==0){
593 		freeruleset(rs);
594 		return nil;
595 	}
596 	if(rs->nact==0 || rs->port==nil){
597 		freeruleset(rs);
598 		parseerror("ruleset must have patterns and actions");
599 		return nil;
600 	}
601 
602 	/* declare ports */
603 	for(i=0; i<rs->nact; i++)
604 		if(rs->act[i]->verb != VTo){
605 			freeruleset(rs);
606 			parseerror("ruleset must have actions");
607 			return nil;
608 		}
609 	for(i=0; i<rs->nact; i++)
610 		addport(rs->act[i]->qarg);
611 	freeruleset(rs);
612 	goto Again;
613 }
614 
615 Ruleset**
readrules(char * name,int fd)616 readrules(char *name, int fd)
617 {
618 	Ruleset *rs, **rules;
619 	int n;
620 
621 	parsing = 1;
622 	pushinput(name, fd, nil);
623 	rules = emalloc(sizeof(Ruleset*));
624 	for(n=0; (rs=readruleset())!=nil; n++){
625 		rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
626 		rules[n] = rs;
627 		rules[n+1] = nil;
628 	}
629 	popinput();
630 	parsing = 0;
631 	return rules;
632 }
633 
634 char*
concat(char * s,char * t)635 concat(char *s, char *t)
636 {
637 	if(t == nil)
638 		return s;
639 	if(s == nil)
640 		s = estrdup(t);
641 	else{
642 		s = erealloc(s, strlen(s)+strlen(t)+1);
643 		strcat(s, t);
644 	}
645 	return s;
646 }
647 
648 char*
printpat(Rule * r)649 printpat(Rule *r)
650 {
651 	char *s;
652 
653 	s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
654 	sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
655 	return s;
656 }
657 
658 char*
printvar(Var * v)659 printvar(Var *v)
660 {
661 	char *s;
662 
663 	s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
664 	sprint(s, "%s=%s\n\n", v->name, v->value);
665 	return s;
666 }
667 
668 char*
printrule(Ruleset * r)669 printrule(Ruleset *r)
670 {
671 	int i;
672 	char *s;
673 
674 	s = nil;
675 	for(i=0; i<r->npat; i++)
676 		s = concat(s, printpat(r->pat[i]));
677 	for(i=0; i<r->nact; i++)
678 		s = concat(s, printpat(r->act[i]));
679 	s = concat(s, "\n");
680 	return s;
681 }
682 
683 char*
printport(char * port)684 printport(char *port)
685 {
686 	char *s;
687 
688 	s = nil;
689 	s = concat(s, "plumb to ");
690 	s = concat(s, port);
691 	s = concat(s, "\n");
692 	return s;
693 }
694 
695 char*
printrules(void)696 printrules(void)
697 {
698 	int i;
699 	char *s;
700 
701 	s = nil;
702 	for(i=0; i<nvars; i++)
703 		s = concat(s, printvar(&vars[i]));
704 	for(i=0; i<nports; i++)
705 		s = concat(s, printport(ports[i]));
706 	s = concat(s, "\n");
707 	for(i=0; rules[i]; i++)
708 		s = concat(s, printrule(rules[i]));
709 	return s;
710 }
711 
712 char*
stringof(char * s,int n)713 stringof(char *s, int n)
714 {
715 	char *t;
716 
717 	t = emalloc(n+1);
718 	memmove(t, s, n);
719 	return t;
720 }
721 
722 uchar*
morerules(uchar * text,int done)723 morerules(uchar *text, int done)
724 {
725 	int n;
726 	Ruleset *rs;
727 	uchar *otext, *s, *endofrule;
728 
729 	pushinput("<rules input>", -1, text);
730 	if(done)
731 		input->end = text+strlen((char*)text);
732 	else{
733 		/*
734 		 * Help user by sending any full rules to parser so any parse errors will
735 		 * occur on write rather than close. A heuristic will do: blank line ends rule.
736 		 */
737 		endofrule = nil;
738 		for(s=text; *s!='\0'; s++)
739 			if(*s=='\n' && *(s+1)=='\n')
740 				endofrule = s+2;
741 		if(endofrule == nil)
742 			return text;
743 		input->end = endofrule;
744 	}
745 	for(n=0; rules[n]; n++)
746 		;
747 	while((rs=readruleset()) != nil){
748 		rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
749 		rules[n++] = rs;
750 		rules[n] = nil;
751 	}
752 	otext =text;
753 	if(input == nil)
754 		text = (uchar*)estrdup("");
755 	else
756 		text = (uchar*)estrdup((char*)input->end);
757 	popinput();
758 	free(otext);
759 	return text;
760 }
761 
762 char*
writerules(char * s,int n)763 writerules(char *s, int n)
764 {
765 	static uchar *text;
766 	char *tmp;
767 
768 	free(lasterror);
769 	lasterror = nil;
770 	parsing = 1;
771 	if(setjmp(parsejmp) == 0){
772 		tmp = stringof(s, n);
773 		text = (uchar*)concat((char*)text, tmp);
774 		free(tmp);
775 		text = morerules(text, n==0);
776 	}
777 	if(s == nil){
778 		free(text);
779 		text = nil;
780 	}
781 	parsing = 0;
782 	makeports(rules);
783 	return lasterror;
784 }
785