1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
2  * See LICENSE file for license details.
3  */
4 #include "dat.h"
5 #include <ctype.h>
6 #include <stdarg.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include "fns.h"
10 
11 typedef union IxpFileIdU IxpFileIdU;
12 union IxpFileIdU {
13 	Bar*		bar;
14 	Bar**		bar_p;
15 	CTuple*		col;
16 	Client*		client;
17 	Ruleset*	rule;
18 	View*		view;
19 	char*		buf;
20 	void*		ref;
21 };
22 
23 #include <ixp_srvutil.h>
24 
25 static IxpPending	events;
26 static IxpPending	pdebug[NDebugOpt];
27 
28 /* Constants */
29 enum {	/* Dirs */
30 	FsDBars,
31 	FsDClient,
32 	FsDClients,
33 	FsDDebug,
34 	FsDTag,
35 	FsDTags,
36 	FsRoot,
37 	/* Files */
38 	FsFBar,
39 	FsFCctl,
40 	FsFClabel,
41 	FsFColRules,
42 	FsFCtags,
43 	FsFDebug,
44 	FsFEvent,
45 	FsFKeys,
46 	FsFRctl,
47 	FsFTagRules,
48 	FsFTctl,
49 	FsFTindex,
50 	FsFprops,
51 };
52 
53 /* Error messages */
54 static char
55 	Enoperm[] = "permission denied",
56 	Enofile[] = "file not found",
57 	Ebadvalue[] = "bad value",
58 	Einterrupted[] = "interrupted";
59 
60 /* Macros */
61 #define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF))
62 
63 /* Global Vars */
64 /***************/
65 Ixp9Srv p9srv = {
66 	.open=	fs_open,
67 	.walk=	fs_walk,
68 	.read=	fs_read,
69 	.stat=	fs_stat,
70 	.write=	fs_write,
71 	.clunk=	fs_clunk,
72 	.flush=	fs_flush,
73 	.attach=fs_attach,
74 	.create=fs_create,
75 	.remove=fs_remove,
76 	.freefid=fs_freefid
77 };
78 
79 /* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled
80  * in by lookup_file
81  */
82 static IxpDirtab
83 dirtab_root[]=	 {{".",		QTDIR,		FsRoot,		0500|DMDIR },
84 		  {"rbar",	QTDIR,		FsDBars,	0700|DMDIR },
85 		  {"lbar",	QTDIR,		FsDBars,	0700|DMDIR },
86 		  {"debug",	QTDIR,		FsDDebug,	0500|DMDIR, FLHide },
87 		  {"client",	QTDIR,		FsDClients,	0500|DMDIR },
88 		  {"tag",	QTDIR,		FsDTags,	0500|DMDIR },
89 		  {"ctl",	QTAPPEND,	FsFRctl,	0600|DMAPPEND },
90 		  {"colrules",	QTFILE,		FsFColRules,	0600 },
91 		  {"event",	QTFILE,		FsFEvent,	0600 },
92 		  {"keys",	QTFILE,		FsFKeys,	0600 },
93 		  {"tagrules",	QTFILE,		FsFTagRules,	0600 },
94 		  {nil}},
95 dirtab_clients[]={{".",		QTDIR,		FsDClients,	0500|DMDIR },
96 		  {"",		QTDIR,		FsDClient,	0500|DMDIR },
97 		  {nil}},
98 dirtab_client[]= {{".",		QTDIR,		FsDClient,	0500|DMDIR },
99 		  {"ctl",	QTAPPEND,	FsFCctl,	0600|DMAPPEND },
100 		  {"label",	QTFILE,		FsFClabel,	0600 },
101 		  {"tags",	QTFILE,		FsFCtags,	0600 },
102 		  {"props",	QTFILE,		FsFprops,	0400 },
103 		  {nil}},
104 dirtab_debug[]=  {{".",		QTDIR,		FsDDebug,	0500|DMDIR, FLHide },
105 		  {"",		QTFILE,		FsFDebug,	0400 },
106 		  {nil}},
107 dirtab_bars[]=	 {{".",		QTDIR,		FsDBars,	0700|DMDIR },
108 		  {"",		QTFILE,		FsFBar,		0600 },
109 		  {nil}},
110 dirtab_tags[]=	 {{".",		QTDIR,		FsDTags,	0500|DMDIR },
111 		  {"",		QTDIR,		FsDTag,		0500|DMDIR },
112 		  {nil}},
113 dirtab_tag[]=	 {{".",		QTDIR,		FsDTag,		0500|DMDIR },
114 		  {"ctl",	QTAPPEND,	FsFTctl,	0600|DMAPPEND },
115 		  {"index",	QTFILE,		FsFTindex,	0400 },
116 		  {nil}};
117 static IxpDirtab* dirtab[] = {
118 	[FsRoot] = dirtab_root,
119 	[FsDBars] = dirtab_bars,
120 	[FsDClients] = dirtab_clients,
121 	[FsDClient] = dirtab_client,
122 	[FsDDebug] = dirtab_debug,
123 	[FsDTags] = dirtab_tags,
124 	[FsDTag] = dirtab_tag,
125 };
126 typedef char* (*MsgFunc)(void*, IxpMsg*);
127 
128 void
event(const char * format,...)129 event(const char *format, ...) {
130 	va_list ap;
131 
132 	va_start(ap, format);
133 	vsnprint(buffer, sizeof buffer, format, ap);
134 	va_end(ap);
135 
136 	ixp_pending_write(&events, buffer, strlen(buffer));
137 }
138 
139 static int dflags;
140 
141 bool
setdebug(int flag)142 setdebug(int flag) {
143 	dflags = flag;
144 	return true;
145 }
146 
147 void
vdebug(int flag,const char * fmt,va_list ap)148 vdebug(int flag, const char *fmt, va_list ap) {
149 	char *s;
150 
151 	if(flag == 0)
152 		flag = dflags;
153 
154 	if(!((debugflag|debugfile) & flag))
155 		return;
156 
157 	s = vsmprint(fmt, ap);
158 	dwrite(flag, s, strlen(s), false);
159 	free(s);
160 }
161 
162 void
debug(int flag,const char * fmt,...)163 debug(int flag, const char *fmt, ...) {
164 	va_list ap;
165 
166 	va_start(ap, fmt);
167 	vdebug(flag, fmt, ap);
168 	va_end(ap);
169 }
170 
171 void
dprint(const char * fmt,...)172 dprint(const char *fmt, ...) {
173 	va_list ap;
174 
175 	va_start(ap, fmt);
176 	vdebug(0, fmt, ap);
177 	va_end(ap);
178 }
179 
180 void
dwrite(int flag,void * buf,int n,bool always)181 dwrite(int flag, void *buf, int n, bool always) {
182 	int i;
183 
184 	if(flag == 0)
185 		flag = dflags;
186 
187 	if(always || debugflag&flag)
188 		write(2, buf, n);
189 
190 	if(debugfile&flag)
191 	for(i=0; i < nelem(pdebug); i++)
192 		if(flag & (1<<i))
193 			ixp_pending_write(pdebug+i, buf, n);
194 }
195 
196 static uint	fs_size(IxpFileId*);
197 
198 static void
dostat(Stat * s,IxpFileId * f)199 dostat(Stat *s, IxpFileId *f) {
200 	s->type = 0;
201 	s->dev = 0;
202 	s->qid.path = QID(f->tab.type, f->id);
203 	s->qid.version = 0;
204 	s->qid.type = f->tab.qtype;
205 	s->mode = f->tab.perm;
206 	s->atime = time(nil);
207 	s->mtime = s->atime;
208 	s->length = fs_size(f);;
209 	s->name = f->tab.name;
210 	s->uid = user;
211 	s->gid = user;
212 	s->muid = user;
213 }
214 
215 /*
216  * All lookups and directory organization should be performed through
217  * lookup_file, mostly through the dirtab[] tree.
218  */
219 static IxpFileId*
lookup_file(IxpFileId * parent,char * name)220 lookup_file(IxpFileId *parent, char *name)
221 {
222 	IxpFileId *ret, *file, **last;
223 	IxpDirtab *dir;
224 	Client *c;
225 	View *v;
226 	Bar *b;
227 	uint id;
228 	int i;
229 
230 
231 	if(!(parent->tab.perm & DMDIR))
232 		return nil;
233 	dir = dirtab[parent->tab.type];
234 	last = &ret;
235 	ret = nil;
236 	for(; dir->name; dir++) {
237 #		define push_file(nam)             \
238 			file = ixp_srv_getfile(); \
239 			*last = file;             \
240 			last = &file->next;       \
241 			file->tab = *dir;         \
242 			file->tab.name = estrdup(nam)
243 		/* Dynamic dirs */
244 		if(dir->name[0] == '\0') {
245 			switch(parent->tab.type) {
246 			case FsDClients:
247 				if(!name || !strcmp(name, "sel")) {
248 					if((c = selclient())) {
249 						push_file("sel");
250 						file->volatil = true;
251 						file->p.client = c;
252 						file->id = c->w.xid;
253 						file->index = c->w.xid;
254 					}
255 					if(name)
256 						goto LastItem;
257 				}
258 				SET(id);
259 				if(name) {
260 					id = (uint)strtol(name, &name, 16);
261 					if(*name)
262 						goto NextItem;
263 				}
264 				for(c=client; c; c=c->next) {
265 					if(!name || c->w.xid == id) {
266 						push_file(sxprint("%C", c));
267 						file->volatil = true;
268 						file->p.client = c;
269 						file->id = c->w.xid;
270 						file->index = c->w.xid;
271 						assert(file->tab.name);
272 						if(name)
273 							goto LastItem;
274 					}
275 				}
276 				break;
277 			case FsDDebug:
278 				for(i=0; i < nelem(pdebug); i++)
279 					if(!name || !strcmp(name, debugtab[i])) {
280 						push_file(debugtab[i]);
281 						file->id = i;
282 						if(name)
283 							goto LastItem;
284 					}
285 				break;
286 			case FsDTags:
287 				if(!name || !strcmp(name, "sel")) {
288 					if(selview) {
289 						push_file("sel");
290 						file->volatil = true;
291 						file->p.view = selview;
292 						file->id = selview->id;
293 					}
294 					if(name)
295 						goto LastItem;
296 				}
297 				for(v=view; v; v=v->next) {
298 					if(!name || !strcmp(name, v->name)) {
299 						push_file(v->name);
300 						file->volatil = true;
301 						file->p.view = v;
302 						file->id = v->id;
303 						if(name)
304 							goto LastItem;
305 					}
306 				}
307 				break;
308 			case FsDBars:
309 				for(b=*parent->p.bar_p; b; b=b->next) {
310 					if(!name || !strcmp(name, b->name)) {
311 						push_file(b->name);
312 						file->volatil = true;
313 						file->p.bar = b;
314 						file->id = b->id;
315 						if(name)
316 							goto LastItem;
317 					}
318 				}
319 				break;
320 			}
321 		}else /* Static dirs */
322 		if(!name && !(dir->flags & FLHide) || name && !strcmp(name, dir->name)) {
323 			push_file(file->tab.name);
324 			file->id = 0;
325 			file->p.ref = parent->p.ref;
326 			file->index = parent->index;
327 			/* Special considerations: */
328 			switch(file->tab.type) {
329 			case FsDBars:
330 				if(!strcmp(file->tab.name, "lbar"))
331 					file->p.bar_p = &screen[0].bar[BLeft];
332 				else
333 					file->p.bar_p = &screen[0].bar[BRight];
334 				file->id = (int)(uintptr_t)file->p.bar_p;
335 				break;
336 			case FsFColRules:
337 				file->p.rule = &def.colrules;
338 				break;
339 			case FsFTagRules:
340 				file->p.rule = &def.tagrules;
341 				break;
342 			}
343 			if(name)
344 				goto LastItem;
345 		}
346 	NextItem:
347 		continue;
348 #		undef push_file
349 	}
350 LastItem:
351 	*last = nil;
352 	return ret;
353 }
354 
355 /* Service Functions */
356 void
fs_attach(Ixp9Req * r)357 fs_attach(Ixp9Req *r) {
358 	IxpFileId *f;
359 
360 	f = ixp_srv_getfile();
361 	f->tab = dirtab[FsRoot][0];
362 	f->tab.name = estrdup("/");
363 	r->fid->aux = f;
364 	r->fid->qid.type = f->tab.qtype;
365 	r->fid->qid.path = QID(f->tab.type, 0);
366 	r->ofcall.rattach.qid = r->fid->qid;
367 	respond(r, nil);
368 }
369 
370 void
fs_walk(Ixp9Req * r)371 fs_walk(Ixp9Req *r) {
372 
373 	ixp_srv_walkandclone(r, lookup_file);
374 }
375 
376 static uint
fs_size(IxpFileId * f)377 fs_size(IxpFileId *f) {
378 	switch(f->tab.type) {
379 	default:
380 		return 0;
381 	case FsFColRules:
382 	case FsFTagRules:
383 		return f->p.rule->size;
384 	case FsFKeys:
385 		return def.keyssz;
386 	case FsFCtags:
387 		return strlen(f->p.client->tags);
388 	case FsFClabel:
389 		return strlen(f->p.client->name);
390 	case FsFprops:
391 		return strlen(f->p.client->props);
392 	}
393 }
394 
395 void
fs_stat(Ixp9Req * r)396 fs_stat(Ixp9Req *r) {
397 	IxpMsg m;
398 	Stat s;
399 	int size;
400 	char *buf;
401 	IxpFileId *f;
402 
403 	f = r->fid->aux;
404 
405 	if(!ixp_srv_verifyfile(f, lookup_file)) {
406 		respond(r, Enofile);
407 		return;
408 	}
409 
410 	dostat(&s, f);
411 	size = ixp_sizeof_stat(&s);
412 	r->ofcall.rstat.nstat = size;
413 	buf = emallocz(size);
414 
415 	m = ixp_message(buf, size, MsgPack);
416 	ixp_pstat(&m, &s);
417 
418 	r->ofcall.rstat.stat = (uchar*)m.data;
419 	respond(r, nil);
420 }
421 
422 void
fs_read(Ixp9Req * r)423 fs_read(Ixp9Req *r) {
424 	char *buf;
425 	IxpFileId *f;
426 	int n;
427 
428 	f = r->fid->aux;
429 
430 	if(!ixp_srv_verifyfile(f, lookup_file)) {
431 		respond(r, Enofile);
432 		return;
433 	}
434 
435 	if(f->tab.perm & DMDIR && f->tab.perm & 0400) {
436 		ixp_srv_readdir(r, lookup_file, dostat);
437 		return;
438 	}
439 	else{
440 		if(f->pending) {
441 			ixp_pending_respond(r);
442 			return;
443 		}
444 		switch(f->tab.type) {
445 		case FsFprops:
446 			ixp_srv_readbuf(r, f->p.client->props, strlen(f->p.client->props));
447 			respond(r, nil);
448 			return;
449 		case FsFColRules:
450 		case FsFTagRules:
451 			ixp_srv_readbuf(r, f->p.rule->string, f->p.rule->size);
452 			respond(r, nil);
453 			return;
454 		case FsFKeys:
455 			ixp_srv_readbuf(r, def.keys, def.keyssz);
456 			respond(r, nil);
457 			return;
458 		case FsFCtags:
459 			ixp_srv_readbuf(r, f->p.client->tags, strlen(f->p.client->tags));
460 			respond(r, nil);
461 			return;
462 		case FsFClabel:
463 			ixp_srv_readbuf(r, f->p.client->name, strlen(f->p.client->name));
464 			respond(r, nil);
465 			return;
466 		case FsFBar:
467 			ixp_srv_readbuf(r, f->p.bar->buf, strlen(f->p.bar->buf));
468 			respond(r, nil);
469 			return;
470 		case FsFRctl:
471 			buf = readctl_root();
472 			ixp_srv_readbuf(r, buf, strlen(buf));
473 			respond(r, nil);
474 			return;
475 		case FsFCctl:
476 			buf = readctl_client(f->p.client);
477 			ixp_srv_readbuf(r, buf, strlen(buf));
478 			respond(r, nil);
479 			return;
480 		case FsFTindex:
481 			buf = view_index(f->p.view);
482 			ixp_srv_readbuf(r, buf, strlen(buf));
483 			respond(r, nil);
484 			return;
485 		case FsFTctl:
486 			buf = readctl_view(f->p.view);
487 			n = strlen(buf);
488 			ixp_srv_readbuf(r, buf, n);
489 			respond(r, nil);
490 			return;
491 		}
492 	}
493 	/* This should not be called if the file is not open for reading. */
494 	die("Read called on an unreadable file");
495 }
496 
497 void
fs_write(Ixp9Req * r)498 fs_write(Ixp9Req *r) {
499 	MsgFunc mf;
500 	IxpFileId *f;
501 	char *errstr;
502 	char *p;
503 	uint i;
504 
505 	if(r->ifcall.io.count == 0) {
506 		respond(r, nil);
507 		return;
508 	}
509 	f = r->fid->aux;
510 
511 	if(!ixp_srv_verifyfile(f, lookup_file)) {
512 		respond(r, Enofile);
513 		return;
514 	}
515 
516 	switch(f->tab.type) {
517 	case FsFColRules:
518 	case FsFTagRules:
519 		ixp_srv_writebuf(r, &f->p.rule->string, &f->p.rule->size, 0);
520 		respond(r, nil);
521 		return;
522 	case FsFKeys:
523 		ixp_srv_writebuf(r, &def.keys, &def.keyssz, 0);
524 		respond(r, nil);
525 		return;
526 	case FsFClabel:
527 		ixp_srv_data2cstring(r);
528 		utfecpy(f->p.client->name,
529 			f->p.client->name+sizeof(client->name),
530 			r->ifcall.io.data);
531 		frame_draw(f->p.client->sel);
532 		update_class(f->p.client);
533 		r->ofcall.io.count = r->ifcall.io.count;
534 		respond(r, nil);
535 		return;
536 	case FsFCtags:
537 		ixp_srv_data2cstring(r);
538 		client_applytags(f->p.client, r->ifcall.io.data);
539 		r->ofcall.io.count = r->ifcall.io.count;
540 		respond(r, nil);
541 		return;
542 	case FsFBar:
543 		i = strlen(f->p.bar->buf);
544 		p = f->p.bar->buf;
545 		ixp_srv_writebuf(r, &p, &i, 279);
546 		bar_load(f->p.bar);
547 		r->ofcall.io.count = i - r->ifcall.io.offset;
548 		respond(r, nil);
549 		return;
550 	case FsFCctl:
551 		mf = (MsgFunc)message_client;
552 		goto msg;
553 	case FsFTctl:
554 		mf = (MsgFunc)message_view;
555 		goto msg;
556 	case FsFRctl:
557 		mf = (MsgFunc)message_root;
558 		goto msg;
559 	msg:
560 		errstr = ixp_srv_writectl(r, mf);
561 		r->ofcall.io.count = r->ifcall.io.count;
562 		respond(r, errstr);
563 		return;
564 	case FsFEvent:
565 		if(r->ifcall.io.data[r->ifcall.io.count-1] == '\n')
566 			event("%.*s", (int)r->ifcall.io.count, r->ifcall.io.data);
567 		else
568 			event("%.*s\n", (int)r->ifcall.io.count, r->ifcall.io.data);
569 		r->ofcall.io.count = r->ifcall.io.count;
570 		respond(r, nil);
571 		return;
572 	}
573 	/*
574 	/* This should not be called if the file is not open for writing. */
575 	die("Write called on an unwritable file");
576 }
577 
578 void
fs_open(Ixp9Req * r)579 fs_open(Ixp9Req *r) {
580 	IxpFileId *f;
581 
582 	f = r->fid->aux;
583 
584 	if(!ixp_srv_verifyfile(f, lookup_file)) {
585 		respond(r, Enofile);
586 		return;
587 	}
588 
589 	switch(f->tab.type) {
590 	case FsFEvent:
591 		ixp_pending_pushfid(&events, r->fid);
592 		break;
593 	case FsFDebug:
594 		ixp_pending_pushfid(pdebug+f->id, r->fid);
595 		debugfile |= 1<<f->id;
596 		break;
597 	}
598 
599 	if((r->ifcall.topen.mode&3) == OEXEC
600 	|| (r->ifcall.topen.mode&3) != OREAD && !(f->tab.perm & 0200)
601 	|| (r->ifcall.topen.mode&3) != OWRITE && !(f->tab.perm & 0400)
602 	|| (r->ifcall.topen.mode & ~(3|OAPPEND|OTRUNC)))
603 		respond(r, Enoperm);
604 	else
605 		respond(r, nil);
606 }
607 
608 void
fs_create(Ixp9Req * r)609 fs_create(Ixp9Req *r) {
610 	IxpFileId *f;
611 
612 	f = r->fid->aux;
613 
614 	switch(f->tab.type) {
615 	default:
616 		respond(r, Enoperm);
617 		return;
618 	case FsDBars:
619 		if(!strlen(r->ifcall.tcreate.name)) {
620 			respond(r, Ebadvalue);
621 			return;
622 		}
623 		bar_create(f->p.bar_p, r->ifcall.tcreate.name);
624 		f = lookup_file(f, r->ifcall.tcreate.name);
625 		if(!f) {
626 			respond(r, Enofile);
627 			return;
628 		}
629 		r->ofcall.ropen.qid.type = f->tab.qtype;
630 		r->ofcall.ropen.qid.path = QID(f->tab.type, f->id);
631 		f->next = r->fid->aux;
632 		r->fid->aux = f;
633 		respond(r, nil);
634 		break;
635 	}
636 }
637 
638 void
fs_remove(Ixp9Req * r)639 fs_remove(Ixp9Req *r) {
640 	IxpFileId *f;
641 	WMScreen *s;
642 
643 	f = r->fid->aux;
644 	if(!ixp_srv_verifyfile(f, lookup_file)) {
645 		respond(r, Enofile);
646 		return;
647 	}
648 
649 
650 	switch(f->tab.type) {
651 	default:
652 		respond(r, Enoperm);
653 		return;
654 	case FsFBar:
655 		s = f->p.bar->screen;
656 		bar_destroy(f->next->p.bar_p, f->p.bar);
657 		bar_draw(s);
658 		respond(r, nil);
659 		break;
660 	}
661 }
662 
663 void
fs_clunk(Ixp9Req * r)664 fs_clunk(Ixp9Req *r) {
665 	IxpFileId *f;
666 
667 	f = r->fid->aux;
668 	if(!ixp_srv_verifyfile(f, lookup_file)) {
669 		respond(r, nil);
670 		return;
671 	}
672 
673 	if(f->pending) {
674 		/* Should probably be in freefid */
675 		if(ixp_pending_clunk(r)) {
676 			if(f->tab.type == FsFDebug)
677 				debugfile &= ~(1<<f->id);
678 		}
679 		return;
680 	}
681 
682 	switch(f->tab.type) {
683 	case FsFColRules:
684 		update_rules(&f->p.rule->rule, f->p.rule->string);
685 		break;
686 	case FsFTagRules:
687 		update_rules(&f->p.rule->rule, f->p.rule->string);
688 		/*
689 		for(c=client; c; c=c->next)
690 			apply_rules(c);
691 		view_update_all();
692 		*/
693 		break;
694 	case FsFKeys:
695 		update_keys();
696 		break;
697 	}
698 	respond(r, nil);
699 }
700 
701 void
fs_flush(Ixp9Req * r)702 fs_flush(Ixp9Req *r) {
703 	Ixp9Req *or;
704 	IxpFileId *f;
705 
706 	or = r->oldreq;
707 	f = or->fid->aux;
708 	if(f->pending)
709 		ixp_pending_flush(r);
710 	/* else die() ? */
711 	respond(r->oldreq, Einterrupted);
712 	respond(r, nil);
713 }
714 
715 void
fs_freefid(Fid * f)716 fs_freefid(Fid *f) {
717 	IxpFileId *id, *tid;
718 
719 	tid = f->aux;
720 	while((id = tid)) {
721 		tid = id->next;
722 		ixp_srv_freefile(id);
723 	}
724 }
725 
726