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