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 } 783