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