]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/execnet/fs.c
cwfs: fix listen filedescriptor leaks
[plan9front.git] / sys / src / cmd / execnet / fs.c
1 /*
2  * ``Exec'' network device.  Mounted on net, provides /net/exec.
3  *
4  *      exec                            protocol directory
5  *              n                               connection directory
6  *                      ctl                             control messages (like connect)
7  *                      data                            data
8  *                      err                             errors
9  *                      local                           local address (pid of command)
10  *                      remote                  remote address (command)
11  *                      status                  status
12  */
13
14 #include <u.h>
15 #include <libc.h>
16 #include <fcall.h>
17 #include <thread.h>
18 #include <9p.h>
19 #include "dat.h"
20
21 int fsdebug;
22
23 enum
24 {
25         Qroot,
26         Qexec,
27         Qclone,
28         Qn,
29         Qctl,
30         Qdata,
31         Qlocal,
32         Qremote,
33         Qstatus,
34 };
35
36 #define PATH(type, n)   ((type)|((n)<<8))
37 #define TYPE(path)              ((int)(path) & 0xFF)
38 #define NUM(path)               ((uint)(path)>>8)
39
40 typedef struct Tab Tab;
41 struct Tab
42 {
43         char *name;
44         ulong mode;
45 };
46
47 Tab tab[] =
48 {
49         "/",            DMDIR|0555,
50         "exec", DMDIR|0555,
51         "clone",        0666,
52         nil,            DMDIR|0555,
53         "ctl",          0666,
54         "data", 0666,
55         "local",        0444,
56         "remote",       0444,
57         "status",       0444,
58 };
59
60 void
61 setexecname(char *s)
62 {
63         tab[Qexec].name = s;
64 }
65
66 ulong time0;
67
68 static void
69 fillstat(Dir *d, ulong path)
70 {
71         Tab *t;
72         int type;
73         char buf[32];
74
75         memset(d, 0, sizeof(*d));
76         d->uid = estrdup("exec");
77         d->gid = estrdup("exec");
78         d->qid.path = path;
79         d->atime = d->mtime = time0;
80         d->length = 0;
81
82         type = TYPE(path);
83         t = &tab[type];
84         if(t->name)
85                 d->name = estrdup(t->name);
86         else{
87                 snprint(buf, sizeof buf, "%ud", NUM(path));
88                 d->name = estrdup(buf);
89         }
90         d->qid.type = t->mode>>24;
91         d->mode = t->mode;
92 }
93
94 static void
95 fsstat(Req *r)
96 {
97         fillstat(&r->d, r->fid->qid.path);
98         respond(r, nil);
99 }
100
101 static int
102 rootgen(int i, Dir *d, void*)
103 {
104         if(i < 1){
105                 fillstat(d, PATH(Qexec, 0));
106                 return 0;
107         }
108         return -1;
109 }
110
111 static int
112 execgen(int i, Dir *d, void*)
113 {
114         if(i < 1){
115                 fillstat(d, PATH(Qclone, 0));
116                 return 0;
117         }
118         i -= 1;
119
120         if(i < nclient){
121                 fillstat(d, PATH(Qn, i));
122                 return 0;
123         }
124         return -1;
125 }
126
127 static int
128 conngen(int i, Dir *d, void *aux)
129 {
130         Client *c;
131
132         c = aux;
133         i += Qn+1;
134         if(i <= Qstatus){
135                 fillstat(d, PATH(i, c->num));
136                 return 0;
137         }
138         return -1;
139 }
140
141 char *statusstr[] = 
142 {
143         "Closed",
144         "Exec",
145         "Established",
146         "Hangup",
147 };
148
149 static void
150 fsread(Req *r)
151 {
152         char e[ERRMAX], *s;
153         ulong path;
154
155         path = r->fid->qid.path;
156         switch(TYPE(path)){
157         default:
158                 snprint(e, sizeof e, "bug in execnet path=%lux", path);
159                 respond(r, e);
160                 break;
161
162         case Qroot:
163                 dirread9p(r, rootgen, nil);
164                 respond(r, nil);
165                 break;
166
167         case Qexec:
168                 dirread9p(r, execgen, nil);
169                 respond(r, nil);
170                 break;
171
172         case Qn:
173                 dirread9p(r, conngen, client[NUM(path)]);
174                 respond(r, nil);
175                 break;
176
177         case Qctl:
178                 snprint(e, sizeof e, "%ud", NUM(path));
179                 readstr(r, e);
180                 respond(r, nil);
181                 break;
182
183         case Qdata:
184                 dataread(r, client[NUM(path)]);
185                 break;
186
187         case Qlocal:
188                 snprint(e, sizeof e, "%d", client[NUM(path)]->pid);
189                 readstr(r, e);
190                 respond(r, nil);
191                 break;
192
193         case Qremote:
194                 s = client[NUM(path)]->cmd;
195                 if(strlen(s) >= 5)      /* "exec " */
196                         readstr(r, s+5);
197                 else
198                         readstr(r, s);
199                 respond(r, nil);
200                 break;
201
202         case Qstatus:
203                 readstr(r, statusstr[client[NUM(path)]->status]);
204                 respond(r, nil);
205                 break;
206         }
207 }
208
209 static void
210 fswrite(Req *r)
211 {
212         char e[ERRMAX];
213         ulong path;
214
215         path = r->fid->qid.path;
216         switch(TYPE(path)){
217         default:
218                 snprint(e, sizeof e, "bug in execnet path=%lux", path);
219                 respond(r, e);
220                 break;
221
222         case Qctl:
223                 ctlwrite(r, client[NUM(path)]);
224                 break;
225
226         case Qdata:
227                 datawrite(r, client[NUM(path)]);
228                 break;
229         }
230 }
231
232
233 static void
234 fsflush(Req *r)
235 {
236         ulong path;
237         Req *or;
238
239         for(or=r; or->ifcall.type==Tflush; or=or->oldreq)
240                 ;
241
242         if(or->ifcall.type != Tread && or->ifcall.type != Twrite)
243                 abort();
244
245         path = or->fid->qid.path;
246         if(TYPE(path) != Qdata)
247                 abort();
248
249         clientflush(or, client[NUM(path)]);
250         respond(r, nil);
251 }
252
253 static void
254 fsattach(Req *r)
255 {
256         if(r->ifcall.aname && r->ifcall.aname[0]){
257                 respond(r, "invalid attach specifier");
258                 return;
259         }
260         r->fid->qid.path = PATH(Qroot, 0);
261         r->fid->qid.type = QTDIR;
262         r->fid->qid.vers = 0;
263         r->ofcall.qid = r->fid->qid;
264         respond(r, nil);
265 }
266
267 static char*
268 fswalk1(Fid *fid, char *name, Qid *qid)
269 {
270         char buf[32];
271         int i, n;
272         ulong path;
273
274         if(!(fid->qid.type&QTDIR))
275                 return "walk in non-directory";
276
277         path = fid->qid.path;
278         if(strcmp(name, "..") == 0){
279                 switch(TYPE(path)){
280                 case Qn:
281                         qid->path = PATH(Qexec, 0);
282                         qid->type = QTDIR;
283                         return nil;
284                 case Qroot:
285                 case Qexec:
286                         qid->path = PATH(Qroot, 0);
287                         qid->type = QTDIR;
288                         return nil;
289                 default:
290                         return "bug in fswalk1";
291                 }
292         }
293
294         i = TYPE(path)+1;
295         for(; i<nelem(tab); i++){
296                 if(i==Qn){
297                         n = atoi(name);
298                         snprint(buf, sizeof buf, "%d", n);
299                         if(n < nclient && strcmp(buf, name) == 0){
300                                 qid->path = PATH(Qn, n);
301                                 qid->type = QTDIR;
302                                 return nil;
303                         }
304                         break;
305                 }
306                 if(strcmp(tab[i].name, name) == 0){
307                         qid->path = PATH(i, NUM(path));
308                         qid->type = tab[i].mode>>24;
309                         return nil;
310                 }
311                 if(tab[i].mode&DMDIR)
312                         break;
313         }
314         return "directory entry not found";
315 }
316
317 static void
318 fsopen(Req *r)
319 {
320         static int need[4] = { 4, 2, 6, 1 };
321         ulong path;
322         int n;
323         Tab *t;
324
325         /*
326          * lib9p already handles the blatantly obvious.
327          * we just have to enforce the permissions we have set.
328          */
329         path = r->fid->qid.path;
330         t = &tab[TYPE(path)];
331         n = need[r->ifcall.mode&3];
332         if((n&t->mode) != n){
333                 respond(r, "permission denied");
334                 return;
335         }
336
337         switch(TYPE(path)){
338         case Qclone:
339                 n = newclient();
340                 path = PATH(Qctl, n);
341                 r->fid->qid.path = path;
342                 r->ofcall.qid.path = path;
343                 if(fsdebug)
344                         fprint(2, "open clone => path=%lux\n", path);
345                 t = &tab[Qctl];
346                 /* fall through */
347         default:
348                 if(t-tab >= Qn)
349                         client[NUM(path)]->ref++;
350                 respond(r, nil);
351                 break;
352         }
353 }
354
355 Channel *cclunk;
356 Channel *cclunkwait;
357 Channel *creq;
358 Channel *creqwait;
359
360 static void
361 fsthread(void*)
362 {
363         ulong path;
364         Alt a[3];
365         Fid *fid;
366         Req *r;
367
368         threadsetname("fsthread");
369
370         a[0].op = CHANRCV;
371         a[0].c = cclunk;
372         a[0].v = &fid;
373         a[1].op = CHANRCV;
374         a[1].c = creq;
375         a[1].v = &r;
376         a[2].op = CHANEND;
377
378         for(;;){
379                 switch(alt(a)){
380                 case 0:
381                         path = fid->qid.path;
382                         if(fid->omode != -1 && TYPE(path) >= Qn)
383                                 closeclient(client[NUM(path)]);
384                         sendp(cclunkwait, nil);
385                         break;
386                 case 1:
387                         switch(r->ifcall.type){
388                         case Tattach:
389                                 fsattach(r);
390                                 break;
391                         case Topen:
392                                 fsopen(r);
393                                 break;
394                         case Tread:
395                                 fsread(r);
396                                 break;
397                         case Twrite:
398                                 fswrite(r);
399                                 break;
400                         case Tstat:
401                                 fsstat(r);
402                                 break;
403                         case Tflush:
404                                 fsflush(r);
405                                 break;
406                         default:
407                                 respond(r, "bug in fsthread");
408                                 break;
409                         }
410                         sendp(creqwait, 0);
411                         break;
412                 }
413         }
414 }
415
416 static void
417 fsdestroyfid(Fid *fid)
418 {
419         sendp(cclunk, fid);
420         recvp(cclunkwait);
421 }
422
423 static void
424 fssend(Req *r)
425 {
426         sendp(creq, r);
427         recvp(creqwait);        /* avoids need to deal with spurious flushes */
428 }
429
430 void
431 initfs(void)
432 {
433         time0 = time(0);
434         creq = chancreate(sizeof(void*), 0);
435         creqwait = chancreate(sizeof(void*), 0);
436         cclunk = chancreate(sizeof(void*), 0);
437         cclunkwait = chancreate(sizeof(void*), 0);
438         procrfork(fsthread, nil, STACK, RFNAMEG);
439 }
440
441 Srv fs = 
442 {
443 .attach=                fssend,
444 .destroyfid=    fsdestroyfid,
445 .walk1=         fswalk1,
446 .open=          fssend,
447 .read=          fssend,
448 .write=         fssend,
449 .stat=          fssend,
450 .flush=         fssend,
451 };