Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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