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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
34 * cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n]
35 * [-o|-e] [-L] [-C] telno | systemname [local-cmd]
37 * legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400.
39 * -c is used to specify which device will be used for making the
40 * call. The device argument is compared to the Type (first)
41 * field in the Devices file, and only those records that
42 * match will be used to make the call. Either -d or -t
43 * would be more intuitive options designations, but they
45 * -l is for specifying a line unit from the file whose
46 * name is defined in /etc/uucp/Devices.
47 * -b is for forcing the number of bits per character processed on
48 * the connection. Valid values are '7' or '8'.
49 * -h is for half-duplex (local echoing).
50 * -t is for adding CR to LF on output to remote (for terminals).
51 * -d can be used to get some tracing & diagnostics.
52 * -o or -e is for odd or even parity on transmission to remote.
53 * -n will request the phone number from the user.
54 * -L will cause cu to go through the login chat sequence in the
56 * -C will cause cu to run the local command specified at the end
57 * of the command line, instead of entering interactive mode.
58 * Telno is a telephone number with `=' for secondary dial-tone.
59 * If "-l dev" is used, speed is taken from /etc/uucp/Devices.
60 * Only systemnames that are included in /etc/uucp/Systems may
63 * Escape with `~' at beginning of line:
67 * ~![cmd] execute shell (or 'cmd') locally,
69 * ~$cmd execute 'cmd' locally, stdout to remote,
71 * ~%break (alias ~%b) transmit BREAK to remote,
72 * ~%cd [dir] change directory to $HOME (or 'dir'),
73 * ~%debug (alias ~%d) toggles on/off the program debug trace,
74 * ~%divert allow unsolicited diversions to files,
75 * ~%ifc (alias ~%nostop) toggles on/off the DC3/DC1 input control,
76 * ~%ofc (alias ~%noostop) toggles on/off the DC3/DC1 output control,
77 * (certain remote systems cannot cope with DC3 or DC1).
78 * ~%old recognize old style silent diversions,
79 * ~%put from [to] put file from local to remote,
80 * ~%take from [to] take file from remote to local,
82 * ~l dump communication line ioctl settings,
83 * ~t dump terminal ioctl settings.
85 * Silent diversions are enabled only for use with the ~%take
86 * command by default for security reasons. Unsolicited diversions
87 * may be enabled using the ~%divert toggle. The 'new-style'
88 * diversion syntax is "~[local]>:filename", and is terminaled
89 * by "~[local]>", where 'local' is the nodename of the local
90 * system. This enables ~%take to operate properly when cu
91 * is used over multiple hops. 'old-style' diversion syntax may
92 * be enabled using the ~%old toggle. ('old-style' diversion
95 * Cu no longer uses dial.c to reach the remote. Instead, cu places
96 * a telephone call to a remote system through the uucp conn() routine
97 * when the user picks the systemname option or through altconn()--
98 * which bypasses /etc/uucp/Systems -- if a telno or direct
99 * line is chosen. The line termio attributes are set in fixline(),
100 * before the remote connection is made. As a device-lockout semaphore
101 * mechanism, uucp creates an entry in /var/spool/locks whose name is
102 * LK.<MAJ>.<maj>.<min> where MAJ is the major device of the
103 * filesystem containing the device, and <maj> and <min> are the
104 * major and minor of the device.
105 * When cu terminates, for whatever reason, cleanup() must be
106 * called to "release" the device, and clean up entries from
107 * the locks directory. Cu runs with uucp ownership, and thus provides
108 * extra insurance that lock files will not be left around.
115 #define MID BUFSIZ/2 /* mnemonic */
116 #define RUB '\177' /* mnemonic */
117 #define XON '\21' /* mnemonic */
118 #define XOFF '\23' /* mnemonic */
119 #define TTYIN 0 /* mnemonic */
120 #define TTYOUT 1 /* mnemonic */
121 #define TTYERR 2 /* mnemonic */
123 #define YES 1 /* mnemonic */
124 #define NO 0 /* mnemonic */
125 #define IOERR 4 /* exit code */
130 int Cn
; /*fd for remote comm line */
131 jmp_buf Sjbuf
; /*needed by uucp routines*/
134 /* Wiobuf contains, in effect, 3 write buffers (to remote, to tty */
135 /* stdout, and to tty stderr) and Riobuf contains 2 read buffers */
136 /* (from remote, from tty). [WR]IOFD decides which one to use. */
137 /* [RW]iop holds current position in each. */
138 #define WIOFD(fd) (fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
139 #define RIOFD(fd) (fd == TTYIN ? 0 : 1)
140 #define WMASK(fd) (fd == Cn ? line_mask : term_mask)
141 #define RMASK(fd) (fd == Cn ? line_mask : term_mask)
143 static char Riobuf
[2*WRIOBSZ
];
144 static char Wiobuf
[3*WRIOBSZ
];
145 static int Riocnt
[2] = {0, 0};
146 static char *Riop
[2];
147 static char *Wiop
[3];
149 extern int optind
; /* variable in getopt() */
154 static struct call Cucall
; /* call structure for altconn() */
156 static int Saved_tty
; /* was TCGETAW of _Tv0 successful? */
157 static int Saved_termios
; /* was TCGETSW of _Tv0 successful? */
158 static struct termio _Tv
, _Tv0
; /* for saving, changing TTY atributes */
159 static struct termios _Tv0s
; /* for saving, changing TTY atributes */
160 static struct termio _Lv
; /* attributes for the line to remote */
161 static struct termios _Lvs
; /* attributes for the line to remote */
162 static char prompt
[BUFSIZ
]= "[";
163 static struct utsname utsn
;
164 static int command_line_hups
= 0;
166 static char filename
[BUFSIZ
] = "/dev/null";
169 _Cxc
, /* place into which we do character io*/
170 _Tintr
, /* current input INTR */
171 _Tquit
, /* current input QUIT */
172 _Terase
, /* current input ERASE */
173 _Tkill
, /* current input KILL */
174 _Teol
, /* current secondary input EOL */
175 _Myeof
, /* current input EOF */
176 term_mask
, /* mask value for local terminal */
177 line_mask
; /* mask value for remote line */
178 /* either '0177' or '0377' */
181 Echoe
, /* save users ECHOE bit */
182 Echok
, /* save users ECHOK bit */
183 Intrupt
=NO
, /* interrupt indicator */
184 Ifc
=YES
, /* NO means remote can't XON/XOFF */
185 Ofc
=YES
, /* NO means local can't XON/XOFF */
186 Rtn_code
=0, /* default return code */
187 Divert
=NO
, /* don't allow unsolicited redirection */
188 OldStyle
=NO
, /* don't handle old '~>:filename' syntax */
189 /* this will be mandatory in SVR4.1 */
190 Takeflag
=NO
, /* indicates a ~%take is in progress */
191 Dologin
=NO
, /* go through the login chat sequence */
192 Docmd
=NO
; /* execute command instead of interactive cu */
194 EXTERN
int /* These are initialized in line.c */
195 Terminal
, /* flag; remote is a terminal */
196 Oddflag
, /* flag- odd parity option*/
197 Evenflag
, /* flag- even parity option*/
198 Duplex
, /* Unix= full duplex=YES; half = NO */
199 term_8bit
, /* is terminal set for 8 bit processing */
200 line_8bit
; /* is line set for 8 bit processing */
202 EXTERN
int clear_hup();
205 Child
, /* pid for receive process */
206 Shell
; /* pid for escape process */
209 dofork(); /* fork and return pid */
212 r_char(), /* local io routine */
213 w_char(), /* local io routine */
217 _onintrpt(), /* interrupt routines */
222 extern void cleanup();
224 extern int conn(), altconn(), transmit(), tilda();
237 extern char *Myline
; /* flag to force the requested line to be used */
238 extern char *Mytype
; /* flag to force requested line type to be used
239 * rddev() will compare the string to the D_TYPE
240 * (first) field of the Devices record and skip any
241 * records where they are not equal. Mytype is set
242 * to point to the argument of the -c option from
243 * the command line. */
244 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";
245 static char *P_CON_FAILED
= "Connect failed: %s\r\n";
246 static char *P_Ct_OPEN
= "Cannot open: %s\r\n";
247 static char *P_LINE_GONE
= "Remote line gone\r\n";
248 static char *P_Ct_EXSH
= "Can't execute shell\r\n";
249 static char *P_Ct_DIVERT
= "Can't divert to %s\r\n";
250 static char *P_Ct_UNDIVERT
= "Can't end diversion to %s\r\n";
251 static char *P_Bad_DIVERT
= "Won't divert to %s. Unsolicited.\r\n";
252 static char *P_STARTWITH
= "Use `~~' to start line with `~'\r\n";
253 static char *P_CNTAFTER
= "File transmission interrupted after %ld bytes.\r\n";
254 static char *P_CNTLINES
= "%d lines/";
255 static char *P_CNTCHAR
= "%ld characters\r\n";
256 static char *P_FILEINTR
= "File transmission interrupted\r\n";
257 static char *P_Ct_FK
= "Can't fork -- try later\r\n";
258 static char *P_Ct_SPECIAL
= "r\nCan't transmit special character `%#o'\r\n";
259 static char *P_TOOLONG
= "\nLine too long\r\n";
260 static char *P_IOERR
= "r\nIO error\r\n";
261 static char *P_USECMD
= "Use `~$'cmd \r\n";
263 static char *P_USEPLUSCMD
="Use `~+'cmd \r\n";
266 static char *P_NOTERMSTAT
= "Can't get terminal status\r\n";
267 static char *P_3BCONSOLE
= "Sorry, you can't cu from a 3B console\r\n";
269 static char *P_TELLENGTH
= "Telno cannot exceed 58 digits!\r\n";
271 /***************************************************************
272 * main: get command line args, establish connection, and fork.
273 * Child invokes "receive" to read from remote & write to TTY.
274 * Main line invokes "transmit" to read TTY & write to remote.
275 ***************************************************************/
282 extern void setservice();
283 extern int sysaccess();
293 /* Set locale environment variables local definitions */
294 (void) setlocale(LC_ALL
, "");
295 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
296 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
298 (void) textdomain(TEXT_DOMAIN
);
300 Riop
[0] = &Riobuf
[0];
301 Riop
[1] = &Riobuf
[WRIOBSZ
];
302 Wiop
[0] = &Wiobuf
[0];
303 Wiop
[1] = &Wiobuf
[WRIOBSZ
];
304 Wiop
[2] = &Wiobuf
[2*WRIOBSZ
];
306 Verbose
= 1; /*for uucp callers, dialers feedback*/
307 if ((string
= strrchr(argv
[0], '/')) != NULL
)
311 if (strlcpy(Progname
, string
, NAMESIZE
) >= NAMESIZE
) {
312 errno
= ENAMETOOLONG
;
316 setservice(Progname
);
317 if ( sysaccess(EACCESS_SYSTEMS
) != 0 ) {
318 (void)fprintf(stderr
,
319 gettext("%s: Cannot read Systems files\n"), Progname
);
322 if ( sysaccess(EACCESS_DEVICES
) != 0 ) {
323 (void)fprintf(stderr
,
324 gettext("%s: Cannot read Devices files\n"), Progname
);
327 if ( sysaccess(EACCESS_DIALERS
) != 0 ) {
328 (void)fprintf(stderr
,
329 gettext("%s: Cannot read Dialers files\n"), Progname
);
333 Cucall
.speed
= "Any"; /*default speed*/
335 Cucall
.telno
= CNULL
;
338 /*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
339 /*are set in fixline() in culine.c before remote connection is made */
341 while((i
= getopt(argc
, argv
, "dhteons:l:c:b:LCH")) != EOF
)
344 Debug
= 9; /*turns on uucp debugging-level 9*/
356 (void)fprintf(stderr
,
357 gettext("%s: Cannot have both even and odd parity\n"),
365 (void)fprintf(stderr
,
366 gettext("%s: Cannot have both even and odd parity\n"),
374 printf(gettext("Please enter the number: "));
375 /* Read line from stdin, remove trailing newline, if any */
376 if (fgets(s
, sizeof(s
), stdin
) != NULL
&&
377 strchr(s
, '\n') != NULL
)
378 s
[strlen(s
)-1] = '\0';
382 Cucall
.speed
= optarg
;
386 Cucall
.line
= optarg
;
389 Cucall
.type
= optarg
;
393 line_8bit
= ((*optarg
=='7') ? NO
: ((*optarg
=='8') ? YES
: -1));
394 if ( line_8bit
== -1 ) {
395 (void) fprintf(stderr
,
396 gettext("%s: b option value must be '7' or '8'\n"),
417 if(fstat(TTYIN
, &buff
) < 0) {
418 VERBOSE(gettext(P_NOTERMSTAT
),"");
420 } else if ( (buff
.st_mode
& S_IFMT
) == S_IFCHR
&& buff
.st_rdev
== 0 ) {
421 VERBOSE(gettext(P_3BCONSOLE
),"");
427 if((optind
< argc
&& optind
> 0) || (nflag
&& optind
> 0)) {
431 string
= strdup(argv
[optind
++]);
432 Cucall
.telno
= string
;
433 if ( strlen(string
) != strspn(string
, "0123456789=-*#") ) {
434 /* if it's not a legitimate telno, then it should be a systemname */
436 (void)fprintf(stderr
, gettext("%s: Bad phone number %s\n"),
438 (void) fprintf(stderr
, gettext("Phone numbers may contain "
439 "only the digits 0 through 9 and the special\n"
440 "characters =, -, * and #.\n"));
446 if(Cucall
.line
== CNULL
) /*if none of above, must be direct */
450 VERBOSE(gettext(P_USAGE
), argv
[0]);
454 if ((Cucall
.telno
!= CNULL
) &&
455 (strlen(Cucall
.telno
) >= (size_t)(MAXPH
- 1))) {
456 VERBOSE(gettext(P_TELLENGTH
),"");
460 /* save initial tty state */
461 if (!(Saved_termios
= ( ioctl(TTYIN
, TCGETS
, &_Tv0s
) >= 0 ))) {
462 Saved_tty
= ( ioctl(TTYIN
, TCGETA
, &_Tv0
) == 0 );
463 _Tv0s
.c_lflag
= _Tv0
.c_lflag
;
464 _Tv0s
.c_oflag
= _Tv0
.c_oflag
;
465 _Tv0s
.c_iflag
= _Tv0
.c_iflag
;
466 _Tv0s
.c_cflag
= _Tv0
.c_cflag
;
467 for(i
= 0; i
< NCC
; i
++)
468 _Tv0s
.c_cc
[i
] = _Tv0
.c_cc
[i
];
471 if (Saved_termios
|| Saved_tty
) {
475 * We consider the terminal to be in 8 bit mode only if cs8 is set,
476 * istrip is not set, and we're not in the "C" locale. The "C"
477 * locale is by definition 7 bit only. This provides reasonable
478 * compatibility when running in the "C" locale (currently the default)
479 * and connecting to other systems, which are most often 7 bit systems.
481 term_8bit
= ( (_Tv0s
.c_cflag
& CS8
) && !(_Tv0s
.c_iflag
& ISTRIP
) &&
482 ((p
= setlocale(LC_CTYPE
, NULL
)) != NULL
) && (strcmp(p
, "C") != 0) );
483 if ( !Oddflag
&& !Evenflag
)
484 if (_Tv0s
.c_cflag
& PARENB
)
485 if (_Tv0s
.c_cflag
& PARODD
)
492 line_8bit
= term_8bit
;
494 term_mask
= ( term_8bit
? 0377 : 0177 );
495 line_mask
= ( line_8bit
? 0377 : 0177 );
497 /* if not set, use the POSIX disabled designation */
498 #ifdef _POSIX_VDISABLE
499 vdisable
= _POSIX_VDISABLE
;
501 vdisable
= fpathconf(TTYIN
, _PC_VDISABLE
);
503 _Tintr
= _Tv0s
.c_cc
[VINTR
] ? _Tv0s
.c_cc
[VINTR
] : vdisable
;
504 _Tquit
= _Tv0s
.c_cc
[VQUIT
] ? _Tv0s
.c_cc
[VQUIT
] : vdisable
;
505 _Terase
= _Tv0s
.c_cc
[VERASE
] ? _Tv0s
.c_cc
[VERASE
] : vdisable
;
506 _Tkill
= _Tv0s
.c_cc
[VKILL
] ? _Tv0s
.c_cc
[VKILL
] : vdisable
;
507 _Teol
= _Tv0s
.c_cc
[VEOL
] ? _Tv0s
.c_cc
[VEOL
] : vdisable
;
508 _Myeof
= _Tv0s
.c_cc
[VEOF
] ? _Tv0s
.c_cc
[VEOF
] : '\04';
509 Echoe
= _Tv0s
.c_lflag
& ECHOE
;
510 Echok
= _Tv0s
.c_lflag
& ECHOK
;
512 (void)signal(SIGHUP
, cleanup
);
513 (void)signal(SIGQUIT
, cleanup
);
514 (void)signal(SIGINT
, cleanup
);
516 /* place call to system; if "cu systemname", use conn() from uucp
517 directly. Otherwise, use altconn() which dummies in the
523 (void)fprintf(stderr
,
524 gettext("%s: Warning: -l flag ignored when system name used\n"),
527 (void)fprintf(stderr
,
528 gettext("%s: Warning: -s flag ignored when system name used\n"),
531 if ( (Cn
< 0) && (Cucall
.type
!= CNULL
) )
532 Cn
= altconn(&Cucall
);
534 Cn
= altconn(&Cucall
);
537 VERBOSE(gettext(P_CON_FAILED
),UERRORTEXT
);
541 if ( fstat(Cn
, &Cnsbuf
) == 0 )
542 Dev_mode
= Cnsbuf
.st_mode
;
544 Dev_mode
= R_DEVICEMODE
;
545 fchmod(Cn
, M_DEVICEMODE
);
548 if ((Docmd
) && (argv
[optind
] == NULL
)) {
549 (void) fprintf(stderr
,gettext("cu: local cmd is required, -C is ignored.\n"));
550 VERBOSE(gettext(P_USAGE
), argv
[0]);
556 if((setuid(getuid()) < 0) || (setgid(getgid()) < 0)) {
557 VERBOSE("Unable to setuid/gid\n%s", "");
565 /* At this point succeeded in getting an open communication line */
566 /* Conn() takes care of closing the Systems file */
569 (void)signal(SIGINT
,_onintrpt
);
570 _mode(1); /* put terminal in `raw' mode */
571 VERBOSE("Connected\007\r\n%s", ""); /*bell!*/
573 /* must catch signals before fork. if not and if _receive() */
574 /* fails in just the right (wrong?) way, _rcvdead() can be */
575 /* called and do "kill(getppid(),SIGUSR1);" before parent */
576 /* has done calls to signal() after recfork(). */
577 (void)signal(SIGUSR1
, _bye
);
578 (void)signal(SIGHUP
, cleanup
);
579 (void)signal(SIGQUIT
, _onintrpt
);
581 sysname(&prompt
[1]); /* set up system name prompt */
582 (void) strcat(prompt
, "]");
584 recfork(); /* checks for child == 0 */
588 * Because the child counts hangups for the -H flag,
589 * and because we fork a new child when doing (e.g.)
590 * ~%take, we assume the first child we fork has
591 * processed all the hangups and we reset the count here.
592 * We really should pass the remaining count back from
593 * the child to the parent when we kill the child.
595 command_line_hups
= 0;
596 Rtn_code
= transmit();
602 * Fork a child to run the specified command,
603 * wait for it to finish, and clean up.
614 execvp(argv
[optind
], &argv
[optind
]);
619 /* XXX - should return wait status as our exit code */
627 * Kill the present child, if it exists, then fork a new one.
635 kill(Child
, SIGKILL
);
636 while ( (ret
= wait(&status
)) != Child
)
637 if (ret
== -1 && errno
!= EINTR
)
642 (void)signal(SIGUSR1
, SIG_DFL
);
643 (void)signal(SIGHUP
, _rcvdead
);
644 (void)signal(SIGQUIT
, SIG_IGN
);
645 (void)signal(SIGINT
, SIG_IGN
);
647 _receive(); /* This should run until killed */
653 /***************************************************************
654 * transmit: copy stdin to remote fd, except:
656 * ~! local login-style shell
657 * ~!cmd execute cmd locally
658 * ~$proc execute proc locally, send output to line
659 * ~%cmd execute builtin cmd (put, take, or break)
660 ****************************************************************/
662 /*****************************************************************
663 * ~+proc execute locally, with stdout to and stdin from line.
664 ******************************************************************/
673 int id
= 0; /* flag for systemname prompt on tilda escape */
675 CDEBUG(4,"transmit started\n\r%s", "");
677 /* In main loop, always waiting to read characters from */
678 /* keyboard; writes characters to remote, or to TTYOUT */
679 /* on a tilda escape */
683 while(r_char(TTYIN
) == YES
) {
684 if(p
== b
) /* Escape on leading ~ */
685 escape
= (_Cxc
== '~');
686 if(p
== b
+1) /* But not on leading ~~ */
687 escape
&= (_Cxc
!= '~');
689 if(_Cxc
== '\n' || _Cxc
== '\r' || _Cxc
== _Teol
) {
691 if(tilda(b
+1) == YES
)
696 if(_Cxc
== _Tintr
|| _Cxc
== _Tkill
|| _Cxc
== _Tquit
||
697 (Intrupt
&& _Cxc
== '\0')) {
700 VERBOSE("\r\n%s", "");
703 if( w_char(Cn
) == NO
) {
704 VERBOSE(gettext(P_LINE_GONE
),"");
711 if((p
== b
+1) && (_Cxc
!= _Terase
) && (!id
)) {
713 VERBOSE("%s", prompt
);
715 if(_Cxc
== _Terase
) {
719 VERBOSE("\b \b%s", "");
721 (void)w_char(TTYOUT
);
723 (void)w_char(TTYOUT
);
727 VERBOSE(gettext(P_TOOLONG
),"");
731 /*not a tilda escape command*/
733 if(Intrupt
&& _Cxc
== '\0') {
734 CDEBUG(4,"got break in transmit\n\r%s", "");
740 if(w_char(Cn
) == NO
) {
741 VERBOSE(gettext(P_LINE_GONE
),"");
745 if((w_char(TTYERR
) == NO
) || (wioflsh(TTYERR
) == NO
))
748 if ((_Cxc
== _Tintr
) || (_Cxc
== _Tquit
) ||
749 ( (p
==b
) && (_Cxc
== _Myeof
) ) ) {
750 CDEBUG(4,"got a tintr\n\r%s", "");
754 if(_Cxc
== '\n' || _Cxc
== '\r' ||
755 _Cxc
== _Teol
|| _Cxc
== _Tkill
) {
766 /***************************************************************
767 * routine to halt input from remote and flush buffers
768 ***************************************************************/
772 (void)ioctl(TTYOUT
, TCXONC
, 0); /* stop tty output */
773 (void)ioctl(Cn
, TCFLSH
, 0); /* flush remote input */
774 (void)ioctl(TTYOUT
, TCFLSH
, 1); /* flush tty output */
775 (void)ioctl(TTYOUT
, TCXONC
, 1); /* restart tty output */
777 return; /* didn't interupt file transmission */
779 VERBOSE(gettext(P_FILEINTR
),"");
781 _w_str("echo '\n~>\n';mesg y;stty echo\n");
786 /**************************************************************
787 * command interpreter for escape lines
788 **************************************************************/
794 VERBOSE("\r\n%s", "");
795 CDEBUG(4,"call tilda(%s)\r\n", cmd
);
801 kill(cmd
[0] == CDSUSP
? getpid() : (pid_t
) 0, SIGTSTP
);
805 if(Cucall
.telno
== CNULL
)
807 _w_str("\04\04\04\04\04");
809 kill(Child
, SIGKILL
);
810 if (ioctl (Cn
, TCGETS
, &_Lvs
) < 0) {
811 (void) ioctl (Cn
, TCGETA
, &_Lv
);
812 /* speed to zero for hangup */
814 (void) ioctl (Cn
, TCSETAW
, &_Lv
);
816 /* speed to zero for hangup */
817 _Lvs
.c_cflag
&= 0xffff0000;
818 cfsetospeed(&_Lvs
, B0
);
819 (void) ioctl (Cn
, TCSETSW
, &_Lvs
);
825 _shell(cmd
); /* local shell */
826 VERBOSE("\r%c\r\n", *cmd
);
827 VERBOSE("(continue)%s", "");
831 VERBOSE(gettext(P_USECMD
),"");
832 VERBOSE("(continue)%s", "");
834 _shell(cmd
); /*Local shell */
835 VERBOSE("\r%c\r\n", *cmd
);
842 VERBOSE(gettext(P_USEPLUSCMD
), "");
843 VERBOSE("(continue)%s", "");
846 /* must suspend receive to give*/
847 /*remote out to stdin of cmd */
848 kill(Child
, SIGKILL
);
849 _shell(cmd
); /* Local shell */
852 VERBOSE("\r%c\r\n", *cmd
);
862 VERBOSE("(continue)%s", "");
866 VERBOSE("(continue)%s", "");
870 VERBOSE(gettext(P_STARTWITH
),"");
871 VERBOSE("(continue)%s", "");
877 /***************************************************************
878 * The routine "shell" takes an argument starting with
879 * either "!" or "$", and terminated with '\0'.
880 * If $arg, arg is the name of a local shell file which
881 * is executed and its output is passed to the remote.
882 * If !arg, we escape to a local shell to execute arg
883 * with output to TTY, and if arg is null, escape to
884 * a local shell and blind the remote line. In either
885 * case, '^D' will kill the escape status.
886 **************************************************************/
889 /***************************************************************
890 * Another argument to the routine "shell" may be +. If +arg,
891 * arg is the name of a local shell file which is executed with
892 * stdin from and stdout to the remote.
893 **************************************************************/
901 void (*xx
)(), (*yy
)();
903 CDEBUG(4,"call _shell(%s)\r\n", str
);
908 _mode(0); /* restore normal tty attributes */
909 xx
= signal(SIGINT
, SIG_IGN
);
910 yy
= signal(SIGQUIT
, SIG_IGN
);
914 if( (shell
= getenv("SHELL")) == NULL
)
915 /* use default if user's shell is not set */
919 /***********************************************
920 * Hook-up our "standard output"
921 * to either the tty for '!' or the line
922 * for '$' as appropriate
923 ***********************************************/
926 /************************************************
927 * Or to the line for '+'.
928 **********************************************/
931 (void)fcntl((*str
== '!')? TTYERR
:Cn
,F_DUPFD
,TTYOUT
);
934 /*************************************************
935 * Hook-up "standard input" to the line for '+'.
936 * **********************************************/
939 (void)fcntl(Cn
,F_DUPFD
,TTYIN
);
943 /***********************************************
944 * Hook-up our "standard input"
945 * to the tty for '!' and '$'.
946 ***********************************************/
948 (void)close(Cn
); /*parent still has Cn*/
949 (void)signal(SIGINT
, SIG_DFL
);
950 (void)signal(SIGHUP
, SIG_DFL
);
951 (void)signal(SIGQUIT
, SIG_DFL
);
952 (void)signal(SIGUSR1
, SIG_DFL
);
954 (void)execl(shell
,shell
,(char*) 0,(char*) 0,(char *) 0);
956 (void)execl(shell
,"sh","-c",str
,(char *) 0);
957 VERBOSE(gettext(P_Ct_EXSH
),"");
960 while ((w_ret
= wait((int*)0)) != fk
)
961 if (w_ret
== -1 && errno
!= EINTR
)
964 (void)signal(SIGINT
, xx
);
965 (void)signal(SIGQUIT
, yy
);
971 /***************************************************************
972 * This function implements the 'put', 'take', 'break',
973 * 'ifc' (aliased to nostop) and 'ofc' (aliased to noostop)
974 * commands which are internal to cu.
975 ***************************************************************/
983 char mypath
[MAXPATH
];
988 CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd
);
990 arg
[narg
=0] = strtok(cmd
, " \t\n");
992 /* following loop breaks out the command and args */
993 while((arg
[++narg
] = strtok((char*) NULL
, " \t\n")) != NULL
) {
1000 /* ~%take file option */
1001 if(EQUALS(arg
[0], "take")) {
1002 if(narg
< 2 || narg
> 3) {
1003 VERBOSE("usage: ~%%take from [to]\r\n%s", "");
1004 VERBOSE("(continue)%s", "");
1009 (void) strcpy(filename
, arg
[2]);
1010 recfork(); /* fork so child (receive) knows filename */
1013 * be sure that the remote file (arg[1]) exists before
1014 * you try to take it. otherwise, the error message from
1015 * cat will wind up in the local file (arg[2])
1017 * what we're doing is:
1020 * then (echo '~[local]'>arg2; cat arg1; echo '~[local]'>)
1021 * else echo can't open: arg1
1026 _w_str("stty -echo;if test -r ");
1028 _w_str("; then (echo '~");
1036 _w_str(">'); else echo cant\\'t open: ");
1038 _w_str("; fi;stty echo\n");
1042 /* ~%put file option*/
1043 if(EQUALS(arg
[0], "put")) {
1045 char ch
, buf
[BUFSIZ
], spec
[NCC
+1], *b
, *p
, *q
;
1046 int i
, j
, len
, tc
=0, lines
=0;
1049 if(narg
< 2 || narg
> 3) {
1050 VERBOSE("usage: ~%%put from [to]\r\n%s", "");
1051 VERBOSE("(continue)%s", "");
1057 if((file
= fopen(arg
[1], "r")) == NULL
) {
1058 VERBOSE(gettext(P_Ct_OPEN
), arg
[1]);
1059 VERBOSE("(continue)%s", "");
1063 * if cannot write into file on remote machine, write into
1066 * what we're doing is:
1068 * (cat - > arg2) || cat - > /dev/null
1071 _w_str("stty -echo;(cat - >");
1073 _w_str(")||cat - >/dev/null;stty echo\n");
1075 for(i
=0,j
=0; i
< NCC
; ++i
)
1076 if((ch
=_Tv0s
.c_cc
[i
]) != '\0')
1079 _mode(2); /*accept interrupts from keyboard*/
1080 (void)sleep(5); /*hope that w_str info digested*/
1082 /* Read characters line by line into buf to write to */
1083 /* remote with character and line count for blckcnt */
1084 while(Intrupt
== NO
&&
1085 fgets(b
= &buf
[MID
],MID
,file
) != NULL
) {
1086 /* worse case is each char must be escaped*/
1088 chars
+= len
; /* character count */
1090 while(q
= strpbrk(p
, spec
)) {
1091 if(*q
== _Tintr
|| *q
== _Tquit
|| *q
== _Teol
) {
1092 VERBOSE(gettext(P_Ct_SPECIAL
), *q
);
1093 (void)strcpy(q
, q
+1);
1096 b
= strncpy(b
-1, b
, q
-b
);
1101 if((tc
+= len
) >= MID
) {
1105 if(write(Cn
, b
, (unsigned)strlen(b
)) < 0) {
1106 VERBOSE(gettext(P_IOERR
),"");
1110 ++lines
; /* line count */
1111 blckcnt((long)chars
);
1114 blckcnt((long)(-2)); /* close */
1116 if(Intrupt
== YES
) {
1119 VERBOSE(gettext(P_CNTAFTER
), ++chars
);
1121 VERBOSE(gettext(P_CNTLINES
), lines
);
1122 VERBOSE(gettext(P_CNTCHAR
),chars
);
1129 /* ~%b or ~%break */
1130 if(EQUALS(arg
[0], "b") || EQUALS(arg
[0], "break")) {
1134 /* ~%d or ~%debug toggle */
1135 if(EQUALS(arg
[0], "d") || EQUALS(arg
[0], "debug")) {
1140 VERBOSE("(continue)%s", "");
1143 /* ~%[ifc|nostop] toggles start/stop input control */
1144 if( EQUALS(arg
[0], "ifc") || EQUALS(arg
[0], "nostop") ) {
1145 (void)ioctl(Cn
, TCGETA
, &_Tv
);
1148 _Tv
.c_iflag
|= IXOFF
;
1150 _Tv
.c_iflag
&= ~IXOFF
;
1151 (void)ioctl(Cn
, TCSETAW
, &_Tv
);
1153 VERBOSE("(ifc %s)", (Ifc
? "enabled" : "disabled"));
1154 VERBOSE("(continue)%s", "");
1157 /* ~%[ofc|noostop] toggles start/stop output control */
1158 if( EQUALS(arg
[0], "ofc") || EQUALS(arg
[0], "noostop") ) {
1159 (void)ioctl(Cn
, TCGETA
, &_Tv
);
1162 _Tv
.c_iflag
|= IXON
;
1164 _Tv
.c_iflag
&= ~IXON
;
1165 (void)ioctl(Cn
, TCSETAW
, &_Tv
);
1167 VERBOSE("(ofc %s)", (Ofc
? "enabled" : "disabled"));
1168 VERBOSE("(continue)%s", "");
1171 /* ~%divert toggles unsolicited redirection security */
1172 if( EQUALS(arg
[0], "divert") ) {
1174 recfork(); /* fork a new child so it knows about change */
1175 VERBOSE("(unsolicited diversion %s)", (Divert
? "enabled" : "disabled"));
1176 VERBOSE("(continue)%s", "");
1179 /* ~%old toggles recognition of old-style '~>:filename' */
1180 if( EQUALS(arg
[0], "old") ) {
1181 OldStyle
= !OldStyle
;
1182 recfork(); /* fork a new child so it knows about change */
1183 VERBOSE("(old-style diversion %s)", (OldStyle
? "enabled" : "disabled"));
1184 VERBOSE("(continue)%s", "");
1187 /* Change local current directory */
1188 if(EQUALS(arg
[0], "cd")) {
1190 getpath
= getenv("HOME");
1191 strlcpy(mypath
, getpath
, sizeof (mypath
));
1192 if(chdir(mypath
) < 0) {
1193 VERBOSE("Cannot change to %s\r\n", mypath
);
1194 VERBOSE("(continue)%s", "");
1197 } else if (chdir(arg
[1]) < 0) {
1198 VERBOSE("Cannot change to %s\r\n", arg
[1]);
1199 VERBOSE("(continue)%s", "");
1202 recfork(); /* fork a new child so it knows about change */
1203 VERBOSE("(continue)%s", "");
1207 if (arg
[0] == (char *) NULL
)
1210 VERBOSE("~%%%s unknown to cu\r\n", arg
[0]);
1211 VERBOSE("(continue)%s", "");
1215 /***************************************************************
1216 * receive: read from remote line, write to fd=1 (TTYOUT)
1222 * ~> (ends diversion)
1223 ***************************************************************/
1228 int silent
= NO
, file
= -1;
1235 int line_ok
= 1, rval
;
1237 CDEBUG(4,"_receive started\r\n%s", "");
1248 if (rval
== HUNGUP
) {
1249 if (command_line_hups
> 0) {
1250 CDEBUG(4, "Ignoring device hangup\n%s", "");
1251 command_line_hups
--;
1252 (void) setuid(Euid
); /* reacquire privileges */
1253 if (clear_hup(Cn
) != SUCCESS
) {
1254 DEBUG(4, "Unable to clear hup on device\n%s", "");
1257 (void) setuid(getuid()); /* relinquish privileges */
1263 if(silent
== NO
) /* ie., if not redirecting from screen */
1264 if(w_char(TTYOUT
) == NO
)
1265 _rcvdead(IOERR
); /* this will exit */
1266 /* remove CR's and fill inserted by remote */
1267 if(_Cxc
== '\0' || _Cxc
== RUB
|| _Cxc
== '\r')
1270 if(_Cxc
!= '\n' && (p
-b
) < BUFSIZ
)
1272 /* ****************************************** */
1273 /* This code deals with ~%take file diversion */
1274 /* ****************************************** */
1278 if (EQUALSN(&b
[1],prompt
,strlen(prompt
))) {
1279 b_p
= b
+ 1 + strlen(prompt
);
1285 if ( (for_me
|| OldStyle
) && (*b_p
== '>') ) {
1286 /* This is an acceptable '~[uname]>' line */
1288 if ( (*b_p
== '\n') && (silent
== YES
) ) {
1289 /* end of diversion */
1291 (void) strcpy(filename
, "/dev/null");
1292 if ( file
>= 0 && close(file
) ) {
1293 VERBOSE(gettext(P_Ct_UNDIVERT
), b_p
);
1294 perror(gettext("cu: close failed"));
1298 blckcnt((long)(-2));
1299 VERBOSE("%s\r\n", b
);
1300 VERBOSE(gettext(P_CNTLINES
), tic
);
1301 VERBOSE(gettext(P_CNTCHAR
), count
);
1305 } else if (*b_p
!= '\n') {
1306 if ( *b_p
== '>' ) {
1310 if ( (for_me
|| (OldStyle
&& (*b_p
== ':'))) && (silent
== NO
) ) {
1311 /* terminate filename string */
1315 if ( !EQUALS(filename
, b_p
) ) {
1316 if ( !Divert
|| !EQUALS(filename
, "/dev/null") ) {
1317 VERBOSE(gettext(P_Bad_DIVERT
), b_p
);
1318 (void) strcpy(filename
, "/dev/null");
1321 (void) strcpy(filename
, b_p
);
1324 if ( append
&& ((file
=open(filename
,O_WRONLY
)) >= 0) )
1325 (void)lseek(file
, 0L, 2);
1327 file
= creat(filename
, PUB_FILEMODE
);
1329 VERBOSE(gettext(P_Ct_DIVERT
), filename
);
1330 perror(gettext("cu: open|creat failed"));
1332 (void)sleep(5); /* 10 seemed too long*/
1342 /* Regular data, divert if appropriate */
1343 if ( silent
== YES
) {
1345 (void)write(file
, b
, (unsigned)(p
-b
));
1346 count
+= p
-b
; /* tally char count */
1347 ++tic
; /* tally lines */
1348 blckcnt((long)count
);
1353 * we used to tell of lost carrier here, but now
1354 * defer to _bye() so that escape processes are
1361 /***************************************************************
1362 * change the TTY attributes of the users terminal:
1363 * 0 means restore attributes to pre-cu status.
1364 * 1 means set `raw' mode for use during cu session.
1365 * 2 means like 1 but accept interrupts from the keyboard.
1366 ***************************************************************/
1372 CDEBUG(4,"call _mode(%d)\r\n", arg
);
1374 if ( Saved_termios
)
1375 (void)ioctl(TTYIN
, TCSETSW
, &_Tv0s
);
1376 else if ( Saved_tty
) {
1377 _Tv0
.c_lflag
= _Tv0s
.c_lflag
;
1378 _Tv0
.c_oflag
= _Tv0s
.c_oflag
;
1379 _Tv0
.c_iflag
= _Tv0s
.c_iflag
;
1380 _Tv0
.c_cflag
= _Tv0s
.c_cflag
;
1381 for(i
= 0; i
< NCC
; i
++)
1382 _Tv0
.c_cc
[i
] = _Tv0s
.c_cc
[i
];
1383 (void)ioctl(TTYIN
, TCSETAW
, &_Tv0
);
1386 (void)ioctl(TTYIN
, TCGETA
, &_Tv
);
1388 _Tv
.c_iflag
&= ~(INLCR
| ICRNL
| IGNCR
| IUCLC
);
1390 _Tv
.c_iflag
|= ISTRIP
;
1391 _Tv
.c_oflag
|= OPOST
;
1392 _Tv
.c_oflag
&= ~(OLCUC
| ONLCR
| OCRNL
| ONOCR
| ONLRET
);
1393 _Tv
.c_lflag
&= ~(ICANON
| ISIG
| ECHO
);
1395 _Tv
.c_iflag
&= ~IXON
;
1397 _Tv
.c_iflag
|= IXON
;
1399 _Tv
.c_iflag
&= ~IXOFF
;
1401 _Tv
.c_iflag
|= IXOFF
;
1403 _Tv
.c_oflag
|= ONLCR
;
1404 _Tv
.c_iflag
|= ICRNL
;
1406 _Tv
.c_cc
[VEOF
] = '\01';
1407 _Tv
.c_cc
[VEOL
] = '\0';
1410 _Tv
.c_iflag
|= IXON
;
1411 _Tv
.c_lflag
|= ISIG
;
1413 (void)ioctl(TTYIN
, TCSETAW
, &_Tv
);
1425 for(i
= 0; i
< 6; ++i
) {
1426 if((x
= fork()) >= 0) {
1431 if(Debug
) perror("dofork");
1433 VERBOSE(gettext(P_Ct_FK
),"");
1443 /* find starting pos in correct buffer in Riobuf */
1445 riobuf
= &Riobuf
[rfd
*WRIOBSZ
];
1447 if (Riop
[rfd
] >= &riobuf
[Riocnt
[rfd
]]) {
1448 /* empty read buffer - refill it */
1450 /* flush any waiting output */
1451 if ( (wioflsh(Cn
) == NO
) || (wioflsh(TTYOUT
) == NO
) )
1454 while((rtn
= read(fd
, riobuf
, WRIOBSZ
)) < 0){
1455 if(errno
== EINTR
) {
1456 /* onintrpt() called asynchronously before this line */
1457 if(Intrupt
== YES
) {
1462 /*a signal other than interrupt*/
1463 /*received during read*/
1467 CDEBUG(4,"got read error, not EINTR\n\r%s", "");
1468 break; /* something wrong */
1472 /* reset current position in buffer */
1473 /* and count of available chars */
1480 _Cxc
= *(Riop
[rfd
]++) & RMASK(fd
); /* mask off appropriate bits */
1482 } else if (rtn
== 0) {
1497 /* find starting pos in correct buffer in Wiobuf */
1499 wiobuf
= &Wiobuf
[wfd
*WRIOBSZ
];
1501 if (Wiop
[wfd
] >= &wiobuf
[WRIOBSZ
]) {
1502 /* full output buffer - flush it */
1503 if ( wioflsh(fd
) == NO
)
1506 *(Wiop
[wfd
]++) = _Cxc
& WMASK(fd
); /* mask off appropriate bits */
1510 /* wioflsh flush output buffer */
1518 /* find starting pos in correct buffer in Wiobuf */
1520 wiobuf
= &Wiobuf
[wfd
*WRIOBSZ
];
1522 if (Wiop
[wfd
] > wiobuf
) {
1523 /* there's something in the buffer */
1524 while(write(fd
, wiobuf
, (Wiop
[wfd
] - wiobuf
)) < 0) {
1525 if(errno
== EINTR
) {
1526 if(Intrupt
== YES
) {
1527 VERBOSE("\ncu: Output blocked\r\n%s", "");
1530 continue; /* alarm went off */
1533 return(NO
); /* bad news */
1548 len
= strlen(string
);
1549 if ( write(Cn
, string
, (unsigned)len
) != len
)
1550 VERBOSE(gettext(P_LINE_GONE
),"");
1559 (void)signal(SIGINT
, _onintrpt
);
1560 (void)signal(SIGQUIT
, _onintrpt
);
1566 _rcvdead(arg
) /* this is executed only in the receive process */
1569 CDEBUG(4,"call _rcvdead(%d)\r\n", arg
);
1570 (void)kill(getppid(), SIGUSR1
);
1571 exit((arg
== SIGHUP
)? SIGHUP
: arg
);
1576 _quit(arg
) /* this is executed only in the parent process */
1579 CDEBUG(4,"call _quit(%d)\r\n", arg
);
1580 (void)kill(Child
, SIGKILL
);
1586 _bye(arg
) /* this is executed only in the parent proccess */
1593 while ((obit
= wait(&status
)) != Shell
) {
1594 if (obit
== -1 && errno
!= EINTR
)
1596 /* _receive (Child) may have ended - check it out */
1601 /* give user customary message after escape command returns */
1603 VERBOSE("\r\nLost Carrier\r\n%s", "");
1605 CDEBUG(4,"call _bye(%d)\r\n", arg
);
1607 (void)signal(SIGINT
, SIG_IGN
);
1608 (void)signal(SIGQUIT
, SIG_IGN
);
1609 /* if _receive() ended already, don't wait for it again */
1611 while ((obit
= wait(&status
)) != Child
)
1612 if (obit
== -1 && errno
!= EINTR
)
1614 VERBOSE("\r\nDisconnected\007\r\n%s", "");
1615 cleanup((arg
== SIGUSR1
)? (status
>>= 8): arg
);
1622 cleanup(code
) /*this is executed only in the parent process*/
1623 int code
; /*Closes device; removes lock files */
1626 CDEBUG(4,"call cleanup(%d)\r\n", code
);
1630 (void)kill(Child
, SIGTERM
);
1632 (void) setuid(Euid
);
1634 fchmod(Cn
, Dev_mode
);
1640 rmlock((char*) NULL
); /* remove all lock files for this process */
1643 exit(code
); /* code=negative for signal causing disconnect*/
1656 VERBOSE("\rdevice status for fd=%d\r\n", arg
);
1657 VERBOSE("F_GETFL=%o,", fcntl(arg
, F_GETFL
,1));
1658 if(ioctl(arg
, TCGETA
, &xv
) < 0) {
1661 (void)snprintf(buf
, sizeof (buf
), gettext("\rtdmp for fd=%d"), arg
);
1666 VERBOSE("iflag=`%o',", xv
.c_iflag
);
1667 VERBOSE("oflag=`%o',", xv
.c_oflag
);
1668 VERBOSE("cflag=`%o',", xv
.c_cflag
);
1669 VERBOSE("lflag=`%o',", xv
.c_lflag
);
1670 VERBOSE("line=`%o'\r\n", xv
.c_line
);
1671 VERBOSE("cc[0]=`%o',", xv
.c_cc
[0]);
1672 for(i
=1; i
<8; ++i
) {
1673 VERBOSE("[%d]=", i
);
1674 VERBOSE("`%o',",xv
.c_cc
[i
]);
1676 VERBOSE("\r\n%s", "");
1689 if(uname(&utsn
) < 0)
1703 static long lcharcnt
= 0;
1708 if(count
== (long) (-1)) { /* initialization call */
1712 c1
= lcharcnt
/BUFSIZ
;
1713 if(count
!= (long)(-2)) { /* regular call */
1715 for(i
= c1
; i
++ < c2
;) {
1719 write(2, "\n\r", 2);
1723 c2
= (lcharcnt
+ BUFSIZ
-1)/BUFSIZ
;
1725 write(2, "+\n\r", 3);
1726 else if(c2
%NPL
!= 0)
1727 write(2, "\n\r", 2);
1736 assert (s1
, s2
, i1
, s3
, i2
)
1739 { } /* for ASSERT in gnamef.c */
1745 { } /* so we can load ulockf() */