/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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
* name is defined in /etc/uucp/Devices.
* -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.
* If "-l dev" is used, speed is taken from /etc/uucp/Devices.
* Only systemnames that are included in /etc/uucp/Systems may
* 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'),
* ~%debug (alias ~%d) toggles on/off the program debug trace,
* ~%divert allow unsolicited diversions to files,
* ~%ifc (alias ~%nostop) toggles on/off the DC3/DC1 input control,
* ~%ofc (alias ~%noostop) toggles on/off the DC3/DC1 output control,
* (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()--
* which bypasses /etc/uucp/Systems -- if a telno or direct
* line is chosen. The line termio attributes are set in fixline(),
* before the remote connection is made. As a device-lockout semaphore
* mechanism, uucp creates an entry in /var/spool/locks whose name is
* 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>
#define MID BUFSIZ/2 /* mnemonic */
#define RUB '\177' /* mnemonic */
#define XON '\21' /* mnemonic */
#define XOFF '\23' /* mnemonic */
#define TTYIN 0 /* mnemonic */
#define TTYOUT 1 /* mnemonic */
#define TTYERR 2 /* mnemonic */
#define HUNGUP 2
#define YES 1 /* mnemonic */
#define NO 0 /* mnemonic */
#define IOERR 4 /* exit code */
#define MAXPATH 100
#define NPL 50
int Sflag=0;
int Cn; /*fd for remote comm line */
jmp_buf Sjbuf; /*needed by uucp routines*/
/* 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. */
#define WIOFD(fd) (fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
#define RIOFD(fd) (fd == TTYIN ? 0 : 1)
#define WMASK(fd) (fd == Cn ? line_mask : term_mask)
#define RMASK(fd) (fd == Cn ? line_mask : term_mask)
#define WRIOBSZ 256
static char Riobuf[2*WRIOBSZ];
static char Wiobuf[3*WRIOBSZ];
static int Riocnt[2] = {0, 0};
static char *Riop[2];
static char *Wiop[3];
extern int optind; /* variable in getopt() */
extern char
*optarg;
static struct call Cucall; /* call structure for altconn() */
static int Saved_tty; /* was TCGETAW of _Tv0 successful? */
static int Saved_termios; /* was TCGETSW of _Tv0 successful? */
static struct termio _Tv, _Tv0; /* for saving, changing TTY atributes */
static struct termios _Tv0s; /* for saving, changing TTY atributes */
static struct termio _Lv; /* attributes for the line to remote */
static struct termios _Lvs; /* attributes for the line to remote */
static char prompt[BUFSIZ]= "[";
static struct utsname utsn;
static int command_line_hups = 0;
static char filename[BUFSIZ] = "/dev/null";
static char
_Cxc, /* place into which we do character io*/
_Tintr, /* current input INTR */
_Tquit, /* current input QUIT */
_Terase, /* current input ERASE */
_Tkill, /* current input KILL */
_Teol, /* current secondary input EOL */
_Myeof, /* current input EOF */
term_mask, /* mask value for local terminal */
line_mask; /* mask value for remote line */
/* either '0177' or '0377' */
int
Echoe, /* save users ECHOE bit */
Echok, /* save users ECHOK bit */
Intrupt=NO, /* interrupt indicator */
Ifc=YES, /* NO means remote can't XON/XOFF */
Ofc=YES, /* NO means local can't XON/XOFF */
Rtn_code=0, /* default return code */
Divert=NO, /* don't allow unsolicited redirection */
OldStyle=NO, /* don't handle old '~>:filename' syntax */
/* this will be mandatory in SVR4.1 */
Takeflag=NO, /* indicates a ~%take is in progress */
Dologin=NO, /* go through the login chat sequence */
Docmd=NO; /* execute command instead of interactive cu */
EXTERN int /* These are initialized in line.c */
Terminal, /* flag; remote is a terminal */
Oddflag, /* flag- odd parity option*/
Evenflag, /* flag- even parity option*/
Duplex, /* Unix= full duplex=YES; half = NO */
term_8bit, /* is terminal set for 8 bit processing */
line_8bit; /* is line set for 8 bit processing */
EXTERN int clear_hup();
pid_t
Child, /* pid for receive process */
Shell; /* pid for escape process */
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();
extern int conn(), altconn(), transmit(), tilda();
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";
static char *P_CON_FAILED = "Connect failed: %s\r\n";
static char *P_Ct_OPEN = "Cannot open: %s\r\n";
static char *P_LINE_GONE = "Remote line gone\r\n";
static char *P_Ct_EXSH = "Can't execute shell\r\n";
static char *P_Ct_DIVERT = "Can't divert to %s\r\n";
static char *P_Ct_UNDIVERT = "Can't end diversion to %s\r\n";
static char *P_Bad_DIVERT = "Won't divert to %s. Unsolicited.\r\n";
static char *P_STARTWITH = "Use `~~' to start line with `~'\r\n";
static char *P_CNTAFTER = "File transmission interrupted after %ld bytes.\r\n";
static char *P_CNTLINES = "%d lines/";
static char *P_CNTCHAR = "%ld characters\r\n";
static char *P_FILEINTR = "File transmission interrupted\r\n";
static char *P_Ct_FK = "Can't fork -- try later\r\n";
static char *P_Ct_SPECIAL = "r\nCan't transmit special character `%#o'\r\n";
static char *P_TOOLONG = "\nLine too long\r\n";
static char *P_IOERR = "r\nIO error\r\n";
static char *P_USECMD = "Use `~$'cmd \r\n";
#ifdef forfutureuse
static char *P_USEPLUSCMD ="Use `~+'cmd \r\n";
#endif
#ifdef u3b
static char *P_NOTERMSTAT = "Can't get terminal status\r\n";
static char *P_3BCONSOLE = "Sorry, you can't cu from a 3B console\r\n";
#endif
static char *P_TELLENGTH = "Telno cannot exceed 58 digits!\r\n";
/***************************************************************
* 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
main(argc, argv)
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 */
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
#endif
(void) textdomain(TEXT_DOMAIN);
Riop[0] = &Riobuf[0];
Riop[1] = &Riobuf[WRIOBSZ];
Wiop[0] = &Wiobuf[0];
Wiop[1] = &Wiobuf[WRIOBSZ];
Wiop[2] = &Wiobuf[2*WRIOBSZ];
Verbose = 1; /*for uucp callers, dialers feedback*/
if ((string = strrchr(argv[0], '/')) != NULL)
string++;
else
string = argv[0];
if (strlcpy(Progname, string, NAMESIZE) >= NAMESIZE) {
errno = ENAMETOOLONG;
perror("cu");
exit(1);
}
setservice(Progname);
if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
(void)fprintf(stderr,
gettext("%s: Cannot read Systems files\n"), Progname);
exit(1);
}
if ( sysaccess(EACCESS_DEVICES) != 0 ) {
(void)fprintf(stderr,
gettext("%s: Cannot read Devices files\n"), Progname);
exit(1);
}
if ( sysaccess(EACCESS_DIALERS) != 0 ) {
(void)fprintf(stderr,
gettext("%s: Cannot read Dialers files\n"), Progname);
exit(1);
}
Cucall.speed = "Any"; /*default speed*/
Cucall.line = CNULL;
Cucall.telno = CNULL;
Cucall.type = CNULL;
/*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 */
while((i = getopt(argc, argv, "dhteons:l:c:b:LCH")) != EOF)
switch(i) {
case 'd':
Debug = 9; /*turns on uucp debugging-level 9*/
break;
case 'h':
Duplex = NO;
Ifc = NO;
Ofc = NO;
break;
case 't':
Terminal = YES;
break;
case 'e':
if ( Oddflag ) {
(void)fprintf(stderr,
gettext("%s: Cannot have both even and odd parity\n"),
argv[0]);
exit(1);
}
Evenflag = 1;
break;
case 'o':
if ( Evenflag ) {
(void)fprintf(stderr,
gettext("%s: Cannot have both even and odd parity\n"),
argv[0]);
exit(1);
}
Oddflag = 1;
break;
case 'n':
nflag++;
printf(gettext("Please enter the number: "));
/* Read line from stdin, remove trailing newline, if any */
if (fgets(s, sizeof(s), stdin) != NULL &&
strchr(s, '\n') != NULL)
s[strlen(s)-1] = '\0';
break;
case 's':
Sflag++;
Cucall.speed = optarg;
break;
case 'l':
lflag++;
Cucall.line = optarg;
break;
case 'c':
Cucall.type = optarg;
Mytype = optarg;
break;
case 'b':
line_8bit = ((*optarg=='7') ? NO : ((*optarg=='8') ? YES : -1));
if ( line_8bit == -1 ) {
(void) fprintf(stderr,
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':
command_line_hups++;
break;
case '?':
++errflag;
}
#ifdef u3b
{
struct stat buff;
if(fstat(TTYIN, &buff) < 0) {
VERBOSE(gettext(P_NOTERMSTAT),"");
exit(1);
} else if ( (buff.st_mode & S_IFMT) == S_IFCHR && buff.st_rdev == 0 ) {
VERBOSE(gettext(P_3BCONSOLE),"");
exit(1);
}
}
#endif
if((optind < argc && optind > 0) || (nflag && optind > 0)) {
if(nflag)
string=s;
else
string = strdup(argv[optind++]);
Cucall.telno = string;
if ( strlen(string) != strspn(string, "0123456789=-*#") ) {
/* if it's not a legitimate telno, then it should be a systemname */
if ( nflag ) {
(void)fprintf(stderr, gettext("%s: Bad phone number %s\n"),
argv[0], string);
(void) fprintf(stderr, gettext("Phone numbers may contain "
"only the digits 0 through 9 and the special\n"
"characters =, -, * and #.\n"));
exit(1);
}
systemname++;
}
} else
if(Cucall.line == CNULL) /*if none of above, must be direct */
++errflag;
if(errflag) {
VERBOSE(gettext(P_USAGE), argv[0]);
exit(1);
}
if ((Cucall.telno != CNULL) &&
(strlen(Cucall.telno) >= (size_t)(MAXPH - 1))) {
VERBOSE(gettext(P_TELLENGTH),"");
exit(0);
}
/* save initial tty state */
if (!(Saved_termios = ( ioctl(TTYIN, TCGETS, &_Tv0s) >= 0 ))) {
Saved_tty = ( ioctl(TTYIN, TCGETA, &_Tv0) == 0 );
_Tv0s.c_lflag = _Tv0.c_lflag;
_Tv0s.c_oflag = _Tv0.c_oflag;
_Tv0s.c_iflag = _Tv0.c_iflag;
_Tv0s.c_cflag = _Tv0.c_cflag;
for(i = 0; i < NCC; i++)
_Tv0s.c_cc[i] = _Tv0.c_cc[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.
*/
term_8bit = ( (_Tv0s.c_cflag & CS8) && !(_Tv0s.c_iflag & ISTRIP) &&
((p = setlocale(LC_CTYPE, NULL)) != NULL) && (strcmp(p, "C") != 0) );
if ( !Oddflag && !Evenflag )
if (_Tv0s.c_cflag & PARENB)
if (_Tv0s.c_cflag & PARODD)
Oddflag = 1;
else
Evenflag = 1;
}
if (line_8bit == -1)
line_8bit = term_8bit;
term_mask = ( term_8bit ? 0377 : 0177 );
line_mask = ( line_8bit ? 0377 : 0177 );
/* if not set, use the POSIX disabled designation */
#ifdef _POSIX_VDISABLE
vdisable = _POSIX_VDISABLE;
#else
vdisable = fpathconf(TTYIN, _PC_VDISABLE);
#endif
_Tintr = _Tv0s.c_cc[VINTR] ? _Tv0s.c_cc[VINTR] : vdisable;
_Tquit = _Tv0s.c_cc[VQUIT] ? _Tv0s.c_cc[VQUIT] : vdisable;
_Terase = _Tv0s.c_cc[VERASE] ? _Tv0s.c_cc[VERASE] : vdisable;
_Tkill = _Tv0s.c_cc[VKILL] ? _Tv0s.c_cc[VKILL] : vdisable;
_Teol = _Tv0s.c_cc[VEOL] ? _Tv0s.c_cc[VEOL] : vdisable;
_Myeof = _Tv0s.c_cc[VEOF] ? _Tv0s.c_cc[VEOF] : '\04';
Echoe = _Tv0s.c_lflag & ECHOE;
Echok = _Tv0s.c_lflag & ECHOK;
(void)signal(SIGHUP, cleanup);
(void)signal(SIGQUIT, cleanup);
(void)signal(SIGINT, cleanup);
/* 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 )
(void)fprintf(stderr,
gettext("%s: Warning: -l flag ignored when system name used\n"),
argv[0]);
if ( Sflag )
(void)fprintf(stderr,
gettext("%s: Warning: -s flag ignored when system name used\n"),
argv[0]);
Cn = conn(string);
if ( (Cn < 0) && (Cucall.type != CNULL) )
Cn = altconn(&Cucall);
} else
Cn = altconn(&Cucall);
if(Cn < 0) {
VERBOSE(gettext(P_CON_FAILED),UERRORTEXT);
cleanup(-Cn);
} else {
struct stat Cnsbuf;
if ( fstat(Cn, &Cnsbuf) == 0 )
Dev_mode = Cnsbuf.st_mode;
else
Dev_mode = R_DEVICEMODE;
fchmod(Cn, M_DEVICEMODE);
}
if ((Docmd) && (argv[optind] == NULL)) {
(void) fprintf(stderr,gettext("cu: local cmd is required, -C is ignored.\n"));
VERBOSE(gettext(P_USAGE), argv[0]);
Docmd=NO;
}
if (!Docmd) {
Euid = geteuid();
if((setuid(getuid()) < 0) || (setgid(getgid()) < 0)) {
VERBOSE("Unable to setuid/gid\n%s", "");
cleanup(101);
}
}
if(Debug)
tdmp(Cn);
/* At this point succeeded in getting an open communication line */
/* Conn() takes care of closing the Systems file */
if (!Docmd) {
(void)signal(SIGINT,_onintrpt);
_mode(1); /* put terminal in `raw' mode */
VERBOSE("Connected\007\r\n%s", ""); /*bell!*/
/* 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(). */
(void)signal(SIGUSR1, _bye);
(void)signal(SIGHUP, cleanup);
(void)signal(SIGQUIT, _onintrpt);
sysname(&prompt[1]); /* set up system name prompt */
(void) strcat(prompt, "]");
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;
Rtn_code = transmit();
_quit(Rtn_code);
/*NOTREACHED*/
}
} else {
/*
* Fork a child to run the specified command,
* wait for it to finish, and clean up.
*/
Child = dofork();
if (Child == 0) {
close(0);
close(1);
dup(Cn);
dup(Cn);
close(Cn);
setgid(getgid());
setuid(getuid());
execvp(argv[optind], &argv[optind]);
exit(-1);
/* NOTREACHED */
}
wait(0);
/* XXX - should return wait status as our exit code */
}
cleanup(Cn);
/*NOTREACHED*/
return (0);
}
/*
* Kill the present child, if it exists, then fork a new one.
*/
static void
recfork()
{
int ret, status;
if (Child) {
kill(Child, SIGKILL);
while ( (ret = wait(&status)) != Child )
if (ret == -1 && errno != EINTR)
break;
}
Child = dofork();
if(Child == 0) {
(void)signal(SIGUSR1, SIG_DFL);
(void)signal(SIGHUP, _rcvdead);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
_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;
int id = 0; /* flag for systemname prompt on tilda escape */
CDEBUG(4,"transmit started\n\r%s", "");
/* In main loop, always waiting to read characters from */
/* keyboard; writes characters to remote, or to TTYOUT */
/* on a tilda escape */
for (;;) {
p = b;
while(r_char(TTYIN) == YES) {
if(p == b) /* Escape on leading ~ */
escape = (_Cxc == '~');
if(p == b+1) /* But not on leading ~~ */
escape &= (_Cxc != '~');
if(escape) {
if(_Cxc == '\n' || _Cxc == '\r' || _Cxc == _Teol) {
*p = '\0';
if(tilda(b+1) == YES)
return(0);
id = 0;
break;
}
if(_Cxc == _Tintr || _Cxc == _Tkill || _Cxc == _Tquit ||
(Intrupt && _Cxc == '\0')) {
if(_Cxc == _Tkill) {
if(Echok)
VERBOSE("\r\n%s", "");
} else {
_Cxc = '\r';
if( w_char(Cn) == NO) {
VERBOSE(gettext(P_LINE_GONE),"");
return(IOERR);
}
id=0;
}
break;
}
if((p == b+1) && (_Cxc != _Terase) && (!id)) {
id = 1;
VERBOSE("%s", prompt);
}
if(_Cxc == _Terase) {
p = (--p < b)? b:p;
if(p > b)
if(Echoe) {
VERBOSE("\b \b%s", "");
} else
(void)w_char(TTYOUT);
} else {
(void)w_char(TTYOUT);
if(p-b < BUFSIZ)
*p++ = _Cxc;
else {
VERBOSE(gettext(P_TOOLONG),"");
break;
}
}
/*not a tilda escape command*/
} else {
if(Intrupt && _Cxc == '\0') {
CDEBUG(4,"got break in transmit\n\r%s", "");
Intrupt = NO;
(*genbrk)(Cn);
_flush();
break;
}
if(w_char(Cn) == NO) {
VERBOSE(gettext(P_LINE_GONE),"");
return(IOERR);
}
if(Duplex == NO) {
if((w_char(TTYERR) == NO) || (wioflsh(TTYERR) == NO))
return(IOERR);
}
if ((_Cxc == _Tintr) || (_Cxc == _Tquit) ||
( (p==b) && (_Cxc == _Myeof) ) ) {
CDEBUG(4,"got a tintr\n\r%s", "");
_flush();
break;
}
if(_Cxc == '\n' || _Cxc == '\r' ||
_Cxc == _Teol || _Cxc == _Tkill) {
id=0;
Takeflag = NO;
break;
}
p = (char*)0;
}
}
}
}
/***************************************************************
* routine to halt input from remote and flush buffers
***************************************************************/
static void
_flush()
{
(void)ioctl(TTYOUT, TCXONC, 0); /* stop tty output */
(void)ioctl(Cn, TCFLSH, 0); /* flush remote input */
(void)ioctl(TTYOUT, TCFLSH, 1); /* flush tty output */
(void)ioctl(TTYOUT, TCXONC, 1); /* restart tty output */
if(Takeflag == NO) {
return; /* didn't interupt file transmission */
}
VERBOSE(gettext(P_FILEINTR),"");
(void)sleep(3);
_w_str("echo '\n~>\n';mesg y;stty echo\n");
Takeflag = NO;
return;
}
/**************************************************************
* command interpreter for escape lines
**************************************************************/
int
tilda(cmd)
char *cmd;
{
VERBOSE("\r\n%s", "");
CDEBUG(4,"call tilda(%s)\r\n", cmd);
switch(cmd[0]) {
case CSUSP:
case CDSUSP:
_mode(0);
kill(cmd[0] == CDSUSP ? getpid() : (pid_t) 0, SIGTSTP);
_mode(1);
break;
case '.':
if(Cucall.telno == CNULL)
if(cmd[1] != '.') {
_w_str("\04\04\04\04\04");
if (Child)
kill(Child, SIGKILL);
if (ioctl (Cn, TCGETS, &_Lvs) < 0) {
(void) ioctl (Cn, TCGETA, &_Lv);
/* speed to zero for hangup */
_Lv.c_cflag = 0;
(void) ioctl (Cn, TCSETAW, &_Lv);
} else {
/* speed to zero for hangup */
_Lvs.c_cflag &= 0xffff0000;
cfsetospeed(&_Lvs, B0);
(void) ioctl (Cn, TCSETSW, &_Lvs);
}
(void) sleep (2);
}
return(YES);
case '!':
_shell(cmd); /* local shell */
VERBOSE("\r%c\r\n", *cmd);
VERBOSE("(continue)%s", "");
break;
case '$':
if(cmd[1] == '\0') {
VERBOSE(gettext(P_USECMD),"");
VERBOSE("(continue)%s", "");
} else {
_shell(cmd); /*Local shell */
VERBOSE("\r%c\r\n", *cmd);
}
break;
#ifdef forfutureuse
case '+':
if(cmd[1] == '\0') {
VERBOSE(gettext(P_USEPLUSCMD), "");
VERBOSE("(continue)%s", "");
} else {
if (*cmd == '+')
/* must suspend receive to give*/
/*remote out to stdin of cmd */
kill(Child, SIGKILL);
_shell(cmd); /* Local shell */
if (*cmd == '+')
recfork();
VERBOSE("\r%c\r\n", *cmd);
}
break;
#endif
case '%':
_dopercen(++cmd);
break;
case 't':
tdmp(TTYIN);
VERBOSE("(continue)%s", "");
break;
case 'l':
tdmp(Cn);
VERBOSE("(continue)%s", "");
break;
default:
VERBOSE(gettext(P_STARTWITH),"");
VERBOSE("(continue)%s", "");
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
_shell(str)
char *str;
{
pid_t fk, w_ret;
void (*xx)(), (*yy)();
CDEBUG(4,"call _shell(%s)\r\n", str);
fk = dofork();
if(fk < 0)
return;
Shell = fk;
_mode(0); /* restore normal tty attributes */
xx = signal(SIGINT, SIG_IGN);
yy = signal(SIGQUIT, SIG_IGN);
if(fk == 0) {
char *shell;
if( (shell = getenv("SHELL")) == NULL)
/* use default if user's shell is not set */
shell = SHELL;
(void)close(TTYOUT);
/***********************************************
* Hook-up our "standard output"
* to either the tty for '!' or the line
* for '$' as appropriate
***********************************************/
#ifdef forfutureuse
/************************************************
* Or to the line for '+'.
**********************************************/
#endif
(void)fcntl((*str == '!')? TTYERR:Cn,F_DUPFD,TTYOUT);
#ifdef forfutureuse
/*************************************************
* Hook-up "standard input" to the line for '+'.
* **********************************************/
if (*str == '+') {
(void)close(TTYIN);
(void)fcntl(Cn,F_DUPFD,TTYIN);
}
#endif
/***********************************************
* Hook-up our "standard input"
* to the tty for '!' and '$'.
***********************************************/
(void)close(Cn); /*parent still has Cn*/
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGHUP, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
(void)signal(SIGUSR1, SIG_DFL);
if(*++str == '\0')
(void)execl(shell,shell,(char*) 0,(char*) 0,(char *) 0);
else
(void)execl(shell,"sh","-c",str,(char *) 0);
VERBOSE(gettext(P_Ct_EXSH),"");
exit(0);
}
while ((w_ret = wait((int*)0)) != fk)
if (w_ret == -1 && errno != EINTR)
break;
Shell = 0;
(void)signal(SIGINT, xx);
(void)signal(SIGQUIT, yy);
_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
_dopercen(cmd)
char *cmd;
{
char *arg[5];
char *getpath;
char mypath[MAXPATH];
int narg;
blckcnt((long)(-1));
CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd);
arg[narg=0] = strtok(cmd, " \t\n");
/* following loop breaks out the command and args */
while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
if(narg < 4)
continue;
else
break;
}
/* ~%take file option */
if(EQUALS(arg[0], "take")) {
if(narg < 2 || narg > 3) {
VERBOSE("usage: ~%%take from [to]\r\n%s", "");
VERBOSE("(continue)%s", "");
return;
}
if(narg == 2)
arg[2] = arg[1];
(void) strcpy(filename, arg[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(arg[1]);
_w_str("; then (echo '~");
_w_str(prompt);
_w_str(">'");
_w_str(arg[2]);
_w_str(";cat ");
_w_str(arg[1]);
_w_str(";echo '~");
_w_str(prompt);
_w_str(">'); else echo cant\\'t open: ");
_w_str(arg[1]);
_w_str("; fi;stty echo\n");
Takeflag = YES;
return;
}
/* ~%put file option*/
if(EQUALS(arg[0], "put")) {
FILE *file;
char ch, buf[BUFSIZ], spec[NCC+1], *b, *p, *q;
int i, j, len, tc=0, lines=0;
long chars=0L;
if(narg < 2 || narg > 3) {
VERBOSE("usage: ~%%put from [to]\r\n%s", "");
VERBOSE("(continue)%s", "");
return;
}
if(narg == 2)
arg[2] = arg[1];
if((file = fopen(arg[1], "r")) == NULL) {
VERBOSE(gettext(P_Ct_OPEN), arg[1]);
VERBOSE("(continue)%s", "");
return;
}
/*
* if cannot write into file on remote machine, write into
* /dev/null
*
* what we're doing is:
* stty -echo
* (cat - > arg2) || cat - > /dev/null
* stty echo
*/
_w_str("stty -echo;(cat - >");
_w_str(arg[2]);
_w_str(")||cat - >/dev/null;stty echo\n");
Intrupt = NO;
for(i=0,j=0; i < NCC; ++i)
if((ch=_Tv0s.c_cc[i]) != '\0')
spec[j++] = ch;
spec[j] = '\0';
_mode(2); /*accept interrupts from keyboard*/
(void)sleep(5); /*hope that w_str info digested*/
/* Read characters line by line into buf to write to */
/* remote with character and line count for blckcnt */
while(Intrupt == NO &&
fgets(b= &buf[MID],MID,file) != NULL) {
/* worse case is each char must be escaped*/
len = strlen(b);
chars += len; /* character count */
p = b;
while(q = strpbrk(p, spec)) {
if(*q == _Tintr || *q == _Tquit || *q == _Teol) {
VERBOSE(gettext(P_Ct_SPECIAL), *q);
(void)strcpy(q, q+1);
Intrupt = YES;
} else {
b = strncpy(b-1, b, q-b);
*(q-1) = '\\';
}
p = q+1;
}
if((tc += len) >= MID) {
(void)sleep(1);
tc = len;
}
if(write(Cn, b, (unsigned)strlen(b)) < 0) {
VERBOSE(gettext(P_IOERR),"");
Intrupt = YES;
break;
}
++lines; /* line count */
blckcnt((long)chars);
}
_mode(1);
blckcnt((long)(-2)); /* close */
(void)fclose(file);
if(Intrupt == YES) {
Intrupt = NO;
_w_str("\n");
VERBOSE(gettext(P_CNTAFTER), ++chars);
} else {
VERBOSE(gettext(P_CNTLINES), lines);
VERBOSE(gettext(P_CNTCHAR),chars);
}
(void)sleep(3);
_w_str("\04");
return;
}
/* ~%b or ~%break */
if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
(*genbrk)(Cn);
return;
}
/* ~%d or ~%debug toggle */
if(EQUALS(arg[0], "d") || EQUALS(arg[0], "debug")) {
if(Debug == 0)
Debug = 9;
else
Debug = 0;
VERBOSE("(continue)%s", "");
return;
}
/* ~%[ifc|nostop] toggles start/stop input control */
if( EQUALS(arg[0], "ifc") || EQUALS(arg[0], "nostop") ) {
(void)ioctl(Cn, TCGETA, &_Tv);
Ifc = !Ifc;
if(Ifc == YES)
_Tv.c_iflag |= IXOFF;
else
_Tv.c_iflag &= ~IXOFF;
(void)ioctl(Cn, TCSETAW, &_Tv);
_mode(1);
VERBOSE("(ifc %s)", (Ifc ? "enabled" : "disabled"));
VERBOSE("(continue)%s", "");
return;
}
/* ~%[ofc|noostop] toggles start/stop output control */
if( EQUALS(arg[0], "ofc") || EQUALS(arg[0], "noostop") ) {
(void)ioctl(Cn, TCGETA, &_Tv);
Ofc = !Ofc;
if(Ofc == YES)
_Tv.c_iflag |= IXON;
else
_Tv.c_iflag &= ~IXON;
(void)ioctl(Cn, TCSETAW, &_Tv);
_mode(1);
VERBOSE("(ofc %s)", (Ofc ? "enabled" : "disabled"));
VERBOSE("(continue)%s", "");
return;
}
/* ~%divert toggles unsolicited redirection security */
if( EQUALS(arg[0], "divert") ) {
Divert = !Divert;
recfork(); /* fork a new child so it knows about change */
VERBOSE("(unsolicited diversion %s)", (Divert ? "enabled" : "disabled"));
VERBOSE("(continue)%s", "");
return;
}
/* ~%old toggles recognition of old-style '~>:filename' */
if( EQUALS(arg[0], "old") ) {
OldStyle = !OldStyle;
recfork(); /* fork a new child so it knows about change */
VERBOSE("(old-style diversion %s)", (OldStyle ? "enabled" : "disabled"));
VERBOSE("(continue)%s", "");
return;
}
/* Change local current directory */
if(EQUALS(arg[0], "cd")) {
if (narg < 2) {
getpath = getenv("HOME");
strlcpy(mypath, getpath, sizeof (mypath));
if(chdir(mypath) < 0) {
VERBOSE("Cannot change to %s\r\n", mypath);
VERBOSE("(continue)%s", "");
return;
}
} else if (chdir(arg[1]) < 0) {
VERBOSE("Cannot change to %s\r\n", arg[1]);
VERBOSE("(continue)%s", "");
return;
}
recfork(); /* fork a new child so it knows about change */
VERBOSE("(continue)%s", "");
return;
}
if (arg[0] == (char *) NULL)
arg[0] = "";
VERBOSE("~%%%s unknown to cu\r\n", arg[0]);
VERBOSE("(continue)%s", "");
return;
}
/***************************************************************
* receive: read from remote line, write to fd=1 (TTYOUT)
* catch:
* ~>[>]:file
* .
* . stuff for file
* .
* ~> (ends diversion)
***************************************************************/
static void
_receive()
{
int silent = NO, file = -1;
char *p;
int tic;
int for_me = NO;
char b[BUFSIZ];
char *b_p;
long count;
int line_ok = 1, rval;
CDEBUG(4,"_receive started\r\n%s", "");
b[0] = '\0';
b_p = p = b;
while(line_ok) {
rval = r_char(Cn);
if (rval == NO) {
line_ok = 0;
continue;
}
if (rval == HUNGUP) {
if (command_line_hups > 0) {
CDEBUG(4, "Ignoring device hangup\n%s", "");
command_line_hups--;
(void) setuid(Euid); /* reacquire privileges */
if (clear_hup(Cn) != SUCCESS) {
DEBUG(4, "Unable to clear hup on device\n%s", "");
line_ok = 0;
}
(void) setuid(getuid()); /* relinquish privileges */
} else
line_ok = 0;
continue;
}
if(silent == NO) /* ie., if not redirecting from screen */
if(w_char(TTYOUT) == NO)
_rcvdead(IOERR); /* this will exit */
/* remove CR's and fill inserted by remote */
if(_Cxc == '\0' || _Cxc == RUB || _Cxc == '\r')
continue;
*p++ = _Cxc;
if(_Cxc != '\n' && (p-b) < BUFSIZ)
continue;
/* ****************************************** */
/* This code deals with ~%take file diversion */
/* ****************************************** */
if (b[0] == '~') {
int append;
if (EQUALSN(&b[1],prompt,strlen(prompt))) {
b_p = b + 1 + strlen(prompt);
for_me = YES;
} else {
b_p = b + 1;
for_me = NO;
}
if ( (for_me || OldStyle) && (*b_p == '>') ) {
/* This is an acceptable '~[uname]>' line */
b_p++;
if ( (*b_p == '\n') && (silent == YES) ) {
/* end of diversion */
*b_p = '\0';
(void) strcpy(filename, "/dev/null");
if ( file >= 0 && close(file) ) {
VERBOSE(gettext(P_Ct_UNDIVERT), b_p);
perror(gettext("cu: close failed"));
VERBOSE("%s","\r");
}
silent = NO;
blckcnt((long)(-2));
VERBOSE("%s\r\n", b);
VERBOSE(gettext(P_CNTLINES), tic);
VERBOSE(gettext(P_CNTCHAR), count);
file = -1;
p = b;
continue;
} else if (*b_p != '\n') {
if ( *b_p == '>' ) {
append = 1;
b_p++;
}
if ( (for_me || (OldStyle && (*b_p == ':'))) && (silent == NO) ) {
/* terminate filename string */
*(p-1) = '\0';
if ( *b_p == ':' )
b_p++;
if ( !EQUALS(filename, b_p) ) {
if ( !Divert || !EQUALS(filename, "/dev/null") ) {
VERBOSE(gettext(P_Bad_DIVERT), b_p);
(void) strcpy(filename, "/dev/null");
append = 1;
} else {
(void) strcpy(filename, b_p);
}
}
if ( append && ((file=open(filename,O_WRONLY)) >= 0) )
(void)lseek(file, 0L, 2);
else
file = creat(filename, PUB_FILEMODE);
if (file < 0) {
VERBOSE(gettext(P_Ct_DIVERT), filename);
perror(gettext("cu: open|creat failed"));
VERBOSE("%s","\r");
(void)sleep(5); /* 10 seemed too long*/
}
silent = YES;
count = tic = 0;
p = b;
continue;
}
}
}
}
/* Regular data, divert if appropriate */
if ( silent == YES ) {
if ( file >= 0)
(void)write(file, b, (unsigned)(p-b));
count += p-b; /* tally char count */
++tic; /* tally lines */
blckcnt((long)count);
}
p = b;
}
/*
* we used to tell of lost carrier here, but now
* defer to _bye() so that escape processes are
* not interrupted.
*/
_rcvdead(IOERR);
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
_mode(arg)
{
int i;
CDEBUG(4,"call _mode(%d)\r\n", arg);
if(arg == 0) {
if ( Saved_termios )
(void)ioctl(TTYIN, TCSETSW, &_Tv0s);
else if ( Saved_tty ) {
_Tv0.c_lflag = _Tv0s.c_lflag;
_Tv0.c_oflag = _Tv0s.c_oflag;
_Tv0.c_iflag = _Tv0s.c_iflag;
_Tv0.c_cflag = _Tv0s.c_cflag;
for(i = 0; i < NCC; i++)
_Tv0.c_cc[i] = _Tv0s.c_cc[i];
(void)ioctl(TTYIN, TCSETAW, &_Tv0);
}
} else {
(void)ioctl(TTYIN, TCGETA, &_Tv);
if(arg == 1) {
_Tv.c_iflag &= ~(INLCR | ICRNL | IGNCR | IUCLC);
if ( !term_8bit )
_Tv.c_iflag |= ISTRIP;
_Tv.c_oflag |= OPOST;
_Tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
_Tv.c_lflag &= ~(ICANON | ISIG | ECHO);
if(Ifc == NO)
_Tv.c_iflag &= ~IXON;
else
_Tv.c_iflag |= IXON;
if(Ofc == NO)
_Tv.c_iflag &= ~IXOFF;
else
_Tv.c_iflag |= IXOFF;
if(Terminal) {
_Tv.c_oflag |= ONLCR;
_Tv.c_iflag |= ICRNL;
}
_Tv.c_cc[VEOF] = '\01';
_Tv.c_cc[VEOL] = '\0';
}
if(arg == 2) {
_Tv.c_iflag |= IXON;
_Tv.c_lflag |= ISIG;
}
(void)ioctl(TTYIN, TCSETAW, &_Tv);
}
return;
}
static pid_t
dofork()
{
int i;
pid_t x;
for(i = 0; i < 6; ++i) {
if((x = fork()) >= 0) {
return(x);
}
}
if(Debug) perror("dofork");
VERBOSE(gettext(P_Ct_FK),"");
return(x);
}
static int
r_char(fd)
{
int rtn = 1, rfd;
char *riobuf;
/* find starting pos in correct buffer in Riobuf */
rfd = RIOFD(fd);
riobuf = &Riobuf[rfd*WRIOBSZ];
if (Riop[rfd] >= &riobuf[Riocnt[rfd]]) {
/* empty read buffer - refill it */
/* flush any waiting output */
if ( (wioflsh(Cn) == NO ) || (wioflsh(TTYOUT) == NO) )
return(NO);
while((rtn = read(fd, riobuf, WRIOBSZ)) < 0){
if(errno == EINTR) {
/* onintrpt() called asynchronously before this line */
if(Intrupt == YES) {
/* got a BREAK */
_Cxc = '\0';
return(YES);
} else {
/*a signal other than interrupt*/
/*received during read*/
continue;
}
} else {
CDEBUG(4,"got read error, not EINTR\n\r%s", "");
break; /* something wrong */
}
}
if (rtn > 0) {
/* reset current position in buffer */
/* and count of available chars */
Riop[rfd] = riobuf;
Riocnt[rfd] = rtn;
}
}
if ( rtn > 0 ) {
_Cxc = *(Riop[rfd]++) & RMASK(fd); /* mask off appropriate bits */
return(YES);
} else if (rtn == 0) {
_Cxc = '\0';
return (HUNGUP);
} else {
_Cxc = '\0';
return(NO);
}
}
static int
w_char(fd)
{
int wfd;
char *wiobuf;
/* find starting pos in correct buffer in Wiobuf */
wfd = WIOFD(fd);
wiobuf = &Wiobuf[wfd*WRIOBSZ];
if (Wiop[wfd] >= &wiobuf[WRIOBSZ]) {
/* full output buffer - flush it */
if ( wioflsh(fd) == NO )
return(NO);
}
*(Wiop[wfd]++) = _Cxc & WMASK(fd); /* mask off appropriate bits */
return(YES);
}
/* wioflsh flush output buffer */
static int
wioflsh(fd)
int fd;
{
int wfd;
char *wiobuf;
/* find starting pos in correct buffer in Wiobuf */
wfd = WIOFD(fd);
wiobuf = &Wiobuf[wfd*WRIOBSZ];
if (Wiop[wfd] > wiobuf) {
/* there's something in the buffer */
while(write(fd, wiobuf, (Wiop[wfd] - wiobuf)) < 0) {
if(errno == EINTR) {
if(Intrupt == YES) {
VERBOSE("\ncu: Output blocked\r\n%s", "");
_quit(IOERR);
} else
continue; /* alarm went off */
} else {
Wiop[wfd] = wiobuf;
return(NO); /* bad news */
}
}
}
Wiop[wfd] = wiobuf;
return(YES);
}
static void
_w_str(string)
char *string;
{
int len;
len = strlen(string);
if ( write(Cn, string, (unsigned)len) != len )
VERBOSE(gettext(P_LINE_GONE),"");
return;
}
/* ARGSUSED */
static void
_onintrpt(sig)
int sig;
{
(void)signal(SIGINT, _onintrpt);
(void)signal(SIGQUIT, _onintrpt);
Intrupt = YES;
return;
}
static void
_rcvdead(arg) /* this is executed only in the receive process */
int arg;
{
CDEBUG(4,"call _rcvdead(%d)\r\n", arg);
(void)kill(getppid(), SIGUSR1);
exit((arg == SIGHUP)? SIGHUP: arg);
/*NOTREACHED*/
}
static void
_quit(arg) /* this is executed only in the parent process */
int arg;
{
CDEBUG(4,"call _quit(%d)\r\n", arg);
(void)kill(Child, SIGKILL);
_bye(arg);
/*NOTREACHED*/
}
static void
_bye(arg) /* this is executed only in the parent proccess */
int arg;
{
int status;
pid_t obit;
if ( Shell > 0 )
while ((obit = wait(&status)) != Shell) {
if (obit == -1 && errno != EINTR)
break;
/* _receive (Child) may have ended - check it out */
if (obit == Child)
Child = 0;
}
/* give user customary message after escape command returns */
if (arg == SIGUSR1)
VERBOSE("\r\nLost Carrier\r\n%s", "");
CDEBUG(4,"call _bye(%d)\r\n", arg);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
/* if _receive() ended already, don't wait for it again */
if ( Child != 0 )
while ((obit = wait(&status)) != Child)
if (obit == -1 && errno != EINTR)
break;
VERBOSE("\r\nDisconnected\007\r\n%s", "");
cleanup((arg == SIGUSR1)? (status >>= 8): arg);
/*NOTREACHED*/
}
void
cleanup(code) /*this is executed only in the parent process*/
int code; /*Closes device; removes lock files */
{
CDEBUG(4,"call cleanup(%d)\r\n", code);
if (Docmd) {
if (Child > 0)
(void)kill(Child, SIGTERM);
} else
(void) setuid(Euid);
if(Cn > 0) {
fchmod(Cn, Dev_mode);
fd_rmlock(Cn);
(void)close(Cn);
}
rmlock((char*) NULL); /* remove all lock files for this process */
if (!Docmd)
_mode(0);
exit(code); /* code=negative for signal causing disconnect*/
}
void
tdmp(arg)
int arg;
{
struct termio xv;
int i;
VERBOSE("\rdevice status for fd=%d\r\n", arg);
VERBOSE("F_GETFL=%o,", fcntl(arg, F_GETFL,1));
if(ioctl(arg, TCGETA, &xv) < 0) {
char buf[100];
i = errno;
(void)snprintf(buf, sizeof (buf), gettext("\rtdmp for fd=%d"), arg);
errno = i;
perror(buf);
return;
}
VERBOSE("iflag=`%o',", xv.c_iflag);
VERBOSE("oflag=`%o',", xv.c_oflag);
VERBOSE("cflag=`%o',", xv.c_cflag);
VERBOSE("lflag=`%o',", xv.c_lflag);
VERBOSE("line=`%o'\r\n", xv.c_line);
VERBOSE("cc[0]=`%o',", xv.c_cc[0]);
for(i=1; i<8; ++i) {
VERBOSE("[%d]=", i);
VERBOSE("`%o',",xv.c_cc[i]);
}
VERBOSE("\r\n%s", "");
return;
}
static void
sysname(name)
char * name;
{
char *s;
if(uname(&utsn) < 0)
s = "Local";
else
s = utsn.nodename;
strcpy(name, s);
return;
}
static void
blckcnt(count)
long count;
{
static long lcharcnt = 0;
long c1, c2;
int i;
char c;
if(count == (long) (-1)) { /* initialization call */
lcharcnt = 0;
return;
}
c1 = lcharcnt/BUFSIZ;
if(count != (long)(-2)) { /* regular call */
c2 = count/BUFSIZ;
for(i = c1; i++ < c2;) {
c = '0' + i%10;
write(2, &c, 1);
if(i%NPL == 0)
write(2, "\n\r", 2);
}
lcharcnt = count;
} else {
c2 = (lcharcnt + BUFSIZ -1)/BUFSIZ;
if(c1 != c2)
write(2, "+\n\r", 3);
else if(c2%NPL != 0)
write(2, "\n\r", 2);
lcharcnt = 0;
}
return;
}
/*VARARGS*/
/*ARGSUSED*/
void
assert (s1, s2, i1, s3, i2)
char *s1, *s2, *s3;
int i1, i2;
{ } /* for ASSERT in gnamef.c */
/*ARGSUSED*/
void
logent (s1, s2)
char *s1, *s2;
{ } /* so we can load ulockf() */