BSD 4_4 release
[unix-history] / usr / src / usr.bin / uucp / uucico / condevs.c
/*-
* Copyright (c) 1985, 1993
* The Regents of the University of California. All rights reserved.
*
* This module is believed to contain source code proprietary to AT&T.
* Use and redistribution is subject to the Berkeley Software License
* Agreement and your Software Agreement with AT&T (Western Electric).
*/
#ifndef lint
static char sccsid[] = "@(#)condevs.c 8.1 (Berkeley) 6/6/93";
#endif /* not lint */
extern int errno;
extern const char *const sys_errlist[];
/*
* Here are various dialers to establish the machine-machine connection.
* conn.c/condevs.c was glued together by Mike Mitchell.
* The dialers were supplied by many people, to whom we are grateful.
*
* ---------------------------------------------------------------------
* NOTE:
* There is a bug that occurs at least on PDP11s due to a limitation of
* setjmp/longjmp. If the routine that does a setjmp is interrupted
* and longjmp-ed to, it loses its register variables (on a pdp11).
* What works is if the routine that does the setjmp
* calls a routine and it is the *subroutine* that is interrupted.
*
* Anyway, in conclusion, condevs.c is plagued with register variables
* that are used inside
* if (setjmp(...)) {
* ....
* }
*
* THE FIX: Don't declare variables to be register
*/
#include "condevs.h"
#include "pathnames.h"
struct condev condevs[] = {
{ "DIR", "direct", diropn, nulldev, dircls },
#ifdef DATAKIT
{ "DK", "datakit", dkopn, nulldev, nulldev },
#endif DATAKIT
#ifdef PNET
{ "PNET", "pnet", pnetopn, nulldev, nulldev },
#endif PNET
#ifdef UNETTCP
{ "TCP", "TCP", unetopn, nulldev, unetcls },
#endif UNETTCP
#ifdef BSDTCP
{ "TCP", "TCP", bsdtcpopn, nulldev, bsdtcpcls },
#endif BSDTCP
#ifdef MICOM
{ "MICOM", "micom", micopn, nulldev, miccls },
#endif MICOM
#ifdef DN11
{ "ACU", "dn11", Acuopn, dnopn, dncls },
#endif DN11
#ifdef HAYES
{ "ACU", "hayes", Acuopn, hyspopn, hyscls },
{ "ACU", "hayespulse", Acuopn, hyspopn, hyscls },
{ "ACU", "hayestone", Acuopn, hystopn, hyscls },
{ "WATS", "hayestone", Acuopn, hystopn, hyscls },
#endif HAYES
#ifdef HAYES2400
{ "ACU", "hayes2400", Acuopn, hyspopn24, hyscls24 },
{ "ACU", "hayes2400pulse", Acuopn, hyspopn24, hyscls24 },
{ "ACU", "hayes2400tone", Acuopn, hystopn24, hyscls24 },
#endif HAYES2400
#ifdef HAYESQ /* a version of hayes that doesn't use result codes */
{ "ACU", "hayesq", Acuopn, hysqpopn, hysqcls },
{ "ACU", "hayesqpulse", Acuopn, hysqpopn, hysqcls },
{ "ACU", "hayesqtone", Acuopn, hysqtopn, hysqcls },
#endif HAYESQ
#ifdef CDS224
{ "ACU", "cds224", Acuopn, cdsopn224, cdscls224},
#endif CDS224
#ifdef NOVATION
{ "ACU", "novation", Acuopn, novopn, novcls},
#endif NOVATION
#ifdef DF02
{ "ACU", "DF02", Acuopn, df2opn, df2cls },
#endif DF02
#ifdef DF112
{ "ACU", "DF112P", Acuopn, df12popn, df12cls },
{ "ACU", "DF112T", Acuopn, df12topn, df12cls },
#endif DF112
#ifdef VENTEL
{ "ACU", "ventel", Acuopn, ventopn, ventcls },
#endif VENTEL
#ifdef PENRIL
{ "ACU", "penril", Acuopn, penopn, pencls },
#endif PENRIL
#ifdef VADIC
{ "ACU", "vadic", Acuopn, vadopn, vadcls },
#endif VADIC
#ifdef VA212
{ "ACU", "va212", Acuopn, va212opn, va212cls },
#endif VA212
#ifdef VA811S
{ "ACU", "va811s", Acuopn, va811opn, va811cls },
#endif VA811S
#ifdef VA820
{ "ACU", "va820", Acuopn, va820opn, va820cls },
{ "WATS", "va820", Acuopn, va820opn, va820cls },
#endif VA820
#ifdef RVMACS
{ "ACU", "rvmacs", Acuopn, rvmacsopn, rvmacscls },
#endif RVMACS
#ifdef VMACS
{ "ACU", "vmacs", Acuopn, vmacsopn, vmacscls },
#endif VMACS
#ifdef SYTEK
{ "SYTEK", "sytek", sykopn, nulldev, sykcls },
#endif SYTEK
#ifdef ATT2224
{ "ACU", "att", Acuopn, attopn, attcls },
#endif ATT2224
/* Insert new entries before this line */
{ NULL, NULL, NULL, NULL, NULL }
};
/*
* nulldev a null device (returns CF_DIAL)
*/
nulldev()
{
return CF_DIAL;
}
/*
* nodev a null device (returns CF_NODEV)
*/
nodev()
{
return CF_NODEV;
}
/*
* Generic devices look through L-devices and call the CU_open routines for
* appropriate devices. Some things, like the tcp/ip interface, or direct
* connect, do not use the CU_open entry. ACUs must search to find the
* right routine to call.
*/
/*
* diropn(flds) connect to hardware line
*
* return codes:
* > 0 - file number - ok
* FAIL - failed
*/
diropn(flds)
register char *flds[];
{
register int dcr, status;
struct Devices dev;
char dcname[20];
FILE *dfp;
#ifdef VMSDTR /* Modem control on vms(works dtr) */
int modem_control;
short iosb[4];
int sys$qiow(); /* use this for long reads on vms */
int ret;
long mode[2];
modem_control = 0;
#endif
dfp = fopen(DEVFILE, "r");
if (dfp == NULL) {
syslog(LOG_ERR, "fopen(%s) failed: %m", DEVFILE);
cleanup(FAIL);
}
while ((status = rddev(dfp, &dev)) != FAIL) {
#ifdef VMSDTR /* Modem control on vms(works dtr) */
/* If we find MOD in the device type field we go into action */
if (strcmp(dev.D_type, "MOD") == SAME) {
modem_control = 1;
DEBUG(7, "Setting Modem control to %d",modem_control);
}
if (strcmp(flds[F_CLASS], dev.D_class) != SAME)
continue;
/*
* Modem control on vms(works dtr) Take anything in MOD class.
* It probably should work differently anyway so we can have
* multiple hardwired lines.
*/
if (!modem_control&&strcmp(flds[F_PHONE], dev.D_line) != SAME)
#else !VMSDTR
if (strcmp(flds[F_CLASS], dev.D_class) != SAME)
continue;
if (strcmp(flds[F_PHONE], dev.D_line) != SAME)
#endif !VMSDTR
continue;
if (mlock(dev.D_line) != FAIL)
break;
}
fclose(dfp);
if (status == FAIL) {
logent("DEVICE", "NO");
return CF_NODEV;
}
sprintf(dcname, "%s/%s", _PATH_DEV, dev.D_line);
if (setjmp(Sjbuf)) {
DEBUG(4, "Open timed out\n", CNULL);
delock(dev.D_line);
return CF_DIAL;
}
signal(SIGALRM, alarmtr);
/* For PC Pursuit, it could take a while to call back */
alarm( strcmp(flds[F_LINE], "PCP") ? 10 : MAXMSGTIME*4 );
getnextfd();
errno = 0;
DEBUG(4,"Opening %s\n",dcname);
dcr = open(dcname, 2); /* read/write */
#ifdef VMSDTR /* Modem control on vms(works dtr) */
fflush(stdout);
if (modem_control) { /* Did we have MOD in the device type field ? */
/* Sense the current terminal setup and save it */
if ((ret = sys$qiow(_$EFN,(fd_fab_pointer[dcr]->fab).fab$l_stv,
IO$_SENSEMODE,iosb,0,0,mode,8,0,0,0,0))
!= SS$_NORMAL) {
DEBUG(7, "ret status on sense failed on Modem sense=%x<", ret);
return CF_DIAL;
}
mode[1] |= TT$M_MODEM; /* Or in modem control(DTR) */
/* Now set the new terminal characteristics */
/* This is temporary and will go away when we let go of it */
if ((ret = sys$qiow(_$EFN,(fd_fab_pointer[dcr]->fab).fab$l_stv,
IO$_SETMODE,iosb,0,0,mode,8,0,0,0,0))
!= SS$_NORMAL) {
DEBUG(7, "ret status on sense failed on Modem setup=%x<", ret);
return CF_DIAL;
}
}
#endif VMSDTR
next_fd = -1;
alarm(0);
if (dcr < 0) {
if (errno == EACCES)
logent(dev.D_line, "CANT OPEN");
DEBUG(4, "OPEN FAILED: errno %d\n", errno);
delock(dev.D_line);
return CF_DIAL;
}
fflush(stdout);
if (fixline(dcr, dev.D_speed) == FAIL) {
DEBUG(4, "FIXLINE FAILED\n", CNULL);
return CF_DIAL;
}
strcpy(devSel, dev.D_line); /* for latter unlock */
CU_end = dircls;
return dcr;
}
dircls(fd)
register int fd;
{
if (fd > 0) {
close(fd);
delock(devSel);
}
}
/*
* open an ACU and dial the number. The condevs table
* will be searched until a dialing unit is found that is free.
*
* return codes: >0 - file number - o.k.
* FAIL - failed
*/
char devSel[20]; /* used for later unlock() */
Acuopn(flds)
register char *flds[];
{
char phone[MAXPH+1];
register struct condev *cd;
register int fd, acustatus;
register FILE *dfp;
struct Devices dev;
int retval = CF_NODEV;
char nobrand[MAXPH], *line;
exphone(flds[F_PHONE], phone);
if (snccmp(flds[F_LINE], "LOCAL") == SAME)
line = "ACU";
else
line = flds[F_LINE];
devSel[0] = '\0';
nobrand[0] = '\0';
DEBUG(4, "Dialing %s\n", phone);
dfp = fopen(DEVFILE, "r");
if (dfp == NULL) {
syslog(LOG_ERR, "fopen(%s) failed: %m", DEVFILE);
cleanup(FAIL);
}
acustatus = 0; /* none found, none locked */
while (rddev(dfp, &dev) != FAIL) {
/*
* for each ACU L.sys line, try at most twice
* (TRYCALLS) to establish carrier. The old way tried every
* available dialer, which on big sites takes forever!
* Sites with a single auto-dialer get one try.
* Sites with multiple dialers get a try on each of two
* different dialers.
* To try 'harder' to connect to a remote site,
* use multiple L.sys entries.
*/
if (acustatus > TRYCALLS)
break;
if (strcmp(flds[F_CLASS], dev.D_class) != SAME)
continue;
if (snccmp(line, dev.D_type) != SAME)
continue;
if (dev.D_brand[0] == '\0') {
logent("Acuopn","No 'brand' name on ACU");
continue;
}
for(cd = condevs; cd->CU_meth != NULL; cd++) {
if (snccmp(line, cd->CU_meth) == SAME) {
if (snccmp(dev.D_brand, cd->CU_brand) == SAME) {
nobrand[0] = '\0';
break;
}
strncpy(nobrand, dev.D_brand, sizeof nobrand);
}
}
if (acustatus < 1)
acustatus = 1; /* has been found */
if (mlock(dev.D_line) == FAIL)
continue;
#ifdef DIALINOUT
#ifdef ALLACUINOUT
if (1) {
#else !ALLACUINOUT
if (snccmp("inout", dev.D_calldev) == SAME) {
#endif !ALLACUINOUT
if (disable(dev.D_line) == FAIL) {
delock(dev.D_line);
continue;
}
} else
reenable();
#endif DIALINOUT
DEBUG(4, "Using %s\n", cd->CU_brand);
acustatus++;
fd = (*(cd->CU_open))(phone, flds, &dev);
if (fd > 0) {
CU_end = cd->CU_clos; /* point CU_end at close func */
fclose(dfp);
strcpy(devSel, dev.D_line); /* save for later unlock() */
return fd;
} else
delock(dev.D_line);
retval = CF_DIAL;
}
fclose(dfp);
if (acustatus == 0) {
if (nobrand[0])
logent(nobrand, "unsupported ACU type");
else
logent("L-devices", "No appropriate ACU");
}
if (acustatus == 1)
logent("DEVICE", "NO");
return retval;
}
/*
* intervaldelay: delay execution for numerator/denominator seconds.
*/
#ifdef INTERVALTIMER
#include <sys/time.h>
#define uucpdelay(num,denom) intervaldelay(num,denom)
intervaldelay(num,denom)
int num, denom;
{
struct timeval tv;
tv.tv_sec = num / denom;
tv.tv_usec = (num * 1000000L / denom ) % 1000000L;
(void) select (0, (int *)0, (int *)0, (int *)0, &tv);
}
#endif INTERVALTIMER
#ifdef FASTTIMER
#define uucpdelay(num,denom) nap(60*num/denom)
/* Sleep in increments of 60ths of second. */
nap (time)
register int time;
{
static int fd;
if (fd == 0)
fd = open (FASTTIMER, 0);
read (fd, 0, time);
}
#endif FASTTIMER
#ifdef FTIME
#define uucpdelay(num,denom) ftimedelay(1000*num/denom)
ftimedelay(n)
{
static struct timeb loctime;
register i = loctime.millitm;
ftime(&loctime);
while (abs((int)(loctime.millitm - i))<n) ftime(&loctime)
;
}
#endif FTIME
#ifdef BUSYLOOP
#define uucpdelay(num,denom) busyloop(CPUSPEED*num/denom)
#define CPUSPEED 1000000 /* VAX 780 is 1MIPS */
#define DELAY(n) { register long N = (n); while (--N > 0); }
busyloop(n)
{
DELAY(n);
}
#endif BUSYLOOP
slowrite(fd, str)
register char *str;
{
DEBUG(6, "slowrite ", CNULL);
while (*str) {
DEBUG(6, "%c", *str);
uucpdelay(1, 10); /* delay 1/10 second */
write(fd, str, 1);
str++;
}
DEBUG(6, "\n", CNULL);
}
#define BSPEED B150
/*
* send a break
*/
genbrk(fn, bnulls)
register int fn, bnulls;
{
#ifdef USG
if (ioctl(fn, TCSBRK, STBNULL) < 0)
DEBUG(5, "break TCSBRK %s\n", sys_errlist[errno]);
#else !USG
# ifdef TIOCSBRK
if (ioctl(fn, TIOCSBRK, STBNULL) < 0)
DEBUG(5, "break TIOCSBRK %s\n", sys_errlist[errno]);
# ifdef TIOCCBRK
uucpdelay(bnulls, 10);
if (ioctl(fn, TIOCCBRK, STBNULL) < 0)
DEBUG(5, "break TIOCCBRK %s\n", sys_errlist[errno]);
# endif TIOCCBRK
DEBUG(4, "ioctl %f second break\n", (float) bnulls/10 );
# else !TIOCSBRK
struct sgttyb ttbuf;
register int sospeed;
if (ioctl(fn, TIOCGETP, &ttbuf) < 0)
DEBUG(5, "break TIOCGETP %s\n", sys_errlist[errno]);
sospeed = ttbuf.sg_ospeed;
ttbuf.sg_ospeed = BSPEED;
if (ioctl(fn, TIOCSETP, &ttbuf) < 0)
DEBUG(5, "break TIOCSETP %s\n", sys_errlist[errno]);
if (write(fn, "\0\0\0\0\0\0\0\0\0\0\0\0", bnulls) != bnulls) {
badbreak:
logent(sys_errlist[errno], "BAD WRITE genbrk");
alarm(0);
longjmp(Sjbuf, 3);
}
ttbuf.sg_ospeed = sospeed;
if (ioctl(fn, TIOCSETP, &ttbuf) < 0)
DEBUG(5, "break ioctl %s\n", sys_errlist[errno]);
if (write(fn, "@", 1) != 1)
goto badbreak;
DEBUG(4, "sent BREAK nulls - %d\n", bnulls);
#endif !TIOCSBRK
#endif !USG
}
#ifdef DIALINOUT
/* DIALIN/OUT CODE (WLS) */
/*
* disable and reenable: allow a single line to be use for dialin/dialout
*
*/
char enbdev[16];
disable(dev)
register char *dev;
{
register char *rdev;
/* strip off directory prefixes */
rdev = dev;
while (*rdev)
rdev++;
while (--rdev >= dev && *rdev != '/')
;
rdev++;
if (enbdev[0]) {
if (strcmp(enbdev, rdev) == SAME)
return SUCCESS; /* already disabled */
delock(enbdev);
reenable(); /* else, reenable the old one */
}
DEBUG(4, "Disable %s\n", rdev);
if (enbcall("disable", rdev) == FAIL)
return FAIL;
strcpy(enbdev, rdev);
return SUCCESS;
}
reenable()
{
if (enbdev[0] == '\0')
return;
DEBUG(4, "Reenable %s\n", enbdev);
(void) enbcall("enable", enbdev);
enbdev[0] = '\0';
}
enbcall(type, dev)
char *type, *dev;
{
int pid;
register char *p;
int fildes[2];
int status;
FILE *fil;
char buf[80];
fflush(stderr);
fflush(stdout);
pipe(fildes);
if ((pid = fork()) == 0) {
DEBUG(4, DIALINOUT, CNULL);
DEBUG(4, " %s", type);
DEBUG(4, " %s\n", dev);
close(fildes[0]);
close(0); close(1); close(2);
open(_PATH_DEVNULL,0);
dup(fildes[1]); dup(fildes[1]);
setuid(geteuid()); /* for chown(uid()) in acu program */
execl(DIALINOUT, "acu", type, dev, Rmtname, (char *)0);
exit(-1);
}
if (pid<0)
return FAIL;
close(fildes[1]);
fil = fdopen(fildes[0],"r");
if (fil!=NULL) {
#ifdef BSD4_2
setlinebuf(fil);
#endif BSD4_2
while (fgets(buf, sizeof buf, fil) != NULL) {
p = buf + strlen(buf) - 1;
if (*p == '\n')
*p = '\0';
DEBUG(4, "ACUCNTRL: %s\n", buf);
}
}
while(wait(&status) != pid)
;
fclose(fil);
return status ? FAIL : SUCCESS;
}
#endif DIALINOUT