1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <bio.h> 6 #include <ndb.h> 7 #include <thread.h> 8 9 /* 10 * This fs presents a 1 level file system. It contains 11 * up to three files per console (xxx and xxxctl and xxxstat) 12 */ 13 14 typedef struct Console Console; 15 typedef struct Fid Fid; 16 typedef struct Request Request; 17 typedef struct Reqlist Reqlist; 18 typedef struct Fs Fs; 19 20 enum 21 { 22 /* last 5 bits of qid.path */ 23 Textern= 0, /* fake parent of top level */ 24 Ttopdir, /* top level directory */ 25 Qctl, 26 Qstat, 27 Qdata, 28 29 Bufsize= 32*1024, /* chars buffered per reader */ 30 Maxcons= 64, /* maximum consoles */ 31 Nhash= 64, /* Fid hash buckets */ 32 }; 33 34 #define TYPE(x) (((ulong)x.path) & 0xf) 35 #define CONS(x) ((((ulong)x.path) >> 4)&0xfff) 36 #define QID(c, x) (((c)<<4) | (x)) 37 38 struct Request 39 { 40 Request *next; 41 Fid *fid; 42 Fs *fs; 43 Fcall f; 44 uchar buf[1]; 45 }; 46 47 struct Reqlist 48 { 49 Lock; 50 Request *first; 51 Request *last; 52 }; 53 54 struct Fid 55 { 56 Lock; 57 Fid *next; /* hash list */ 58 Fid *cnext; /* list of Fid's on a console */ 59 int fid; 60 int ref; 61 62 int attached; 63 int open; 64 char *user; 65 char mbuf[Bufsize]; /* message */ 66 int bufn; 67 int used; 68 Qid qid; 69 70 Console *c; 71 72 char buf[Bufsize]; 73 char *rp; 74 char *wp; 75 76 Reqlist r; /* active read requests */ 77 }; 78 79 struct Console 80 { 81 Lock; 82 83 char *name; 84 char *dev; 85 int speed; 86 int cronly; 87 int ondemand; /* open only on demand */ 88 int chat; /* chat consoles are special */ 89 90 int pid; /* pid of reader */ 91 92 int fd; 93 int cfd; 94 int sfd; 95 96 Fid *flist; /* open fids to broadcast to */ 97 }; 98 99 struct Fs 100 { 101 Lock; 102 103 int fd; /* to kernel mount point */ 104 int messagesize; 105 Fid *hash[Nhash]; 106 Console *cons[Maxcons]; 107 int ncons; 108 }; 109 110 extern void console(Fs*, char*, char*, int, int, int); 111 extern Fs* fsmount(char*); 112 113 extern void fsreader(void*); 114 extern void fsrun(void*); 115 extern Fid* fsgetfid(Fs*, int); 116 extern void fsputfid(Fs*, Fid*); 117 extern int fsdirgen(Fs*, Qid, int, Dir*, uchar*, int); 118 extern void fsreply(Fs*, Request*, char*); 119 extern void fskick(Fs*, Fid*); 120 extern int fsreopen(Fs*, Console*); 121 122 extern void fsversion(Fs*, Request*, Fid*); 123 extern void fsflush(Fs*, Request*, Fid*); 124 extern void fsauth(Fs*, Request*, Fid*); 125 extern void fsattach(Fs*, Request*, Fid*); 126 extern void fswalk(Fs*, Request*, Fid*); 127 extern void fsclwalk(Fs*, Request*, Fid*); 128 extern void fsopen(Fs*, Request*, Fid*); 129 extern void fscreate(Fs*, Request*, Fid*); 130 extern void fsread(Fs*, Request*, Fid*); 131 extern void fswrite(Fs*, Request*, Fid*); 132 extern void fsclunk(Fs*, Request*, Fid*); 133 extern void fsremove(Fs*, Request*, Fid*); 134 extern void fsstat(Fs*, Request*, Fid*); 135 extern void fswstat(Fs*, Request*, Fid*); 136 137 138 void (*fcall[])(Fs*, Request*, Fid*) = 139 { 140 [Tflush] fsflush, 141 [Tversion] fsversion, 142 [Tauth] fsauth, 143 [Tattach] fsattach, 144 [Twalk] fswalk, 145 [Topen] fsopen, 146 [Tcreate] fscreate, 147 [Tread] fsread, 148 [Twrite] fswrite, 149 [Tclunk] fsclunk, 150 [Tremove] fsremove, 151 [Tstat] fsstat, 152 [Twstat] fswstat 153 }; 154 155 char Eperm[] = "permission denied"; 156 char Eexist[] = "file does not exist"; 157 char Enotdir[] = "not a directory"; 158 char Eisopen[] = "file already open"; 159 char Ebadcount[] = "bad read/write count"; 160 char Enofid[] = "no such fid"; 161 162 char *consoledb = "/lib/ndb/consoledb"; 163 char *mntpt = "/mnt/consoles"; 164 165 int messagesize = 8192+IOHDRSZ; 166 167 void 168 fatal(char *fmt, ...) 169 { 170 va_list arg; 171 char buf[1024]; 172 173 write(2, "consolefs: ", 10); 174 va_start(arg, fmt); 175 vseprint(buf, buf+1024, fmt, arg); 176 va_end(arg); 177 write(2, buf, strlen(buf)); 178 write(2, "\n", 1); 179 threadexitsall(fmt); 180 } 181 182 183 void* 184 emalloc(uint n) 185 { 186 void *p; 187 188 p = malloc(n); 189 if(p == nil) 190 fatal("malloc failed: %r"); 191 memset(p, 0, n); 192 return p; 193 } 194 195 int debug; 196 Ndb *db; 197 198 /* 199 * any request that can get queued for a delayed reply 200 */ 201 Request* 202 allocreq(Fs *fs, int bufsize) 203 { 204 Request *r; 205 206 r = emalloc(sizeof(Request)+bufsize); 207 r->fs = fs; 208 r->next = nil; 209 return r; 210 } 211 212 /* 213 * for maintaining lists of requests 214 */ 215 void 216 addreq(Reqlist *l, Request *r) 217 { 218 lock(l); 219 if(l->first == nil) 220 l->first = r; 221 else 222 l->last->next = r; 223 l->last = r; 224 r->next = nil; 225 unlock(l); 226 } 227 228 /* 229 * remove the first request from a list of requests 230 */ 231 Request* 232 remreq(Reqlist *l) 233 { 234 Request *r; 235 236 lock(l); 237 r = l->first; 238 if(r != nil) 239 l->first = r->next; 240 unlock(l); 241 return r; 242 } 243 244 /* 245 * remove a request with the given tag from a list of requests 246 */ 247 Request* 248 remtag(Reqlist *l, int tag) 249 { 250 Request *or, **ll; 251 252 lock(l); 253 ll = &l->first; 254 for(or = *ll; or; or = or->next){ 255 if(or->f.tag == tag){ 256 *ll = or->next; 257 unlock(l); 258 return or; 259 } 260 ll = &or->next; 261 } 262 unlock(l); 263 return nil; 264 } 265 266 Qid 267 parentqid(Qid q) 268 { 269 if(q.type & QTDIR) 270 return (Qid){QID(0, Textern), 0, QTDIR}; 271 else 272 return (Qid){QID(0, Ttopdir), 0, QTDIR}; 273 } 274 275 int 276 fsdirgen(Fs *fs, Qid parent, int i, Dir *d, uchar *buf, int nbuf) 277 { 278 static char name[64]; 279 char *p; 280 int xcons; 281 282 d->uid = d->gid = d->muid = "network"; 283 d->length = 0; 284 d->atime = time(nil); 285 d->mtime = d->atime; 286 d->type = 'C'; 287 d->dev = '0'; 288 289 switch(TYPE(parent)){ 290 case Textern: 291 if(i != 0) 292 return -1; 293 p = "consoles"; 294 d->mode = DMDIR|0555; 295 d->qid.type = QTDIR; 296 d->qid.path = QID(0, Ttopdir); 297 d->qid.vers = 0; 298 break; 299 case Ttopdir: 300 xcons = i/3; 301 if(xcons >= fs->ncons) 302 return -1; 303 p = fs->cons[xcons]->name; 304 switch(i%3){ 305 case 0: 306 if(fs->cons[xcons]->cfd < 0) 307 return 0; 308 snprint(name, sizeof name, "%sctl", p); 309 p = name; 310 d->qid.type = QTFILE; 311 d->qid.path = QID(xcons, Qctl); 312 d->qid.vers = 0; 313 break; 314 case 1: 315 if(fs->cons[xcons]->sfd < 0) 316 return 0; 317 snprint(name, sizeof name, "%sstat", p); 318 p = name; 319 d->qid.type = QTFILE; 320 d->qid.path = QID(xcons, Qstat); 321 d->qid.vers = 0; 322 break; 323 case 2: 324 d->qid.type = QTFILE; 325 d->qid.path = QID(xcons, Qdata); 326 d->qid.vers = 0; 327 break; 328 } 329 d->mode = 0666; 330 break; 331 default: 332 return -1; 333 } 334 d->name = p; 335 if(buf != nil) 336 return convD2M(d, buf, nbuf); 337 return 1; 338 } 339 340 /* 341 * mount the user interface and start a request processor 342 */ 343 Fs* 344 fsmount(char *mntpt) 345 { 346 Fs *fs; 347 int pfd[2], srv; 348 char buf[32]; 349 int n; 350 static void *v[2]; 351 352 fs = emalloc(sizeof(Fs)); 353 354 if(pipe(pfd) < 0) 355 fatal("opening pipe: %r"); 356 357 /* start up the file system process */ 358 v[0] = fs; 359 v[1] = pfd; 360 proccreate(fsrun, v, 16*1024); 361 362 /* Typically mounted before /srv exists */ 363 if(access("#s/consoles", AEXIST) < 0){ 364 srv = create("#s/consoles", OWRITE, 0666); 365 if(srv < 0) 366 fatal("post: %r"); 367 368 n = sprint(buf, "%d", pfd[1]); 369 if(write(srv, buf, n) < 0) 370 fatal("write srv: %r"); 371 372 close(srv); 373 } 374 375 mount(pfd[1], -1, mntpt, MBEFORE, ""); 376 close(pfd[1]); 377 return fs; 378 } 379 380 /* 381 * reopen a console 382 */ 383 int 384 fsreopen(Fs* fs, Console *c) 385 { 386 char buf[128]; 387 static void *v[2]; 388 389 if(c->pid){ 390 if(postnote(PNPROC, c->pid, "reopen") != 0) 391 fprint(2, "postnote failed: %r\n"); 392 c->pid = 0; 393 } 394 395 if(c->fd >= 0){ 396 close(c->fd); 397 close(c->cfd); 398 close(c->sfd); 399 c->cfd = -1; 400 c->fd = -1; 401 c->sfd = -1; 402 } 403 404 if(c->flist == nil && c->ondemand) 405 return 0; 406 407 c->fd = open(c->dev, ORDWR); 408 if(c->fd < 0) 409 return -1; 410 411 snprint(buf, sizeof(buf), "%sctl", c->dev); 412 c->cfd = open(buf, ORDWR); 413 fprint(c->cfd, "b%d", c->speed); 414 415 snprint(buf, sizeof(buf), "%sstat", c->dev); 416 c->sfd = open(buf, OREAD); 417 418 v[0] = fs; 419 v[1] = c; 420 proccreate(fsreader, v, 16*1024); 421 422 return 0; 423 } 424 425 void 426 change(Fs *fs, Console *c, int doreopen, int speed, int cronly, int ondemand) 427 { 428 lock(c); 429 430 if(speed != c->speed){ 431 c->speed = speed; 432 doreopen = 1; 433 } 434 if(ondemand != c->ondemand){ 435 c->ondemand = ondemand; 436 doreopen = 1; 437 } 438 c->cronly = cronly; 439 if(doreopen) 440 fsreopen(fs, c); 441 442 unlock(c); 443 } 444 445 /* 446 * create a console interface 447 */ 448 void 449 console(Fs* fs, char *name, char *dev, int speed, int cronly, int ondemand) 450 { 451 Console *c; 452 char *x; 453 int i, doreopen; 454 455 if(fs->ncons >= Maxcons) 456 fatal("too many consoles, too little time"); 457 458 doreopen = 0; 459 for(i = 0; i < fs->ncons; i++){ 460 c = fs->cons[i]; 461 if(strcmp(name, c->name) == 0){ 462 if(strcmp(dev, c->dev) != 0){ 463 /* new device */ 464 x = c->dev; 465 c->dev = strdup(dev); 466 free(x); 467 doreopen = 1; 468 } 469 change(fs, c, doreopen, speed, cronly, ondemand); 470 return; 471 } 472 } 473 for(i = 0; i < fs->ncons; i++){ 474 c = fs->cons[i]; 475 if(strcmp(dev, c->dev) == 0){ 476 /* at least a rename */ 477 x = c->name; 478 c->name = strdup(name); 479 free(x); 480 change(fs, c, doreopen, speed, cronly, ondemand); 481 return; 482 } 483 } 484 485 c = emalloc(sizeof(Console)); 486 fs->cons[fs->ncons] = c; 487 fs->ncons++; 488 c->name = strdup(name); 489 c->dev = strdup(dev); 490 if(strcmp(c->dev, "/dev/null") == 0) 491 c->chat = 1; 492 else 493 c->chat = 0; 494 c->fd = -1; 495 c->cfd = -1; 496 c->sfd = -1; 497 change(fs, c, 1, speed, cronly, ondemand); 498 } 499 500 /* 501 * buffer data from console to a client. 502 * circular q with writer able to catch up to reader. 503 * the reader may miss data but always sees an in order sequence. 504 */ 505 void 506 fromconsole(Fid *f, char *p, int n) 507 { 508 char *rp, *wp, *ep; 509 int pass; 510 511 lock(f); 512 rp = f->rp; 513 wp = f->wp; 514 ep = f->buf + sizeof(f->buf); 515 pass = 0; 516 while(n--){ 517 *wp++ = *p++; 518 if(wp >= ep) 519 wp = f->buf; 520 if(rp == wp) 521 pass = 1; 522 } 523 f->wp = wp; 524 525 /* we overtook the read pointer, push it up so readers always 526 * see the tail of what was written 527 */ 528 if(pass){ 529 wp++; 530 if(wp >= ep) 531 f->rp = f->buf; 532 else 533 f->rp = wp; 534 } 535 unlock(f); 536 } 537 538 /* 539 * broadcast a list of members to all listeners 540 */ 541 void 542 bcastmembers(Fs *fs, Console *c, char *msg, Fid *f) 543 { 544 int n; 545 Fid *fl; 546 char buf[512]; 547 548 sprint(buf, "[%s%s", msg, f->user); 549 for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){ 550 if(f == fl) 551 continue; 552 strcat(buf, ", "); 553 strcat(buf, fl->user); 554 } 555 strcat(buf, "]\n"); 556 557 n = strlen(buf); 558 for(fl = c->flist; fl; fl = fl->cnext){ 559 fromconsole(fl, buf, n); 560 fskick(fs, fl); 561 } 562 } 563 564 void 565 handler(void*, char *msg) 566 { 567 if(strstr(msg, "reopen")) 568 noted(NCONT); 569 noted(NDFLT); 570 } 571 572 /* 573 * a process to read console output and broadcast it (one per console) 574 */ 575 void 576 fsreader(void *v) 577 { 578 int n; 579 Fid *fl; 580 char buf[1024]; 581 Fs *fs; 582 Console *c; 583 void **a; 584 585 a = v; 586 fs = a[0]; 587 c = a[1]; 588 c->pid = getpid(); 589 notify(handler); 590 if(c->chat) 591 threadexits(nil); 592 for(;;){ 593 n = read(c->fd, buf, sizeof(buf)); 594 if(n < 0) 595 break; 596 lock(c); 597 for(fl = c->flist; fl; fl = fl->cnext){ 598 fromconsole(fl, buf, n); 599 fskick(fs, fl); 600 } 601 unlock(c); 602 } 603 } 604 605 void 606 readdb(Fs *fs) 607 { 608 Ndbtuple *t, *nt; 609 char *dev, *cons; 610 int cronly, speed, ondemand; 611 612 ndbreopen(db); 613 614 /* start a listener for each console */ 615 for(;;){ 616 t = ndbparse(db); 617 if(t == nil) 618 break; 619 dev = nil; 620 cons = nil; 621 speed = 9600; 622 cronly = 0; 623 ondemand = 0; 624 for(nt = t; nt; nt = nt->entry){ 625 if(strcmp(nt->attr, "console") == 0) 626 cons = nt->val; 627 else if(strcmp(nt->attr, "dev") == 0) 628 dev = nt->val; 629 else if(strcmp(nt->attr, "speed") == 0) 630 speed = atoi(nt->val); 631 else if(strcmp(nt->attr, "cronly") == 0) 632 cronly = 1; 633 else if(strcmp(nt->attr, "openondemand") == 0) 634 ondemand = 1; 635 } 636 if(dev != nil && cons != nil) 637 console(fs, cons, dev, speed, cronly, ondemand); 638 ndbfree(t); 639 } 640 } 641 642 int dbmtime; 643 644 /* 645 * a request processor (one per Fs) 646 */ 647 void 648 fsrun(void *v) 649 { 650 int n, t; 651 Request *r; 652 Fid *f; 653 Dir *d; 654 void **a = v; 655 Fs* fs; 656 int *pfd; 657 658 fs = a[0]; 659 pfd = a[1]; 660 fs->fd = pfd[0]; 661 for(;;){ 662 d = dirstat(consoledb); 663 if(d != nil && d->mtime != dbmtime){ 664 dbmtime = d->mtime; 665 readdb(fs); 666 } 667 free(d); 668 r = allocreq(fs, messagesize); 669 n = read9pmsg(fs->fd, r->buf, messagesize); 670 if(n <= 0) 671 fatal("unmounted"); 672 673 if(convM2S(r->buf, n, &r->f) == 0){ 674 fprint(2, "can't convert %ux %ux %ux\n", r->buf[0], 675 r->buf[1], r->buf[2]); 676 free(r); 677 continue; 678 } 679 680 681 f = fsgetfid(fs, r->f.fid); 682 r->fid = f; 683 if(debug) 684 fprint(2, "%F path %llux\n", &r->f, f->qid.path); 685 686 t = r->f.type; 687 r->f.type++; 688 (*fcall[t])(fs, r, f); 689 } 690 } 691 692 Fid* 693 fsgetfid(Fs *fs, int fid) 694 { 695 Fid *f, *nf; 696 697 lock(fs); 698 for(f = fs->hash[fid%Nhash]; f; f = f->next){ 699 if(f->fid == fid){ 700 f->ref++; 701 unlock(fs); 702 return f; 703 } 704 } 705 706 nf = emalloc(sizeof(Fid)); 707 nf->next = fs->hash[fid%Nhash]; 708 fs->hash[fid%Nhash] = nf; 709 nf->fid = fid; 710 nf->ref = 1; 711 nf->wp = nf->buf; 712 nf->rp = nf->wp; 713 unlock(fs); 714 return nf; 715 } 716 717 void 718 fsputfid(Fs *fs, Fid *f) 719 { 720 Fid **l, *nf; 721 722 lock(fs); 723 if(--f->ref > 0){ 724 unlock(fs); 725 return; 726 } 727 for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next) 728 if(nf == f){ 729 *l = f->next; 730 break; 731 } 732 unlock(fs); 733 free(f->user); 734 free(f); 735 } 736 737 void 738 fsauth(Fs *fs, Request *r, Fid*) 739 { 740 fsreply(fs, r, "consolefs: authentication not required"); 741 } 742 743 void 744 fsversion(Fs *fs, Request *r, Fid*) 745 { 746 747 if(r->f.msize < 256){ 748 fsreply(fs, r, "message size too small"); 749 return; 750 } 751 messagesize = r->f.msize; 752 if(messagesize > 8192+IOHDRSZ) 753 messagesize = 8192+IOHDRSZ; 754 r->f.msize = messagesize; 755 if(strncmp(r->f.version, "9P2000", 6) != 0){ 756 fsreply(fs, r, "unrecognized 9P version"); 757 return; 758 } 759 r->f.version = "9P2000"; 760 761 fsreply(fs, r, nil); 762 } 763 764 void 765 fsflush(Fs *fs, Request *r, Fid *f) 766 { 767 Request *or; 768 769 or = remtag(&f->r, r->f.oldtag); 770 if(or != nil){ 771 fsputfid(fs, or->fid); 772 free(or); 773 } 774 fsreply(fs, r, nil); 775 } 776 777 void 778 fsattach(Fs *fs, Request *r, Fid *f) 779 { 780 f->qid.type = QTDIR; 781 f->qid.path = QID(0, Ttopdir); 782 f->qid.vers = 0; 783 784 if(r->f.uname[0]) 785 f->user = strdup(r->f.uname); 786 else 787 f->user = strdup("none"); 788 789 /* hold down the fid till the clunk */ 790 f->attached = 1; 791 lock(fs); 792 f->ref++; 793 unlock(fs); 794 795 r->f.qid = f->qid; 796 fsreply(fs, r, nil); 797 } 798 799 void 800 fswalk(Fs *fs, Request *r, Fid *f) 801 { 802 char *name; 803 Dir d; 804 int i, n, nqid, nwname; 805 Qid qid, wqid[MAXWELEM]; 806 Fid *nf; 807 char *err; 808 809 if(f->attached == 0){ 810 fsreply(fs, r, Enofid); 811 return; 812 } 813 814 nf = nil; 815 if(r->f.fid != r->f.newfid){ 816 nf = fsgetfid(fs, r->f.newfid); 817 nf->attached = f->attached; 818 nf->open = f->open; 819 nf->qid = f->qid; 820 nf->user = strdup(f->user); 821 nf->c = f->c; 822 nf->wp = nf->buf; 823 nf->rp = nf->wp; 824 f = nf; 825 } 826 827 qid = f->qid; 828 err = nil; 829 nwname = r->f.nwname; 830 nqid = 0; 831 if(nwname > 0){ 832 for(; err == nil && nqid < nwname; nqid++){ 833 if(nqid >= MAXWELEM){ 834 err = "too many name elements"; 835 break; 836 } 837 name = r->f.wname[nqid]; 838 if(strcmp(name, "..") == 0) 839 qid = parentqid(qid); 840 else if(strcmp(name, ".") != 0){ 841 for(i = 0; ; i++){ 842 n = fsdirgen(fs, qid, i, &d, nil, 0); 843 if(n < 0){ 844 err = Eexist; 845 break; 846 } 847 if(n > 0 && strcmp(name, d.name) == 0){ 848 qid = d.qid; 849 break; 850 } 851 } 852 } 853 wqid[nqid] = qid; 854 } 855 if(nf != nil && nqid < nwname) 856 fsputfid(fs, nf); 857 if(nqid == nwname) 858 f->qid = qid; 859 } 860 861 memmove(r->f.wqid, wqid, nqid*sizeof(Qid)); 862 r->f.nwqid = nqid; 863 fsreply(fs, r, err); 864 } 865 866 int 867 ingroup(char *user, char *group) 868 { 869 Ndbtuple *t, *nt; 870 Ndbs s; 871 872 t = ndbsearch(db, &s, "group", group); 873 if(t == nil) 874 return 0; 875 for(nt = t; nt; nt = nt->entry){ 876 if(strcmp(nt->attr, "uid") == 0) 877 if(strcmp(nt->val, user) == 0) 878 break; 879 } 880 ndbfree(t); 881 return nt != nil; 882 } 883 884 int 885 userok(char *u, char *cname) 886 { 887 Ndbtuple *t, *nt; 888 Ndbs s; 889 890 t = ndbsearch(db, &s, "console", cname); 891 if(t == nil) 892 return 0; 893 894 for(nt = t; nt; nt = nt->entry){ 895 if(strcmp(nt->attr, "uid") == 0) 896 if(strcmp(nt->val, u) == 0) 897 break; 898 if(strcmp(nt->attr, "gid") == 0) 899 if(ingroup(u, nt->val)) 900 break; 901 } 902 ndbfree(t); 903 904 return nt != nil; 905 } 906 907 int m2p[] ={ 908 [OREAD] 4, 909 [OWRITE] 2, 910 [ORDWR] 6 911 }; 912 913 /* 914 * broadcast a message to all listeners 915 */ 916 void 917 bcastmsg(Fs *fs, Console *c, char *msg, int n) 918 { 919 Fid *fl; 920 921 for(fl = c->flist; fl; fl = fl->cnext){ 922 fromconsole(fl, msg, n); 923 fskick(fs, fl); 924 } 925 } 926 927 void 928 fsopen(Fs *fs, Request *r, Fid *f) 929 { 930 int mode; 931 Console *c; 932 933 if(f->attached == 0){ 934 fsreply(fs, r, Enofid); 935 return; 936 } 937 938 if(f->open){ 939 fsreply(fs, r, Eisopen); 940 return; 941 } 942 943 mode = r->f.mode & 3; 944 945 if((QTDIR & f->qid.type) && mode != OREAD){ 946 fsreply(fs, r, Eperm); 947 return; 948 } 949 950 switch(TYPE(f->qid)){ 951 case Qdata: 952 c = fs->cons[CONS(f->qid)]; 953 if(!userok(f->user, c->name)){ 954 fsreply(fs, r, Eperm); 955 return; 956 } 957 f->rp = f->buf; 958 f->wp = f->buf; 959 f->c = c; 960 lock(c); 961 sprint(f->mbuf, "[%s] ", f->user); 962 f->bufn = strlen(f->mbuf); 963 f->used = 0; 964 f->cnext = c->flist; 965 c->flist = f; 966 bcastmembers(fs, c, "+", f); 967 if(c->pid == 0) 968 fsreopen(fs, c); 969 unlock(c); 970 break; 971 case Qctl: 972 c = fs->cons[CONS(f->qid)]; 973 if(!userok(f->user, c->name)){ 974 fsreply(fs, r, Eperm); 975 return; 976 } 977 f->c = c; 978 break; 979 case Qstat: 980 c = fs->cons[CONS(f->qid)]; 981 if(!userok(f->user, c->name)){ 982 fsreply(fs, r, Eperm); 983 return; 984 } 985 f->c = c; 986 break; 987 } 988 989 f->open = 1; 990 r->f.iounit = messagesize-IOHDRSZ; 991 r->f.qid = f->qid; 992 fsreply(fs, r, nil); 993 } 994 995 void 996 fscreate(Fs *fs, Request *r, Fid*) 997 { 998 fsreply(fs, r, Eperm); 999 } 1000 1001 void 1002 fsread(Fs *fs, Request *r, Fid *f) 1003 { 1004 uchar *p, *e; 1005 int i, m, off; 1006 vlong offset; 1007 Dir d; 1008 char sbuf[ERRMAX]; 1009 1010 if(f->attached == 0){ 1011 fsreply(fs, r, Enofid); 1012 return; 1013 } 1014 1015 if((int)r->f.count < 0){ 1016 fsreply(fs, r, Ebadcount); 1017 return; 1018 } 1019 1020 if(QTDIR & f->qid.type){ 1021 p = r->buf + IOHDRSZ; 1022 e = p + r->f.count; 1023 offset = r->f.offset; 1024 off = 0; 1025 for(i=0; p<e; i++, off+=m){ 1026 m = fsdirgen(fs, f->qid, i, &d, p, e-p); 1027 if(m < 0) 1028 break; 1029 if(m > BIT16SZ && off >= offset) 1030 p += m; 1031 } 1032 r->f.data = (char*)r->buf + IOHDRSZ; 1033 r->f.count = (char*)p - r->f.data; 1034 } else { 1035 switch(TYPE(f->qid)){ 1036 case Qdata: 1037 addreq(&f->r, r); 1038 fskick(fs, f); 1039 return; 1040 case Qctl: 1041 r->f.data = (char*)r->buf+IOHDRSZ; 1042 r->f.count = 0; 1043 break; 1044 case Qstat: 1045 if(r->f.count > sizeof(sbuf)) 1046 r->f.count = sizeof(sbuf); 1047 i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset); 1048 if(i < 0){ 1049 errstr(sbuf, sizeof sbuf); 1050 fsreply(fs, r, sbuf); 1051 return; 1052 } 1053 r->f.data = sbuf; 1054 r->f.count = i; 1055 break; 1056 default: 1057 fsreply(fs, r, Eexist); 1058 return; 1059 } 1060 } 1061 fsreply(fs, r, nil); 1062 } 1063 1064 void 1065 fswrite(Fs *fs, Request *r, Fid *f) 1066 { 1067 int i, eol = 0; 1068 1069 if(f->attached == 0){ 1070 fsreply(fs, r, Enofid); 1071 return; 1072 } 1073 1074 if((int)r->f.count < 0){ 1075 fsreply(fs, r, Ebadcount); 1076 return; 1077 } 1078 1079 if(QTDIR & f->qid.type){ 1080 fsreply(fs, r, Eperm); 1081 return; 1082 } 1083 1084 switch(TYPE(f->qid)){ 1085 default: 1086 fsreply(fs, r, Eperm); 1087 return; 1088 case Qctl: 1089 write(f->c->cfd, r->f.data, r->f.count); 1090 break; 1091 case Qdata: 1092 for(i = 0; i < r->f.count; i++){ 1093 if(r->f.data[i] == '\n'){ 1094 if(f->c->chat && f->used) 1095 eol = 1; 1096 if(f->c->cronly) 1097 r->f.data[i] = '\r'; 1098 } 1099 else 1100 f->used = 1; 1101 } 1102 if(f->c->chat){ 1103 fskick(fs, f); 1104 if(!f->used) 1105 break; 1106 1107 if(f->bufn + r->f.count > Bufsize){ 1108 r->f.count -= (f->bufn + r->f.count) % Bufsize; 1109 eol = 1; 1110 } 1111 strncat(f->mbuf, r->f.data, r->f.count); 1112 f->bufn += r->f.count; 1113 if(eol){ 1114 bcastmsg(fs, f->c, f->mbuf, f->bufn); 1115 sprint(f->mbuf, "[%s] ", f->user); 1116 f->bufn = strlen(f->mbuf); 1117 f->used = 0; 1118 } 1119 } 1120 else 1121 write(f->c->fd, r->f.data, r->f.count); 1122 break; 1123 } 1124 fsreply(fs, r, nil); 1125 } 1126 1127 void 1128 fsclunk(Fs *fs, Request *r, Fid *f) 1129 { 1130 Fid **l, *fl; 1131 Request *nr; 1132 1133 if(f->open && TYPE(f->qid) == Qdata){ 1134 while((nr = remreq(&f->r)) != nil){ 1135 fsputfid(fs, f); 1136 free(nr); 1137 } 1138 1139 lock(f->c); 1140 for(l = &f->c->flist; *l; l = &fl->cnext){ 1141 fl = *l; 1142 if(fl == f){ 1143 *l = fl->cnext; 1144 break; 1145 } 1146 } 1147 bcastmembers(fs, f->c, "-", f); 1148 if(f->c->ondemand && f->c->flist == nil) 1149 fsreopen(fs, f->c); 1150 unlock(f->c); 1151 } 1152 fsreply(fs, r, nil); 1153 fsputfid(fs, f); 1154 } 1155 1156 void 1157 fsremove(Fs *fs, Request *r, Fid*) 1158 { 1159 fsreply(fs, r, Eperm); 1160 } 1161 1162 void 1163 fsstat(Fs *fs, Request *r, Fid *f) 1164 { 1165 int i, n; 1166 Qid q; 1167 Dir d; 1168 1169 q = parentqid(f->qid); 1170 for(i = 0; ; i++){ 1171 r->f.stat = r->buf+IOHDRSZ; 1172 n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ); 1173 if(n < 0){ 1174 fsreply(fs, r, Eexist); 1175 return; 1176 } 1177 r->f.nstat = n; 1178 if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path) 1179 break; 1180 } 1181 fsreply(fs, r, nil); 1182 } 1183 1184 void 1185 fswstat(Fs *fs, Request *r, Fid*) 1186 { 1187 fsreply(fs, r, Eperm); 1188 } 1189 1190 void 1191 fsreply(Fs *fs, Request *r, char *err) 1192 { 1193 int n; 1194 uchar buf[8192+IOHDRSZ]; 1195 1196 if(err){ 1197 r->f.type = Rerror; 1198 r->f.ename = err; 1199 } 1200 n = convS2M(&r->f, buf, messagesize); 1201 if(debug) 1202 fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n); 1203 fsputfid(fs, r->fid); 1204 if(write(fs->fd, buf, n) != n) 1205 fatal("unmounted"); 1206 free(r); 1207 } 1208 1209 /* 1210 * called whenever input or a read request has been received 1211 */ 1212 void 1213 fskick(Fs *fs, Fid *f) 1214 { 1215 Request *r; 1216 char *p, *rp, *wp, *ep; 1217 int i; 1218 1219 lock(f); 1220 while(f->rp != f->wp){ 1221 r = remreq(&f->r); 1222 if(r == nil) 1223 break; 1224 p = (char*)r->buf; 1225 rp = f->rp; 1226 wp = f->wp; 1227 ep = &f->buf[Bufsize]; 1228 for(i = 0; i < r->f.count && rp != wp; i++){ 1229 *p++ = *rp++; 1230 if(rp >= ep) 1231 rp = f->buf; 1232 } 1233 f->rp = rp; 1234 r->f.data = (char*)r->buf; 1235 r->f.count = p - (char*)r->buf; 1236 fsreply(fs, r, nil); 1237 } 1238 unlock(f); 1239 } 1240 1241 void 1242 usage(void) 1243 { 1244 fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n"); 1245 threadexitsall("usage"); 1246 } 1247 1248 void 1249 threadmain(int argc, char **argv) 1250 { 1251 fmtinstall('F', fcallfmt); 1252 1253 ARGBEGIN{ 1254 case 'd': 1255 debug++; 1256 break; 1257 case 'c': 1258 consoledb = ARGF(); 1259 if(consoledb == nil) 1260 usage(); 1261 break; 1262 case 'm': 1263 mntpt = ARGF(); 1264 if(mntpt == nil) 1265 usage(); 1266 break; 1267 }ARGEND; 1268 1269 db = ndbopen(consoledb); 1270 if(db == nil) 1271 fatal("can't open %s: %r", consoledb); 1272 1273 fsmount(mntpt); 1274 } 1275