]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/plumb/rules.c
rsa: rename getkey() to getrsakey(), document rsa2csr in rsa(8)
[plan9front.git] / sys / src / cmd / plumb / rules.c
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", 4) == 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                                 free(rs->port);
577                                 rs->port = estrdup(r->qarg);
578                         }else
579                                 ncmd++; /* start or client rule */
580                         break;
581                 }
582         }
583         if(ncmd > 1){
584                 freeruleset(rs);
585                 parseerror("ruleset has more than one client or start action");
586         }
587         if(rs->npat>0 && rs->nact>0)
588                 return rs;
589         if(rs->npat==0 && rs->nact==0){
590                 freeruleset(rs);
591                 return nil;
592         }
593         if(rs->nact==0 || rs->port==nil){
594                 freeruleset(rs);
595                 parseerror("ruleset must have patterns and actions");
596                 return nil;
597         }
598
599         /* declare ports */
600         for(i=0; i<rs->nact; i++)
601                 if(rs->act[i]->verb != VTo){
602                         freeruleset(rs);
603                         parseerror("ruleset must have actions");
604                         return nil;
605                 }
606         for(i=0; i<rs->nact; i++)
607                 addport(rs->act[i]->qarg);
608         freeruleset(rs);
609         goto Again;
610 }
611
612 Ruleset**
613 readrules(char *name, int fd)
614 {
615         Ruleset *rs, **rules;
616         int n;
617
618         parsing = 1;
619         pushinput(name, fd, nil);
620         rules = emalloc(sizeof(Ruleset*));
621         for(n=0; (rs=readruleset())!=nil; n++){
622                 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
623                 rules[n] = rs;
624                 rules[n+1] = nil;
625         }
626         popinput();
627         parsing = 0;
628         return rules;
629 }
630
631 char*
632 concat(char *s, char *t)
633 {
634         if(t == nil)
635                 return s;
636         if(s == nil)
637                 s = estrdup(t);
638         else{
639                 s = erealloc(s, strlen(s)+strlen(t)+1);
640                 strcat(s, t);
641         }
642         return s;
643 }
644
645 char*
646 printpat(Rule *r)
647 {
648         char *s;
649
650         s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
651         sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
652         return s;
653 }
654
655 char*
656 printvar(Var *v)
657 {
658         char *s;
659
660         s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
661         sprint(s, "%s=%s\n\n", v->name, v->value);
662         return s;
663 }
664
665 char*
666 printrule(Ruleset *r)
667 {
668         int i;
669         char *s;
670
671         s = nil;
672         for(i=0; i<r->npat; i++)
673                 s = concat(s, printpat(r->pat[i]));
674         for(i=0; i<r->nact; i++)
675                 s = concat(s, printpat(r->act[i]));
676         s = concat(s, "\n");
677         return s;
678 }
679
680 char*
681 printport(char *port)
682 {
683         char *s;
684
685         s = nil;
686         s = concat(s, "plumb to ");
687         s = concat(s, port);
688         s = concat(s, "\n");
689         return s;
690 }
691
692 char*
693 printrules(void)
694 {
695         int i;
696         char *s;
697
698         s = nil;
699         for(i=0; i<nvars; i++)
700                 s = concat(s, printvar(&vars[i]));
701         for(i=0; i<nports; i++)
702                 s = concat(s, printport(ports[i]));
703         s = concat(s, "\n");
704         for(i=0; rules[i]; i++)
705                 s = concat(s, printrule(rules[i]));
706         return s;
707 }
708
709 char*
710 stringof(char *s, int n)
711 {
712         char *t;
713
714         t = emalloc(n+1);
715         memmove(t, s, n);
716         return t;
717 }
718
719 uchar*
720 morerules(uchar *text, int done)
721 {
722         int n;
723         Ruleset *rs;
724         uchar *otext, *s, *endofrule;
725
726         pushinput("<rules input>", -1, text);
727         if(done)
728                 input->end = text+strlen((char*)text);
729         else{
730                 /*
731                  * Help user by sending any full rules to parser so any parse errors will
732                  * occur on write rather than close. A heuristic will do: blank line ends rule.
733                  */
734                 endofrule = nil;
735                 for(s=text; *s!='\0'; s++)
736                         if(*s=='\n' && *++s=='\n')
737                                 endofrule = s+1;
738                 if(endofrule == nil)
739                         return text;
740                 input->end = endofrule;
741         }
742         for(n=0; rules[n]; n++)
743                 ;
744         while((rs=readruleset()) != nil){
745                 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
746                 rules[n++] = rs;
747                 rules[n] = nil;
748         }
749         otext =text;
750         if(input == nil)
751                 text = (uchar*)estrdup("");
752         else
753                 text = (uchar*)estrdup((char*)input->end);
754         popinput();
755         free(otext);
756         return text;
757 }
758
759 char*
760 writerules(char *s, int n)
761 {
762         static uchar *text;
763         char *tmp;
764
765         free(lasterror);
766         lasterror = nil;
767         parsing = 1;
768         if(setjmp(parsejmp) == 0){
769                 tmp = stringof(s, n);
770                 text = (uchar*)concat((char*)text, tmp);
771                 free(tmp);
772                 text = morerules(text, s==nil);
773         }
774         if(s == nil){
775                 free(text);
776                 text = nil;
777         }
778         parsing = 0;
779         makeports(rules);
780         return lasterror;
781 }