1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555 |
- #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";
- void
- usage(void)
- {
- fprint(2, "usage: %s [-vp] [-i srcid] dev ...\n", argv0);
- exits("usage");
- }
- void
- notifyf(void *a, char *s)
- {
- USED(a);
- if(strncmp(s, "interrupt", 9) == 0)
- noted(NCONT);
- noted(NDFLT);
- }
- void
- main(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
- */
- int
- devstat(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
- */
- int
- devgen(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
- */
- void
- takeanote(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)){
- default:
- return "file is a directory";
- case Qctl:
- d = &dev[DEV(f->qid)];
- clen = strlen(cmsg);
- if(cnt < clen || strncmp(thdr.data, cmsg, clen) != 0){
- /*
- * send control message to real control file
- */
- if(seek(d->ctl, off, 0) < 0 || write(d->ctl, thdr.data, cnt) < 0){
- errstr(errbuf, sizeof errbuf);
- return errbuf;
- }
- } else {
- /*
- * connect
- */
- cnt -= clen;
- if(cnt >= sizeof(buf))
- cnt = sizeof(buf) - 1;
- if(cnt < 0)
- return Ebadaddr;
- strncpy(buf, &thdr.data[clen], cnt);
- buf[cnt] = 0;
- cp = dialout(d, buf);
- if(cp)
- return cp;
- }
- rhdr.count = cnt;
- break;
- case Qdata:
- d = &dev[DEV(f->qid)];
- if(write(d->data, thdr.data, cnt) < 0){
- errstr(errbuf, sizeof errbuf);
- return errbuf;
- }
- rhdr.count = cnt;
- break;
- }
- return 0;
- }
- char *
- rclunk(Fid *f)
- {
- Dev *d;
- if(f->open)
- switch(TYPE(f->qid)){
- case Qdata:
- case Qctl:
- d = &dev[DEV(f->qid)];
- if(d->open == 1)
- onhook(d);
- d->open--;
- break;
- }
- free(f->user);
- f->busy = 0;
- f->open = 0;
- return 0;
- }
- char *
- rremove(Fid *f)
- {
- USED(f);
- return Eperm;
- }
- char *
- rstat(Fid *f)
- {
- Dir d;
- d.qid = f->qid;
- rhdr.stat = statbuf;
- rhdr.nstat = devstat(&d, statbuf, sizeof statbuf);
- return 0;
- }
- char *
- rwstat(Fid *f)
- {
- Dev *d;
- Dir dir;
- if(TYPE(f->qid) < Qlvl3)
- return Eperm;
- convM2D(thdr.stat, thdr.nstat, &dir, rhdr.data); /* rhdr.data is a known place to scribble */
- d = &dev[DEV(f->qid)];
- /*
- * To change mode, must be owner
- */
- if(d->perm != dir.mode){
- if(strcmp(f->user, d->user) != 0)
- if(strcmp(f->user, user) != 0)
- return Eperm;
- }
- /* all ok; do it */
- d->perm = dir.mode & ~DMDIR;
- return 0;
- }
- Fid *
- newfid(int fid)
- {
- Fid *f, *ff;
- ff = 0;
- for(f = fids; f; f = f->next)
- if(f->fid == fid)
- return f;
- else if(!ff && !f->busy)
- ff = f;
- if(ff){
- ff->fid = fid;
- return ff;
- }
- f = emalloc(sizeof *f);
- f->fid = fid;
- f->next = fids;
- fids = f;
- return f;
- }
- /*
- * read fs requests and dispatch them
- */
- void
- io(void)
- {
- char *err;
- int n;
- for(;;){
- /*
- * reading from a pipe or a network device
- * will give an error after a few eof reads
- * however, we cannot tell the difference
- * between a zero-length read and an interrupt
- * on the processes writing to us,
- * so we wait for the error
- */
- n = read9pmsg(mfd[0], mdata, messagesize);
- if(n == 0)
- continue;
- if(n < 0)
- error("mount read");
- if(convM2S(mdata, n, &thdr) != n)
- error("convM2S error");
- rhdr.data = (char*)mdata + IOHDRSZ;
- if(!fcalls[thdr.type])
- err = "bad fcall type";
- else
- err = (*fcalls[thdr.type])(newfid(thdr.fid));
- if(err){
- if(*err == 0)
- continue; /* assigned to a slave */
- rhdr.type = Rerror;
- rhdr.ename = err;
- }else{
- rhdr.type = thdr.type + 1;
- rhdr.fid = thdr.fid;
- }
- rhdr.tag = thdr.tag;
- n = convS2M(&rhdr, mdata, messagesize);
- if(write(mfd[1], mdata, n) != n)
- error("mount write");
- }
- }
- int
- perm(Fid *f, Dev *d, int p)
- {
- if((p*Pother) & d->perm)
- return 1;
- if(strcmp(f->user, user)==0 && ((p*Pgroup) & d->perm))
- return 1;
- if(strcmp(f->user, d->user)==0 && ((p*Powner) & d->perm))
- return 1;
- return 0;
- }
- void
- error(char *s)
- {
- fprint(2, "%s: %s: %r\n", argv0, s);
- syslog(0, LOGFILE, "%s: %r", s);
- remove("/srv/telco");
- postnote(PNGROUP, getpid(), "exit");
- exits(s);
- }
- void *
- emalloc(ulong n)
- {
- void *p;
- p = mallocz(n, 1);
- if(!p)
- error("out of memory");
- return p;
- }
- void *
- erealloc(void *p, ulong n)
- {
- p = realloc(p, n);
- if(!p)
- error("out of memory");
- return p;
- }
- /*
- * send bytes to modem
- */
- int
- send(Dev *d, char *x)
- {
- if(verbose)
- syslog(0, LOGFILE, "->%s", x);
- return write(d->data, x, strlen(x));
- }
- /*
- * apply a string of commands to modem
- */
- int
- apply(Dev *d, char *s, char *substr, int secs)
- {
- char buf[128];
- char *p;
- int c, m;
- p = buf;
- m = Ok;
- while(*s){
- c = *p++ = *s++;
- if(c == '\r' || *s == 0){
- if(c != '\r')
- *p++ = '\r';
- *p = 0;
- if(send(d, buf) < 0)
- return Failure;
- m = readmsg(d, secs, substr);
- p = buf;
- }
- }
- return m;
- }
- /*
- * apply a command type
- */
- int
- applyspecial(Dev *d, int index)
- {
- char *cmd;
- cmd = d->t->commands[index];
- if(cmd == 0 && d->baset)
- cmd = d->baset->commands[index];
- if(cmd == 0)
- return Failure;
- return apply(d, cmd, 0, 2);
- }
- /*
- * get modem into command mode if it isn't already
- */
- int
- attention(Dev *d)
- {
- int i;
- for(i = 0; i < 2; i++){
- sleep(250);
- if(send(d, "+") < 0)
- continue;
- sleep(250);
- if(send(d, "+") < 0)
- continue;
- sleep(250);
- if(send(d, "+") < 0)
- continue;
- sleep(250);
- readmsg(d, 0, 0);
- if(apply(d, "ATZH0", 0, 2) == Ok)
- return Ok;
- }
- return Failure;
- }
- int portspeed[] = { 56000, 38400, 19200, 14400, 9600, 4800, 2400, 1200, 600, 300, 0 };
- /*
- * get the modem's type and speed
- */
- char*
- modemtype(Dev *d, int limit, int fax)
- {
- int *p;
- Type *t, *bt;
- char buf[28];
- d->t = typetab;
- d->baset = 0;
- /* assume we're at a good speed, try getting attention a few times */
- attention(d);
- /* find a common port rate */
- for(p = portspeed; *p; p++){
- if(*p > limit)
- continue;
- setspeed(d, *p);
- if(attention(d) == Ok)
- break;
- }
- if(*p == 0)
- return Eattn;
- d->speed = *p;
- if(verbose)
- syslog(0, LOGFILE, "port speed %d", *p);
- /*
- * basic Hayes commands everyone implements (we hope)
- * Q0 = report result codes
- * V1 = full word result codes
- * E0 = don't echo commands
- * M1 = speaker on until on-line
- * S0=0 = autoanswer off
- */
- if(apply(d, "ATQ0V1E0M1S0=0", 0, 2) != Ok)
- return Eattn;
- /* find modem type */
- for(t = typetab; t->name; t++){
- if(t->ident == 0 || t->response == 0)
- continue;
- if(apply(d, t->ident, t->response, 2) == Found)
- break;
- readmsg(d, 0, 0);
- }
- readmsg(d, 0, 0);
- if(t->name){
- d->t = t;
- if(t->basetype){
- for(bt = typetab; bt->name; bt++)
- if(strcmp(bt->name, t->basetype) == 0)
- break;
- if(bt->name)
- d->baset = bt;
- }
- }
- if(verbose)
- syslog(0, LOGFILE, "modem %s", d->t->name);
- /* try setting fax modes */
- d->fclass = 0;
- if(fax){
- /* set up fax parameters */
- if(applyspecial(d, Cfclass2) != Failure)
- d->fclass = 2;
- /* setup a source id */
- if(srcid){
- sprint(buf, "AT+FLID=\"%s\"", srcid);
- apply(d, buf, 0, 2);
- }
- /* allow both data and fax calls in */
- apply(d, "AT+FAA=1", 0, 2);
- } else
- applyspecial(d, Cfclass0);
- return 0;
- }
- /*
- * a process to read input from a modem.
- */
- void
- monitor(Dev *d)
- {
- int n;
- char *p;
- char file[256];
- int background;
- background = 0;
- d->ctl = d->data = -1;
- for(;;){
- lock(d);
- sprint(file, "%sctl", d->path);
- d->ctl = open(file, ORDWR);
- if(d->ctl < 0)
- error("opening ctl");
- d->data = open(d->path, ORDWR);
- if(d->data < 0)
- error("opening data");
- d->wp = d->rp = d->rbuf;
- unlock(d);
- if(!background){
- background = 1;
- switch(d->pid = rfork(RFPROC|RFMEM)){
- case -1:
- error("out of processes");
- case 0:
- break;
- default:
- return;
- }
- }
- /* wait for ring or off hook */
- while(d->open == 0){
- d->rp = d->rbuf;
- p = d->wp;
- n = read(d->data, p, 1);
- if(n < 1)
- continue;
- if(p < &d->rbuf[Nrbuf] - 2)
- d->wp++;
- if(*p == '\r' || *p == '\n'){
- *(p+1) = 0;
- if(verbose)
- syslog(0, LOGFILE, "<:-%s", d->rp);
- if(answer && strncmp(d->rp, "RING", 4) == 0){
- receiver(d);
- continue;
- }
- if(d->open == 0)
- d->wp = d->rbuf;
- }
- }
- /* shuttle bytes till on hook */
- while(d->open){
- if(d->wp >= d->rp)
- n = &d->rbuf[Nrbuf] - d->wp;
- else
- n = d->rp - d->wp - 1;
- if(n > 0)
- n = read(d->data, d->wp, n);
- else {
- read(d->data, file, sizeof(file));
- continue;
- }
- if(n < 0)
- break;
- lock(d);
- if(d->wp + n >= &d->rbuf[Nrbuf])
- d->wp = d->rbuf;
- else
- d->wp += n;
- serve(d);
- unlock(d);
- }
- close(d->ctl);
- close(d->data);
- }
- }
- /*
- * get bytes input by monitor() (only routine that changes d->rp)
- */
- int
- getinput(Dev *d, char *buf, int n)
- {
- char *p;
- int i;
- p = buf;
- while(n > 0){
- if(d->wp == d->rp)
- break;
- if(d->wp < d->rp)
- i = &d->rbuf[Nrbuf] - d->rp;
- else
- i = d->wp - d->rp;
- if(i > n)
- i = n;
- memmove(p, d->rp, i);
- if(d->rp + i == &d->rbuf[Nrbuf])
- d->rp = d->rbuf;
- else
- d->rp += i;
- n -= i;
- p += i;
- }
- return p - buf;
- }
- /*
- * fulfill a read request (we assume d is locked)
- */
- void
- serve(Dev *d)
- {
- Request *r;
- int n;
- Fcall rhdr;
- uchar *mdata;
- char *buf;
- mdata = malloc(messagesize);
- buf = malloc(messagesize-IOHDRSZ);
- for(;;){
- if(d->r == 0 || d->rp == d->wp)
- break;
- r = d->r;
- if(r->count > sizeof(buf))
- r->count = sizeof(buf);
- n = getinput(d, buf, r->count);
- if(n == 0)
- break;
- d->r = r->next;
- rhdr.type = Rread;
- rhdr.fid = r->fid->fid;
- rhdr.tag = r->tag;
- rhdr.data = buf;
- rhdr.count = n;
- n = convS2M(&rhdr, mdata, messagesize);
- if(write(mfd[1], mdata, n) != n)
- fprint(2, "telco: error writing\n");
- free(r);
- }
- free(mdata);
- free(buf);
- }
- /*
- * dial a number
- */
- char*
- dialout(Dev *d, char *number)
- {
- int i, m, compress, rateadjust, speed, fax;
- char *err;
- char *field[5];
- char dialstr[128];
- compress = Ok;
- rateadjust = Failure;
- speed = maxspeed;
- fax = Failure;
- m = getfields(number, field, 5, 1, "!");
- for(i = 1; i < m; i++){
- if(field[i][0] >= '0' && field[i][0] <= '9')
- speed = atoi(field[i]);
- else if(strcmp(field[i], "nocompress") == 0)
- compress = Failure;
- else if(strcmp(field[i], "fax") == 0)
- fax = Ok;
- }
- syslog(0, LOGFILE, "dialing %s speed=%d %s", number, speed, fax==Ok?"fax":"");
-
- err = modemtype(d, speed, fax == Ok);
- if(err)
- return err;
- /*
- * extented Hayes commands, meaning depends on modem (VGA all over again)
- */
- if(fax != Ok){
- if(d->fclass != 0)
- applyspecial(d, Cfclass0);
- applyspecial(d, Cerrorcorrection);
- if(compress == Ok)
- compress = applyspecial(d, Ccompression);
- if(compress != Ok)
- rateadjust = applyspecial(d, Crateadjust);
- }
- applyspecial(d, Cflowctl);
- /* dialout */
- sprint(dialstr, "ATD%c%s\r", pulsed ? 'P' : 'T', number);
- if(send(d, dialstr) < 0)
- return Edial;
- if(fax == Ok)
- return 0; /* fax sender worries about the rest */
- switch(readmsg(d, 120, 0)){
- case Success:
- break;
- default:
- return d->msgbuf;
- }
- /* change line rate if not compressing */
- if(rateadjust == Ok)
- setspeed(d, getspeed(d->msgbuf, d->speed));
- return 0;
- }
- /*
- * start a receiving process
- */
- void
- receiver(Dev *d)
- {
- int fd;
- char file[256];
- char *argv[8];
- int argc;
- int pfd[2];
- char *prog;
- pipe(pfd);
- switch(rfork(RFPROC|RFMEM|RFFDG|RFNAMEG)){
- case -1:
- return;
- case 0:
- fd = open("/srv/telco", ORDWR);
- if(fd < 0){
- syslog(0, LOGFILE, "can't open telco: %r");
- exits(0);
- }
- if(mount(fd, -1, "/net", MAFTER, "") < 0){
- syslog(0, LOGFILE, "can't mount: %r");
- exits(0);
- }
- close(fd);
- /* open connection through the file system interface */
- sprint(file, "/net/telco/%ld/data", d - dev);
- fd = open(file, ORDWR);
- if(fd < 0){
- syslog(0, LOGFILE, "can't open %s: %r", file);
- exits(0);
- }
- /* let parent continue */
- close(pfd[0]);
- close(pfd[1]);
- /* answer the phone and see what flavor call this is */
- prog = "/bin/service/telcodata";
- switch(apply(d, "ATA", "+FCON", 30)){
- case Success:
- break;
- case Found:
- prog = "/bin/service/telcofax";
- break;
- default:
- syslog(0, LOGFILE, "bad ATA response");
- exits(0);
- }
- /* fork a receiving process */
- dup(fd, 0);
- dup(fd, 1);
- close(fd);
- argc = 0;
- argv[argc++] = strrchr(prog, '/')+1;
- argv[argc++] = file;
- argv[argc++] = dev->t->name;
- argv[argc] = 0;
- exec(prog, argv);
- syslog(0, LOGFILE, "can't exec %s: %r\n", prog);
- exits(0);
- default:
- /* wait till child gets the device open */
- close(pfd[1]);
- read(pfd[0], file, 1);
- close(pfd[0]);
- break;
- }
- }
- /*
- * hang up an connections in progress
- */
- void
- onhook(Dev *d)
- {
- write(d->ctl, "d0", 2);
- write(d->ctl, "r0", 2);
- sleep(250);
- write(d->ctl, "r1", 2);
- write(d->ctl, "d1", 2);
- modemtype(d, maxspeed, 1);
- }
- /*
- * read till we see a message or we time out
- */
- int
- readmsg(Dev *d, int secs, char *substr)
- {
- ulong start;
- char *p;
- int i, len;
- Msg *pp;
- int found = 0;
- p = d->msgbuf;
- len = sizeof(d->msgbuf) - 1;
- for(start = time(0); time(0) <= start+secs;){
- if(len && d->rp == d->wp){
- sleep(100);
- continue;
- }
- i = getinput(d, p, 1);
- if(i == 0)
- continue;
- if(*p == '\n' || *p == '\r' || len == 0){
- *p = 0;
- if(verbose && p != d->msgbuf)
- syslog(0, LOGFILE, "<-%s", d->msgbuf);
- if(substr && strstr(d->msgbuf, substr))
- found = 1;
- for(pp = msgs; pp->text; pp++)
- if(strncmp(pp->text, d->msgbuf, strlen(pp->text))==0)
- return found ? Found : pp->type;
- start = time(0);
- p = d->msgbuf;
- len = sizeof(d->msgbuf) - 1;
- continue;
- }
- len--;
- p++;
- }
- strcpy(d->msgbuf, "No response from modem");
- return found ? Found : Noise;
- }
- /*
- * get baud rate from a connect message
- */
- int
- getspeed(char *msg, int speed)
- {
- char *p;
- int s;
- p = msg + sizeof("CONNECT") - 1;
- while(*p == ' ' || *p == '\t')
- p++;
- s = atoi(p);
- if(s <= 0)
- return speed;
- else
- return s;
- }
- /*
- * set speed and RTS/CTS modem flow control
- */
- void
- setspeed(Dev *d, int baud)
- {
- char buf[32];
- if(d->ctl < 0)
- return;
- sprint(buf, "b%d", baud);
- write(d->ctl, buf, strlen(buf));
- write(d->ctl, "m1", 2);
- }
|