📄 telco.c
字号:
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 */voidio(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"); }}intperm(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;}voiderror(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 */intsend(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 */intapply(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 */intapplyspecial(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 */intattention(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. */voidmonitor(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) */intgetinput(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) */voidserve(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 */voidreceiver(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 */voidonhook(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 */intreadmsg(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 */intgetspeed(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 */voidsetspeed(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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -