diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/tip | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/tip')
27 files changed, 5492 insertions, 0 deletions
diff --git a/usr/src/cmd/tip/Makefile b/usr/src/cmd/tip/Makefile new file mode 100644 index 0000000000..af3e06e7e6 --- /dev/null +++ b/usr/src/cmd/tip/Makefile @@ -0,0 +1,117 @@ +# +# Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Makefile for intermachine communications package. +# +# Files are: +# /etc/remote remote host description file +# /etc/phones phone number file, owned by $(OWNER) and +# mode 6?? +# /var/adm/aculog ACU accounting file, owned by $(OWNER) and +# mode 6?? (if ACULOG defined) +# Presently supports: +# BIZCOMP +# DEC DF02-AC, DF03-AC +# DEC DN-11/Able Quadracall +# VENTEL 212+ +# VADIC 831 RS232 adaptor +# VADIC 3451 +# HAYES SmartModem +# (drivers are located in aculib.a) +# +# Configuration defines: +# DF02, DF03, DN11 ACU's supported +# BIZ1031, BIZ1022, VENTEL, V831, V3451, HAYES +# ACULOG turn on tip logging of ACU use +# PRISTINE no phone #'s put in ACU log file +# DEFBR default baud rate to make connection at +# DEFFS default frame size for FTP buffering of +# writes on local side +# BUFSIZ buffer sizing from stdio, must be fed +# explicitly to remcap.c if not 1024 +# +# cmd/tip/Makefile + +PROG= tip + +OBJS= acu.o cmds.o cmdtab.o cu.o hunt.o \ + log.o partab.o remote.o tip.o tipout.o value.o vars.o \ + acutab.o remcap.o uucplock.o + +# sigh, NSE can't handle wildcards +#DRIVERS= aculib/*.c + +SRCS= $(OBJS:.o=.c) + +SOURCES=$(SRCS) $(DRIVERS) + +include ../Makefile.cmd + +REMOTE= etc.remote +ACULOG= aculog +ROOTETCREMOTE= $(ROOTETC)/remote +ROOTACULOGD= $(ROOT)/var/adm +ROOTACULOG= $(ROOTACULOGD)/aculog + +$(ROOTPROG) := FILEMODE = 4511 +$(ROOTPROG) := OWNER = uucp +$(ROOTETCREMOTE) := FILEMODE = 644 +$(ROOTACULOG) := FILEMODE = 600 +$(ROOTACULOG) := OWNER = uucp + +CPPFLAGS += -DDEFBR=300 -DDEFFS=BUFSIZ -DACULOG -DUSG +CONFIG= -DV831 -DVENTEL -DV3451 -DDF02 -DDF03 -DBIZ1031 -DBIZ1022 -DHAYES +ACULIB= aculib/aculib.a +LDLIBS= $(ACULIB) $(LDLIBS.cmd) + +# install rules +$(ROOTACULOGD)/% : % + $(INS.file) + +$(ROOTETC)/% : etc.% + $(INS.rename) + +.KEEP_STATE: + +.PARALLEL: $(OBJS) + +all: $(PROG) $(REMOTE) $(ACULOG) + +$(PROG): $(OBJS) $(ACULIB) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +# special build rules +remcap.o := CPPFLAGS += -DBUFSIZ=1024 +acutab.o := CPPFLAGS += $(CONFIG) + +# acutab is configuration dependent, and so depends on the makefile +acutab.o: Makefile + +# remote.o depends on the makefile because of DEFBR and DEFFS +remote.o: Makefile + +# log.o depends on the makefile because of ACULOG +log.o: Makefile + +$(ACULIB): FRC + cd aculib; $(MAKE) + +install: all $(ROOTPROG) $(ROOTETCREMOTE) $(ROOTACULOG) + +$(ACULOG): + cp /dev/null $(ACULOG) + +clean: FRC + cd aculib; $(MAKE) clean + $(RM) $(OBJS) + +lint: + -lint -hbacvx $(CFLAGS) $(SOURCES) + +include ../Makefile.targ + +FRC: diff --git a/usr/src/cmd/tip/acu.c b/usr/src/cmd/tip/acu.c new file mode 100644 index 0000000000..76de8d1f58 --- /dev/null +++ b/usr/src/cmd/tip/acu.c @@ -0,0 +1,159 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.3 4/3/86 */ + +#include "tip.h" + +static acu_t *acu = NOACU; +static int conflag; +static void acuabort(); +static acu_t *acutype(); +static sigjmp_buf jmpbuf; +/* + * Establish connection for tip + * + * If DU is true, we should dial an ACU whose type is AT. + * The phone numbers are in PN, and the call unit is in CU. + * + * If the PN is an '@', then we consult the PHONES file for + * the phone numbers. This file is /etc/phones, unless overriden + * by an exported shell variable. + * + * The data base files must be in the format: + * host-name[ \t]*phone-number + * with the possibility of multiple phone numbers + * for a single host acting as a rotary (in the order + * found in the file). + */ +char * +connect() +{ + register char *cp = PN; + char *phnum, string[256]; + int tried = 0; + + if (!DU) + return (NOSTR); + /* + * @ =>'s use data base in PHONES environment variable + * otherwise, use /etc/phones + */ + if (sigsetjmp(jmpbuf, 1)) { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + printf("\ncall aborted\n"); + logent(value(HOST), "", "", "call aborted"); + if (acu != NOACU) { + boolean(value(VERBOSE)) = FALSE; + if (conflag) + disconnect(NOSTR); + else + (*acu->acu_abort)(); + } + myperm(); + delock(uucplock); + exit(1); + } + signal(SIGINT, acuabort); + signal(SIGQUIT, acuabort); + if ((acu = acutype(AT)) == NOACU) + return ("unknown ACU type"); + if (*cp != '@') { + while (*cp) { + for (phnum = cp; *cp && *cp != '|'; cp++) + ; + if (*cp) + *cp++ = '\0'; + + if (conflag = (*acu->acu_dialer)(phnum, CU)) { + logent(value(HOST), phnum, acu->acu_name, + "call completed"); + return (NOSTR); + } else + logent(value(HOST), phnum, acu->acu_name, + "call failed"); + tried++; + } + } else { + if (phfd == NOFILE) { + printf("%s: ", PH); + return ("can't open phone number file"); + } + rewind(phfd); + while (fgets(string, sizeof (string), phfd) != NOSTR) { + if (string[0] == '#') + continue; + for (cp = string; !any(*cp, " \t\n"); cp++) + ; + if (*cp == '\n') + return ("unrecognizable host name"); + *cp++ = '\0'; + if (!equal(string, value(HOST))) + continue; + while (any(*cp, " \t")) + cp++; + if (*cp == '\n') + return ("missing phone number"); + for (phnum = cp; *cp && *cp != '|' && *cp != '\n'; cp++) + ; + *cp = '\0'; + + if (conflag = (*acu->acu_dialer)(phnum, CU)) { + logent(value(HOST), phnum, acu->acu_name, + "call completed"); + return (NOSTR); + } else + logent(value(HOST), phnum, acu->acu_name, + "call failed"); + tried++; + } + } + if (!tried) + logent(value(HOST), "", acu->acu_name, "missing phone number"); + else + (*acu->acu_abort)(); + return (tried ? "call failed" : "missing phone number"); +} + +disconnect(reason) + char *reason; +{ + if (!conflag) + return; + if (reason == NOSTR) { + logent(value(HOST), "", acu->acu_name, "call terminated"); + if (boolean(value(VERBOSE))) + printf("\r\ndisconnecting..."); + } else + logent(value(HOST), "", acu->acu_name, reason); + (*acu->acu_disconnect)(); +} + +static void +acuabort(s) +{ + signal(s, SIG_IGN); + siglongjmp(jmpbuf, 1); +} + +static acu_t * +acutype(s) + register char *s; +{ + register acu_t *p; + extern acu_t acutable[]; + + if (s != NOSTR) + for (p = acutable; p->acu_name != '\0'; p++) + if (equal(s, p->acu_name)) + return (p); + return (NOACU); +} diff --git a/usr/src/cmd/tip/aculib/Makefile b/usr/src/cmd/tip/aculib/Makefile new file mode 100644 index 0000000000..4df6c48095 --- /dev/null +++ b/usr/src/cmd/tip/aculib/Makefile @@ -0,0 +1,41 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright (c) 1983 Regents of the University of California. +# All rights reserved. The Berkeley software License Agreement +# specifies the terms and conditions for redistribution. +# +# ident "%Z%%M% %I% %E% SMI" +# +# make file for tip device drivers +# +# Current drivers: +# BIZCOMP +# DEC DF02-AC, DF03-AC +# DEC DN-11/Able Quadracall +# VENTEL 212+ (w/o echo) +# VADIC 831 RS232 adaptor +# VADIC 3451 +# HAYES SmartModem +# +# cmd/tip/aculib/Makefile + +ACULIB= aculib.a +OBJS= biz22.o biz31.o df.o dn11.o hayes.o ventel.o v831.o v3451.o + +include ../../Makefile.cmd + +CPPFLAGS= -I../ -DUSG $(CPPFLAGS.master) + +.KEEP_STATE: + +.PARALLEL: $(OBJS) + +all: $(ACULIB) + +$(ACULIB): $(OBJS) + $(AR) cr $(ACULIB) $(OBJS) + +clean: + $(RM) $(ACULIB) $(OBJS) core errs diff --git a/usr/src/cmd/tip/aculib/biz22.c b/usr/src/cmd/tip/aculib/biz22.c new file mode 100644 index 0000000000..c570e4a5ac --- /dev/null +++ b/usr/src/cmd/tip/aculib/biz22.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* from UCB 4.4 6/25/83 */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "tip.h" + +#define DISCONNECT_CMD "\20\04" /* disconnection string */ + +static void sigALRM(); +static int timeout = 0; +static sigjmp_buf timeoutbuf; + +/* + * Dial up on a BIZCOMP Model 1022 with either + * tone dialing (mod = "V") + * pulse dialing (mod = "W") + */ +static int +biz_dialer(num, mod) + char *num, *mod; +{ + register int connected = 0; + char cbuf[40]; + + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); + /* + * Disable auto-answer and configure for tone/pulse + * dialing + */ + if (cmd("\02K\r")) { + printf("can't initialize bizcomp..."); + return (0); + } + strcpy(cbuf, "\02.\r"); + cbuf[1] = *mod; + if (cmd(cbuf)) { + printf("can't set dialing mode..."); + return (0); + } + strcpy(cbuf, "\02D"); + strlcat(cbuf, num, sizeof (cbuf)); + strlcat(cbuf, "\r", sizeof (cbuf)); + write(FD, cbuf, strlen(cbuf)); + if (!detect("7\r")) { + printf("can't get dial tone..."); + return (0); + } + if (boolean(value(VERBOSE))) + printf("ringing..."); + /* + * The reply from the BIZCOMP should be: + * 2 \r or 7 \r failure + * 1 \r success + */ + connected = detect("1\r"); +#ifdef ACULOG + if (timeout) { + char line[80]; + + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "biz1022", line); + } +#endif + if (timeout) + biz22_disconnect(); /* insurance */ + return (connected); +} + +biz22w_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "W")); +} + +biz22f_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "V")); +} + +biz22_disconnect() +{ + + write(FD, DISCONNECT_CMD, 4); + sleep(2); + ioctl(FD, TCFLSH, TCOFLUSH); +} + +biz22_abort() +{ + + write(FD, "\02", 1); +} + +static void +sigALRM() +{ + + timeout = 1; + siglongjmp(timeoutbuf, 1); +} + +static int +cmd(s) + register char *s; +{ + char c; + sig_handler_t f; + + write(FD, s, strlen(s)); + f = signal(SIGALRM, (sig_handler_t)sigALRM); + if (sigsetjmp(timeoutbuf, 1)) { + biz22_abort(); + signal(SIGALRM, f); + return (1); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + signal(SIGALRM, f); + c &= 0177; + return (c != '\r'); +} + +static int +detect(s) + register char *s; +{ + char c; + sig_handler_t f; + + f = signal(SIGALRM, (sig_handler_t)sigALRM); + timeout = 0; + while (*s) { + if (sigsetjmp(timeoutbuf, 1)) { + biz22_abort(); + break; + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + c &= 0177; + if (c != *s++) + return (0); + } + signal(SIGALRM, f); + return (timeout == 0); +} diff --git a/usr/src/cmd/tip/aculib/biz31.c b/usr/src/cmd/tip/aculib/biz31.c new file mode 100644 index 0000000000..fe0fbac5dc --- /dev/null +++ b/usr/src/cmd/tip/aculib/biz31.c @@ -0,0 +1,223 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.7 6/25/83 */ + +#include "tip.h" + +#define MAXRETRY 3 /* sync up retry count */ +#define DISCONNECT_CMD "\21\25\11\24" /* disconnection string */ + +static void sigALRM(); +static int timeout = 0; +static sigjmp_buf timeoutbuf; + +/* + * Dial up on a BIZCOMP Model 1031 with either + * tone dialing (mod = "f") + * pulse dialing (mod = "w") + */ +static int +biz_dialer(num, mod) + char *num, *mod; +{ + register int connected = 0; + + if (!bizsync(FD)) { + logent(value(HOST), "", "biz", "out of sync"); + printf("bizcomp out of sync\n"); + delock(uucplock); + exit(0); + } + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); + echo("#\rk$\r$\n"); /* disable auto-answer */ + echo("$>$.$ #\r"); /* tone/pulse dialing */ + echo(mod); + echo("$\r$\n"); + echo("$>$.$ #\re$ "); /* disconnection sequence */ + echo(DISCONNECT_CMD); + echo("\r$\n$\r$\n"); + echo("$>$.$ #\rr$ "); /* repeat dial */ + echo(num); + echo("\r$\n"); + if (boolean(value(VERBOSE))) + printf("ringing..."); + /* + * The reply from the BIZCOMP should be: + * `^G NO CONNECTION\r\n^G\r\n' failure + * ` CONNECTION\r\n^G' success + */ + connected = detect(" "); +#ifdef ACULOG + if (timeout) { + char line[80]; + + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "biz", line); + } +#endif + if (!connected) + flush(" NO CONNECTION\r\n\07\r\n"); + else + flush("CONNECTION\r\n\07"); + if (timeout) + biz31_disconnect(); /* insurance */ + return (connected); +} + +biz31w_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "w")); +} + +biz31f_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "f")); +} + +biz31_disconnect() +{ + + write(FD, DISCONNECT_CMD, 4); + sleep(2); + ioctl(FD, TCFLSH, TCOFLUSH); +} + +biz31_abort() +{ + + write(FD, "\33", 1); +} + +static int +echo(s) + register char *s; +{ + char c; + + while (c = *s++) { + switch (c) { + case '$': + read(FD, &c, 1); + s++; + break; + + case '#': + c = *s++; + write(FD, &c, 1); + break; + + default: + write(FD, &c, 1); + read(FD, &c, 1); + } + } +} + +static void +sigALRM() +{ + + timeout = 1; + siglongjmp(timeoutbuf, 1); +} + +static int +detect(s) + register char *s; +{ + char c; + sig_handler_t f; + + f = signal(SIGALRM, (sig_handler_t)sigALRM); + timeout = 0; + while (*s) { + if (sigsetjmp(timeoutbuf, 1)) { + printf("\07timeout waiting for reply\n"); + biz31_abort(); + break; + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + if (c != *s++) + break; + } + signal(SIGALRM, f); + return (timeout == 0); +} + +static int +flush(s) + register char *s; +{ + char c; + sig_handler_t f; + + f = signal(SIGALRM, (sig_handler_t)sigALRM); + while (*s++) { + if (sigsetjmp(timeoutbuf, 1)) + break; + alarm(10); + read(FD, &c, 1); + alarm(0); + } + signal(SIGALRM, f); + timeout = 0; /* guard against disconnection */ +} + +/* + * This convoluted piece of code attempts to get + * the bizcomp in sync. If you don't have the capacity or nread + * call there are gory ways to simulate this. + */ +static int +bizsync(fd) +{ +#ifdef FIOCAPACITY + struct capacity b; +#define chars(b) ((b).cp_nbytes) +#define IOCTL FIOCAPACITY +#endif +#ifdef FIONREAD + long b; +#define chars(b) (b) +#define IOCTL FIONREAD +#endif + register int already = 0; + char buf[10]; + +retry: + if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0 && chars(b) > 0) + ioctl(fd, TCFLSH, TCIOFLUSH); + write(fd, "\rp>\r", 4); + sleep(1); + if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0) { + if (chars(b) != 10) { + nono: + if (already > MAXRETRY) + return (0); + write(fd, DISCONNECT_CMD, 4); + sleep(2); + already++; + goto retry; + } else { + read(fd, buf, 10); + if (strncmp(buf, "p >\r\n\r\n>", 8)) + goto nono; + } + } + return (1); +} diff --git a/usr/src/cmd/tip/aculib/df.c b/usr/src/cmd/tip/aculib/df.c new file mode 100644 index 0000000000..22f00d9345 --- /dev/null +++ b/usr/src/cmd/tip/aculib/df.c @@ -0,0 +1,111 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.8 6/25/83 */ + +/* + * Dial the DF02-AC or DF03-AC + */ + +#include "tip.h" + +static sigjmp_buf Sjbuf; +static void timeout(); + +df02_dialer(num, acu) + char *num, *acu; +{ + + return (df_dialer(num, acu, 0)); +} + +df03_dialer(num, acu) + char *num, *acu; +{ + + return (df_dialer(num, acu, 1)); +} + +df_dialer(num, acu, df03) + char *num, *acu; + int df03; +{ + register int f = FD; + struct termios buf; + int speed = 0; + char c = '\0'; + + ioctl(f, TCGETS, &buf); + buf.c_cflag |= HUPCL; + ioctl(f, TCSETS, &buf); + if (sigsetjmp(Sjbuf, 1)) { + printf("connection timed out\r\n"); + df_disconnect(); + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + fflush(stdout); +#ifdef TIOCMSET + if (df03) { + int st = TIOCM_ST; /* secondary Transmit flag */ + + ioctl(f, TCGETS, &buf); + if (cfgetospeed(&buf) != B1200) { /* must dial at 1200 baud */ + speed = cfgetospeed(&buf); + cfsetospeed(&buf, B0); + cfsetispeed(&buf, B0); + cfsetospeed(&buf, B1200); + ioctl(f, TCSETSW, &buf); + ioctl(f, TIOCMBIC, &st); /* clear ST for 300 baud */ + } else + ioctl(f, TIOCMBIS, &st); /* set ST for 1200 baud */ + } +#endif + signal(SIGALRM, timeout); + alarm(5 * strlen(num) + 10); + ioctl(f, TCFLSH, TCOFLUSH); + write(f, "\001", 1); + sleep(1); + write(f, "\002", 1); + write(f, num, strlen(num)); + read(f, &c, 1); +#ifdef TIOCMSET + if (df03 && speed) { + cfsetospeed(&buf, B0); + cfsetispeed(&buf, B0); + cfsetospeed(&buf, speed); + ioctl(f, TCSETSW, &buf); + } +#endif + return (c == 'A'); +} + +df_disconnect() +{ + + write(FD, "\001", 1); + sleep(1); + ioctl(FD, TCFLSH, TCOFLUSH); +} + + +df_abort() +{ + + df_disconnect(); +} + + +static void +timeout() +{ + + siglongjmp(Sjbuf, 1); +} diff --git a/usr/src/cmd/tip/aculib/dn11.c b/usr/src/cmd/tip/aculib/dn11.c new file mode 100644 index 0000000000..24bac51d5f --- /dev/null +++ b/usr/src/cmd/tip/aculib/dn11.c @@ -0,0 +1,122 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.14 6/25/83 */ + +/* + * Routines for dialing up on DN-11 + */ +#include "tip.h" + +int dn_abort(); +void alarmtr(); +static sigjmp_buf jmpbuf; +static int child = -1, dn; + +dn_dialer(num, acu) + char *num, *acu; +{ + extern errno; + char *p, *q, phone[40]; + int lt, nw, connected = 1; + register int timelim; + struct termios buf; + + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); + if ((dn = open(acu, 1)) < 0) { + if (errno == EBUSY) + printf("line busy..."); + else + printf("acu open error..."); + return (0); + } + if (sigsetjmp(jmpbuf, 1)) { + kill(child, SIGKILL); + close(dn); + return (0); + } + signal(SIGALRM, alarmtr); + timelim = 5 * strlen(num); + alarm(timelim < 30 ? 30 : timelim); + if ((child = fork()) == 0) { + /* + * ignore this stuff for aborts + */ + signal(SIGALRM, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + sleep(2); + nw = write(dn, num, lt = strlen(num)); + exit(nw != lt); + } + /* + * open line - will return on carrier + */ + if ((FD = open(DV, 2)) < 0) { + if (errno == EIO) + printf("lost carrier..."); + else + printf("dialup line open failed..."); + alarm(0); + kill(child, SIGKILL); + close(dn); + return (0); + } + alarm(0); + ioctl(dn, TCGETS, &buf); + buf.c_cflag |= HUPCL; + ioctl(dn, TCSETSF, &buf); + signal(SIGALRM, SIG_DFL); + while ((nw = wait(<)) != child && nw != -1) + ; + fflush(stdout); + close(dn); + if (lt != 0) { + close(FD); + return (0); + } + return (1); +} + +void +alarmtr() +{ + + alarm(0); + siglongjmp(jmpbuf, 1); +} + +/* + * Insurance, for some reason we don't seem to be + * hanging up... + */ +dn_disconnect() +{ + int dtr = TIOCM_DTR; + + sleep(2); + if (FD > 0) + ioctl(FD, TIOCMBIC, &dtr); + close(FD); +} + +dn_abort() +{ + int dtr = TIOCM_DTR; + + sleep(2); + if (child > 0) + kill(child, SIGKILL); + if (dn > 0) + close(dn); + if (FD > 0) + ioctl(FD, TIOCMBIC, &dtr); + close(FD); +} diff --git a/usr/src/cmd/tip/aculib/hayes.c b/usr/src/cmd/tip/aculib/hayes.c new file mode 100644 index 0000000000..85a26a8dc1 --- /dev/null +++ b/usr/src/cmd/tip/aculib/hayes.c @@ -0,0 +1,144 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" + +#include "tip.h" + +static void sigALRM(); +static sigjmp_buf timeoutbuf; + +/* + * Dial up on a Hayes Smart Modem 1200 or 2400 + */ +int +hayes_dialer(num, acu) + char *num, *acu; +{ + char code = 0, cr = 0; + void (*f)(); + struct termios buf; + + f = signal(SIGALRM, sigALRM); + + if (!hayes_sync(FD)) { + printf("can't synchronize with hayes\n"); +#ifdef ACULOG + logent(value(HOST), num, "hayes", "can't synch up"); +#endif + signal(SIGALRM, f); + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + fflush(stdout); + ioctl(FD, TCGETS, &buf); + buf.c_cflag |= HUPCL; + ioctl(FD, TCSETS, &buf); + ioctl(FD, TCFLSH, TCIOFLUSH); + + if (sigsetjmp(timeoutbuf, 1)) { +#ifdef ACULOG + char line[80]; + + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "hayes", line); +#endif + hayes_disconnect(); + signal(SIGALRM, f); + return (0); + } + alarm(number(value(DIALTIMEOUT))); + ioctl(FD, TCFLSH, TCIOFLUSH); + if (*num == 'S') + write(FD, "AT", 2); + else + write(FD, "ATDT", 4); /* use tone dialing */ + write(FD, num, strlen(num)); + write(FD, "\r", 1); + read(FD, &code, 1); + read(FD, &cr, 1); + if (code == '1' && cr == '0') + read(FD, &cr, 1); + alarm(0); + signal(SIGALRM, f); + if ((code == '1' || code == '5') && cr == '\r') + return (1); + return (0); +} + +hayes_disconnect() +{ + close(FD); +} + +hayes_abort() +{ + int dtr = TIOCM_DTR; + + alarm(0); + ioctl(FD, TIOCMBIC, &dtr); + sleep(2); + ioctl(FD, TCFLSH, TCIOFLUSH); + close(FD); +} + +static void +sigALRM() +{ + siglongjmp(timeoutbuf, 1); +} + +/* + * This piece of code attempts to get the hayes in sync. + */ +static int +hayes_sync(fd) +{ + register int tries; + char code = 0, cr = 0; + int dtr = TIOCM_DTR; + + /* + * Toggle DTR to force anyone off that might have left + * the modem connected, and insure a consistent state + * to start from. + */ + ioctl(fd, TIOCMBIC, &dtr); + sleep(1); + ioctl(fd, TIOCMBIS, &dtr); + for (tries = 0; tries < 3; tries++) { + /* + * After reseting the modem, initialize all + * parameters to required vaules: + * + * V0 - result codes are single digits + * Q0 - result codes ARE sent + * E0 - do not echo + * S0=1 - automatically answer phone + * S2=255 - disable escape character + * S12=255 - longest possible escape guard time + */ + write(fd, "ATV0Q0E0S0=1S2=255S12=255\r", 26); + sleep(1); + /* flush any echoes or return codes */ + ioctl(fd, TCFLSH, TCIOFLUSH); + /* now see if the modem is talking to us properly */ + write(fd, "AT\r", 3); + if (sigsetjmp(timeoutbuf, 1) == 0) { + alarm(2); + read(FD, &code, 1); + read(FD, &cr, 1); + if (code == '0' && cr == '\r') + return (1); + } + } + return (0); +} diff --git a/usr/src/cmd/tip/aculib/v3451.c b/usr/src/cmd/tip/aculib/v3451.c new file mode 100644 index 0000000000..221e67c85a --- /dev/null +++ b/usr/src/cmd/tip/aculib/v3451.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* from UCB 4.4 6/25/83 */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Routines for calling up on a Vadic 3451 Modem + */ +#include "tip.h" + +static sigjmp_buf Sjbuf; + +v3451_dialer(num, acu) + register char *num; + char *acu; +{ + int ok; + void (*func)(); + struct termios buf; + int slow = number(value(BAUDRATE)) < 1200; + char phone[50]; +#ifdef ACULOG + char line[80]; +#endif + + /* + * Get in synch + */ + vawrite("I\r", 1 + slow); + vawrite("I\r", 1 + slow); + vawrite("I\r", 1 + slow); + vawrite("\005\r", 2 + slow); + if (!expect("READY")) { + printf("can't synchronize with vadic 3451\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "can't synch up"); +#endif + return (0); + } + ioctl(FD, TCGETS, &buf); + buf.c_cflag |= HUPCL; + ioctl(FD, TCSETSF, &buf); + sleep(1); + vawrite("D\r", 2 + slow); + if (!expect("NUMBER?")) { + printf("Vadic will not accept dial command\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "will not accept dial"); +#endif + return (0); + } + strlcpy(phone, num, sizeof (phone)); + strlcat(phone, "\r", sizeof (phone)); + vawrite(phone, 1 + slow); + if (!expect(phone)) { + printf("Vadic will not accept phone number\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "will not accept number"); +#endif + return (0); + } + func = signal(SIGINT, SIG_IGN); + /* + * You cannot interrupt the Vadic when its dialing; + * even dropping DTR does not work (definitely a + * brain damaged design). + */ + vawrite("\r", 1 + slow); + vawrite("\r", 1 + slow); + if (!expect("DIALING:")) { + printf("Vadic failed to dial\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "failed to dial"); +#endif + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + ok = expect("ON LINE"); + signal(SIGINT, func); + if (!ok) { + printf("call failed\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "call failed"); +#endif + return (0); + } + ioctl(FD, TCFLSH, TCOFLUSH); + return (1); +} + +v3451_disconnect() +{ + + close(FD); +} + +v3451_abort() +{ + + close(FD); +} + +static +vawrite(cp, delay) + register char *cp; + int delay; +{ + + for (; *cp; sleep(delay), cp++) + write(FD, cp, 1); +} + +static +expect(cp) + register char *cp; +{ + char buf[300]; + register char *rp = buf; + static void alarmtr(); + int timeout = 30, online = 0; + + if (strcmp(cp, "\"\"") == 0) + return (1); + *rp = 0; + /* + * If we are waiting for the Vadic to complete + * dialing and get a connection, allow more time + * Unfortunately, the Vadic times out 24 seconds after + * the last digit is dialed + */ + online = strcmp(cp, "ON LINE") == 0; + if (online) + timeout = number(value(DIALTIMEOUT)); + signal(SIGALRM, alarmtr); + if (sigsetjmp(Sjbuf, 1)) + return (0); + alarm(timeout); + while (notin(cp, buf) && rp < buf + sizeof (buf) - 1) { + if (online && notin("FAILED CALL", buf) == 0) + return (0); + if (read(FD, rp, 1) < 0) { + alarm(0); + return (0); + } + if (*rp &= 0177) + rp++; + *rp = '\0'; + } + alarm(0); + return (1); +} + +static void +alarmtr() +{ + + siglongjmp(Sjbuf, 1); +} + +static +notin(sh, lg) + char *sh, *lg; +{ + + for (; *lg; lg++) + if (prefix(sh, lg)) + return (0); + return (1); +} + +static +prefix(s1, s2) + register char *s1, *s2; +{ + register char c; + + while ((c = *s1++) == *s2++) + if (c == '\0') + return (1); + return (c == '\0'); +} diff --git a/usr/src/cmd/tip/aculib/v831.c b/usr/src/cmd/tip/aculib/v831.c new file mode 100644 index 0000000000..d117bb4bf1 --- /dev/null +++ b/usr/src/cmd/tip/aculib/v831.c @@ -0,0 +1,244 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.5 6/25/83 */ + +/* + * Routines for dialing up on Vadic 831 + */ +#include <sys/time.h> + +#include "tip.h" + +int v831_abort(); +static void alarmtr(); +extern errno; + +static sigjmp_buf jmpbuf; +static int child = -1; + +v831_dialer(num, acu) + char *num, *acu; +{ + int status, pid, connected = 1; + register int timelim; + + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); +#ifdef DEBUG + printf("(acu=%s)\n", acu); +#endif + if ((AC = open(acu, O_RDWR)) < 0) { + if (errno == EBUSY) + printf("line busy..."); + else + printf("acu open error..."); + return (0); + } + if (sigsetjmp(jmpbuf, 1)) { + kill(child, SIGKILL); + close(AC); + return (0); + } + signal(SIGALRM, alarmtr); + timelim = 5 * strlen(num); + alarm(timelim < 30 ? 30 : timelim); + if ((child = fork()) == 0) { + /* + * ignore this stuff for aborts + */ + signal(SIGALRM, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + sleep(2); + exit(dialit(num, acu) != 'A'); + } + /* + * open line - will return on carrier + */ + if ((FD = open(DV, O_RDWR)) < 0) { +#ifdef DEBUG + printf("(after open, errno=%d)\n", errno); +#endif + if (errno == EIO) + printf("lost carrier..."); + else + printf("dialup line open failed..."); + alarm(0); + kill(child, SIGKILL); + close(AC); + return (0); + } + alarm(0); + signal(SIGALRM, SIG_DFL); + while ((pid = wait(&status)) != child && pid != -1) + ; + if (status) { + close(AC); + return (0); + } + return (1); +} + +static void +alarmtr() +{ + + alarm(0); + siglongjmp(jmpbuf, 1); +} + +/* + * Insurance, for some reason we don't seem to be + * hanging up... + */ +v831_disconnect() +{ + struct termios cntrl; + int dtr = TIOCM_DTR; + + sleep(2); +#ifdef DEBUG + printf("[disconnect: FD=%d]\n", FD); +#endif + if (FD > 0) { + ioctl(FD, TIOCMBIC, &dtr); + ioctl(FD, TCGETS, &cntrl); + cfsetospeed(&cntrl, B0); + cntrl.c_cflag &= ~XCLUDE; + ioctl(FD, TCSETSF, &cntrl); + } + close(FD); +} + +v831_abort() +{ + int dtr = TIOCM_DTR; + struct termios buf; + +#ifdef DEBUG + printf("[abort: AC=%d]\n", AC); +#endif + sleep(2); + if (child > 0) + kill(child, SIGKILL); + if (AC > 0) { + ioctl(FD, TCGETS, &buf); + buf.c_cflag &= ~XCLUDE; + ioctl(FD, TCSETSF, &buf); + close(AC); + } + if (FD > 0) + ioctl(FD, TIOCMBIC, &dtr); + close(FD); +} + +/* + * Sigh, this probably must be changed at each site. + */ +struct vaconfig { + char *vc_name; + char vc_rack; + char vc_modem; +} vaconfig[] = { + { "/dev/cua0", '4', '0' }, + { "/dev/cua1", '4', '1' }, + { 0 } +}; + +#define pc(x) (c = x, write(AC, &c, 1)) +#define ABORT 01 +#define SI 017 +#define STX 02 +#define ETX 03 + +static +dialit(phonenum, acu) + register char *phonenum; + char *acu; +{ + register struct vaconfig *vp; + struct termios cntrl; + char c, *sanitize(); + int i; + + phonenum = sanitize(phonenum); +#ifdef DEBUG + printf("(dial phonenum=%s)\n", phonenum); +#endif + if (*phonenum == '<' && phonenum[1] == 0) + return ('Z'); + for (vp = vaconfig; vp->vc_name; vp++) + if (strcmp(vp->vc_name, acu) == 0) + break; + if (vp->vc_name == 0) { + printf("Unable to locate dialer (%s)\n", acu); + return ('K'); + } + ioctl(AC, TCGETS, &cntrl); + cfsetospeed(&cntrl, B0); + cfsetispeed(&cntrl, B0); + cntrl.c_cflag &= ~(CSIZE|PARENB|PARODD); + cfsetospeed(&cntrl, B2400); + cntrl.c_cflag |= CS8; + cntrl.c_iflag &= IXOFF|IXANY; + cntrl.c_lflag &= ~(ICANON|ISIG); + cntrl.c_oflag = 0; + cntrl.c_cc[VMIN] = cntrl.c_cc[VTIME] = 0; + ioctl(AC, TCSETSF, &cntrl); + ioctl(AC, TCFLSH, TCOFLUSH); + pc(STX); + pc(vp->vc_rack); + pc(vp->vc_modem); + while (*phonenum && *phonenum != '<') + pc(*phonenum++); + pc(SI); + pc(ETX); + sleep(1); + i = read(AC, &c, 1); +#ifdef DEBUG + printf("read %d chars, char=%c, errno %d\n", i, c, errno); +#endif + if (i != 1) + c = 'M'; + if (c == 'B' || c == 'G') { + char cc, oc = c; + + pc(ABORT); + read(AC, &cc, 1); +#ifdef DEBUG + printf("abort response=%c\n", cc); +#endif + c = oc; + v831_disconnect(); + } + close(AC); +#ifdef DEBUG + printf("dialit: returns %c\n", c); +#endif + return (c); +} + +static char * +sanitize(s) + register char *s; +{ + static char buf[128]; + register char *cp; + + for (cp = buf; *s; s++) { + if (!isdigit(*s) && *s == '<' && *s != '_') + continue; + if (*s == '_') + *s = '='; + *cp++ = *s; + } + *cp++ = 0; + return (buf); +} diff --git a/usr/src/cmd/tip/aculib/ventel.c b/usr/src/cmd/tip/aculib/ventel.c new file mode 100644 index 0000000000..635a879c5c --- /dev/null +++ b/usr/src/cmd/tip/aculib/ventel.c @@ -0,0 +1,214 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 1.5 6/25/83 */ + +/* + * Routines for calling up on a Ventel Modem + * Define VENNOECHO if the Ventel is strapped for "no echo". + */ +#include "tip.h" + +#define MAXRETRY 5 + +static void sigALRM(); +static int timeout = 0; +static sigjmp_buf timeoutbuf; + +ven_dialer(num, acu) + register char *num; + char *acu; +{ + register char *cp; + register int connected = 0; + struct termios buf; +#ifdef ACULOG + char line[80]; +#endif + /* + * Get in synch with a couple of carriage returns + */ + if (!vensync(FD)) { + printf("can't synchronize with ventel\n"); +#ifdef ACULOG + logent(value(HOST), num, "ventel", "can't synch up"); +#endif + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + fflush(stdout); + ioctl(FD, TCGETS, &buf); + buf.c_cflag |= HUPCL; + ioctl(FD, TCSETSF, &buf); +#ifdef VENNOECHO + echo("#k$\r$\n$D$I$A$L$:$ "); + for (cp = num; *cp; cp++) { + sleep(1); + write(FD, cp, 1); + } + echo("\r$\n"); +#else + echo("k$\r$\n$D$I$A$L$:$ <"); + for (cp = num; *cp; cp++) { + char c; + + sleep(1); + write(FD, cp, 1); + read(FD, &c, 1); + } + echo(">\r$\n"); +#endif + if (gobble('\n')) + connected = gobble('!'); + ioctl(FD, TCFLSH, TCIOFLUSH); +#ifdef ACULOG + if (timeout) { + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "ventel", line); + } +#endif + if (timeout) + ven_disconnect(); /* insurance */ + return (connected); +} + +ven_disconnect() +{ + + close(FD); +} + +ven_abort() +{ + + write(FD, "\03", 1); + close(FD); +} + +static int +echo(s) + register char *s; +{ + char c; + + while (c = *s++) { + switch (c) { + case '$': + read(FD, &c, 1); + s++; + break; + + case '#': + c = *s++; + write(FD, &c, 1); + break; + + default: + write(FD, &c, 1); + read(FD, &c, 1); + } + } +} + +static void +sigALRM() +{ + + printf("\07timeout waiting for reply\n"); + timeout = 1; + siglongjmp(timeoutbuf, 1); +} + +static int +gobble(match) + register char match; +{ + char c; + sig_handler_t f; + + f = signal(SIGALRM, (sig_handler_t)sigALRM); + timeout = 0; + do { + if (sigsetjmp(timeoutbuf, 1)) { + signal(SIGALRM, f); + return (0); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + c &= 0177; +#ifdef notdef + if (boolean(value(VERBOSE))) + putchar(c); +#endif + } while (c != '\n' && c != match); + signal(SIGALRM, SIG_DFL); + return (c == match); +} + +#define min(a, b) (((a) > (b)) ? (b) : (a)) +/* + * This convoluted piece of code attempts to get + * the ventel in sync. If you don't have FIONREAD + * there are gory ways to simulate this. + */ +static int +vensync(fd) +{ + int already = 0, nread; + char buf[60]; + int dtr = TIOCM_DTR; + + /* + * Toggle DTR to force anyone off that might have left + * the modem connected, and insure a consistent state + * to start from. + * + * If you don't have the ioctl calls to diddle directly + * with DTR, you can always try setting the baud rate to 0. + */ + ioctl(FD, TIOCMBIC, &dtr); + sleep(2); + ioctl(FD, TIOCMBIS, &dtr); + while (already < MAXRETRY) { + /* + * After reseting the modem, send it two \r's to + * autobaud on. Make sure to delay between them + * so the modem can frame the incoming characters. + */ + write(fd, "\r", 1); +#ifdef VMUNIX + { +#include <sys/time.h> + struct timeval tv = {0, 500000}; + + select(0, 0, 0, 0, &tv); + } +#else + sleep(1); +#endif + write(fd, "\r", 1); + sleep(3); + if (ioctl(fd, FIONREAD, (caddr_t)&nread) < 0) { + perror("tip: ioctl"); + continue; + } + while (nread > 0) { + read(fd, buf, min(nread, 60)); + if ((buf[nread - 1] & 0177) == '$') + return (1); + nread -= min(nread, 60); + } + sleep(1); + already++; + } + return (0); +} diff --git a/usr/src/cmd/tip/acutab.c b/usr/src/cmd/tip/acutab.c new file mode 100644 index 0000000000..b7711869b2 --- /dev/null +++ b/usr/src/cmd/tip/acutab.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.7 6/25/83 */ + +#include "tip.h" + +extern int df02_dialer(), df03_dialer(), df_disconnect(), df_abort(), + biz31f_dialer(), biz31_disconnect(), biz31_abort(), + biz31w_dialer(), + biz22f_dialer(), biz22_disconnect(), biz22_abort(), + biz22w_dialer(), + hayes_dialer(), hayes_disconnect(), hayes_abort(), + ven_dialer(), ven_disconnect(), ven_abort(), + v3451_dialer(), v3451_disconnect(), v3451_abort(), + v831_dialer(), v831_disconnect(), v831_abort(), + dn_dialer(), dn_disconnect(), dn_abort(); + +acu_t acutable[] = { +#if BIZ1031 + "biz31f", biz31f_dialer, biz31_disconnect, biz31_abort, + "biz31w", biz31w_dialer, biz31_disconnect, biz31_abort, +#endif +#if BIZ1022 + "biz22f", biz22f_dialer, biz22_disconnect, biz22_abort, + "biz22w", biz22w_dialer, biz22_disconnect, biz22_abort, +#endif +#if DF02 + "df02", df02_dialer, df_disconnect, df_abort, +#endif +#if DF03 + "df03", df03_dialer, df_disconnect, df_abort, +#endif +#if DN11 + "dn11", dn_dialer, dn_disconnect, dn_abort, +#endif +#ifdef VENTEL + "ventel", ven_dialer, ven_disconnect, ven_abort, +#endif +#ifdef V3451 +#ifndef V831 + "vadic", v3451_dialer, v3451_disconnect, v3451_abort, +#endif + "v3451", v3451_dialer, v3451_disconnect, v3451_abort, +#endif +#ifdef V831 +#ifndef V3451 + "vadic", v831_dialer, v831_disconnect, v831_abort, +#endif + "v831", v831_dialer, v831_disconnect, v831_abort, +#endif +#ifdef HAYES + "hayes", hayes_dialer, hayes_disconnect, hayes_abort, + "at", hayes_dialer, hayes_disconnect, hayes_abort, +#endif + 0, 0, 0, 0 +}; diff --git a/usr/src/cmd/tip/cmds.c b/usr/src/cmd/tip/cmds.c new file mode 100644 index 0000000000..79fc0d58a3 --- /dev/null +++ b/usr/src/cmd/tip/cmds.c @@ -0,0 +1,927 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.4 5/5/86 */ + +#include "tip.h" +#ifdef USG +#include <unistd.h> +#else +#include <vfork.h> +#endif + +/* + * tip + * + * miscellaneous commands + */ + +int quant[] = { 60, 60, 24 }; + +char null = '\0'; +char *sep[] = { "second", "minute", "hour" }; +static char *argv[10]; /* argument vector for take and put */ + +sigjmp_buf intbuf; /* for interrupts and timeouts */ + +void timeout(); /* timeout function called on alarm */ +void intcopy(); /* interrupt routine for file transfers */ + +/* + * FTP - remote ==> local + * get a file from the remote host + */ +getfl(c) + char c; +{ + char buf[256], *cp, *expand(); + + putchar(c); + /* + * get the UNIX receiving file's name + */ + if (prompt("Local file name? ", copyname, sizeof (copyname))) + return; + cp = expand(copyname); + if (cp == NOSTR) + return; + if ((sfd = creat(cp, 0666)) < 0) { + printf("\r\n%s: cannot creat\r\n", copyname); + return; + } + + /* + * collect parameters + */ + if (prompt("List command for remote system? ", buf, sizeof (buf))) { + unlink(copyname); + return; + } + transfer(buf, sfd, value(EOFREAD)); +} + +/* + * Cu-like take command + */ +cu_take(cc) + char cc; +{ + int fd, argc; + char line[BUFSIZ], *expand(), *cp; + + if (prompt("[take] ", copyname, sizeof (copyname))) + return; + argc = args(copyname, argv, sizeof (argv)/sizeof (char *)); + if (argc < 1 || argc > 2) { + printf("usage: <take> from [to]\r\n"); + return; + } + if (argc == 1) + argv[1] = argv[0]; + cp = expand(argv[1]); + if (cp == NOSTR) + return; + if ((fd = creat(cp, 0666)) < 0) { + printf("\r\n%s: cannot create\r\n", argv[1]); + return; + } + sprintf(line, "cat %s; echo \01", argv[0]); + transfer(line, fd, "\01"); +} + +/* + * Bulk transfer routine -- + * used by getfl(), cu_take(), and pipefile() + */ +transfer(buf, fd, eofchars) + char *buf, *eofchars; +{ + register int ct; + char c, buffer[BUFSIZ]; + char *p = buffer; /* can't be register because of longjmp */ + register int cnt, eof, bol; + time_t start; + void (*f)(); + + parwrite(FD, buf, strlen(buf)); + kill(pid, SIGIOT); + read(repdes[0], (char *)&ccc, 1); /* Wait until read process stops */ + + /* + * finish command + */ + parwrite(FD, "\r", 1); + do + read(FD, &c, 1); + while ((c&0177) != '\n'); + + if (sigsetjmp(intbuf, 1)) + goto out; + f = signal(SIGINT, intcopy); + intr("on"); + + start = time(0); + bol = 1; + ct = 0; + for (;;) { + eof = read(FD, &c, 1) <= 0; + if (noparity) + c &= 0377; + else + c &= 0177; + if (eof || (bol && any(c, eofchars))) + break; + if (c == 0) + continue; /* ignore nulls */ + if (c == '\r') + continue; + *p++ = c; + + if (c == '\n') { + bol = 1; + if (boolean(value(VERBOSE))) + printf("\r%d", ++ct); + } else + bol = 0; + if ((cnt = (p-buffer)) == number(value(FRAMESIZE))) { + if (write(fd, buffer, cnt) != cnt) { + printf("\r\nwrite error\r\n"); + goto out; + } + p = buffer; + } + } +out: + if (cnt = (p-buffer)) + if (write(fd, buffer, cnt) != cnt) + printf("\r\nwrite error\r\n"); + + if (boolean(value(VERBOSE))) + prtime(" lines transferred in ", time(0)-start); + intr("off"); + write(fildes[1], (char *)&ccc, 1); + signal(SIGINT, f); + close(fd); +} + +/* + * FTP - remote ==> local process + * send remote input to local process via pipe + */ +pipefile() +{ + int cpid, pdes[2]; + char buf[256]; + int status, p; + extern int errno; + + if (prompt("Local command? ", buf, sizeof (buf))) + return; + + if (pipe(pdes)) { + printf("can't establish pipe\r\n"); + return; + } + + if ((cpid = fork()) < 0) { + printf("can't fork!\r\n"); + return; + } else if (cpid) { + if (prompt("List command for remote system? ", buf, + sizeof (buf))) { + close(pdes[0]), close(pdes[1]); + kill(cpid, SIGKILL); + } else { + close(pdes[0]); + signal(SIGPIPE, intcopy); + transfer(buf, pdes[1], value(EOFREAD)); + signal(SIGPIPE, SIG_DFL); + while ((p = wait(&status)) > 0 && p != cpid) + ; + } + } else { + register int f; + + userperm(); + dup2(pdes[0], 0); + close(pdes[0]); + for (f = 3; f < 20; f++) + close(f); + execute(buf); + printf("can't execl!\r\n"); + exit(0); + } +} + +/* + * FTP - local ==> remote + * send local file to remote host + * terminate transmission with pseudo EOF sequence + */ +sendfile(cc) + char cc; +{ + FILE *fd; + char *fnamex; + char *expand(); + + putchar(cc); + /* + * get file name + */ + if (prompt("Local file name? ", fname, sizeof (fname))) + return; + + /* + * look up file + */ + fnamex = expand(fname); + if (fnamex == NOSTR) + return; + if ((fd = fopen(fnamex, "r")) == NULL) { + printf("%s: cannot open\r\n", fname); + return; + } + transmit(fd, value(EOFWRITE), NULL); + if (!boolean(value(ECHOCHECK))) { + struct termios buf; + + ioctl(FD, TCGETS, (char *)&buf); /* this does a */ + ioctl(FD, TCSETSF, (char *)&buf); /* wflushtty */ + } +} + +/* + * Bulk transfer routine to remote host -- + * used by sendfile() and cu_put() + */ +transmit(fd, eofchars, command) + FILE *fd; + char *eofchars, *command; +{ + void (*ointr)(); + char *pc, lastc, rc; + int c, ccount, lcount; + time_t start_t, stop_t; + + kill(pid, SIGIOT); /* put TIPOUT into a wait state */ + timedout = 0; + if (sigsetjmp(intbuf, 1)) { + if (timedout) + printf("\r\ntimed out at eol\r\n"); + alarm(0); + goto out; + } + ointr = signal(SIGINT, intcopy); + intr("on"); + read(repdes[0], (char *)&ccc, 1); + if (command != NULL) { + for (pc = command; *pc; pc++) + send(*pc); + if (boolean(value(ECHOCHECK))) + read(FD, (char *)&c, 1); /* trailing \n */ + else { + struct termios buf; + + sleep(5); /* wait for remote stty to take effect */ + ioctl(FD, TCGETS, (char *)&buf); /* this does a */ + ioctl(FD, TCSETSF, (char *)&buf); /* wflushtty */ + } + } + lcount = 0; + lastc = '\0'; + start_t = time(0); + if (boolean(value(RAWFTP))) { + while ((c = getc(fd)) != EOF) { + lcount++; + send(c); + if (boolean(value(VERBOSE)) && lcount%100 == 0) + printf("\r%d", lcount); + } + if (boolean(value(VERBOSE))) + printf("\r%d", lcount); + goto out; + } + for (;;) { + ccount = 0; + do { + c = getc(fd); + if (c == EOF) + goto out; + if (c == 0177) + continue; + lastc = c; + if (c < 040) { + if (c == '\n') { + c = '\r'; + } else if (c == '\t') { + if (boolean(value(TABEXPAND))) { + send(' '); + while ((++ccount % 8) != 0) + send(' '); + continue; + } + } else + continue; + } + send(c); + } while (c != '\r'); + if (boolean(value(VERBOSE))) + printf("\r%d", ++lcount); + if (boolean(value(ECHOCHECK))) { + alarm(number(value(ETIMEOUT))); + do { /* wait for prompt */ + read(FD, &rc, 1); + } while ((rc&0177) != character(value(PROMPT))); + alarm(0); + } + } +out: + if (lastc != '\n' && !boolean(value(RAWFTP))) + send('\r'); + if (eofchars) + for (pc = eofchars; *pc; pc++) + send(*pc); + stop_t = time(0); + fclose(fd); + if (boolean(value(VERBOSE))) + if (boolean(value(RAWFTP))) + prtime(" chars transferred in ", stop_t-start_t); + else + prtime(" lines transferred in ", stop_t-start_t); + write(fildes[1], (char *)&ccc, 1); + intr("off"); + signal(SIGINT, ointr); +} + +/* + * Cu-like put command + */ +cu_put(cc) + char cc; +{ + FILE *fd; + char line[BUFSIZ]; + int argc; + char *expand(); + char *copynamex; + + if (prompt("[put] ", copyname, sizeof (copyname))) + return; + argc = args(copyname, argv, sizeof (argv)/sizeof (char *)); + if (argc < 1 || argc > 2) { + printf("usage: <put> from [to]\r\n"); + return; + } + if (argc == 1) + argv[1] = argv[0]; + copynamex = expand(argv[0]); + if (copynamex == NOSTR) + return; + if ((fd = fopen(copynamex, "r")) == NULL) { + printf("%s: cannot open\r\n", copynamex); + return; + } + if (boolean(value(ECHOCHECK))) + sprintf(line, "cat>%s\r", argv[1]); + else + sprintf(line, "stty -echo; cat>%s; stty echo\r", argv[1]); + transmit(fd, "\04", line); +} + +/* + * FTP - send single character + * wait for echo & handle timeout + */ +send(c) + char c; +{ + char cc; + int retry = 0; + + cc = c; + parwrite(FD, &cc, 1); +#ifdef notdef + if (number(value(CDELAY)) > 0 && c != '\r') + nap(number(value(CDELAY))); +#endif + if (!boolean(value(ECHOCHECK))) { +#ifdef notdef + if (number(value(LDELAY)) > 0 && c == '\r') + nap(number(value(LDELAY))); +#endif + return; + } +tryagain: + timedout = 0; + if (sigsetjmp(intbuf, 1) && timedout) { + printf("\r\ntimeout error (%s)\r\n", ctrl(c)); + if (retry++ > 3) + return; + parwrite(FD, &null, 1); /* poke it */ + goto tryagain; + } + alarm(number(value(ETIMEOUT))); + read(FD, &cc, 1); + alarm(0); +} + +void +timeout() +{ + signal(SIGALRM, (sig_handler_t)timeout); + timedout = 1; + siglongjmp(intbuf, 1); +} + +/* + * Stolen from consh() -- puts a remote file on the output of a local command. + * Identical to consh() except for where stdout goes. + */ +pipeout(c) +{ + char buf[256]; + int cpid, status, p; + time_t start; + + putchar(c); + if (prompt("Local command? ", buf, sizeof (buf))) + return; + kill(pid, SIGIOT); /* put TIPOUT into a wait state */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + intr("on"); + read(repdes[0], (char *)&ccc, 1); + /* + * Set up file descriptors in the child and + * let it go... + */ + if ((cpid = fork()) < 0) + printf("can't fork!\r\n"); + else if (cpid) { + start = time(0); + while ((p = wait(&status)) > 0 && p != cpid) + ; + } else { + register int i; + + userperm(); + dup2(FD, 1); + for (i = 3; i < 20; i++) + close(i); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + execute(buf); + printf("can't find `%s'\r\n", buf); + exit(0); + } + if (boolean(value(VERBOSE))) + prtime("away for ", time(0)-start); + write(fildes[1], (char *)&ccc, 1); + intr("off"); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); +} + +/* + * Fork a program with: + * 0 <-> remote tty in + * 1 <-> remote tty out + * 2 <-> local tty stderr out + */ +consh(c) +{ + char buf[256]; + int cpid, status, p; + void (*ointr)(), (*oquit)(); + time_t start; + + putchar(c); + if (prompt("Local command? ", buf, sizeof (buf))) + return; + kill(pid, SIGIOT); /* put TIPOUT into a wait state */ + read(repdes[0], (char *)&ccc, 1); + ointr = signal(SIGINT, SIG_IGN); + oquit = signal(SIGQUIT, SIG_IGN); + unraw(); + /* + * Set up file descriptors in the child and + * let it go... + */ + if ((cpid = fork()) < 0) + printf("can't fork!\r\n"); + else if (cpid) { + start = time(0); + while ((p = wait(&status)) > 0 && p != cpid) + ; + raw(); + signal(SIGINT, ointr); + signal(SIGQUIT, oquit); + } else { + register int i; + + userperm(); + dup2(FD, 0); + dup2(0, 1); + for (i = 3; i < 20; i++) + close(i); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + execute(buf); + printf("can't find `%s'\r\n", buf); + exit(0); + } + if (boolean(value(VERBOSE))) + prtime("\r\naway for ", time(0)-start); + write(fildes[1], (char *)&ccc, 1); +} + +/* + * Escape to local shell + */ +shell() +{ + int shpid, status; + void (*ointr)(), (*oquit)(); + char *cp; + + printf("[sh]\r\n"); + ointr = signal(SIGINT, SIG_IGN); + oquit = signal(SIGQUIT, SIG_IGN); + unraw(); + if (shpid = fork()) { + while (shpid != wait(&status)) + ; + raw(); + printf("\r\n!\r\n"); + signal(SIGINT, ointr); + signal(SIGQUIT, oquit); + } else { + userperm(); + signal(SIGQUIT, SIG_DFL); + signal(SIGINT, SIG_DFL); + if ((cp = strrchr(value(SHELL), '/')) == NULL) + cp = value(SHELL); + else + cp++; + execl(value(SHELL), cp, 0); + printf("\r\ncan't execl!\r\n"); + exit(1); + } +} + +/* + * TIPIN portion of scripting + * initiate the conversation with TIPOUT + */ +setscript() +{ + char c; + /* + * enable TIPOUT side for dialogue + */ + kill(pid, SIGEMT); + if (boolean(value(SCRIPT))) + write(fildes[1], value(RECORD), strlen(value(RECORD))); + write(fildes[1], "\n", 1); + /* + * wait for TIPOUT to finish + */ + read(repdes[0], &c, 1); + if (c == 'n') + fprintf(stderr, "tip: can't create record file %s\r\n", + value(RECORD)); +} + +/* + * Change current working directory of + * local portion of tip + */ +chdirectory() +{ + char dirname[80]; + register char *cp = dirname; + + if (prompt("[cd] ", dirname, sizeof (dirname))) { + if (stoprompt) + return; + cp = value(HOME); + } + if (chdir(cp) < 0) + printf("%s: bad directory\r\n", cp); + printf("!\r\n"); +} + +/* XXX - really should rename this routine to avoid conflict with libc */ +abort(msg) + char *msg; +{ + + signal(SIGCHLD, SIG_DFL); /* don't want to hear about our child */ + kill(pid, SIGTERM); + myperm(); + disconnect(msg); + if (msg != NOSTR) + printf("\r\n%s", msg); + printf("\r\n[EOT]\r\n"); + delock(uucplock); + unraw(); + exit(0); +} + +finish() +{ + char *dismsg; + + if ((dismsg = value(DISCONNECT)) != NOSTR) { + write(FD, dismsg, strlen(dismsg)); + sleep(5); + } + abort(NOSTR); +} + +void +intcopy() +{ + + signal(SIGINT, SIG_IGN); + siglongjmp(intbuf, 1); +} + +execute(s) + char *s; +{ + register char *cp; + + if ((cp = strrchr(value(SHELL), '/')) == NULL) + cp = value(SHELL); + else + cp++; + execl(value(SHELL), cp, "-c", s, 0); +} + +args(buf, a, na) + char *buf, *a[]; + size_t na; +{ + register char *p = buf, *start; + register char **parg = a; + register int n = 0; + + do { + while (*p && (*p == ' ' || *p == '\t')) + p++; + start = p; + if (*p) + *parg = p; + while (*p && (*p != ' ' && *p != '\t')) + p++; + if (p != start) + parg++, n++; + if (*p) + *p++ = '\0'; + } while (*p && n < na); + + return (n); +} + +prtime(s, a) + char *s; + time_t a; +{ + register i; + int nums[3]; + + for (i = 0; i < 3; i++) { + nums[i] = (int)(a % quant[i]); + a /= quant[i]; + } + printf("%s", s); + while (--i >= 0) + if (nums[i] || i == 0 && nums[1] == 0 && nums[2] == 0) + printf("%d %s%c ", nums[i], sep[i], + nums[i] == 1 ? '\0' : 's'); + printf("\r\n!\r\n"); +} + +variable() +{ + char buf[256]; + + if (prompt("[set] ", buf, sizeof (buf))) + return; + vlex(buf); + if (vtable[BEAUTIFY].v_access&CHANGED) { + vtable[BEAUTIFY].v_access &= ~CHANGED; + kill(pid, SIGSYS); + } + if (vtable[SCRIPT].v_access&CHANGED) { + vtable[SCRIPT].v_access &= ~CHANGED; + setscript(); + /* + * So that "set record=blah script" doesn't + * cause two transactions to occur. + */ + if (vtable[RECORD].v_access&CHANGED) + vtable[RECORD].v_access &= ~CHANGED; + } + if (vtable[RECORD].v_access&CHANGED) { + vtable[RECORD].v_access &= ~CHANGED; + if (boolean(value(SCRIPT))) + setscript(); + } + if (vtable[TAND].v_access&CHANGED) { + vtable[TAND].v_access &= ~CHANGED; + if (boolean(value(TAND))) + tandem("on"); + else + tandem("off"); + } + if (vtable[LECHO].v_access&CHANGED) { + vtable[LECHO].v_access &= ~CHANGED; + boolean(value(HALFDUPLEX)) = boolean(value(LECHO)); + } + if (vtable[PARITY].v_access&CHANGED) { + vtable[PARITY].v_access &= ~CHANGED; + setparity(NULL); + } + if (vtable[BAUDRATE].v_access&CHANGED) { + vtable[BAUDRATE].v_access &= ~CHANGED; + ttysetup(speed(number(value(BAUDRATE)))); + } + if (vtable[HARDWAREFLOW].v_access & CHANGED) { + vtable[HARDWAREFLOW].v_access &= ~CHANGED; + if (boolean(value(HARDWAREFLOW))) + hardwareflow("on"); + else + hardwareflow("off"); + } +} + +/* + * Turn tandem mode on or off for remote tty. + */ +tandem(option) + char *option; +{ + struct termios rmtty; + + ioctl(FD, TCGETS, (char *)&rmtty); + if (equal(option, "on")) { + rmtty.c_iflag |= IXOFF|IXON; + arg.c_iflag |= IXOFF|IXON; + rmtty.c_cc[VSTART] = defarg.c_cc[VSTART]; + rmtty.c_cc[VSTOP] = defarg.c_cc[VSTOP]; + } else { + rmtty.c_iflag &= ~(IXOFF|IXON); + arg.c_iflag &= ~(IXOFF|IXON); + } + ioctl(FD, TCSETSF, (char *)&rmtty); + ioctl(0, TCSETSF, (char *)&arg); +} + +/* + * Turn hardwareflow mode on or off for remote tty. + */ +hardwareflow(option) + char *option; +{ + struct termios rmtty; + + ioctl(FD, TCGETS, (char *)&rmtty); + if (equal(option, "on")) { + rmtty.c_cflag |= (CRTSCTS|CRTSXOFF); + } else { + rmtty.c_cflag &= ~(CRTSCTS|CRTSXOFF); + } + ioctl(FD, TCSETSF, (char *)&rmtty); +} + +/* + * Turn interrupts from local tty on or off. + */ +intr(option) + char *option; +{ + + if (equal(option, "on")) + arg.c_lflag |= ISIG; + else + arg.c_lflag &= ~ISIG; + ioctl(0, TCSETSF, (char *)&arg); +} + +/* + * Send a break. + */ +genbrk() +{ + + ioctl(FD, TCSBRK, 0); +} + +/* + * Suspend tip + */ +suspend(c) + char c; +{ + + unraw(); + kill(c == _CTRL('y') ? getpid() : 0, SIGTSTP); + raw(); +} + +/* + * expand a file name if it includes shell meta characters + */ + +char * +expand(name) + char name[]; +{ + static char xname[BUFSIZ]; + char cmdbuf[BUFSIZ]; + register int pid, l, rc; + register char *cp, *Shell; + int s, pivec[2]; + void (*sigint)(); + + if (!anyof(name, "~{[*?$`'\"\\")) + return (name); + /* sigint = signal(SIGINT, SIG_IGN); */ + if (pipe(pivec) < 0) { + perror("pipe"); + /* signal(SIGINT, sigint) */ + return (name); + } + sprintf(cmdbuf, "echo %s", name); + if ((pid = vfork()) == 0) { + userperm(); + Shell = value(SHELL); + if (Shell == NOSTR) + Shell = "/bin/sh"; + close(pivec[0]); + close(1); + dup(pivec[1]); + close(pivec[1]); + close(2); + execl(Shell, Shell, "-c", cmdbuf, 0); + _exit(1); + } + if (pid == -1) { + perror("fork"); + close(pivec[0]); + close(pivec[1]); + return (NOSTR); + } + close(pivec[1]); + l = read(pivec[0], xname, BUFSIZ); + close(pivec[0]); + while (wait(&s) != pid); + ; + s &= 0377; + if (s != 0 && s != SIGPIPE) { + fprintf(stderr, "\"Echo\" failed\n"); + return (NOSTR); + } + if (l < 0) { + perror("read"); + return (NOSTR); + } + if (l == 0) { + fprintf(stderr, "\"%s\": No match\n", name); + return (NOSTR); + } + if (l == BUFSIZ) { + fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name); + return (NOSTR); + } + xname[l] = 0; + for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) + ; + *++cp = '\0'; + return (xname); +} + +/* + * Are any of the characters in the two strings the same? + */ + +anyof(s1, s2) + register char *s1, *s2; +{ + register int c; + + while (c = *s1++) + if (any(c, s2)) + return (1); + return (0); +} diff --git a/usr/src/cmd/tip/cmdtab.c b/usr/src/cmd/tip/cmdtab.c new file mode 100644 index 0000000000..52857f5685 --- /dev/null +++ b/usr/src/cmd/tip/cmdtab.c @@ -0,0 +1,37 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.3 5/5/86 */ + +#include "tip.h" + +extern int shell(), getfl(), sendfile(), chdirectory(); +extern int finish(), help(), pipefile(), pipeout(), consh(), variable(); +extern int cu_take(), cu_put(), dollar(), genbrk(), suspend(); + +esctable_t etable[] = { + { '!', NORM, "shell", shell }, + { '<', NORM, "receive file from remote host", getfl }, + { '>', NORM, "send file to remote host", sendfile }, + { 't', NORM, "take file from remote UNIX", cu_take }, + { 'p', NORM, "put file to remote UNIX", cu_put }, + { '|', NORM, "pipe remote file", pipefile }, + { 'C', NORM, "connect program to remote host", consh }, + { 'c', NORM, "change directory", chdirectory }, + { '.', NORM, "exit from tip", finish }, + {_CTRL('d'), NORM, "exit from tip", finish }, + { '$', NORM, "pipe local command to remote host", pipeout }, + {_CTRL('y'), NORM, "suspend tip (local only)", suspend }, + {_CTRL('z'), NORM, "suspend tip (local+remote)", suspend }, + { 's', NORM, "set variable", variable }, + { '?', NORM, "get this summary", help }, + { '#', NORM, "send break", genbrk }, + { 0, 0, 0 } +}; diff --git a/usr/src/cmd/tip/cu.c b/usr/src/cmd/tip/cu.c new file mode 100644 index 0000000000..e67101f8bc --- /dev/null +++ b/usr/src/cmd/tip/cu.c @@ -0,0 +1,119 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.2 1/13/86 */ + +#include "tip.h" + +void cleanup(); +void timeout(); + +/* + * Botch the interface to look like cu's + */ +cumain(argc, argv) + char *argv[]; +{ + register int i; + static char sbuf[12]; + + if (argc < 2) { +usage: + fprintf(stderr, + "usage: cu telno [-t] [-s speed] [-a acu] [-l line] [-#]\n"); + exit(8); + } + CU = DV = NOSTR; + for (; argc > 1; argv++, argc--) { + if (argv[1][0] != '-') + PN = argv[1]; + else if (argv[1][1] != '\0' && argv[1][2] != '\0') { + fprintf(stderr, "cu: extra characters after flag: %s\n", + argv[1]); + goto usage; + } else switch (argv[1][1]) { + + case 't': + HW = 1, DU = -1; + --argc; + continue; + + case 'a': + CU = argv[2]; ++argv; --argc; + break; + + case 's': + if (argc < 3) + goto usage; + if (speed(atoi(argv[2])) == 0) { + fprintf(stderr, "cu: unsupported speed %s\n", + argv[2]); + exit(3); + } + BR = atoi(argv[2]); ++argv; --argc; + break; + + case 'l': + DV = argv[2]; ++argv; --argc; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (CU) + CU[strlen(CU)-1] = argv[1][1]; + if (DV) + DV[strlen(DV)-1] = argv[1][1]; + break; + + default: + fprintf(stderr, "cu: bad flag %s\n", argv[1]); + goto usage; + } + } + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGHUP, cleanup); + signal(SIGTERM, cleanup); + + /* + * The "cu" host name is used to define the + * attributes of the generic dialer. + */ + sprintf(sbuf, "cu%d", BR); + if ((i = hunt(sbuf)) == 0) { + printf("all ports busy\n"); + exit(3); + } + if (i == -1) { + printf("link down\n"); + delock(uucplock); + exit(3); + } + setbuf(stdout, NULL); + loginit(); + gid = getgid(); + egid = getegid(); + uid = getuid(); + euid = geteuid(); + userperm(); + vinit(); + setparity("none"); + boolean(value(VERBOSE)) = 0; + if (HW) + ttysetup(speed(BR)); + if (connect()) { + printf("Connect failed\n"); + myperm(); + delock(uucplock); + exit(1); + } + if (!HW) + ttysetup(speed(BR)); +} diff --git a/usr/src/cmd/tip/etc.remote b/usr/src/cmd/tip/etc.remote new file mode 100644 index 0000000000..4522fc447b --- /dev/null +++ b/usr/src/cmd/tip/etc.remote @@ -0,0 +1,42 @@ +cuab:dv=/dev/cua/b:br#2400 +dialup1|Dial-up system:\ + :pn=2015551212:tc=UNIX-2400: +hardwire:\ + :dv=/dev/term/b:br#9600:el=^C^S^Q^U^D:ie=%$:oe=^D: +tip300:tc=UNIX-300: +tip1200:tc=UNIX-1200: +tip0|tip2400:tc=UNIX-2400: +tip9600:tc=UNIX-9600: +tip19200:tc=UNIX-19200: +UNIX-300:\ + :el=^D^U^C^S^Q^O@:du:at=hayes:ie=#$%:oe=^D:br#300:tc=dialers: +UNIX-1200:\ + :el=^D^U^C^S^Q^O@:du:at=hayes:ie=#$%:oe=^D:br#1200:tc=dialers: +UNIX-2400:\ + :el=^D^U^C^S^Q^O@:du:at=hayes:ie=#$%:oe=^D:br#2400:tc=dialers: +UNIX-9600:\ + :el=^D^U^C^S^Q^O@:du:at=hayes:ie=#$%:oe=^D:br#9600:tc=dialers: +UNIX-19200:\ + :el=^D^U^C^S^Q^O@:du:at=hayes:ie=#$%:oe=^D:br#19200:tc=dialers: +VMS-300|TOPS20-300:\ + :el=^Z^U^C^S^Q^O:du:at=hayes:ie=$@:oe=^Z:br#300:tc=dialers: +VMS-1200|TOPS20-1200:\ + :el=^Z^U^C^S^Q^O:du:at=hayes:ie=$@:oe=^Z:br#1200:tc=dialers: +dialers:\ + :dv=/dev/cua/b: +-------------------------------------------------------------------- +The attributes are: + +dv device to use for the tty +el EOL marks (default is NULL) +du make a call flag (dial up) +pn phone numbers (@ =>'s search phones file; possibly taken from + PHONES environment variable) +at ACU type +ie input EOF marks (default is NULL) +oe output EOF string (default is NULL) +cu call unit (default is dv) +br baud rate (defaults to 300) +fs frame size (default is BUFSIZ) -- used in buffering writes + on receive operations +tc to continue a capability diff --git a/usr/src/cmd/tip/hunt.c b/usr/src/cmd/tip/hunt.c new file mode 100644 index 0000000000..77795255c4 --- /dev/null +++ b/usr/src/cmd/tip/hunt.c @@ -0,0 +1,82 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.7 6/25/83 */ + +#include "tip.h" + +extern char *getremote(); +extern int errno; + +static sigjmp_buf deadline; +static int deadfl; + +void +dead() +{ + + deadfl = 1; + siglongjmp(deadline, 1); +} + +hunt(name) + char *name; +{ + register char *cp; + void (*f)(); + + f = signal(SIGALRM, (sig_handler_t)dead); + while (cp = getremote(name)) { + deadfl = 0; + uucplock = cp; + if (mlock(uucplock) < 0) { + delock(uucplock); + continue; + } + /* + * Straight through call units, such as the BIZCOMP, + * VADIC and the DF, must indicate they're hardwired in + * order to get an open file descriptor placed in FD. + * Otherwise, as for a DN-11, the open will have to + * be done in the "open" routine. + */ + if (!HW) + break; + if (sigsetjmp(deadline, 1) == 0) { + alarm(10); + if (!trusted_device) + userperm(); + errno = 0; + if ((FD = open(cp, O_RDWR)) < 0 && errno != EBUSY) { + fprintf(stderr, "tip: "); + perror(cp); + } + if (!trusted_device) + myperm(); + if (FD >= 0 && !isatty(FD)) { + fprintf(stderr, "tip: %s: not a tty\n", cp); + close(FD); + FD = -1; + } + } + alarm(0); + if (!deadfl && FD >= 0) { + struct termios t; + + ioctl(FD, TCGETS, &t); + t.c_cflag |= XCLUDE|HUPCL; + ioctl(FD, TCSETSF, &t); + signal(SIGALRM, f); + return ((int)cp); + } + delock(uucplock); + } + signal(SIGALRM, f); + return (deadfl ? -1 : (int)cp); +} diff --git a/usr/src/cmd/tip/log.c b/usr/src/cmd/tip/log.c new file mode 100644 index 0000000000..6350746d24 --- /dev/null +++ b/usr/src/cmd/tip/log.c @@ -0,0 +1,65 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.6 6/25/83 */ + +#include "tip.h" + +static FILE *flog = NULL; + +/* + * Log file maintenance routines + */ + +logent(group, num, acu, message) + char *group, *num, *acu, *message; +{ + char *user, *timestamp; + struct passwd *pwd; + time_t t; + + if (flog == NULL) + return; +#ifndef USG + if (flock(fileno(flog), LOCK_EX) < 0) { + perror("tip: flock"); + return; + } +#endif + if ((user = getlogin()) == NOSTR) + if ((pwd = getpwuid(uid)) == NOPWD) + user = "???"; + else + user = pwd->pw_name; + t = time(0); + timestamp = ctime(&t); + timestamp[24] = '\0'; + fprintf(flog, "%s (%s) <%s, %s, %s> %s\n", + user, timestamp, group, +#ifdef PRISTINE + "", +#else + num, +#endif + acu, message); + fflush(flog); +#ifndef USG + (void) flock(fileno(flog), LOCK_UN); +#endif +} + +loginit() +{ + +#ifdef ACULOG + flog = fopen(value(LOG), "a"); + if (flog == NULL) + fprintf(stderr, "tip: can't open log file %s\r\n", value(LOG)); +#endif +} diff --git a/usr/src/cmd/tip/partab.c b/usr/src/cmd/tip/partab.c new file mode 100644 index 0000000000..818269641d --- /dev/null +++ b/usr/src/cmd/tip/partab.c @@ -0,0 +1,55 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.2 6/25/83 */ + +/* + * Even parity table for 0-0177 + */ +unsigned char evenpartab[] = { + 0000, 0201, 0202, 0003, 0204, 0005, 0006, 0207, + 0210, 0011, 0012, 0213, 0014, 0215, 0216, 0017, + 0220, 0021, 0022, 0223, 0024, 0225, 0226, 0027, + 0030, 0231, 0232, 0033, 0234, 0035, 0036, 0237, + 0240, 0041, 0042, 0243, 0044, 0245, 0246, 0047, + 0050, 0251, 0252, 0053, 0254, 0055, 0056, 0257, + 0060, 0261, 0262, 0063, 0264, 0065, 0066, 0267, + 0270, 0071, 0072, 0273, 0074, 0275, 0276, 0077, + 0300, 0101, 0102, 0303, 0104, 0305, 0306, 0107, + 0110, 0311, 0312, 0113, 0314, 0115, 0116, 0317, + 0120, 0321, 0322, 0123, 0324, 0125, 0126, 0327, + 0330, 0131, 0132, 0333, 0134, 0335, 0336, 0137, + 0140, 0341, 0342, 0143, 0344, 0145, 0146, 0347, + 0350, 0151, 0152, 0353, 0154, 0355, 0356, 0157, + 0360, 0161, 0162, 0363, 0164, 0365, 0366, 0167, + 0170, 0371, 0372, 0173, 0374, 0175, 0176, 0377, + + /* + * The following is meaningless for parity + * but it lets us share the same table for the + * algortithms in tip.c (pwrite()). + */ + + 0000, 0201, 0202, 0003, 0204, 0005, 0006, 0207, + 0210, 0011, 0012, 0213, 0014, 0215, 0216, 0017, + 0220, 0021, 0022, 0223, 0024, 0225, 0226, 0027, + 0030, 0231, 0232, 0033, 0234, 0035, 0036, 0237, + 0240, 0041, 0042, 0243, 0044, 0245, 0246, 0047, + 0050, 0251, 0252, 0053, 0254, 0055, 0056, 0257, + 0060, 0261, 0262, 0063, 0264, 0065, 0066, 0267, + 0270, 0071, 0072, 0273, 0074, 0275, 0276, 0077, + 0300, 0101, 0102, 0303, 0104, 0305, 0306, 0107, + 0110, 0311, 0312, 0113, 0314, 0115, 0116, 0317, + 0120, 0321, 0322, 0123, 0324, 0125, 0126, 0327, + 0330, 0131, 0132, 0333, 0134, 0335, 0336, 0137, + 0140, 0341, 0342, 0143, 0344, 0145, 0146, 0347, + 0350, 0151, 0152, 0353, 0154, 0355, 0356, 0157, + 0360, 0161, 0162, 0363, 0164, 0365, 0366, 0167, + 0170, 0371, 0372, 0173, 0374, 0175, 0176, 0377 +}; diff --git a/usr/src/cmd/tip/remcap.c b/usr/src/cmd/tip/remcap.c new file mode 100644 index 0000000000..ee515cf077 --- /dev/null +++ b/usr/src/cmd/tip/remcap.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2000,2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* from UCB 4.8 6/25/83 */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * remcap - routines for dealing with the remote host data base + * + * derived from termcap + */ +#ifdef USG +#include <sys/types.h> +#include <fcntl.h> /* for O_RDONLY */ +#else +#include <sys/file.h> /* for O_RDONLY */ +#include <ctype.h> +#endif + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif +#define MAXHOP 32 /* max number of tc= indirections */ +#define SYSREMOTE "/etc/remote" /* system remote file */ + +#define tgetent rgetent +#define tnchktc rnchktc +#define tnamatch rnamatch +#define tgetnum rgetnum +#define tgetflag rgetflag +#define tgetstr rgetstr +#define E_TERMCAP RM = SYSREMOTE +#define V_TERMCAP "REMOTE" +#define V_TERM "HOST" + +char *RM; + +/* + * termcap - routines for dealing with the terminal capability data base + * + * BUG: Should use a "last" pointer in tbuf, so that searching + * for capabilities alphabetically would not be a n**2/2 + * process when large numbers of capabilities are given. + * Note: If we add a last pointer now we will screw up the + * tc capability. We really should compile termcap. + * + * Essentially all the work here is scanning and decoding escapes + * in string capabilities. We don't use stdio because the editor + * doesn't, and because living w/o it is not hard. + */ + +static char *tbuf; +static int hopcount; /* detect infinite loops in termcap, init 0 */ +char *tskip(); +char *tgetstr(); +char *tdecode(); +char *getenv(); +static char *remotefile; + +/* + * If we use a user specified entry to get the device name, + * we need to open the device as the user. + */ +int trusted_device = 0; + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +tgetent(bp, name, len) + char *bp, *name; + int len; +{ + char lbuf[BUFSIZ], *cp, *p; + int rc1, rc2; + + trusted_device = 1; + + remotefile = cp = getenv(V_TERMCAP); + if (cp == (char *)0 || strcmp(cp, SYSREMOTE) == 0) { + remotefile = cp = SYSREMOTE; + return (getent(bp, name, cp, len)); + } else { + if ((rc1 = getent(bp, name, cp, len)) != 1) + *bp = '\0'; + remotefile = cp = SYSREMOTE; + rc2 = getent(lbuf, name, cp, sizeof (lbuf)); + if (rc1 != 1 && rc2 != 1) + return (rc2); + if (rc2 == 1) { + p = lbuf; + if (rc1 == 1) + while (*p++ != ':') + ; + if (strlen(bp) + strlen(p) >= len) { + write(2, "Remcap entry too long\n", 23); + return (-1); + } + strcat(bp, p); + } + tbuf = bp; + return (1); + } +} + +getent(bp, name, cp, len) + char *bp, *name, *cp; + int len; +{ + register int c; + register int i = 0, cnt = 0; + char ibuf[BUFSIZ], *cp2; + int tf; + int safe = 1; /* reset only when we open the user's $REMOTE */ + + tbuf = bp; + tf = 0; + /* + * TERMCAP can have one of two things in it. It can be the + * name of a file to use instead of /etc/termcap. In this + * case it better start with a "/". Or it can be an entry to + * use so we don't have to read the file. In this case it + * has to already have the newlines crunched out. + */ + if (cp && *cp) { + if (*cp != '/') { + cp2 = getenv(V_TERM); + if (cp2 == (char *)0 || strcmp(name, cp2) == 0) { + if (strstr(cp, "dv=") != 0) + trusted_device = 0; + strncpy(bp, cp, len-1); + bp[len-1] = '\0'; + return (tnchktc()); + } else + tf = open(E_TERMCAP, O_RDONLY); + } else { + /* open SYSREMOTE as uucp, other files as user */ + safe = strcmp(cp, SYSREMOTE) == 0; + if (!safe) + userperm(); + tf = open(RM = cp, O_RDONLY); + if (!safe) + myperm(); + } + } + if (tf == 0) + tf = open(E_TERMCAP, O_RDONLY); + if (tf < 0) + return (-1); + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, BUFSIZ); + if (cnt <= 0) { + close(tf); + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\') { + cp--; + continue; + } + break; + } + if (cp >= bp+len) { + write(2, "Remcap entry too long\n", 23); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (tnamatch(name)) { + /* + * if a dv= entry is obtained from $REMOTE, + * switch off trusted_device status + */ + if (!safe && strstr(bp, "dv=") != 0) + trusted_device = 0; + close(tf); + return (tnchktc()); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +tnchktc() +{ + register char *p, *q; + char tcname[64]; /* name of similar terminal */ + char tcbuf[BUFSIZ]; + char *holdtbuf = tbuf; + int l; + char *cp; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p < tbuf) { + write(2, "Bad remcap entry\n", 18); + return (0); + } + p++; + /* p now points to beginning of last field */ + if (p[0] != 't' || p[1] != 'c') + return (1); + strlcpy(tcname, p+3, sizeof (tcname)); + q = tcname; + while (*q && *q != ':') + q++; + *q = 0; + if (++hopcount > MAXHOP) { + write(2, "Infinite tc= loop\n", 18); + return (0); + } + if (getent(tcbuf, tcname, remotefile, sizeof (tcbuf)) != 1) { + if (strcmp(remotefile, SYSREMOTE) == 0) + return (0); + else if (getent(tcbuf, tcname, SYSREMOTE, sizeof (tcbuf)) != 1) + return (0); + } + for (q = tcbuf; *q++ != ':'; ) + ; + l = p - holdtbuf + strlen(q); + if (l > BUFSIZ) { + write(2, "Remcap entry too long\n", 23); + q[BUFSIZ - (p-holdtbuf)] = 0; + } + strcpy(p, q); + tbuf = holdtbuf; + return (1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +tnamatch(np) + char *np; +{ + register char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return (0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +tskip(bp) + register char *bp; +{ + + while (*bp && *bp != ':') + bp++; + if (*bp == ':') { + do { + bp++; + while (isspace(*bp)) + bp++; + } while (*bp == ':'); + } + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +tgetnum(id) + char *id; +{ + register int i, base; + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (*bp == 0) + return (-1); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return (-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit(*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +tgetflag(id) + char *id; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { + if (!*bp || *bp == ':') + return (1); + else if (*bp == '@') + return (0); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +tgetstr(id, area) + char *id, **area; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return (0); + if (*bp != '=') + continue; + bp++; + return (tdecode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +tdecode(str, area) + register char *str; + char **area; +{ + register char *cp; + register int c; + register char *dp; + int i; + + cp = *area; + while ((c = *str++) && c != ':') { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit(c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit(*str)); + } + break; + } + *cp++ = c; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/usr/src/cmd/tip/remote.c b/usr/src/cmd/tip/remote.c new file mode 100644 index 0000000000..57848eec8f --- /dev/null +++ b/usr/src/cmd/tip/remote.c @@ -0,0 +1,181 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.3 4/30/86 */ + +#include "tip.h" + +/* + * Attributes to be gleened from remote host description + * data base. + */ +static char **caps[] = { + &AT, &DV, &CM, &CU, &EL, &IE, &OE, &PN, &PR, &DI, + &ES, &EX, &FO, &RC, &RE, &PA +}; + +static char *capstrings[] = { + "at", "dv", "cm", "cu", "el", "ie", "oe", "pn", "pr", + "di", "es", "ex", "fo", "rc", "re", "pa", 0 +}; + +char *rgetstr(); + +static +getremcap(host) + register char *host; +{ + int stat; + char tbuf[BUFSIZ]; + static char buf[BUFSIZ/2]; + char *bp = buf; + register char **p, ***q; + + if ((stat = rgetent(tbuf, host, sizeof (tbuf))) <= 0) { + if (DV || + host[0] == '/' && access(DV = host, R_OK | W_OK) == 0) { + /* + * If the user specifies a device on the commandline, + * don't trust it. + */ + if (host[0] == '/') + trusted_device = 0; + CU = DV; + HO = host; + HW = 1; + DU = 0; + if (!BR) + BR = DEFBR; + FS = DEFFS; + RE = (char *)"tip.record"; + EX = (char *)"\t\n\b\f"; + DL = 0; + CL = 0; + ET = 10; + return; + } + fprintf(stderr, stat == 0 ? + "tip: unknown host %s\n" : + "tip: can't open host description file\n", host); + exit(3); + } + + for (p = capstrings, q = caps; *p != NULL; p++, q++) + if (**q == NULL) + **q = rgetstr(*p, &bp); + if (!BR && (BR = rgetnum("br")) < 0) + BR = DEFBR; + if ((FS = rgetnum("fs")) < 0) + FS = DEFFS; + if (DU < 0) + DU = 0; + else + DU = rgetflag("du"); + if (DV == NOSTR) { + fprintf(stderr, "%s: missing device spec\n", host); + exit(3); + } + if (DU && CU == NOSTR) + CU = DV; + if (DU && PN == NOSTR) { + fprintf(stderr, "%s: missing phone number\n", host); + exit(3); + } + DB = rgetflag("db"); + + /* + * This effectively eliminates the "hw" attribute + * from the description file + */ + if (!HW) + HW = (CU == NOSTR) || (DU && equal(DV, CU)); + HO = host; + /* + * see if uppercase mode should be turned on initially + */ + if (rgetflag("ra")) + boolean(value(RAISE)) = 1; + if (rgetflag("ec")) + boolean(value(ECHOCHECK)) = 1; + if (rgetflag("be")) + boolean(value(BEAUTIFY)) = 1; + if (rgetflag("nb")) + boolean(value(BEAUTIFY)) = 0; + if (rgetflag("sc")) + boolean(value(SCRIPT)) = 1; + if (rgetflag("tb")) + boolean(value(TABEXPAND)) = 1; + if (rgetflag("vb")) + boolean(value(VERBOSE)) = 1; + if (rgetflag("nv")) + boolean(value(VERBOSE)) = 0; + if (rgetflag("ta")) + boolean(value(TAND)) = 1; + if (rgetflag("nt")) + boolean(value(TAND)) = 0; + if (rgetflag("rw")) + boolean(value(RAWFTP)) = 1; + if (rgetflag("hd")) + boolean(value(HALFDUPLEX)) = 1; + if (rgetflag("hf")) + boolean(value(HARDWAREFLOW)) = 1; + if (RE == NULL) + RE = (char *)"tip.record"; + if (EX == NULL) + EX = (char *)"\t\n\b\f"; + if (ES != NOSTR) + vstring("es", ES); + if (FO != NOSTR) + vstring("fo", FO); + if (PR != NOSTR) + vstring("pr", PR); + if (RC != NOSTR) + vstring("rc", RC); + if ((DL = rgetnum("dl")) < 0) + DL = 0; + if ((CL = rgetnum("cl")) < 0) + CL = 0; + if ((ET = rgetnum("et")) < 0) + ET = 10; +} + +char * +getremote(host) + char *host; +{ + register char *cp; + static char *next; + static int lookedup = 0; + + if (!lookedup) { + if (host == NOSTR && (host = getenv("HOST")) == NOSTR) { + fprintf(stderr, "tip: no host specified\n"); + exit(3); + } + getremcap(host); + next = DV; + lookedup++; + } + /* + * We return a new device each time we're called (to allow + * a rotary action to be simulated) + */ + if (next == NOSTR) + return (NOSTR); + if ((cp = strchr(next, ',')) == NULL) { + DV = next; + next = NOSTR; + } else { + *cp++ = '\0'; + DV = next; + next = cp; + } + return (DV); +} diff --git a/usr/src/cmd/tip/tip.c b/usr/src/cmd/tip/tip.c new file mode 100644 index 0000000000..87eee957a8 --- /dev/null +++ b/usr/src/cmd/tip/tip.c @@ -0,0 +1,614 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.4 4/3/86 */ + +/* + * tip - UNIX link to other systems + * tip [-v] [-speed] system-name + * or + * cu phone-number [-s speed] [-l line] [-a acu] + */ +#include "tip.h" +#include <sys/wait.h> +#include <locale.h> + +/* + * Baud rate mapping table + */ +int bauds[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, + 1200, 1800, 2400, 4800, 9600, 19200, 38400, + 57600, 76800, 115200, 153600, 230400, 307200, 460800, -1 +}; + +void intprompt(); +void timeout(); +void deadkid(); +void cleanup(); +char *sname(); +char PNbuf[256]; /* This limits the size of a number */ +int noparity = 0; + + +main(argc, argv) + char *argv[]; +{ + char *system = NOSTR; + register int i; + register char *p; + char sbuf[12]; + + gid = getgid(); + egid = getegid(); + uid = getuid(); + euid = geteuid(); + if (equal(sname(argv[0]), "cu")) { + cumode = 1; + cumain(argc, argv); + goto cucommon; + } + + if (argc > 4) { + fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n"); + exit(1); + } + if (!isatty(0)) { + fprintf(stderr, "tip: must be interactive\n"); + exit(1); + } + + for (; argc > 1; argv++, argc--) { + if (argv[1][0] != '-') + system = argv[1]; + else switch (argv[1][1]) { + + case 'v': + vflag++; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + BR = atoi(&argv[1][1]); + break; + + default: + fprintf(stderr, "tip: %s, unknown option\n", argv[1]); + break; + } + } + + (void) setlocale(LC_CTYPE, ""); + + if (system == NOSTR) + goto notnumber; + for (p = system; *p; p++) + if (isalpha(*p)) + goto notnumber; + /* + * System name is really a phone number... + * Copy the number then stomp on the original (in case the number + * is private, we don't want 'ps' or 'w' to find it). + */ + if (strlen(system) > sizeof (PNbuf) - 1) { + fprintf(stderr, "tip: phone number too long (max = %d bytes)\n", + sizeof (PNbuf) - 1); + exit(1); + } + strncpy(PNbuf, system, sizeof (PNbuf) - 1); + for (p = system; *p; p++) + *p = '\0'; + PN = PNbuf; + sprintf(sbuf, "tip%d", BR); + system = sbuf; + +notnumber: + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGHUP, cleanup); + signal(SIGTERM, cleanup); + + if ((i = hunt(system)) == 0) { + printf("all ports busy\n"); + exit(3); + } + if (i == -1) { + printf("link down\n"); + delock(uucplock); + exit(3); + } + setbuf(stdout, NULL); + loginit(); + + /* + * Now that we have the logfile and the ACU open + * return to the real uid and gid. These things will + * be closed on exit. The saved-setuid uid and gid + * allows us to get the original setuid permissions back + * for removing the uucp lock. + */ + userperm(); + + /* + * Kludge, there's no easy way to get the initialization + * in the right order, so force it here. + * Do the open here, before we change back to real uid. + * We will check whether the open succeeded later, when + * (and if) we actually go to use the file. + */ + if ((PH = getenv("PHONES")) == NOSTR) { + myperm(); + PH = "/etc/phones"; + } + phfd = fopen(PH, "r"); + + userperm(); + + vinit(); /* init variables */ + setparity("none"); /* set the parity table */ + if ((i = speed(number(value(BAUDRATE)))) == NULL) { + printf("tip: bad baud rate %d\n", number(value(BAUDRATE))); + myperm(); + delock(uucplock); + exit(3); + } + + + /* + * Hardwired connections require the + * line speed set before they make any transmissions + * (this is particularly true of things like a DF03-AC) + */ + if (HW) + ttysetup(i); + if (p = connect()) { + printf("\07%s\n[EOT]\n", p); + myperm(); + delock(uucplock); + exit(1); + } + + /* + * Always setup the tty again here in case hardware flow + * control was selected, which can only be set after the + * connection is made, or in case this is not a hardwired + * modem (rare these days) that likewise can only be setup + * after the connection is made. + */ + ttysetup(i); +cucommon: + /* + * From here down the code is shared with + * the "cu" version of tip. + */ + + ioctl(0, TCGETS, (char *)&defarg); + arg = defarg; + /* turn off input processing */ + arg.c_lflag &= ~(ICANON|ISIG|ECHO|IEXTEN); + arg.c_cc[VMIN] = 1; + arg.c_cc[VTIME] = 0; + arg.c_iflag &= ~(INPCK|IXON|IXOFF|ICRNL); + arg.c_oflag = 0; /* turn off all output processing */ + /* handle tandem mode in case was set in remote file */ + if (boolean(value(TAND))) + tandem("on"); + else + tandem("off"); + raw(); + + pipe(fildes); pipe(repdes); + signal(SIGALRM, timeout); + + /* + * Everything's set up now: + * connection established (hardwired or dialup) + * line conditioned (baud rate, mode, etc.) + * internal data structures (variables) + * so, fork one process for local side and one for remote. + */ + if (CM != NOSTR) { + sleep(2); /* let line settle */ + parwrite(FD, CM, strlen(CM)); + } + printf(cumode ? "Connected\r\n" : "\07connected\r\n"); + signal(SIGCHLD, deadkid); + if (pid = fork()) + tipin(); + else + tipout(); + /*NOTREACHED*/ +} + +void +deadkid() +{ + + if (pid >= 0 && waitpid(pid, NULL, WNOHANG) == pid) + abort("Connection Closed"); +} + +void +cleanup() +{ + + if (uid != getuid()) { + myperm(); + } + delock(uucplock); + exit(0); +} + +/* + * put the controlling keyboard into raw mode + */ +raw() +{ + + ioctl(0, TCSETSF, (char *)&arg); +} + + +/* + * return keyboard to normal mode + */ +unraw() +{ + + ioctl(0, TCSETSF, (char *)&defarg); +} + +/* + * switch to using invoking user's permissions + */ +userperm() +{ + + setegid(gid); + seteuid(uid); +} + +/* + * switch to using my special (setuid) permissions + */ +myperm() +{ + + setegid(egid); + seteuid(euid); +} + +static sigjmp_buf promptbuf; + +/* + * Print string ``s'', then read a string + * in from the terminal. Handles signals & allows use of + * normal erase and kill characters. + */ +prompt(s, p, len) + char *s; + register char *p; + size_t len; +{ + register char *b = p; + register int c; + void (*ointr)(), (*oquit)(); + + stoprompt = 0; + ointr = signal(SIGINT, intprompt); + oquit = signal(SIGQUIT, SIG_IGN); + unraw(); + printf("%s", s); + if (sigsetjmp(promptbuf, 1) == 0) + while (p < b + len - 1 && + ((c = getchar()) != EOF) && (c != '\n')) + *p++ = c; + *p = '\0'; + + raw(); + signal(SIGINT, ointr); + signal(SIGQUIT, oquit); + return (stoprompt || p == b); +} + +/* + * Interrupt service routine during prompting + */ +void +intprompt() +{ + + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + stoprompt = 1; + printf("\r\n"); + siglongjmp(promptbuf, 1); +} + +/* + * ****TIPIN TIPIN**** + */ +tipin() +{ + unsigned char gch, c; + int bol = 1; + + /* + * Kinda klugey here... + * check for scripting being turned on from the .tiprc file, + * but be careful about just using setscript(), as we may + * send a SIGEMT before tipout has a chance to set up catching + * it; so wait a second, then setscript() + */ + if (boolean(value(SCRIPT))) { + sleep(1); + setscript(); + } + + for (;;) { + gch = getchar()&0377; + if ((gch == character(value(ESCAPE))) && bol) { + if (!(gch = escape())) + continue; + } else if (!cumode && gch == character(value(RAISECHAR))) { + boolean(value(RAISE)) = !boolean(value(RAISE)); + continue; + } else if (gch == '\r') { + bol = 1; + parwrite(FD, &gch, 1); + if (boolean(value(HALFDUPLEX))) + printf("\r\n"); + continue; + } else if (!cumode && gch == character(value(FORCE))) + gch = getchar()&0377; + bol = any(gch, value(EOL)); + if (boolean(value(RAISE)) && islower(gch)) + gch = toupper(gch); + c = gch; + parwrite(FD, &gch, 1); + if (boolean(value(HALFDUPLEX))) + putchar(c); + } +} + +/* + * Escape handler -- + * called on recognition of ``escapec'' at the beginning of a line + */ +escape() +{ + register unsigned char gch; + register esctable_t *p; + char c = character(value(ESCAPE)); + extern esctable_t etable[]; + + gch = (getchar()&0377); + for (p = etable; p->e_char; p++) + if (p->e_char == gch) { + if ((p->e_flags&PRIV) && uid) + continue; + printf("%s", ctrl(c)); + (*p->e_func)(gch); + return (0); + } + /* ESCAPE ESCAPE forces ESCAPE */ + if (c != gch) + parwrite(FD, &c, 1); + return (gch); +} + +speed(n) + int n; +{ + register int *p; + + for (p = bauds; *p != -1; p++) + if (*p == n) + return (p - bauds); + return (NULL); +} + +any(c, p) + register char c, *p; +{ + while (p && *p) + if (*p++ == c) + return (1); + return (0); +} + +char * +interp(s) + register char *s; +{ + static char buf[256]; + register char *p = buf, c, *q; + + while (c = *s++) { + for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) + if (*q++ == c) { + *p++ = '\\'; *p++ = *q; + goto next; + } + if (c < 040) { + *p++ = '^'; *p++ = c + 'A'-1; + } else if (c == 0177) { + *p++ = '^'; *p++ = '?'; + } else + *p++ = c; + next: + ; + } + *p = '\0'; + return (buf); +} + +char * +ctrl(c) + char c; +{ + static char s[3]; + + if (c < 040 || c == 0177) { + s[0] = '^'; + s[1] = c == 0177 ? '?' : c+'A'-1; + s[2] = '\0'; + } else { + s[0] = c; + s[1] = '\0'; + } + return (s); +} + +/* + * Help command + */ +help(c) + char c; +{ + register esctable_t *p; + extern esctable_t etable[]; + + printf("%c\r\n", c); + for (p = etable; p->e_char; p++) { + if ((p->e_flags&PRIV) && uid) + continue; + printf("%2s", ctrl(character(value(ESCAPE)))); + printf("%-2s %c %s\r\n", ctrl(p->e_char), + p->e_flags&EXP ? '*': ' ', p->e_help); + } +} + +/* + * Set up the "remote" tty's state + */ +ttysetup(speed) + int speed; +{ + struct termios buf; + char *loc; + + ioctl(FD, TCGETS, (char *)&buf); + buf.c_cflag &= (CREAD|HUPCL|CLOCAL|CRTSCTS|CRTSXOFF); + buf.c_cflag |= CS8; + cfsetospeed(&buf, speed); + if (boolean(value(HARDWAREFLOW))) { + int i = TIOCM_CAR; + + /* + * Only set hardware flow control if carrier is up, + * because some devices require both CD and RTS to + * be up before sending. + */ + ioctl(FD, TIOCMGET, &i); + if (i & TIOCM_CAR) + buf.c_cflag |= (CRTSCTS|CRTSXOFF); + } + + /* + * Careful to only penalize the 8-bit users here on the + * incoming tty port. The default 7-bit users will + * still get the parity bit from the other side's login + * process (which happens to be the default for sun tip + * configurations). + */ + loc = setlocale(LC_CTYPE, NULL); + if (noparity && loc != 0 && strcmp(loc, "C") != 0) + buf.c_iflag = 0; + else + buf.c_iflag = ISTRIP; + buf.c_oflag = 0; + buf.c_lflag = 0; + buf.c_cc[VMIN] = 1; + buf.c_cc[VTIME] = 0; + ioctl(FD, TCSETSF, (char *)&buf); +} + +/* + * Return "simple" name from a file name, + * strip leading directories. + */ +char * +sname(s) + register char *s; +{ + register char *p = s; + + while (*s) + if (*s++ == '/') + p = s; + return (p); +} + +static char partab[0400]; + +/* + * Do a write to the remote machine with the correct parity. + * We are doing 8 bit wide output, so we just generate a character + * with the right parity and output it. + */ +parwrite(fd, buf, n) + int fd; + unsigned char *buf; + register int n; +{ + register int i; + register unsigned char *bp; + extern int errno; + + bp = buf; + for (i = 0; i < n; i++) { + *bp = partab[(*bp)&0377]; + bp++; + } + if (write(fd, buf, n) < 0) { + if (errno == EIO || errno == ENXIO) + abort("Lost carrier."); + /* this is questionable */ + perror("write"); + } +} + +/* + * Build a parity table with appropriate high-order bit. + */ +setparity(defparity) + char *defparity; +{ + register int i; + char *parity; + extern char evenpartab[]; + + if (value(PARITY) == NOSTR) + value(PARITY) = defparity; + parity = value(PARITY); + for (i = 0; i < 0400; i++) + partab[i] = evenpartab[i]; + if (equal(parity, "even")) + ; + else if (equal(parity, "odd")) { + for (i = 0; i < 0400; i++) + partab[i] ^= 0200; /* reverse bit 7 */ + } else if (equal(parity, "none")) { + /* Do nothing so we can pass thru 8-bit chars */ + noparity = 1; + for (i = 0; i < 0400; i++) + partab[i] = i; + } else if (equal(parity, "zero")) { + for (i = 0; i < 0400; i++) + partab[i] &= ~0200; /* turn off bit 7 */ + } else if (equal(parity, "one")) { + for (i = 0; i < 0400; i++) + partab[i] |= 0200; /* turn on bit 7 */ + } else { + fprintf(stderr, "%s: unknown parity value\n", PA); + fflush(stderr); + } +} diff --git a/usr/src/cmd/tip/tip.h b/usr/src/cmd/tip/tip.h new file mode 100644 index 0000000000..2fe34824c0 --- /dev/null +++ b/usr/src/cmd/tip/tip.h @@ -0,0 +1,262 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * tip - terminal interface program + */ + +#include <sys/types.h> +#ifdef USG +#include <fcntl.h> /* for O_RDWR, etc. */ +#include <unistd.h> /* for R_OK, etc. */ +#else +#include <sys/file.h> +#endif + +#include <sys/termios.h> +#include <sys/filio.h> /* XXX - for FIONREAD only */ +#include <signal.h> +#include <stdio.h> +#include <pwd.h> +#include <ctype.h> +#include <setjmp.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/isa_defs.h> /* for ENDIAN defines */ + +#define _CTRL(c) (c&037) + +#ifdef USG +#define signal(_sig_, _hdlr_) sigset((_sig_), (_hdlr_)) +#endif +typedef void (*sig_handler_t)(); /* works on BSD and SV */ + +/* + * Remote host attributes + */ +char *DV; /* UNIX device(s) to open */ +char *EL; /* chars marking an EOL */ +char *CM; /* initial connection message */ +char *IE; /* EOT to expect on input */ +char *OE; /* EOT to send to complete FT */ +char *CU; /* call unit if making a phone call */ +char *AT; /* acu type */ +char *PN; /* phone number(s) */ +char *DI; /* disconnect string */ +char *PA; /* parity to be generated */ + +char *PH; /* phone number file */ +char *RM; /* remote file name */ +char *HO; /* host name */ + +int BR; /* line speed for conversation */ +int FS; /* frame size for transfers */ + +signed char DU; /* this host is dialed up */ +char HW; /* this device is hardwired, see hunt.c */ +char *ES; /* escape character */ +char *EX; /* exceptions */ +char *FO; /* force (literal next) char */ +char *RC; /* raise character */ +char *RE; /* script record file */ +char *PR; /* remote prompt */ +int DL; /* line delay for file transfers to remote */ +int CL; /* char delay for file transfers to remote */ +int ET; /* echocheck timeout */ +int DB; /* dialback - ignore hangup */ + +/* + * String value table + */ +typedef + struct { + char *v_name; /* whose name is it */ + char v_type; /* for interpreting set's */ + char v_access; /* protection of touchy ones */ + char *v_abrev; /* possible abreviation */ + char *v_value; /* casted to a union later */ + } + value_t; + +#define STRING 01 /* string valued */ +#define BOOL 02 /* true-false value */ +#define NUMBER 04 /* numeric value */ +#define CHAR 010 /* character value */ + +#define WRITE 01 /* write access to variable */ +#define READ 02 /* read access */ + +#define CHANGED 01 /* low bit is used to show modification */ +#define PUBLIC 1 /* public access rights */ +#define PRIVATE 03 /* private to definer */ +#define ROOT 05 /* root defined */ + +#define TRUE 1 +#define FALSE 0 + +#define ENVIRON 020 /* initialize out of the environment */ +#define IREMOTE 040 /* initialize out of remote structure */ +#define INIT 0100 /* static data space used for initialization */ +#define TMASK 017 + +/* + * Definition of ACU line description + */ +typedef + struct { + char *acu_name; + int (*acu_dialer)(); + int (*acu_disconnect)(); + int (*acu_abort)(); + } + acu_t; + +#define equal(a, b) (strcmp(a, b) == 0) /* A nice function to compare */ + +/* + * variable manipulation stuff -- + * if we defined the value entry in value_t, then we couldn't + * initialize it in vars.c, so we cast it as needed to keep lint + * happy. + */ +typedef + union { + int zz_number; + int *zz_address; +#if defined(_LITTLE_ENDIAN) + short zz_boolean; + char zz_character; +#endif +#if defined(_BIG_ENDIAN) + int zz_boolean; + int zz_character; +#endif + } + zzhack; + +#define value(v) vtable[v].v_value + +#define boolean(v) ((((zzhack *)(&(v))))->zz_boolean) +#define number(v) ((((zzhack *)(&(v))))->zz_number) +#define character(v) ((((zzhack *)(&(v))))->zz_character) +#define address(v) ((((zzhack *)(&(v))))->zz_address) + +/* + * Escape command table definitions -- + * lookup in this table is performed when ``escapec'' is recognized + * at the begining of a line (as defined by the eolmarks variable). + */ + +typedef + struct { + char e_char; /* char to match on */ + char e_flags; /* experimental, priviledged */ + char *e_help; /* help string */ + int (*e_func)(); /* command */ + } + esctable_t; + +#define NORM 00 /* normal protection, execute anyone */ +#define EXP 01 /* experimental, mark it with a `*' on help */ +#define PRIV 02 /* priviledged, root execute only */ + +extern int vflag; /* verbose during reading of .tiprc file */ +extern value_t vtable[]; /* variable table */ +extern int noparity; + + +#ifndef ACULOG +#define logent(a, b, c, d) +#define loginit() +#endif + +/* + * Definition of indices into variable table so + * value(DEFINE) turns into a static address. + */ + +#define BEAUTIFY 0 +#define BAUDRATE 1 +#define DIALTIMEOUT 2 +#define EOFREAD 3 +#define EOFWRITE 4 +#define EOL 5 +#define ESCAPE 6 +#define EXCEPTIONS 7 +#define FORCE 8 +#define FRAMESIZE 9 +#define HOST 10 +#define LOG 11 +#define PHONES 12 +#define PROMPT 13 +#define RAISE 14 +#define RAISECHAR 15 +#define RECORD 16 +#define REMOTE 17 +#define SCRIPT 18 +#define TABEXPAND 19 +#define VERBOSE 20 +#define SHELL 21 +#define HOME 22 +#define ECHOCHECK 23 +#define DISCONNECT 24 +#define TAND 25 +#define LDELAY 26 +#define CDELAY 27 +#define ETIMEOUT 28 +#define RAWFTP 29 +#define HALFDUPLEX 30 +#define LECHO 31 +#define PARITY 32 +#define HARDWAREFLOW 33 + +#define NOVAL ((value_t *)NULL) +#define NOACU ((acu_t *)NULL) +#define NOSTR ((char *)NULL) +#define NOFILE ((FILE *)NULL) +#define NOPWD ((struct passwd *)0) + +struct termios arg; /* current mode of local terminal */ +struct termios defarg; /* initial mode of local terminal */ + +FILE *fscript; /* FILE for scripting */ +FILE *phfd; /* FILE for PHONES file */ + +int fildes[2]; /* file transfer synchronization channel */ +int repdes[2]; /* read process sychronization channel */ +int FD; /* open file descriptor to remote host */ +int AC; /* open file descriptor to dialer (v831 only) */ +int vflag; /* print .tiprc initialization sequence */ +int sfd; /* for ~< operation */ +int pid; /* pid of tipout */ +int uid, euid; /* real and effective user id's */ +int gid, egid; /* real and effective group id's */ +int stoprompt; /* for interrupting a prompt session */ +int timedout; /* ~> transfer timedout */ +int cumode; /* simulating the "cu" program */ + +char fname[80]; /* file name buffer for ~< */ +char copyname[80]; /* file name buffer for ~> */ +char ccc; /* synchronization character */ +char ch; /* for tipout */ +char *uucplock; /* name of lock file for uucp's */ +extern int trusted_device; + +extern char *ctrl(); +extern char *ctime(); +extern struct passwd *getpwuid(); +extern char *getlogin(); +extern char *vinterp(); +extern char *getenv(); +extern char *malloc(); +extern char *connect(); diff --git a/usr/src/cmd/tip/tipout.c b/usr/src/cmd/tip/tipout.c new file mode 100644 index 0000000000..fbfb9212e0 --- /dev/null +++ b/usr/src/cmd/tip/tipout.c @@ -0,0 +1,159 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ident "%Z%%M% %I% %E% SMI" /* from UCB 5.1 4/30/85 */ + +#include "tip.h" +/* + * tip + * + * lower fork of tip -- handles passive side + * reading from the remote host + */ + +static sigjmp_buf sigbuf; + +/* + * TIPOUT wait state routine -- + * sent by TIPIN when it wants to posses the remote host + */ +void +intIOT() +{ + + write(repdes[1], &ccc, 1); + read(fildes[0], &ccc, 1); + siglongjmp(sigbuf, 1); +} + +/* + * Scripting command interpreter -- + * accepts script file name over the pipe and acts accordingly + */ +void +intEMT() +{ + char c, line[256]; + register char *pline = line; + char reply; + + read(fildes[0], &c, 1); + while (c != '\n') { + *pline++ = c; + read(fildes[0], &c, 1); + } + *pline = '\0'; + if (boolean(value(SCRIPT)) && fscript != NULL) + fclose(fscript); + if (pline == line) { + boolean(value(SCRIPT)) = FALSE; + reply = 'y'; + } else { + if ((fscript = fopen(line, "a")) == NULL) + reply = 'n'; + else { + reply = 'y'; + boolean(value(SCRIPT)) = TRUE; + } + } + write(repdes[1], &reply, 1); + siglongjmp(sigbuf, 1); +} + +void +intTERM() +{ + + if (boolean(value(SCRIPT)) && fscript != NULL) + fclose(fscript); + exit(0); +} + +void +intSYS() +{ + + boolean(value(BEAUTIFY)) = !boolean(value(BEAUTIFY)); + siglongjmp(sigbuf, 1); +} + +/* + * ****TIPOUT TIPOUT**** + */ +tipout() +{ + char buf[BUFSIZ]; + register char *cp; + register int cnt; + extern int errno; + sigset_t omask, bmask, tmask; + + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGEMT, (sig_handler_t)intEMT); /* attention from TIPIN */ + signal(SIGTERM, (sig_handler_t)intTERM); /* time to go signal */ + signal(SIGIOT, (sig_handler_t)intIOT); /* scripting going on signal */ + signal(SIGHUP, (sig_handler_t)intTERM); /* for dial-ups */ + signal(SIGSYS, (sig_handler_t)intSYS); /* beautify toggle */ + (void) sigsetjmp(sigbuf, 1); + + sigemptyset(&omask); + sigemptyset(&bmask); + sigaddset(&bmask, SIGEMT); + sigaddset(&bmask, SIGTERM); + sigaddset(&bmask, SIGIOT); + sigaddset(&bmask, SIGSYS); + sigemptyset(&tmask); + sigaddset(&tmask, SIGTERM); + for (;;) { + cnt = read(FD, buf, BUFSIZ); + if (cnt <= 0) { + /* + * If dialback is specified, ignore the hangup + * and clear the hangup condition on the device. + */ + if (cnt == 0 && DB) { + int fd; + + DB = 0; + if ((fd = open(DV, O_RDWR)) >= 0) { + if (fd != FD) + close(fd); + } + continue; + } + /* lost carrier */ + if ((cnt < 0 && errno == EIO) || + (cnt == 0)) { + (void) sigprocmask(SIG_BLOCK, &tmask, NULL); + intTERM(); + /*NOTREACHED*/ + } + } else { + (void) sigprocmask(SIG_BLOCK, &bmask, &omask); + if (!noparity) + for (cp = buf; cp < buf + cnt; cp++) + *cp &= 0177; + + write(1, buf, cnt); + if (boolean(value(SCRIPT)) && fscript != NULL) { + if (!boolean(value(BEAUTIFY))) { + fwrite(buf, 1, cnt, fscript); + } else { + for (cp = buf; cp < buf + cnt; cp++) + if ((*cp >= ' ' && *cp <= '~')|| + any(*cp, value(EXCEPTIONS))) + putc(*cp, fscript); + } + } + } + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + } +} diff --git a/usr/src/cmd/tip/uucplock.c b/usr/src/cmd/tip/uucplock.c new file mode 100644 index 0000000000..a9b5b5285c --- /dev/null +++ b/usr/src/cmd/tip/uucplock.c @@ -0,0 +1,303 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.6 6/25/83 */ +/* + * defs that come from uucp.h + */ +#define NAMESIZE 40 +#define FAIL -1 +#define SAME 0 +#define SLCKTIME (8*60*60) /* device timeout (LCK.. files) in seconds */ +#ifdef __STDC__ +#define ASSERT(e, f, v) if (!(e)) {\ + fprintf(stderr, "AERROR - (%s) ", #e); \ + fprintf(stderr, f, v); \ + finish(FAIL); \ +} +#else +#define ASSERT(e, f, v) if (!(e)) {\ + fprintf(stderr, "AERROR - (%s) ", "e"); \ + fprintf(stderr, f, v); \ + finish(FAIL); \ +} +#endif +#define SIZEOFPID 10 /* maximum number of digits in a pid */ + +#define LOCKDIR "/var/spool/locks" +#define LOCKPRE "LK" + +/* + * This code is taken almost directly from uucp and follows the same + * conventions. This is important since uucp and tip should + * respect each others locks. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mkdev.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> + +static void stlock(); +static int onelock(); +static int checkLock(); + + +/* + * ulockf(file, atime) + * char *file; + * time_t atime; + * + * ulockf - this routine will create a lock file (file). + * If one already exists, send a signal 0 to the process--if + * it fails, then unlink it and make a new one. + * + * input: + * file - name of the lock file + * atime - is unused, but we keep it for lint compatibility + * with non-ATTSVKILL + * + * return codes: 0 | FAIL + */ + +static +ulockf(file, atime) + char *file; + time_t atime; +{ + static char pid[SIZEOFPID+2] = { '\0' }; /* +2 for '\n' and NULL */ + static char tempfile[NAMESIZE]; + + if (pid[0] == '\0') { + (void) sprintf(pid, "%*d\n", SIZEOFPID, getpid()); + (void) sprintf(tempfile, "%s/LTMP.%d", LOCKDIR, getpid()); + } + if (onelock(pid, tempfile, file) == -1) { + /* lock file exists */ + (void) unlink(tempfile); + if (checkLock(file)) + return (FAIL); + else { + if (onelock(pid, tempfile, file)) { + (void) unlink(tempfile); + return (FAIL); + } + } + } + stlock(file); + return (0); +} + +/* + * check to see if the lock file exists and is still active + * - use kill(pid, 0) - (this only works on ATTSV and some hacked + * BSD systems at this time) + * return: + * 0 -> success (lock file removed - no longer active) + * FAIL -> lock file still active + */ +static int +checkLock(file) +register char *file; +{ + register int ret; + int lpid = -1; + char alpid[SIZEOFPID+2]; /* +2 for '\n' and NULL */ + int fd; + extern int errno; + + fd = open(file, 0); + if (fd == -1) { + if (errno == ENOENT) /* file does not exist -- OK */ + return (0); + goto unlk; + } + ret = read(fd, (char *)alpid, SIZEOFPID+1); /* +1 for '\n' */ + (void) close(fd); + if (ret != (SIZEOFPID+1)) + goto unlk; + lpid = atoi(alpid); + if ((ret = kill(lpid, 0)) == 0 || errno == EPERM) + return (FAIL); + +unlk: + if (unlink(file) != 0) + return (FAIL); + return (0); +} + +#define MAXLOCKS 10 /* maximum number of lock files */ +char *Lockfile[MAXLOCKS]; +int Nlocks = 0; + +/* + * stlock(name) put name in list of lock files + * char *name; + * + * return codes: none + */ + +static void +stlock(name) + char *name; +{ + char *p; + extern char *calloc(); + int i; + + for (i = 0; i < Nlocks; i++) { + if (Lockfile[i] == NULL) + break; + } + ASSERT(i < MAXLOCKS, "TOO MANY LOCKS %d", i); + if (i >= Nlocks) + i = Nlocks++; + p = calloc(strlen(name) + 1, sizeof (char)); + ASSERT(p != NULL, "CAN NOT ALLOCATE FOR %s", name); + strcpy(p, name); + Lockfile[i] = p; +} + +/* + * rmlock(name) remove all lock files in list + * char *name; or name + * + * return codes: none + */ + +static +rmlock(name) + char *name; +{ + int i; + + for (i = 0; i < Nlocks; i++) { + if (Lockfile[i] == NULL) + continue; + if (name == NULL || strcmp(name, Lockfile[i]) == SAME) { + unlink(Lockfile[i]); + free(Lockfile[i]); + Lockfile[i] = NULL; + } + } +} + +static +onelock(pid, tempfile, name) + char *pid, *tempfile, *name; +{ + int fd; + static int first = 1; + extern int errno; + + fd = creat(tempfile, 0444); + if (fd < 0) { + if (first) { + if (errno == EACCES) { + fprintf(stderr, + "tip: can't create files in lock file directory %s\n", + LOCKDIR); + } else if (access(LOCKDIR, 0) < 0) { + fprintf(stderr, "tip: lock file directory %s: ", + LOCKDIR); + perror(""); + } + first = 0; + } + if (errno == EMFILE || errno == ENFILE) + (void) unlink(tempfile); + return (-1); + } + /* +1 for '\n' */ + if (write(fd, pid, SIZEOFPID+1) != (SIZEOFPID+1)) { + fprintf(stderr, + "tip: can't write to files in lock file directory %s: %s\n", + LOCKDIR, strerror(errno)); + (void) unlink(tempfile); + return (-1); + } + fchmod(fd, 0444); + close(fd); + if (link(tempfile, name) < 0) { + unlink(tempfile); + return (-1); + } + unlink(tempfile); + return (0); +} + +/* + * delock(sys) remove a lock file + * char *sys; + * + * return codes: 0 | FAIL + */ + +delock(sys) + char *sys; +{ + struct stat sb; + char lname[NAMESIZE]; + + if (stat(sys, &sb) < 0) + return (FAIL); + sprintf(lname, "%s/%s.%3.3lu.%3.3lu.%3.3lu", LOCKDIR, LOCKPRE, + (unsigned long)major(sb.st_dev), + (unsigned long)major(sb.st_rdev), + (unsigned long)minor(sb.st_rdev)); + rmlock(lname); +} + +/* + * mlock(sys) create system lock + * char *sys; + * + * return codes: 0 | FAIL + */ + +mlock(sys) + char *sys; +{ + struct stat sb; + char lname[NAMESIZE]; + + if (stat(sys, &sb) < 0) + return (FAIL); + sprintf(lname, "%s/%s.%3.3lu.%3.3lu.%3.3lu", LOCKDIR, LOCKPRE, + (unsigned long)major(sb.st_dev), + (unsigned long)major(sb.st_rdev), + (unsigned long)minor(sb.st_rdev)); + return (ulockf(lname, (time_t)SLCKTIME) < 0 ? FAIL : 0); +} + +/* + * update access and modify times for lock files + * return: + * none + */ +void +ultouch() +{ + register int i; + time_t time(); + + struct ut { + time_t actime; + time_t modtime; + } ut; + + ut.actime = time(&ut.modtime); + for (i = 0; i < Nlocks; i++) { + if (Lockfile[i] == NULL) + continue; + utime(Lockfile[i], &ut); + } +} diff --git a/usr/src/cmd/tip/value.c b/usr/src/cmd/tip/value.c new file mode 100644 index 0000000000..a48e0fa764 --- /dev/null +++ b/usr/src/cmd/tip/value.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* from UCB 4.5 6/25/83 */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "tip.h" + +#define MIDDLE 35 + +static value_t *vlookup(); +static int col = 0; + +/* + * Variable manipulation + */ +vinit() +{ + register value_t *p; + register char *cp; + FILE *f; + char file[1024]; + + for (p = vtable; p->v_name != NULL; p++) { + if (p->v_type&ENVIRON) + if (cp = getenv(p->v_name)) + p->v_value = cp; + if (p->v_type&IREMOTE) + number(p->v_value) = *address(p->v_value); + } + /* + * Read the .tiprc file in the HOME directory + * for sets + */ + if ((cp = value(HOME)) == NULL) + cp = ""; + strlcpy(file, cp, sizeof (file)); + strlcat(file, "/.tiprc", sizeof (file)); + if ((f = fopen(file, "r")) != NULL) { + register char *tp; + + while (fgets(file, sizeof (file)-1, f) != NULL) { + if (file[0] == '#') + continue; + if (vflag) + printf("set %s", file); + if (tp = strrchr(file, '\n')) + *tp = '\0'; + vlex(file); + } + fclose(f); + } + /* + * To allow definition of exception prior to fork + */ + vtable[EXCEPTIONS].v_access &= ~(WRITE<<PUBLIC); +} + +/*VARARGS1*/ +vassign(p, v) + register value_t *p; + char *v; +{ + + if (!vaccess(p->v_access, WRITE)) { + printf("access denied\r\n"); + return; + } + switch (p->v_type&TMASK) { + + case STRING: + if (p->v_value != (char *)NULL) { + if (equal(p->v_value, v)) + return; + if (!(p->v_type&(ENVIRON|INIT))) + free(p->v_value); + } + if ((p->v_value = malloc(strlen(v)+1)) == NOSTR) { + printf("out of core\r\n"); + return; + } + p->v_type &= ~(ENVIRON|INIT); + strcpy(p->v_value, v); + break; + + case NUMBER: + if (number(p->v_value) == number(v)) + return; + number(p->v_value) = number(v); + break; + + case BOOL: + if (boolean(p->v_value) == (*v != '!')) + return; + boolean(p->v_value) = (*v != '!'); + break; + + case CHAR: + if (character(p->v_value) == *v) + return; + character(p->v_value) = *v; + } + p->v_access |= CHANGED; +} + +vlex(s) + register char *s; +{ + register value_t *p; + + if (equal(s, "all")) { + for (p = vtable; p->v_name; p++) + if (vaccess(p->v_access, READ)) + vprint(p); + } else { + register char *cp; + + do { + if (cp = vinterp(s, ' ')) + cp++; + vtoken(s); + s = cp; + } while (s); + } + if (col > 0) { + printf("\r\n"); + col = 0; + } +} + +static int +vtoken(s) + register char *s; +{ + register value_t *p; + register char *cp, *cp2; + char *expand(); + + if (cp = strchr(s, '=')) { + *cp = '\0'; + if (p = vlookup(s)) { + cp++; + if (p->v_type&NUMBER) + vassign(p, atoi(cp)); + else { + if (strcmp(s, "record") == 0) + if ((cp2 = expand(cp)) != NOSTR) + cp = cp2; + vassign(p, cp); + } + return; + } + } else if (cp = strchr(s, '?')) { + *cp = '\0'; + if ((p = vlookup(s)) && vaccess(p->v_access, READ)) { + vprint(p); + return; + } + } else { + if (*s != '!') + p = vlookup(s); + else + p = vlookup(s+1); + if (p != NOVAL) { + if (p->v_type&BOOL) + vassign(p, s); + else + printf("%s: no value specified\r\n", s); + return; + } + } + printf("%s: unknown variable\r\n", s); +} + +static int +vprint(p) + register value_t *p; +{ + register char *cp; + extern char *interp(), *ctrl(); + + if (col > 0 && col < MIDDLE) + while (col++ < MIDDLE) + putchar(' '); + col += strlen(p->v_name); + switch (p->v_type&TMASK) { + + case BOOL: + if (boolean(p->v_value) == FALSE) { + col++; + putchar('!'); + } + printf("%s", p->v_name); + break; + + case STRING: + printf("%s=", p->v_name); + col++; + if (p->v_value) { + cp = interp(p->v_value, NULL); + col += strlen(cp); + printf("%s", cp); + } + break; + + case NUMBER: + col += 6; + printf("%s=%-5d", p->v_name, number(p->v_value)); + break; + + case CHAR: + printf("%s=", p->v_name); + col++; + if (p->v_value) { + cp = ctrl(character(p->v_value)); + col += strlen(cp); + printf("%s", cp); + } + break; + } + if (col >= MIDDLE) { + col = 0; + printf("\r\n"); + return; + } +} + + +static int +vaccess(mode, rw) + register unsigned mode, rw; +{ + if (mode & (rw<<PUBLIC)) + return (1); + if (mode & (rw<<PRIVATE)) + return (1); + return ((mode & (rw<<ROOT)) && uid == 0); +} + +static value_t * +vlookup(s) + register char *s; +{ + register value_t *p; + + for (p = vtable; p->v_name; p++) + if (equal(p->v_name, s) || (p->v_abrev && equal(p->v_abrev, s))) + return (p); + return (NULL); +} + +char * +vinterp(s, stop) + register char *s; + char stop; +{ + register char *p = s, c; + int num; + + while ((c = *s++) && c != stop) + switch (c) { + + case '^': + if (*s) + *p++ = *s++ - 0100; + else + *p++ = c; + break; + + case '\\': + num = 0; + c = *s++; + if (c >= '0' && c <= '7') + num = (num<<3)+(c-'0'); + else { + register char *q = "n\nr\rt\tb\bf\f"; + + for (; *q; q++) + if (c == *q++) { + *p++ = *q; + goto cont; + } + *p++ = c; + cont: + break; + } + if ((c = *s++) >= '0' && c <= '7') { + num = (num<<3)+(c-'0'); + if ((c = *s++) >= '0' && c <= '7') + num = (num<<3)+(c-'0'); + else + s--; + } else + s--; + *p++ = num; + break; + + default: + *p++ = c; + } + *p = '\0'; + return (c == stop ? s-1 : NULL); +} + +/* + * assign variable s with value v (for NUMBER or STRING or CHAR types) + */ + +vstring(s, v) + register char *s; + register char *v; +{ + register value_t *p; + char *v2; + char *expand(); + + p = vlookup(s); + if (p == 0) + return (1); + if (p->v_type&NUMBER) + vassign(p, atoi(v)); + else { + if (strcmp(s, "record") == 0) + if ((v2 = expand(v)) != NOSTR) + v = v2; + vassign(p, v); + } + return (0); +} diff --git a/usr/src/cmd/tip/vars.c b/usr/src/cmd/tip/vars.c new file mode 100644 index 0000000000..4367e28ba5 --- /dev/null +++ b/usr/src/cmd/tip/vars.c @@ -0,0 +1,87 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ +#ident "%Z%%M% %I% %E% SMI" /* from UCB 4.6 6/25/83 */ + +#include "tip.h" + +/* + * Definition of variables + */ +value_t vtable[] = { + { "beautify", BOOL, (READ|WRITE)<<PUBLIC, + "be", (char *)TRUE }, + { "baudrate", NUMBER|IREMOTE|INIT, (READ<<PUBLIC)|(WRITE<<ROOT), + "ba", (char *)&BR }, + { "dialtimeout", NUMBER, (READ<<PUBLIC)|(WRITE<<ROOT), + "dial", (char *)60 }, + { "eofread", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "eofr", (char *)&IE }, + { "eofwrite", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "eofw", (char *)&OE }, + { "eol", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + NOSTR, (char *)&EL }, + { "escape", CHAR, (READ|WRITE)<<PUBLIC, + "es", (char *)'~' }, + { "exceptions", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC, + "ex", (char *)&EX }, + { "force", CHAR, (READ|WRITE)<<PUBLIC, + "fo", (char *)0377 }, + { "framesize", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "fr", (char *)&FS }, + { "host", STRING|IREMOTE|INIT, READ<<PUBLIC, + "ho", (char *)&HO }, + { "log", STRING|INIT, (READ|WRITE)<<ROOT, + NOSTR, "/var/adm/aculog" }, + { "phones", STRING|INIT|IREMOTE, READ<<PUBLIC, + NOSTR, (char *)&PH }, + { "prompt", CHAR, (READ|WRITE)<<PUBLIC, + "pr", (char *)'\n' }, + { "raise", BOOL, (READ|WRITE)<<PUBLIC, + "ra", (char *)FALSE }, + { "raisechar", CHAR, (READ|WRITE)<<PUBLIC, + "rc", (char *)0377 }, + { "record", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC, + "rec", (char *)&RE }, + { "remote", STRING|INIT|IREMOTE, READ<<PUBLIC, + NOSTR, (char *)&RM }, + { "script", BOOL, (READ|WRITE)<<PUBLIC, + "sc", (char *)FALSE }, + { "tabexpand", BOOL, (READ|WRITE)<<PUBLIC, + "tab", (char *)FALSE }, + { "verbose", BOOL, (READ|WRITE)<<PUBLIC, + "verb", (char *)TRUE }, + { "SHELL", STRING|ENVIRON|INIT, (READ|WRITE)<<PUBLIC, + NULL, "/bin/sh" }, + { "HOME", STRING|ENVIRON, (READ|WRITE)<<PUBLIC, + NOSTR, NOSTR }, + { "echocheck", BOOL, (READ|WRITE)<<PUBLIC, + "ec", (char *)FALSE }, + { "disconnect", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "di", (char *)&DI }, + { "tandem", BOOL, (READ|WRITE)<<PUBLIC, + "ta", (char *)TRUE }, + { "linedelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "ldelay", (char *)&DL }, + { "chardelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "cdelay", (char *)&CL }, + { "etimeout", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC, + "et", (char *)&ET }, + { "rawftp", BOOL, (READ|WRITE)<<PUBLIC, + "raw", (char *)FALSE }, + { "halfduplex", BOOL, (READ|WRITE)<<PUBLIC, + "hdx", (char *)FALSE }, + { "localecho", BOOL, (READ|WRITE)<<PUBLIC, + "le", (char *)FALSE }, + { "parity", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC, + "par", (char *)&PA }, + { "hardwareflow", BOOL, (READ|WRITE)<<PUBLIC, + "hf", (char *)FALSE }, + { NOSTR, NULL, NULL, NOSTR, NOSTR } +}; |
