Commit | Line | Data |
---|---|---|
3b600ead TL |
1 | #include <stdio.h> |
2 | #include <signal.h> | |
3 | #include <sgtty.h> | |
4 | /* | |
5 | * cu telno [-t] [-s speed] [-l line] [-a acu] | |
6 | * | |
7 | * -t is for dial-out to terminal. | |
8 | * speeds are: 110, 134, 150, 300, 1200. 300 is default. | |
9 | * | |
10 | * Escape with `~' at beginning of line. | |
11 | * Ordinary diversions are ~<, ~> and ~>>. | |
12 | * Silent output diversions are ~>: and ~>>:. | |
13 | * Terminate output diversion with ~> alone. | |
14 | * Quit is ~. and ~! gives local command or shell. | |
15 | * Also ~$ for canned procedure pumping remote. | |
16 | * ~%put from [to] and ~%take from [to] invoke builtins | |
17 | */ | |
18 | ||
19 | #define CRLF "\r\n" | |
20 | #define wrc(ds) write(ds,&c,1) | |
21 | ||
22 | ||
23 | char *devcul = "/dev/cul0"; | |
24 | char *devcua = "/dev/cua0"; | |
25 | char *lspeed = "300"; | |
26 | ||
27 | int ln; /* fd for comm line */ | |
28 | char tkill, terase; /* current input kill & erase */ | |
29 | char c; | |
30 | ||
31 | char *connmsg[] = { | |
32 | "", | |
33 | "line busy", | |
34 | "call dropped", | |
35 | "no carrier", | |
36 | "can't fork", | |
37 | "acu access", | |
38 | "tty access", | |
39 | "tty hung", | |
40 | "usage: cu telno [-t] [-s speed] [-l line] [-a acu]" | |
41 | }; | |
42 | ||
43 | rdc(ds) { | |
44 | ||
45 | ds=read(ds,&c,1); | |
46 | c&= 0177; | |
47 | return (ds); | |
48 | } | |
49 | ||
50 | int intr; | |
51 | ||
52 | sig2() | |
53 | { | |
54 | signal(SIGINT, SIG_IGN); | |
55 | intr = 1; | |
56 | } | |
57 | ||
58 | int set14; | |
59 | ||
60 | xsleep(n) | |
61 | { | |
62 | xalarm(n); | |
63 | pause(); | |
64 | xalarm(0); | |
65 | } | |
66 | ||
67 | xalarm(n) | |
68 | { | |
69 | set14=n; | |
70 | alarm(n); | |
71 | } | |
72 | ||
73 | sig14() | |
74 | { | |
75 | signal(SIGALRM, sig14); | |
76 | if (set14) alarm(1); | |
77 | } | |
78 | ||
79 | int dout; | |
80 | int nhup; | |
81 | ||
82 | /* | |
83 | * main: get connection, set speed for line. | |
84 | * spawn child to invoke rd to read from line, output to fd 1 | |
85 | * main line invokes wr to read tty, write to line | |
86 | */ | |
87 | main(ac,av) | |
88 | char *av[]; | |
89 | { | |
90 | int fk; | |
91 | int speed; | |
92 | char *telno; | |
93 | struct sgttyb stbuf; | |
94 | ||
95 | signal(SIGALRM, sig14); | |
96 | if (ac < 2) { | |
97 | prf(connmsg[8]); | |
98 | exit(8); | |
99 | } | |
100 | telno = av[1]; | |
101 | av += 2; | |
102 | ac -= 2; | |
103 | for (; ac > 0; av++) { | |
104 | if (equal(*av, "-t")) { | |
105 | dout = 1; | |
106 | --ac; | |
107 | continue; | |
108 | } | |
109 | if (ac < 2) | |
110 | break; | |
111 | if (equal(*av, "-s")) | |
112 | lspeed = *++av; | |
113 | else if (equal(*av, "-l")) | |
114 | devcul = *++av; | |
115 | else if (equal(*av, "-a")) | |
116 | devcua = *++av; | |
117 | else | |
118 | break; | |
119 | ac -= 2; | |
120 | } | |
121 | if (!exists(devcua) || !exists(devcul)) | |
122 | exit(9); | |
123 | ln = conn(devcul, devcua, telno); | |
124 | if (ln < 0) { | |
125 | prf("Connect failed: %s",connmsg[-ln]); | |
126 | exit(-ln); | |
127 | } | |
128 | switch(atoi(lspeed)) { | |
129 | case 110: | |
130 | speed = B110;break; | |
131 | case 150: | |
132 | speed = B150;break; | |
133 | default: | |
134 | case 300: | |
135 | speed = B300;break; | |
136 | case 1200: | |
137 | speed = B1200;break; | |
138 | } | |
139 | stbuf.sg_ispeed = speed; | |
140 | stbuf.sg_ospeed = speed; | |
141 | stbuf.sg_flags = EVENP|ODDP; | |
142 | if (!dout) | |
143 | stbuf.sg_flags |= RAW; | |
144 | ioctl(TIOCSETP, ln, &stbuf); | |
145 | ioctl(TIOCEXCL, ln, (struct sgttyb *)NULL); | |
146 | ioctl(TIOCHPCL, ln, (struct sgttyb *)NULL); | |
147 | prf("Connected"); | |
148 | if (dout) | |
149 | fk = -1; | |
150 | else | |
151 | fk = fork(); | |
152 | nhup = (int)signal(SIGINT, SIG_IGN); | |
153 | if (fk == 0) { | |
154 | rd(); | |
155 | prf("\007Lost carrier"); | |
156 | exit(3); | |
157 | } | |
158 | mode(1); | |
159 | wr(); | |
160 | mode(0); | |
161 | kill(fk, SIGKILL); | |
162 | wait((int *)NULL); | |
163 | stbuf.sg_ispeed = 0; | |
164 | stbuf.sg_ospeed = 0; | |
165 | ioctl(TIOCSETP, ln, &stbuf); | |
166 | prf("Disconnected"); | |
167 | exit(0); | |
168 | } | |
169 | ||
170 | /* | |
171 | * conn: establish dial-out connection. | |
172 | * Example: fd = conn("/dev/ttyh","/dev/dn1","4500"); | |
173 | * Returns descriptor open to tty for reading and writing. | |
174 | * Negative values (-1...-7) denote errors in connmsg. | |
175 | * Uses alarm and fork/wait; requires sig14 handler. | |
176 | * Be sure to disconnect tty when done, via HUPCL or stty 0. | |
177 | */ | |
178 | ||
179 | conn(dev,acu,telno) | |
180 | char *dev, *acu, *telno; | |
181 | { | |
182 | struct sgttyb stbuf; | |
183 | extern errno; | |
184 | char *p, *q, b[30]; | |
185 | int er, fk, dn, dh, t; | |
186 | er=0; | |
187 | fk=(-1); | |
188 | if ((dn=open(acu,1))<0) { | |
189 | er=(errno == 6? 1:5); | |
190 | goto X; | |
191 | } | |
192 | if ((fk=fork()) == (-1)) { | |
193 | er=4; | |
194 | goto X; | |
195 | } | |
196 | if (fk == 0) { | |
197 | open(dev,2); | |
198 | for (;;) pause(); | |
199 | } | |
200 | xsleep(2); | |
201 | /* | |
202 | * copy phone #, assure EON | |
203 | */ | |
204 | p=b; | |
205 | q=telno; | |
206 | while (*p++=(*q++)) | |
207 | ; | |
208 | p--; | |
209 | if (*(p-1)!='<') { | |
210 | if (*(p-1)!='-') *p++='-'; | |
211 | *p++='<'; | |
212 | } | |
213 | t=p-b; | |
214 | xalarm(5*t); | |
215 | t=write(dn,b,t); | |
216 | xalarm(0); | |
217 | if (t<0) { | |
218 | er=2; | |
219 | goto X; | |
220 | } | |
221 | /* close(dn) */ | |
222 | xalarm(40); /* was 5; sometimes missed carrier */ | |
223 | dh = open(dev,2); | |
224 | xalarm(0); | |
225 | if (dh<0) { | |
226 | er=(errno == 4? 3:6); | |
227 | goto X; | |
228 | } | |
229 | ioctl(TIOCGETP, ln, &stbuf); | |
230 | stbuf.sg_flags &= ~ECHO; | |
231 | xalarm(10); | |
232 | ioctl(TIOCSETP, dh, &stbuf); | |
233 | ioctl(TIOCHPCL, dh, (struct sgttyb *)NULL); | |
234 | xalarm(0); | |
235 | X: | |
236 | if (er) close(dn); | |
237 | if (fk!=(-1)) { | |
238 | kill(fk, SIGKILL); | |
239 | xalarm(10); | |
240 | while ((t=wait((int *)NULL))!=(-1) && t!=fk); | |
241 | xalarm(0); | |
242 | } | |
243 | return (er? -er:dh); | |
244 | } | |
245 | ||
246 | /* | |
247 | * wr: write to remote: 0 -> line. | |
248 | * ~. terminate | |
249 | * ~<file send file | |
250 | * ~! local login-style shell | |
251 | * ~!cmd execute cmd locally | |
252 | * ~$proc execute proc locally, send output to line | |
253 | * ~%cmd execute builtin cmd (put and take) | |
254 | */ | |
255 | ||
256 | wr() | |
257 | { | |
258 | int ds,fk,lcl,x; | |
259 | char *p,b[600]; | |
260 | for (;;) { | |
261 | p=b; | |
262 | while (rdc(0) == 1) { | |
263 | if (p == b) lcl=(c == '~'); | |
264 | if (p == b+1 && b[0] == '~') lcl=(c!='~'); | |
265 | if (c == 0) c=0177; | |
266 | if (!lcl) { | |
267 | if (wrc(ln) == 0) { | |
268 | prf("line gone"); return; | |
269 | } | |
270 | } | |
271 | if (lcl) { | |
272 | if (c == 0177) c=tkill; | |
273 | if (c == '\r' || c == '\n') goto A; | |
274 | if (!dout) wrc(0); | |
275 | } | |
276 | *p++=c; | |
277 | if (c == terase) { | |
278 | p=p-2; | |
279 | if (p<b) p=b; | |
280 | } | |
281 | if (c == tkill || c == 0177 || c == '\r' || c == '\n') p=b; | |
282 | } | |
283 | return; | |
284 | A: | |
285 | if (!dout) echo(""); | |
286 | *p=0; | |
287 | switch (b[1]) { | |
288 | case '.': | |
289 | case '\004': | |
290 | return; | |
291 | case '!': | |
292 | case '$': | |
293 | fk = fork(); | |
294 | if (fk == 0) { | |
295 | close(1); | |
296 | dup(b[1] == '$'? ln:2); | |
297 | close(ln); | |
298 | mode(0); | |
299 | if (!nhup) signal(SIGINT, SIG_DFL); | |
300 | if (b[2] == 0) execl("/bin/sh","-",0); | |
301 | else execl("/bin/sh","sh","-c",b+2,0); | |
302 | prf("Can't execute shell"); | |
303 | exit(~0); | |
304 | } | |
305 | if (fk!=(-1)) { | |
306 | while (wait(&x)!=fk); | |
307 | } | |
308 | mode(1); | |
309 | if (b[1] == '!') echo("!"); | |
310 | else { | |
311 | if (dout) echo("$"); | |
312 | } | |
313 | break; | |
314 | case '<': | |
315 | if (b[2] == 0) break; | |
316 | if ((ds=open(b+2,0))<0) { | |
317 | prf("Can't divert %s",b+1); | |
318 | break; | |
319 | } | |
320 | intr=x=0; | |
321 | mode(2); | |
322 | if (!nhup) signal(SIGINT, sig2); | |
323 | while (!intr && rdc(ds) == 1) { | |
324 | if (wrc(ln) == 0) { | |
325 | x=1; | |
326 | break; | |
327 | } | |
328 | } | |
329 | signal(SIGINT, SIG_IGN); | |
330 | close(ds); | |
331 | mode(1); | |
332 | if (x) return; | |
333 | if (dout) echo("<"); | |
334 | break; | |
335 | case '%': | |
336 | dopercen(&b[2]); | |
337 | break; | |
338 | default: | |
339 | prf("Use `~~' to start line with `~'"); | |
340 | } | |
341 | continue; | |
342 | } | |
343 | } | |
344 | ||
345 | dopercen(line) | |
346 | register char *line; | |
347 | { | |
348 | char *args[10]; | |
349 | register narg, f; | |
350 | int rcount; | |
351 | for (narg = 0; narg < 10;) { | |
352 | while(*line == ' ' || *line == '\t') | |
353 | line++; | |
354 | if (*line == '\0') | |
355 | break; | |
356 | args[narg++] = line; | |
357 | while(*line != '\0' && *line != ' ' && *line != '\t') | |
358 | line++; | |
359 | if (*line == '\0') | |
360 | break; | |
361 | *line++ = '\0'; | |
362 | } | |
363 | if (equal(args[0], "take")) { | |
364 | if (narg < 2) { | |
365 | prf("usage: ~%%take from [to]"); | |
366 | return; | |
367 | } | |
368 | if (narg < 3) | |
369 | args[2] = args[1]; | |
370 | wrln("echo '~>:'"); | |
371 | wrln(args[2]); | |
372 | wrln(";tee /dev/null <"); | |
373 | wrln(args[1]); | |
374 | wrln(";echo '~>'\n"); | |
375 | return; | |
376 | } else if (equal(args[0], "put")) { | |
377 | if (narg < 2) { | |
378 | prf("usage: ~%%put from [to]"); | |
379 | return; | |
380 | } | |
381 | if (narg < 3) | |
382 | args[2] = args[1]; | |
383 | if ((f = open(args[1], 0)) < 0) { | |
384 | prf("cannot open: %s", args[1]); | |
385 | return; | |
386 | } | |
387 | wrln("stty -echo;cat >"); | |
388 | wrln(args[2]); | |
389 | wrln(";stty echo\n"); | |
390 | xsleep(5); | |
391 | intr = 0; | |
392 | if (!nhup) | |
393 | signal(SIGINT, sig2); | |
394 | mode(2); | |
395 | rcount = 0; | |
396 | while(!intr && rdc(f) == 1) { | |
397 | rcount++; | |
398 | if (c == tkill || c == terase) | |
399 | wrln("\\"); | |
400 | if (wrc(ln) != 1) { | |
401 | xsleep(2); | |
402 | if (wrc(ln) != 1) { | |
403 | prf("character missed"); | |
404 | intr = 1; | |
405 | break; | |
406 | } | |
407 | } | |
408 | } | |
409 | signal(SIGINT, SIG_IGN); | |
410 | close(f); | |
411 | if (intr) { | |
412 | wrln("\n"); | |
413 | prf("stopped after %d bytes", rcount); | |
414 | } | |
415 | wrln("\004"); | |
416 | xsleep(5); | |
417 | mode(1); | |
418 | return; | |
419 | } | |
420 | prf("~%%%s unknown\n", args[0]); | |
421 | } | |
422 | ||
423 | equal(s1, s2) | |
424 | register char *s1, *s2; | |
425 | { | |
426 | while (*s1++ == *s2) | |
427 | if (*s2++ == '\0') | |
428 | return(1); | |
429 | return(0); | |
430 | } | |
431 | ||
432 | wrln(s) | |
433 | register char *s; | |
434 | { | |
435 | while (*s) | |
436 | write(ln, s++, 1); | |
437 | } | |
438 | ||
439 | /* | |
440 | * rd: read from remote: line -> 1 | |
441 | * catch: | |
442 | * ~>[>][:][file] | |
443 | * stuff from file... | |
444 | * ~> (ends diversion) | |
445 | */ | |
446 | ||
447 | rd() | |
448 | { | |
449 | int ds,slnt; | |
450 | char *p,*q,b[600]; | |
451 | p=b; | |
452 | ds=(-1); | |
453 | while (rdc(ln) == 1) { | |
454 | if (ds<0) slnt=0; | |
455 | if (!slnt) wrc(1); | |
456 | *p++=c; | |
457 | if (c!='\n') continue; | |
458 | q=p; | |
459 | p=b; | |
460 | if (b[0]!='~' || b[1]!='>') { | |
461 | if (*(q-2) == '\r') { | |
462 | q--; | |
463 | *(q-1)=(*q); | |
464 | } | |
465 | if (ds>=0) write(ds,b,q-b); | |
466 | continue; | |
467 | } | |
468 | if (ds>=0) close(ds); | |
469 | if (slnt) { | |
470 | write(1, b, q - b); | |
471 | write(1, CRLF, sizeof(CRLF)); | |
472 | } | |
473 | if (*(q-2) == '\r') q--; | |
474 | *(q-1)=0; | |
475 | slnt=0; | |
476 | q=b+2; | |
477 | if (*q == '>') q++; | |
478 | if (*q == ':') { | |
479 | slnt=1; | |
480 | q++; | |
481 | } | |
482 | if (*q == 0) { | |
483 | ds=(-1); | |
484 | continue; | |
485 | } | |
486 | if (b[2]!='>' || (ds=open(q,1))<0) ds=creat(q,0644); | |
487 | lseek(ds, (long)0, 2); | |
488 | if (ds<0) prf("Can't divert %s",b+1); | |
489 | } | |
490 | } | |
491 | ||
492 | struct {char lobyte; char hibyte;}; | |
493 | mode(f) | |
494 | { | |
495 | struct sgttyb stbuf; | |
496 | if (dout) return; | |
497 | ioctl(TIOCGETP, 0, &stbuf); | |
498 | tkill = stbuf.sg_kill; | |
499 | terase = stbuf.sg_erase; | |
500 | if (f == 0) { | |
501 | stbuf.sg_flags &= ~RAW; | |
502 | stbuf.sg_flags |= ECHO|CRMOD; | |
503 | } | |
504 | if (f == 1) { | |
505 | stbuf.sg_flags |= RAW; | |
506 | stbuf.sg_flags &= ECHO|CRMOD; | |
507 | } | |
508 | if (f == 2) { | |
509 | stbuf.sg_flags &= ~RAW; | |
510 | stbuf.sg_flags &= ~(ECHO|CRMOD); | |
511 | } | |
512 | ioctl(TIOCSETP, 0, &stbuf); | |
513 | } | |
514 | ||
515 | echo(s) | |
516 | char *s; | |
517 | { | |
518 | char *p; | |
519 | for (p=s;*p;p++); | |
520 | if (p>s) write(0,s,p-s); | |
521 | write(0,CRLF, sizeof(CRLF)); | |
522 | } | |
523 | ||
524 | prf(f, s) | |
525 | char *f; | |
526 | char *s; | |
527 | { | |
528 | fprintf(stderr, f, s); | |
529 | fprintf(stderr, CRLF); | |
530 | } | |
531 | ||
532 | exists(devname) | |
533 | char *devname; | |
534 | { | |
535 | if (access(devname, 0)==0) | |
536 | return(1); | |
537 | prf("%s does not exist", devname); | |
538 | return(0); | |
539 | } |