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