xref: /plan9/sys/src/cmd/telco/telco.c (revision 5d459b5a09e427ae1acd4e6afcf028853c73946e)
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