]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ptrap.c
rio: open /dev/snarf with OCEXEC flag when writing
[plan9front.git] / sys / src / cmd / ptrap.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include <9p.h>
6 #include <plumb.h>
7 #include <regexp.h>
8
9 typedef struct IOProc IOProc;
10 typedef struct PFilter PFilter;
11 typedef struct FAttr FAttr;
12 typedef struct PFid PFid;
13
14 struct IOProc {
15         int id;
16         Channel *ch;
17         IOProc *next;
18 };
19 QLock freellock;
20 IOProc *freel;
21
22 struct PFid {
23         char *name;
24         PFilter *filter;
25         int fd;
26         char *msg;
27         int msgn, msgp;
28 };
29 Qid rootqid = {.type QTDIR};
30
31 struct FAttr {
32         char *name;
33         Reprog *filt;
34         int invert;
35         FAttr *next;
36 };
37
38 struct PFilter {
39         char *name;
40         Reprog *filt;
41         int invert;
42         FAttr *attr;
43         PFilter *next;
44 };
45 PFilter *filters;
46
47 static char *
48 fname(char *n)
49 {
50         static char *last;
51         
52         if(last != nil){
53                 free(last);
54                 last = nil;
55         }
56         if(n == nil)
57                 return "/mnt/plumb";
58         last = smprint("/mnt/plumb/%s", n);
59         return last;
60 }
61
62 static void theioproc(void *);
63 static IOProc *
64 getioproc(void)
65 {
66         IOProc *p;
67
68         qlock(&freellock);
69         p = freel;
70         if(p != nil)
71                 freel = freel->next;
72         qunlock(&freellock);
73         if(p == nil){
74                 p = emalloc9p(sizeof(IOProc));
75                 p->ch = chancreate(sizeof(Req*), 0);
76                 p->id = proccreate(theioproc, p, 4096);
77         }
78         return p;
79 }
80
81 static void
82 putioproc(IOProc *p)
83 {
84         qlock(&freellock);
85         p->next = freel;
86         freel = p;
87         qunlock(&freellock);
88 }
89
90 static void
91 ptrapattach(Req *r)
92 {
93         PFid *pf;
94         
95         pf = emalloc9p(sizeof(PFid));
96         pf->fd = -1;
97         r->fid->aux = pf;
98         r->ofcall.qid = rootqid;
99         r->fid->qid = rootqid;
100         respond(r, nil);
101 }
102
103 static char *
104 ptrapclone(Fid *old, Fid *new)
105 {
106         PFid *pf;
107
108         pf = emalloc9p(sizeof(PFid));
109         memcpy(pf, old->aux, sizeof(PFid));
110         new->aux = pf;
111         return nil;
112 }
113
114 static void
115 ptrapdestroyfid(Fid *f)
116 {
117         PFid *pf;
118         
119         pf = f->aux;
120         if(pf == nil) return;
121         free(pf->name);
122         if(pf->fd >= 0)
123                 close(pf->fd);
124         free(pf);
125         f->aux = nil;
126 }
127
128 static char *
129 ptrapwalk1(Fid *fid, char *name, Qid *qid)
130 {
131         PFid *pf;
132         Dir *d;
133         static char err[ERRMAX];
134         
135         pf = fid->aux;
136         if(pf->name != nil)
137                 return "phase error";
138         d = dirstat(fname(name));
139         if(d == nil){
140                 rerrstr(err, ERRMAX);
141                 return err;
142         }
143         pf->name = strdup(name);
144         fid->qid = d->qid;
145         *qid = d->qid;
146         free(d);
147         return nil;     
148 }
149
150 static void
151 ptrapopen(Req *r)
152 {
153         PFid* pf;
154         PFilter *f;
155         
156         pf = r->fid->aux;
157         pf->fd = open(fname(pf->name), r->ifcall.mode);
158         if(pf->fd < 0){
159                 responderror(r);
160                 return;
161         }
162         if(pf->name == nil){
163                 respond(r, nil);
164                 return;
165         }
166         for(f = filters; f != nil; f = f->next)
167                 if(strcmp(f->name, pf->name) == 0)
168                         break;
169         pf->filter = f;
170         respond(r, nil);
171 }
172
173 static int
174 filter(PFilter *f, Plumbmsg *pm)
175 {
176         FAttr *a;
177         char *value;
178
179         if(!(regexec(f->filt, pm->data, nil, 0) ^ f->invert))
180                 return 0;
181         for(a = f->attr; a; a = a->next){
182                 value = plumblookup(pm->attr, a->name);
183                 if(value == nil)
184                         return 0;
185                 if(!(regexec(a->filt, value, nil, 0) ^ f->attr->invert))
186                         return 0;
187         }
188         return 1;
189 }
190
191 static int
192 filterread(Req *r, PFid *pf)
193 {
194         int rc, len, more;
195         char *buf;
196         Plumbmsg *pm;
197         PFilter *f;
198         
199         f = pf->filter;
200         for(;;){
201                 if(pf->msg != nil){
202                         rc = r->ifcall.count;
203                         if(pf->msgp + rc >= pf->msgn)
204                                 rc = pf->msgn - pf->msgp;
205                         r->ofcall.count = rc;
206                         memmove(r->ofcall.data, pf->msg + pf->msgp, rc);
207                         pf->msgp += rc;
208                         if(pf->msgp >= pf->msgn){
209                                 free(pf->msg);
210                                 pf->msg = nil;
211                         }
212                         return 0;
213                 }
214                 buf = emalloc9p(4096);
215                 rc = read(pf->fd, buf, 4096);
216                 if(rc < 0) goto err;
217                 len = rc;
218                 while(pm = plumbunpackpartial(buf, len, &more), pm == nil){
219                         if(more == 0) goto err;
220                         buf = erealloc9p(buf, len + more);
221                         rc = readn(pf->fd, buf + len, more);
222                         if(rc < 0) goto err;
223                         len += rc;
224                 }
225                 free(buf);
226                 if(filter(f, pm)){
227                         pf->msg = plumbpack(pm, &pf->msgn);
228                         pf->msgp = 0;
229                 }
230                 plumbfree(pm);
231         }
232 err:
233         free(buf);
234         return -1;
235 }
236
237 static void
238 theioproc(void *iopp)
239 {
240         Req *r;
241         PFid *pf;
242         IOProc *iop;
243         char *buf;
244         int fd, rc;
245         
246         rfork(RFNOTEG);
247         
248         buf = smprint("/proc/%d/ctl", getpid());
249         fd = open(buf, OWRITE);
250         free(buf);
251         
252         iop = iopp;
253         for(;;){
254                 if(fd >= 0)
255                         write(fd, "nointerrupt", 11);
256                 r = recvp(iop->ch);
257                 r->aux = iop;
258                 pf = r->fid->aux;
259                 switch(r->ifcall.type){
260                 case Tread:
261                         if(!pf->filter){
262                                 rc = pread(pf->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
263                                 if(rc < 0){
264                                         responderror(r);
265                                         break;
266                                 }
267                                 r->ofcall.count = rc;
268                                 respond(r, nil);
269                                 break;
270                         }
271                         if(filterread(r, pf) < 0)
272                                 responderror(r);
273                         else
274                                 respond(r, nil);
275                         break;
276                 case Twrite:
277                         rc = pwrite(pf->fd, r->ifcall.data, r->ifcall.count, r->ifcall.offset);
278                         if(rc < 0)
279                                 responderror(r);
280                         else{
281                                 r->ofcall.count = rc;
282                                 respond(r, nil);
283                         }
284                         break;
285                 }
286                 putioproc(iop);
287         }
288 }
289
290 static void
291 ptrapread(Req *r)
292 {
293         IOProc *iop;
294         
295         iop = getioproc();
296         send(iop->ch, &r);
297 }
298
299 static void
300 ptrapwrite(Req *r)
301 {
302         IOProc *iop;
303         
304         iop = getioproc();
305         send(iop->ch, &r);
306 }
307
308 static void
309 ptrapstat(Req *r)
310 {
311         PFid *pf;
312         Dir *d;
313         
314         pf = r->fid->aux;
315         if(pf->fd >= 0)
316                 d = dirfstat(pf->fd);
317         else
318                 d = dirstat(fname(pf->name));
319         if(d == nil){
320                 responderror(r);
321                 return;
322         }
323         memmove(&r->d, d, sizeof(Dir));
324         r->d.name = strdup(d->name);
325         r->d.uid = strdup(d->uid);
326         r->d.muid = strdup(d->muid);
327         r->d.gid = strdup(d->gid);
328         free(d);
329         respond(r, nil);
330 }
331
332 static void
333 ptrapwstat(Req *r)
334 {
335         PFid *pf;
336         int rc;
337         
338         pf = r->fid->aux;
339         if(pf->fd >= 0)
340                 rc = dirfwstat(pf->fd, &r->d);
341         else
342                 rc = dirwstat(fname(pf->name), &r->d);
343         if(rc < 0)
344                 responderror(r);
345         else
346                 respond(r, nil);
347 }
348
349 static void
350 ptrapflush(Req *r)
351 {
352         if(r->oldreq->aux != nil)
353                 threadint(((IOProc*)r->oldreq->aux)->id);
354         respond(r, nil);
355 }
356
357 Srv ptrapsrv = {
358         .attach = ptrapattach,
359         .clone = ptrapclone,
360         .destroyfid = ptrapdestroyfid,
361         .walk1 = ptrapwalk1,
362         .open = ptrapopen,
363         .read = ptrapread,
364         .write = ptrapwrite,
365         .stat = ptrapstat,
366         .wstat = ptrapwstat,
367         .flush = ptrapflush,
368 };
369
370 void
371 usage(void)
372 {
373         fprint(2, "usage: %s port regex [ +attr regex ... ] ...\n", argv0);
374         exits("usage");
375 }
376
377 void
378 threadmain(int argc, char **argv)
379 {
380         PFilter *f;
381         FAttr *fa;
382         char *p;
383         int i;
384
385         ARGBEGIN{
386         default: usage();
387         }ARGEND;
388
389         if(argc == 0 || argc % 2) usage();
390         for(i = 0; i+1 < argc;){
391                 p = argv[i];
392                 f = emalloc9p(sizeof(PFilter));
393                 f->name = estrdup9p(p);
394                 p = argv[i+1];
395                 if(p[0] == '!'){
396                         p++;
397                         f->invert = 1;
398                 }
399                 if((f->filt = regcomp(p)) == nil)
400                         sysfatal("regcomp: %r");
401                 f->next = filters;
402                 filters = f;
403                 for(i += 2; p = argv[i], i+1 < argc && p[0] == '+'; i += 2){
404                         p++;
405                         fa = emalloc9p(sizeof(FAttr));
406                         fa->name = estrdup9p(p);
407                         p = argv[i+1];
408                         if(p[0] == '!'){
409                                 p++;
410                                 fa->invert = 1;
411                         }
412                         if((fa->filt = regcomp(p)) == nil)
413                                 sysfatal("regcomp: %r");
414                         fa->next = f->attr;
415                         f->attr = fa;
416                 }
417         }
418         threadpostmountsrv(&ptrapsrv, nil, "/mnt/plumb", MREPL | MCREATE);
419
420         threadexits(nil);
421 }