11 * This fs presents a 1 level file system. It contains
12 * up to three files per console (xxx and xxxctl and xxxstat)
15 typedef struct Console Console;
16 typedef struct Aux Aux;
17 typedef struct Request Request;
18 typedef struct Reqlist Reqlist;
23 /* last 5 bits of qid.path */
24 Textern= 0, /* fake parent of top level */
25 Ttopdir, /* top level directory */
30 Bufsize= 32*1024, /* chars buffered per reader */
31 Maxcons= 64, /* maximum consoles */
32 Nhash= 64, /* Aux hash buckets */
35 #define TYPE(x) (((ulong)x.path) & 0xf)
36 #define CONS(x) ((((ulong)x.path) >> 4)&0xfff)
37 #define QID(c, x) (((c)<<4) | (x))
58 Aux *next; /* hash list */
59 Aux *cnext; /* list of Aux's on a console */
66 char mbuf[Bufsize]; /* message */
77 Reqlist r; /* active read requests */
88 int ondemand; /* open only on demand */
89 int chat; /* chat consoles are special */
91 int pid; /* pid of reader */
97 Aux *flist; /* open fids to broadcast to */
104 int fd; /* to kernel mount point */
107 Console *cons[Maxcons];
111 extern void console(Fs*, char*, char*, int, int, int);
112 extern Fs* fsmount(char*);
114 extern void fsreader(void*);
115 extern void fsrun(void*);
116 extern Aux* fsgetfid(Fs*, int);
117 extern void fsputfid(Fs*, Aux*);
118 extern int fsdirgen(Fs*, Qid, int, Dir*, uchar*, int);
119 extern void fsreply(Fs*, Request*, char*);
120 extern void fskick(Fs*, Aux*);
121 extern int fsreopen(Fs*, Console*);
123 extern void fsversion(Fs*, Request*, Aux*);
124 extern void fsflush(Fs*, Request*, Aux*);
125 extern void fsauth(Fs*, Request*, Aux*);
126 extern void fsattach(Fs*, Request*, Aux*);
127 extern void fswalk(Fs*, Request*, Aux*);
128 extern void fsclwalk(Fs*, Request*, Aux*);
129 extern void fsopen(Fs*, Request*, Aux*);
130 extern void fscreate(Fs*, Request*, Aux*);
131 extern void fsread(Fs*, Request*, Aux*);
132 extern void fswrite(Fs*, Request*, Aux*);
133 extern void fsclunk(Fs*, Request*, Aux*);
134 extern void fsremove(Fs*, Request*, Aux*);
135 extern void fsstat(Fs*, Request*, Aux*);
136 extern void fswstat(Fs*, Request*, Aux*);
139 void (*fcall[])(Fs*, Request*, Aux*) =
142 [Tversion] fsversion,
156 char Eperm[] = "permission denied";
157 char Eexist[] = "file does not exist";
158 char Enotdir[] = "not a directory";
159 char Eisopen[] = "file already open";
160 char Ebadcount[] = "bad read/write count";
161 char Enofid[] = "no such fid";
163 char *consoledb = "/lib/ndb/consoledb";
164 char *mntpt = "/mnt/consoles";
166 int messagesize = 8192+IOHDRSZ;
169 fatal(char *fmt, ...)
174 write(2, "consolefs: ", 10);
176 vseprint(buf, buf+1024, fmt, arg);
178 write(2, buf, strlen(buf));
191 fatal("malloc failed: %r");
200 * any request that can get queued for a delayed reply
203 alloccreq(Fs *fs, int bufsize)
207 r = emalloc(sizeof(Request)+bufsize);
214 * for maintaining lists of requests
217 addreq(Reqlist *l, Request *r)
230 * remove the first request from a list of requests
246 * remove a request with the given tag from a list of requests
249 remtag(Reqlist *l, int tag)
255 for(or = *ll; or; or = or->next){
256 if(or->f.tag == tag){
271 return (Qid){QID(0, Textern), 0, QTDIR};
273 return (Qid){QID(0, Ttopdir), 0, QTDIR};
277 fsdirgen(Fs *fs, Qid parent, int i, Dir *d, uchar *buf, int nbuf)
279 static char name[64];
283 d->uid = d->gid = d->muid = "network";
285 d->atime = time(nil);
290 switch(TYPE(parent)){
295 d->mode = DMDIR|0555;
297 d->qid.path = QID(0, Ttopdir);
302 if(xcons >= fs->ncons)
304 p = fs->cons[xcons]->name;
307 if(fs->cons[xcons]->cfd < 0)
309 snprint(name, sizeof name, "%sctl", p);
311 d->qid.type = QTFILE;
312 d->qid.path = QID(xcons, Qctl);
316 if(fs->cons[xcons]->sfd < 0)
318 snprint(name, sizeof name, "%sstat", p);
320 d->qid.type = QTFILE;
321 d->qid.path = QID(xcons, Qstat);
325 d->qid.type = QTFILE;
326 d->qid.path = QID(xcons, Qdata);
337 return convD2M(d, buf, nbuf);
342 * mount the user interface and start a request processor
353 fs = emalloc(sizeof(Fs));
356 fatal("opening pipe: %r");
358 /* start up the file system process */
361 proccreate(fsrun, v, 16*1024);
363 if(postfd("consoles", pfd[1]) < 0)
364 sysfatal("post: %r");
368 dirwstat("/srv/consoles", &d);
371 if(amount(pfd[1], mntpt, MBEFORE, "") == -1)
372 sysfatal("mount %s: %r", mntpt);
383 fsreopen(Fs* fs, Console *c)
389 if(postnote(PNPROC, c->pid, "reopen") != 0)
390 fprint(2, "postnote failed: %r\n");
403 if(c->flist == nil && c->ondemand)
406 c->fd = open(c->dev, ORDWR);
410 snprint(buf, sizeof(buf), "%sctl", c->dev);
411 c->cfd = open(buf, ORDWR);
412 fprint(c->cfd, "b%d", c->speed);
414 snprint(buf, sizeof(buf), "%sstat", c->dev);
415 c->sfd = open(buf, OREAD);
419 proccreate(fsreader, v, 16*1024);
425 change(Fs *fs, Console *c, int doreopen, int speed, int cronly, int ondemand)
429 if(speed != c->speed){
433 if(ondemand != c->ondemand){
434 c->ondemand = ondemand;
445 * create a console interface
448 console(Fs* fs, char *name, char *dev, int speed, int cronly, int ondemand)
454 if(fs->ncons >= Maxcons)
455 fatal("too many consoles, too little time");
458 for(i = 0; i < fs->ncons; i++){
460 if(strcmp(name, c->name) == 0){
461 if(strcmp(dev, c->dev) != 0){
464 c->dev = strdup(dev);
468 change(fs, c, doreopen, speed, cronly, ondemand);
472 #ifdef sapedoesntlikethis
474 * The code below prevents this from working. I can't
475 * think of scenarios where the code below actually helps
478 * console=borneo dev=/dev/eia1
481 * console=tottie dev=/dev/eia1
485 for(i = 0; i < fs->ncons; i++){
487 if(strcmp(dev, c->dev) == 0){
488 /* at least a rename */
490 c->name = strdup(name);
492 change(fs, c, doreopen, speed, cronly, ondemand);
497 c = emalloc(sizeof(Console));
498 fs->cons[fs->ncons] = c;
500 c->name = strdup(name);
501 c->dev = strdup(dev);
502 if(strcmp(c->dev, "/dev/null") == 0)
509 change(fs, c, 1, speed, cronly, ondemand);
513 * buffer data from console to a client.
514 * circular q with writer able to catch up to reader.
515 * the reader may miss data but always sees an in order sequence.
518 fromconsole(Aux *f, char *p, int n)
526 ep = f->buf + sizeof(f->buf);
537 /* we overtook the read pointer, push it up so readers always
538 * see the tail of what was written
551 * broadcast a list of members to all listeners
554 bcastmembers(Fs *fs, Console *c, char *msg, Aux *f)
560 sprint(buf, "[%s%s", msg, f->user);
561 for(fl = c->flist; fl != nil && strlen(buf) + 64 < sizeof(buf); fl = fl->cnext){
565 strcat(buf, fl->user);
570 for(fl = c->flist; fl; fl = fl->cnext){
571 fromconsole(fl, buf, n);
577 handler(void*, char *msg)
579 if(strstr(msg, "reopen") != nil ||
580 strstr(msg, "write on closed pipe") != nil)
586 * a process to read console output and broadcast it (one per console)
606 n = read(c->fd, buf, sizeof(buf));
610 for(fl = c->flist; fl; fl = fl->cnext){
611 fromconsole(fl, buf, n);
623 int cronly, speed, ondemand;
627 /* start a listener for each console */
637 for(nt = t; nt; nt = nt->entry){
638 if(strcmp(nt->attr, "console") == 0)
640 else if(strcmp(nt->attr, "dev") == 0)
642 else if(strcmp(nt->attr, "speed") == 0)
643 speed = atoi(nt->val);
644 else if(strcmp(nt->attr, "cronly") == 0)
646 else if(strcmp(nt->attr, "openondemand") == 0)
649 if(dev != nil && cons != nil)
650 console(fs, cons, dev, speed, cronly, ondemand);
656 * a request processor (one per Fs)
676 r = alloccreq(fs, messagesize);
677 while((n = read9pmsg(fs->fd, r->buf, messagesize)) == 0)
682 if(convM2S(r->buf, n, &r->f) == 0){
683 fprint(2, "can't convert %ux %ux %ux\n", r->buf[0],
684 r->buf[1], r->buf[2]);
690 f = fsgetfid(fs, r->f.fid);
693 fprint(2, "%F path %llux\n", &r->f, f->qid.path);
697 (*fcall[t])(fs, r, f);
702 fsgetfid(Fs *fs, int fid)
707 for(f = fs->hash[fid%Nhash]; f; f = f->next){
715 nf = emalloc(sizeof(Aux));
716 nf->next = fs->hash[fid%Nhash];
717 fs->hash[fid%Nhash] = nf;
727 fsputfid(Fs *fs, Aux *f)
736 for(l = &fs->hash[f->fid%Nhash]; nf = *l; l = &nf->next)
747 fsauth(Fs *fs, Request *r, Aux*)
749 fsreply(fs, r, "consolefs: authentication not required");
753 fsversion(Fs *fs, Request *r, Aux*)
756 if(r->f.msize < 256){
757 fsreply(fs, r, "message size too small");
760 messagesize = r->f.msize;
761 if(messagesize > 8192+IOHDRSZ)
762 messagesize = 8192+IOHDRSZ;
763 r->f.msize = messagesize;
764 if(strncmp(r->f.version, "9P2000", 6) != 0){
765 fsreply(fs, r, "unrecognized 9P version");
768 r->f.version = "9P2000";
774 fsflush(Fs *fs, Request *r, Aux *f)
778 or = remtag(&f->r, r->f.oldtag);
780 fsputfid(fs, or->fid);
790 fsattach(Fs *fs, Request *r, Aux *f)
793 f->qid.path = QID(0, Ttopdir);
797 f->user = strdup(r->f.uname);
799 f->user = strdup("none");
801 /* hold down the fid till the clunk */
812 fswalk(Fs *fs, Request *r, Aux *f)
816 int i, n, nqid, nwname;
817 Qid qid, wqid[MAXWELEM];
821 if(f->attached == 0){
822 fsreply(fs, r, Enofid);
827 if(r->f.fid != r->f.newfid){
828 nf = fsgetfid(fs, r->f.newfid);
829 nf->attached = f->attached;
832 nf->user = strdup(f->user);
841 nwname = r->f.nwname;
844 for(; err == nil && nqid < nwname; nqid++){
845 if(nqid >= MAXWELEM){
846 err = "too many name elements";
849 name = r->f.wname[nqid];
850 if(strcmp(name, "..") == 0)
851 qid = parentqid(qid);
852 else if(strcmp(name, ".") != 0){
854 n = fsdirgen(fs, qid, i, &d, nil, 0);
859 if(n > 0 && strcmp(name, d.name) == 0){
867 if(nf != nil && nqid < nwname)
873 memmove(r->f.wqid, wqid, nqid*sizeof(Qid));
879 ingroup(char *user, char *group)
884 t = ndbsearch(db, &s, "group", group);
887 for(nt = t; nt; nt = nt->entry){
888 if(strcmp(nt->attr, "uid") == 0)
889 if(strcmp(nt->val, user) == 0)
897 userok(char *u, char *cname)
902 t = ndbsearch(db, &s, "console", cname);
906 for(nt = t; nt; nt = nt->entry){
907 if(strcmp(nt->attr, "uid") == 0)
908 if(strcmp(nt->val, u) == 0)
910 if(strcmp(nt->attr, "gid") == 0)
911 if(ingroup(u, nt->val))
926 * broadcast a message to all listeners
929 bcastmsg(Fs *fs, Console *c, char *msg, int n)
933 for(fl = c->flist; fl; fl = fl->cnext){
934 fromconsole(fl, msg, n);
940 fsopen(Fs *fs, Request *r, Aux *f)
945 if(f->attached == 0){
946 fsreply(fs, r, Enofid);
951 fsreply(fs, r, Eisopen);
955 mode = r->f.mode & 3;
957 if((QTDIR & f->qid.type) && mode != OREAD){
958 fsreply(fs, r, Eperm);
962 switch(TYPE(f->qid)){
964 c = fs->cons[CONS(f->qid)];
965 if(!userok(f->user, c->name)){
966 fsreply(fs, r, Eperm);
973 sprint(f->mbuf, "[%s] ", f->user);
974 f->bufn = strlen(f->mbuf);
978 bcastmembers(fs, c, "+", f);
984 c = fs->cons[CONS(f->qid)];
985 if(!userok(f->user, c->name)){
986 fsreply(fs, r, Eperm);
992 c = fs->cons[CONS(f->qid)];
993 if(!userok(f->user, c->name)){
994 fsreply(fs, r, Eperm);
1002 r->f.iounit = messagesize-IOHDRSZ;
1004 fsreply(fs, r, nil);
1008 fscreate(Fs *fs, Request *r, Aux*)
1010 fsreply(fs, r, Eperm);
1014 fsread(Fs *fs, Request *r, Aux *f)
1022 if(f->attached == 0){
1023 fsreply(fs, r, Enofid);
1027 if((int)r->f.count < 0){
1028 fsreply(fs, r, Ebadcount);
1032 if(QTDIR & f->qid.type){
1033 p = r->buf + IOHDRSZ;
1035 offset = r->f.offset;
1037 for(i=0; p<e; i++, off+=m){
1038 m = fsdirgen(fs, f->qid, i, &d, p, e-p);
1041 if(m > BIT16SZ && off >= offset)
1044 r->f.data = (char*)r->buf + IOHDRSZ;
1045 r->f.count = (char*)p - r->f.data;
1047 switch(TYPE(f->qid)){
1053 r->f.data = (char*)r->buf+IOHDRSZ;
1057 if(r->f.count > sizeof(sbuf))
1058 r->f.count = sizeof(sbuf);
1059 i = pread(f->c->sfd, sbuf, r->f.count, r->f.offset);
1061 errstr(sbuf, sizeof sbuf);
1062 fsreply(fs, r, sbuf);
1069 fsreply(fs, r, Eexist);
1073 fsreply(fs, r, nil);
1077 fswrite(Fs *fs, Request *r, Aux *f)
1081 if(f->attached == 0){
1082 fsreply(fs, r, Enofid);
1086 if((int)r->f.count < 0){
1087 fsreply(fs, r, Ebadcount);
1091 if(QTDIR & f->qid.type){
1092 fsreply(fs, r, Eperm);
1096 switch(TYPE(f->qid)){
1098 fsreply(fs, r, Eperm);
1101 write(f->c->cfd, r->f.data, r->f.count);
1104 for(i = 0; i < r->f.count; i++){
1105 if(r->f.data[i] == '\n'){
1106 if(f->c->chat && f->used)
1109 r->f.data[i] = '\r';
1119 if(f->bufn + r->f.count > Bufsize){
1120 r->f.count -= (f->bufn + r->f.count) % Bufsize;
1123 strncat(f->mbuf, r->f.data, r->f.count);
1124 f->bufn += r->f.count;
1126 bcastmsg(fs, f->c, f->mbuf, f->bufn);
1127 sprint(f->mbuf, "[%s] ", f->user);
1128 f->bufn = strlen(f->mbuf);
1133 write(f->c->fd, r->f.data, r->f.count);
1136 fsreply(fs, r, nil);
1140 fsclunk(Fs *fs, Request *r, Aux *f)
1145 if(f->open && TYPE(f->qid) == Qdata){
1146 while((nr = remreq(&f->r)) != nil){
1152 for(l = &f->c->flist; *l; l = &fl->cnext){
1159 bcastmembers(fs, f->c, "-", f);
1160 if(f->c->ondemand && f->c->flist == nil)
1164 fsreply(fs, r, nil);
1169 fsremove(Fs *fs, Request *r, Aux*)
1171 fsreply(fs, r, Eperm);
1175 fsstat(Fs *fs, Request *r, Aux *f)
1181 q = parentqid(f->qid);
1183 r->f.stat = r->buf+IOHDRSZ;
1184 n = fsdirgen(fs, q, i, &d, r->f.stat, messagesize-IOHDRSZ);
1186 fsreply(fs, r, Eexist);
1190 if(r->f.nstat > BIT16SZ && d.qid.path == f->qid.path)
1193 fsreply(fs, r, nil);
1197 fswstat(Fs *fs, Request *r, Aux*)
1199 fsreply(fs, r, Eperm);
1203 fsreply(Fs *fs, Request *r, char *err)
1206 uchar buf[8192+IOHDRSZ];
1212 n = convS2M(&r->f, buf, messagesize);
1214 fprint(2, "%F path %llux n=%d\n", &r->f, r->fid->qid.path, n);
1215 fsputfid(fs, r->fid);
1216 if(write(fs->fd, buf, n) != n)
1222 * called whenever input or a read request has been received
1225 fskick(Fs *fs, Aux *f)
1228 char *p, *rp, *wp, *ep;
1232 while(f->rp != f->wp){
1239 ep = &f->buf[Bufsize];
1240 for(i = 0; i < r->f.count && rp != wp; i++){
1246 r->f.data = (char*)r->buf;
1247 r->f.count = p - (char*)r->buf;
1248 fsreply(fs, r, nil);
1256 fprint(2, "usage: consolefs [-d] [-m mount-point] [-c console-db]\n");
1257 threadexitsall("usage");
1261 threadmain(int argc, char **argv)
1263 fmtinstall('F', fcallfmt);
1271 if(consoledb == nil)
1281 db = ndbopen(consoledb);
1283 fatal("can't open %s: %r", consoledb);