]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/telco/telco.c
fix permissiosn for upas and telco rc script
[plan9front.git] / sys / src / cmd / telco / telco.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5
6 #define LOGFILE "telco"
7
8 /*
9  * Rather than reading /adm/users, which is a lot of work for
10  * a toy progdev, we assume all groups have the form
11  *      NNN:user:user:
12  * meaning that each user is the leader of his own group.
13  */
14
15 enum
16 {
17         OPERM   = 0x3,          /* mask of all permission types in open mode */
18         Ndev    = 8,
19         Nreq    = (Ndev*3)/2,
20         Nrbuf   = 32*1024,
21 };
22
23 typedef struct Fid Fid;
24 typedef struct Dev Dev;
25 typedef struct Request Request;
26 typedef struct Type Type;
27
28 struct Fid
29 {
30         Qid     qid;
31         short   busy;
32         short   open;
33         int     fid;
34         Fid     *next;
35         char    *user;
36 };
37
38 struct Request
39 {
40         Request *next;
41
42         Fid     *fid;
43         ulong   tag;
44         int     count;
45         int     flushed;
46 };
47
48 struct Dev
49 {
50         Lock;
51
52         /* device state */
53         int     ctl;            /* control fd */
54         int     data;           /* data fd */
55         char    *path;          /* to device */
56         Type    *t;
57         Type    *baset;
58         int     speed;
59         int     fclass;
60
61         /* fs emulation */
62         int     open;
63         long    perm;
64         char    *name;
65         char    *user;
66         char    msgbuf[128];
67         Request *r;
68         Request *rlast;
69
70         /* input reader */
71         int     monitoring;     /* monitor pid */
72         char    rbuf[Nrbuf];
73         char    *rp;
74         char    *wp;
75         long    pid;
76 };
77
78 enum
79 {
80         Devmask=        (Ndev-1)<<8,
81         
82         Qlvl1=          0,
83         Qlvl2=          1,
84         Qclone=         2,
85         Qlvl3=          3,
86         Qdata=          4,
87         Qctl=           5,
88
89         Pexec =         1,
90         Pwrite =        2,
91         Pread =         4,
92         Pother =        1,
93         Pgroup =        8,
94         Powner =        64,
95 };
96
97 char *names[] =
98 {
99 [Qlvl1]         "/",
100 [Qlvl2]         "telco",
101 [Qclone]        "clone",
102 [Qlvl3]         "",
103 [Qdata]         "data",
104 [Qctl]          "ctl",
105 };
106
107 #define DEV(q) ((((ulong)(q).path)&Devmask)>>8)
108 #define TYPE(q) (((ulong)(q).path)&((1<<8)-1))
109 #define MKQID(t, i) ((((i)<<8)&Devmask) | (t))
110
111 enum
112 {
113         /*
114          *  modem specific commands
115          */
116         Cerrorcorrection        = 0,    /* error correction */
117         Ccompression,                   /* compression */
118         Cflowctl,                       /* CTS/RTS */
119         Crateadjust,                    /* follow line speed */
120         Cfclass2,                       /* set up for fax */
121         Cfclass0,                       /* set up for data */
122         Ncommand,
123 };
124
125 struct Type
126 {
127         char    *name;
128         char    *ident;         /* inquire request */
129         char    *response;      /* inquire response (strstr) */
130         char    *basetype;      /* name of base type */
131
132         char    *commands[Ncommand];
133 };
134
135 /*
136  *  Fax setup summary
137  *
138  *      FCLASS=2        - set to service class 2, i.e., one where the fax handles timing 
139  *      FTBC=0          - ???
140  *      FREL=1          - ???
141  *      FCQ=1           - receive copy quality checking enabled
142  *      FBOR=1          - set reversed bit order for phase C data
143  *      FCR=1           - the DCE can receive message data, bit 10 in the DIS or
144  *                        DTC frame will be set
145  *      FDIS=,3         - limit com speed to 9600 baud
146  */
147
148 Type typetab[] =
149 {
150  {      "Rockwell",             0,      0,      0,
151         "AT\\N7",       /* auto reliable (V.42, fall back to MNP, to none) */
152         "AT%C1\\J0",    /* negotiate for compression, don't change port baud rate */
153         "AT\\Q3",       /* CTS/RTS flow control */
154         "AT\\J1",
155         "AT+FCLASS=2\rAT+FCR=1\r",
156         "AT+FCLASS=0",
157  },
158
159  {      "ATT2400",      "ATI9", "E2400",        "Rockwell",
160         "AT\\N3",       /* auto reliable (MNP, fall back to none) */
161         0,      
162         0,
163         0,
164         0,
165         0,
166  },
167
168  {      "ATT14400",     "ATI9", "E14400",       "Rockwell",
169         0,
170         0,      
171         0,
172         0,
173         0,
174         0,
175  },
176
177  {      "MT1432",       "ATI2", "MT1432",       0,
178         "AT&E1",        /* auto reliable (V.42, fall back to none) */
179         "AT&E15$BA0",   /* negotiate for compression */
180         "AT&E4",        /* CTS/RTS flow control */
181         "AT$BA1",       /* don't change port baud rate */
182         "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1\rAT+FDIS=,3",
183         "AT+FCLASS=0",
184  },
185
186  {      "MT2834",       "ATI2", "MT2834",       "MT1432",
187         0,
188         0,
189         0,
190         0,
191         "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1",
192         0,
193  },
194
195  {      "VOCAL",        "ATI6", "144DPL+FAX",   "Rockwell",
196         "AT\\N3",       /* auto reliable (V.42, fall back to MNP, fall back to none) */
197         "AT%C3\\J0",    /* negotiate for compression, don't change port baud rate */    
198         0,
199         0,
200         "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1",
201         "AT+FCLASS=0",
202  },
203
204  { 0, },
205 };
206
207 /*
208  *  modem return codes
209  */
210 enum
211 {
212         Ok,
213         Success,
214         Failure,
215         Noise,
216         Found,
217 };
218
219 /*
220  *  modem return messages
221  */
222 typedef struct Msg      Msg;
223 struct Msg
224 {
225         char    *text;
226         int     type;
227 };
228
229 Msg msgs[] =
230 {
231         { "OK",                 Ok, },
232         { "NO CARRIER",         Failure, },
233         { "ERROR",              Failure, },
234         { "NO DIALTONE",        Failure, },
235         { "BUSY",               Failure, },
236         { "NO ANSWER",          Failure, },
237         { "CONNECT",            Success, },
238         { 0,                    0 },
239 };
240
241 Fid     *fids;
242 Dev     *dev;
243 int     ndev;
244 int     mfd[2];
245 char    *user;
246 uchar   mdata[8192+IOHDRSZ];
247 int     messagesize = sizeof mdata;
248 Fcall   thdr;
249 Fcall   rhdr;
250 char    errbuf[ERRMAX];
251 uchar   statbuf[STATMAX];
252 int     pulsed;
253 int     verbose;
254 int     maxspeed = 56000;
255 char    *srcid = "plan9";
256 int     answer = 1;
257
258 Fid     *newfid(int);
259 int     devstat(Dir*, uchar*, int);
260 int     devgen(Qid, int, Dir*, uchar*, int);
261 void    error(char*);
262 void    io(void);
263 void    *erealloc(void*, ulong);
264 void    *emalloc(ulong);
265 void    usage(void);
266 int     perm(Fid*, Dev*, int);
267 void    setspeed(Dev*, int);
268 int     getspeed(char*, int);
269 char    *dialout(Dev*, char*);
270 void    onhook(Dev*);
271 int     readmsg(Dev*, int, char*);
272 void    monitor(Dev*);
273 int     getinput(Dev*, char*, int);
274 void    serve(Dev*);
275 void    receiver(Dev*);
276 char*   modemtype(Dev*, int, int);
277
278
279 char    *rflush(Fid*), *rversion(Fid*),
280         *rattach(Fid*), *rauth(Fid*), *rwalk(Fid*),
281         *ropen(Fid*), *rcreate(Fid*),
282         *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
283         *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
284
285 char    *(*fcalls[])(Fid*) = {
286         [Tflush]        rflush,
287         [Tversion]      rversion,
288         [Tattach]       rattach,
289         [Tauth] rauth,
290         [Twalk]         rwalk,
291         [Topen]         ropen,
292         [Tcreate]       rcreate,
293         [Tread]         rread,
294         [Twrite]        rwrite,
295         [Tclunk]        rclunk,
296         [Tremove]       rremove,
297         [Tstat]         rstat,
298         [Twstat]        rwstat,
299 };
300
301 char    Eperm[] =       "permission denied";
302 char    Enotdir[] =     "not a directory";
303 char    Enotexist[] =   "file does not exist";
304 char    Ebadaddr[] =    "bad address";
305 char    Eattn[] =       "can't get modem's attention";
306 char    Edial[] =       "can't dial";
307 char    Enoauth[] =     "telco: authentication not required";
308 char    Eisopen[] =     "file already open for I/O";
309 char    Enodev[] =      "no free modems";
310 char    Enostream[] =   "stream closed prematurely";
311
312 void
313 usage(void)
314 {
315         fprint(2, "usage: %s [-vp] [-i srcid] dev ...\n", argv0);
316         exits("usage");
317 }
318
319 void
320 notifyf(void *a, char *s)
321 {
322         USED(a);
323         if(strncmp(s, "interrupt", 9) == 0)
324                 noted(NCONT);
325         noted(NDFLT);
326 }
327
328 void
329 main(int argc, char *argv[])
330 {
331         int p[2];
332         int fd;
333         char buf[10];
334         Dev *d;
335
336         ARGBEGIN{
337         case 'p':
338                 pulsed = 1;
339                 break;
340         case 'v':
341                 verbose = 1;
342                 break;
343         case 'i':
344                 srcid = ARGF();
345                 break;
346         case 's':
347                 maxspeed = atoi(ARGF());
348                 break;
349         case 'n':
350                 answer = 0;
351                 break;
352         default:
353                 usage();
354         }ARGEND
355
356         if(argc == 0)
357                 usage();
358         if(argc > Ndev)
359                 argc = Ndev;
360
361         if(pipe(p) < 0)
362                 error("pipe failed");
363
364         notify(notifyf);
365         fmtinstall('F', fcallfmt);
366         user = getuser();
367
368         switch(rfork(RFFDG|RFPROC|RFREND|RFNOTEG)){
369         case -1:
370                 error("fork");
371         case 0:
372                 close(p[1]);
373                 mfd[0] = mfd[1] = p[0];
374                 break;
375         default:
376                 close(p[0]);
377                 fd = create("/srv/telco", OWRITE, 0666);
378                 if(fd < 0)
379                         error("create of /srv/telco failed");
380                 sprint(buf, "%d", p[1]);
381                 if(write(fd, buf, strlen(buf)) < 0)
382                         error("writing /srv/telco");
383                 close(fd);
384                 if(mount(p[1], -1, "/net", MBEFORE, "") < 0)
385                         error("mount failed");
386                 exits(0);
387         }
388
389         dev = mallocz(argc*sizeof(Dev), 1);
390         for(ndev = 0; ndev < argc; ndev++){
391                 d = &dev[ndev];
392                 d->path = argv[ndev];
393                 d->rp = d->wp = d->rbuf;
394                 monitor(d);
395                 d->open++;
396                 onhook(d);
397                 d->open--;
398         }
399
400         io();
401 }
402
403 /*
404  *  generate a stat structure for a qid
405  */
406 int
407 devstat(Dir *dir, uchar *buf, int nbuf)
408 {
409         Dev *d;
410         int t;
411         static char tmp[10][32];
412         static int ntmp;
413
414         t = TYPE(dir->qid);
415         if(t != Qlvl3)
416                 dir->name = names[t];
417         else{
418                 dir->name = tmp[ntmp % nelem(tmp)];
419                 sprint(dir->name, "%lud", DEV(dir->qid));
420                 ntmp++;
421         }
422         dir->mode = 0755;
423         dir->uid = user;
424         dir->gid = user;
425         dir->muid = user;
426         if(t >= Qlvl3){
427                 d = &dev[DEV(dir->qid)];
428                 if(d->open){
429                         dir->mode = d->perm;
430                         dir->uid = d->user;
431                 }
432         }
433         if(dir->qid.type & QTDIR)
434                 dir->mode |= DMDIR;
435         if(t == Qdata){
436                 d = &dev[DEV(dir->qid)];
437                 dir->length = d->wp - d->rp;
438                 if(dir->length < 0)
439                         dir->length += Nrbuf;
440         } else
441                 dir->length = 0;
442         dir->atime = time(0);
443         dir->mtime = dir->atime;
444         if(buf)
445                 return convD2M(dir, buf, nbuf);
446         return 0;
447 }
448
449 /*
450  *  enumerate file's we can walk to from q
451  */
452 int
453 devgen(Qid q, int i, Dir *d, uchar *buf, int nbuf)
454 {
455         static ulong v;
456
457         d->qid.vers = v++;
458         switch(TYPE(q)){
459         case Qlvl1:
460                 if(i != 0)
461                         return -1;
462                 d->qid.type = QTDIR;
463                 d->qid.path = Qlvl2;
464                 break;
465         case Qlvl2:
466                 switch(i){
467                 case -1:
468                         d->qid.type = QTDIR;
469                         d->qid.path = Qlvl1;
470                         break;
471                 case 0:
472                         d->qid.type = QTFILE;
473                         d->qid.path = Qclone;
474                         break;
475                 default:
476                         if(i > ndev)
477                                 return -1;
478                         d->qid.type = QTDIR;
479                         d->qid.path = MKQID(Qlvl3, i-1);
480                         break;
481                 }
482                 break;
483         case Qlvl3:
484                 switch(i){
485                 case -1:
486                         d->qid.type = QTDIR;
487                         d->qid.path = Qlvl2;
488                         break;
489                 case 0:
490                         d->qid.type = QTFILE;
491                         d->qid.path = MKQID(Qdata, DEV(q));
492                         break;
493                 case 1:
494                         d->qid.type = QTFILE;
495                         d->qid.path = MKQID(Qctl, DEV(q));
496                         break;
497                 default:
498                         return -1;
499                 }
500                 break;
501         default:
502                 return -1;
503         }
504         return devstat(d, buf, nbuf);
505 }
506
507 char*
508 rversion(Fid *)
509 {
510         Fid *f;
511
512         for(f = fids; f; f = f->next)
513                 if(f->busy)
514                         rclunk(f);
515
516         if(thdr.msize < 256)
517                 return "version: message size too small";
518         messagesize = thdr.msize;
519         if(messagesize > sizeof mdata)
520                 messagesize = sizeof mdata;
521         rhdr.msize = messagesize;
522         if(strncmp(thdr.version, "9P2000", 6) != 0)
523                 return "unrecognized 9P version";
524         rhdr.version = "9P2000";
525         return 0;
526 }
527
528 char*
529 rflush(Fid *f)
530 {
531         Request *r, **l;
532         Dev *d;
533
534         USED(f);
535         for(d = dev; d < &dev[ndev]; d++){
536                 lock(d);
537                 for(l = &d->r; r = *l; l = &r->next)
538                         if(r->tag == thdr.oldtag){
539                                 *l = r->next;
540                                 free(r);
541                                 break;
542                         }
543                 unlock(d);
544         }
545         return 0;
546 }
547
548 char *
549 rauth(Fid *f)
550 {
551         USED(f);
552         return Enoauth;
553 }
554
555 char*
556 rattach(Fid *f)
557 {
558         f->busy = 1;
559         f->qid.type = QTDIR;
560         f->qid.path = Qlvl1;
561         f->qid.vers = 0;
562         rhdr.qid = f->qid;
563         if(thdr.uname[0])
564                 f->user = strdup(thdr.uname);
565         else
566                 f->user = "none";
567         return 0;
568 }
569
570 char*
571 rwalk(Fid *f)
572 {
573         Fid *nf;
574         int i, nqid;
575         char *name, *err;
576         Dir dir;
577         Qid q;
578
579         nf = nil;
580         if(thdr.fid != thdr.newfid){
581                 if(f->open)
582                         return Eisopen;
583                 if(f->busy == 0)
584                         return Enotexist;
585                 nf = newfid(thdr.newfid);
586                 nf->busy = 1;
587                 nf->open = 0;
588                 nf->qid = f->qid;
589                 nf->user = strdup(f->user);
590                 f = nf; /* walk f */
591         }
592
593         err = nil;
594         dir.qid = f->qid;
595         nqid = 0;
596         if(thdr.nwname > 0){
597                 for(; nqid < thdr.nwname; nqid++) {
598                         if((dir.qid.type & QTDIR) == 0){
599                                 err = Enotdir;
600                                 break;
601                         }
602                         name = thdr.wname[nqid];
603                         if(strcmp(name, ".") == 0){
604                                 /* nothing to do */
605                         }else if(strcmp(name, "..") == 0) {
606                                 if(devgen(f->qid, -1, &dir, 0, 0) < 0)
607                                         break;
608                         }
609                         else{
610                                 q = dir.qid;
611                                 for(i = 0;; i++){
612                                         if(devgen(q, i, &dir, 0, 0) < 0)
613                                                 goto Out;
614                                         if(strcmp(name, dir.name) == 0)
615                                                 break;
616                                 }
617                         }
618                         rhdr.wqid[nqid] = dir.qid;
619                 }
620     Out:
621                 if(nqid == 0 && err == nil)
622                         err = Enotexist;
623                 if(nf != nil && thdr.fid != thdr.newfid && nqid < thdr.nwname)
624                         rclunk(nf);
625         }
626
627         rhdr.nwqid = nqid;
628         if(nqid > 0 && nqid == thdr.nwname)
629                 f->qid = dir.qid;
630         return err;
631 }
632
633 char *
634 ropen(Fid *f)
635 {
636         Dev *d;
637         int mode, t;
638
639         if(f->open)
640                 return Eisopen;
641         mode = thdr.mode;
642         mode &= OPERM;
643         if(f->qid.type & QTDIR){
644                 if(mode != OREAD)
645                         return Eperm;
646                 rhdr.qid = f->qid;
647                 return 0;
648         }
649         if(mode==OEXEC)
650                 return Eperm;
651         t = TYPE(f->qid);
652         if(t == Qclone){
653                 for(d = dev; d < &dev[ndev]; d++)
654                         if(d->open == 0)
655                                 break;
656                 if(d == &dev[ndev])
657                         return Enodev;
658                 f->qid.path = MKQID(Qctl, d-dev);
659                 t = Qctl;
660         }
661         switch(t){
662         case Qdata:
663         case Qctl:
664                 d = &dev[DEV(f->qid)];
665                 if(d->open == 0){
666                         d->user = strdup(f->user);
667                         d->perm = 0660;
668                 }else {
669                         if(mode==OWRITE || mode==ORDWR)
670                                 if(!perm(f, d, Pwrite))
671                                         return Eperm;
672                         if(mode==OREAD || mode==ORDWR)
673                                 if(!perm(f, d, Pread))
674                                         return Eperm;
675                 }
676                 d->open++;
677                 break;
678         }
679         rhdr.qid = f->qid;
680         rhdr.iounit = messagesize - IOHDRSZ;
681         f->open = 1;
682         return 0;
683 }
684
685 char *
686 rcreate(Fid *f)
687 {
688         USED(f);
689         return Eperm;
690 }
691
692 /*
693  *  intercept a note
694  */
695 void
696 takeanote(void *u, char *note)
697 {
698         USED(u);
699         if(strstr(note, "interrupted"))
700                 noted(NCONT);
701         noted(NDFLT);
702 }
703
704 char*
705 rread(Fid *f)
706 {
707         char *buf;
708         long off, start;
709         int i, m, n, cnt, t;
710         Dir dir;
711         char num[32];
712         Dev *d;
713         Request *r;
714
715         n = 0;
716         rhdr.count = 0;
717         off = thdr.offset;
718         cnt = thdr.count;
719         buf = rhdr.data;
720         t = TYPE(f->qid);
721         switch(t){
722         default:
723                 start = 0;
724                 for(i = 0; n < cnt; i++){
725                         m = devgen(f->qid, i, &dir, (uchar*)buf+n, cnt-n);
726                         if(m <= BIT16SZ)
727                                 break;
728                         if(start >= off)
729                                 n += m;
730                         start += m;
731                 }
732                 break;
733         case Qctl:
734                 i = sprint(num, "%lud", DEV(f->qid));
735                 if(off < i){
736                         n = cnt;
737                         if(off + n > i)
738                                 n = i - off;
739                         memmove(buf, num + off, n);
740                 } else
741                         n = 0;
742                 break;
743         case Qdata:
744                 d = &dev[DEV(f->qid)];
745                 r = mallocz(sizeof(Request), 1);
746                 r->tag = thdr.tag;
747                 r->count = thdr.count;
748                 r->fid = f;
749                 r->flushed = 0;
750                 lock(d);
751                 if(d->r)
752                         d->rlast->next = r;
753                 else
754                         d->r = r;
755                 d->rlast = r;
756                 serve(d);
757                 unlock(d);
758                 return "";
759         }
760         rhdr.count = n;
761         return 0;
762 }
763
764 char *cmsg = "connect ";
765 int clen;
766
767 char*
768 rwrite(Fid *f)
769 {
770         Dev *d;
771         ulong off;
772         int cnt;
773         char *cp;
774         char buf[64];
775
776         off = thdr.offset;
777         cnt = thdr.count;
778         switch(TYPE(f->qid)){
779         default:
780                 return "file is a directory";
781         case Qctl:
782                 d = &dev[DEV(f->qid)];
783                 clen = strlen(cmsg);
784                 if(cnt < clen || strncmp(thdr.data, cmsg, clen) != 0){
785                         /*
786                          *  send control message to real control file
787                          */
788                         if(seek(d->ctl, off, 0) < 0 || write(d->ctl, thdr.data, cnt) < 0){
789                                 errstr(errbuf, sizeof errbuf);
790                                 return errbuf;
791                         }
792                 } else {
793                         /*
794                          *  connect
795                          */
796                         cnt -= clen;
797                         if(cnt >= sizeof(buf))
798                                 cnt = sizeof(buf) - 1;
799                         if(cnt < 0)
800                                 return Ebadaddr;
801                         strncpy(buf, &thdr.data[clen], cnt);
802                         buf[cnt] = 0;
803                         cp = dialout(d, buf);
804                         if(cp)
805                                 return cp;
806                 }
807                 rhdr.count = cnt;
808                 break;
809         case Qdata:
810                 d = &dev[DEV(f->qid)];
811                 if(write(d->data, thdr.data, cnt) < 0){
812                         errstr(errbuf, sizeof errbuf);
813                         return errbuf;
814                 }
815                 rhdr.count = cnt;
816                 break;
817         }
818         return 0;
819 }
820
821 char *
822 rclunk(Fid *f)
823 {
824         Dev *d;
825
826         if(f->open)
827                 switch(TYPE(f->qid)){
828                 case Qdata:
829                 case Qctl:
830                         d = &dev[DEV(f->qid)];
831                         if(d->open == 1)
832                                 onhook(d);
833                         d->open--;
834                         break;
835                 }
836         free(f->user);
837         f->busy = 0;
838         f->open = 0;
839         return 0;
840 }
841
842 char *
843 rremove(Fid *f)
844 {
845         USED(f);
846         return Eperm;
847 }
848
849 char *
850 rstat(Fid *f)
851 {
852         Dir d;
853
854         d.qid = f->qid;
855         rhdr.stat = statbuf;
856         rhdr.nstat = devstat(&d, statbuf, sizeof statbuf);
857         return 0;
858 }
859
860 char *
861 rwstat(Fid *f)
862 {
863         Dev *d;
864         Dir dir;
865
866         if(TYPE(f->qid) < Qlvl3)
867                 return Eperm;
868
869         convM2D(thdr.stat, thdr.nstat, &dir, rhdr.data);        /* rhdr.data is a known place to scribble */
870         d = &dev[DEV(f->qid)];
871
872         /*
873          * To change mode, must be owner
874          */
875         if(d->perm != dir.mode){
876                 if(strcmp(f->user, d->user) != 0)
877                 if(strcmp(f->user, user) != 0)
878                         return Eperm;
879         }
880
881         /* all ok; do it */
882         d->perm = dir.mode & ~DMDIR;
883         return 0;
884 }
885
886 Fid *
887 newfid(int fid)
888 {
889         Fid *f, *ff;
890
891         ff = 0;
892         for(f = fids; f; f = f->next)
893                 if(f->fid == fid)
894                         return f;
895                 else if(!ff && !f->busy)
896                         ff = f;
897         if(ff){
898                 ff->fid = fid;
899                 return ff;
900         }
901         f = emalloc(sizeof *f);
902         f->fid = fid;
903         f->next = fids;
904         fids = f;
905         return f;
906 }
907
908 /*
909  *  read fs requests and dispatch them
910  */
911 void
912 io(void)
913 {
914         char *err;
915         int n;
916
917         for(;;){
918                 /*
919                  * reading from a pipe or a network device
920                  * will give an error after a few eof reads
921                  * however, we cannot tell the difference
922                  * between a zero-length read and an interrupt
923                  * on the processes writing to us,
924                  * so we wait for the error
925                  */
926                 n = read9pmsg(mfd[0], mdata, messagesize);
927                 if(n == 0)
928                         continue;
929                 if(n < 0)
930                         error("mount read");
931                 if(convM2S(mdata, n, &thdr) != n)
932                         error("convM2S error");
933
934                 rhdr.data = (char*)mdata + IOHDRSZ;
935                 if(!fcalls[thdr.type])
936                         err = "bad fcall type";
937                 else
938                         err = (*fcalls[thdr.type])(newfid(thdr.fid));
939                 if(err){
940                         if(*err == 0)
941                                 continue;       /* assigned to a slave */
942                         rhdr.type = Rerror;
943                         rhdr.ename = err;
944                 }else{
945                         rhdr.type = thdr.type + 1;
946                         rhdr.fid = thdr.fid;
947                 }
948                 rhdr.tag = thdr.tag;
949                 n = convS2M(&rhdr, mdata, messagesize);
950                 if(write(mfd[1], mdata, n) != n)
951                         error("mount write");
952         }
953 }
954
955
956 int
957 perm(Fid *f, Dev *d, int p)
958 {
959         if((p*Pother) & d->perm)
960                 return 1;
961         if(strcmp(f->user, user)==0 && ((p*Pgroup) & d->perm))
962                 return 1;
963         if(strcmp(f->user, d->user)==0 && ((p*Powner) & d->perm))
964                 return 1;
965         return 0;
966 }
967
968 void
969 error(char *s)
970 {
971         fprint(2, "%s: %s: %r\n", argv0, s);
972         syslog(0, LOGFILE, "%s: %r", s);
973         remove("/srv/telco");
974         postnote(PNGROUP, getpid(), "exit");
975         exits(s);
976 }
977
978 void *
979 emalloc(ulong n)
980 {
981         void *p;
982
983         p = mallocz(n, 1);
984         if(!p)
985                 error("out of memory");
986         return p;
987 }
988
989 void *
990 erealloc(void *p, ulong n)
991 {
992         p = realloc(p, n);
993         if(!p)
994                 error("out of memory");
995         return p;
996 }
997
998 /*
999  *  send bytes to modem
1000  */
1001 int
1002 send(Dev *d, char *x)
1003 {
1004         if(verbose)
1005                 syslog(0, LOGFILE, "->%s", x);
1006         return write(d->data, x, strlen(x));
1007 }
1008
1009 /*
1010  *  apply a string of commands to modem
1011  */
1012 int
1013 apply(Dev *d, char *s, char *substr, int secs)
1014 {
1015         char buf[128];
1016         char *p;
1017         int c, m;
1018
1019         p = buf;
1020         m = Ok;
1021         while(*s){
1022                 c = *p++ = *s++;
1023                 if(c == '\r' || *s == 0){
1024                         if(c != '\r')
1025                                 *p++ = '\r';
1026                         *p = 0;
1027                         if(send(d, buf) < 0)
1028                                 return Failure;
1029                         m = readmsg(d, secs, substr);
1030                         p = buf;
1031                 }
1032         }
1033         return m;
1034 }
1035
1036 /*
1037  *  apply a command type
1038  */
1039 int
1040 applyspecial(Dev *d, int index)
1041 {
1042         char *cmd;
1043
1044         cmd = d->t->commands[index];
1045         if(cmd == 0 && d->baset)
1046                 cmd = d->baset->commands[index];
1047         if(cmd == 0)
1048                 return Failure;
1049
1050         return apply(d, cmd, 0, 2);
1051 }
1052
1053 /*
1054  *  get modem into command mode if it isn't already
1055  */
1056 int
1057 attention(Dev *d)
1058 {
1059         int i;
1060
1061         for(i = 0; i < 2; i++){
1062                 sleep(250);
1063                 if(send(d, "+") < 0)
1064                         continue;
1065                 sleep(250);
1066                 if(send(d, "+") < 0)
1067                         continue;
1068                 sleep(250);
1069                 if(send(d, "+") < 0)
1070                         continue;
1071                 sleep(250);
1072                 readmsg(d, 0, 0);
1073                 if(apply(d, "ATZH0", 0, 2) == Ok)
1074                         return Ok;
1075         }
1076         return Failure;
1077 }
1078
1079 int portspeed[] = { 56000, 38400, 19200, 14400, 9600, 4800, 2400, 1200, 600, 300, 0 };
1080
1081 /*
1082  *  get the modem's type and speed
1083  */
1084 char*
1085 modemtype(Dev *d, int limit,  int fax)
1086 {
1087         int *p;
1088         Type *t, *bt;
1089         char buf[28];
1090
1091         d->t = typetab;
1092         d->baset = 0;
1093
1094         /* assume we're at a good speed, try getting attention a few times */
1095         attention(d);
1096
1097         /* find a common port rate */
1098         for(p = portspeed; *p; p++){
1099                 if(*p > limit)
1100                         continue;
1101                 setspeed(d, *p);
1102                 if(attention(d) == Ok)
1103                         break;
1104         }
1105         if(*p == 0)
1106                 return Eattn;
1107         d->speed = *p;
1108         if(verbose)
1109                 syslog(0, LOGFILE, "port speed %d", *p);
1110
1111         /*
1112          *  basic Hayes commands everyone implements (we hope)
1113          *      Q0 = report result codes
1114          *      V1 = full word result codes
1115          *      E0 = don't echo commands
1116          *      M1 = speaker on until on-line
1117          *      S0=0 = autoanswer off
1118          */
1119         if(apply(d, "ATQ0V1E0M1S0=0", 0, 2) != Ok)
1120                 return Eattn;
1121
1122         /* find modem type */
1123         for(t = typetab; t->name; t++){
1124                 if(t->ident == 0 || t->response == 0)
1125                         continue;
1126                 if(apply(d, t->ident, t->response, 2) == Found)
1127                         break;
1128                 readmsg(d, 0, 0);
1129         }
1130         readmsg(d, 0, 0);
1131         if(t->name){
1132                 d->t = t;
1133                 if(t->basetype){
1134                         for(bt = typetab; bt->name; bt++)
1135                                 if(strcmp(bt->name, t->basetype) == 0)
1136                                         break;
1137                         if(bt->name)
1138                                 d->baset = bt;
1139                 }
1140         }
1141         if(verbose)
1142                 syslog(0, LOGFILE, "modem %s", d->t->name);
1143
1144         /* try setting fax modes */
1145         d->fclass = 0;
1146         if(fax){
1147                 /* set up fax parameters */
1148                 if(applyspecial(d, Cfclass2) != Failure)
1149                         d->fclass = 2;
1150
1151                 /* setup a source id */
1152                 if(srcid){
1153                         sprint(buf, "AT+FLID=\"%s\"", srcid);
1154                         apply(d, buf, 0, 2);
1155                 }
1156
1157                 /* allow both data and fax calls in */
1158                 apply(d, "AT+FAA=1", 0, 2);
1159         } else
1160                 applyspecial(d, Cfclass0);
1161         return 0;
1162 }
1163
1164 /*
1165  *  a process to read input from a modem.
1166  */
1167 void
1168 monitor(Dev *d)
1169 {
1170         int n;
1171         char *p;
1172         char file[256];
1173         int background;
1174
1175         background = 0;
1176         d->ctl = d->data = -1;
1177
1178         for(;;){
1179                 lock(d);
1180                 sprint(file, "%sctl", d->path);
1181                 d->ctl = open(file, ORDWR);
1182                 if(d->ctl < 0)
1183                         error("opening ctl");
1184                 d->data = open(d->path, ORDWR);
1185                 if(d->data < 0)
1186                         error("opening data");
1187                 d->wp = d->rp = d->rbuf;
1188                 unlock(d);
1189
1190                 if(!background){
1191                         background = 1;
1192                         switch(d->pid = rfork(RFPROC|RFMEM)){
1193                         case -1:
1194                                 error("out of processes");
1195                         case 0:
1196                                 break;
1197                         default:
1198                                 return;
1199                         }
1200                 }
1201
1202                 /* wait for ring or off hook */
1203                 while(d->open == 0){
1204                         d->rp = d->rbuf;
1205                         p = d->wp;
1206                         n = read(d->data, p, 1);
1207                         if(n < 1)
1208                                 continue;
1209                         if(p < &d->rbuf[Nrbuf] - 2)
1210                                 d->wp++;
1211                         if(*p == '\r' || *p == '\n'){
1212                                 *(p+1) = 0;
1213                                 if(verbose)
1214                                         syslog(0, LOGFILE, "<:-%s", d->rp);
1215                                 if(answer && strncmp(d->rp, "RING", 4) == 0){
1216                                         receiver(d);
1217                                         continue;
1218                                 }
1219                                 if(d->open == 0)
1220                                         d->wp = d->rbuf;
1221                         }
1222                 }
1223
1224                 /* shuttle bytes till on hook */
1225                 while(d->open){
1226                         if(d->wp >= d->rp)
1227                                 n = &d->rbuf[Nrbuf] - d->wp;
1228                         else
1229                                 n = d->rp - d->wp - 1;
1230                         if(n > 0)
1231                                 n = read(d->data, d->wp, n);
1232                         else {
1233                                 read(d->data, file, sizeof(file));
1234                                 continue;
1235                         }
1236                         if(n < 0)
1237                                 break;
1238                         lock(d);
1239                         if(d->wp + n >= &d->rbuf[Nrbuf])
1240                                 d->wp = d->rbuf;
1241                         else
1242                                 d->wp += n;
1243                         serve(d);
1244                         unlock(d);
1245                 }
1246
1247                 close(d->ctl);
1248                 close(d->data);
1249         }
1250 }
1251
1252 /*
1253  *  get bytes input by monitor() (only routine that changes d->rp)
1254  */
1255 int
1256 getinput(Dev *d, char *buf, int n)
1257 {
1258         char *p;
1259         int i;
1260
1261         p = buf;
1262         while(n > 0){
1263                 if(d->wp == d->rp)
1264                         break;
1265                 if(d->wp < d->rp)
1266                         i = &d->rbuf[Nrbuf] - d->rp;
1267                 else
1268                         i = d->wp - d->rp;
1269                 if(i > n)
1270                         i = n;
1271                 memmove(p, d->rp, i);
1272                 if(d->rp + i == &d->rbuf[Nrbuf])
1273                         d->rp = d->rbuf;
1274                 else
1275                         d->rp += i;
1276                 n -= i;
1277                 p += i;
1278         }
1279         return p - buf;
1280 }
1281
1282 /*
1283  *  fulfill a read request (we assume d is locked)
1284  */
1285 void
1286 serve(Dev *d)
1287 {
1288         Request *r;
1289         int n;
1290         Fcall rhdr;
1291         uchar *mdata;
1292         char *buf;
1293
1294         mdata = malloc(messagesize);
1295         buf = malloc(messagesize-IOHDRSZ);
1296
1297         for(;;){
1298                 if(d->r == 0 || d->rp == d->wp)
1299                         break;
1300                 r = d->r;
1301                 if(r->count > sizeof(buf))
1302                         r->count = sizeof(buf);
1303
1304                 n = getinput(d, buf, r->count);
1305                 if(n == 0)
1306                         break;
1307                 d->r = r->next;
1308
1309                 rhdr.type = Rread;
1310                 rhdr.fid = r->fid->fid;
1311                 rhdr.tag = r->tag;
1312                 rhdr.data = buf;
1313                 rhdr.count = n;
1314                 n = convS2M(&rhdr, mdata, messagesize);
1315                 if(write(mfd[1], mdata, n) != n)
1316                         fprint(2, "telco: error writing\n");
1317                 free(r);
1318         }
1319         free(mdata);
1320         free(buf);
1321 }
1322
1323 /*
1324  *  dial a number
1325  */
1326 char*
1327 dialout(Dev *d, char *number)
1328 {
1329         int i, m, compress, rateadjust, speed, fax;
1330         char *err;
1331         char *field[5];
1332         char dialstr[128];
1333
1334         compress = Ok;
1335         rateadjust = Failure;
1336         speed = maxspeed;
1337         fax = Failure;
1338
1339         m = getfields(number, field, 5, 1, "!");
1340         for(i = 1; i < m; i++){
1341                 if(field[i][0] >= '0' && field[i][0] <= '9')
1342                         speed = atoi(field[i]);
1343                 else if(strcmp(field[i], "nocompress") == 0)
1344                         compress = Failure;
1345                 else if(strcmp(field[i], "fax") == 0)
1346                         fax = Ok;
1347         }
1348
1349         syslog(0, LOGFILE, "dialing %s speed=%d %s", number, speed, fax==Ok?"fax":"");
1350         
1351         err = modemtype(d, speed, fax == Ok);
1352         if(err)
1353                 return err;
1354
1355         /*
1356          *  extented Hayes commands, meaning depends on modem (VGA all over again)
1357          */
1358         if(fax != Ok){
1359                 if(d->fclass != 0)
1360                         applyspecial(d, Cfclass0);
1361                 applyspecial(d, Cerrorcorrection);
1362                 if(compress == Ok)
1363                         compress = applyspecial(d, Ccompression);
1364                 if(compress != Ok)
1365                         rateadjust = applyspecial(d, Crateadjust);
1366         }
1367         applyspecial(d, Cflowctl);
1368
1369         /* dialout */
1370         sprint(dialstr, "ATD%c%s\r", pulsed ? 'P' : 'T', number);
1371         if(send(d, dialstr) < 0)
1372                 return Edial;
1373
1374         if(fax == Ok)
1375                 return 0;               /* fax sender worries about the rest */
1376
1377         switch(readmsg(d, 120, 0)){
1378         case Success:
1379                 break;
1380         default:
1381                 return d->msgbuf;
1382         }
1383
1384         /* change line rate if not compressing */
1385         if(rateadjust == Ok)
1386                 setspeed(d, getspeed(d->msgbuf, d->speed));
1387
1388         return 0;
1389 }
1390
1391 /*
1392  *  start a receiving process
1393  */
1394 void
1395 receiver(Dev *d)
1396 {
1397         int fd;
1398         char file[256];
1399         char *argv[8];
1400         int argc;
1401         int pfd[2];
1402         char *prog;
1403
1404         pipe(pfd);
1405         switch(rfork(RFPROC|RFMEM|RFFDG|RFNAMEG)){
1406         case -1:
1407                 return;
1408         case 0:
1409                 fd = open("/srv/telco", ORDWR);
1410                 if(fd < 0){
1411                         syslog(0, LOGFILE, "can't open telco: %r");
1412                         exits(0);
1413                 }
1414                 if(mount(fd, -1, "/net", MAFTER, "") < 0){
1415                         syslog(0, LOGFILE, "can't mount: %r");
1416                         exits(0);
1417                 }
1418                 close(fd);
1419
1420                 /* open connection through the file system interface */
1421                 sprint(file, "/net/telco/%ld/data", d - dev);
1422                 fd = open(file, ORDWR);
1423                 if(fd < 0){
1424                         syslog(0, LOGFILE, "can't open %s: %r", file);
1425                         exits(0);
1426                 }
1427
1428                 /* let parent continue */
1429                 close(pfd[0]);
1430                 close(pfd[1]);
1431
1432                 /* answer the phone and see what flavor call this is */
1433                 prog = "/bin/service/telcodata";
1434                 switch(apply(d, "ATA", "+FCON", 30)){
1435                 case Success:
1436                         break;
1437                 case Found:
1438                         prog = "/bin/service/telcofax";
1439                         break;
1440                 default:
1441                         syslog(0, LOGFILE, "bad ATA response");
1442                         exits(0);
1443                 }
1444
1445                 /* fork a receiving process */
1446                 dup(fd, 0);
1447                 dup(fd, 1);
1448                 close(fd);
1449                 argc = 0;
1450                 argv[argc++] = strrchr(prog, '/')+1;
1451                 argv[argc++] = file;
1452                 argv[argc++] = dev->t->name;
1453                 argv[argc] = 0;
1454                 exec(prog, argv);
1455                 syslog(0, LOGFILE, "can't exec %s: %r\n", prog);
1456                 exits(0);
1457         default:
1458                 /* wait till child gets the device open */
1459                 close(pfd[1]);
1460                 read(pfd[0], file, 1);
1461                 close(pfd[0]);
1462                 break;
1463         }
1464 }
1465
1466 /*
1467  *  hang up an connections in progress
1468  */
1469 void
1470 onhook(Dev *d)
1471 {
1472         write(d->ctl, "d0", 2);
1473         write(d->ctl, "r0", 2);
1474         sleep(250);
1475         write(d->ctl, "r1", 2);
1476         write(d->ctl, "d1", 2);
1477         modemtype(d, maxspeed, 1);
1478 }
1479
1480 /*
1481  *  read till we see a message or we time out
1482  */
1483 int
1484 readmsg(Dev *d, int secs, char *substr)
1485 {
1486         ulong start;
1487         char *p;
1488         int i, len;
1489         Msg *pp;
1490         int found = 0;
1491
1492         p = d->msgbuf;
1493         len = sizeof(d->msgbuf) - 1;
1494         for(start = time(0); time(0) <= start+secs;){
1495                 if(len && d->rp == d->wp){
1496                         sleep(100);
1497                         continue;
1498                 }
1499                 i = getinput(d, p, 1);
1500                 if(i == 0)
1501                         continue;
1502                 if(*p == '\n' || *p == '\r' || len == 0){
1503                         *p = 0;
1504                         if(verbose && p != d->msgbuf)
1505                                 syslog(0, LOGFILE, "<-%s", d->msgbuf);
1506                         if(substr && strstr(d->msgbuf, substr))
1507                                 found = 1;
1508                         for(pp = msgs; pp->text; pp++)
1509                                 if(strncmp(pp->text, d->msgbuf, strlen(pp->text))==0)
1510                                         return found ? Found : pp->type;
1511                         start = time(0);
1512                         p = d->msgbuf;
1513                         len = sizeof(d->msgbuf) - 1;
1514                         continue;
1515                 }
1516                 len--;
1517                 p++;
1518         }
1519         strcpy(d->msgbuf, "No response from modem");
1520         return found ? Found : Noise;
1521 }
1522
1523 /*
1524  *  get baud rate from a connect message
1525  */
1526 int
1527 getspeed(char *msg, int speed)
1528 {
1529         char *p;
1530         int s;
1531
1532         p = msg + sizeof("CONNECT") - 1;
1533         while(*p == ' ' || *p == '\t')
1534                 p++;
1535         s = atoi(p);
1536         if(s <= 0)
1537                 return speed;
1538         else
1539                 return s;
1540 }
1541
1542 /*
1543  *  set speed and RTS/CTS modem flow control
1544  */
1545 void
1546 setspeed(Dev *d, int baud)
1547 {
1548         char buf[32];
1549
1550         if(d->ctl < 0)
1551                 return;
1552         sprint(buf, "b%d", baud);
1553         write(d->ctl, buf, strlen(buf));
1554         write(d->ctl, "m1", 2);
1555 }