1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <auth.h>
4219b2ee8SDavid du Colombier #include <fcall.h>
5219b2ee8SDavid du Colombier
6219b2ee8SDavid du Colombier #define LOGFILE "telco"
7219b2ee8SDavid du Colombier
8219b2ee8SDavid du Colombier /*
9219b2ee8SDavid du Colombier * Rather than reading /adm/users, which is a lot of work for
10219b2ee8SDavid du Colombier * a toy progdev, we assume all groups have the form
11219b2ee8SDavid du Colombier * NNN:user:user:
12219b2ee8SDavid du Colombier * meaning that each user is the leader of his own group.
13219b2ee8SDavid du Colombier */
14219b2ee8SDavid du Colombier
15219b2ee8SDavid du Colombier enum
16219b2ee8SDavid du Colombier {
17219b2ee8SDavid du Colombier OPERM = 0x3, /* mask of all permission types in open mode */
18219b2ee8SDavid du Colombier Ndev = 8,
19219b2ee8SDavid du Colombier Nreq = (Ndev*3)/2,
20219b2ee8SDavid du Colombier Nrbuf = 32*1024,
21219b2ee8SDavid du Colombier };
22219b2ee8SDavid du Colombier
23219b2ee8SDavid du Colombier typedef struct Fid Fid;
24219b2ee8SDavid du Colombier typedef struct Dev Dev;
25219b2ee8SDavid du Colombier typedef struct Request Request;
26219b2ee8SDavid du Colombier typedef struct Type Type;
27219b2ee8SDavid du Colombier
28219b2ee8SDavid du Colombier struct Fid
29219b2ee8SDavid du Colombier {
30219b2ee8SDavid du Colombier Qid qid;
31219b2ee8SDavid du Colombier short busy;
32219b2ee8SDavid du Colombier short open;
33219b2ee8SDavid du Colombier int fid;
34219b2ee8SDavid du Colombier Fid *next;
35219b2ee8SDavid du Colombier char *user;
36219b2ee8SDavid du Colombier };
37219b2ee8SDavid du Colombier
38219b2ee8SDavid du Colombier struct Request
39219b2ee8SDavid du Colombier {
40219b2ee8SDavid du Colombier Request *next;
41219b2ee8SDavid du Colombier
42219b2ee8SDavid du Colombier Fid *fid;
43219b2ee8SDavid du Colombier ulong tag;
44219b2ee8SDavid du Colombier int count;
45219b2ee8SDavid du Colombier int flushed;
46219b2ee8SDavid du Colombier };
47219b2ee8SDavid du Colombier
48219b2ee8SDavid du Colombier struct Dev
49219b2ee8SDavid du Colombier {
50219b2ee8SDavid du Colombier Lock;
51219b2ee8SDavid du Colombier
52219b2ee8SDavid du Colombier /* device state */
53219b2ee8SDavid du Colombier int ctl; /* control fd */
54219b2ee8SDavid du Colombier int data; /* data fd */
55219b2ee8SDavid du Colombier char *path; /* to device */
56219b2ee8SDavid du Colombier Type *t;
57219b2ee8SDavid du Colombier Type *baset;
58219b2ee8SDavid du Colombier int speed;
59219b2ee8SDavid du Colombier int fclass;
60219b2ee8SDavid du Colombier
61219b2ee8SDavid du Colombier /* fs emulation */
62219b2ee8SDavid du Colombier int open;
63219b2ee8SDavid du Colombier long perm;
649a747e4fSDavid du Colombier char *name;
659a747e4fSDavid du Colombier char *user;
66219b2ee8SDavid du Colombier char msgbuf[128];
67219b2ee8SDavid du Colombier Request *r;
68219b2ee8SDavid du Colombier Request *rlast;
69219b2ee8SDavid du Colombier
70219b2ee8SDavid du Colombier /* input reader */
71219b2ee8SDavid du Colombier int monitoring; /* monitor pid */
72219b2ee8SDavid du Colombier char rbuf[Nrbuf];
73219b2ee8SDavid du Colombier char *rp;
74219b2ee8SDavid du Colombier char *wp;
75219b2ee8SDavid du Colombier long pid;
76219b2ee8SDavid du Colombier };
77219b2ee8SDavid du Colombier
78219b2ee8SDavid du Colombier enum
79219b2ee8SDavid du Colombier {
80219b2ee8SDavid du Colombier Devmask= (Ndev-1)<<8,
81219b2ee8SDavid du Colombier
82219b2ee8SDavid du Colombier Qlvl1= 0,
83219b2ee8SDavid du Colombier Qlvl2= 1,
84219b2ee8SDavid du Colombier Qclone= 2,
85219b2ee8SDavid du Colombier Qlvl3= 3,
86219b2ee8SDavid du Colombier Qdata= 4,
87219b2ee8SDavid du Colombier Qctl= 5,
88219b2ee8SDavid du Colombier
89219b2ee8SDavid du Colombier Pexec = 1,
90219b2ee8SDavid du Colombier Pwrite = 2,
91219b2ee8SDavid du Colombier Pread = 4,
92219b2ee8SDavid du Colombier Pother = 1,
93219b2ee8SDavid du Colombier Pgroup = 8,
94219b2ee8SDavid du Colombier Powner = 64,
95219b2ee8SDavid du Colombier };
96219b2ee8SDavid du Colombier
97219b2ee8SDavid du Colombier char *names[] =
98219b2ee8SDavid du Colombier {
99219b2ee8SDavid du Colombier [Qlvl1] "/",
100219b2ee8SDavid du Colombier [Qlvl2] "telco",
101219b2ee8SDavid du Colombier [Qclone] "clone",
102219b2ee8SDavid du Colombier [Qlvl3] "",
103219b2ee8SDavid du Colombier [Qdata] "data",
104219b2ee8SDavid du Colombier [Qctl] "ctl",
105219b2ee8SDavid du Colombier };
106219b2ee8SDavid du Colombier
1079a747e4fSDavid du Colombier #define DEV(q) ((((ulong)(q).path)&Devmask)>>8)
1089a747e4fSDavid du Colombier #define TYPE(q) (((ulong)(q).path)&((1<<8)-1))
109219b2ee8SDavid du Colombier #define MKQID(t, i) ((((i)<<8)&Devmask) | (t))
110219b2ee8SDavid du Colombier
111219b2ee8SDavid du Colombier enum
112219b2ee8SDavid du Colombier {
113219b2ee8SDavid du Colombier /*
114219b2ee8SDavid du Colombier * modem specific commands
115219b2ee8SDavid du Colombier */
116219b2ee8SDavid du Colombier Cerrorcorrection = 0, /* error correction */
117219b2ee8SDavid du Colombier Ccompression, /* compression */
118219b2ee8SDavid du Colombier Cflowctl, /* CTS/RTS */
119219b2ee8SDavid du Colombier Crateadjust, /* follow line speed */
120219b2ee8SDavid du Colombier Cfclass2, /* set up for fax */
121219b2ee8SDavid du Colombier Cfclass0, /* set up for data */
122219b2ee8SDavid du Colombier Ncommand,
123219b2ee8SDavid du Colombier };
124219b2ee8SDavid du Colombier
125219b2ee8SDavid du Colombier struct Type
126219b2ee8SDavid du Colombier {
127219b2ee8SDavid du Colombier char *name;
128219b2ee8SDavid du Colombier char *ident; /* inquire request */
129219b2ee8SDavid du Colombier char *response; /* inquire response (strstr) */
130219b2ee8SDavid du Colombier char *basetype; /* name of base type */
131219b2ee8SDavid du Colombier
132219b2ee8SDavid du Colombier char *commands[Ncommand];
133219b2ee8SDavid du Colombier };
134219b2ee8SDavid du Colombier
135219b2ee8SDavid du Colombier /*
136219b2ee8SDavid du Colombier * Fax setup summary
137219b2ee8SDavid du Colombier *
138219b2ee8SDavid du Colombier * FCLASS=2 - set to service class 2, i.e., one where the fax handles timing
139219b2ee8SDavid du Colombier * FTBC=0 - ???
140219b2ee8SDavid du Colombier * FREL=1 - ???
141219b2ee8SDavid du Colombier * FCQ=1 - receive copy quality checking enabled
142219b2ee8SDavid du Colombier * FBOR=1 - set reversed bit order for phase C data
143219b2ee8SDavid du Colombier * FCR=1 - the DCE can receive message data, bit 10 in the DIS or
144219b2ee8SDavid du Colombier * DTC frame will be set
145219b2ee8SDavid du Colombier * FDIS=,3 - limit com speed to 9600 baud
146219b2ee8SDavid du Colombier */
147219b2ee8SDavid du Colombier
148219b2ee8SDavid du Colombier Type typetab[] =
149219b2ee8SDavid du Colombier {
150219b2ee8SDavid du Colombier { "Rockwell", 0, 0, 0,
151219b2ee8SDavid du Colombier "AT\\N7", /* auto reliable (V.42, fall back to MNP, to none) */
152219b2ee8SDavid du Colombier "AT%C1\\J0", /* negotiate for compression, don't change port baud rate */
153219b2ee8SDavid du Colombier "AT\\Q3", /* CTS/RTS flow control */
154219b2ee8SDavid du Colombier "AT\\J1",
155219b2ee8SDavid du Colombier "AT+FCLASS=2\rAT+FCR=1\r",
156219b2ee8SDavid du Colombier "AT+FCLASS=0",
157219b2ee8SDavid du Colombier },
158219b2ee8SDavid du Colombier
159219b2ee8SDavid du Colombier { "ATT2400", "ATI9", "E2400", "Rockwell",
160219b2ee8SDavid du Colombier "AT\\N3", /* auto reliable (MNP, fall back to none) */
161219b2ee8SDavid du Colombier 0,
162219b2ee8SDavid du Colombier 0,
163219b2ee8SDavid du Colombier 0,
164219b2ee8SDavid du Colombier 0,
165219b2ee8SDavid du Colombier 0,
166219b2ee8SDavid du Colombier },
167219b2ee8SDavid du Colombier
168219b2ee8SDavid du Colombier { "ATT14400", "ATI9", "E14400", "Rockwell",
169219b2ee8SDavid du Colombier 0,
170219b2ee8SDavid du Colombier 0,
171219b2ee8SDavid du Colombier 0,
172219b2ee8SDavid du Colombier 0,
173219b2ee8SDavid du Colombier 0,
174219b2ee8SDavid du Colombier 0,
175219b2ee8SDavid du Colombier },
176219b2ee8SDavid du Colombier
177219b2ee8SDavid du Colombier { "MT1432", "ATI2", "MT1432", 0,
178219b2ee8SDavid du Colombier "AT&E1", /* auto reliable (V.42, fall back to none) */
179219b2ee8SDavid du Colombier "AT&E15$BA0", /* negotiate for compression */
180219b2ee8SDavid du Colombier "AT&E4", /* CTS/RTS flow control */
181219b2ee8SDavid du Colombier "AT$BA1", /* don't change port baud rate */
182219b2ee8SDavid du Colombier "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1\rAT+FDIS=,3",
183219b2ee8SDavid du Colombier "AT+FCLASS=0",
184219b2ee8SDavid du Colombier },
185219b2ee8SDavid du Colombier
186219b2ee8SDavid du Colombier { "MT2834", "ATI2", "MT2834", "MT1432",
187219b2ee8SDavid du Colombier 0,
188219b2ee8SDavid du Colombier 0,
189219b2ee8SDavid du Colombier 0,
190219b2ee8SDavid du Colombier 0,
191219b2ee8SDavid du Colombier "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1",
192219b2ee8SDavid du Colombier 0,
193219b2ee8SDavid du Colombier },
194219b2ee8SDavid du Colombier
195219b2ee8SDavid du Colombier { "VOCAL", "ATI6", "144DPL+FAX", "Rockwell",
196219b2ee8SDavid du Colombier "AT\\N3", /* auto reliable (V.42, fall back to MNP, fall back to none) */
197219b2ee8SDavid du Colombier "AT%C3\\J0", /* negotiate for compression, don't change port baud rate */
198219b2ee8SDavid du Colombier 0,
199219b2ee8SDavid du Colombier 0,
200219b2ee8SDavid du Colombier "AT+FCLASS=2\rAT+FTBC=0\rAT+FREL=1\rAT+FCQ=1\rAT+FBOR=1\rAT+FCR=1",
201219b2ee8SDavid du Colombier "AT+FCLASS=0",
202219b2ee8SDavid du Colombier },
203219b2ee8SDavid du Colombier
204219b2ee8SDavid du Colombier { 0, },
205219b2ee8SDavid du Colombier };
206219b2ee8SDavid du Colombier
207219b2ee8SDavid du Colombier /*
208219b2ee8SDavid du Colombier * modem return codes
209219b2ee8SDavid du Colombier */
210219b2ee8SDavid du Colombier enum
211219b2ee8SDavid du Colombier {
212219b2ee8SDavid du Colombier Ok,
213219b2ee8SDavid du Colombier Success,
214219b2ee8SDavid du Colombier Failure,
215219b2ee8SDavid du Colombier Noise,
216219b2ee8SDavid du Colombier Found,
217219b2ee8SDavid du Colombier };
218219b2ee8SDavid du Colombier
219219b2ee8SDavid du Colombier /*
220219b2ee8SDavid du Colombier * modem return messages
221219b2ee8SDavid du Colombier */
222219b2ee8SDavid du Colombier typedef struct Msg Msg;
223219b2ee8SDavid du Colombier struct Msg
224219b2ee8SDavid du Colombier {
225219b2ee8SDavid du Colombier char *text;
226219b2ee8SDavid du Colombier int type;
227219b2ee8SDavid du Colombier };
228219b2ee8SDavid du Colombier
229219b2ee8SDavid du Colombier Msg msgs[] =
230219b2ee8SDavid du Colombier {
231219b2ee8SDavid du Colombier { "OK", Ok, },
232219b2ee8SDavid du Colombier { "NO CARRIER", Failure, },
233219b2ee8SDavid du Colombier { "ERROR", Failure, },
234219b2ee8SDavid du Colombier { "NO DIALTONE", Failure, },
235219b2ee8SDavid du Colombier { "BUSY", Failure, },
236219b2ee8SDavid du Colombier { "NO ANSWER", Failure, },
237219b2ee8SDavid du Colombier { "CONNECT", Success, },
238219b2ee8SDavid du Colombier { 0, 0 },
239219b2ee8SDavid du Colombier };
240219b2ee8SDavid du Colombier
241219b2ee8SDavid du Colombier Fid *fids;
242219b2ee8SDavid du Colombier Dev *dev;
243219b2ee8SDavid du Colombier int ndev;
244219b2ee8SDavid du Colombier int mfd[2];
2459a747e4fSDavid du Colombier char *user;
2469a747e4fSDavid du Colombier uchar mdata[8192+IOHDRSZ];
2479a747e4fSDavid du Colombier int messagesize = sizeof mdata;
248219b2ee8SDavid du Colombier Fcall thdr;
2499a747e4fSDavid du Colombier Fcall rhdr;
2509a747e4fSDavid du Colombier char errbuf[ERRMAX];
2519a747e4fSDavid du Colombier uchar statbuf[STATMAX];
252219b2ee8SDavid du Colombier int pulsed;
253219b2ee8SDavid du Colombier int verbose;
254219b2ee8SDavid du Colombier int maxspeed = 56000;
255219b2ee8SDavid du Colombier char *srcid = "plan9";
2567dd7cddfSDavid du Colombier int answer = 1;
257219b2ee8SDavid du Colombier
258219b2ee8SDavid du Colombier Fid *newfid(int);
2599a747e4fSDavid du Colombier int devstat(Dir*, uchar*, int);
2609a747e4fSDavid du Colombier int devgen(Qid, int, Dir*, uchar*, int);
261219b2ee8SDavid du Colombier void error(char*);
262219b2ee8SDavid du Colombier void io(void);
263219b2ee8SDavid du Colombier void *erealloc(void*, ulong);
264219b2ee8SDavid du Colombier void *emalloc(ulong);
265219b2ee8SDavid du Colombier void usage(void);
266219b2ee8SDavid du Colombier int perm(Fid*, Dev*, int);
267219b2ee8SDavid du Colombier void setspeed(Dev*, int);
268219b2ee8SDavid du Colombier int getspeed(char*, int);
269219b2ee8SDavid du Colombier char *dialout(Dev*, char*);
270219b2ee8SDavid du Colombier void onhook(Dev*);
271219b2ee8SDavid du Colombier int readmsg(Dev*, int, char*);
272219b2ee8SDavid du Colombier void monitor(Dev*);
273219b2ee8SDavid du Colombier int getinput(Dev*, char*, int);
274219b2ee8SDavid du Colombier void serve(Dev*);
275219b2ee8SDavid du Colombier void receiver(Dev*);
276219b2ee8SDavid du Colombier char* modemtype(Dev*, int, int);
277219b2ee8SDavid du Colombier
278219b2ee8SDavid du Colombier
2799a747e4fSDavid du Colombier char *rflush(Fid*), *rversion(Fid*),
2809a747e4fSDavid du Colombier *rattach(Fid*), *rauth(Fid*), *rwalk(Fid*),
2819a747e4fSDavid du Colombier *ropen(Fid*), *rcreate(Fid*),
282219b2ee8SDavid du Colombier *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
2839a747e4fSDavid du Colombier *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
284219b2ee8SDavid du Colombier
285219b2ee8SDavid du Colombier char *(*fcalls[])(Fid*) = {
286219b2ee8SDavid du Colombier [Tflush] rflush,
2879a747e4fSDavid du Colombier [Tversion] rversion,
288219b2ee8SDavid du Colombier [Tattach] rattach,
2899a747e4fSDavid du Colombier [Tauth] rauth,
290219b2ee8SDavid du Colombier [Twalk] rwalk,
291219b2ee8SDavid du Colombier [Topen] ropen,
292219b2ee8SDavid du Colombier [Tcreate] rcreate,
293219b2ee8SDavid du Colombier [Tread] rread,
294219b2ee8SDavid du Colombier [Twrite] rwrite,
295219b2ee8SDavid du Colombier [Tclunk] rclunk,
296219b2ee8SDavid du Colombier [Tremove] rremove,
297219b2ee8SDavid du Colombier [Tstat] rstat,
298219b2ee8SDavid du Colombier [Twstat] rwstat,
299219b2ee8SDavid du Colombier };
300219b2ee8SDavid du Colombier
301219b2ee8SDavid du Colombier char Eperm[] = "permission denied";
302219b2ee8SDavid du Colombier char Enotdir[] = "not a directory";
303219b2ee8SDavid du Colombier char Enotexist[] = "file does not exist";
304219b2ee8SDavid du Colombier char Ebadaddr[] = "bad address";
305219b2ee8SDavid du Colombier char Eattn[] = "can't get modem's attention";
306219b2ee8SDavid du Colombier char Edial[] = "can't dial";
3079a747e4fSDavid du Colombier char Enoauth[] = "telco: authentication not required";
308219b2ee8SDavid du Colombier char Eisopen[] = "file already open for I/O";
309219b2ee8SDavid du Colombier char Enodev[] = "no free modems";
310219b2ee8SDavid du Colombier char Enostream[] = "stream closed prematurely";
311219b2ee8SDavid du Colombier
312219b2ee8SDavid du Colombier void
usage(void)313219b2ee8SDavid du Colombier usage(void)
314219b2ee8SDavid du Colombier {
315219b2ee8SDavid du Colombier fprint(2, "usage: %s [-vp] [-i srcid] dev ...\n", argv0);
316219b2ee8SDavid du Colombier exits("usage");
317219b2ee8SDavid du Colombier }
318219b2ee8SDavid du Colombier
319219b2ee8SDavid du Colombier void
notifyf(void * a,char * s)320219b2ee8SDavid du Colombier notifyf(void *a, char *s)
321219b2ee8SDavid du Colombier {
322219b2ee8SDavid du Colombier USED(a);
323219b2ee8SDavid du Colombier if(strncmp(s, "interrupt", 9) == 0)
324219b2ee8SDavid du Colombier noted(NCONT);
325219b2ee8SDavid du Colombier noted(NDFLT);
326219b2ee8SDavid du Colombier }
327219b2ee8SDavid du Colombier
328219b2ee8SDavid du Colombier void
main(int argc,char * argv[])329219b2ee8SDavid du Colombier main(int argc, char *argv[])
330219b2ee8SDavid du Colombier {
331219b2ee8SDavid du Colombier int p[2];
332219b2ee8SDavid du Colombier int fd;
333219b2ee8SDavid du Colombier char buf[10];
334219b2ee8SDavid du Colombier Dev *d;
335219b2ee8SDavid du Colombier
336219b2ee8SDavid du Colombier ARGBEGIN{
337219b2ee8SDavid du Colombier case 'p':
338219b2ee8SDavid du Colombier pulsed = 1;
339219b2ee8SDavid du Colombier break;
340219b2ee8SDavid du Colombier case 'v':
341219b2ee8SDavid du Colombier verbose = 1;
342219b2ee8SDavid du Colombier break;
343219b2ee8SDavid du Colombier case 'i':
344219b2ee8SDavid du Colombier srcid = ARGF();
345219b2ee8SDavid du Colombier break;
346219b2ee8SDavid du Colombier case 's':
347219b2ee8SDavid du Colombier maxspeed = atoi(ARGF());
348219b2ee8SDavid du Colombier break;
3497dd7cddfSDavid du Colombier case 'n':
3507dd7cddfSDavid du Colombier answer = 0;
3517dd7cddfSDavid du Colombier break;
352219b2ee8SDavid du Colombier default:
353219b2ee8SDavid du Colombier usage();
354219b2ee8SDavid du Colombier }ARGEND
355219b2ee8SDavid du Colombier
356219b2ee8SDavid du Colombier if(argc == 0)
357219b2ee8SDavid du Colombier usage();
358219b2ee8SDavid du Colombier if(argc > Ndev)
359219b2ee8SDavid du Colombier argc = Ndev;
360219b2ee8SDavid du Colombier
361219b2ee8SDavid du Colombier if(pipe(p) < 0)
362219b2ee8SDavid du Colombier error("pipe failed");
363219b2ee8SDavid du Colombier
364219b2ee8SDavid du Colombier notify(notifyf);
3659a747e4fSDavid du Colombier fmtinstall('F', fcallfmt);
3669a747e4fSDavid du Colombier user = getuser();
367219b2ee8SDavid du Colombier
36859cc4ca5SDavid du Colombier switch(rfork(RFFDG|RFPROC|RFREND|RFNOTEG)){
369219b2ee8SDavid du Colombier case -1:
370219b2ee8SDavid du Colombier error("fork");
371219b2ee8SDavid du Colombier case 0:
372219b2ee8SDavid du Colombier close(p[1]);
373219b2ee8SDavid du Colombier mfd[0] = mfd[1] = p[0];
374219b2ee8SDavid du Colombier break;
375219b2ee8SDavid du Colombier default:
376219b2ee8SDavid du Colombier close(p[0]);
377219b2ee8SDavid du Colombier fd = create("/srv/telco", OWRITE, 0666);
378219b2ee8SDavid du Colombier if(fd < 0)
379219b2ee8SDavid du Colombier error("create of /srv/telco failed");
380219b2ee8SDavid du Colombier sprint(buf, "%d", p[1]);
381219b2ee8SDavid du Colombier if(write(fd, buf, strlen(buf)) < 0)
382219b2ee8SDavid du Colombier error("writing /srv/telco");
383219b2ee8SDavid du Colombier close(fd);
3849a747e4fSDavid du Colombier if(mount(p[1], -1, "/net", MBEFORE, "") < 0)
385219b2ee8SDavid du Colombier error("mount failed");
386219b2ee8SDavid du Colombier exits(0);
387219b2ee8SDavid du Colombier }
388219b2ee8SDavid du Colombier
3897dd7cddfSDavid du Colombier dev = mallocz(argc*sizeof(Dev), 1);
390219b2ee8SDavid du Colombier for(ndev = 0; ndev < argc; ndev++){
391219b2ee8SDavid du Colombier d = &dev[ndev];
392219b2ee8SDavid du Colombier d->path = argv[ndev];
393219b2ee8SDavid du Colombier d->rp = d->wp = d->rbuf;
394219b2ee8SDavid du Colombier monitor(d);
395219b2ee8SDavid du Colombier d->open++;
396219b2ee8SDavid du Colombier onhook(d);
397219b2ee8SDavid du Colombier d->open--;
398219b2ee8SDavid du Colombier }
399219b2ee8SDavid du Colombier
400219b2ee8SDavid du Colombier io();
401219b2ee8SDavid du Colombier }
402219b2ee8SDavid du Colombier
403219b2ee8SDavid du Colombier /*
404219b2ee8SDavid du Colombier * generate a stat structure for a qid
405219b2ee8SDavid du Colombier */
4069a747e4fSDavid du Colombier int
devstat(Dir * dir,uchar * buf,int nbuf)4079a747e4fSDavid du Colombier devstat(Dir *dir, uchar *buf, int nbuf)
408219b2ee8SDavid du Colombier {
409219b2ee8SDavid du Colombier Dev *d;
410219b2ee8SDavid du Colombier int t;
4119a747e4fSDavid du Colombier static char tmp[10][32];
4129a747e4fSDavid du Colombier static int ntmp;
413219b2ee8SDavid du Colombier
414219b2ee8SDavid du Colombier t = TYPE(dir->qid);
415219b2ee8SDavid du Colombier if(t != Qlvl3)
4169a747e4fSDavid du Colombier dir->name = names[t];
4179a747e4fSDavid du Colombier else{
4189a747e4fSDavid du Colombier dir->name = tmp[ntmp % nelem(tmp)];
4197dd7cddfSDavid du Colombier sprint(dir->name, "%lud", DEV(dir->qid));
4209a747e4fSDavid du Colombier ntmp++;
4219a747e4fSDavid du Colombier }
422219b2ee8SDavid du Colombier dir->mode = 0755;
4239a747e4fSDavid du Colombier dir->uid = user;
4249a747e4fSDavid du Colombier dir->gid = user;
4259a747e4fSDavid du Colombier dir->muid = user;
426219b2ee8SDavid du Colombier if(t >= Qlvl3){
427219b2ee8SDavid du Colombier d = &dev[DEV(dir->qid)];
428219b2ee8SDavid du Colombier if(d->open){
429219b2ee8SDavid du Colombier dir->mode = d->perm;
4309a747e4fSDavid du Colombier dir->uid = d->user;
431219b2ee8SDavid du Colombier }
432219b2ee8SDavid du Colombier }
4339a747e4fSDavid du Colombier if(dir->qid.type & QTDIR)
4349a747e4fSDavid du Colombier dir->mode |= DMDIR;
435219b2ee8SDavid du Colombier if(t == Qdata){
436219b2ee8SDavid du Colombier d = &dev[DEV(dir->qid)];
437219b2ee8SDavid du Colombier dir->length = d->wp - d->rp;
438219b2ee8SDavid du Colombier if(dir->length < 0)
439219b2ee8SDavid du Colombier dir->length += Nrbuf;
440219b2ee8SDavid du Colombier } else
441219b2ee8SDavid du Colombier dir->length = 0;
442219b2ee8SDavid du Colombier dir->atime = time(0);
443219b2ee8SDavid du Colombier dir->mtime = dir->atime;
444219b2ee8SDavid du Colombier if(buf)
4459a747e4fSDavid du Colombier return convD2M(dir, buf, nbuf);
4469a747e4fSDavid du Colombier return 0;
447219b2ee8SDavid du Colombier }
448219b2ee8SDavid du Colombier
449219b2ee8SDavid du Colombier /*
450219b2ee8SDavid du Colombier * enumerate file's we can walk to from q
451219b2ee8SDavid du Colombier */
452219b2ee8SDavid du Colombier int
devgen(Qid q,int i,Dir * d,uchar * buf,int nbuf)4539a747e4fSDavid du Colombier devgen(Qid q, int i, Dir *d, uchar *buf, int nbuf)
454219b2ee8SDavid du Colombier {
455219b2ee8SDavid du Colombier static ulong v;
456219b2ee8SDavid du Colombier
457219b2ee8SDavid du Colombier d->qid.vers = v++;
458219b2ee8SDavid du Colombier switch(TYPE(q)){
459219b2ee8SDavid du Colombier case Qlvl1:
460219b2ee8SDavid du Colombier if(i != 0)
461219b2ee8SDavid du Colombier return -1;
4629a747e4fSDavid du Colombier d->qid.type = QTDIR;
4639a747e4fSDavid du Colombier d->qid.path = Qlvl2;
464219b2ee8SDavid du Colombier break;
465219b2ee8SDavid du Colombier case Qlvl2:
466219b2ee8SDavid du Colombier switch(i){
467219b2ee8SDavid du Colombier case -1:
4689a747e4fSDavid du Colombier d->qid.type = QTDIR;
4699a747e4fSDavid du Colombier d->qid.path = Qlvl1;
470219b2ee8SDavid du Colombier break;
471219b2ee8SDavid du Colombier case 0:
4729a747e4fSDavid du Colombier d->qid.type = QTFILE;
473219b2ee8SDavid du Colombier d->qid.path = Qclone;
474219b2ee8SDavid du Colombier break;
475219b2ee8SDavid du Colombier default:
476219b2ee8SDavid du Colombier if(i > ndev)
477219b2ee8SDavid du Colombier return -1;
4789a747e4fSDavid du Colombier d->qid.type = QTDIR;
4799a747e4fSDavid du Colombier d->qid.path = MKQID(Qlvl3, i-1);
480219b2ee8SDavid du Colombier break;
481219b2ee8SDavid du Colombier }
482219b2ee8SDavid du Colombier break;
483219b2ee8SDavid du Colombier case Qlvl3:
484219b2ee8SDavid du Colombier switch(i){
485219b2ee8SDavid du Colombier case -1:
4869a747e4fSDavid du Colombier d->qid.type = QTDIR;
4879a747e4fSDavid du Colombier d->qid.path = Qlvl2;
488219b2ee8SDavid du Colombier break;
489219b2ee8SDavid du Colombier case 0:
4909a747e4fSDavid du Colombier d->qid.type = QTFILE;
491219b2ee8SDavid du Colombier d->qid.path = MKQID(Qdata, DEV(q));
492219b2ee8SDavid du Colombier break;
493219b2ee8SDavid du Colombier case 1:
4949a747e4fSDavid du Colombier d->qid.type = QTFILE;
495219b2ee8SDavid du Colombier d->qid.path = MKQID(Qctl, DEV(q));
496219b2ee8SDavid du Colombier break;
497219b2ee8SDavid du Colombier default:
498219b2ee8SDavid du Colombier return -1;
499219b2ee8SDavid du Colombier }
500219b2ee8SDavid du Colombier break;
501219b2ee8SDavid du Colombier default:
502219b2ee8SDavid du Colombier return -1;
503219b2ee8SDavid du Colombier }
5049a747e4fSDavid du Colombier return devstat(d, buf, nbuf);
505219b2ee8SDavid du Colombier }
506219b2ee8SDavid du Colombier
507219b2ee8SDavid du Colombier char*
rversion(Fid *)5089a747e4fSDavid du Colombier rversion(Fid *)
509219b2ee8SDavid du Colombier {
510219b2ee8SDavid du Colombier Fid *f;
511219b2ee8SDavid du Colombier
512219b2ee8SDavid du Colombier for(f = fids; f; f = f->next)
513219b2ee8SDavid du Colombier if(f->busy)
514219b2ee8SDavid du Colombier rclunk(f);
5159a747e4fSDavid du Colombier
5169a747e4fSDavid du Colombier if(thdr.msize < 256)
5179a747e4fSDavid du Colombier return "version: message size too small";
5189a747e4fSDavid du Colombier messagesize = thdr.msize;
5199a747e4fSDavid du Colombier if(messagesize > sizeof mdata)
5209a747e4fSDavid du Colombier messagesize = sizeof mdata;
5219a747e4fSDavid du Colombier rhdr.msize = messagesize;
5229a747e4fSDavid du Colombier if(strncmp(thdr.version, "9P2000", 6) != 0)
5239a747e4fSDavid du Colombier return "unrecognized 9P version";
5249a747e4fSDavid du Colombier rhdr.version = "9P2000";
525219b2ee8SDavid du Colombier return 0;
526219b2ee8SDavid du Colombier }
527219b2ee8SDavid du Colombier
528219b2ee8SDavid du Colombier char*
rflush(Fid * f)529219b2ee8SDavid du Colombier rflush(Fid *f)
530219b2ee8SDavid du Colombier {
531219b2ee8SDavid du Colombier Request *r, **l;
532219b2ee8SDavid du Colombier Dev *d;
533219b2ee8SDavid du Colombier
534219b2ee8SDavid du Colombier USED(f);
535219b2ee8SDavid du Colombier for(d = dev; d < &dev[ndev]; d++){
536219b2ee8SDavid du Colombier lock(d);
537219b2ee8SDavid du Colombier for(l = &d->r; r = *l; l = &r->next)
5389a747e4fSDavid du Colombier if(r->tag == thdr.oldtag){
539219b2ee8SDavid du Colombier *l = r->next;
540219b2ee8SDavid du Colombier free(r);
541219b2ee8SDavid du Colombier break;
542219b2ee8SDavid du Colombier }
543219b2ee8SDavid du Colombier unlock(d);
544219b2ee8SDavid du Colombier }
545219b2ee8SDavid du Colombier return 0;
546219b2ee8SDavid du Colombier }
547219b2ee8SDavid du Colombier
548219b2ee8SDavid du Colombier char *
rauth(Fid * f)549219b2ee8SDavid du Colombier rauth(Fid *f)
550219b2ee8SDavid du Colombier {
551219b2ee8SDavid du Colombier USED(f);
552219b2ee8SDavid du Colombier return Enoauth;
553219b2ee8SDavid du Colombier }
554219b2ee8SDavid du Colombier
555219b2ee8SDavid du Colombier char*
rattach(Fid * f)556219b2ee8SDavid du Colombier rattach(Fid *f)
557219b2ee8SDavid du Colombier {
558219b2ee8SDavid du Colombier f->busy = 1;
5599a747e4fSDavid du Colombier f->qid.type = QTDIR;
5609a747e4fSDavid du Colombier f->qid.path = Qlvl1;
561219b2ee8SDavid du Colombier f->qid.vers = 0;
5629a747e4fSDavid du Colombier rhdr.qid = f->qid;
5639a747e4fSDavid du Colombier if(thdr.uname[0])
5649a747e4fSDavid du Colombier f->user = strdup(thdr.uname);
565219b2ee8SDavid du Colombier else
566219b2ee8SDavid du Colombier f->user = "none";
567219b2ee8SDavid du Colombier return 0;
568219b2ee8SDavid du Colombier }
569219b2ee8SDavid du Colombier
570219b2ee8SDavid du Colombier char*
rwalk(Fid * f)5719a747e4fSDavid du Colombier rwalk(Fid *f)
572219b2ee8SDavid du Colombier {
573219b2ee8SDavid du Colombier Fid *nf;
5749a747e4fSDavid du Colombier int i, nqid;
5759a747e4fSDavid du Colombier char *name, *err;
5769a747e4fSDavid du Colombier Dir dir;
5779a747e4fSDavid du Colombier Qid q;
578219b2ee8SDavid du Colombier
5799a747e4fSDavid du Colombier nf = nil;
5809a747e4fSDavid du Colombier if(thdr.fid != thdr.newfid){
581219b2ee8SDavid du Colombier if(f->open)
582219b2ee8SDavid du Colombier return Eisopen;
583219b2ee8SDavid du Colombier if(f->busy == 0)
584219b2ee8SDavid du Colombier return Enotexist;
5859a747e4fSDavid du Colombier nf = newfid(thdr.newfid);
586219b2ee8SDavid du Colombier nf->busy = 1;
587219b2ee8SDavid du Colombier nf->open = 0;
588219b2ee8SDavid du Colombier nf->qid = f->qid;
589219b2ee8SDavid du Colombier nf->user = strdup(f->user);
5909a747e4fSDavid du Colombier f = nf; /* walk f */
591219b2ee8SDavid du Colombier }
592219b2ee8SDavid du Colombier
5939a747e4fSDavid du Colombier err = nil;
594219b2ee8SDavid du Colombier dir.qid = f->qid;
5959a747e4fSDavid du Colombier nqid = 0;
5969a747e4fSDavid du Colombier if(thdr.nwname > 0){
5979a747e4fSDavid du Colombier for(; nqid < thdr.nwname; nqid++) {
5989a747e4fSDavid du Colombier if((dir.qid.type & QTDIR) == 0){
5999a747e4fSDavid du Colombier err = Enotdir;
6009a747e4fSDavid du Colombier break;
6019a747e4fSDavid du Colombier }
6029a747e4fSDavid du Colombier name = thdr.wname[nqid];
6039a747e4fSDavid du Colombier if(strcmp(name, ".") == 0){
6049a747e4fSDavid du Colombier /* nothing to do */
6059a747e4fSDavid du Colombier }else if(strcmp(name, "..") == 0) {
6069a747e4fSDavid du Colombier if(devgen(f->qid, -1, &dir, 0, 0) < 0)
6079a747e4fSDavid du Colombier break;
6089a747e4fSDavid du Colombier }
6099a747e4fSDavid du Colombier else{
6109a747e4fSDavid du Colombier q = dir.qid;
6119a747e4fSDavid du Colombier for(i = 0;; i++){
6129a747e4fSDavid du Colombier if(devgen(q, i, &dir, 0, 0) < 0)
6139a747e4fSDavid du Colombier goto Out;
614219b2ee8SDavid du Colombier if(strcmp(name, dir.name) == 0)
615219b2ee8SDavid du Colombier break;
616219b2ee8SDavid du Colombier }
6179a747e4fSDavid du Colombier }
6189a747e4fSDavid du Colombier rhdr.wqid[nqid] = dir.qid;
6199a747e4fSDavid du Colombier }
6209a747e4fSDavid du Colombier Out:
6219a747e4fSDavid du Colombier if(nqid == 0 && err == nil)
6229a747e4fSDavid du Colombier err = Enotexist;
6239a747e4fSDavid du Colombier if(nf != nil && thdr.fid != thdr.newfid && nqid < thdr.nwname)
6249a747e4fSDavid du Colombier rclunk(nf);
625219b2ee8SDavid du Colombier }
626219b2ee8SDavid du Colombier
6279a747e4fSDavid du Colombier rhdr.nwqid = nqid;
6289a747e4fSDavid du Colombier if(nqid > 0 && nqid == thdr.nwname)
6299a747e4fSDavid du Colombier f->qid = dir.qid;
630219b2ee8SDavid du Colombier return err;
631219b2ee8SDavid du Colombier }
632219b2ee8SDavid du Colombier
633219b2ee8SDavid du Colombier char *
ropen(Fid * f)634219b2ee8SDavid du Colombier ropen(Fid *f)
635219b2ee8SDavid du Colombier {
636219b2ee8SDavid du Colombier Dev *d;
637219b2ee8SDavid du Colombier int mode, t;
638219b2ee8SDavid du Colombier
639219b2ee8SDavid du Colombier if(f->open)
640219b2ee8SDavid du Colombier return Eisopen;
6419a747e4fSDavid du Colombier mode = thdr.mode;
642219b2ee8SDavid du Colombier mode &= OPERM;
6439a747e4fSDavid du Colombier if(f->qid.type & QTDIR){
644219b2ee8SDavid du Colombier if(mode != OREAD)
645219b2ee8SDavid du Colombier return Eperm;
6469a747e4fSDavid du Colombier rhdr.qid = f->qid;
647219b2ee8SDavid du Colombier return 0;
648219b2ee8SDavid du Colombier }
649219b2ee8SDavid du Colombier if(mode==OEXEC)
650219b2ee8SDavid du Colombier return Eperm;
651219b2ee8SDavid du Colombier t = TYPE(f->qid);
652219b2ee8SDavid du Colombier if(t == Qclone){
653219b2ee8SDavid du Colombier for(d = dev; d < &dev[ndev]; d++)
654219b2ee8SDavid du Colombier if(d->open == 0)
655219b2ee8SDavid du Colombier break;
656219b2ee8SDavid du Colombier if(d == &dev[ndev])
657219b2ee8SDavid du Colombier return Enodev;
658219b2ee8SDavid du Colombier f->qid.path = MKQID(Qctl, d-dev);
659219b2ee8SDavid du Colombier t = Qctl;
660219b2ee8SDavid du Colombier }
661219b2ee8SDavid du Colombier switch(t){
662219b2ee8SDavid du Colombier case Qdata:
663219b2ee8SDavid du Colombier case Qctl:
664219b2ee8SDavid du Colombier d = &dev[DEV(f->qid)];
665219b2ee8SDavid du Colombier if(d->open == 0){
6669a747e4fSDavid du Colombier d->user = strdup(f->user);
667219b2ee8SDavid du Colombier d->perm = 0660;
668219b2ee8SDavid du Colombier }else {
669219b2ee8SDavid du Colombier if(mode==OWRITE || mode==ORDWR)
670219b2ee8SDavid du Colombier if(!perm(f, d, Pwrite))
671219b2ee8SDavid du Colombier return Eperm;
672219b2ee8SDavid du Colombier if(mode==OREAD || mode==ORDWR)
673219b2ee8SDavid du Colombier if(!perm(f, d, Pread))
674219b2ee8SDavid du Colombier return Eperm;
675219b2ee8SDavid du Colombier }
676219b2ee8SDavid du Colombier d->open++;
677219b2ee8SDavid du Colombier break;
678219b2ee8SDavid du Colombier }
6799a747e4fSDavid du Colombier rhdr.qid = f->qid;
6809a747e4fSDavid du Colombier rhdr.iounit = messagesize - IOHDRSZ;
681219b2ee8SDavid du Colombier f->open = 1;
682219b2ee8SDavid du Colombier return 0;
683219b2ee8SDavid du Colombier }
684219b2ee8SDavid du Colombier
685219b2ee8SDavid du Colombier char *
rcreate(Fid * f)686219b2ee8SDavid du Colombier rcreate(Fid *f)
687219b2ee8SDavid du Colombier {
688219b2ee8SDavid du Colombier USED(f);
689219b2ee8SDavid du Colombier return Eperm;
690219b2ee8SDavid du Colombier }
691219b2ee8SDavid du Colombier
692219b2ee8SDavid du Colombier /*
693219b2ee8SDavid du Colombier * intercept a note
694219b2ee8SDavid du Colombier */
695219b2ee8SDavid du Colombier void
takeanote(void * u,char * note)696219b2ee8SDavid du Colombier takeanote(void *u, char *note)
697219b2ee8SDavid du Colombier {
698219b2ee8SDavid du Colombier USED(u);
699219b2ee8SDavid du Colombier if(strstr(note, "flushed"))
700219b2ee8SDavid du Colombier noted(NCONT);
701219b2ee8SDavid du Colombier noted(NDFLT);
702219b2ee8SDavid du Colombier }
703219b2ee8SDavid du Colombier
704219b2ee8SDavid du Colombier char*
rread(Fid * f)705219b2ee8SDavid du Colombier rread(Fid *f)
706219b2ee8SDavid du Colombier {
707219b2ee8SDavid du Colombier char *buf;
7089a747e4fSDavid du Colombier long off, start;
7099a747e4fSDavid du Colombier int i, m, n, cnt, t;
710219b2ee8SDavid du Colombier Dir dir;
711219b2ee8SDavid du Colombier char num[32];
712219b2ee8SDavid du Colombier Dev *d;
713219b2ee8SDavid du Colombier Request *r;
714219b2ee8SDavid du Colombier
715219b2ee8SDavid du Colombier n = 0;
7169a747e4fSDavid du Colombier rhdr.count = 0;
7179a747e4fSDavid du Colombier off = thdr.offset;
7189a747e4fSDavid du Colombier cnt = thdr.count;
7199a747e4fSDavid du Colombier buf = rhdr.data;
720219b2ee8SDavid du Colombier t = TYPE(f->qid);
721219b2ee8SDavid du Colombier switch(t){
722219b2ee8SDavid du Colombier default:
7239a747e4fSDavid du Colombier start = 0;
7249a747e4fSDavid du Colombier for(i = 0; n < cnt; i++){
7259a747e4fSDavid du Colombier m = devgen(f->qid, i, &dir, (uchar*)buf+n, cnt-n);
7269a747e4fSDavid du Colombier if(m <= BIT16SZ)
727219b2ee8SDavid du Colombier break;
7289a747e4fSDavid du Colombier if(start >= off)
7299a747e4fSDavid du Colombier n += m;
7309a747e4fSDavid du Colombier start += m;
731219b2ee8SDavid du Colombier }
732219b2ee8SDavid du Colombier break;
733219b2ee8SDavid du Colombier case Qctl:
7347dd7cddfSDavid du Colombier i = sprint(num, "%lud", DEV(f->qid));
735219b2ee8SDavid du Colombier if(off < i){
736219b2ee8SDavid du Colombier n = cnt;
737219b2ee8SDavid du Colombier if(off + n > i)
738219b2ee8SDavid du Colombier n = i - off;
739219b2ee8SDavid du Colombier memmove(buf, num + off, n);
740219b2ee8SDavid du Colombier } else
741219b2ee8SDavid du Colombier n = 0;
742219b2ee8SDavid du Colombier break;
743219b2ee8SDavid du Colombier case Qdata:
744219b2ee8SDavid du Colombier d = &dev[DEV(f->qid)];
7457dd7cddfSDavid du Colombier r = mallocz(sizeof(Request), 1);
7469a747e4fSDavid du Colombier r->tag = thdr.tag;
7479a747e4fSDavid du Colombier r->count = thdr.count;
748219b2ee8SDavid du Colombier r->fid = f;
749219b2ee8SDavid du Colombier r->flushed = 0;
750219b2ee8SDavid du Colombier lock(d);
751219b2ee8SDavid du Colombier if(d->r)
752219b2ee8SDavid du Colombier d->rlast->next = r;
753219b2ee8SDavid du Colombier else
754219b2ee8SDavid du Colombier d->r = r;
755219b2ee8SDavid du Colombier d->rlast = r;
756219b2ee8SDavid du Colombier serve(d);
757219b2ee8SDavid du Colombier unlock(d);
758219b2ee8SDavid du Colombier return "";
759219b2ee8SDavid du Colombier }
7609a747e4fSDavid du Colombier rhdr.count = n;
761219b2ee8SDavid du Colombier return 0;
762219b2ee8SDavid du Colombier }
763219b2ee8SDavid du Colombier
764219b2ee8SDavid du Colombier char *cmsg = "connect ";
765219b2ee8SDavid du Colombier int clen;
766219b2ee8SDavid du Colombier
767219b2ee8SDavid du Colombier char*
rwrite(Fid * f)768219b2ee8SDavid du Colombier rwrite(Fid *f)
769219b2ee8SDavid du Colombier {
770219b2ee8SDavid du Colombier Dev *d;
771219b2ee8SDavid du Colombier ulong off;
772219b2ee8SDavid du Colombier int cnt;
773219b2ee8SDavid du Colombier char *cp;
774219b2ee8SDavid du Colombier char buf[64];
775219b2ee8SDavid du Colombier
7769a747e4fSDavid du Colombier off = thdr.offset;
7779a747e4fSDavid du Colombier cnt = thdr.count;
778219b2ee8SDavid du Colombier switch(TYPE(f->qid)){
779219b2ee8SDavid du Colombier default:
780219b2ee8SDavid du Colombier return "file is a directory";
781219b2ee8SDavid du Colombier case Qctl:
782219b2ee8SDavid du Colombier d = &dev[DEV(f->qid)];
783219b2ee8SDavid du Colombier clen = strlen(cmsg);
7849a747e4fSDavid du Colombier if(cnt < clen || strncmp(thdr.data, cmsg, clen) != 0){
785219b2ee8SDavid du Colombier /*
786219b2ee8SDavid du Colombier * send control message to real control file
787219b2ee8SDavid du Colombier */
7889a747e4fSDavid du Colombier if(seek(d->ctl, off, 0) < 0 || write(d->ctl, thdr.data, cnt) < 0){
7899a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf);
790219b2ee8SDavid du Colombier return errbuf;
791219b2ee8SDavid du Colombier }
792219b2ee8SDavid du Colombier } else {
793219b2ee8SDavid du Colombier /*
794219b2ee8SDavid du Colombier * connect
795219b2ee8SDavid du Colombier */
796219b2ee8SDavid du Colombier cnt -= clen;
797219b2ee8SDavid du Colombier if(cnt >= sizeof(buf))
798219b2ee8SDavid du Colombier cnt = sizeof(buf) - 1;
799219b2ee8SDavid du Colombier if(cnt < 0)
800219b2ee8SDavid du Colombier return Ebadaddr;
8019a747e4fSDavid du Colombier strncpy(buf, &thdr.data[clen], cnt);
802219b2ee8SDavid du Colombier buf[cnt] = 0;
803219b2ee8SDavid du Colombier cp = dialout(d, buf);
804219b2ee8SDavid du Colombier if(cp)
805219b2ee8SDavid du Colombier return cp;
806219b2ee8SDavid du Colombier }
8079a747e4fSDavid du Colombier rhdr.count = cnt;
808219b2ee8SDavid du Colombier break;
809219b2ee8SDavid du Colombier case Qdata:
810219b2ee8SDavid du Colombier d = &dev[DEV(f->qid)];
8119a747e4fSDavid du Colombier if(write(d->data, thdr.data, cnt) < 0){
8129a747e4fSDavid du Colombier errstr(errbuf, sizeof errbuf);
813219b2ee8SDavid du Colombier return errbuf;
814219b2ee8SDavid du Colombier }
8159a747e4fSDavid du Colombier rhdr.count = cnt;
816219b2ee8SDavid du Colombier break;
817219b2ee8SDavid du Colombier }
818219b2ee8SDavid du Colombier return 0;
819219b2ee8SDavid du Colombier }
820219b2ee8SDavid du Colombier
821219b2ee8SDavid du Colombier char *
rclunk(Fid * f)822219b2ee8SDavid du Colombier rclunk(Fid *f)
823219b2ee8SDavid du Colombier {
824219b2ee8SDavid du Colombier Dev *d;
825219b2ee8SDavid du Colombier
826219b2ee8SDavid du Colombier if(f->open)
827219b2ee8SDavid du Colombier switch(TYPE(f->qid)){
828219b2ee8SDavid du Colombier case Qdata:
829219b2ee8SDavid du Colombier case Qctl:
830219b2ee8SDavid du Colombier d = &dev[DEV(f->qid)];
831219b2ee8SDavid du Colombier if(d->open == 1)
832219b2ee8SDavid du Colombier onhook(d);
833219b2ee8SDavid du Colombier d->open--;
834219b2ee8SDavid du Colombier break;
835219b2ee8SDavid du Colombier }
836219b2ee8SDavid du Colombier free(f->user);
837219b2ee8SDavid du Colombier f->busy = 0;
838219b2ee8SDavid du Colombier f->open = 0;
839219b2ee8SDavid du Colombier return 0;
840219b2ee8SDavid du Colombier }
841219b2ee8SDavid du Colombier
842219b2ee8SDavid du Colombier char *
rremove(Fid * f)843219b2ee8SDavid du Colombier rremove(Fid *f)
844219b2ee8SDavid du Colombier {
845219b2ee8SDavid du Colombier USED(f);
846219b2ee8SDavid du Colombier return Eperm;
847219b2ee8SDavid du Colombier }
848219b2ee8SDavid du Colombier
849219b2ee8SDavid du Colombier char *
rstat(Fid * f)850219b2ee8SDavid du Colombier rstat(Fid *f)
851219b2ee8SDavid du Colombier {
852219b2ee8SDavid du Colombier Dir d;
853219b2ee8SDavid du Colombier
854219b2ee8SDavid du Colombier d.qid = f->qid;
8559a747e4fSDavid du Colombier rhdr.stat = statbuf;
8569a747e4fSDavid du Colombier rhdr.nstat = devstat(&d, statbuf, sizeof statbuf);
857219b2ee8SDavid du Colombier return 0;
858219b2ee8SDavid du Colombier }
859219b2ee8SDavid du Colombier
860219b2ee8SDavid du Colombier char *
rwstat(Fid * f)861219b2ee8SDavid du Colombier rwstat(Fid *f)
862219b2ee8SDavid du Colombier {
863219b2ee8SDavid du Colombier Dev *d;
864219b2ee8SDavid du Colombier Dir dir;
865219b2ee8SDavid du Colombier
866219b2ee8SDavid du Colombier if(TYPE(f->qid) < Qlvl3)
867219b2ee8SDavid du Colombier return Eperm;
868219b2ee8SDavid du Colombier
8699a747e4fSDavid du Colombier convM2D(thdr.stat, thdr.nstat, &dir, rhdr.data); /* rhdr.data is a known place to scribble */
870219b2ee8SDavid du Colombier d = &dev[DEV(f->qid)];
871219b2ee8SDavid du Colombier
872219b2ee8SDavid du Colombier /*
873219b2ee8SDavid du Colombier * To change mode, must be owner
874219b2ee8SDavid du Colombier */
875219b2ee8SDavid du Colombier if(d->perm != dir.mode){
876219b2ee8SDavid du Colombier if(strcmp(f->user, d->user) != 0)
877219b2ee8SDavid du Colombier if(strcmp(f->user, user) != 0)
878219b2ee8SDavid du Colombier return Eperm;
879219b2ee8SDavid du Colombier }
880219b2ee8SDavid du Colombier
881219b2ee8SDavid du Colombier /* all ok; do it */
8829a747e4fSDavid du Colombier d->perm = dir.mode & ~DMDIR;
883219b2ee8SDavid du Colombier return 0;
884219b2ee8SDavid du Colombier }
885219b2ee8SDavid du Colombier
886219b2ee8SDavid du Colombier Fid *
newfid(int fid)887219b2ee8SDavid du Colombier newfid(int fid)
888219b2ee8SDavid du Colombier {
889219b2ee8SDavid du Colombier Fid *f, *ff;
890219b2ee8SDavid du Colombier
891219b2ee8SDavid du Colombier ff = 0;
892219b2ee8SDavid du Colombier for(f = fids; f; f = f->next)
893219b2ee8SDavid du Colombier if(f->fid == fid)
894219b2ee8SDavid du Colombier return f;
895219b2ee8SDavid du Colombier else if(!ff && !f->busy)
896219b2ee8SDavid du Colombier ff = f;
897219b2ee8SDavid du Colombier if(ff){
898219b2ee8SDavid du Colombier ff->fid = fid;
899219b2ee8SDavid du Colombier return ff;
900219b2ee8SDavid du Colombier }
901219b2ee8SDavid du Colombier f = emalloc(sizeof *f);
902219b2ee8SDavid du Colombier f->fid = fid;
903219b2ee8SDavid du Colombier f->next = fids;
904219b2ee8SDavid du Colombier fids = f;
905219b2ee8SDavid du Colombier return f;
906219b2ee8SDavid du Colombier }
907219b2ee8SDavid du Colombier
908219b2ee8SDavid du Colombier /*
909219b2ee8SDavid du Colombier * read fs requests and dispatch them
910219b2ee8SDavid du Colombier */
911219b2ee8SDavid du Colombier void
io(void)912219b2ee8SDavid du Colombier io(void)
913219b2ee8SDavid du Colombier {
914219b2ee8SDavid du Colombier char *err;
915219b2ee8SDavid du Colombier int n;
916219b2ee8SDavid du Colombier
917219b2ee8SDavid du Colombier for(;;){
918219b2ee8SDavid du Colombier /*
919219b2ee8SDavid du Colombier * reading from a pipe or a network device
920219b2ee8SDavid du Colombier * will give an error after a few eof reads
921219b2ee8SDavid du Colombier * however, we cannot tell the difference
922219b2ee8SDavid du Colombier * between a zero-length read and an interrupt
923219b2ee8SDavid du Colombier * on the processes writing to us,
924219b2ee8SDavid du Colombier * so we wait for the error
925219b2ee8SDavid du Colombier */
9269a747e4fSDavid du Colombier n = read9pmsg(mfd[0], mdata, messagesize);
927219b2ee8SDavid du Colombier if(n == 0)
928219b2ee8SDavid du Colombier continue;
929219b2ee8SDavid du Colombier if(n < 0)
930219b2ee8SDavid du Colombier error("mount read");
9319a747e4fSDavid du Colombier if(convM2S(mdata, n, &thdr) != n)
9329a747e4fSDavid du Colombier error("convM2S error");
933219b2ee8SDavid du Colombier
9349a747e4fSDavid du Colombier rhdr.data = (char*)mdata + IOHDRSZ;
9359a747e4fSDavid du Colombier if(!fcalls[thdr.type])
936219b2ee8SDavid du Colombier err = "bad fcall type";
937219b2ee8SDavid du Colombier else
9389a747e4fSDavid du Colombier err = (*fcalls[thdr.type])(newfid(thdr.fid));
939219b2ee8SDavid du Colombier if(err){
940219b2ee8SDavid du Colombier if(*err == 0)
941219b2ee8SDavid du Colombier continue; /* assigned to a slave */
9429a747e4fSDavid du Colombier rhdr.type = Rerror;
9439a747e4fSDavid du Colombier rhdr.ename = err;
944219b2ee8SDavid du Colombier }else{
9459a747e4fSDavid du Colombier rhdr.type = thdr.type + 1;
9469a747e4fSDavid du Colombier rhdr.fid = thdr.fid;
947219b2ee8SDavid du Colombier }
9489a747e4fSDavid du Colombier rhdr.tag = thdr.tag;
9499a747e4fSDavid du Colombier n = convS2M(&rhdr, mdata, messagesize);
9509a747e4fSDavid du Colombier if(write(mfd[1], mdata, n) != n)
951219b2ee8SDavid du Colombier error("mount write");
952219b2ee8SDavid du Colombier }
953219b2ee8SDavid du Colombier }
954219b2ee8SDavid du Colombier
955219b2ee8SDavid du Colombier
956219b2ee8SDavid du Colombier int
perm(Fid * f,Dev * d,int p)957219b2ee8SDavid du Colombier perm(Fid *f, Dev *d, int p)
958219b2ee8SDavid du Colombier {
959219b2ee8SDavid du Colombier if((p*Pother) & d->perm)
960219b2ee8SDavid du Colombier return 1;
961219b2ee8SDavid du Colombier if(strcmp(f->user, user)==0 && ((p*Pgroup) & d->perm))
962219b2ee8SDavid du Colombier return 1;
963219b2ee8SDavid du Colombier if(strcmp(f->user, d->user)==0 && ((p*Powner) & d->perm))
964219b2ee8SDavid du Colombier return 1;
965219b2ee8SDavid du Colombier return 0;
966219b2ee8SDavid du Colombier }
967219b2ee8SDavid du Colombier
968219b2ee8SDavid du Colombier void
error(char * s)969219b2ee8SDavid du Colombier error(char *s)
970219b2ee8SDavid du Colombier {
971219b2ee8SDavid du Colombier fprint(2, "%s: %s: %r\n", argv0, s);
97259cc4ca5SDavid du Colombier syslog(0, LOGFILE, "%s: %r", s);
973219b2ee8SDavid du Colombier remove("/srv/telco");
974219b2ee8SDavid du Colombier postnote(PNGROUP, getpid(), "exit");
975219b2ee8SDavid du Colombier exits(s);
976219b2ee8SDavid du Colombier }
977219b2ee8SDavid du Colombier
978219b2ee8SDavid du Colombier void *
emalloc(ulong n)979219b2ee8SDavid du Colombier emalloc(ulong n)
980219b2ee8SDavid du Colombier {
981219b2ee8SDavid du Colombier void *p;
982219b2ee8SDavid du Colombier
9837dd7cddfSDavid du Colombier p = mallocz(n, 1);
984219b2ee8SDavid du Colombier if(!p)
985219b2ee8SDavid du Colombier error("out of memory");
986219b2ee8SDavid du Colombier return p;
987219b2ee8SDavid du Colombier }
988219b2ee8SDavid du Colombier
989219b2ee8SDavid du Colombier void *
erealloc(void * p,ulong n)990219b2ee8SDavid du Colombier erealloc(void *p, ulong n)
991219b2ee8SDavid du Colombier {
992219b2ee8SDavid du Colombier p = realloc(p, n);
993219b2ee8SDavid du Colombier if(!p)
994219b2ee8SDavid du Colombier error("out of memory");
995219b2ee8SDavid du Colombier return p;
996219b2ee8SDavid du Colombier }
997219b2ee8SDavid du Colombier
998219b2ee8SDavid du Colombier /*
999219b2ee8SDavid du Colombier * send bytes to modem
1000219b2ee8SDavid du Colombier */
1001219b2ee8SDavid du Colombier int
send(Dev * d,char * x)1002219b2ee8SDavid du Colombier send(Dev *d, char *x)
1003219b2ee8SDavid du Colombier {
1004219b2ee8SDavid du Colombier if(verbose)
1005219b2ee8SDavid du Colombier syslog(0, LOGFILE, "->%s", x);
1006219b2ee8SDavid du Colombier return write(d->data, x, strlen(x));
1007219b2ee8SDavid du Colombier }
1008219b2ee8SDavid du Colombier
1009219b2ee8SDavid du Colombier /*
1010219b2ee8SDavid du Colombier * apply a string of commands to modem
1011219b2ee8SDavid du Colombier */
1012219b2ee8SDavid du Colombier int
apply(Dev * d,char * s,char * substr,int secs)1013219b2ee8SDavid du Colombier apply(Dev *d, char *s, char *substr, int secs)
1014219b2ee8SDavid du Colombier {
1015219b2ee8SDavid du Colombier char buf[128];
1016219b2ee8SDavid du Colombier char *p;
1017219b2ee8SDavid du Colombier int c, m;
1018219b2ee8SDavid du Colombier
1019219b2ee8SDavid du Colombier p = buf;
1020219b2ee8SDavid du Colombier m = Ok;
1021219b2ee8SDavid du Colombier while(*s){
1022219b2ee8SDavid du Colombier c = *p++ = *s++;
1023219b2ee8SDavid du Colombier if(c == '\r' || *s == 0){
1024219b2ee8SDavid du Colombier if(c != '\r')
1025219b2ee8SDavid du Colombier *p++ = '\r';
1026219b2ee8SDavid du Colombier *p = 0;
1027219b2ee8SDavid du Colombier if(send(d, buf) < 0)
1028219b2ee8SDavid du Colombier return Failure;
1029219b2ee8SDavid du Colombier m = readmsg(d, secs, substr);
1030219b2ee8SDavid du Colombier p = buf;
1031219b2ee8SDavid du Colombier }
1032219b2ee8SDavid du Colombier }
1033219b2ee8SDavid du Colombier return m;
1034219b2ee8SDavid du Colombier }
1035219b2ee8SDavid du Colombier
1036219b2ee8SDavid du Colombier /*
1037219b2ee8SDavid du Colombier * apply a command type
1038219b2ee8SDavid du Colombier */
1039219b2ee8SDavid du Colombier int
applyspecial(Dev * d,int index)1040219b2ee8SDavid du Colombier applyspecial(Dev *d, int index)
1041219b2ee8SDavid du Colombier {
1042219b2ee8SDavid du Colombier char *cmd;
1043219b2ee8SDavid du Colombier
1044219b2ee8SDavid du Colombier cmd = d->t->commands[index];
1045219b2ee8SDavid du Colombier if(cmd == 0 && d->baset)
1046219b2ee8SDavid du Colombier cmd = d->baset->commands[index];
1047219b2ee8SDavid du Colombier if(cmd == 0)
1048219b2ee8SDavid du Colombier return Failure;
1049219b2ee8SDavid du Colombier
1050219b2ee8SDavid du Colombier return apply(d, cmd, 0, 2);
1051219b2ee8SDavid du Colombier }
1052219b2ee8SDavid du Colombier
1053219b2ee8SDavid du Colombier /*
1054219b2ee8SDavid du Colombier * get modem into command mode if it isn't already
1055219b2ee8SDavid du Colombier */
1056219b2ee8SDavid du Colombier int
attention(Dev * d)1057219b2ee8SDavid du Colombier attention(Dev *d)
1058219b2ee8SDavid du Colombier {
1059219b2ee8SDavid du Colombier int i;
1060219b2ee8SDavid du Colombier
1061219b2ee8SDavid du Colombier for(i = 0; i < 2; i++){
1062219b2ee8SDavid du Colombier sleep(250);
1063219b2ee8SDavid du Colombier if(send(d, "+") < 0)
1064219b2ee8SDavid du Colombier continue;
1065219b2ee8SDavid du Colombier sleep(250);
1066219b2ee8SDavid du Colombier if(send(d, "+") < 0)
1067219b2ee8SDavid du Colombier continue;
1068219b2ee8SDavid du Colombier sleep(250);
1069219b2ee8SDavid du Colombier if(send(d, "+") < 0)
1070219b2ee8SDavid du Colombier continue;
1071219b2ee8SDavid du Colombier sleep(250);
1072219b2ee8SDavid du Colombier readmsg(d, 0, 0);
1073219b2ee8SDavid du Colombier if(apply(d, "ATZH0", 0, 2) == Ok)
1074219b2ee8SDavid du Colombier return Ok;
1075219b2ee8SDavid du Colombier }
1076219b2ee8SDavid du Colombier return Failure;
1077219b2ee8SDavid du Colombier }
1078219b2ee8SDavid du Colombier
1079219b2ee8SDavid du Colombier int portspeed[] = { 56000, 38400, 19200, 14400, 9600, 4800, 2400, 1200, 600, 300, 0 };
1080219b2ee8SDavid du Colombier
1081219b2ee8SDavid du Colombier /*
1082219b2ee8SDavid du Colombier * get the modem's type and speed
1083219b2ee8SDavid du Colombier */
1084219b2ee8SDavid du Colombier char*
modemtype(Dev * d,int limit,int fax)1085219b2ee8SDavid du Colombier modemtype(Dev *d, int limit, int fax)
1086219b2ee8SDavid du Colombier {
1087219b2ee8SDavid du Colombier int *p;
1088219b2ee8SDavid du Colombier Type *t, *bt;
1089219b2ee8SDavid du Colombier char buf[28];
1090219b2ee8SDavid du Colombier
1091219b2ee8SDavid du Colombier d->t = typetab;
1092219b2ee8SDavid du Colombier d->baset = 0;
1093219b2ee8SDavid du Colombier
1094219b2ee8SDavid du Colombier /* assume we're at a good speed, try getting attention a few times */
1095219b2ee8SDavid du Colombier attention(d);
1096219b2ee8SDavid du Colombier
1097219b2ee8SDavid du Colombier /* find a common port rate */
1098219b2ee8SDavid du Colombier for(p = portspeed; *p; p++){
1099219b2ee8SDavid du Colombier if(*p > limit)
1100219b2ee8SDavid du Colombier continue;
1101219b2ee8SDavid du Colombier setspeed(d, *p);
1102219b2ee8SDavid du Colombier if(attention(d) == Ok)
1103219b2ee8SDavid du Colombier break;
1104219b2ee8SDavid du Colombier }
1105219b2ee8SDavid du Colombier if(*p == 0)
1106219b2ee8SDavid du Colombier return Eattn;
1107219b2ee8SDavid du Colombier d->speed = *p;
1108219b2ee8SDavid du Colombier if(verbose)
1109219b2ee8SDavid du Colombier syslog(0, LOGFILE, "port speed %d", *p);
1110219b2ee8SDavid du Colombier
1111219b2ee8SDavid du Colombier /*
1112219b2ee8SDavid du Colombier * basic Hayes commands everyone implements (we hope)
1113219b2ee8SDavid du Colombier * Q0 = report result codes
1114219b2ee8SDavid du Colombier * V1 = full word result codes
1115219b2ee8SDavid du Colombier * E0 = don't echo commands
1116219b2ee8SDavid du Colombier * M1 = speaker on until on-line
1117219b2ee8SDavid du Colombier * S0=0 = autoanswer off
1118219b2ee8SDavid du Colombier */
1119219b2ee8SDavid du Colombier if(apply(d, "ATQ0V1E0M1S0=0", 0, 2) != Ok)
1120219b2ee8SDavid du Colombier return Eattn;
1121219b2ee8SDavid du Colombier
1122219b2ee8SDavid du Colombier /* find modem type */
1123219b2ee8SDavid du Colombier for(t = typetab; t->name; t++){
1124219b2ee8SDavid du Colombier if(t->ident == 0 || t->response == 0)
1125219b2ee8SDavid du Colombier continue;
1126219b2ee8SDavid du Colombier if(apply(d, t->ident, t->response, 2) == Found)
1127219b2ee8SDavid du Colombier break;
1128219b2ee8SDavid du Colombier readmsg(d, 0, 0);
1129219b2ee8SDavid du Colombier }
1130219b2ee8SDavid du Colombier readmsg(d, 0, 0);
1131219b2ee8SDavid du Colombier if(t->name){
1132219b2ee8SDavid du Colombier d->t = t;
1133219b2ee8SDavid du Colombier if(t->basetype){
1134219b2ee8SDavid du Colombier for(bt = typetab; bt->name; bt++)
1135219b2ee8SDavid du Colombier if(strcmp(bt->name, t->basetype) == 0)
1136219b2ee8SDavid du Colombier break;
1137219b2ee8SDavid du Colombier if(bt->name)
1138219b2ee8SDavid du Colombier d->baset = bt;
1139219b2ee8SDavid du Colombier }
1140219b2ee8SDavid du Colombier }
1141219b2ee8SDavid du Colombier if(verbose)
1142219b2ee8SDavid du Colombier syslog(0, LOGFILE, "modem %s", d->t->name);
1143219b2ee8SDavid du Colombier
1144219b2ee8SDavid du Colombier /* try setting fax modes */
1145219b2ee8SDavid du Colombier d->fclass = 0;
1146219b2ee8SDavid du Colombier if(fax){
1147219b2ee8SDavid du Colombier /* set up fax parameters */
1148219b2ee8SDavid du Colombier if(applyspecial(d, Cfclass2) != Failure)
1149219b2ee8SDavid du Colombier d->fclass = 2;
1150219b2ee8SDavid du Colombier
1151219b2ee8SDavid du Colombier /* setup a source id */
1152219b2ee8SDavid du Colombier if(srcid){
1153219b2ee8SDavid du Colombier sprint(buf, "AT+FLID=\"%s\"", srcid);
1154219b2ee8SDavid du Colombier apply(d, buf, 0, 2);
1155219b2ee8SDavid du Colombier }
1156219b2ee8SDavid du Colombier
1157219b2ee8SDavid du Colombier /* allow both data and fax calls in */
1158219b2ee8SDavid du Colombier apply(d, "AT+FAA=1", 0, 2);
1159219b2ee8SDavid du Colombier } else
1160219b2ee8SDavid du Colombier applyspecial(d, Cfclass0);
1161219b2ee8SDavid du Colombier return 0;
1162219b2ee8SDavid du Colombier }
1163219b2ee8SDavid du Colombier
1164219b2ee8SDavid du Colombier /*
1165219b2ee8SDavid du Colombier * a process to read input from a modem.
1166219b2ee8SDavid du Colombier */
1167219b2ee8SDavid du Colombier void
monitor(Dev * d)1168219b2ee8SDavid du Colombier monitor(Dev *d)
1169219b2ee8SDavid du Colombier {
1170219b2ee8SDavid du Colombier int n;
1171219b2ee8SDavid du Colombier char *p;
1172219b2ee8SDavid du Colombier char file[256];
117359cc4ca5SDavid du Colombier int background;
1174219b2ee8SDavid du Colombier
117559cc4ca5SDavid du Colombier background = 0;
1176219b2ee8SDavid du Colombier d->ctl = d->data = -1;
1177219b2ee8SDavid du Colombier
1178219b2ee8SDavid du Colombier for(;;){
1179219b2ee8SDavid du Colombier lock(d);
1180219b2ee8SDavid du Colombier sprint(file, "%sctl", d->path);
1181219b2ee8SDavid du Colombier d->ctl = open(file, ORDWR);
1182219b2ee8SDavid du Colombier if(d->ctl < 0)
1183219b2ee8SDavid du Colombier error("opening ctl");
1184219b2ee8SDavid du Colombier d->data = open(d->path, ORDWR);
1185219b2ee8SDavid du Colombier if(d->data < 0)
1186219b2ee8SDavid du Colombier error("opening data");
1187219b2ee8SDavid du Colombier d->wp = d->rp = d->rbuf;
1188219b2ee8SDavid du Colombier unlock(d);
1189219b2ee8SDavid du Colombier
119059cc4ca5SDavid du Colombier if(!background){
119159cc4ca5SDavid du Colombier background = 1;
119259cc4ca5SDavid du Colombier switch(d->pid = rfork(RFPROC|RFMEM)){
119359cc4ca5SDavid du Colombier case -1:
119459cc4ca5SDavid du Colombier error("out of processes");
119559cc4ca5SDavid du Colombier case 0:
119659cc4ca5SDavid du Colombier break;
119759cc4ca5SDavid du Colombier default:
119859cc4ca5SDavid du Colombier return;
119959cc4ca5SDavid du Colombier }
120059cc4ca5SDavid du Colombier }
120159cc4ca5SDavid du Colombier
1202219b2ee8SDavid du Colombier /* wait for ring or off hook */
1203219b2ee8SDavid du Colombier while(d->open == 0){
1204219b2ee8SDavid du Colombier d->rp = d->rbuf;
1205219b2ee8SDavid du Colombier p = d->wp;
1206219b2ee8SDavid du Colombier n = read(d->data, p, 1);
1207219b2ee8SDavid du Colombier if(n < 1)
1208219b2ee8SDavid du Colombier continue;
1209219b2ee8SDavid du Colombier if(p < &d->rbuf[Nrbuf] - 2)
1210219b2ee8SDavid du Colombier d->wp++;
1211219b2ee8SDavid du Colombier if(*p == '\r' || *p == '\n'){
1212219b2ee8SDavid du Colombier *(p+1) = 0;
1213219b2ee8SDavid du Colombier if(verbose)
1214219b2ee8SDavid du Colombier syslog(0, LOGFILE, "<:-%s", d->rp);
12157dd7cddfSDavid du Colombier if(answer && strncmp(d->rp, "RING", 4) == 0){
1216219b2ee8SDavid du Colombier receiver(d);
1217219b2ee8SDavid du Colombier continue;
1218219b2ee8SDavid du Colombier }
1219219b2ee8SDavid du Colombier if(d->open == 0)
1220219b2ee8SDavid du Colombier d->wp = d->rbuf;
1221219b2ee8SDavid du Colombier }
1222219b2ee8SDavid du Colombier }
1223219b2ee8SDavid du Colombier
1224219b2ee8SDavid du Colombier /* shuttle bytes till on hook */
1225219b2ee8SDavid du Colombier while(d->open){
1226219b2ee8SDavid du Colombier if(d->wp >= d->rp)
1227219b2ee8SDavid du Colombier n = &d->rbuf[Nrbuf] - d->wp;
1228219b2ee8SDavid du Colombier else
1229219b2ee8SDavid du Colombier n = d->rp - d->wp - 1;
1230219b2ee8SDavid du Colombier if(n > 0)
1231219b2ee8SDavid du Colombier n = read(d->data, d->wp, n);
1232219b2ee8SDavid du Colombier else {
1233219b2ee8SDavid du Colombier read(d->data, file, sizeof(file));
1234219b2ee8SDavid du Colombier continue;
1235219b2ee8SDavid du Colombier }
1236219b2ee8SDavid du Colombier if(n < 0)
1237219b2ee8SDavid du Colombier break;
1238219b2ee8SDavid du Colombier lock(d);
1239219b2ee8SDavid du Colombier if(d->wp + n >= &d->rbuf[Nrbuf])
1240219b2ee8SDavid du Colombier d->wp = d->rbuf;
1241219b2ee8SDavid du Colombier else
1242219b2ee8SDavid du Colombier d->wp += n;
1243219b2ee8SDavid du Colombier serve(d);
1244219b2ee8SDavid du Colombier unlock(d);
1245219b2ee8SDavid du Colombier }
1246219b2ee8SDavid du Colombier
1247219b2ee8SDavid du Colombier close(d->ctl);
1248219b2ee8SDavid du Colombier close(d->data);
1249219b2ee8SDavid du Colombier }
1250219b2ee8SDavid du Colombier }
1251219b2ee8SDavid du Colombier
1252219b2ee8SDavid du Colombier /*
1253219b2ee8SDavid du Colombier * get bytes input by monitor() (only routine that changes d->rp)
1254219b2ee8SDavid du Colombier */
1255219b2ee8SDavid du Colombier int
getinput(Dev * d,char * buf,int n)1256219b2ee8SDavid du Colombier getinput(Dev *d, char *buf, int n)
1257219b2ee8SDavid du Colombier {
1258219b2ee8SDavid du Colombier char *p;
1259219b2ee8SDavid du Colombier int i;
1260219b2ee8SDavid du Colombier
1261219b2ee8SDavid du Colombier p = buf;
1262219b2ee8SDavid du Colombier while(n > 0){
1263219b2ee8SDavid du Colombier if(d->wp == d->rp)
1264219b2ee8SDavid du Colombier break;
1265219b2ee8SDavid du Colombier if(d->wp < d->rp)
1266219b2ee8SDavid du Colombier i = &d->rbuf[Nrbuf] - d->rp;
1267219b2ee8SDavid du Colombier else
1268219b2ee8SDavid du Colombier i = d->wp - d->rp;
1269219b2ee8SDavid du Colombier if(i > n)
1270219b2ee8SDavid du Colombier i = n;
1271219b2ee8SDavid du Colombier memmove(p, d->rp, i);
1272219b2ee8SDavid du Colombier if(d->rp + i == &d->rbuf[Nrbuf])
1273219b2ee8SDavid du Colombier d->rp = d->rbuf;
1274219b2ee8SDavid du Colombier else
1275219b2ee8SDavid du Colombier d->rp += i;
1276219b2ee8SDavid du Colombier n -= i;
1277219b2ee8SDavid du Colombier p += i;
1278219b2ee8SDavid du Colombier }
1279219b2ee8SDavid du Colombier return p - buf;
1280219b2ee8SDavid du Colombier }
1281219b2ee8SDavid du Colombier
1282219b2ee8SDavid du Colombier /*
1283219b2ee8SDavid du Colombier * fulfill a read request (we assume d is locked)
1284219b2ee8SDavid du Colombier */
1285219b2ee8SDavid du Colombier void
serve(Dev * d)1286219b2ee8SDavid du Colombier serve(Dev *d)
1287219b2ee8SDavid du Colombier {
1288219b2ee8SDavid du Colombier Request *r;
1289219b2ee8SDavid du Colombier int n;
12909a747e4fSDavid du Colombier Fcall rhdr;
12919a747e4fSDavid du Colombier uchar *mdata;
12929a747e4fSDavid du Colombier char *buf;
12939a747e4fSDavid du Colombier
12949a747e4fSDavid du Colombier mdata = malloc(messagesize);
12959a747e4fSDavid du Colombier buf = malloc(messagesize-IOHDRSZ);
1296219b2ee8SDavid du Colombier
1297219b2ee8SDavid du Colombier for(;;){
1298219b2ee8SDavid du Colombier if(d->r == 0 || d->rp == d->wp)
1299*5d459b5aSDavid du Colombier break;
1300219b2ee8SDavid du Colombier r = d->r;
1301219b2ee8SDavid du Colombier if(r->count > sizeof(buf))
1302219b2ee8SDavid du Colombier r->count = sizeof(buf);
1303219b2ee8SDavid du Colombier
1304219b2ee8SDavid du Colombier n = getinput(d, buf, r->count);
1305219b2ee8SDavid du Colombier if(n == 0)
13069a747e4fSDavid du Colombier break;
1307219b2ee8SDavid du Colombier d->r = r->next;
1308219b2ee8SDavid du Colombier
13099a747e4fSDavid du Colombier rhdr.type = Rread;
13109a747e4fSDavid du Colombier rhdr.fid = r->fid->fid;
13119a747e4fSDavid du Colombier rhdr.tag = r->tag;
13129a747e4fSDavid du Colombier rhdr.data = buf;
13139a747e4fSDavid du Colombier rhdr.count = n;
13149a747e4fSDavid du Colombier n = convS2M(&rhdr, mdata, messagesize);
13159a747e4fSDavid du Colombier if(write(mfd[1], mdata, n) != n)
1316219b2ee8SDavid du Colombier fprint(2, "telco: error writing\n");
1317219b2ee8SDavid du Colombier free(r);
1318219b2ee8SDavid du Colombier }
13199a747e4fSDavid du Colombier free(mdata);
13209a747e4fSDavid du Colombier free(buf);
1321219b2ee8SDavid du Colombier }
1322219b2ee8SDavid du Colombier
1323219b2ee8SDavid du Colombier /*
1324219b2ee8SDavid du Colombier * dial a number
1325219b2ee8SDavid du Colombier */
1326219b2ee8SDavid du Colombier char*
dialout(Dev * d,char * number)1327219b2ee8SDavid du Colombier dialout(Dev *d, char *number)
1328219b2ee8SDavid du Colombier {
1329219b2ee8SDavid du Colombier int i, m, compress, rateadjust, speed, fax;
1330219b2ee8SDavid du Colombier char *err;
1331219b2ee8SDavid du Colombier char *field[5];
13329a747e4fSDavid du Colombier char dialstr[128];
1333219b2ee8SDavid du Colombier
1334219b2ee8SDavid du Colombier compress = Ok;
1335219b2ee8SDavid du Colombier rateadjust = Failure;
1336219b2ee8SDavid du Colombier speed = maxspeed;
1337219b2ee8SDavid du Colombier fax = Failure;
1338219b2ee8SDavid du Colombier
13397dd7cddfSDavid du Colombier m = getfields(number, field, 5, 1, "!");
1340219b2ee8SDavid du Colombier for(i = 1; i < m; i++){
1341219b2ee8SDavid du Colombier if(field[i][0] >= '0' && field[i][0] <= '9')
1342219b2ee8SDavid du Colombier speed = atoi(field[i]);
1343219b2ee8SDavid du Colombier else if(strcmp(field[i], "nocompress") == 0)
1344219b2ee8SDavid du Colombier compress = Failure;
1345219b2ee8SDavid du Colombier else if(strcmp(field[i], "fax") == 0)
1346219b2ee8SDavid du Colombier fax = Ok;
1347219b2ee8SDavid du Colombier }
1348219b2ee8SDavid du Colombier
13497dd7cddfSDavid du Colombier syslog(0, LOGFILE, "dialing %s speed=%d %s", number, speed, fax==Ok?"fax":"");
13507dd7cddfSDavid du Colombier
1351219b2ee8SDavid du Colombier err = modemtype(d, speed, fax == Ok);
1352219b2ee8SDavid du Colombier if(err)
1353219b2ee8SDavid du Colombier return err;
1354219b2ee8SDavid du Colombier
1355219b2ee8SDavid du Colombier /*
1356219b2ee8SDavid du Colombier * extented Hayes commands, meaning depends on modem (VGA all over again)
1357219b2ee8SDavid du Colombier */
1358219b2ee8SDavid du Colombier if(fax != Ok){
1359219b2ee8SDavid du Colombier if(d->fclass != 0)
1360219b2ee8SDavid du Colombier applyspecial(d, Cfclass0);
1361219b2ee8SDavid du Colombier applyspecial(d, Cerrorcorrection);
1362219b2ee8SDavid du Colombier if(compress == Ok)
1363219b2ee8SDavid du Colombier compress = applyspecial(d, Ccompression);
1364219b2ee8SDavid du Colombier if(compress != Ok)
1365219b2ee8SDavid du Colombier rateadjust = applyspecial(d, Crateadjust);
1366219b2ee8SDavid du Colombier }
1367219b2ee8SDavid du Colombier applyspecial(d, Cflowctl);
1368219b2ee8SDavid du Colombier
1369219b2ee8SDavid du Colombier /* dialout */
1370219b2ee8SDavid du Colombier sprint(dialstr, "ATD%c%s\r", pulsed ? 'P' : 'T', number);
1371219b2ee8SDavid du Colombier if(send(d, dialstr) < 0)
1372219b2ee8SDavid du Colombier return Edial;
13737dd7cddfSDavid du Colombier
1374219b2ee8SDavid du Colombier if(fax == Ok)
1375219b2ee8SDavid du Colombier return 0; /* fax sender worries about the rest */
13767dd7cddfSDavid du Colombier
1377219b2ee8SDavid du Colombier switch(readmsg(d, 120, 0)){
1378219b2ee8SDavid du Colombier case Success:
1379219b2ee8SDavid du Colombier break;
1380219b2ee8SDavid du Colombier default:
1381219b2ee8SDavid du Colombier return d->msgbuf;
1382219b2ee8SDavid du Colombier }
1383219b2ee8SDavid du Colombier
1384219b2ee8SDavid du Colombier /* change line rate if not compressing */
1385219b2ee8SDavid du Colombier if(rateadjust == Ok)
1386219b2ee8SDavid du Colombier setspeed(d, getspeed(d->msgbuf, d->speed));
1387219b2ee8SDavid du Colombier
1388219b2ee8SDavid du Colombier return 0;
1389219b2ee8SDavid du Colombier }
1390219b2ee8SDavid du Colombier
1391219b2ee8SDavid du Colombier /*
1392219b2ee8SDavid du Colombier * start a receiving process
1393219b2ee8SDavid du Colombier */
1394219b2ee8SDavid du Colombier void
receiver(Dev * d)1395219b2ee8SDavid du Colombier receiver(Dev *d)
1396219b2ee8SDavid du Colombier {
1397219b2ee8SDavid du Colombier int fd;
1398219b2ee8SDavid du Colombier char file[256];
1399219b2ee8SDavid du Colombier char *argv[8];
1400219b2ee8SDavid du Colombier int argc;
1401219b2ee8SDavid du Colombier int pfd[2];
1402219b2ee8SDavid du Colombier char *prog;
1403219b2ee8SDavid du Colombier
1404219b2ee8SDavid du Colombier pipe(pfd);
1405219b2ee8SDavid du Colombier switch(rfork(RFPROC|RFMEM|RFFDG|RFNAMEG)){
1406219b2ee8SDavid du Colombier case -1:
1407219b2ee8SDavid du Colombier return;
1408219b2ee8SDavid du Colombier case 0:
1409219b2ee8SDavid du Colombier fd = open("/srv/telco", ORDWR);
1410219b2ee8SDavid du Colombier if(fd < 0){
1411219b2ee8SDavid du Colombier syslog(0, LOGFILE, "can't open telco: %r");
1412219b2ee8SDavid du Colombier exits(0);
1413219b2ee8SDavid du Colombier }
14149a747e4fSDavid du Colombier if(mount(fd, -1, "/net", MAFTER, "") < 0){
1415219b2ee8SDavid du Colombier syslog(0, LOGFILE, "can't mount: %r");
1416219b2ee8SDavid du Colombier exits(0);
1417219b2ee8SDavid du Colombier }
1418219b2ee8SDavid du Colombier close(fd);
1419219b2ee8SDavid du Colombier
1420219b2ee8SDavid du Colombier /* open connection through the file system interface */
14217dd7cddfSDavid du Colombier sprint(file, "/net/telco/%ld/data", d - dev);
1422219b2ee8SDavid du Colombier fd = open(file, ORDWR);
1423219b2ee8SDavid du Colombier if(fd < 0){
1424219b2ee8SDavid du Colombier syslog(0, LOGFILE, "can't open %s: %r", file);
1425219b2ee8SDavid du Colombier exits(0);
1426219b2ee8SDavid du Colombier }
1427219b2ee8SDavid du Colombier
1428219b2ee8SDavid du Colombier /* let parent continue */
1429219b2ee8SDavid du Colombier close(pfd[0]);
1430219b2ee8SDavid du Colombier close(pfd[1]);
1431219b2ee8SDavid du Colombier
1432219b2ee8SDavid du Colombier /* answer the phone and see what flavor call this is */
1433219b2ee8SDavid du Colombier prog = "/bin/service/telcodata";
1434219b2ee8SDavid du Colombier switch(apply(d, "ATA", "+FCON", 30)){
1435219b2ee8SDavid du Colombier case Success:
1436219b2ee8SDavid du Colombier break;
1437219b2ee8SDavid du Colombier case Found:
1438219b2ee8SDavid du Colombier prog = "/bin/service/telcofax";
1439219b2ee8SDavid du Colombier break;
1440219b2ee8SDavid du Colombier default:
1441219b2ee8SDavid du Colombier syslog(0, LOGFILE, "bad ATA response");
1442219b2ee8SDavid du Colombier exits(0);
1443219b2ee8SDavid du Colombier }
1444219b2ee8SDavid du Colombier
1445219b2ee8SDavid du Colombier /* fork a receiving process */
1446219b2ee8SDavid du Colombier dup(fd, 0);
1447219b2ee8SDavid du Colombier dup(fd, 1);
1448219b2ee8SDavid du Colombier close(fd);
1449219b2ee8SDavid du Colombier argc = 0;
1450219b2ee8SDavid du Colombier argv[argc++] = strrchr(prog, '/')+1;
1451219b2ee8SDavid du Colombier argv[argc++] = file;
1452219b2ee8SDavid du Colombier argv[argc++] = dev->t->name;
1453219b2ee8SDavid du Colombier argv[argc] = 0;
1454219b2ee8SDavid du Colombier exec(prog, argv);
1455219b2ee8SDavid du Colombier syslog(0, LOGFILE, "can't exec %s: %r\n", prog);
1456219b2ee8SDavid du Colombier exits(0);
1457219b2ee8SDavid du Colombier default:
1458219b2ee8SDavid du Colombier /* wait till child gets the device open */
1459219b2ee8SDavid du Colombier close(pfd[1]);
1460219b2ee8SDavid du Colombier read(pfd[0], file, 1);
1461219b2ee8SDavid du Colombier close(pfd[0]);
1462219b2ee8SDavid du Colombier break;
1463219b2ee8SDavid du Colombier }
1464219b2ee8SDavid du Colombier }
1465219b2ee8SDavid du Colombier
1466219b2ee8SDavid du Colombier /*
1467219b2ee8SDavid du Colombier * hang up an connections in progress
1468219b2ee8SDavid du Colombier */
1469219b2ee8SDavid du Colombier void
onhook(Dev * d)1470219b2ee8SDavid du Colombier onhook(Dev *d)
1471219b2ee8SDavid du Colombier {
1472219b2ee8SDavid du Colombier write(d->ctl, "d0", 2);
1473219b2ee8SDavid du Colombier write(d->ctl, "r0", 2);
1474219b2ee8SDavid du Colombier sleep(250);
1475219b2ee8SDavid du Colombier write(d->ctl, "r1", 2);
1476219b2ee8SDavid du Colombier write(d->ctl, "d1", 2);
1477219b2ee8SDavid du Colombier modemtype(d, maxspeed, 1);
1478219b2ee8SDavid du Colombier }
1479219b2ee8SDavid du Colombier
1480219b2ee8SDavid du Colombier /*
1481219b2ee8SDavid du Colombier * read till we see a message or we time out
1482219b2ee8SDavid du Colombier */
1483219b2ee8SDavid du Colombier int
readmsg(Dev * d,int secs,char * substr)1484219b2ee8SDavid du Colombier readmsg(Dev *d, int secs, char *substr)
1485219b2ee8SDavid du Colombier {
1486219b2ee8SDavid du Colombier ulong start;
1487219b2ee8SDavid du Colombier char *p;
1488219b2ee8SDavid du Colombier int i, len;
1489219b2ee8SDavid du Colombier Msg *pp;
1490219b2ee8SDavid du Colombier int found = 0;
1491219b2ee8SDavid du Colombier
1492219b2ee8SDavid du Colombier p = d->msgbuf;
1493219b2ee8SDavid du Colombier len = sizeof(d->msgbuf) - 1;
1494219b2ee8SDavid du Colombier for(start = time(0); time(0) <= start+secs;){
1495219b2ee8SDavid du Colombier if(len && d->rp == d->wp){
1496219b2ee8SDavid du Colombier sleep(100);
1497219b2ee8SDavid du Colombier continue;
1498219b2ee8SDavid du Colombier }
1499219b2ee8SDavid du Colombier i = getinput(d, p, 1);
1500219b2ee8SDavid du Colombier if(i == 0)
1501219b2ee8SDavid du Colombier continue;
1502219b2ee8SDavid du Colombier if(*p == '\n' || *p == '\r' || len == 0){
1503219b2ee8SDavid du Colombier *p = 0;
1504219b2ee8SDavid du Colombier if(verbose && p != d->msgbuf)
1505219b2ee8SDavid du Colombier syslog(0, LOGFILE, "<-%s", d->msgbuf);
1506219b2ee8SDavid du Colombier if(substr && strstr(d->msgbuf, substr))
1507219b2ee8SDavid du Colombier found = 1;
1508219b2ee8SDavid du Colombier for(pp = msgs; pp->text; pp++)
1509219b2ee8SDavid du Colombier if(strncmp(pp->text, d->msgbuf, strlen(pp->text))==0)
1510219b2ee8SDavid du Colombier return found ? Found : pp->type;
1511219b2ee8SDavid du Colombier start = time(0);
1512219b2ee8SDavid du Colombier p = d->msgbuf;
1513219b2ee8SDavid du Colombier len = sizeof(d->msgbuf) - 1;
1514219b2ee8SDavid du Colombier continue;
1515219b2ee8SDavid du Colombier }
1516219b2ee8SDavid du Colombier len--;
1517219b2ee8SDavid du Colombier p++;
1518219b2ee8SDavid du Colombier }
1519219b2ee8SDavid du Colombier strcpy(d->msgbuf, "No response from modem");
1520219b2ee8SDavid du Colombier return found ? Found : Noise;
1521219b2ee8SDavid du Colombier }
1522219b2ee8SDavid du Colombier
1523219b2ee8SDavid du Colombier /*
1524219b2ee8SDavid du Colombier * get baud rate from a connect message
1525219b2ee8SDavid du Colombier */
1526219b2ee8SDavid du Colombier int
getspeed(char * msg,int speed)1527219b2ee8SDavid du Colombier getspeed(char *msg, int speed)
1528219b2ee8SDavid du Colombier {
1529219b2ee8SDavid du Colombier char *p;
1530219b2ee8SDavid du Colombier int s;
1531219b2ee8SDavid du Colombier
1532219b2ee8SDavid du Colombier p = msg + sizeof("CONNECT") - 1;
1533219b2ee8SDavid du Colombier while(*p == ' ' || *p == '\t')
1534219b2ee8SDavid du Colombier p++;
1535219b2ee8SDavid du Colombier s = atoi(p);
1536219b2ee8SDavid du Colombier if(s <= 0)
1537219b2ee8SDavid du Colombier return speed;
1538219b2ee8SDavid du Colombier else
1539219b2ee8SDavid du Colombier return s;
1540219b2ee8SDavid du Colombier }
1541219b2ee8SDavid du Colombier
1542219b2ee8SDavid du Colombier /*
1543219b2ee8SDavid du Colombier * set speed and RTS/CTS modem flow control
1544219b2ee8SDavid du Colombier */
1545219b2ee8SDavid du Colombier void
setspeed(Dev * d,int baud)1546219b2ee8SDavid du Colombier setspeed(Dev *d, int baud)
1547219b2ee8SDavid du Colombier {
1548219b2ee8SDavid du Colombier char buf[32];
1549219b2ee8SDavid du Colombier
1550219b2ee8SDavid du Colombier if(d->ctl < 0)
1551219b2ee8SDavid du Colombier return;
1552219b2ee8SDavid du Colombier sprint(buf, "b%d", baud);
1553219b2ee8SDavid du Colombier write(d->ctl, buf, strlen(buf));
1554219b2ee8SDavid du Colombier write(d->ctl, "m1", 2);
1555219b2ee8SDavid du Colombier }
1556