xref: /plan9/sys/src/cmd/execnet/fs.c (revision fb7f0c934c48abaed6040d054ef636408c3c522d)
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