/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n]
* [-o|-e] [-L] [-C] telno | systemname [local-cmd]
*
* legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400.
*
* -c is used to specify which device will be used for making the
* call. The device argument is compared to the Type (first)
* field in the Devices file, and only those records that
* match will be used to make the call. Either -d or -t
* would be more intuitive options designations, but they
* are already in use.
* -l is for specifying a line unit from the file whose
* -b is for forcing the number of bits per character processed on
* the connection. Valid values are '7' or '8'.
* -h is for half-duplex (local echoing).
* -t is for adding CR to LF on output to remote (for terminals).
* -d can be used to get some tracing & diagnostics.
* -o or -e is for odd or even parity on transmission to remote.
* -n will request the phone number from the user.
* -L will cause cu to go through the login chat sequence in the
* Systems file.
* -C will cause cu to run the local command specified at the end
* of the command line, instead of entering interactive mode.
* Telno is a telephone number with `=' for secondary dial-tone.
* be used.
*
* Escape with `~' at beginning of line:
*
* ~. quit,
*
* ~![cmd] execute shell (or 'cmd') locally,
*
* ~$cmd execute 'cmd' locally, stdout to remote,
*
* ~%break (alias ~%b) transmit BREAK to remote,
* ~%cd [dir] change directory to $HOME (or 'dir'),
* ~%divert allow unsolicited diversions to files,
* (certain remote systems cannot cope with DC3 or DC1).
* ~%old recognize old style silent diversions,
* ~%put from [to] put file from local to remote,
* ~%take from [to] take file from remote to local,
*
* ~l dump communication line ioctl settings,
* ~t dump terminal ioctl settings.
*
* Silent diversions are enabled only for use with the ~%take
* command by default for security reasons. Unsolicited diversions
* may be enabled using the ~%divert toggle. The 'new-style'
* diversion syntax is "~[local]>:filename", and is terminaled
* by "~[local]>", where 'local' is the nodename of the local
* system. This enables ~%take to operate properly when cu
* is used over multiple hops. 'old-style' diversion syntax may
* be enabled using the ~%old toggle. ('old-style' diversion
* should be avoided!)
*
* Cu no longer uses dial.c to reach the remote. Instead, cu places
* a telephone call to a remote system through the uucp conn() routine
* when the user picks the systemname option or through altconn()--
* line is chosen. The line termio attributes are set in fixline(),
* before the remote connection is made. As a device-lockout semaphore
* LK.<MAJ>.<maj>.<min> where MAJ is the major device of the
* filesystem containing the device, and <maj> and <min> are the
* major and minor of the device.
* When cu terminates, for whatever reason, cleanup() must be
* called to "release" the device, and clean up entries from
* the locks directory. Cu runs with uucp ownership, and thus provides
* extra insurance that lock files will not be left around.
*/
#include "uucp.h"
#include <locale.h>
#include <stropts.h>
int Sflag=0;
/* io buffering */
/* Wiobuf contains, in effect, 3 write buffers (to remote, to tty */
/* stdout, and to tty stderr) and Riobuf contains 2 read buffers */
/* (from remote, from tty). [WR]IOFD decides which one to use. */
/* [RW]iop holds current position in each. */
extern int optind; /* variable in getopt() */
extern char
*optarg;
static int command_line_hups = 0;
static char
/* either '0177' or '0377' */
int
/* this will be mandatory in SVR4.1 */
static pid_t
dofork(); /* fork and return pid */
static int
r_char(), /* local io routine */
w_char(), /* local io routine */
wioflsh();
static void
_onintrpt(), /* interrupt routines */
_rcvdead(),
_quit(),
_bye();
extern void cleanup();
extern void tdmp();
static void
recfork(),
sysname(),
blckcnt(),
_flush(),
_shell(),
_dopercen(),
_receive(),
_mode(),
_w_str();
extern char *Myline; /* flag to force the requested line to be used */
extern char *Mytype; /* flag to force requested line type to be used
* rddev() will compare the string to the D_TYPE
* (first) field of the Devices record and skip any
* records where they are not equal. Mytype is set
* to point to the argument of the -c option from
* the command line. */
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";
#ifdef forfutureuse
#endif
#ifdef u3b
#endif
/***************************************************************
* main: get command line args, establish connection, and fork.
* Child invokes "receive" to read from remote & write to TTY.
* Main line invokes "transmit" to read TTY & write to remote.
***************************************************************/
int
int argc;
char *argv[];
{
extern void setservice();
extern int sysaccess();
char s[MAXPH];
char *string;
int i;
int errflag=0;
int lflag=0;
int nflag=0;
int systemname = 0;
char vdisable;
/* Set locale environment variables local definitions */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
string++;
else
perror("cu");
exit(1);
}
if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
exit(1);
}
if ( sysaccess(EACCESS_DEVICES) != 0 ) {
exit(1);
}
if ( sysaccess(EACCESS_DIALERS) != 0 ) {
exit(1);
}
/*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
/*are set in fixline() in culine.c before remote connection is made */
switch(i) {
case 'd':
break;
case 'h':
break;
case 't':
break;
case 'e':
if ( Oddflag ) {
gettext("%s: Cannot have both even and odd parity\n"),
argv[0]);
exit(1);
}
Evenflag = 1;
break;
case 'o':
if ( Evenflag ) {
gettext("%s: Cannot have both even and odd parity\n"),
argv[0]);
exit(1);
}
Oddflag = 1;
break;
case 'n':
nflag++;
/* Read line from stdin, remove trailing newline, if any */
break;
case 's':
Sflag++;
break;
case 'l':
lflag++;
break;
case 'c':
break;
case 'b':
if ( line_8bit == -1 ) {
gettext("%s: b option value must be '7' or '8'\n"),
argv[0]);
exit(1);
}
break;
case 'L':
Dologin++;
break;
case 'C':
Docmd++;
break;
case 'H':
break;
case '?':
++errflag;
}
#ifdef u3b
{
exit(1);
exit(1);
}
}
#endif
if(nflag)
string=s;
else
/* if it's not a legitimate telno, then it should be a systemname */
if ( nflag ) {
"only the digits 0 through 9 and the special\n"
"characters =, -, * and #.\n"));
exit(1);
}
systemname++;
}
} else
++errflag;
if(errflag) {
exit(1);
}
exit(0);
}
/* save initial tty state */
for(i = 0; i < NCC; i++)
}
if (Saved_termios || Saved_tty) {
char *p;
/*
* We consider the terminal to be in 8 bit mode only if cs8 is set,
* istrip is not set, and we're not in the "C" locale. The "C"
* locale is by definition 7 bit only. This provides reasonable
* compatibility when running in the "C" locale (currently the default)
* and connecting to other systems, which are most often 7 bit systems.
*/
Oddflag = 1;
else
Evenflag = 1;
}
if (line_8bit == -1)
/* if not set, use the POSIX disabled designation */
#ifdef _POSIX_VDISABLE
#else
#endif
/* place call to system; if "cu systemname", use conn() from uucp
directly. Otherwise, use altconn() which dummies in the
Systems file line.
*/
if(systemname) {
if ( lflag )
gettext("%s: Warning: -l flag ignored when system name used\n"),
argv[0]);
if ( Sflag )
gettext("%s: Warning: -s flag ignored when system name used\n"),
argv[0]);
} else
if(Cn < 0) {
} else {
else
}
}
if (!Docmd) {
cleanup(101);
}
}
if(Debug)
/* At this point succeeded in getting an open communication line */
/* Conn() takes care of closing the Systems file */
if (!Docmd) {
/* must catch signals before fork. if not and if _receive() */
/* fails in just the right (wrong?) way, _rcvdead() can be */
/* called and do "kill(getppid(),SIGUSR1);" before parent */
/* has done calls to signal() after recfork(). */
recfork(); /* checks for child == 0 */
if(Child > 0) {
/*
* Because the child counts hangups for the -H flag,
* and because we fork a new child when doing (e.g.)
* ~%take, we assume the first child we fork has
* processed all the hangups and we reset the count here.
* We really should pass the remaining count back from
* the child to the parent when we kill the child.
*/
command_line_hups = 0;
/*NOTREACHED*/
}
} else {
/*
* Fork a child to run the specified command,
* wait for it to finish, and clean up.
*/
if (Child == 0) {
close(0);
close(1);
exit(-1);
/* NOTREACHED */
}
wait(0);
/* XXX - should return wait status as our exit code */
}
/*NOTREACHED*/
return (0);
}
/*
* Kill the present child, if it exists, then fork a new one.
*/
static void
recfork()
{
if (Child) {
break;
}
if(Child == 0) {
_receive(); /* This should run until killed */
/*NOTREACHED*/
}
return;
}
/***************************************************************
* transmit: copy stdin to remote fd, except:
* ~. terminate
* ~! local login-style shell
* ~!cmd execute cmd locally
* ~$proc execute proc locally, send output to line
* ~%cmd execute builtin cmd (put, take, or break)
****************************************************************/
#ifdef forfutureuse
/*****************************************************************
* ~+proc execute locally, with stdout to and stdin from line.
******************************************************************/
#endif
int
transmit()
{
char b[BUFSIZ];
char *p;
int escape;
/* In main loop, always waiting to read characters from */
/* keyboard; writes characters to remote, or to TTYOUT */
/* on a tilda escape */
for (;;) {
p = b;
if(p == b) /* Escape on leading ~ */
if(p == b+1) /* But not on leading ~~ */
if(escape) {
*p = '\0';
return(0);
id = 0;
break;
}
if(Echok)
} else {
_Cxc = '\r';
return(IOERR);
}
id=0;
}
break;
}
id = 1;
}
p = (--p < b)? b:p;
if(p > b)
if(Echoe) {
} else
} else {
if(p-b < BUFSIZ)
*p++ = _Cxc;
else {
break;
}
}
/*not a tilda escape command*/
} else {
_flush();
break;
}
return(IOERR);
}
return(IOERR);
}
_flush();
break;
}
id=0;
break;
}
p = (char*)0;
}
}
}
}
/***************************************************************
* routine to halt input from remote and flush buffers
***************************************************************/
static void
_flush()
{
return; /* didn't interupt file transmission */
}
(void)sleep(3);
_w_str("echo '\n~>\n';mesg y;stty echo\n");
return;
}
/**************************************************************
* command interpreter for escape lines
**************************************************************/
int
char *cmd;
{
switch(cmd[0]) {
case CSUSP:
case CDSUSP:
_mode(0);
_mode(1);
break;
case '.':
_w_str("\04\04\04\04\04");
if (Child)
/* speed to zero for hangup */
} else {
/* speed to zero for hangup */
}
(void) sleep (2);
}
return(YES);
case '!':
break;
case '$':
} else {
}
break;
#ifdef forfutureuse
case '+':
} else {
if (*cmd == '+')
/* must suspend receive to give*/
/*remote out to stdin of cmd */
if (*cmd == '+')
recfork();
}
break;
#endif
case '%':
break;
case 't':
break;
case 'l':
break;
default:
break;
}
return(NO);
}
/***************************************************************
* The routine "shell" takes an argument starting with
* either "!" or "$", and terminated with '\0'.
* If $arg, arg is the name of a local shell file which
* is executed and its output is passed to the remote.
* If !arg, we escape to a local shell to execute arg
* with output to TTY, and if arg is null, escape to
* a local shell and blind the remote line. In either
* case, '^D' will kill the escape status.
**************************************************************/
#ifdef forfutureuse
/***************************************************************
* Another argument to the routine "shell" may be +. If +arg,
* arg is the name of a local shell file which is executed with
* stdin from and stdout to the remote.
**************************************************************/
#endif
static void
char *str;
{
if(fk < 0)
return;
_mode(0); /* restore normal tty attributes */
if(fk == 0) {
char *shell;
/* use default if user's shell is not set */
/***********************************************
* Hook-up our "standard output"
* to either the tty for '!' or the line
* for '$' as appropriate
***********************************************/
#ifdef forfutureuse
/************************************************
* Or to the line for '+'.
**********************************************/
#endif
#ifdef forfutureuse
/*************************************************
* Hook-up "standard input" to the line for '+'.
* **********************************************/
if (*str == '+') {
}
#endif
/***********************************************
* Hook-up our "standard input"
* to the tty for '!' and '$'.
***********************************************/
if(*++str == '\0')
else
exit(0);
}
break;
Shell = 0;
_mode(1);
return;
}
/***************************************************************
* This function implements the 'put', 'take', 'break',
* 'ifc' (aliased to nostop) and 'ofc' (aliased to noostop)
* commands which are internal to cu.
***************************************************************/
static void
char *cmd;
{
char *getpath;
int narg;
blckcnt((long)(-1));
/* following loop breaks out the command and args */
if(narg < 4)
continue;
else
break;
}
/* ~%take file option */
return;
}
if(narg == 2)
recfork(); /* fork so child (receive) knows filename */
/*
* be sure that the remote file (arg[1]) exists before
* you try to take it. otherwise, the error message from
* cat will wind up in the local file (arg[2])
*
* what we're doing is:
* stty -echo; \
* if test -r arg1
* then (echo '~[local]'>arg2; cat arg1; echo '~[local]'>)
* else echo can't open: arg1
* fi; \
* stty echo
*
*/
_w_str("stty -echo;if test -r ");
_w_str("; then (echo '~");
_w_str(">'");
_w_str(";cat ");
_w_str(";echo '~");
_w_str(">'); else echo cant\\'t open: ");
_w_str("; fi;stty echo\n");
return;
}
/* ~%put file option*/
long chars=0L;
return;
}
if(narg == 2)
return;
}
/*
* if cannot write into file on remote machine, write into
*
* what we're doing is:
* stty -echo
* stty echo
*/
_w_str("stty -echo;(cat - >");
for(i=0,j=0; i < NCC; ++i)
spec[j] = '\0';
/* Read characters line by line into buf to write to */
/* remote with character and line count for blckcnt */
/* worse case is each char must be escaped*/
p = b;
(void)strcpy(q, q+1);
} else {
b = strncpy(b-1, b, q-b);
*(q-1) = '\\';
}
p = q+1;
}
(void)sleep(1);
}
break;
}
++lines; /* line count */
}
_mode(1);
_w_str("\n");
} else {
}
(void)sleep(3);
_w_str("\04");
return;
}
/* ~%b or ~%break */
return;
}
/* ~%d or ~%debug toggle */
if(Debug == 0)
Debug = 9;
else
Debug = 0;
return;
}
else
_mode(1);
return;
}
else
_mode(1);
return;
}
/* ~%divert toggles unsolicited redirection security */
recfork(); /* fork a new child so it knows about change */
return;
}
/* ~%old toggles recognition of old-style '~>:filename' */
recfork(); /* fork a new child so it knows about change */
return;
}
/* Change local current directory */
if (narg < 2) {
return;
}
return;
}
recfork(); /* fork a new child so it knows about change */
return;
}
arg[0] = "";
return;
}
/***************************************************************
* receive: read from remote line, write to fd=1 (TTYOUT)
* catch:
* ~>[>]:file
* .
* . stuff for file
* .
* ~> (ends diversion)
***************************************************************/
static void
_receive()
{
char *p;
int tic;
char b[BUFSIZ];
char *b_p;
long count;
b[0] = '\0';
b_p = p = b;
while(line_ok) {
line_ok = 0;
continue;
}
if (command_line_hups > 0) {
line_ok = 0;
}
} else
line_ok = 0;
continue;
}
/* remove CR's and fill inserted by remote */
continue;
*p++ = _Cxc;
continue;
/* ****************************************** */
/* This code deals with ~%take file diversion */
/* ****************************************** */
if (b[0] == '~') {
int append;
} else {
b_p = b + 1;
}
/* This is an acceptable '~[uname]>' line */
b_p++;
/* end of diversion */
*b_p = '\0';
}
blckcnt((long)(-2));
VERBOSE("%s\r\n", b);
file = -1;
p = b;
continue;
} else if (*b_p != '\n') {
if ( *b_p == '>' ) {
append = 1;
b_p++;
}
/* terminate filename string */
*(p-1) = '\0';
if ( *b_p == ':' )
b_p++;
append = 1;
} else {
}
}
else
if (file < 0) {
}
p = b;
continue;
}
}
}
}
/* Regular data, divert if appropriate */
if ( file >= 0)
count += p-b; /* tally char count */
++tic; /* tally lines */
}
p = b;
}
/*
* we used to tell of lost carrier here, but now
* defer to _bye() so that escape processes are
* not interrupted.
*/
return;
}
/***************************************************************
* change the TTY attributes of the users terminal:
* 0 means restore attributes to pre-cu status.
* 1 means set `raw' mode for use during cu session.
* 2 means like 1 but accept interrupts from the keyboard.
***************************************************************/
static void
{
int i;
if(arg == 0) {
if ( Saved_termios )
else if ( Saved_tty ) {
for(i = 0; i < NCC; i++)
}
} else {
if(arg == 1) {
if ( !term_8bit )
else
else
if(Terminal) {
}
}
if(arg == 2) {
}
}
return;
}
static pid_t
dofork()
{
int i;
pid_t x;
for(i = 0; i < 6; ++i) {
if((x = fork()) >= 0) {
return(x);
}
}
return(x);
}
static int
{
char *riobuf;
/* find starting pos in correct buffer in Riobuf */
/* empty read buffer - refill it */
/* flush any waiting output */
return(NO);
/* onintrpt() called asynchronously before this line */
/* got a BREAK */
_Cxc = '\0';
return(YES);
} else {
/*a signal other than interrupt*/
/*received during read*/
continue;
}
} else {
break; /* something wrong */
}
}
if (rtn > 0) {
/* reset current position in buffer */
/* and count of available chars */
}
}
if ( rtn > 0 ) {
return(YES);
} else if (rtn == 0) {
_Cxc = '\0';
return (HUNGUP);
} else {
_Cxc = '\0';
return(NO);
}
}
static int
{
int wfd;
char *wiobuf;
/* find starting pos in correct buffer in Wiobuf */
/* full output buffer - flush it */
return(NO);
}
return(YES);
}
/* wioflsh flush output buffer */
static int
int fd;
{
int wfd;
char *wiobuf;
/* find starting pos in correct buffer in Wiobuf */
/* there's something in the buffer */
} else
continue; /* alarm went off */
} else {
return(NO); /* bad news */
}
}
}
return(YES);
}
static void
char *string;
{
int len;
return;
}
/* ARGSUSED */
static void
int sig;
{
return;
}
static void
int arg;
{
/*NOTREACHED*/
}
static void
int arg;
{
/*NOTREACHED*/
}
static void
int arg;
{
int status;
if ( Shell > 0 )
break;
/* _receive (Child) may have ended - check it out */
Child = 0;
}
/* give user customary message after escape command returns */
/* if _receive() ended already, don't wait for it again */
if ( Child != 0 )
break;
/*NOTREACHED*/
}
void
int code; /*Closes device; removes lock files */
{
if (Docmd) {
if (Child > 0)
} else
if(Cn > 0) {
}
if (!Docmd)
_mode(0);
}
void
int arg;
{
int i;
i = errno;
errno = i;
return;
}
for(i=1; i<8; ++i) {
VERBOSE("[%d]=", i);
}
return;
}
static void
char * name;
{
char *s;
s = "Local";
else
return;
}
static void
long count;
{
static long lcharcnt = 0;
int i;
char c;
lcharcnt = 0;
return;
}
c = '0' + i%10;
if(i%NPL == 0)
}
} else {
lcharcnt = 0;
}
return;
}
/*VARARGS*/
/*ARGSUSED*/
void
{ } /* for ASSERT in gnamef.c */
/*ARGSUSED*/
void
{ } /* so we can load ulockf() */