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