match.c (8171B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <regexp.h> 5 #include <thread.h> 6 #include <plumb.h> 7 #include "plumber.h" 8 9 /* 10 static char* 11 nonnil(char *s) 12 { 13 if(s == nil) 14 return ""; 15 return s; 16 } 17 */ 18 19 int 20 verbis(int obj, Plumbmsg *m, Rule *r) 21 { 22 switch(obj){ 23 default: 24 fprint(2, "unimplemented 'is' object %d\n", obj); 25 break; 26 case OData: 27 return strcmp(m->data, r->qarg) == 0; 28 case ODst: 29 return strcmp(m->dst, r->qarg) == 0; 30 case OType: 31 return strcmp(m->type, r->qarg) == 0; 32 case OWdir: 33 return strcmp(m->wdir, r->qarg) == 0; 34 case OSrc: 35 return strcmp(m->src, r->qarg) == 0; 36 } 37 return 0; 38 } 39 40 static void 41 setvar(Resub rs[NMATCHSUBEXP], char *match[NMATCHSUBEXP]) 42 { 43 int i, n; 44 45 for(i=0; i<NMATCHSUBEXP; i++){ 46 free(match[i]); 47 match[i] = nil; 48 if(rs[i].s.sp != nil){ 49 n = rs[i].e.ep-rs[i].s.sp; 50 match[i] = emalloc(n+1); 51 memmove(match[i], rs[i].s.sp, n); 52 match[i][n] = '\0'; 53 } 54 } 55 } 56 57 int 58 clickmatch(Reprog *re, char *text, Resub rs[NMATCHSUBEXP], int click) 59 { 60 char *clickp; 61 int i, w; 62 Rune r; 63 64 /* click is in characters, not bytes */ 65 for(i=0; i<click && text[i]!='\0'; i+=w) 66 w = chartorune(&r, text+i); 67 clickp = text+i; 68 for(i=0; i<=click; i++){ 69 memset(rs, 0, NMATCHSUBEXP*sizeof(Resub)); 70 if(regexec(re, text+i, rs, NMATCHSUBEXP)) 71 if(rs[0].s.sp<=clickp && clickp<=rs[0].e.ep) 72 return 1; 73 } 74 return 0; 75 } 76 77 int 78 verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e) 79 { 80 Resub rs[NMATCHSUBEXP]; 81 char *clickval, *alltext; 82 int p0, p1, ntext; 83 84 memset(rs, 0, sizeof rs); 85 ntext = -1; 86 switch(obj){ 87 default: 88 fprint(2, "unimplemented 'matches' object %d\n", obj); 89 break; 90 case OData: 91 clickval = plumblookup(m->attr, "click"); 92 if(clickval == nil){ 93 alltext = m->data; 94 ntext = m->ndata; 95 goto caseAlltext; 96 } 97 if(!clickmatch(r->regex, m->data, rs, atoi(clickval))) 98 break; 99 p0 = rs[0].s.sp - m->data; 100 p1 = rs[0].e.ep - m->data; 101 if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1)) 102 break; 103 e->clearclick = 1; 104 e->setdata = 1; 105 e->p0 = p0; 106 e->p1 = p1; 107 setvar(rs, e->match); 108 return 1; 109 case ODst: 110 alltext = m->dst; 111 goto caseAlltext; 112 case OType: 113 alltext = m->type; 114 goto caseAlltext; 115 case OWdir: 116 alltext = m->wdir; 117 goto caseAlltext; 118 case OSrc: 119 alltext = m->src; 120 /* fall through */ 121 caseAlltext: 122 /* must match full text */ 123 if(ntext < 0) 124 ntext = strlen(alltext); 125 if(!regexec(r->regex, alltext, rs, NMATCHSUBEXP) 126 || rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext) 127 break; 128 setvar(rs, e->match); 129 return 1; 130 } 131 return 0; 132 } 133 134 int 135 isfile(char *file, ulong maskon, ulong maskoff) 136 { 137 Dir *d; 138 int mode; 139 140 d = dirstat(file); 141 if(d == nil) 142 return 0; 143 mode = d->mode; 144 free(d); 145 if((mode & maskon) == 0) 146 return 0; 147 if(mode & maskoff) 148 return 0; 149 return 1; 150 } 151 152 char* 153 absolute(char *dir, char *file) 154 { 155 char *p; 156 157 if(file[0] == '/') 158 return estrdup(file); 159 p = emalloc(strlen(dir)+1+strlen(file)+1); 160 sprint(p, "%s/%s", dir, file); 161 return cleanname(p); 162 } 163 164 int 165 verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var) 166 { 167 char *file; 168 169 switch(obj){ 170 default: 171 fprint(2, "unimplemented 'isfile' object %d\n", obj); 172 break; 173 case OArg: 174 file = absolute(m->wdir, expand(e, r->arg, nil)); 175 if(isfile(file, maskon, maskoff)){ 176 *var = file; 177 return 1; 178 } 179 free(file); 180 break; 181 case OData: 182 case OWdir: 183 file = absolute(m->wdir, obj==OData? m->data : m->wdir); 184 if(isfile(file, maskon, maskoff)){ 185 *var = file; 186 return 1; 187 } 188 free(file); 189 break; 190 } 191 return 0; 192 } 193 194 int 195 verbset(int obj, Plumbmsg *m, Rule *r, Exec *e) 196 { 197 char *new; 198 199 switch(obj){ 200 default: 201 fprint(2, "unimplemented 'is' object %d\n", obj); 202 break; 203 case OData: 204 new = estrdup(expand(e, r->arg, nil)); 205 m->ndata = strlen(new); 206 free(m->data); 207 m->data = new; 208 e->p0 = -1; 209 e->p1 = -1; 210 e->setdata = 0; 211 return 1; 212 case ODst: 213 new = estrdup(expand(e, r->arg, nil)); 214 free(m->dst); 215 m->dst = new; 216 return 1; 217 case OType: 218 new = estrdup(expand(e, r->arg, nil)); 219 free(m->type); 220 m->type = new; 221 return 1; 222 case OWdir: 223 new = estrdup(expand(e, r->arg, nil)); 224 free(m->wdir); 225 m->wdir = new; 226 return 1; 227 case OSrc: 228 new = estrdup(expand(e, r->arg, nil)); 229 free(m->src); 230 m->src = new; 231 return 1; 232 } 233 return 0; 234 } 235 236 int 237 verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e) 238 { 239 switch(obj){ 240 default: 241 fprint(2, "unimplemented 'add' object %d\n", obj); 242 break; 243 case OAttr: 244 m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil))); 245 return 1; 246 } 247 return 0; 248 } 249 250 int 251 verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e) 252 { 253 char *a; 254 255 switch(obj){ 256 default: 257 fprint(2, "unimplemented 'delete' object %d\n", obj); 258 break; 259 case OAttr: 260 a = expand(e, r->arg, nil); 261 if(plumblookup(m->attr, a) == nil) 262 break; 263 m->attr = plumbdelattr(m->attr, a); 264 return 1; 265 } 266 return 0; 267 } 268 269 int 270 matchpat(Plumbmsg *m, Exec *e, Rule *r) 271 { 272 switch(r->verb){ 273 default: 274 fprint(2, "unimplemented verb %d\n", r->verb); 275 break; 276 case VAdd: 277 return verbadd(r->obj, m, r, e); 278 case VDelete: 279 return verbdelete(r->obj, m, r, e); 280 case VIs: 281 return verbis(r->obj, m, r); 282 case VIsdir: 283 return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir); 284 case VIsfile: 285 return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file); 286 case VMatches: 287 return verbmatches(r->obj, m, r, e); 288 case VSet: 289 verbset(r->obj, m, r, e); 290 return 1; 291 } 292 return 0; 293 } 294 295 void 296 freeexec(Exec *exec) 297 { 298 int i; 299 300 if(exec == nil) 301 return; 302 free(exec->dir); 303 free(exec->file); 304 for(i=0; i<NMATCHSUBEXP; i++) 305 free(exec->match[i]); 306 free(exec); 307 } 308 309 Exec* 310 newexec(Plumbmsg *m) 311 { 312 Exec *exec; 313 314 exec = emalloc(sizeof(Exec)); 315 exec->msg = m; 316 exec->p0 = -1; 317 exec->p1 = -1; 318 return exec; 319 } 320 321 void 322 rewrite(Plumbmsg *m, Exec *e) 323 { 324 Plumbattr *a, *prev; 325 326 if(e->clearclick){ 327 prev = nil; 328 for(a=m->attr; a!=nil; a=a->next){ 329 if(strcmp(a->name, "click") == 0){ 330 if(prev == nil) 331 m->attr = a->next; 332 else 333 prev->next = a->next; 334 free(a->name); 335 free(a->value); 336 free(a); 337 break; 338 } 339 prev = a; 340 } 341 if(e->setdata){ 342 free(m->data); 343 m->data = estrdup(expand(e, "$0", nil)); 344 m->ndata = strlen(m->data); 345 } 346 } 347 } 348 349 char** 350 buildargv(char *s, Exec *e) 351 { 352 char **av; 353 int ac; 354 355 ac = 0; 356 av = nil; 357 for(;;){ 358 av = erealloc(av, (ac+1) * sizeof(char*)); 359 av[ac] = nil; 360 while(*s==' ' || *s=='\t') 361 s++; 362 if(*s == '\0') 363 break; 364 av[ac++] = estrdup(expand(e, s, &s)); 365 } 366 return av; 367 } 368 369 Exec* 370 matchruleset(Plumbmsg *m, Ruleset *rs) 371 { 372 int i; 373 Exec *exec; 374 375 if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0) 376 return nil; 377 exec = newexec(m); 378 for(i=0; i<rs->npat; i++) 379 if(!matchpat(m, exec, rs->pat[i])){ 380 freeexec(exec); 381 return nil; 382 } 383 if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){ 384 free(m->dst); 385 m->dst = estrdup(rs->port); 386 } 387 rewrite(m, exec); 388 return exec; 389 } 390 391 enum 392 { 393 NARGS = 100, 394 NARGCHAR = 8*1024, 395 EXECSTACK = 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR 396 }; 397 398 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */ 399 void 400 stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR]) 401 { 402 int i, n; 403 char *s, *a; 404 405 s = args; 406 for(i=0; i<NARGS; i++){ 407 a = inargv[i]; 408 if(a == nil) 409 break; 410 n = strlen(a)+1; 411 if((s-args)+n >= NARGCHAR) /* too many characters */ 412 break; 413 argv[i] = s; 414 memmove(s, a, n); 415 s += n; 416 free(a); 417 } 418 argv[i] = nil; 419 } 420 421 422 void 423 execproc(void *v) 424 { 425 int fd[3]; 426 char **av; 427 char *args[NARGS+1], argc[NARGCHAR]; 428 429 fd[0] = open("/dev/null", OREAD); 430 fd[1] = dup(1, -1); 431 fd[2] = dup(2, -1); 432 av = v; 433 stackargv(av, args, argc); 434 free(av); 435 threadexec(nil, fd, args[0], args); 436 threadexits("can't exec"); 437 } 438 439 char* 440 startup(Ruleset *rs, Exec *e) 441 { 442 char **argv; 443 int i; 444 445 if(rs != nil) 446 for(i=0; i<rs->nact; i++){ 447 if(rs->act[i]->verb == VStart) 448 goto Found; 449 if(rs->act[i]->verb == VClient){ 450 if(e->msg->dst==nil || e->msg->dst[0]=='\0') 451 return "no port for \"client\" rule"; 452 e->holdforclient = 1; 453 goto Found; 454 } 455 } 456 return "no start action for plumb message"; 457 458 Found: 459 argv = buildargv(rs->act[i]->arg, e); 460 if(argv[0] == nil) 461 return "empty argument list"; 462 threadcreate(execproc, argv, EXECSTACK); 463 return nil; 464 }