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 #ifdef sapedoesntlikethis 474 /* 475 * The code below prevents this from working. I can't 476 * think of scenarios where the code below actually helps 477 * Sape 478 * 479 * console=borneo dev=/dev/eia1 480 * speed=9600 481 * openondemand=1 482 * console=tottie dev=/dev/eia1 483 * speed=115200 484 * openondemand=1 485 */ 486 for(i = 0; i < fs->ncons; i++){ 487 c = fs->cons[i]; 488 if(strcmp(dev, c->dev) == 0){ 489 /* at least a rename */ 490 x = c->name; 491 c->name = strdup(name); 492 free(x); 493 change(fs, c, doreopen, speed, cronly, ondemand); 494 return; 495 } 496 } 497 #endif 498 c = emalloc(sizeof(Console)); 499 fs->cons[fs->ncons] = c; 500 fs->ncons++; 501 c->name = strdup(name); 502 c->dev = strdup(dev); 503 if(strcmp(c->dev, "/dev/null") == 0) 504 c->chat = 1; 505 else 506 c->chat = 0; 507 c->fd = -1; 508 c->cfd = -1; 509 c->sfd = -1; 510 change(fs, c, 1, speed, cronly, ondemand); 511 } 512 513 /* 514 * buffer data from console to a client. 515 * circular q with writer able to catch up to reader. 516 * the reader may miss data but always sees an in order sequence. 517 */ 518 void 519 fromconsole(Fid *f, char *p, int n) 520 { 521 char *rp, *wp, *ep; 522 int pass; 523 524 lock(f); 525 rp = f->rp; 526 wp = f->wp; 527 ep = f->buf + sizeof(f->buf); 528 pass = 0; 529 while(n--){ 530 *wp++ = *p++; 531 if(wp >= ep) 532 wp = f->buf; 533 if(rp == wp) 534 pass = 1; 535 } 536 f->wp = wp; 537 538 /* we overtook the read pointer, push it up so readers always 539 * see the tail of what was written 540 */ 541 if(pass){ 542 wp++; 543 if(wp >= ep) 544 f->rp = f->buf; 545 else 546 f->rp = wp; 547 } 548 unlock(f); 549 } 550 551 /* 552 * broadcast a list of members to all listeners 553 */ 554 void 555 bcastmembers(Fs *fs, Console *c, char *msg, Fid *f) 556 { 557 int n; 558 Fid *fl; 559 char buf[512]; 560 561 sprint(buf, "[%s%s", msg, f->user); 562 for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){ 563 if(f == fl) 564 continue; 565 strcat(buf, ", "); 566 strcat(buf, fl->user); 567 } 568 strcat(buf, "]\n"); 569 570 n = strlen(buf); 571 for(fl = c->flist; fl; fl = fl->cnext){ 572 fromconsole(fl, buf, n); 573 fskick(fs, fl); 574 } 575 } 576 577 void 578 handler(void*, char *msg) 579 { 580 if(strstr(msg, "reopen") != nil || 581 strstr(msg, "write on closed pipe") != nil) 582 noted(NCONT); 583 noted(NDFLT); 584 } 585 586 /* 587 * a process to read console output and broadcast it (one per console) 588 */ 589 void 590 fsreader(void *v) 591 { 592 int n; 593 Fid *fl; 594 char buf[1024]; 595 Fs *fs; 596 Console *c; 597 void **a; 598 599 a = v; 600 fs = a[0]; 601 c = a[1]; 602 c->pid = getpid(); 603 notify(handler); 604 if(c->chat) 605 threadexits(nil); 606 for(;;){ 607 n = read(c->fd, buf, sizeof(buf)); 608 if(n < 0) 609 break; 610 lock(c); 611 for(fl = c->flist; fl; fl = fl->cnext){ 612 fromconsole(fl, buf, n); 613 fskick(fs, fl); 614 } 615 unlock(c); 616 } 617 } 618 619 void 620 readdb(Fs *fs) 621 { 622 Ndbtuple *t, *nt; 623 char *dev, *cons; 624 int cronly, speed, ondemand; 625 626 ndbreopen(db); 627 628 /* start a listener for each console */ 629 for(;;){ 630 t = ndbparse(db); 631 if(t == nil) 632 break; 633 dev = nil; 634 cons = nil; 635 speed = 9600; 636 cronly = 0; 637 ondemand = 0; 638 for(nt = t; nt; nt = nt->entry){ 639 if(strcmp(nt->attr, "console") == 0) 640 cons = nt->val; 641 else if(strcmp(nt->attr, "dev") == 0) 642 dev = nt->val; 643 else if(strcmp(nt->attr, "speed") == 0) 644 speed = atoi(nt->val); 645 else if(strcmp(nt->attr, "cronly") == 0) 646 cronly = 1; 647 else if(strcmp(nt->attr, "openondemand") == 0) 648 ondemand = 1; 649 } 650 if(dev != nil && cons != nil) 651 console(fs, cons, dev, speed, cronly, ondemand); 652 ndbfree(t); 653 } 654 } 655 656 int dbmtime; 657 658 /* 659 * a request processor (one per Fs) 660 */ 661 void 662 fsrun(void *v) 663 { 664 int n, t; 665 Request *r; 666 Fid *f; 667 Dir *d; 668 void **a = v; 669 Fs* fs; 670 int *pfd; 671 672 fs = a[0]; 673 pfd = a[1]; 674 fs->fd = pfd[0]; 675 notify(handler); 676 for(;;){ 677 d = dirstat(consoledb); 678 if(d != nil && d->mtime != dbmtime){ 679 dbmtime = d->mtime; 680 readdb(fs); 681 } 682 free(d); 683 r = allocreq(fs, messagesize); 684 n = read9pmsg(fs->fd, r->buf, messagesize); 685 if(n <= 0) 686 fatal("unmounted"); 687 688 if(convM2S(r->buf, n, &r->f) == 0){ 689 fprint(2, "can't convert %ux %ux %ux\n", r->buf[0], 690 r->buf[1], r->buf[2]); 691 free(r); 692 continue; 693 } 694 695 696 f = fsgetfid(fs, r->f.fid); 697 r->fid = f; 698 if(debug) 699 fprint(2, "%F path %llux\n", &r->f, f->qid.path); 700 701 t = r->f.type; 702 r->f.type++; 703 (*fcall[t])(fs, r, f); 704 } 705 } 706 707 Fid* 708 fsgetfid(Fs *fs, int fid) 709 { 710 Fid *f, *nf; 711 712 lock(fs); 713 for(f = fs->hash[fid%Nhash]; f; f = f->next){ 714 if(f->fid == fid){ 715 f->ref++; 716 unlock(fs); 717 return f; 718 } 719 } 720 721 nf = emalloc(sizeof(Fid)); 722 nf->next = fs->hash[fid%Nhash]; 723 fs->hash[fid%Nhash] = nf; 724 nf->fid = fid; 725 nf->ref = 1; 726 nf->wp = nf->buf; 727 nf->rp = nf->wp; 728 unlock(fs); 729 return nf; 730 } 731 732 void 733 fsputfid(Fs *fs, Fid *f) 734 { 735 Fid **l, *nf; 736 737 lock(fs); 738 if(--f->ref > 0){ 739 unlock(fs); 740 return; 741 } 742 for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next) 743 if(nf == f){ 744 *l = f->next; 745 break; 746 } 747 unlock(fs); 748 free(f->user); 749 free(f); 750 } 751 752 void 753 fsauth(Fs *fs, Request *r, Fid*) 754 { 755 fsreply(fs, r, "consolefs: authentication not required"); 756 } 757 758 void 759 fsversion(Fs *fs, Request *r, Fid*) 760 { 761 762 if(r->f.msize < 256){ 763 fsreply(fs, r, "message size too small"); 764 return; 765 } 766 messagesize = r->f.msize; 767 if(messagesize > 8192+IOHDRSZ) 768 messagesize = 8192+IOHDRSZ; 769 r->f.msize = messagesize; 770 if(strncmp(r->f.version, "9P2000", 6) != 0){ 771 fsreply(fs, r, "unrecognized 9P version"); 772 return; 773 } 774 r->f.version = "9P2000"; 775 776 fsreply(fs, r, nil); 777 } 778 779 void 780 fsflush(Fs *fs, Request *r, Fid *f) 781 { 782 Request *or; 783 784 or = remtag(&f->r, r->f.oldtag); 785 if(or != nil){ 786 fsputfid(fs, or->fid); 787 free(or); 788 } 789 fsreply(fs, r, nil); 790 } 791 792 void 793 fsattach(Fs *fs, Request *r, Fid *f) 794 { 795 f->qid.type = QTDIR; 796 f->qid.path = QID(0, Ttopdir); 797 f->qid.vers = 0; 798 799 if(r->f.uname[0]) 800 f->user = strdup(r->f.uname); 801 else 802 f->user = strdup("none"); 803 804 /* hold down the fid till the clunk */ 805 f->attached = 1; 806 lock(fs); 807 f->ref++; 808 unlock(fs); 809 810 r->f.qid = f->qid; 811 fsreply(fs, r, nil); 812 } 813 814 void 815 fswalk(Fs *fs, Request *r, Fid *f) 816 { 817 char *name; 818 Dir d; 819 int i, n, nqid, nwname; 820 Qid qid, wqid[MAXWELEM]; 821 Fid *nf; 822 char *err; 823 824 if(f->attached == 0){ 825 fsreply(fs, r, Enofid); 826 return; 827 } 828 829 nf = nil; 830 if(r->f.fid != r->f.newfid){ 831 nf = fsgetfid(fs, r->f.newfid); 832 nf->attached = f->attached; 833 nf->open = f->open; 834 nf->qid = f->qid; 835 nf->user = strdup(f->user); 836 nf->c = f->c; 837 nf->wp = nf->buf; 838 nf->rp = nf->wp; 839 f = nf; 840 } 841 842 qid = f->qid; 843 err = nil; 844 nwname = r->f.nwname; 845 nqid = 0; 846 if(nwname > 0){ 847 for(; err == nil && nqid < nwname; nqid++){ 848 if(nqid >= MAXWELEM){ 849 err = "too many name elements"; 850 break; 851 } 852 name = r->f.wname[nqid]; 853 if(strcmp(name, "..") == 0) 854 qid = parentqid(qid); 855 else if(strcmp(name, ".") != 0){ 856 for(i = 0; ; i++){ 857 n = fsdirgen(fs, qid, i, &d, nil, 0); 858 if(n < 0){ 859 err = Eexist; 860 break; 861 } 862 if(n > 0 && strcmp(name, d.name) == 0){ 863 qid = d.qid; 864 break; 865 } 866 } 867 } 868 wqid[nqid] = qid; 869 } 870 if(nf != nil && nqid < nwname) 871 fsputfid(fs, nf); 872 if(nqid == nwname) 873 f->qid = qid; 874 } 875 876 memmove(r->f.wqid, wqid, nqid*sizeof(Qid)); 877 r->f.nwqid = nqid; 878 fsreply(fs, r, err); 879 } 880 881 int 882 ingroup(char *user, char *group) 883 { 884 Ndbtuple *t, *nt; 885 Ndbs s; 886 887 t = ndbsearch(db, &s, "group", group); 888 if(t == nil) 889 return 0; 890 for(nt = t; nt; nt = nt->entry){ 891 if(strcmp(nt->attr, "uid") == 0) 892 if(strcmp(nt->val, user) == 0) 893 break; 894 } 895 ndbfree(t); 896 return nt != nil; 897 } 898 899 int 900 userok(char *u, char *cname) 901 { 902 Ndbtuple *t, *nt; 903 Ndbs s; 904 905 t = ndbsearch(db, &s, "console", cname); 906 if(t == nil) 907 return 0; 908 909 for(nt = t; nt; nt = nt->entry){ 910 if(strcmp(nt->attr, "uid") == 0) 911 if(strcmp(nt->val, u) == 0) 912 break; 913 if(strcmp(nt->attr, "gid") == 0) 914 if(ingroup(u, nt->val)) 915 break; 916 } 917 ndbfree(t); 918 919 return nt != nil; 920 } 921 922 int m2p[] ={ 923 [OREAD] 4, 924 [OWRITE] 2, 925 [ORDWR] 6 926 }; 927 928 /* 929 * broadcast a message to all listeners 930 */ 931 void 932 bcastmsg(Fs *fs, Console *c, char *msg, int n) 933 { 934 Fid *fl; 935 936 for(fl = c->flist; fl; fl = fl->cnext){ 937 fromconsole(fl, msg, n); 938 fskick(fs, fl); 939 } 940 } 941 942 void 943 fsopen(Fs *fs, Request *r, Fid *f) 944 { 945 int mode; 946 Console *c; 947 948 if(f->attached == 0){ 949 fsreply(fs, r, Enofid); 950 return; 951 } 952 953 if(f->open){ 954 fsreply(fs, r, Eisopen); 955 return; 956 } 957 958 mode = r->f.mode & 3; 959 960 if((QTDIR & f->qid.type) && mode != OREAD){ 961 fsreply(fs, r, Eperm); 962 return; 963 } 964 965 switch(TYPE(f->qid)){ 966 case Qdata: 967 c = fs->cons[CONS(f->qid)]; 968 if(!userok(f->user, c->name)){ 969 fsreply(fs, r, Eperm); 970 return; 971 } 972 f->rp = f->buf; 973 f->wp = f->buf; 974 f->c = c; 975 lock(c); 976 sprint(f->mbuf, "[%s] ", f->user); 977 f->bufn = strlen(f->mbuf); 978 f->used = 0; 979 f->cnext = c->flist; 980 c->flist = f; 981 bcastmembers(fs, c, "+", f); 982 if(c->pid == 0) 983 fsreopen(fs, c); 984 unlock(c); 985 break; 986 case Qctl: 987 c = fs->cons[CONS(f->qid)]; 988 if(!userok(f->user, c->name)){ 989 fsreply(fs, r, Eperm); 990 return; 991 } 992 f->c = c; 993 break; 994 case Qstat: 995 c = fs->cons[CONS(f->qid)]; 996 if(!userok(f->user, c->name)){ 997 fsreply(fs, r, Eperm); 998 return; 999 } 1000 f->c = c; 1001 break; 1002 } 1003 1004 f->open = 1; 1005 r->f.iounit = messagesize-IOHDRSZ; 1006 r->f.qid = f->qid; 1007 fsreply(fs, r, nil); 1008 } 1009 1010 void 1011 fscreate(Fs *fs, Request *r, Fid*) 1012 { 1013 fsreply(fs, r, Eperm); 1014 } 1015 1016 void 1017 fsread(Fs *fs, Request *r, Fid *f) 1018 { 1019 uchar *p, *e; 1020 int i, m, off; 1021 vlong offset; 1022 Dir d; 1023 char sbuf[ERRMAX]; 1024 1025 if(f->attached == 0){ 1026 fsreply(fs, r, Enofid); 1027 return; 1028 } 1029 1030 if((int)r->f.count < 0){ 1031 fsreply(fs, r, Ebadcount); 1032 return; 1033 } 1034 1035 if(QTDIR & f->qid.type){ 1036 p = r->buf + IOHDRSZ; 1037 e = p + r->f.count; 1038 offset = r->f.offset; 1039 off = 0; 1040 for(i=0; p<e; i++, off+=m){ 1041 m = fsdirgen(fs, f->qid, i, &d, p, e-p); 1042 if(m < 0) 1043 break; 1044 if(m > BIT16SZ && off >= offset) 1045 p += m; 1046 } 1047 r->f.data = (char*)r->buf + IOHDRSZ; 1048 r->f.count = (char*)p - r->f.data; 1049 } else { 1050 switch(TYPE(f->qid)){ 1051 case Qdata: 1052 addreq(&f->r, r); 1053 fskick(fs, f); 1054 return; 1055 case Qctl: 1056 r->f.data = (char*)r->buf+IOHDRSZ; 1057 r->f.count = 0; 1058 break; 1059 case Qstat: 1060 if(r->f.count > sizeof(sbuf)) 1061 r->f.count = sizeof(sbuf); 1062 i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset); 1063 if(i < 0){ 1064 errstr(sbuf, sizeof sbuf); 1065 fsreply(fs, r, sbuf); 1066 return; 1067 } 1068 r->f.data = sbuf; 1069 r->f.count = i; 1070 break; 1071 default: 1072 fsreply(fs, r, Eexist); 1073 return; 1074 } 1075 } 1076 fsreply(fs, r, nil); 1077 } 1078 1079 void 1080 fswrite(Fs *fs, Request *r, Fid *f) 1081 { 1082 int i, eol = 0; 1083 1084 if(f->attached == 0){ 1085 fsreply(fs, r, Enofid); 1086 return; 1087 } 1088 1089 if((int)r->f.count < 0){ 1090 fsreply(fs, r, Ebadcount); 1091 return; 1092 } 1093 1094 if(QTDIR & f->qid.type){ 1095 fsreply(fs, r, Eperm); 1096 return; 1097 } 1098 1099 switch(TYPE(f->qid)){ 1100 default: 1101 fsreply(fs, r, Eperm); 1102 return; 1103 case Qctl: 1104 write(f->c->cfd, r->f.data, r->f.count); 1105 break; 1106 case Qdata: 1107 for(i = 0; i < r->f.count; i++){ 1108 if(r->f.data[i] == '\n'){ 1109 if(f->c->chat && f->used) 1110 eol = 1; 1111 if(f->c->cronly) 1112 r->f.data[i] = '\r'; 1113 } 1114 else 1115 f->used = 1; 1116 } 1117 if(f->c->chat){ 1118 fskick(fs, f); 1119 if(!f->used) 1120 break; 1121 1122 if(f->bufn + r->f.count > Bufsize){ 1123 r->f.count -= (f->bufn + r->f.count) % Bufsize; 1124 eol = 1; 1125 } 1126 strncat(f->mbuf, r->f.data, r->f.count); 1127 f->bufn += r->f.count; 1128 if(eol){ 1129 bcastmsg(fs, f->c, f->mbuf, f->bufn); 1130 sprint(f->mbuf, "[%s] ", f->user); 1131 f->bufn = strlen(f->mbuf); 1132 f->used = 0; 1133 } 1134 } 1135 else 1136 write(f->c->fd, r->f.data, r->f.count); 1137 break; 1138 } 1139 fsreply(fs, r, nil); 1140 } 1141 1142 void 1143 fsclunk(Fs *fs, Request *r, Fid *f) 1144 { 1145 Fid **l, *fl; 1146 Request *nr; 1147 1148 if(f->open && TYPE(f->qid) == Qdata){ 1149 while((nr = remreq(&f->r)) != nil){ 1150 fsputfid(fs, f); 1151 free(nr); 1152 } 1153 1154 lock(f->c); 1155 for(l = &f->c->flist; *l; l = &fl->cnext){ 1156 fl = *l; 1157 if(fl == f){ 1158 *l = fl->cnext; 1159 break; 1160 } 1161 } 1162 bcastmembers(fs, f->c, "-", f); 1163 if(f->c->ondemand && f->c->flist == nil) 1164 fsreopen(fs, f->c); 1165 unlock(f->c); 1166 } 1167 fsreply(fs, r, nil); 1168 fsputfid(fs, f); 1169 } 1170 1171 void 1172 fsremove(Fs *fs, Request *r, Fid*) 1173 { 1174 fsreply(fs, r, Eperm); 1175 } 1176 1177 void 1178 fsstat(Fs *fs, Request *r, Fid *f) 1179 { 1180 int i, n; 1181 Qid q; 1182 Dir d; 1183 1184 q = parentqid(f->qid); 1185 for(i = 0; ; i++){ 1186 r->f.stat = r->buf+IOHDRSZ; 1187 n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ); 1188 if(n < 0){ 1189 fsreply(fs, r, Eexist); 1190 return; 1191 } 1192 r->f.nstat = n; 1193 if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path) 1194 break; 1195 } 1196 fsreply(fs, r, nil); 1197 } 1198 1199 void 1200 fswstat(Fs *fs, Request *r, Fid*) 1201 { 1202 fsreply(fs, r, Eperm); 1203 } 1204 1205 void 1206 fsreply(Fs *fs, Request *r, char *err) 1207 { 1208 int n; 1209 uchar buf[8192+IOHDRSZ]; 1210 1211 if(err){ 1212 r->f.type = Rerror; 1213 r->f.ename = err; 1214 } 1215 n = convS2M(&r->f, buf, messagesize); 1216 if(debug) 1217 fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n); 1218 fsputfid(fs, r->fid); 1219 if(write(fs->fd, buf, n) != n) 1220 fatal("unmounted"); 1221 free(r); 1222 } 1223 1224 /* 1225 * called whenever input or a read request has been received 1226 */ 1227 void 1228 fskick(Fs *fs, Fid *f) 1229 { 1230 Request *r; 1231 char *p, *rp, *wp, *ep; 1232 int i; 1233 1234 lock(f); 1235 while(f->rp != f->wp){ 1236 r = remreq(&f->r); 1237 if(r == nil) 1238 break; 1239 p = (char*)r->buf; 1240 rp = f->rp; 1241 wp = f->wp; 1242 ep = &f->buf[Bufsize]; 1243 for(i = 0; i < r->f.count && rp != wp; i++){ 1244 *p++ = *rp++; 1245 if(rp >= ep) 1246 rp = f->buf; 1247 } 1248 f->rp = rp; 1249 r->f.data = (char*)r->buf; 1250 r->f.count = p - (char*)r->buf; 1251 fsreply(fs, r, nil); 1252 } 1253 unlock(f); 1254 } 1255 1256 void 1257 usage(void) 1258 { 1259 fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n"); 1260 threadexitsall("usage"); 1261 } 1262 1263 void 1264 threadmain(int argc, char **argv) 1265 { 1266 rfork(RFNOTEG); 1267 fmtinstall('F', fcallfmt); 1268 1269 ARGBEGIN{ 1270 case 'd': 1271 debug++; 1272 break; 1273 case 'c': 1274 consoledb = ARGF(); 1275 if(consoledb == nil) 1276 usage(); 1277 break; 1278 case 'm': 1279 mntpt = ARGF(); 1280 if(mntpt == nil) 1281 usage(); 1282 break; 1283 }ARGEND; 1284 1285 db = ndbopen(consoledb); 1286 if(db == nil) 1287 fatal("can't open %s: %r", consoledb); 1288 1289 fsmount(mntpt); 1290 } 1291