Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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*
10
nonnil(char *s)
11
{
12
	if(s == nil)
13
		return "";
14
	return s;
15
}
16
 
17
int
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
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
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
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
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*
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
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
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
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
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
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
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*
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
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**
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*
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
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
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*
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
}