Blob


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"
9 /*
10 static char*
11 nonnil(char *s)
12 {
13 if(s == nil)
14 return "";
15 return s;
16 }
17 */
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 }
40 static void
41 setvar(Resub rs[10], char *match[10])
42 {
43 int i, n;
45 for(i=0; i<10; i++){
46 free(match[i]);
47 match[i] = nil;
48 }
49 for(i=0; i<10 && rs[i].s.sp!=nil; i++){
50 n = rs[i].e.ep-rs[i].s.sp;
51 match[i] = emalloc(n+1);
52 memmove(match[i], rs[i].s.sp, n);
53 match[i][n] = '\0';
54 }
55 }
57 int
58 clickmatch(Reprog *re, char *text, Resub rs[10], int click)
59 {
60 char *clickp;
61 int i, w;
62 Rune r;
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, 10*sizeof(Resub));
70 if(regexec(re, text+i, rs, 10))
71 if(rs[0].s.sp<=clickp && clickp<=rs[0].e.ep)
72 return 1;
73 }
74 return 0;
75 }
77 int
78 verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
79 {
80 Resub rs[10];
81 char *clickval, *alltext;
82 int p0, p1, ntext;
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, 10) || rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext)
126 break;
127 setvar(rs, e->match);
128 return 1;
130 return 0;
133 int
134 isfile(char *file, ulong maskon, ulong maskoff)
136 Dir *d;
137 int mode;
139 d = dirstat(file);
140 if(d == nil)
141 return 0;
142 mode = d->mode;
143 free(d);
144 if((mode & maskon) == 0)
145 return 0;
146 if(mode & maskoff)
147 return 0;
148 return 1;
151 char*
152 absolute(char *dir, char *file)
154 char *p;
156 if(file[0] == '/')
157 return estrdup(file);
158 p = emalloc(strlen(dir)+1+strlen(file)+1);
159 sprint(p, "%s/%s", dir, file);
160 return cleanname(p);
163 int
164 verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
166 char *file;
168 switch(obj){
169 default:
170 fprint(2, "unimplemented 'isfile' object %d\n", obj);
171 break;
172 case OArg:
173 file = absolute(m->wdir, expand(e, r->arg, nil));
174 if(isfile(file, maskon, maskoff)){
175 *var = file;
176 return 1;
178 free(file);
179 break;
180 case OData:
181 case OWdir:
182 file = absolute(m->wdir, obj==OData? m->data : m->wdir);
183 if(isfile(file, maskon, maskoff)){
184 *var = file;
185 return 1;
187 free(file);
188 break;
190 return 0;
193 int
194 verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
196 char *new;
198 switch(obj){
199 default:
200 fprint(2, "unimplemented 'is' object %d\n", obj);
201 break;
202 case OData:
203 new = estrdup(expand(e, r->arg, nil));
204 m->ndata = strlen(new);
205 free(m->data);
206 m->data = new;
207 e->p0 = -1;
208 e->p1 = -1;
209 e->setdata = 0;
210 return 1;
211 case ODst:
212 new = estrdup(expand(e, r->arg, nil));
213 free(m->dst);
214 m->dst = new;
215 return 1;
216 case OType:
217 new = estrdup(expand(e, r->arg, nil));
218 free(m->type);
219 m->type = new;
220 return 1;
221 case OWdir:
222 new = estrdup(expand(e, r->arg, nil));
223 free(m->wdir);
224 m->wdir = new;
225 return 1;
226 case OSrc:
227 new = estrdup(expand(e, r->arg, nil));
228 free(m->src);
229 m->src = new;
230 return 1;
232 return 0;
235 int
236 verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
238 switch(obj){
239 default:
240 fprint(2, "unimplemented 'add' object %d\n", obj);
241 break;
242 case OAttr:
243 m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
244 return 1;
246 return 0;
249 int
250 verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
252 char *a;
254 switch(obj){
255 default:
256 fprint(2, "unimplemented 'delete' object %d\n", obj);
257 break;
258 case OAttr:
259 a = expand(e, r->arg, nil);
260 if(plumblookup(m->attr, a) == nil)
261 break;
262 m->attr = plumbdelattr(m->attr, a);
263 return 1;
265 return 0;
268 int
269 matchpat(Plumbmsg *m, Exec *e, Rule *r)
271 switch(r->verb){
272 default:
273 fprint(2, "unimplemented verb %d\n", r->verb);
274 break;
275 case VAdd:
276 return verbadd(r->obj, m, r, e);
277 case VDelete:
278 return verbdelete(r->obj, m, r, e);
279 case VIs:
280 return verbis(r->obj, m, r);
281 case VIsdir:
282 return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
283 case VIsfile:
284 return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
285 case VMatches:
286 return verbmatches(r->obj, m, r, e);
287 case VSet:
288 verbset(r->obj, m, r, e);
289 return 1;
291 return 0;
294 void
295 freeexec(Exec *exec)
297 int i;
299 if(exec == nil)
300 return;
301 free(exec->dir);
302 free(exec->file);
303 for(i=0; i<10; i++)
304 free(exec->match[i]);
305 free(exec);
308 Exec*
309 newexec(Plumbmsg *m)
311 Exec *exec;
313 exec = emalloc(sizeof(Exec));
314 exec->msg = m;
315 exec->p0 = -1;
316 exec->p1 = -1;
317 return exec;
320 void
321 rewrite(Plumbmsg *m, Exec *e)
323 Plumbattr *a, *prev;
325 if(e->clearclick){
326 prev = nil;
327 for(a=m->attr; a!=nil; a=a->next){
328 if(strcmp(a->name, "click") == 0){
329 if(prev == nil)
330 m->attr = a->next;
331 else
332 prev->next = a->next;
333 free(a->name);
334 free(a->value);
335 free(a);
336 break;
338 prev = a;
340 if(e->setdata){
341 free(m->data);
342 m->data = estrdup(expand(e, "$0", nil));
343 m->ndata = strlen(m->data);
348 char**
349 buildargv(char *s, Exec *e)
351 char **av;
352 int ac;
354 ac = 0;
355 av = nil;
356 for(;;){
357 av = erealloc(av, (ac+1) * sizeof(char*));
358 av[ac] = nil;
359 while(*s==' ' || *s=='\t')
360 s++;
361 if(*s == '\0')
362 break;
363 av[ac++] = estrdup(expand(e, s, &s));
365 return av;
368 Exec*
369 matchruleset(Plumbmsg *m, Ruleset *rs)
371 int i;
372 Exec *exec;
374 if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
375 return nil;
376 exec = newexec(m);
377 for(i=0; i<rs->npat; i++)
378 if(!matchpat(m, exec, rs->pat[i])){
379 freeexec(exec);
380 return nil;
382 if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
383 free(m->dst);
384 m->dst = estrdup(rs->port);
386 rewrite(m, exec);
387 return exec;
390 enum
392 NARGS = 100,
393 NARGCHAR = 8*1024,
394 EXECSTACK = 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR
395 };
397 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
398 void
399 stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
401 int i, n;
402 char *s, *a;
404 s = args;
405 for(i=0; i<NARGS; i++){
406 a = inargv[i];
407 if(a == nil)
408 break;
409 n = strlen(a)+1;
410 if((s-args)+n >= NARGCHAR) /* too many characters */
411 break;
412 argv[i] = s;
413 memmove(s, a, n);
414 s += n;
415 free(a);
417 argv[i] = nil;
421 void
422 execproc(void *v)
424 int fd[3];
425 char **av;
426 char *args[NARGS+1], argc[NARGCHAR];
428 fd[0] = open("/dev/null", OREAD);
429 fd[1] = dup(1, -1);
430 fd[2] = dup(2, -1);
431 av = v;
432 stackargv(av, args, argc);
433 free(av);
434 threadexec(nil, fd, args[0], args);
435 threadexits("can't exec");
438 char*
439 startup(Ruleset *rs, Exec *e)
441 char **argv;
442 int i;
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;
455 return "no start action for plumb message";
457 Found:
458 argv = buildargv(rs->act[i]->arg, e);
459 if(argv[0] == nil)
460 return "empty argument list";
461 threadcreate(execproc, argv, EXECSTACK);
462 return nil;