xref: /plan9/sys/src/cmd/plumb/match.c (revision 853458f38e7eb3a48cfa3a36aefdb799375e398a)
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