SRI-NOSC/mmdf/dialdir/dlopen.c
# include "rm.h"
# include "rmerr.h"
# include "rmenv.h"
/*
* Local convenience definitions
*/
# define FATAL 1 /* error has no recovery or retry */
# define NONFATAL 2 /* error with success possible with retry */
# define PORTRAW 0341 /* mode of port when interpreting script */
# define PORTCONN 0301 /* port mode during protocol */
/* globals for 'dlopen' */
struct
{
int o_sline; /* number of current line in script file */
int o_nqueue; /* number of characters in 'o_inque' */
char o_slave[LSLAVNAM]; /* slave startup string */
char o_canon[30]; /* translated canonical phone number */
char o_inque[PUSHBACK]; /* queue for pushed back input characters */
char o_portnm[64]; /* name of dial out port */
char o_linebuf[MAXLINE]; /* line buffer for script file */
char *o_inqpt; /* pointer to 'o_inque' */
struct iobuf o_sbuff; /* io buffer for script file */
} d_open;
extern struct rmenv rmenv; /* protocol environment structure */
extern char dialdev[]; /* dialing device name */
/*
* routine which is called to establish a connection with the other
* side.
*/
dlopen(scriptfile, port, logfile, tranfile, debugf, ttylog)
char *scriptfile, *port, *logfile, *tranfile;
int debugf, ttylog;
{
extern int errno;
register int result;
char msgbuff[MAXMESG];
/* set up defaults */
rmenv.d_linelgh = MAXMESG;
rmenv.d_window = WINDOW;
/* start logging and save the port name */
result = d_strtlog(logfile, tranfile, debugf, ttylog);
if (result < 0)
return(result);
cpstr(port, d_open.o_portnm);
/* try to open script file */
if (fopen(scriptfile, &d_open.o_sbuff) < 0)
{
d_log("***** couldn't open script file - errno %d", errno);
return(EUSER);
}
/* now interpret it */
result = d_script();
if (result < 0)
return(result);
/* write the slave startup string on the port and look for a RUN reply */
alarm(MATCHTIM);
result = write(rmenv.d_portfd, d_open.o_slave, length(d_open.o_slave));
alarm (0);
if (result < 0)
{
d_log("***** error writing slave startup string on port - errno %d",
errno);
return(ESYSTEM);
}
result = d_initrecv();
if (result < 0)
return(result);
result = d_initsend();
if (result < 0)
return(result);
return(OK);
}
/*
* this routine is called to close the port and cause the line to
* be hung up
*/
dlclose()
{
if (close(rmenv.d_portfd) < 0)
{
d_log("***** error closing port - errno %d", errno);
return(ESYSTEM);
}
return(OK);
}
/*
* this routine reads the script file and interprets it
*/
d_script()
{
register int nfields, result;
char *fields[MAXFIELDS], sendbuff[MAXLINE];
/* read in lines from the script file and break them up into fields. */
/* a 0 return from the 'parselin' function indicates that a blank */
/* line was encountered, which we'll ignore */
while (1)
{
nfields = parselin(fields, d_open.o_linebuf, &d_open.o_sbuff, MAXLINE, MAXFIELDS);
d_open.o_sline++;
switch (nfields)
{
case 0:
continue;
case -1:
d_serror("unexpected eof");
return(EUSER);
case -2:
d_serror("line too long");
return(EUSER);
case -3:
d_serror("too many fields on line");
return(EUSER);
case -4:
d_serror("quoted string not closed");
return(EUSER);
}
/* now use the code character in field 0 to determine function */
switch (*fields[0])
{
/* get phone number and dial it */
case 'p':
if (nfields != 2)
{
d_serror("2 fields expected for 'p'");
return(EUSER);
}
result = d_illchar(fields[1], d_open.o_canon);
if (result < 0)
return(result);
result = d_dodial(d_open.o_canon);
if (result < 0)
return(result);
continue;
/* get startup string for slave process */
case 'u':
if (nfields != 2)
{
d_serror("2 fields expected for 'u'");
return(EUSER);
}
if (d_canon(fields[1], d_open.o_slave) < 0)
{
d_serror("canonical string error");
return(EUSER);
}
continue;
/* send string to remote system */
case 's':
case 't':
if (nfields != 2)
{
d_serror("2 fields expected for 's' or 't'");
return(EUSER);
}
if (d_canon(fields[1], sendbuff) < 0)
{
d_serror("canonical string error");
return(EUSER);
}
d_dbglog("script: sending '%s'", fields[1]);
result = d_strout(sendbuff);
if (result < 0)
return(result);
continue;
/* watch for string from remote system */
case 'r':
result = d_strwatch(fields, nfields);
if (result < 0)
return(result);
continue;
/* pause in script interpretation to do the file transfers */
case 'g':
if ((result = d_chgport(PORTCONN)) < 0)
return(result);
d_log("script pause - starting protocol");
return(OK);
/* unrecognized script command character */
default:
d_serror("unrecognized script command character");
return(EUSER);
}
}
}
/*
* routine which manages the dialing. it forks off the dialer and then
* trys to open the port. if the open completes without error, we have
* a successful connection. if the call was interrupted by a signal,
* die quietly.
*/
d_dodial(number)
char *number;
{
extern int errno;
register int result;
int pid;
int status;
/* spawn the dialer */
result = d_dialer(number, &pid);
if (result < 0)
return(result);
/* try to open the dialer. if there's and error and it's and interrupt, */
/* then the dialer may have had a problem. check for the right signal */
alarm(MATCHTIM);
rmenv.d_portfd = open(d_open.o_portnm, 2);
alarm (0);
if (rmenv.d_portfd < 0)
if (errno != UEINTR)
{
kill (pid, 9);
d_log("error opening port '%s' errno %d", d_open.o_portnm, errno);
return(ESYSTEM);
}
else
{
alarm(MATCHTIM);
while ((result = waita(&status)) != pid && result != -1);
alarm (0);
if (status.hibyte == SIGIOT)
{
d_log("dial failed");
return(ESYSTEM);
}
else
{
d_log("signal %d interrupted port open call", status.hibyte);
return(ESYSTEM);
}
}
else
if ((result = d_setport(PORTRAW)) < 0)
{
alarm(MATCHTIM);
while ((result = waita(&status)) != pid && result != -1);
alarm (0);
return(result);
}
/* collect the dead child */
alarm(MATCHTIM);
while ((result = waita(&status)) != pid && result != -1);
alarm (0);
d_log("connection established");
}
/*
* this routine spawns a child process to open the acu and do the
* actual dialing of the number. the argument string must have the
* exact format to be passed to the dialer including tandem dialing
* indiactors
*/
d_dialer(number, pid)
char *number;
int *pid; /* so caller can do wait */
{
extern int errno;
extern d_iotcatch();
register int result, try;
int parentpid;
parentpid = getpid();
signal(SIGIOT, d_iotcatch);
/* fork so dial process can proceed in parallel */
/* d_dbglog("dialer: calling number '%s'", number);*/
d_dbglog("dialer: calling number");
*pid = tryfork();
if (*pid < 0)
{
d_log("couldn't fork for dialer");
return(ESYSTEM);
}
/* parent just returns and child does actual dialing */
if (*pid)
return(0);
for (try = 0; try < NDIALS; try++)
{
d_log("Dialing...");
result = d_realdial(number);
if (result == FATAL)
break;
if (try < (NDIALS - 1))
sleep(DIALTIME);
}
/* we can't get through, so we should signal our parents and die */
kill(parentpid, SIGIOT);
exit (-1);
}
/*
* this routine does the real dialing and decoding of errors
*/
dlopsig ()
{
signal (14, &dlopsig);
}
d_realdial(number)
register char *number;
{
register int acu, flag, errsave;
/* open the acu, and if we're successful write the number on it */
signal (14, &dlopsig);
flag = 0;
alarm(MATCHTIM);
/* acu = open("/dev/acu13",2); */
if(dialdev[0]==0){
exit(0);
}
acu = open(dialdev,2);
if (acu != -1)
{
flag = 1;
alarm(MATCHTIM);
if (write(acu,number,length(number)) != -1)
exit();
}
alarm (0);
/* handle all errors. close the acu first so we can return with no */
/* extraneous open files to cause problems with retries */
errsave = errno;
close(acu);
switch (errsave)
{
case UEBUSY:
if (flag)
d_log("That number is busy");
else
d_log("dialer in use");
return(NONFATAL);
case UEDNPWR:
d_log("dialer power off");
return(FATAL);
case UEDNABAN:
d_log("call abandoned");
return(NONFATAL);
case UEDNDIG:
d_log("internal error: bad digit to dialer");
return(FATAL);
default:
d_log("system error #%d", errsave);
return(FATAL);
}
}
/*
* routine to catch iot signals so they'll interrupt open calls to the
* port
*/
d_iotcatch()
{
signal(SIGIOT, d_iotcatch);
}
/*
* this routine interprets the match string line and attempts to
* find the sequence in the input stream
*/
d_strwatch(fields, nfields)
char *fields[];
int nfields;
{
register int timer, val;
char tbuff[80];
if ((nfields < 2) || (nfields > 3))
{
d_serror("line format error");
return(EUSER);
}
if (d_canon(fields[1], tbuff) < 0)
{
d_serror("string format error");
return(EUSER);
}
if (length(tbuff) > PUSHBACK)
{
d_serror("match string too long");
return(EUSER);
}
/* convert the time out value if one is given */
if (nfields == 3)
{
timer = atoi(fields[2]);
if ((timer < 0) || (timer > 300))
{
d_serror("bad timeout value");
return(ETIMEOUT);
}
}
else
timer = MATCHTIM;
/* look for the string. watch for interrupted calls from the alarm */
d_dbglog("strwatch: trying to match '%s'", tbuff);
alarm(timer);
while (1)
{
val = d_match(tbuff);
if (val == -1)
if (d_rawgetc() < 0)
break;
else
continue;
if (val == -2)
break;
alarm(0);
return(OK);
}
/* alarm timeouts come here */
d_log("no match for '%s' after %d seconds", fields[2], timer);
return(EUSER);
}
/*
* routine to translate the external form of a startup string to an
* internal 'canonical' form
*/
d_canon(in,out)
register char *in,*out;
{
extern char *d_escvalue();
register int value;
char c;
/* check for obvious bad format */
if (*in++ != '"')
return(-1);
/* loop through all the characters. lots of special cases */
while (1)
{
c = *in++;
switch (c)
{
default:
*out++ = c;
continue;
case '\0':
return(-1);
case '"':
*out = '\0';
return(0);
case '\\':
/* handle the escapes */
c = *in++;
switch (c)
{
case '"':
*out++ = '"';
continue;
case '\0':
return(-1);
case '\\':
*out++ = '\\';
continue;
case 'r':
*out++ = 015;
continue;
case 'n':
*out++ = 012;
continue;
case 't':
*out++ = 011;
continue;
case 'x':
*out++ = 0377;
continue;
/* allow the specification of characters by up to 3 octal digits after a */
/* backslash */
default:
in = d_escvalue(--in,out++);
}
}
}
}
/*
* this routine does the translation of numerical escape sequences into
* a character with the given octal value
*/
char *d_escvalue(string,out)
register char *string,*out;
{
register int value;
int count;
char c;
value = 0;
c = *string++;
if ((c < '0') || (c > '7'))
{
*out = c;
return(string);
}
value = c & 07;
for (count = 0; count <= 1; count++)
{
c = *string++;
if ((c >= '0') && (c <= '9'))
value = (value << 3) | (c & 07);
else
{
*out = value;
return(--string);
}
}
*out = value;
return(string);
}
/*
* routine which checks for legal characters in a phone number, discards
* extraneous ones, and performs some mapping. it complains about
* illegal ones and number longer than 20 digits or codes.
*/
d_illchar(string,outbuff)
register char *string,*outbuff;
{
register int count;
count = 0;
while (*string)
{
if (count > 20)
{
d_serror("phone number too long");
return(EUSER);
}
if ((*string >= '0') && (*string <= '9'))
{
*outbuff++ = *string++;
count++;
continue;
}
switch (*string)
{
case '(':
case ')':
case '-':
string++;
continue;
case ':':
*outbuff++ = '=';
count++;
string++;
continue;
default:
*outbuff++ = *string++;
count++;
continue;
/* d_serror("unknown character in phone number");
return(EUSER); */
}
}
return(0);
}
/*
* this routine checks for a match between the string and the input
* stream. what it dies is read characters from the input and compare
* them with the string. if a match is not found, all the characters
* that have been read are pushed back. if a match is read, the
* characters are eaten. 1 is returned on a match, -1 on failure.
*/
d_match(string)
char *string;
{
register int c, val;
if (*string == '\0')
return(1);
c = d_rawgetc();
if (c < 0)
return(c);
if (c == *string)
{
val = d_match(++string);
if (val < 0)
{
und_rawgetc(c);
return(val);
}
else
return(1);
}
else
{
und_rawgetc(c);
return(-1);
}
}
/*
* this routine reads a character from the comm line and filters out the
* wierd ones. this routine is called only during script interpretation.
*/
d_rawgetc()
{
extern int errno;
register int result;
char c;
/* if there's anything queued, use it first */
if (d_open.o_nqueue)
{
d_open.o_nqueue--;
return(*d_open.o_inqpt--);
}
/* no such luck. we have to do a read. don't return anything wierd */
while (1)
{
result = read(rmenv.d_portfd, &c, 1);
if (result == 0)
return(ESYSTEM);
if (result < 0)
if (errno == UEINTR)
return(ETIMEOUT);
else
return(ESYSTEM);
c =& 0177;
if (((c < 040) && (c != '\t') && (c != '\n')) || (c == 0177))
continue;
/* write it on the transcript file */
d_tscribe(&c, 1);
return(c);
}
}
/*
* this returns a character to the input queue so he'll get read again
*/
und_rawgetc(c)
char c;
{
/* if none are currently queued, set things up */
if (d_open.o_nqueue == 0)
{
d_open.o_inqpt = d_open.o_inque;
*d_open.o_inqpt = c;
d_open.o_nqueue = 1;
}
else
{
*++d_open.o_inqpt = c;
d_open.o_nqueue++;
}
}
/*
* routine which dumps a string on the port and also interprets the
* sleep characters
*/
d_strout(string)
char *string;
{
int result;
while (*string)
{
if (*string == 0377)
sleep(1);
else
{
alarm(MATCHTIM);
result = write(rmenv.d_portfd, string, 1);
alarm (0);
if (result <= 0)
{
d_log("raw mode port write error");
return(ESYSTEM);
}
}
string++;
}
}
/*
* routine to log errors found while interpreting script file
*/
d_serror(message)
char *message;
{
d_log("***** Script %d: %s", d_open.o_sline, message);
}