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
setexecname(char * s)61 setexecname(char *s)
62 {
63 tab[Qexec].name = s;
64 }
65
66 ulong time0;
67
68 static void
fillstat(Dir * d,ulong path)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
fsstat(Req * r)95 fsstat(Req *r)
96 {
97 fillstat(&r->d, r->fid->qid.path);
98 respond(r, nil);
99 }
100
101 static int
rootgen(int i,Dir * d,void *)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
execgen(int i,Dir * d,void *)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
conngen(int i,Dir * d,void * aux)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
fsread(Req * r)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
fswrite(Req * r)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
fsflush(Req * r)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
fsattach(Req * r)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*
fswalk1(Fid * fid,char * name,Qid * qid)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
fsopen(Req * r)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
fsthread(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
fsdestroyfid(Fid * fid)417 fsdestroyfid(Fid *fid)
418 {
419 sendp(cclunk, fid);
420 recvp(cclunkwait);
421 }
422
423 static void
fssend(Req * r)424 fssend(Req *r)
425 {
426 sendp(creq, r);
427 recvp(creqwait); /* avoids need to deal with spurious flushes */
428 }
429
430 void
initfs(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 };
452