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 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 nonnil(char *s) 220 { 221 if(s == nil) 222 return ""; 223 return s; 224 } 225 226 static char* 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* 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(*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* 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 337 regerror(char *msg) 338 { 339 if(parsing){ 340 parsing = 0; 341 parseerror("%s", msg); 342 } 343 error("%s", msg); 344 } 345 346 void 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 375 assignment(char *p) 376 { 377 char *var, *qval; 378 int n; 379 380 if(!isalpha(p[0])) 381 return 0; 382 for(var=p; isalnum(*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 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, "/sys/lib/plumb/%s", t); 418 t = 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* 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 503 freerule(Rule *r) 504 { 505 free(r->arg); 506 free(r->qarg); 507 free(r->regex); 508 } 509 510 void 511 freerules(Rule **r) 512 { 513 while(*r) 514 freerule(*r++); 515 } 516 517 void 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* 529 readruleset(void) 530 { 531 Ruleset *rs; 532 Rule *r; 533 int eof, inrule, i, ncmd; 534 535 Again: 536 eof = 0; 537 rs = emalloc(sizeof(Ruleset)); 538 rs->pat = emalloc(sizeof(Rule*)); 539 rs->act = emalloc(sizeof(Rule*)); 540 inrule = 0; 541 ncmd = 0; 542 for(;;){ 543 r = readrule(&eof); 544 if(eof) 545 break; 546 if(r==nil){ 547 if(inrule) 548 break; 549 continue; 550 } 551 inrule = 1; 552 switch(r->obj){ 553 case OArg: 554 case OAttr: 555 case OData: 556 case ODst: 557 case OType: 558 case OWdir: 559 case OSrc: 560 rs->npat++; 561 rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*)); 562 rs->pat[rs->npat-1] = r; 563 rs->pat[rs->npat] = nil; 564 break; 565 case OPlumb: 566 rs->nact++; 567 rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*)); 568 rs->act[rs->nact-1] = r; 569 rs->act[rs->nact] = nil; 570 if(r->verb == VTo){ 571 if(rs->npat>0 && rs->port != nil) /* npat==0 implies port declaration */ 572 parseerror("too many ports"); 573 if(lookup(r->qarg, badports) >= 0) 574 parseerror("illegal port name %s", r->qarg); 575 rs->port = estrdup(r->qarg); 576 }else 577 ncmd++; /* start or client rule */ 578 break; 579 } 580 } 581 if(ncmd > 1){ 582 freeruleset(rs); 583 parseerror("ruleset has more than one client or start action"); 584 } 585 if(rs->npat>0 && rs->nact>0) 586 return rs; 587 if(rs->npat==0 && rs->nact==0){ 588 freeruleset(rs); 589 return nil; 590 } 591 if(rs->nact==0 || rs->port==nil){ 592 freeruleset(rs); 593 parseerror("ruleset must have patterns and actions"); 594 return nil; 595 } 596 597 /* declare ports */ 598 for(i=0; i<rs->nact; i++) 599 if(rs->act[i]->verb != VTo){ 600 freeruleset(rs); 601 parseerror("ruleset must have actions"); 602 return nil; 603 } 604 for(i=0; i<rs->nact; i++) 605 addport(rs->act[i]->qarg); 606 freeruleset(rs); 607 goto Again; 608 } 609 610 Ruleset** 611 readrules(char *name, int fd) 612 { 613 Ruleset *rs, **rules; 614 int n; 615 616 parsing = 1; 617 pushinput(name, fd, nil); 618 rules = emalloc(sizeof(Ruleset*)); 619 for(n=0; (rs=readruleset())!=nil; n++){ 620 rules = erealloc(rules, (n+2)*sizeof(Ruleset*)); 621 rules[n] = rs; 622 rules[n+1] = nil; 623 } 624 popinput(); 625 parsing = 0; 626 return rules; 627 } 628 629 char* 630 concat(char *s, char *t) 631 { 632 if(t == nil) 633 return s; 634 if(s == nil) 635 s = estrdup(t); 636 else{ 637 s = erealloc(s, strlen(s)+strlen(t)+1); 638 strcat(s, t); 639 } 640 return s; 641 } 642 643 char* 644 printpat(Rule *r) 645 { 646 char *s; 647 648 s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1); 649 sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg); 650 return s; 651 } 652 653 char* 654 printvar(Var *v) 655 { 656 char *s; 657 658 s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1); 659 sprint(s, "%s=%s\n\n", v->name, v->value); 660 return s; 661 } 662 663 char* 664 printrule(Ruleset *r) 665 { 666 int i; 667 char *s; 668 669 s = nil; 670 for(i=0; i<r->npat; i++) 671 s = concat(s, printpat(r->pat[i])); 672 for(i=0; i<r->nact; i++) 673 s = concat(s, printpat(r->act[i])); 674 s = concat(s, "\n"); 675 return s; 676 } 677 678 char* 679 printport(char *port) 680 { 681 char *s; 682 683 s = nil; 684 s = concat(s, "plumb to "); 685 s = concat(s, port); 686 s = concat(s, "\n"); 687 return s; 688 } 689 690 char* 691 printrules(void) 692 { 693 int i; 694 char *s; 695 696 s = nil; 697 for(i=0; i<nvars; i++) 698 s = concat(s, printvar(&vars[i])); 699 for(i=0; i<nports; i++) 700 s = concat(s, printport(ports[i])); 701 s = concat(s, "\n"); 702 for(i=0; rules[i]; i++) 703 s = concat(s, printrule(rules[i])); 704 return s; 705 } 706 707 char* 708 stringof(char *s, int n) 709 { 710 char *t; 711 712 t = emalloc(n+1); 713 memmove(t, s, n); 714 return t; 715 } 716 717 uchar* 718 morerules(uchar *text, int done) 719 { 720 int n; 721 Ruleset *rs; 722 uchar *otext, *s, *endofrule; 723 724 pushinput("<rules input>", -1, text); 725 if(done) 726 input->end = text+strlen((char*)text); 727 else{ 728 /* 729 * Help user by sending any full rules to parser so any parse errors will 730 * occur on write rather than close. A heuristic will do: blank line ends rule. 731 */ 732 endofrule = nil; 733 for(s=text; *s!='\0'; s++) 734 if(*s=='\n' && *++s=='\n') 735 endofrule = s+1; 736 if(endofrule == nil) 737 return text; 738 input->end = endofrule; 739 } 740 for(n=0; rules[n]; n++) 741 ; 742 while((rs=readruleset()) != nil){ 743 rules = erealloc(rules, (n+2)*sizeof(Ruleset*)); 744 rules[n++] = rs; 745 rules[n] = nil; 746 } 747 otext =text; 748 if(input == nil) 749 text = (uchar*)estrdup(""); 750 else 751 text = (uchar*)estrdup((char*)input->end); 752 popinput(); 753 free(otext); 754 return text; 755 } 756 757 char* 758 writerules(char *s, int n) 759 { 760 static uchar *text; 761 char *tmp; 762 763 free(lasterror); 764 lasterror = nil; 765 parsing = 1; 766 if(setjmp(parsejmp) == 0){ 767 tmp = stringof(s, n); 768 text = (uchar*)concat((char*)text, tmp); 769 free(tmp); 770 text = morerules(text, s==nil); 771 } 772 if(s == nil){ 773 free(text); 774 text = nil; 775 } 776 parsing = 0; 777 makeports(rules); 778 return lasterror; 779 } 780