1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n]
32 * [-o|-e] [-L] [-C] telno | systemname [local-cmd]
33 *
34 * legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400.
35 *
36 * -c is used to specify which device will be used for making the
37 * call. The device argument is compared to the Type (first)
38 * field in the Devices file, and only those records that
39 * match will be used to make the call. Either -d or -t
40 * would be more intuitive options designations, but they
41 * are already in use.
42 * -l is for specifying a line unit from the file whose
43 * name is defined in /etc/uucp/Devices.
44 * -b is for forcing the number of bits per character processed on
45 * the connection. Valid values are '7' or '8'.
46 * -h is for half-duplex (local echoing).
47 * -t is for adding CR to LF on output to remote (for terminals).
48 * -d can be used to get some tracing & diagnostics.
49 * -o or -e is for odd or even parity on transmission to remote.
50 * -n will request the phone number from the user.
51 * -L will cause cu to go through the login chat sequence in the
52 * Systems file.
53 * -C will cause cu to run the local command specified at the end
54 * of the command line, instead of entering interactive mode.
55 * Telno is a telephone number with `=' for secondary dial-tone.
56 * If "-l dev" is used, speed is taken from /etc/uucp/Devices.
57 * Only systemnames that are included in /etc/uucp/Systems may
58 * be used.
59 *
60 * Escape with `~' at beginning of line:
61 *
62 * ~. quit,
63 *
64 * ~![cmd] execute shell (or 'cmd') locally,
65 *
66 * ~$cmd execute 'cmd' locally, stdout to remote,
67 *
68 * ~%break (alias ~%b) transmit BREAK to remote,
69 * ~%cd [dir] change directory to $HOME (or 'dir'),
70 * ~%debug (alias ~%d) toggles on/off the program debug trace,
71 * ~%divert allow unsolicited diversions to files,
72 * ~%ifc (alias ~%nostop) toggles on/off the DC3/DC1 input control,
73 * ~%ofc (alias ~%noostop) toggles on/off the DC3/DC1 output control,
74 * (certain remote systems cannot cope with DC3 or DC1).
75 * ~%old recognize old style silent diversions,
76 * ~%put from [to] put file from local to remote,
77 * ~%take from [to] take file from remote to local,
78 *
79 * ~l dump communication line ioctl settings,
80 * ~t dump terminal ioctl settings.
81 *
82 * Silent diversions are enabled only for use with the ~%take
83 * command by default for security reasons. Unsolicited diversions
84 * may be enabled using the ~%divert toggle. The 'new-style'
85 * diversion syntax is "~[local]>:filename", and is terminaled
86 * by "~[local]>", where 'local' is the nodename of the local
87 * system. This enables ~%take to operate properly when cu
88 * is used over multiple hops. 'old-style' diversion syntax may
89 * be enabled using the ~%old toggle. ('old-style' diversion
90 * should be avoided!)
91 *
92 * Cu no longer uses dial.c to reach the remote. Instead, cu places
93 * a telephone call to a remote system through the uucp conn() routine
94 * when the user picks the systemname option or through altconn()--
95 * which bypasses /etc/uucp/Systems -- if a telno or direct
96 * line is chosen. The line termio attributes are set in fixline(),
97 * before the remote connection is made. As a device-lockout semaphore
98 * mechanism, uucp creates an entry in /var/spool/locks whose name is
99 * LK.<MAJ>.<maj>.<min> where MAJ is the major device of the
100 * filesystem containing the device, and <maj> and <min> are the
101 * major and minor of the device.
102 * When cu terminates, for whatever reason, cleanup() must be
103 * called to "release" the device, and clean up entries from
104 * the locks directory. Cu runs with uucp ownership, and thus provides
105 * extra insurance that lock files will not be left around.
106 */
107
108 #include "uucp.h"
109 #include <locale.h>
110 #include <stropts.h>
111
112 #define MID BUFSIZ/2 /* mnemonic */
113 #define RUB '\177' /* mnemonic */
114 #define XON '\21' /* mnemonic */
115 #define XOFF '\23' /* mnemonic */
116 #define TTYIN 0 /* mnemonic */
117 #define TTYOUT 1 /* mnemonic */
118 #define TTYERR 2 /* mnemonic */
119 #define HUNGUP 2
120 #define YES 1 /* mnemonic */
121 #define NO 0 /* mnemonic */
122 #define IOERR 4 /* exit code */
123 #define MAXPATH 100
124 #define NPL 50
125
126 int Sflag=0;
127 int Cn; /*fd for remote comm line */
128 jmp_buf Sjbuf; /*needed by uucp routines*/
129
130 /* io buffering */
131 /* Wiobuf contains, in effect, 3 write buffers (to remote, to tty */
132 /* stdout, and to tty stderr) and Riobuf contains 2 read buffers */
133 /* (from remote, from tty). [WR]IOFD decides which one to use. */
134 /* [RW]iop holds current position in each. */
135 #define WIOFD(fd) (fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
136 #define RIOFD(fd) (fd == TTYIN ? 0 : 1)
137 #define WMASK(fd) (fd == Cn ? line_mask : term_mask)
138 #define RMASK(fd) (fd == Cn ? line_mask : term_mask)
139 #define WRIOBSZ 256
140 static char Riobuf[2*WRIOBSZ];
141 static char Wiobuf[3*WRIOBSZ];
142 static int Riocnt[2] = {0, 0};
143 static char *Riop[2];
144 static char *Wiop[3];
145
146 extern int optind; /* variable in getopt() */
147
148 extern char
149 *optarg;
150
151 static struct call Cucall; /* call structure for altconn() */
152
153 static int Saved_tty; /* was TCGETAW of _Tv0 successful? */
154 static int Saved_termios; /* was TCGETSW of _Tv0 successful? */
155 static struct termio _Tv, _Tv0; /* for saving, changing TTY atributes */
156 static struct termios _Tv0s; /* for saving, changing TTY atributes */
157 static struct termio _Lv; /* attributes for the line to remote */
158 static struct termios _Lvs; /* attributes for the line to remote */
159 static char prompt[BUFSIZ]= "[";
160 static struct utsname utsn;
161 static int command_line_hups = 0;
162
163 static char filename[BUFSIZ] = "/dev/null";
164
165 static char
166 _Cxc, /* place into which we do character io*/
167 _Tintr, /* current input INTR */
168 _Tquit, /* current input QUIT */
169 _Terase, /* current input ERASE */
170 _Tkill, /* current input KILL */
171 _Teol, /* current secondary input EOL */
172 _Myeof, /* current input EOF */
173 term_mask, /* mask value for local terminal */
174 line_mask; /* mask value for remote line */
175 /* either '0177' or '0377' */
176
177 int
178 Echoe, /* save users ECHOE bit */
179 Echok, /* save users ECHOK bit */
180 Intrupt=NO, /* interrupt indicator */
181 Ifc=YES, /* NO means remote can't XON/XOFF */
182 Ofc=YES, /* NO means local can't XON/XOFF */
183 Rtn_code=0, /* default return code */
184 Divert=NO, /* don't allow unsolicited redirection */
185 OldStyle=NO, /* don't handle old '~>:filename' syntax */
186 /* this will be mandatory in SVR4.1 */
187 Takeflag=NO, /* indicates a ~%take is in progress */
188 Dologin=NO, /* go through the login chat sequence */
189 Docmd=NO; /* execute command instead of interactive cu */
190
191 EXTERN int /* These are initialized in line.c */
192 Terminal, /* flag; remote is a terminal */
193 Oddflag, /* flag- odd parity option*/
194 Evenflag, /* flag- even parity option*/
195 Duplex, /* Unix= full duplex=YES; half = NO */
196 term_8bit, /* is terminal set for 8 bit processing */
197 line_8bit; /* is line set for 8 bit processing */
198
199 EXTERN int clear_hup();
200
201 pid_t
202 Child, /* pid for receive process */
203 Shell; /* pid for escape process */
204
205 static pid_t
206 dofork(); /* fork and return pid */
207
208 static int
209 r_char(), /* local io routine */
210 w_char(), /* local io routine */
211 wioflsh();
212
213 static void
214 _onintrpt(), /* interrupt routines */
215 _rcvdead(),
216 _quit(),
217 _bye();
218
219 extern void cleanup();
220 extern void tdmp();
221 extern int conn(), altconn(), transmit(), tilda();
222
223 static void
224 recfork(),
225 sysname(),
226 blckcnt(),
227 _flush(),
228 _shell(),
229 _dopercen(),
230 _receive(),
231 _mode(),
232 _w_str();
233
234 extern char *Myline; /* flag to force the requested line to be used */
235 extern char *Mytype; /* flag to force requested line type to be used
236 * rddev() will compare the string to the D_TYPE
237 * (first) field of the Devices record and skip any
238 * records where they are not equal. Mytype is set
239 * to point to the argument of the -c option from
240 * the command line. */
241 static char *P_USAGE= "Usage: %s [-dhtnLC] [-c device] [-s speed] [-l line] [-b 7|8]\n\t[-o | -e] telno | systemname [local-cmd]\n";
242 static char *P_CON_FAILED = "Connect failed: %s\r\n";
243 static char *P_Ct_OPEN = "Cannot open: %s\r\n";
244 static char *P_LINE_GONE = "Remote line gone\r\n";
245 static char *P_Ct_EXSH = "Can't execute shell\r\n";
246 static char *P_Ct_DIVERT = "Can't divert to %s\r\n";
247 static char *P_Ct_UNDIVERT = "Can't end diversion to %s\r\n";
248 static char *P_Bad_DIVERT = "Won't divert to %s. Unsolicited.\r\n";
249 static char *P_STARTWITH = "Use `~~' to start line with `~'\r\n";
250 static char *P_CNTAFTER = "File transmission interrupted after %ld bytes.\r\n";
251 static char *P_CNTLINES = "%d lines/";
252 static char *P_CNTCHAR = "%ld characters\r\n";
253 static char *P_FILEINTR = "File transmission interrupted\r\n";
254 static char *P_Ct_FK = "Can't fork -- try later\r\n";
255 static char *P_Ct_SPECIAL = "r\nCan't transmit special character `%#o'\r\n";
256 static char *P_TOOLONG = "\nLine too long\r\n";
257 static char *P_IOERR = "r\nIO error\r\n";
258 static char *P_USECMD = "Use `~$'cmd \r\n";
259 #ifdef forfutureuse
260 static char *P_USEPLUSCMD ="Use `~+'cmd \r\n";
261 #endif
262 #ifdef u3b
263 static char *P_NOTERMSTAT = "Can't get terminal status\r\n";
264 static char *P_3BCONSOLE = "Sorry, you can't cu from a 3B console\r\n";
265 #endif
266 static char *P_TELLENGTH = "Telno cannot exceed 58 digits!\r\n";
267
268 /***************************************************************
269 * main: get command line args, establish connection, and fork.
270 * Child invokes "receive" to read from remote & write to TTY.
271 * Main line invokes "transmit" to read TTY & write to remote.
272 ***************************************************************/
273
274 int
main(int argc,char * argv[])275 main(int argc, char *argv[])
276 {
277 extern void setservice();
278 extern int sysaccess();
279 char s[MAXPH];
280 char *string;
281 int i;
282 int errflag=0;
283 int lflag=0;
284 int nflag=0;
285 int systemname = 0;
286 char vdisable;
287
288 /* Set locale environment variables local definitions */
289 (void) setlocale(LC_ALL, "");
290 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
291 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
292 #endif
293 (void) textdomain(TEXT_DOMAIN);
294
295 Riop[0] = &Riobuf[0];
296 Riop[1] = &Riobuf[WRIOBSZ];
297 Wiop[0] = &Wiobuf[0];
298 Wiop[1] = &Wiobuf[WRIOBSZ];
299 Wiop[2] = &Wiobuf[2*WRIOBSZ];
300
301 Verbose = 1; /*for uucp callers, dialers feedback*/
302 if ((string = strrchr(argv[0], '/')) != NULL)
303 string++;
304 else
305 string = argv[0];
306 if (strlcpy(Progname, string, NAMESIZE) >= NAMESIZE) {
307 errno = ENAMETOOLONG;
308 perror("cu");
309 exit(1);
310 }
311 setservice(Progname);
312 if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
313 (void)fprintf(stderr,
314 gettext("%s: Cannot read Systems files\n"), Progname);
315 exit(1);
316 }
317 if ( sysaccess(EACCESS_DEVICES) != 0 ) {
318 (void)fprintf(stderr,
319 gettext("%s: Cannot read Devices files\n"), Progname);
320 exit(1);
321 }
322 if ( sysaccess(EACCESS_DIALERS) != 0 ) {
323 (void)fprintf(stderr,
324 gettext("%s: Cannot read Dialers files\n"), Progname);
325 exit(1);
326 }
327
328 Cucall.speed = "Any"; /*default speed*/
329 Cucall.line = CNULL;
330 Cucall.telno = CNULL;
331 Cucall.type = CNULL;
332
333 /*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
334 /*are set in fixline() in culine.c before remote connection is made */
335
336 while((i = getopt(argc, argv, "dhteons:l:c:b:LCH")) != EOF)
337 switch(i) {
338 case 'd':
339 Debug = 9; /*turns on uucp debugging-level 9*/
340 break;
341 case 'h':
342 Duplex = NO;
343 Ifc = NO;
344 Ofc = NO;
345 break;
346 case 't':
347 Terminal = YES;
348 break;
349 case 'e':
350 if ( Oddflag ) {
351 (void)fprintf(stderr,
352 gettext("%s: Cannot have both even and odd parity\n"),
353 argv[0]);
354 exit(1);
355 }
356 Evenflag = 1;
357 break;
358 case 'o':
359 if ( Evenflag ) {
360 (void)fprintf(stderr,
361 gettext("%s: Cannot have both even and odd parity\n"),
362 argv[0]);
363 exit(1);
364 }
365 Oddflag = 1;
366 break;
367 case 'n':
368 nflag++;
369 printf(gettext("Please enter the number: "));
370 /* Read line from stdin, remove trailing newline, if any */
371 if (fgets(s, sizeof(s), stdin) != NULL &&
372 strchr(s, '\n') != NULL)
373 s[strlen(s)-1] = '\0';
374 break;
375 case 's':
376 Sflag++;
377 Cucall.speed = optarg;
378 break;
379 case 'l':
380 lflag++;
381 Cucall.line = optarg;
382 break;
383 case 'c':
384 Cucall.type = optarg;
385 Mytype = optarg;
386 break;
387 case 'b':
388 line_8bit = ((*optarg=='7') ? NO : ((*optarg=='8') ? YES : -1));
389 if ( line_8bit == -1 ) {
390 (void) fprintf(stderr,
391 gettext("%s: b option value must be '7' or '8'\n"),
392 argv[0]);
393 exit(1);
394 }
395 break;
396 case 'L':
397 Dologin++;
398 break;
399 case 'C':
400 Docmd++;
401 break;
402 case 'H':
403 command_line_hups++;
404 break;
405 case '?':
406 ++errflag;
407 }
408
409 #ifdef u3b
410 {
411 struct stat buff;
412 if(fstat(TTYIN, &buff) < 0) {
413 VERBOSE(gettext(P_NOTERMSTAT),"");
414 exit(1);
415 } else if ( (buff.st_mode & S_IFMT) == S_IFCHR && buff.st_rdev == 0 ) {
416 VERBOSE(gettext(P_3BCONSOLE),"");
417 exit(1);
418 }
419 }
420 #endif
421
422 if((optind < argc && optind > 0) || (nflag && optind > 0)) {
423 if(nflag)
424 string=s;
425 else
426 string = strdup(argv[optind++]);
427 Cucall.telno = string;
428 if ( strlen(string) != strspn(string, "0123456789=-*#") ) {
429 /* if it's not a legitimate telno, then it should be a systemname */
430 if ( nflag ) {
431 (void)fprintf(stderr, gettext("%s: Bad phone number %s\n"),
432 argv[0], string);
433 (void) fprintf(stderr, gettext("Phone numbers may contain "
434 "only the digits 0 through 9 and the special\n"
435 "characters =, -, * and #.\n"));
436 exit(1);
437 }
438 systemname++;
439 }
440 } else
441 if(Cucall.line == CNULL) /*if none of above, must be direct */
442 ++errflag;
443
444 if(errflag) {
445 VERBOSE(gettext(P_USAGE), argv[0]);
446 exit(1);
447 }
448
449 if ((Cucall.telno != CNULL) &&
450 (strlen(Cucall.telno) >= (size_t)(MAXPH - 1))) {
451 VERBOSE(gettext(P_TELLENGTH),"");
452 exit(0);
453 }
454
455 /* save initial tty state */
456 if (!(Saved_termios = ( ioctl(TTYIN, TCGETS, &_Tv0s) >= 0 ))) {
457 Saved_tty = ( ioctl(TTYIN, TCGETA, &_Tv0) == 0 );
458 _Tv0s.c_lflag = _Tv0.c_lflag;
459 _Tv0s.c_oflag = _Tv0.c_oflag;
460 _Tv0s.c_iflag = _Tv0.c_iflag;
461 _Tv0s.c_cflag = _Tv0.c_cflag;
462 for(i = 0; i < NCC; i++)
463 _Tv0s.c_cc[i] = _Tv0.c_cc[i];
464 }
465
466 if (Saved_termios || Saved_tty) {
467 char *p;
468
469 /*
470 * We consider the terminal to be in 8 bit mode only if cs8 is set,
471 * istrip is not set, and we're not in the "C" locale. The "C"
472 * locale is by definition 7 bit only. This provides reasonable
473 * compatibility when running in the "C" locale (currently the default)
474 * and connecting to other systems, which are most often 7 bit systems.
475 */
476 term_8bit = ( (_Tv0s.c_cflag & CS8) && !(_Tv0s.c_iflag & ISTRIP) &&
477 ((p = setlocale(LC_CTYPE, NULL)) != NULL) && (strcmp(p, "C") != 0) );
478 if ( !Oddflag && !Evenflag )
479 if (_Tv0s.c_cflag & PARENB)
480 if (_Tv0s.c_cflag & PARODD)
481 Oddflag = 1;
482 else
483 Evenflag = 1;
484 }
485
486 if (line_8bit == -1)
487 line_8bit = term_8bit;
488
489 term_mask = ( term_8bit ? 0377 : 0177 );
490 line_mask = ( line_8bit ? 0377 : 0177 );
491
492 /* if not set, use the POSIX disabled designation */
493 #ifdef _POSIX_VDISABLE
494 vdisable = _POSIX_VDISABLE;
495 #else
496 vdisable = fpathconf(TTYIN, _PC_VDISABLE);
497 #endif
498 _Tintr = _Tv0s.c_cc[VINTR] ? _Tv0s.c_cc[VINTR] : vdisable;
499 _Tquit = _Tv0s.c_cc[VQUIT] ? _Tv0s.c_cc[VQUIT] : vdisable;
500 _Terase = _Tv0s.c_cc[VERASE] ? _Tv0s.c_cc[VERASE] : vdisable;
501 _Tkill = _Tv0s.c_cc[VKILL] ? _Tv0s.c_cc[VKILL] : vdisable;
502 _Teol = _Tv0s.c_cc[VEOL] ? _Tv0s.c_cc[VEOL] : vdisable;
503 _Myeof = _Tv0s.c_cc[VEOF] ? _Tv0s.c_cc[VEOF] : '\04';
504 Echoe = _Tv0s.c_lflag & ECHOE;
505 Echok = _Tv0s.c_lflag & ECHOK;
506
507 (void)signal(SIGHUP, cleanup);
508 (void)signal(SIGQUIT, cleanup);
509 (void)signal(SIGINT, cleanup);
510
511 /* place call to system; if "cu systemname", use conn() from uucp
512 directly. Otherwise, use altconn() which dummies in the
513 Systems file line.
514 */
515
516 if(systemname) {
517 if ( lflag )
518 (void)fprintf(stderr,
519 gettext("%s: Warning: -l flag ignored when system name used\n"),
520 argv[0]);
521 if ( Sflag )
522 (void)fprintf(stderr,
523 gettext("%s: Warning: -s flag ignored when system name used\n"),
524 argv[0]);
525 Cn = conn(string);
526 if ( (Cn < 0) && (Cucall.type != CNULL) )
527 Cn = altconn(&Cucall);
528 } else
529 Cn = altconn(&Cucall);
530
531 if(Cn < 0) {
532 VERBOSE(gettext(P_CON_FAILED),UERRORTEXT);
533 cleanup(-Cn);
534 } else {
535 struct stat Cnsbuf;
536 if ( fstat(Cn, &Cnsbuf) == 0 )
537 Dev_mode = Cnsbuf.st_mode;
538 else
539 Dev_mode = R_DEVICEMODE;
540 fchmod(Cn, M_DEVICEMODE);
541 }
542
543 if ((Docmd) && (argv[optind] == NULL)) {
544 (void) fprintf(stderr,gettext("cu: local cmd is required, -C is ignored.\n"));
545 VERBOSE(gettext(P_USAGE), argv[0]);
546 Docmd=NO;
547 }
548
549 if (!Docmd) {
550 Euid = geteuid();
551 if((setuid(getuid()) < 0) || (setgid(getgid()) < 0)) {
552 VERBOSE("Unable to setuid/gid\n%s", "");
553 cleanup(101);
554 }
555 }
556
557 if(Debug)
558 tdmp(Cn);
559
560 /* At this point succeeded in getting an open communication line */
561 /* Conn() takes care of closing the Systems file */
562
563 if (!Docmd) {
564 (void)signal(SIGINT,_onintrpt);
565 _mode(1); /* put terminal in `raw' mode */
566 VERBOSE("Connected\007\r\n%s", ""); /*bell!*/
567
568 /* must catch signals before fork. if not and if _receive() */
569 /* fails in just the right (wrong?) way, _rcvdead() can be */
570 /* called and do "kill(getppid(),SIGUSR1);" before parent */
571 /* has done calls to signal() after recfork(). */
572 (void)signal(SIGUSR1, _bye);
573 (void)signal(SIGHUP, cleanup);
574 (void)signal(SIGQUIT, _onintrpt);
575
576 sysname(&prompt[1]); /* set up system name prompt */
577 (void) strcat(prompt, "]");
578
579 recfork(); /* checks for child == 0 */
580
581 if(Child > 0) {
582 /*
583 * Because the child counts hangups for the -H flag,
584 * and because we fork a new child when doing (e.g.)
585 * ~%take, we assume the first child we fork has
586 * processed all the hangups and we reset the count here.
587 * We really should pass the remaining count back from
588 * the child to the parent when we kill the child.
589 */
590 command_line_hups = 0;
591 Rtn_code = transmit();
592 _quit(Rtn_code);
593 /*NOTREACHED*/
594 }
595 } else {
596 /*
597 * Fork a child to run the specified command,
598 * wait for it to finish, and clean up.
599 */
600 Child = dofork();
601 if (Child == 0) {
602 close(0);
603 close(1);
604 dup(Cn);
605 dup(Cn);
606 close(Cn);
607 setgid(getgid());
608 setuid(getuid());
609 execvp(argv[optind], &argv[optind]);
610 exit(-1);
611 /* NOTREACHED */
612 }
613 wait(0);
614 /* XXX - should return wait status as our exit code */
615 }
616 cleanup(Cn);
617 /*NOTREACHED*/
618 return (0);
619 }
620
621 /*
622 * Kill the present child, if it exists, then fork a new one.
623 */
624
625 static void
recfork(void)626 recfork(void)
627 {
628 int ret, status;
629 if (Child) {
630 kill(Child, SIGKILL);
631 while ( (ret = wait(&status)) != Child )
632 if (ret == -1 && errno != EINTR)
633 break;
634 }
635 Child = dofork();
636 if(Child == 0) {
637 (void)signal(SIGUSR1, SIG_DFL);
638 (void)signal(SIGHUP, _rcvdead);
639 (void)signal(SIGQUIT, SIG_IGN);
640 (void)signal(SIGINT, SIG_IGN);
641
642 _receive(); /* This should run until killed */
643 /*NOTREACHED*/
644 }
645 return;
646 }
647
648 /***************************************************************
649 * transmit: copy stdin to remote fd, except:
650 * ~. terminate
651 * ~! local login-style shell
652 * ~!cmd execute cmd locally
653 * ~$proc execute proc locally, send output to line
654 * ~%cmd execute builtin cmd (put, take, or break)
655 ****************************************************************/
656 #ifdef forfutureuse
657 /*****************************************************************
658 * ~+proc execute locally, with stdout to and stdin from line.
659 ******************************************************************/
660 #endif
661
662 int
transmit(void)663 transmit(void)
664 {
665 char b[BUFSIZ];
666 char *p;
667 int escape;
668 int id = 0; /* flag for systemname prompt on tilda escape */
669
670 CDEBUG(4,"transmit started\n\r%s", "");
671
672 /* In main loop, always waiting to read characters from */
673 /* keyboard; writes characters to remote, or to TTYOUT */
674 /* on a tilda escape */
675
676 for (;;) {
677 p = b;
678 while(r_char(TTYIN) == YES) {
679 if(p == b) /* Escape on leading ~ */
680 escape = (_Cxc == '~');
681 if(p == b+1) /* But not on leading ~~ */
682 escape &= (_Cxc != '~');
683 if(escape) {
684 if(_Cxc == '\n' || _Cxc == '\r' || _Cxc == _Teol) {
685 *p = '\0';
686 if(tilda(b+1) == YES)
687 return(0);
688 id = 0;
689 break;
690 }
691 if(_Cxc == _Tintr || _Cxc == _Tkill || _Cxc == _Tquit ||
692 (Intrupt && _Cxc == '\0')) {
693 if(_Cxc == _Tkill) {
694 if(Echok)
695 VERBOSE("\r\n%s", "");
696 } else {
697 _Cxc = '\r';
698 if( w_char(Cn) == NO) {
699 VERBOSE(gettext(P_LINE_GONE),"");
700 return(IOERR);
701 }
702 id=0;
703 }
704 break;
705 }
706 if((p == b+1) && (_Cxc != _Terase) && (!id)) {
707 id = 1;
708 VERBOSE("%s", prompt);
709 }
710 if(_Cxc == _Terase) {
711 p = (--p < b)? b:p;
712 if(p > b)
713 if(Echoe) {
714 VERBOSE("\b \b%s", "");
715 } else
716 (void)w_char(TTYOUT);
717 } else {
718 (void)w_char(TTYOUT);
719 if(p-b < BUFSIZ)
720 *p++ = _Cxc;
721 else {
722 VERBOSE(gettext(P_TOOLONG),"");
723 break;
724 }
725 }
726 /*not a tilda escape command*/
727 } else {
728 if(Intrupt && _Cxc == '\0') {
729 CDEBUG(4,"got break in transmit\n\r%s", "");
730 Intrupt = NO;
731 (*genbrk)(Cn);
732 _flush();
733 break;
734 }
735 if(w_char(Cn) == NO) {
736 VERBOSE(gettext(P_LINE_GONE),"");
737 return(IOERR);
738 }
739 if(Duplex == NO) {
740 if((w_char(TTYERR) == NO) || (wioflsh(TTYERR) == NO))
741 return(IOERR);
742 }
743 if ((_Cxc == _Tintr) || (_Cxc == _Tquit) ||
744 ( (p==b) && (_Cxc == _Myeof) ) ) {
745 CDEBUG(4,"got a tintr\n\r%s", "");
746 _flush();
747 break;
748 }
749 if(_Cxc == '\n' || _Cxc == '\r' ||
750 _Cxc == _Teol || _Cxc == _Tkill) {
751 id=0;
752 Takeflag = NO;
753 break;
754 }
755 p = (char*)0;
756 }
757 }
758 }
759 }
760
761 /***************************************************************
762 * routine to halt input from remote and flush buffers
763 ***************************************************************/
764 static void
_flush(void)765 _flush(void)
766 {
767 (void)ioctl(TTYOUT, TCXONC, 0); /* stop tty output */
768 (void)ioctl(Cn, TCFLSH, 0); /* flush remote input */
769 (void)ioctl(TTYOUT, TCFLSH, 1); /* flush tty output */
770 (void)ioctl(TTYOUT, TCXONC, 1); /* restart tty output */
771 if(Takeflag == NO) {
772 return; /* didn't interupt file transmission */
773 }
774 VERBOSE(gettext(P_FILEINTR),"");
775 (void)sleep(3);
776 _w_str("echo '\n~>\n';mesg y;stty echo\n");
777 Takeflag = NO;
778 return;
779 }
780
781 /**************************************************************
782 * command interpreter for escape lines
783 **************************************************************/
784 int
tilda(cmd)785 tilda(cmd)
786 char *cmd;
787 {
788
789 VERBOSE("\r\n%s", "");
790 CDEBUG(4,"call tilda(%s)\r\n", cmd);
791
792 switch(cmd[0]) {
793 case CSUSP:
794 case CDSUSP:
795 _mode(0);
796 kill(cmd[0] == CDSUSP ? getpid() : (pid_t) 0, SIGTSTP);
797 _mode(1);
798 break;
799 case '.':
800 if(Cucall.telno == CNULL)
801 if(cmd[1] != '.') {
802 _w_str("\04\04\04\04\04");
803 if (Child)
804 kill(Child, SIGKILL);
805 if (ioctl (Cn, TCGETS, &_Lvs) < 0) {
806 (void) ioctl (Cn, TCGETA, &_Lv);
807 /* speed to zero for hangup */
808 _Lv.c_cflag = 0;
809 (void) ioctl (Cn, TCSETAW, &_Lv);
810 } else {
811 /* speed to zero for hangup */
812 _Lvs.c_cflag &= 0xffff0000;
813 cfsetospeed(&_Lvs, B0);
814 (void) ioctl (Cn, TCSETSW, &_Lvs);
815 }
816 (void) sleep (2);
817 }
818 return(YES);
819 case '!':
820 _shell(cmd); /* local shell */
821 VERBOSE("\r%c\r\n", *cmd);
822 VERBOSE("(continue)%s", "");
823 break;
824 case '$':
825 if(cmd[1] == '\0') {
826 VERBOSE(gettext(P_USECMD),"");
827 VERBOSE("(continue)%s", "");
828 } else {
829 _shell(cmd); /*Local shell */
830 VERBOSE("\r%c\r\n", *cmd);
831 }
832 break;
833
834 #ifdef forfutureuse
835 case '+':
836 if(cmd[1] == '\0') {
837 VERBOSE(gettext(P_USEPLUSCMD), "");
838 VERBOSE("(continue)%s", "");
839 } else {
840 if (*cmd == '+')
841 /* must suspend receive to give*/
842 /*remote out to stdin of cmd */
843 kill(Child, SIGKILL);
844 _shell(cmd); /* Local shell */
845 if (*cmd == '+')
846 recfork();
847 VERBOSE("\r%c\r\n", *cmd);
848 }
849 break;
850 #endif
851 case '%':
852 _dopercen(++cmd);
853 break;
854
855 case 't':
856 tdmp(TTYIN);
857 VERBOSE("(continue)%s", "");
858 break;
859 case 'l':
860 tdmp(Cn);
861 VERBOSE("(continue)%s", "");
862 break;
863
864 default:
865 VERBOSE(gettext(P_STARTWITH),"");
866 VERBOSE("(continue)%s", "");
867 break;
868 }
869 return(NO);
870 }
871
872 /***************************************************************
873 * The routine "shell" takes an argument starting with
874 * either "!" or "$", and terminated with '\0'.
875 * If $arg, arg is the name of a local shell file which
876 * is executed and its output is passed to the remote.
877 * If !arg, we escape to a local shell to execute arg
878 * with output to TTY, and if arg is null, escape to
879 * a local shell and blind the remote line. In either
880 * case, '^D' will kill the escape status.
881 **************************************************************/
882
883 #ifdef forfutureuse
884 /***************************************************************
885 * Another argument to the routine "shell" may be +. If +arg,
886 * arg is the name of a local shell file which is executed with
887 * stdin from and stdout to the remote.
888 **************************************************************/
889 #endif
890
891 static void
_shell(char * str)892 _shell(char *str)
893 {
894 pid_t fk, w_ret;
895 void (*xx)(), (*yy)();
896
897 CDEBUG(4,"call _shell(%s)\r\n", str);
898 fk = dofork();
899 if(fk < 0)
900 return;
901 Shell = fk;
902 _mode(0); /* restore normal tty attributes */
903 xx = signal(SIGINT, SIG_IGN);
904 yy = signal(SIGQUIT, SIG_IGN);
905 if(fk == 0) {
906 char *shell;
907
908 if( (shell = getenv("SHELL")) == NULL)
909 /* use default if user's shell is not set */
910 shell = SHELL;
911 (void)close(TTYOUT);
912
913 /***********************************************
914 * Hook-up our "standard output"
915 * to either the tty for '!' or the line
916 * for '$' as appropriate
917 ***********************************************/
918 #ifdef forfutureuse
919
920 /************************************************
921 * Or to the line for '+'.
922 **********************************************/
923 #endif
924
925 (void)fcntl((*str == '!')? TTYERR:Cn,F_DUPFD,TTYOUT);
926
927 #ifdef forfutureuse
928 /*************************************************
929 * Hook-up "standard input" to the line for '+'.
930 * **********************************************/
931 if (*str == '+') {
932 (void)close(TTYIN);
933 (void)fcntl(Cn,F_DUPFD,TTYIN);
934 }
935 #endif
936
937 /***********************************************
938 * Hook-up our "standard input"
939 * to the tty for '!' and '$'.
940 ***********************************************/
941
942 (void)close(Cn); /*parent still has Cn*/
943 (void)signal(SIGINT, SIG_DFL);
944 (void)signal(SIGHUP, SIG_DFL);
945 (void)signal(SIGQUIT, SIG_DFL);
946 (void)signal(SIGUSR1, SIG_DFL);
947 if(*++str == '\0')
948 (void)execl(shell,shell,(char*) 0,(char*) 0,(char *) 0);
949 else
950 (void)execl(shell,"sh","-c",str,(char *) 0);
951 VERBOSE(gettext(P_Ct_EXSH),"");
952 exit(0);
953 }
954 while ((w_ret = wait((int*)0)) != fk)
955 if (w_ret == -1 && errno != EINTR)
956 break;
957 Shell = 0;
958 (void)signal(SIGINT, xx);
959 (void)signal(SIGQUIT, yy);
960 _mode(1);
961 return;
962 }
963
964
965 /***************************************************************
966 * This function implements the 'put', 'take', 'break',
967 * 'ifc' (aliased to nostop) and 'ofc' (aliased to noostop)
968 * commands which are internal to cu.
969 ***************************************************************/
970
971 static void
_dopercen(char * cmd)972 _dopercen(char *cmd)
973 {
974 char *arg[5];
975 char *getpath;
976 char mypath[MAXPATH];
977 int narg;
978
979 blckcnt((long)(-1));
980
981 CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd);
982
983 arg[narg=0] = strtok(cmd, " \t\n");
984
985 /* following loop breaks out the command and args */
986 while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
987 if(narg < 4)
988 continue;
989 else
990 break;
991 }
992
993 /* ~%take file option */
994 if(EQUALS(arg[0], "take")) {
995 if(narg < 2 || narg > 3) {
996 VERBOSE("usage: ~%%take from [to]\r\n%s", "");
997 VERBOSE("(continue)%s", "");
998 return;
999 }
1000 if(narg == 2)
1001 arg[2] = arg[1];
1002 (void) strcpy(filename, arg[2]);
1003 recfork(); /* fork so child (receive) knows filename */
1004
1005 /*
1006 * be sure that the remote file (arg[1]) exists before
1007 * you try to take it. otherwise, the error message from
1008 * cat will wind up in the local file (arg[2])
1009 *
1010 * what we're doing is:
1011 * stty -echo; \
1012 * if test -r arg1
1013 * then (echo '~[local]'>arg2; cat arg1; echo '~[local]'>)
1014 * else echo can't open: arg1
1015 * fi; \
1016 * stty echo
1017 *
1018 */
1019 _w_str("stty -echo;if test -r ");
1020 _w_str(arg[1]);
1021 _w_str("; then (echo '~");
1022 _w_str(prompt);
1023 _w_str(">'");
1024 _w_str(arg[2]);
1025 _w_str(";cat ");
1026 _w_str(arg[1]);
1027 _w_str(";echo '~");
1028 _w_str(prompt);
1029 _w_str(">'); else echo cant\\'t open: ");
1030 _w_str(arg[1]);
1031 _w_str("; fi;stty echo\n");
1032 Takeflag = YES;
1033 return;
1034 }
1035 /* ~%put file option*/
1036 if(EQUALS(arg[0], "put")) {
1037 FILE *file;
1038 char ch, buf[BUFSIZ], spec[NCC+1], *b, *p, *q;
1039 int i, j, len, tc=0, lines=0;
1040 long chars=0L;
1041
1042 if(narg < 2 || narg > 3) {
1043 VERBOSE("usage: ~%%put from [to]\r\n%s", "");
1044 VERBOSE("(continue)%s", "");
1045 return;
1046 }
1047 if(narg == 2)
1048 arg[2] = arg[1];
1049
1050 if((file = fopen(arg[1], "r")) == NULL) {
1051 VERBOSE(gettext(P_Ct_OPEN), arg[1]);
1052 VERBOSE("(continue)%s", "");
1053 return;
1054 }
1055 /*
1056 * if cannot write into file on remote machine, write into
1057 * /dev/null
1058 *
1059 * what we're doing is:
1060 * stty -echo
1061 * (cat - > arg2) || cat - > /dev/null
1062 * stty echo
1063 */
1064 _w_str("stty -echo;(cat - >");
1065 _w_str(arg[2]);
1066 _w_str(")||cat - >/dev/null;stty echo\n");
1067 Intrupt = NO;
1068 for(i=0,j=0; i < NCC; ++i)
1069 if((ch=_Tv0s.c_cc[i]) != '\0')
1070 spec[j++] = ch;
1071 spec[j] = '\0';
1072 _mode(2); /*accept interrupts from keyboard*/
1073 (void)sleep(5); /*hope that w_str info digested*/
1074
1075 /* Read characters line by line into buf to write to */
1076 /* remote with character and line count for blckcnt */
1077 while(Intrupt == NO &&
1078 fgets(b= &buf[MID],MID,file) != NULL) {
1079 /* worse case is each char must be escaped*/
1080 len = strlen(b);
1081 chars += len; /* character count */
1082 p = b;
1083 while(q = strpbrk(p, spec)) {
1084 if(*q == _Tintr || *q == _Tquit || *q == _Teol) {
1085 VERBOSE(gettext(P_Ct_SPECIAL), *q);
1086 (void)strcpy(q, q+1);
1087 Intrupt = YES;
1088 } else {
1089 b = strncpy(b-1, b, q-b);
1090 *(q-1) = '\\';
1091 }
1092 p = q+1;
1093 }
1094 if((tc += len) >= MID) {
1095 (void)sleep(1);
1096 tc = len;
1097 }
1098 if(write(Cn, b, (unsigned)strlen(b)) < 0) {
1099 VERBOSE(gettext(P_IOERR),"");
1100 Intrupt = YES;
1101 break;
1102 }
1103 ++lines; /* line count */
1104 blckcnt((long)chars);
1105 }
1106 _mode(1);
1107 blckcnt((long)(-2)); /* close */
1108 (void)fclose(file);
1109 if(Intrupt == YES) {
1110 Intrupt = NO;
1111 _w_str("\n");
1112 VERBOSE(gettext(P_CNTAFTER), ++chars);
1113 } else {
1114 VERBOSE(gettext(P_CNTLINES), lines);
1115 VERBOSE(gettext(P_CNTCHAR),chars);
1116 }
1117 (void)sleep(3);
1118 _w_str("\04");
1119 return;
1120 }
1121
1122 /* ~%b or ~%break */
1123 if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
1124 (*genbrk)(Cn);
1125 return;
1126 }
1127 /* ~%d or ~%debug toggle */
1128 if(EQUALS(arg[0], "d") || EQUALS(arg[0], "debug")) {
1129 if(Debug == 0)
1130 Debug = 9;
1131 else
1132 Debug = 0;
1133 VERBOSE("(continue)%s", "");
1134 return;
1135 }
1136 /* ~%[ifc|nostop] toggles start/stop input control */
1137 if( EQUALS(arg[0], "ifc") || EQUALS(arg[0], "nostop") ) {
1138 (void)ioctl(Cn, TCGETA, &_Tv);
1139 Ifc = !Ifc;
1140 if(Ifc == YES)
1141 _Tv.c_iflag |= IXOFF;
1142 else
1143 _Tv.c_iflag &= ~IXOFF;
1144 (void)ioctl(Cn, TCSETAW, &_Tv);
1145 _mode(1);
1146 VERBOSE("(ifc %s)", (Ifc ? "enabled" : "disabled"));
1147 VERBOSE("(continue)%s", "");
1148 return;
1149 }
1150 /* ~%[ofc|noostop] toggles start/stop output control */
1151 if( EQUALS(arg[0], "ofc") || EQUALS(arg[0], "noostop") ) {
1152 (void)ioctl(Cn, TCGETA, &_Tv);
1153 Ofc = !Ofc;
1154 if(Ofc == YES)
1155 _Tv.c_iflag |= IXON;
1156 else
1157 _Tv.c_iflag &= ~IXON;
1158 (void)ioctl(Cn, TCSETAW, &_Tv);
1159 _mode(1);
1160 VERBOSE("(ofc %s)", (Ofc ? "enabled" : "disabled"));
1161 VERBOSE("(continue)%s", "");
1162 return;
1163 }
1164 /* ~%divert toggles unsolicited redirection security */
1165 if( EQUALS(arg[0], "divert") ) {
1166 Divert = !Divert;
1167 recfork(); /* fork a new child so it knows about change */
1168 VERBOSE("(unsolicited diversion %s)", (Divert ? "enabled" : "disabled"));
1169 VERBOSE("(continue)%s", "");
1170 return;
1171 }
1172 /* ~%old toggles recognition of old-style '~>:filename' */
1173 if( EQUALS(arg[0], "old") ) {
1174 OldStyle = !OldStyle;
1175 recfork(); /* fork a new child so it knows about change */
1176 VERBOSE("(old-style diversion %s)", (OldStyle ? "enabled" : "disabled"));
1177 VERBOSE("(continue)%s", "");
1178 return;
1179 }
1180 /* Change local current directory */
1181 if(EQUALS(arg[0], "cd")) {
1182 if (narg < 2) {
1183 getpath = getenv("HOME");
1184 strlcpy(mypath, getpath, sizeof (mypath));
1185 if(chdir(mypath) < 0) {
1186 VERBOSE("Cannot change to %s\r\n", mypath);
1187 VERBOSE("(continue)%s", "");
1188 return;
1189 }
1190 } else if (chdir(arg[1]) < 0) {
1191 VERBOSE("Cannot change to %s\r\n", arg[1]);
1192 VERBOSE("(continue)%s", "");
1193 return;
1194 }
1195 recfork(); /* fork a new child so it knows about change */
1196 VERBOSE("(continue)%s", "");
1197 return;
1198 }
1199
1200 if (arg[0] == (char *) NULL)
1201 arg[0] = "";
1202
1203 VERBOSE("~%%%s unknown to cu\r\n", arg[0]);
1204 VERBOSE("(continue)%s", "");
1205 return;
1206 }
1207
1208 /***************************************************************
1209 * receive: read from remote line, write to fd=1 (TTYOUT)
1210 * catch:
1211 * ~>[>]:file
1212 * .
1213 * . stuff for file
1214 * .
1215 * ~> (ends diversion)
1216 ***************************************************************/
1217
1218 static void
_receive(void)1219 _receive(void)
1220 {
1221 int silent = NO, file = -1;
1222 char *p;
1223 int tic;
1224 int for_me = NO;
1225 char b[BUFSIZ];
1226 char *b_p;
1227 long count;
1228 int line_ok = 1, rval;
1229
1230 CDEBUG(4,"_receive started\r\n%s", "");
1231
1232 b[0] = '\0';
1233 b_p = p = b;
1234
1235 while(line_ok) {
1236 rval = r_char(Cn);
1237 if (rval == NO) {
1238 line_ok = 0;
1239 continue;
1240 }
1241 if (rval == HUNGUP) {
1242 if (command_line_hups > 0) {
1243 CDEBUG(4, "Ignoring device hangup\n%s", "");
1244 command_line_hups--;
1245 (void) setuid(Euid); /* reacquire privileges */
1246 if (clear_hup(Cn) != SUCCESS) {
1247 DEBUG(4, "Unable to clear hup on device\n%s", "");
1248 line_ok = 0;
1249 }
1250 (void) setuid(getuid()); /* relinquish privileges */
1251 } else
1252 line_ok = 0;
1253 continue;
1254 }
1255
1256 if(silent == NO) /* ie., if not redirecting from screen */
1257 if(w_char(TTYOUT) == NO)
1258 _rcvdead(IOERR); /* this will exit */
1259 /* remove CR's and fill inserted by remote */
1260 if(_Cxc == '\0' || _Cxc == RUB || _Cxc == '\r')
1261 continue;
1262 *p++ = _Cxc;
1263 if(_Cxc != '\n' && (p-b) < BUFSIZ)
1264 continue;
1265 /* ****************************************** */
1266 /* This code deals with ~%take file diversion */
1267 /* ****************************************** */
1268 if (b[0] == '~') {
1269 int append;
1270
1271 if (EQUALSN(&b[1],prompt,strlen(prompt))) {
1272 b_p = b + 1 + strlen(prompt);
1273 for_me = YES;
1274 } else {
1275 b_p = b + 1;
1276 for_me = NO;
1277 }
1278 if ( (for_me || OldStyle) && (*b_p == '>') ) {
1279 /* This is an acceptable '~[uname]>' line */
1280 b_p++;
1281 if ( (*b_p == '\n') && (silent == YES) ) {
1282 /* end of diversion */
1283 *b_p = '\0';
1284 (void) strcpy(filename, "/dev/null");
1285 if ( file >= 0 && close(file) ) {
1286 VERBOSE(gettext(P_Ct_UNDIVERT), b_p);
1287 perror(gettext("cu: close failed"));
1288 VERBOSE("%s","\r");
1289 }
1290 silent = NO;
1291 blckcnt((long)(-2));
1292 VERBOSE("%s\r\n", b);
1293 VERBOSE(gettext(P_CNTLINES), tic);
1294 VERBOSE(gettext(P_CNTCHAR), count);
1295 file = -1;
1296 p = b;
1297 continue;
1298 } else if (*b_p != '\n') {
1299 if ( *b_p == '>' ) {
1300 append = 1;
1301 b_p++;
1302 }
1303 if ( (for_me || (OldStyle && (*b_p == ':'))) && (silent == NO) ) {
1304 /* terminate filename string */
1305 *(p-1) = '\0';
1306 if ( *b_p == ':' )
1307 b_p++;
1308 if ( !EQUALS(filename, b_p) ) {
1309 if ( !Divert || !EQUALS(filename, "/dev/null") ) {
1310 VERBOSE(gettext(P_Bad_DIVERT), b_p);
1311 (void) strcpy(filename, "/dev/null");
1312 append = 1;
1313 } else {
1314 (void) strcpy(filename, b_p);
1315 }
1316 }
1317 if ( append && ((file=open(filename,O_WRONLY)) >= 0) )
1318 (void)lseek(file, 0L, 2);
1319 else
1320 file = creat(filename, PUB_FILEMODE);
1321 if (file < 0) {
1322 VERBOSE(gettext(P_Ct_DIVERT), filename);
1323 perror(gettext("cu: open|creat failed"));
1324 VERBOSE("%s","\r");
1325 (void)sleep(5); /* 10 seemed too long*/
1326 }
1327 silent = YES;
1328 count = tic = 0;
1329 p = b;
1330 continue;
1331 }
1332 }
1333 }
1334 }
1335 /* Regular data, divert if appropriate */
1336 if ( silent == YES ) {
1337 if ( file >= 0)
1338 (void)write(file, b, (unsigned)(p-b));
1339 count += p-b; /* tally char count */
1340 ++tic; /* tally lines */
1341 blckcnt((long)count);
1342 }
1343 p = b;
1344 }
1345 /*
1346 * we used to tell of lost carrier here, but now
1347 * defer to _bye() so that escape processes are
1348 * not interrupted.
1349 */
1350 _rcvdead(IOERR);
1351 return;
1352 }
1353
1354 /***************************************************************
1355 * change the TTY attributes of the users terminal:
1356 * 0 means restore attributes to pre-cu status.
1357 * 1 means set `raw' mode for use during cu session.
1358 * 2 means like 1 but accept interrupts from the keyboard.
1359 ***************************************************************/
1360 static void
_mode(int arg)1361 _mode(int arg)
1362 {
1363 int i;
1364
1365 CDEBUG(4,"call _mode(%d)\r\n", arg);
1366 if(arg == 0) {
1367 if ( Saved_termios )
1368 (void)ioctl(TTYIN, TCSETSW, &_Tv0s);
1369 else if ( Saved_tty ) {
1370 _Tv0.c_lflag = _Tv0s.c_lflag;
1371 _Tv0.c_oflag = _Tv0s.c_oflag;
1372 _Tv0.c_iflag = _Tv0s.c_iflag;
1373 _Tv0.c_cflag = _Tv0s.c_cflag;
1374 for(i = 0; i < NCC; i++)
1375 _Tv0.c_cc[i] = _Tv0s.c_cc[i];
1376 (void)ioctl(TTYIN, TCSETAW, &_Tv0);
1377 }
1378 } else {
1379 (void)ioctl(TTYIN, TCGETA, &_Tv);
1380 if(arg == 1) {
1381 _Tv.c_iflag &= ~(INLCR | ICRNL | IGNCR | IUCLC);
1382 if ( !term_8bit )
1383 _Tv.c_iflag |= ISTRIP;
1384 _Tv.c_oflag |= OPOST;
1385 _Tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
1386 _Tv.c_lflag &= ~(ICANON | ISIG | ECHO);
1387 if(Ifc == NO)
1388 _Tv.c_iflag &= ~IXON;
1389 else
1390 _Tv.c_iflag |= IXON;
1391 if(Ofc == NO)
1392 _Tv.c_iflag &= ~IXOFF;
1393 else
1394 _Tv.c_iflag |= IXOFF;
1395 if(Terminal) {
1396 _Tv.c_oflag |= ONLCR;
1397 _Tv.c_iflag |= ICRNL;
1398 }
1399 _Tv.c_cc[VEOF] = '\01';
1400 _Tv.c_cc[VEOL] = '\0';
1401 }
1402 if(arg == 2) {
1403 _Tv.c_iflag |= IXON;
1404 _Tv.c_lflag |= ISIG;
1405 }
1406 (void)ioctl(TTYIN, TCSETAW, &_Tv);
1407 }
1408 return;
1409 }
1410
1411
1412 static pid_t
dofork(void)1413 dofork(void)
1414 {
1415 int i;
1416 pid_t x;
1417
1418 for(i = 0; i < 6; ++i) {
1419 if((x = fork()) >= 0) {
1420 return(x);
1421 }
1422 }
1423
1424 if(Debug) perror("dofork");
1425
1426 VERBOSE(gettext(P_Ct_FK),"");
1427 return(x);
1428 }
1429
1430 static int
r_char(int fd)1431 r_char(int fd)
1432 {
1433 int rtn = 1, rfd;
1434 char *riobuf;
1435
1436 /* find starting pos in correct buffer in Riobuf */
1437 rfd = RIOFD(fd);
1438 riobuf = &Riobuf[rfd*WRIOBSZ];
1439
1440 if (Riop[rfd] >= &riobuf[Riocnt[rfd]]) {
1441 /* empty read buffer - refill it */
1442
1443 /* flush any waiting output */
1444 if ( (wioflsh(Cn) == NO ) || (wioflsh(TTYOUT) == NO) )
1445 return(NO);
1446
1447 while((rtn = read(fd, riobuf, WRIOBSZ)) < 0){
1448 if(errno == EINTR) {
1449 /* onintrpt() called asynchronously before this line */
1450 if(Intrupt == YES) {
1451 /* got a BREAK */
1452 _Cxc = '\0';
1453 return(YES);
1454 } else {
1455 /*a signal other than interrupt*/
1456 /*received during read*/
1457 continue;
1458 }
1459 } else {
1460 CDEBUG(4,"got read error, not EINTR\n\r%s", "");
1461 break; /* something wrong */
1462 }
1463 }
1464 if (rtn > 0) {
1465 /* reset current position in buffer */
1466 /* and count of available chars */
1467 Riop[rfd] = riobuf;
1468 Riocnt[rfd] = rtn;
1469 }
1470 }
1471
1472 if ( rtn > 0 ) {
1473 _Cxc = *(Riop[rfd]++) & RMASK(fd); /* mask off appropriate bits */
1474 return(YES);
1475 } else if (rtn == 0) {
1476 _Cxc = '\0';
1477 return (HUNGUP);
1478 } else {
1479 _Cxc = '\0';
1480 return(NO);
1481 }
1482 }
1483
1484 static int
w_char(int fd)1485 w_char(int fd)
1486 {
1487 int wfd;
1488 char *wiobuf;
1489
1490 /* find starting pos in correct buffer in Wiobuf */
1491 wfd = WIOFD(fd);
1492 wiobuf = &Wiobuf[wfd*WRIOBSZ];
1493
1494 if (Wiop[wfd] >= &wiobuf[WRIOBSZ]) {
1495 /* full output buffer - flush it */
1496 if ( wioflsh(fd) == NO )
1497 return(NO);
1498 }
1499 *(Wiop[wfd]++) = _Cxc & WMASK(fd); /* mask off appropriate bits */
1500 return(YES);
1501 }
1502
1503 /* wioflsh flush output buffer */
1504 static int
wioflsh(int fd)1505 wioflsh(int fd)
1506 {
1507 int wfd;
1508 char *wiobuf;
1509
1510 /* find starting pos in correct buffer in Wiobuf */
1511 wfd = WIOFD(fd);
1512 wiobuf = &Wiobuf[wfd*WRIOBSZ];
1513
1514 if (Wiop[wfd] > wiobuf) {
1515 /* there's something in the buffer */
1516 while(write(fd, wiobuf, (Wiop[wfd] - wiobuf)) < 0) {
1517 if(errno == EINTR) {
1518 if(Intrupt == YES) {
1519 VERBOSE("\ncu: Output blocked\r\n%s", "");
1520 _quit(IOERR);
1521 } else
1522 continue; /* alarm went off */
1523 } else {
1524 Wiop[wfd] = wiobuf;
1525 return(NO); /* bad news */
1526 }
1527 }
1528 }
1529 Wiop[wfd] = wiobuf;
1530 return(YES);
1531 }
1532
1533
1534 static void
_w_str(char * string)1535 _w_str(char *string)
1536 {
1537 int len;
1538
1539 len = strlen(string);
1540 if ( write(Cn, string, (unsigned)len) != len )
1541 VERBOSE(gettext(P_LINE_GONE),"");
1542 return;
1543 }
1544
1545 static void
_onintrpt(int sig __unused)1546 _onintrpt(int sig __unused)
1547 {
1548 (void)signal(SIGINT, _onintrpt);
1549 (void)signal(SIGQUIT, _onintrpt);
1550 Intrupt = YES;
1551 return;
1552 }
1553
1554 static void
_rcvdead(int arg)1555 _rcvdead(int arg) /* this is executed only in the receive process */
1556 {
1557 CDEBUG(4,"call _rcvdead(%d)\r\n", arg);
1558 (void)kill(getppid(), SIGUSR1);
1559 exit((arg == SIGHUP)? SIGHUP: arg);
1560 /*NOTREACHED*/
1561 }
1562
1563 static void
_quit(int arg)1564 _quit(int arg) /* this is executed only in the parent process */
1565 {
1566 CDEBUG(4,"call _quit(%d)\r\n", arg);
1567 (void)kill(Child, SIGKILL);
1568 _bye(arg);
1569 /*NOTREACHED*/
1570 }
1571
1572 static void
_bye(int arg)1573 _bye(int arg) /* this is executed only in the parent proccess */
1574 {
1575 int status;
1576 pid_t obit;
1577
1578 if ( Shell > 0 )
1579 while ((obit = wait(&status)) != Shell) {
1580 if (obit == -1 && errno != EINTR)
1581 break;
1582 /* _receive (Child) may have ended - check it out */
1583 if (obit == Child)
1584 Child = 0;
1585 }
1586
1587 /* give user customary message after escape command returns */
1588 if (arg == SIGUSR1)
1589 VERBOSE("\r\nLost Carrier\r\n%s", "");
1590
1591 CDEBUG(4,"call _bye(%d)\r\n", arg);
1592
1593 (void)signal(SIGINT, SIG_IGN);
1594 (void)signal(SIGQUIT, SIG_IGN);
1595 /* if _receive() ended already, don't wait for it again */
1596 if ( Child != 0 )
1597 while ((obit = wait(&status)) != Child)
1598 if (obit == -1 && errno != EINTR)
1599 break;
1600 VERBOSE("\r\nDisconnected\007\r\n%s", "");
1601 cleanup((arg == SIGUSR1)? (status >>= 8): arg);
1602 /*NOTREACHED*/
1603 }
1604
1605
1606
1607 void
cleanup(int code)1608 cleanup(int code) /*this is executed only in the parent process*/
1609 {
1610
1611 CDEBUG(4,"call cleanup(%d)\r\n", code);
1612
1613 if (Docmd) {
1614 if (Child > 0)
1615 (void)kill(Child, SIGTERM);
1616 } else
1617 (void) setuid(Euid);
1618 if(Cn > 0) {
1619 fchmod(Cn, Dev_mode);
1620 fd_rmlock(Cn);
1621 (void)close(Cn);
1622 }
1623
1624
1625 rmlock((char*) NULL); /* remove all lock files for this process */
1626 if (!Docmd)
1627 _mode(0);
1628 exit(code); /* code=negative for signal causing disconnect*/
1629 }
1630
1631
1632
1633 void
tdmp(int arg)1634 tdmp(int arg)
1635 {
1636
1637 struct termio xv;
1638 int i;
1639
1640 VERBOSE("\rdevice status for fd=%d\r\n", arg);
1641 VERBOSE("F_GETFL=%o,", fcntl(arg, F_GETFL,1));
1642 if(ioctl(arg, TCGETA, &xv) < 0) {
1643 char buf[100];
1644 i = errno;
1645 (void)snprintf(buf, sizeof (buf), gettext("\rtdmp for fd=%d"), arg);
1646 errno = i;
1647 perror(buf);
1648 return;
1649 }
1650 VERBOSE("iflag=`%o',", xv.c_iflag);
1651 VERBOSE("oflag=`%o',", xv.c_oflag);
1652 VERBOSE("cflag=`%o',", xv.c_cflag);
1653 VERBOSE("lflag=`%o',", xv.c_lflag);
1654 VERBOSE("line=`%o'\r\n", xv.c_line);
1655 VERBOSE("cc[0]=`%o',", xv.c_cc[0]);
1656 for(i=1; i<8; ++i) {
1657 VERBOSE("[%d]=", i);
1658 VERBOSE("`%o',",xv.c_cc[i]);
1659 }
1660 VERBOSE("\r\n%s", "");
1661 return;
1662 }
1663
1664
1665
1666 static void
sysname(char * name)1667 sysname(char *name)
1668 {
1669
1670 char *s;
1671
1672 if(uname(&utsn) < 0)
1673 s = "Local";
1674 else
1675 s = utsn.nodename;
1676
1677 strcpy(name, s);
1678 return;
1679 }
1680
1681
1682 static void
blckcnt(long count)1683 blckcnt(long count)
1684 {
1685 static long lcharcnt = 0;
1686 long c1, c2;
1687 int i;
1688 char c;
1689
1690 if(count == (long) (-1)) { /* initialization call */
1691 lcharcnt = 0;
1692 return;
1693 }
1694 c1 = lcharcnt/BUFSIZ;
1695 if(count != (long)(-2)) { /* regular call */
1696 c2 = count/BUFSIZ;
1697 for(i = c1; i++ < c2;) {
1698 c = '0' + i%10;
1699 write(2, &c, 1);
1700 if(i%NPL == 0)
1701 write(2, "\n\r", 2);
1702 }
1703 lcharcnt = count;
1704 } else {
1705 c2 = (lcharcnt + BUFSIZ -1)/BUFSIZ;
1706 if(c1 != c2)
1707 write(2, "+\n\r", 3);
1708 else if(c2%NPL != 0)
1709 write(2, "\n\r", 2);
1710 lcharcnt = 0;
1711 }
1712 return;
1713 }
1714
1715 void
assert(char * s1 __unused,char * s2 __unused,int i1 __unused,char * s3 __unused,int i2 __unused)1716 assert (char *s1 __unused, char *s2 __unused, int i1 __unused,
1717 char *s3 __unused, int i2 __unused)
1718 { /* for ASSERT in gnamef.c */
1719 }
1720
1721 void
logent(char * s1 __unused,char * s2 __unused)1722 logent (char *s1 __unused, char *s2 __unused)
1723 { /* so we can load ulockf() */
1724 }
1725