📄 telco.c
字号:
#include <u.h>#include <libc.h>#include <auth.h>#include <fcall.h>#define LOGFILE "telco"/* * Rather than reading /adm/users, which is a lot of work for * a toy progdev, we assume all groups have the form * NNN:user:user: * meaning that each user is the leader of his own group. */enum{ OPERM = 0x3, /* mask of all permission types in open mode */ Ndev = 8, Nreq = (Ndev*3)/2, Nrbuf = 32*1024,};typedef struct Fid Fid;typedef struct Dev Dev;typedef struct Request Request;typedef struct Type Type;struct Fid{ Qid qid; short busy; short open; int fid; Fid *next; char *user;};struct Request{ Request *next; Fid *fid; ulong tag; int count; int flushed;};struct Dev{ Lock; /* device state */ int ctl; /* control fd */ int data; /* data fd */ char *path; /* to device */ Type *t; Type *baset; int speed; int fclass; /* fs emulation */ int open; long perm; char *name; char *user; char msgbuf[128]; Request *r; Request *rlast; /* input reader */ int monitoring; /* monitor pid */ char rbuf[Nrbuf]; char *rp; char *wp; long pid;};enum{ Devmask= (Ndev-1)<<8, Qlvl1= 0, Qlvl2= 1, Qclone= 2, Qlvl3= 3, Qdata= 4, Qctl= 5, Pexec = 1, Pwrite = 2, Pread = 4, Pother = 1, Pgroup = 8, Powner = 64,};char *names[] ={[Qlvl1] "/",[Qlvl2] "telco",[Qclone] "clone",[Qlvl3] "",[Qdata] "data",[Qctl] "ctl",};#define DEV(q) ((((ulong)(q).path)&Devmask)>>8)#define TYPE(q) (((ulong)(q).path)&((1<<8)-1))#define MKQID(t, i) ((((i)<<8)&Devmask) | (t))enum{ /* * modem specific commands */ Cerrorcorrection = 0, /* error correction */ Ccompression, /* compression */ Cflowctl, /* CTS/RTS */ Crateadjust, /* follow line speed */ Cfclass2, /* set up for fax */ Cfclass0, /* set up for data */ Ncommand,};struct Type{ char *name; char *ident; /* inquire request */ char *response; /* inquire response (strstr) */ char *basetype; /* name of base type */ char *commands[Ncommand];};/* * Fax setup summary * * FCLASS=2 - set to service class 2, i.e., one where the fax handles timing * FTBC=0 - ??? * FREL=1 - ??? * FCQ=1 - receive copy quality checking enabled * FBOR=1 - set reversed bit order for phase C data * FCR=1 - the DCE can receive message data, bit 10 in the DIS or * DTC frame will be set * FDIS=,3 - limit com speed to 9600 baud */Type typetab[] ={ { "Rockwell", 0, 0, 0, "AT\\N7", /* auto reliable (V.42, fall back to MNP, to none) */ "AT%C1\\J0", /* negotiate for compression, don't change port baud rate */ "AT\\Q3", /* CTS/RTS flow control */ "AT\\J1", "AT+FCLASS=2\rAT+FCR=1\r", "AT+FCLASS=0", }, { "ATT2400", "ATI9", "E2400", "Rockwell", "AT\\N3", /* auto reliable (MNP, fall back to none) */ 0, 0, 0, 0, 0, }, { "ATT14400", "ATI9", "E14400", "Rockwell", 0, 0, 0, 0, 0, 0, }, { "MT1432", "ATI2", "MT1432", 0, "AT&E1", /* auto reliable (V.42, fall back to none) */ "AT&E15$BA0", /* negotiate for compression */ "AT&E4", /* CTS/RTS flow control */ "AT$BA1", /* don't change port baud rate */ "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1\rAT+FDIS=,3", "AT+FCLASS=0", }, { "MT2834", "ATI2", "MT2834", "MT1432", 0, 0, 0, 0, "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1", 0, }, { "VOCAL", "ATI6", "144DPL+FAX", "Rockwell", "AT\\N3", /* auto reliable (V.42, fall back to MNP, fall back to none) */ "AT%C3\\J0", /* negotiate for compression, don't change port baud rate */ 0, 0, "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1", "AT+FCLASS=0", }, { 0, },};/* * modem return codes */enum{ Ok, Success, Failure, Noise, Found,};/* * modem return messages */typedef struct Msg Msg;struct Msg{ char *text; int type;};Msg msgs[] ={ { "OK", Ok, }, { "NO CARRIER", Failure, }, { "ERROR", Failure, }, { "NO DIALTONE", Failure, }, { "BUSY", Failure, }, { "NO ANSWER", Failure, }, { "CONNECT", Success, }, { 0, 0 },};Fid *fids;Dev *dev;int ndev;int mfd[2];char *user;uchar mdata[8192+IOHDRSZ];int messagesize = sizeof mdata;Fcall thdr;Fcall rhdr;char errbuf[ERRMAX];uchar statbuf[STATMAX];int pulsed;int verbose;int maxspeed = 56000;char *srcid = "plan9";int answer = 1;Fid *newfid(int);int devstat(Dir*, uchar*, int);int devgen(Qid, int, Dir*, uchar*, int);void error(char*);void io(void);void *erealloc(void*, ulong);void *emalloc(ulong);void usage(void);int perm(Fid*, Dev*, int);void setspeed(Dev*, int);int getspeed(char*, int);char *dialout(Dev*, char*);void onhook(Dev*);int readmsg(Dev*, int, char*);void monitor(Dev*);int getinput(Dev*, char*, int);void serve(Dev*);void receiver(Dev*);char* modemtype(Dev*, int, int);char *rflush(Fid*), *rversion(Fid*), *rattach(Fid*), *rauth(Fid*), *rwalk(Fid*), *ropen(Fid*), *rcreate(Fid*), *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);char *(*fcalls[])(Fid*) = { [Tflush] rflush, [Tversion] rversion, [Tattach] rattach, [Tauth] rauth, [Twalk] rwalk, [Topen] ropen, [Tcreate] rcreate, [Tread] rread, [Twrite] rwrite, [Tclunk] rclunk, [Tremove] rremove, [Tstat] rstat, [Twstat] rwstat,};char Eperm[] = "permission denied";char Enotdir[] = "not a directory";char Enotexist[] = "file does not exist";char Ebadaddr[] = "bad address";char Eattn[] = "can't get modem's attention";char Edial[] = "can't dial";char Enoauth[] = "telco: authentication not required";char Eisopen[] = "file already open for I/O";char Enodev[] = "no free modems";char Enostream[] = "stream closed prematurely";voidusage(void){ fprint(2, "usage: %s [-vp] [-i srcid] dev ...\n", argv0); exits("usage");}voidnotifyf(void *a, char *s){ USED(a); if(strncmp(s, "interrupt", 9) == 0) noted(NCONT); noted(NDFLT);}voidmain(int argc, char *argv[]){ int p[2]; int fd; char buf[10]; Dev *d; ARGBEGIN{ case 'p': pulsed = 1; break; case 'v': verbose = 1; break; case 'i': srcid = ARGF(); break; case 's': maxspeed = atoi(ARGF()); break; case 'n': answer = 0; break; default: usage(); }ARGEND if(argc == 0) usage(); if(argc > Ndev) argc = Ndev; if(pipe(p) < 0) error("pipe failed"); notify(notifyf); fmtinstall('F', fcallfmt); user = getuser(); switch(rfork(RFFDG|RFPROC|RFREND|RFNOTEG)){ case -1: error("fork"); case 0: close(p[1]); mfd[0] = mfd[1] = p[0]; break; default: close(p[0]); fd = create("/srv/telco", OWRITE, 0666); if(fd < 0) error("create of /srv/telco failed"); sprint(buf, "%d", p[1]); if(write(fd, buf, strlen(buf)) < 0) error("writing /srv/telco"); close(fd); if(mount(p[1], -1, "/net", MBEFORE, "") < 0) error("mount failed"); exits(0); } dev = mallocz(argc*sizeof(Dev), 1); for(ndev = 0; ndev < argc; ndev++){ d = &dev[ndev]; d->path = argv[ndev]; d->rp = d->wp = d->rbuf; monitor(d); d->open++; onhook(d); d->open--; } io();}/* * generate a stat structure for a qid */intdevstat(Dir *dir, uchar *buf, int nbuf){ Dev *d; int t; static char tmp[10][32]; static int ntmp; t = TYPE(dir->qid); if(t != Qlvl3) dir->name = names[t]; else{ dir->name = tmp[ntmp % nelem(tmp)]; sprint(dir->name, "%lud", DEV(dir->qid)); ntmp++; } dir->mode = 0755; dir->uid = user; dir->gid = user; dir->muid = user; if(t >= Qlvl3){ d = &dev[DEV(dir->qid)]; if(d->open){ dir->mode = d->perm; dir->uid = d->user; } } if(dir->qid.type & QTDIR) dir->mode |= DMDIR; if(t == Qdata){ d = &dev[DEV(dir->qid)]; dir->length = d->wp - d->rp; if(dir->length < 0) dir->length += Nrbuf; } else dir->length = 0; dir->atime = time(0); dir->mtime = dir->atime; if(buf) return convD2M(dir, buf, nbuf); return 0;}/* * enumerate file's we can walk to from q */intdevgen(Qid q, int i, Dir *d, uchar *buf, int nbuf){ static ulong v; d->qid.vers = v++; switch(TYPE(q)){ case Qlvl1: if(i != 0) return -1; d->qid.type = QTDIR; d->qid.path = Qlvl2; break; case Qlvl2: switch(i){ case -1: d->qid.type = QTDIR; d->qid.path = Qlvl1; break; case 0: d->qid.type = QTFILE; d->qid.path = Qclone; break; default: if(i > ndev) return -1; d->qid.type = QTDIR; d->qid.path = MKQID(Qlvl3, i-1); break; } break; case Qlvl3: switch(i){ case -1: d->qid.type = QTDIR; d->qid.path = Qlvl2; break; case 0: d->qid.type = QTFILE; d->qid.path = MKQID(Qdata, DEV(q)); break; case 1: d->qid.type = QTFILE; d->qid.path = MKQID(Qctl, DEV(q)); break; default: return -1; } break; default: return -1; } return devstat(d, buf, nbuf);}char*rversion(Fid *){ Fid *f; for(f = fids; f; f = f->next) if(f->busy) rclunk(f); if(thdr.msize < 256) return "version: message size too small"; messagesize = thdr.msize; if(messagesize > sizeof mdata) messagesize = sizeof mdata; rhdr.msize = messagesize; if(strncmp(thdr.version, "9P2000", 6) != 0) return "unrecognized 9P version"; rhdr.version = "9P2000"; return 0;}char*rflush(Fid *f){ Request *r, **l; Dev *d; USED(f); for(d = dev; d < &dev[ndev]; d++){ lock(d); for(l = &d->r; r = *l; l = &r->next) if(r->tag == thdr.oldtag){ *l = r->next; free(r); break; } unlock(d); } return 0;}char *rauth(Fid *f){ USED(f); return Enoauth;}char*rattach(Fid *f){ f->busy = 1; f->qid.type = QTDIR; f->qid.path = Qlvl1; f->qid.vers = 0; rhdr.qid = f->qid; if(thdr.uname[0]) f->user = strdup(thdr.uname); else f->user = "none"; return 0;}char*rwalk(Fid *f){ Fid *nf; int i, nqid; char *name, *err; Dir dir; Qid q; nf = nil; if(thdr.fid != thdr.newfid){ if(f->open) return Eisopen; if(f->busy == 0) return Enotexist; nf = newfid(thdr.newfid); nf->busy = 1; nf->open = 0; nf->qid = f->qid; nf->user = strdup(f->user); f = nf; /* walk f */ } err = nil; dir.qid = f->qid; nqid = 0; if(thdr.nwname > 0){ for(; nqid < thdr.nwname; nqid++) { if((dir.qid.type & QTDIR) == 0){ err = Enotdir; break; } name = thdr.wname[nqid]; if(strcmp(name, ".") == 0){ /* nothing to do */ }else if(strcmp(name, "..") == 0) { if(devgen(f->qid, -1, &dir, 0, 0) < 0) break; } else{ q = dir.qid; for(i = 0;; i++){ if(devgen(q, i, &dir, 0, 0) < 0) goto Out; if(strcmp(name, dir.name) == 0) break; } } rhdr.wqid[nqid] = dir.qid; } Out: if(nqid == 0 && err == nil) err = Enotexist; if(nf != nil && thdr.fid != thdr.newfid && nqid < thdr.nwname) rclunk(nf); } rhdr.nwqid = nqid; if(nqid > 0 && nqid == thdr.nwname) f->qid = dir.qid; return err;}char *ropen(Fid *f){ Dev *d; int mode, t; if(f->open) return Eisopen; mode = thdr.mode; mode &= OPERM; if(f->qid.type & QTDIR){ if(mode != OREAD) return Eperm; rhdr.qid = f->qid; return 0; } if(mode==OEXEC) return Eperm; t = TYPE(f->qid); if(t == Qclone){ for(d = dev; d < &dev[ndev]; d++) if(d->open == 0) break; if(d == &dev[ndev]) return Enodev; f->qid.path = MKQID(Qctl, d-dev); t = Qctl; } switch(t){ case Qdata: case Qctl: d = &dev[DEV(f->qid)]; if(d->open == 0){ d->user = strdup(f->user); d->perm = 0660; }else { if(mode==OWRITE || mode==ORDWR) if(!perm(f, d, Pwrite)) return Eperm; if(mode==OREAD || mode==ORDWR) if(!perm(f, d, Pread)) return Eperm; } d->open++; break; } rhdr.qid = f->qid; rhdr.iounit = messagesize - IOHDRSZ; f->open = 1; return 0;}char *rcreate(Fid *f){ USED(f); return Eperm;}/* * intercept a note */voidtakeanote(void *u, char *note){ USED(u); if(strstr(note, "flushed")) noted(NCONT); noted(NDFLT);}char*rread(Fid *f){ char *buf; long off, start; int i, m, n, cnt, t; Dir dir; char num[32]; Dev *d; Request *r; n = 0; rhdr.count = 0; off = thdr.offset; cnt = thdr.count; buf = rhdr.data; t = TYPE(f->qid); switch(t){ default: start = 0; for(i = 0; n < cnt; i++){ m = devgen(f->qid, i, &dir, (uchar*)buf+n, cnt-n); if(m <= BIT16SZ) break; if(start >= off) n += m; start += m; } break; case Qctl: i = sprint(num, "%lud", DEV(f->qid)); if(off < i){ n = cnt; if(off + n > i) n = i - off; memmove(buf, num + off, n); } else n = 0; break; case Qdata: d = &dev[DEV(f->qid)]; r = mallocz(sizeof(Request), 1); r->tag = thdr.tag; r->count = thdr.count; r->fid = f; r->flushed = 0; lock(d); if(d->r) d->rlast->next = r; else d->r = r; d->rlast = r; serve(d); unlock(d); return ""; } rhdr.count = n; return 0;}char *cmsg = "connect ";int clen;char*rwrite(Fid *f){ Dev *d; ulong off; int cnt; char *cp; char buf[64]; off = thdr.offset; cnt = thdr.count; switch(TYPE(f->qid)){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -