#include "uucp.h"
#if USE_RCS_ID
const char serial_rcsid[] = "$Id: serial.c,v 1.78 2002/03/05 19:10:42 ian Rel $";
#endif
#include "uudefs.h"
#include "uuconf.h"
#include "system.h"
#include "conn.h"
#include "sysdep.h"
#include <errno.h>
#include <ctype.h>
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#if HAVE_TLI
#if HAVE_TIUSER_H
#include <tiuser.h>
#else
#if HAVE_XTI_H
#include <xti.h>
#endif
#endif
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 1
#endif
#if HAVE_SYS_IOCTL_H || HAVE_TXADDCD
#include <sys/ioctl.h>
#endif
#if HAVE_SELECT
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif
#if HAVE_TIME_H
#if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif
#if HAVE_STRIP_BUG && HAVE_BSD_TTY
#include <termio.h>
#endif
#if HAVE_SVR4_LOCKFILES
#if MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#endif
#if MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#if ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS
#ifndef major
#define major(i) (((i) >> 8) & 0xff)
#endif
#ifndef minor
#define minor(i) ((i) & 0xff)
#endif
#endif
#endif
#if HAVE_DEV_INFO
#include <sys/dev.h>
#endif
#if HAVE_SYS_TERMIOX_H
#include <sys/termiox.h>
#endif
#ifndef O_NDELAY
#ifdef FNDELAY
#define O_NDELAY FNDELAY
#else
#define O_NDELAY 0
#endif
#endif
#ifndef O_NONBLOCK
#ifdef FNBLOCK
#define O_NONBLOCK FNBLOCK
#else
#define O_NONBLOCK 0
#endif
#endif
#if O_NDELAY == 0 && O_NONBLOCK == 0
#error No way to do nonblocking I/O
#endif
#ifndef EAGAIN
#ifndef EWOULDBLOCK
#define EAGAIN (-1)
#define EWOULDBLOCK (-1)
#else
#define EAGAIN EWOULDBLOCK
#endif
#else
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
#endif
#ifndef ENODATA
#define ENODATA EAGAIN
#endif
#ifndef MAX_INPUT
#define MAX_INPUT (256)
#endif
#ifdef TIOCSINUSE
#define HAVE_TIOCSINUSE 1
#else
#ifdef TIOCEXCL
#define HAVE_TIOCEXCL 1
#endif
#endif
#if HAVE_TLI
extern int t_errno;
extern char *t_errlist[];
extern int t_nerr;
#endif
#ifndef IMAXBEL
#define IMAXBEL 0
#endif
#ifndef PENDIN
#define PENDIN 0
#endif
#if HAVE_SYSV_TERMIO
#define ICLEAR_IFLAG (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK \
| ISTRIP | INLCR | IGNCR | ICRNL | IUCLC \
| IXON | IXANY | IXOFF | IMAXBEL)
#define ICLEAR_OFLAG (OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET \
| OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY \
| VTDLY | FFDLY)
#define ICLEAR_CFLAG (CBAUD | CSIZE | PARENB | PARODD)
#define ISET_CFLAG (CS8 | CREAD | HUPCL)
#define ICLEAR_LFLAG (ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK \
| ECHONL | NOFLSH | PENDIN)
#endif
#if HAVE_POSIX_TERMIOS
#define ICLEAR_IFLAG (BRKINT | ICRNL | IGNBRK | IGNCR | IGNPAR \
| INLCR | INPCK | ISTRIP | IXOFF | IXON \
| PARMRK | IMAXBEL)
#define ICLEAR_OFLAG (OPOST)
#define ICLEAR_CFLAG (CSIZE | PARENB | PARODD)
#define ISET_CFLAG (CS8 | CREAD | HUPCL)
#define ICLEAR_LFLAG (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN \
| ISIG | NOFLSH | TOSTOP | PENDIN)
#endif
enum tclocal_setting
{
SET_CLOCAL,
CLEAR_CLOCAL,
IGNORE_CLOCAL
};
static RETSIGTYPE usalarm P((int isig));
static boolean fsserial_init P((struct sconnection *qconn,
const struct sconncmds *qcmds,
const char *zdevice));
static void usserial_free P((struct sconnection *qconn));
static boolean fsserial_lockfile P((boolean flok,
const struct sconnection *));
static boolean fsserial_lock P((struct sconnection *qconn,
boolean fin, boolean fuser));
static boolean fsserial_unlock P((struct sconnection *qconn));
static boolean fsserial_open P((struct sconnection *qconn, long ibaud,
boolean fwait, boolean fuser,
enum tclocal_setting tlocal));
static boolean fsstdin_open P((struct sconnection *qconn, long ibaud,
boolean fwait, boolean fuser));
static boolean fsmodem_open P((struct sconnection *qconn, long ibaud,
boolean fwait, boolean fuser));
static boolean fsdirect_open P((struct sconnection *qconn, long ibaud,
boolean fwait, boolean fuser));
static boolean fsblock P((struct ssysdep_conn *q, boolean fblock));
static boolean fsserial_close P((struct ssysdep_conn *q));
static boolean fsstdin_close P((struct sconnection *qconn,
pointer puuconf,
struct uuconf_dialer *qdialer,
boolean fsuccess));
static boolean fsmodem_close P((struct sconnection *qconn,
pointer puuconf,
struct uuconf_dialer *qdialer,
boolean fsuccess));
static boolean fsdirect_close P((struct sconnection *qconn,
pointer puuconf,
struct uuconf_dialer *qdialer,
boolean fsuccess));
static boolean fsserial_break P((struct sconnection *qconn));
static boolean fsstdin_break P((struct sconnection *qconn));
static boolean fsserial_set P((struct sconnection *qconn,
enum tparitysetting tparity,
enum tstripsetting tstrip,
enum txonxoffsetting txonxoff));
static boolean fsstdin_set P((struct sconnection *qconn,
enum tparitysetting tparity,
enum tstripsetting tstrip,
enum txonxoffsetting txonxoff));
static boolean fsmodem_carrier P((struct sconnection *qconn,
boolean fcarrier));
static boolean fsserial_hardflow P((struct sconnection *qconn,
boolean fhardflow));
static boolean fsrun_chat P((int oread, int owrite, char **pzprog));
static long isserial_baud P((struct sconnection *qconn));
static const struct sconncmds sstdincmds =
{
usserial_free,
NULL,
NULL,
fsstdin_open,
fsstdin_close,
NULL,
fsdouble_read,
fsdouble_write,
fsysdep_conn_io,
fsstdin_break,
fsstdin_set,
NULL,
fsdouble_chat,
isserial_baud
};
static const struct sconncmds smodemcmds =
{
usserial_free,
fsserial_lock,
fsserial_unlock,
fsmodem_open,
fsmodem_close,
fmodem_dial,
fsysdep_conn_read,
fsysdep_conn_write,
fsysdep_conn_io,
fsserial_break,
fsserial_set,
fsmodem_carrier,
fsysdep_conn_chat,
isserial_baud
};
static const struct sconncmds sdirectcmds =
{
usserial_free,
fsserial_lock,
fsserial_unlock,
fsdirect_open,
fsdirect_close,
NULL,
fsysdep_conn_read,
fsysdep_conn_write,
fsysdep_conn_io,
fsserial_break,
fsserial_set,
NULL,
fsysdep_conn_chat,
isserial_baud
};
static int iSunblock = O_NDELAY | O_NONBLOCK;
volatile sig_atomic_t fSalarm;
static RETSIGTYPE
usalarm (isig)
int isig ATTRIBUTE_UNUSED;
{
#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET
(void) signal (isig, usalarm);
#endif
fSalarm = TRUE;
#if HAVE_RESTARTABLE_SYSCALLS
longjmp (sSjmp_buf, 1);
#else
alarm (1);
#endif
}
#if HAVE_SIGPROCMASK
#define HELD_SIG_MASK sigset_t
static sigset_t isblocksigs P((void));
static sigset_t
isblocksigs ()
{
sigset_t sblock, sold;
(void) (sigemptyset (&sblock));
(void) (sigaddset (&sblock, SIGINT));
(void) (sigaddset (&sblock, SIGQUIT));
(void) (sigaddset (&sblock, SIGTERM));
(void) (sigaddset (&sblock, SIGPIPE));
(void) sigprocmask (SIG_BLOCK, &sblock, &sold);
return sold;
}
#define usunblocksigs(s) \
((void) sigprocmask (SIG_SETMASK, &(s), (sigset_t *) NULL))
#else
#if HAVE_SIGBLOCK
#define HELD_SIG_MASK int
#ifndef sigmask
#define sigmask(i) (1 << ((i) - 1))
#endif
#define isblocksigs() \
sigblock (sigmask (SIGINT) | sigmask (SIGQUIT) \
| sigmask (SIGTERM) | sigmask (SIGPIPE))
#define usunblocksigs(i) ((void) sigsetmask (i))
#else
#if HAVE_SIGHOLD
#define HELD_SIG_MASK int
static int isblocksigs P((void));
static int
isblocksigs ()
{
sighold (SIGINT);
sighold (SIGQUIT);
sighold (SIGTERM);
sighold (SIGPIPE);
return 0;
}
static void usunblocksigs P((int));
static void
usunblocksigs (i)
int i;
{
sigrelse (SIGINT);
sigrelse (SIGQUIT);
sigrelse (SIGTERM);
sigrelse (SIGPIPE);
}
#else
#define HELD_SIG_MASK int
#define isblocksigs() 0
#define usunblocksigs(i)
#endif
#endif
#endif
static boolean
fsserial_init (qconn, qcmds, zdevice)
struct sconnection *qconn;
const struct sconncmds *qcmds;
const char *zdevice;
{
struct ssysdep_conn *q;
q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
if (zdevice == NULL
&& qconn->qport != NULL
&& qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)
zdevice = qconn->qport->uuconf_zname;
if (zdevice == NULL)
q->zdevice = NULL;
else if (*zdevice == '/')
q->zdevice = zbufcpy (zdevice);
else
{
size_t clen;
clen = strlen (zdevice);
q->zdevice = zbufalc (sizeof "/dev/" + clen);
memcpy (q->zdevice, "/dev/", sizeof "/dev/" - 1);
memcpy (q->zdevice + sizeof "/dev/" - 1, zdevice, clen);
q->zdevice[sizeof "/dev/" + clen - 1] = '\0';
}
q->o = -1;
q->ord = -1;
q->owr = -1;
q->ftli = FALSE;
qconn->psysdep = (pointer) q;
qconn->qcmds = qcmds;
return TRUE;
}
boolean
fsysdep_stdin_init (qconn)
struct sconnection *qconn;
{
(void) chmod ("/dev/tty", S_IRUSR | S_IWUSR);
return fsserial_init (qconn, &sstdincmds, (const char *) NULL);
}
boolean
fsysdep_modem_init (qconn)
struct sconnection *qconn;
{
return fsserial_init (qconn, &smodemcmds,
qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdevice);
}
boolean
fsysdep_direct_init (qconn)
struct sconnection *qconn;
{
return fsserial_init (qconn, &sdirectcmds,
qconn->qport->uuconf_u.uuconf_sdirect.uuconf_zdevice);
}
static void
usserial_free (qconn)
struct sconnection *qconn;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
ubuffree (qsysdep->zdevice);
xfree ((pointer) qsysdep);
qconn->psysdep = NULL;
}
#if HAVE_SEQUENT_LOCKFILES
#define LCK_TEMPLATE "LCK..tty"
#else
#define LCK_TEMPLATE "LCK.."
#endif
static boolean
fsserial_lockfile (flok, qconn)
boolean flok;
const struct sconnection *qconn;
{
struct ssysdep_conn *qsysdep;
const char *z;
char *zalc;
boolean fret;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
if (qconn->qport == NULL)
z = NULL;
else
z = qconn->qport->uuconf_zlockname;
zalc = NULL;
if (z == NULL)
{
#if HAVE_QNX_LOCKFILES
{
nid_t idevice_nid;
char abdevice_nid[13];
size_t cdevice_nid;
const char *zbase;
size_t clen;
if (qsysdep->zdevice[0] == '/' && qsysdep->zdevice[1] == '/')
idevice_nid = (nid_t) strtol (qsysdep->zdevice + 2,
(char **) NULL, 10);
else
idevice_nid = getnid ();
sprintf (abdevice_nid, "%ld.", (long) idevice_nid);
cdevice_nid = strlen (abdevice_nid);
zbase = strrchr (qsysdep->zdevice, '/') + 1;
clen = strlen (zbase);
zalc = zbufalc (sizeof LCK_TEMPLATE + cdevice_nid + clen);
memcpy (zalc, LCK_TEMPLATE, sizeof LCK_TEMPLATE - 1);
memcpy (zalc + sizeof LCK_TEMPLATE - 1, abdevice_nid, cdevice_nid);
memcpy (zalc + sizeof LCK_TEMPLATE - 1 + cdevice_nid,
zbase, clen + 1);
z = zalc;
}
#else
#if ! HAVE_SVR4_LOCKFILES
{
const char *zbase;
size_t clen;
zbase = strrchr (qsysdep->zdevice, '/') + 1;
clen = strlen (zbase);
zalc = zbufalc (sizeof LCK_TEMPLATE + clen);
memcpy (zalc, LCK_TEMPLATE, sizeof LCK_TEMPLATE - 1);
memcpy (zalc + sizeof LCK_TEMPLATE - 1, zbase, clen + 1);
#if HAVE_SCO_LOCKFILES
{
char *zl;
zl = zalc + sizeof LCK_TEMPLATE + clen - 2;
if (isupper (*zl))
*zl = tolower (*zl);
}
#endif
z = zalc;
}
#else
{
struct stat s;
if (stat (qsysdep->zdevice, &s) != 0)
{
ulog (LOG_ERROR, "stat (%s): %s", qsysdep->zdevice,
strerror (errno));
return FALSE;
}
zalc = zbufalc (sizeof "LK.1234567890.1234567890.1234567890");
sprintf (zalc, "LK.%03d.%03d.%03d", major (s.st_dev),
major (s.st_rdev), minor (s.st_rdev));
z = zalc;
}
#endif
#endif
}
if (flok)
fret = fsdo_lock (z, FALSE, (boolean *) NULL);
else
fret = fsdo_unlock (z, FALSE);
#if HAVE_COHERENT_LOCKFILES
if (fret)
{
if (flok)
{
if (lockttyexist (z + sizeof LCK_TEMPLATE - 1))
{
ulog (LOG_NORMAL, "%s: port already locked",
z + sizeof LCK_TEMPLATE - 1);
fret = FALSE;
}
else
fret = fscoherent_disable_tty (z + sizeof LCK_TEMPLATE - 1,
&qsysdep->zenable);
}
else
{
fret = TRUE;
if (qsysdep->zenable != NULL)
{
const char *azargs[3];
int aidescs[3];
pid_t ipid;
azargs[0] = "/etc/enable";
azargs[1] = qsysdep->zenable;
azargs[2] = NULL;
aidescs[0] = SPAWN_NULL;
aidescs[1] = SPAWN_NULL;
aidescs[2] = SPAWN_NULL;
ipid = ixsspawn (azargs, aidescs, TRUE, FALSE,
(const char *) NULL, TRUE, TRUE,
(const char *) NULL, (const char *) NULL,
(const char *) NULL);
if (ipid < 0)
{
ulog (LOG_ERROR, "ixsspawn (/etc/enable %s): %s",
qsysdep->zenable, strerror (errno));
fret = FALSE;
}
else
{
if (ixswait ((unsigned long) ipid, (const char *) NULL)
== 0)
fret = TRUE;
else
fret = FALSE;
}
ubuffree (qsysdep->zenable);
qsysdep->zenable = NULL;
}
}
}
#endif
ubuffree (zalc);
return fret;
}
static boolean
fsserial_lock (qconn, fin, fuser)
struct sconnection *qconn;
boolean fin;
boolean fuser;
{
if (! fsserial_lockfile (TRUE, qconn))
return FALSE;
#if HAVE_TIOCSINUSE || HAVE_TIOCEXCL || HAVE_DEV_INFO
{
struct ssysdep_conn *qsysdep;
int iflag;
uid_t ieuid;
gid_t iegid;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
if (fin)
iflag = 0;
else
iflag = iSunblock;
if (fuser)
{
if (! fsuser_perms (&ieuid, &iegid))
{
(void) fsserial_lockfile (FALSE, qconn);
return FALSE;
}
}
qsysdep->o = open (qsysdep->zdevice, O_RDWR | iflag);
if (qsysdep->o < 0)
{
#if O_NONBLOCK != 0
if (! fin && iSunblock != O_NONBLOCK && errno == EINVAL)
{
iSunblock = O_NONBLOCK;
qsysdep->o = open (qsysdep->zdevice,
O_RDWR | O_NONBLOCK);
}
#endif
if (qsysdep->o < 0)
{
int ierr;
ierr = errno;
if (fuser)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
if (ierr != EBUSY)
ulog (LOG_ERROR, "open (%s): %s", qsysdep->zdevice,
strerror (ierr));
(void) fsserial_lockfile (FALSE, qconn);
return FALSE;
}
}
if (fuser)
{
if (! fsuucp_perms ((long) ieuid, (long) iegid))
{
(void) close (qsysdep->o);
qsysdep->o = -1;
(void) fsserial_lockfile (FALSE, qconn);
return FALSE;
}
}
#if HAVE_TIOCSINUSE
if (ioctl (qsysdep->o, TIOCSINUSE, 0) < 0)
{
if (errno != EALREADY)
ulog (LOG_ERROR, "ioctl (TIOCSINUSE): %s", strerror (errno));
#ifdef TIOCNOTTY
(void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
#endif
(void) close (qsysdep->o);
qsysdep->o = -1;
(void) fsserial_lockfile (FALSE, qconn);
return FALSE;
}
#endif
#if HAVE_DEV_INFO
{
struct _dev_info_entry sdevinfo;
if (dev_info (qsysdep->o, &sdevinfo) == -1)
{
ulog (LOG_ERROR, "dev_info: %s", strerror (errno));
sdevinfo.open_count = 2;
}
if (sdevinfo.open_count != 1)
{
#ifdef TIOCNOTTY
(void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
#endif
(void) close (qsysdep->o);
qsysdep->o = -1;
(void) fsserial_lockfile (FALSE, qconn);
return FALSE;
}
}
#endif
if (fcntl (qsysdep->o, F_SETFD,
fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
#ifdef TIOCNOTTY
(void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
#endif
(void) close (qsysdep->o);
qsysdep->o = -1;
(void) fsserial_lockfile (FALSE, qconn);
return FALSE;
}
}
#endif
return TRUE;
}
static boolean
fsserial_unlock (qconn)
struct sconnection *qconn;
{
boolean fret;
struct ssysdep_conn *qsysdep;
fret = TRUE;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
if (qsysdep->o >= 0)
{
#ifdef TIOCNOTTY
(void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
#endif
if (close (qsysdep->o) < 0)
{
ulog (LOG_ERROR, "close: %s", strerror (errno));
fret = FALSE;
}
qsysdep->o = -1;
}
if (! fsserial_lockfile (FALSE, qconn))
fret = FALSE;
return fret;
}
#if HAVE_POSIX_TERMIOS
typedef speed_t baud_code;
#else
typedef int baud_code;
#endif
static struct sbaud_table
{
baud_code icode;
long ibaud;
} asSbaud_table[] =
{
{ B50, 50 },
{ B75, 75 },
{ B110, 110 },
{ B134, 134 },
{ B150, 150 },
{ B200, 200 },
{ B300, 300 },
{ B600, 600 },
{ B1200, 1200 },
{ B1800, 1800 },
{ B2400, 2400 },
{ B4800, 4800 },
{ B9600, 9600 },
#ifdef B19200
{ B19200, 19200 },
#else
#ifdef EXTA
{ EXTA, 19200 },
#endif
#endif
#ifdef B38400
{ B38400, 38400 },
#else
#ifdef EXTB
{ EXTB, 38400 },
#endif
#endif
#ifdef B57600
{ B57600, 57600 },
#endif
#ifdef B76800
{ B76800, 76800 },
#endif
#ifdef B115200
{ B115200, 115200 },
#endif
#ifdef B230400
{ B230400, 230400 },
#else
#ifdef _B230400
{ _B230400, 230400 },
#endif
#endif
#ifdef B460800
{ B460800, 460800 },
#else
#ifdef _B460800
{ _B460800, 460800 },
#endif
#endif
#ifdef B500000
{ B500000, 500000 },
#endif
#ifdef B576000
{ B576000, 576000 },
#endif
#ifdef B921600
{ B921600, 921600 },
#endif
#ifdef B1000000
{ B1000000, 1000000 },
#endif
#ifdef B1152000
{ B1152000, 1152000 },
#endif
#ifdef B1500000
{ B1500000, 1500000 },
#endif
#ifdef B2000000
{ B2000000, 2000000 },
#endif
#ifdef B2500000
{ B2500000, 2500000 },
#endif
#ifdef B3000000
{ B3000000, 3000000 },
#endif
#ifdef B3500000
{ B3500000, 3500000 },
#endif
#ifdef B4000000
{ B4000000, 4000000 },
#endif
{ B0, 0 }
};
#define CBAUD_TABLE (sizeof asSbaud_table / sizeof asSbaud_table[0])
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
static int cSmin;
#endif
static boolean
fsserial_open (qconn, ibaud, fwait, fuser, tlocal)
struct sconnection *qconn;
long ibaud;
boolean fwait;
boolean fuser;
enum tclocal_setting tlocal;
{
struct ssysdep_conn *q;
baud_code ib;
q = (struct ssysdep_conn *) qconn->psysdep;
if (q->zdevice != NULL)
{
#if LOG_DEVICE_PREFIX
ulog_device (q->zdevice);
#else
const char *z;
if (strncmp (q->zdevice, "/dev/", sizeof "/dev/" - 1) == 0)
z = q->zdevice + sizeof "/dev/" - 1;
else
z = q->zdevice;
ulog_device (z);
#endif
}
else
{
const char *zport;
boolean fdummy;
#if DEBUG > 0
if (qconn->qport != NULL &&
qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)
ulog (LOG_FATAL, "fsserial_open: Can't happen");
#endif
zport = zsysdep_port_name (&fdummy);
if (zport != NULL)
ulog_device (zport);
}
ib = B0;
if (ibaud != 0)
{
size_t i;
for (i = 0; i < CBAUD_TABLE; i++)
if (asSbaud_table[i].ibaud == ibaud)
break;
if (i >= CBAUD_TABLE)
{
ulog (LOG_ERROR, "Unsupported baud rate %ld", ibaud);
return FALSE;
}
ib = asSbaud_table[i].icode;
}
if (q->o < 0)
{
int iflag;
uid_t ieuid;
gid_t iegid;
if (fwait)
iflag = 0;
else
iflag = iSunblock;
if (fuser)
{
if (! fsuser_perms (&ieuid, &iegid))
return FALSE;
}
q->o = open (q->zdevice, O_RDWR | iflag);
if (q->o < 0)
{
#if O_NONBLOCK != 0
if (! fwait && iSunblock != O_NONBLOCK && errno == EINVAL)
{
iSunblock = O_NONBLOCK;
q->o = open (q->zdevice, O_RDWR | O_NONBLOCK);
}
#endif
if (q->o < 0)
{
int ierr;
ierr = errno;
if (fuser)
(void) fsuucp_perms ((long) ieuid, (long) iegid);
ulog (LOG_ERROR, "open (%s): %s", q->zdevice,
strerror (ierr));
return FALSE;
}
}
if (fuser)
{
if (! fsuucp_perms ((long) ieuid, (long) iegid))
return FALSE;
}
if (fcntl (q->o, F_SETFD, fcntl (q->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
return FALSE;
}
}
q->iflags = fcntl (q->o, F_GETFL, 0);
if (q->iflags < 0)
{
ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
return FALSE;
}
q->iwr_flags = -1;
if (! fsblock (q, TRUE))
return FALSE;
if (! fgetterminfo (q->o, &q->sorig))
{
q->fterminal = FALSE;
return TRUE;
}
q->fterminal = TRUE;
q->snew = q->sorig;
#if HAVE_BSD_TTY
q->snew.stty.sg_flags = RAW | ANYP;
if (ibaud == 0)
ib = q->snew.stty.sg_ospeed;
else
{
q->snew.stty.sg_ispeed = ib;
q->snew.stty.sg_ospeed = ib;
}
q->snew.stchars.t_intrc = -1;
q->snew.stchars.t_quitc = -1;
q->snew.stchars.t_eofc = -1;
q->snew.stchars.t_brkc = -1;
q->snew.sltchars.t_suspc = -1;
q->snew.sltchars.t_rprntc = -1;
q->snew.sltchars.t_dsuspc = -1;
q->snew.sltchars.t_flushc = -1;
q->snew.sltchars.t_werasc = -1;
q->snew.sltchars.t_lnextc = -1;
#ifdef NTTYDISC
{
int iparam;
if (ioctl (q->o, TIOCGETD, &iparam) >= 0
&& iparam != NTTYDISC)
{
iparam = NTTYDISC;
(void) ioctl (q->o, TIOCSETD, &iparam);
}
}
#endif
#ifdef TIOCHPCL
(void) ioctl (q->o, TIOCHPCL, 0);
#endif
#ifdef TIOCFLUSH
{
int iparam;
#ifdef FREAD
iparam = FREAD;
#else
iparam = 0;
#endif
(void) ioctl (q->o, TIOCFLUSH, &iparam);
}
#endif
#endif
#if HAVE_SYSV_TERMIO
if (ibaud == 0)
ib = q->snew.c_cflag & CBAUD;
q->snew.c_iflag &=~ ICLEAR_IFLAG;
q->snew.c_oflag &=~ ICLEAR_OFLAG;
q->snew.c_cflag &=~ ICLEAR_CFLAG;
q->snew.c_cflag |= ib | ISET_CFLAG;
q->snew.c_lflag &=~ ICLEAR_LFLAG;
cSmin = 1;
q->snew.c_cc[VMIN] = cSmin;
q->snew.c_cc[VTIME] = 1;
#ifdef TCFLSH
(void) ioctl (q->o, TCFLSH, 0);
#endif
#endif
#if HAVE_POSIX_TERMIOS
if (ibaud == 0)
ib = cfgetospeed (&q->snew);
q->snew.c_iflag &=~ ICLEAR_IFLAG;
q->snew.c_oflag &=~ ICLEAR_OFLAG;
q->snew.c_cflag &=~ ICLEAR_CFLAG;
q->snew.c_cflag |= ISET_CFLAG;
q->snew.c_lflag &=~ ICLEAR_LFLAG;
cSmin = 1;
q->snew.c_cc[VMIN] = cSmin;
q->snew.c_cc[VTIME] = 1;
(void) cfsetospeed (&q->snew, ib);
(void) cfsetispeed (&q->snew, ib);
(void) tcflush (q->o, TCIFLUSH);
#endif
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
switch (tlocal)
{
case SET_CLOCAL:
q->snew.c_cflag |= CLOCAL;
break;
case CLEAR_CLOCAL:
q->snew.c_cflag &=~ CLOCAL;
break;
case IGNORE_CLOCAL:
break;
}
#endif
if (! fsetterminfo (q->o, &q->snew))
{
ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno));
return FALSE;
}
#ifdef TIOCSCTTY
(void) ioctl (q->o, TIOCSCTTY, 0);
#endif
if (ibaud != 0)
q->ibaud = ibaud;
else
{
size_t i;
q->ibaud = (long) 1200;
for (i = 0; i < CBAUD_TABLE; i++)
{
if (asSbaud_table[i].icode == ib
&& asSbaud_table[i].ibaud != 0)
{
q->ibaud = asSbaud_table[i].ibaud;
break;
}
}
DEBUG_MESSAGE1 (DEBUG_PORT,
"fsserial_open: Baud rate is %ld", q->ibaud);
}
return TRUE;
}
static boolean
fsstdin_open (qconn, ibaud, fwait, fuser)
struct sconnection *qconn;
long ibaud;
boolean fwait;
boolean fuser;
{
struct ssysdep_conn *q;
q = (struct ssysdep_conn *) qconn->psysdep;
q->ord = 0;
q->owr = 1;
q->o = q->ord;
if (! fsserial_open (qconn, ibaud, fwait, fuser, IGNORE_CLOCAL))
return FALSE;
q->iwr_flags = fcntl (q->owr, F_GETFL, 0);
if (q->iwr_flags < 0)
{
ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
return FALSE;
}
return TRUE;
}
static boolean
fsmodem_open (qconn, ibaud, fwait, fuser)
struct sconnection *qconn;
long ibaud;
boolean fwait;
boolean fuser;
{
struct uuconf_modem_port *qm;
qm = &qconn->qport->uuconf_u.uuconf_smodem;
if (ibaud == (long) 0)
ibaud = qm->uuconf_ibaud;
if (! fsserial_open (qconn, ibaud, fwait, fuser,
fwait ? CLEAR_CLOCAL : SET_CLOCAL))
return FALSE;
if (fwait
&& ! fsserial_hardflow (qconn, qm->uuconf_fhardflow))
return FALSE;
return TRUE;
}
static boolean
fsdirect_open (qconn, ibaud, fwait, fuser)
struct sconnection *qconn;
long ibaud;
boolean fwait;
boolean fuser;
{
struct uuconf_direct_port *qd;
qd = &qconn->qport->uuconf_u.uuconf_sdirect;
if (ibaud == (long) 0)
ibaud = qd->uuconf_ibaud;
if (! fsserial_open (qconn, ibaud, fwait, fuser,
qd->uuconf_fcarrier ? CLEAR_CLOCAL : SET_CLOCAL))
return FALSE;
return fsserial_hardflow (qconn, qd->uuconf_fhardflow);
}
static boolean
fsblock (qs, fblock)
struct ssysdep_conn *qs;
boolean fblock;
{
int iwant;
int isys;
if (fblock)
iwant = qs->iflags &~ (O_NDELAY | O_NONBLOCK);
else
iwant = qs->iflags | iSunblock;
if (iwant == qs->iflags)
return TRUE;
isys = fcntl (qs->o, F_SETFL, iwant);
if (isys < 0)
{
#if O_NONBLOCK != 0
if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL)
{
iSunblock = O_NONBLOCK;
iwant = qs->iflags | O_NONBLOCK;
isys = fcntl (qs->o, F_SETFL, iwant);
}
#endif
if (isys < 0)
{
ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
return FALSE;
}
}
qs->iflags = iwant;
if (qs->iwr_flags >= 0 && qs->ord != qs->owr)
{
if (fblock)
iwant = qs->iwr_flags &~ (O_NDELAY | O_NONBLOCK);
else
iwant = qs->iwr_flags | iSunblock;
isys = fcntl (qs->owr, F_SETFL, iwant);
if (isys < 0)
{
#if O_NONBLOCK != 0
if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL)
{
iSunblock = O_NONBLOCK;
iwant = qs->iwr_flags | O_NONBLOCK;
isys = fcntl (qs->owr, F_SETFL, iwant);
}
#endif
if (isys < 0)
{
ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
return FALSE;
}
}
qs->iwr_flags = iwant;
}
return TRUE;
}
static boolean
fsserial_close (q)
struct ssysdep_conn *q;
{
if (q->o >= 0)
{
if (q->fterminal)
{
fSalarm = FALSE;
if (fsysdep_catch ())
{
usysdep_start_catch ();
usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
(void) alarm (30);
(void) fsetterminfodrain (q->o, &q->sorig);
}
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
(void) alarm (0);
usysdep_end_catch ();
if (fSalarm)
(void) fsetterminfo (q->o, &q->sorig);
}
#ifdef TIOCNOTTY
(void) ioctl (q->o, TIOCNOTTY, (char *) NULL);
#endif
(void) close (q->o);
q->o = -1;
sleep (2);
}
return TRUE;
}
static boolean
fsstdin_close (qconn, puuconf, qdialer, fsuccess)
struct sconnection *qconn;
pointer puuconf ATTRIBUTE_UNUSED;
struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED;
boolean fsuccess ATTRIBUTE_UNUSED;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
(void) close (qsysdep->owr);
(void) close (2);
qsysdep->o = qsysdep->ord;
return fsserial_close (qsysdep);
}
static boolean
fsmodem_close (qconn, puuconf, qdialer, fsuccess)
struct sconnection *qconn;
pointer puuconf;
struct uuconf_dialer *qdialer;
boolean fsuccess;
{
struct ssysdep_conn *qsysdep;
boolean fret;
struct uuconf_dialer sdialer;
const struct uuconf_chat *qchat;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
fret = TRUE;
if (qdialer == NULL)
{
if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL)
{
const char *zdialer;
int iuuconf;
zdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0];
iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer);
if (iuuconf == UUCONF_SUCCESS)
qdialer = &sdialer;
else
{
ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
fret = FALSE;
}
}
else
qdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer;
}
qchat = NULL;
if (qdialer != NULL)
{
if (fsuccess)
qchat = &qdialer->uuconf_scomplete;
else
qchat = &qdialer->uuconf_sabort;
}
if (qchat != NULL
&& (qchat->uuconf_pzprogram != NULL
|| qchat->uuconf_pzchat != NULL))
{
boolean fsighup_ignored;
HELD_SIG_MASK smask;
int i;
sig_atomic_t afhold[INDEXSIG_COUNT];
(void) fsmodem_carrier (qconn, FALSE);
usset_signal (SIGHUP, SIG_IGN, FALSE, &fsighup_ignored);
smask = isblocksigs ();
for (i = 0; i < INDEXSIG_COUNT; i++)
{
afhold[i] = afSignal[i];
afSignal[i] = FALSE;
}
usunblocksigs (smask);
if (! fchat (qconn, puuconf, qchat, (const struct uuconf_system *) NULL,
(const struct uuconf_dialer *) NULL, (const char *) NULL,
FALSE, qconn->qport->uuconf_zname,
qsysdep->ibaud))
fret = FALSE;
for (i = 0; i < INDEXSIG_COUNT; i++)
if (afhold[i])
afSignal[i] = TRUE;
if (! fsighup_ignored)
usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL);
}
if (qdialer != NULL
&& qdialer == &sdialer)
(void) uuconf_dialer_free (puuconf, &sdialer);
#if ! HAVE_RESET_BUG
if (qsysdep->fterminal)
{
#if HAVE_BSD_TTY
qsysdep->snew.stty.sg_ispeed = B0;
qsysdep->snew.stty.sg_ospeed = B0;
#endif
#if HAVE_SYSV_TERMIO
qsysdep->snew.c_cflag = (qsysdep->snew.c_cflag &~ CBAUD) | B0;
#endif
#if HAVE_POSIX_TERMIOS
(void) cfsetospeed (&qsysdep->snew, B0);
#endif
fSalarm = FALSE;
if (fsysdep_catch ())
{
usysdep_start_catch ();
usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
(void) alarm (30);
(void) fsetterminfodrain (qsysdep->o, &qsysdep->snew);
}
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
(void) alarm (0);
usysdep_end_catch ();
sleep (2);
}
#endif
if (! fsserial_close (qsysdep))
fret = FALSE;
return fret;
}
static boolean
fsdirect_close (qconn, puuconf, qdialer, fsuccess)
struct sconnection *qconn;
pointer puuconf ATTRIBUTE_UNUSED;
struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED;
boolean fsuccess ATTRIBUTE_UNUSED;
{
return fsserial_close ((struct ssysdep_conn *) qconn->psysdep);
}
boolean
fsysdep_modem_begin_dial (qconn, qdial)
struct sconnection *qconn;
struct uuconf_dialer *qdial;
{
struct ssysdep_conn *qsysdep;
const char *z;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
#ifdef TIOCMODEM
{
int iperm;
iperm = 0;
(void) ioctl (qsysdep->o, TIOCMODEM, &iperm);
}
#endif
if (qdial->uuconf_fdtr_toggle)
{
#ifdef TIOCCDTR
(void) ioctl (qsysdep->o, TIOCCDTR, 0);
sleep (2);
(void) ioctl (qsysdep->o, TIOCSDTR, 0);
#else
if (qsysdep->fterminal)
{
sterminal sbaud;
sbaud = qsysdep->snew;
#if HAVE_BSD_TTY
sbaud.stty.sg_ispeed = B0;
sbaud.stty.sg_ospeed = B0;
#endif
#if HAVE_SYSV_TERMIO
sbaud.c_cflag = (sbaud.c_cflag &~ CBAUD) | B0;
#endif
#if HAVE_POSIX_TERMIOS
(void) cfsetospeed (&sbaud, B0);
#endif
(void) fsetterminfodrain (qsysdep->o, &sbaud);
sleep (2);
(void) fsetterminfo (qsysdep->o, &qsysdep->snew);
}
#endif
if (qdial->uuconf_fdtr_toggle_wait)
sleep (2);
}
if (! fsmodem_carrier (qconn, FALSE))
return FALSE;
z = qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device;
if (z != NULL)
{
char *zfree;
int o;
qsysdep->ohold = qsysdep->o;
zfree = NULL;
if (*z != '/')
{
zfree = zbufalc (sizeof "/dev/" + strlen (z));
sprintf (zfree, "/dev/%s", z);
z = zfree;
}
o = open ((char *) z, O_RDWR | O_NOCTTY);
if (o < 0)
{
ulog (LOG_ERROR, "open (%s): %s", z, strerror (errno));
ubuffree (zfree);
return FALSE;
}
ubuffree (zfree);
if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
(void) close (o);
return FALSE;
}
qsysdep->o = o;
}
return TRUE;
}
static boolean
fsmodem_carrier (qconn, fcarrier)
struct sconnection *qconn;
boolean fcarrier;
{
register struct ssysdep_conn *q;
struct uuconf_modem_port *qm;
q = (struct ssysdep_conn *) qconn->psysdep;
if (! q->fterminal)
return TRUE;
qm = &qconn->qport->uuconf_u.uuconf_smodem;
if (fcarrier)
{
if (qm->uuconf_fcarrier)
{
#ifdef TIOCCAR
if (ioctl (q->o, TIOCCAR, 0) < 0)
{
ulog (LOG_ERROR, "ioctl (TIOCCAR): %s", strerror (errno));
return FALSE;
}
#endif
#if HAVE_BSD_TTY
#ifdef LNOMDM
{
int iparam;
iparam = LNOMDM;
if (ioctl (q->o, TIOCLBIC, &iparam) < 0)
{
ulog (LOG_ERROR, "ioctl (TIOCLBIC, LNOMDM): %s",
strerror (errno));
return FALSE;
}
}
#endif
#endif
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
q->snew.c_cflag &=~ CLOCAL;
if (! fsetterminfo (q->o, &q->snew))
{
ulog (LOG_ERROR, "Can't clear CLOCAL: %s", strerror (errno));
return FALSE;
}
#endif
}
if (! fsserial_hardflow (qconn, qm->uuconf_fhardflow))
return FALSE;
}
else
{
if (! fsserial_hardflow (qconn, FALSE))
return FALSE;
#ifdef TIOCNCAR
if (ioctl (q->o, TIOCNCAR, 0) < 0)
{
ulog (LOG_ERROR, "ioctl (TIOCNCAR): %s", strerror (errno));
return FALSE;
}
#endif
#if HAVE_BSD_TTY
#ifdef LNOMDM
{
int iparam;
iparam = LNOMDM;
if (ioctl (q->o, TIOCLBIS, &iparam) < 0)
{
ulog (LOG_ERROR, "ioctl (TIOCLBIS, LNOMDM): %s",
strerror (errno));
return FALSE;
}
}
#endif
#endif
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
q->snew.c_cflag |= CLOCAL;
if (! fsetterminfo (q->o, &q->snew))
{
ulog (LOG_ERROR, "Can't set CLOCAL: %s", strerror (errno));
return FALSE;
}
#if HAVE_CLOCAL_BUG
{
int onew;
onew = open (q->zdevice, O_RDWR);
if (onew < 0)
{
ulog (LOG_ERROR, "open (%s): %s", q->zdevice, strerror (errno));
return FALSE;
}
if (fcntl (onew, F_SETFD,
fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
(void) close (onew);
return FALSE;
}
(void) close (q->o);
q->o = onew;
}
#endif
#endif
}
return TRUE;
}
static boolean
fsserial_hardflow (qconn, fhardflow)
struct sconnection *qconn;
boolean fhardflow;
{
register struct ssysdep_conn *q;
q = (struct ssysdep_conn *) qconn->psysdep;
if (! q->fterminal)
return TRUE;
#if HAVE_BSD_TTY
#define HAVE_HARDFLOW 0
#endif
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
#if ! HAVE_TXADDCD
#ifndef CRTSFL
#ifndef CRTSCTS
#ifndef CTSCD
#ifndef CCTS_OFLOW
#ifndef IRTS
#define HAVE_HARDFLOW 0
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#ifndef HAVE_HARDFLOW
#define HAVE_HARDFLOW 1
#endif
#if HAVE_HARDFLOW
if (fhardflow)
{
#if HAVE_TXADDCD
(void) ioctl (q->o, TXADDCD, "rts");
#else
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
#ifdef CRTSFL
q->snew.c_cflag |= CRTSFL;
q->snew.c_cflag &=~ (RTSFLOW | CTSFLOW);
#endif
#ifdef CRTSCTS
q->snew.c_cflag |= CRTSCTS;
#endif
#ifdef CRTSXOFF
q->snew.c_cflag |= CRTSXOFF;
#endif
#ifdef CTSCD
q->snew.c_cflag |= CTSCD;
#endif
#ifdef CCTS_OFLOW
q->snew.c_cflag |= CCTS_OFLOW | CRTS_IFLOW;
#endif
#ifdef IRTS
q->snew.c_iflag |= IRTS;
#endif
#endif
if (! fsetterminfo (q->o, &q->snew))
{
ulog (LOG_ERROR, "Can't enable hardware flow control: %s",
strerror (errno));
return FALSE;
}
#if HAVE_SYS_TERMIOX
#ifdef TCGETX
{
struct termiox tx;
if (ioctl (q->o, TCGETX, &tx) < 0)
{
ulog (LOG_ERROR,
"Can't enable hardware flow control: ioctl (TCGETX): %s",
strerror (errno));
return FALSE;
}
tx.x_hflag |= RTSXOFF | CTSXON;
if (ioctl (q->o, TCSETX, &tx) < 0)
{
ulog (LOG_ERROR,
"Can't enable hardware flow control: ioctl (TCSETX): %s",
strerror (errno));
return FALSE;
}
}
#endif
#endif
#endif
}
else
{
#if HAVE_TXADDCD
(void) ioctl (q->o, TXDELCD, "rts");
#else
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
#ifdef CRTSFL
q->snew.c_cflag &=~ CRTSFL;
q->snew.c_cflag &=~ (RTSFLOW | CTSFLOW);
#endif
#ifdef CRTSCTS
q->snew.c_cflag &=~ CRTSCTS;
#endif
#ifdef CRTSXOFF
q->snew.c_cflag &=~ CRTSXOFF;
#endif
#ifdef CTSCD
q->snew.c_cflag &=~ CTSCD;
#endif
#ifdef CCTS_OFLOW
q->snew.c_cflag &=~ (CCTS_OFLOW | CRTS_IFLOW);
#endif
#ifdef IRTS
q->snew.c_iflag &=~ IRTS;
#endif
#endif
if (! fsetterminfo (q->o, &q->snew))
{
ulog (LOG_ERROR, "Can't disable hardware flow control: %s",
strerror (errno));
return FALSE;
}
#if HAVE_SYS_TERMIOX
#ifdef TCGETX
{
struct termiox tx;
if (ioctl (q->o, TCGETX, &tx) < 0)
{
ulog (LOG_ERROR,
"Can't disable hardware flow control: ioctl (TCGETX): %s",
strerror (errno));
return FALSE;
}
tx.x_hflag &=~ (RTSXOFF | CTSXON);
if (ioctl (q->o, TCSETX, &tx) < 0)
{
ulog (LOG_ERROR,
"Can't disable hardware flow control: ioctl (TCSETX): %s",
strerror (errno));
return FALSE;
}
}
#endif
#endif
#endif
}
#endif
return TRUE;
}
boolean
fsysdep_modem_end_dial (qconn, qdial)
struct sconnection *qconn;
struct uuconf_dialer *qdial;
{
struct ssysdep_conn *q;
q = (struct ssysdep_conn *) qconn->psysdep;
if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL)
{
(void) close (q->o);
q->o = q->ohold;
}
if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier
&& qdial->uuconf_fcarrier)
{
if (! fsmodem_carrier (qconn, TRUE))
return FALSE;
#ifdef TIOCWONLINE
if (FGOT_QUIT_SIGNAL ())
return FALSE;
fSalarm = FALSE;
if (fsysdep_catch ())
{
usysdep_start_catch ();
usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
(void) alarm (qdial->uuconf_ccarrier_wait);
while (ioctl (q->o, TIOCWONLINE, 0) < 0
&& errno == EINTR)
{
ulog (LOG_ERROR, (const char *) NULL);
if (FGOT_QUIT_SIGNAL () || fSalarm)
break;
}
}
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
(void) alarm (0);
usysdep_end_catch ();
if (FGOT_QUIT_SIGNAL ())
return FALSE;
if (fSalarm)
{
ulog (LOG_ERROR, "Timed out waiting for carrier");
return FALSE;
}
#else
{
int onew;
onew = open (q->zdevice, O_RDWR);
if (onew >= 0)
{
boolean fbad;
int iflags;
fbad = FALSE;
if (fcntl (onew, F_SETFD,
fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
fbad = TRUE;
if (! fbad)
{
iflags = fcntl (onew, F_GETFL, 0);
if (iflags < 0
|| ! fsetterminfo (onew, &q->snew))
fbad = TRUE;
}
if (fbad)
(void) close (onew);
else
{
(void) close (q->o);
q->o = onew;
q->iflags = iflags;
#if HAVE_TIOCSINUSE
(void) ioctl (onew, TIOCSINUSE, 0);
#endif
}
}
}
#endif
}
return TRUE;
}
boolean
fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport)
struct sconnection *qconn;
char *zbuf;
size_t *pclen;
size_t cmin;
int ctimeout;
boolean freport;
{
CATCH_PROTECT size_t cwant;
boolean fret;
register struct ssysdep_conn * const q
= (struct ssysdep_conn *) qconn->psysdep;
int cwouldblock;
cwant = *pclen;
*pclen = 0;
if (ctimeout <= 0)
return TRUE;
if (! fsblock (q, TRUE))
return FALSE;
fSalarm = FALSE;
if (fsysdep_catch ())
{
usysdep_start_catch ();
usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
alarm (ctimeout);
}
else
{
}
fret = FALSE;
cwouldblock = 0;
while (TRUE)
{
int cgot;
#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
if (q->fterminal)
{
int csetmin;
if (cmin < 127)
csetmin = cmin;
else
csetmin = 127;
if (csetmin != cSmin)
{
q->snew.c_cc[VMIN] = csetmin;
while (! fsetterminfo (q->o, &q->snew))
{
if (errno != EINTR
|| FGOT_QUIT_SIGNAL ())
{
int ierr;
ierr = errno;
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
alarm (0);
usysdep_end_catch ();
ulog (LOG_ERROR, "Can't set MIN for terminal: %s",
strerror (ierr));
return FALSE;
}
if (fSalarm)
{
ulog (LOG_ERROR,
"Timed out when setting MIN to %d; retrying",
csetmin);
fSalarm = FALSE;
alarm (ctimeout);
}
}
cSmin = csetmin;
}
}
#endif
if (FGOT_QUIT_SIGNAL ())
break;
if (fSalarm)
{
fret = TRUE;
break;
}
#if HAVE_TLI
if (q->ftli)
{
int iflags;
cgot = t_rcv (q->o, zbuf, cwant, &iflags);
if (cgot < 0 && t_errno != TSYSERR)
{
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
alarm (0);
usysdep_end_catch ();
if (freport)
ulog (LOG_ERROR, "t_rcv: %s",
(t_errno >= 0 && t_errno < t_nerr
? t_errlist[t_errno]
: "unknown TLI error"));
return FALSE;
}
}
else
#endif
cgot = read (q->o, zbuf, cwant);
if (cgot < 0)
{
if (errno == EINTR)
{
ulog (LOG_ERROR, (const char *) NULL);
}
if (fSalarm)
{
fret = TRUE;
break;
}
if (FGOT_QUIT_SIGNAL ())
break;
}
if (cgot > 0)
cwouldblock = 0;
else
{
if (cgot < 0 && errno == EINTR)
cgot = 0;
else if (cgot < 0
&& (errno == EAGAIN || errno == EWOULDBLOCK)
&& cwouldblock < 2)
{
++cwouldblock;
cgot = 0;
}
else
{
int ierr;
ierr = errno;
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
alarm (0);
usysdep_end_catch ();
if (freport)
{
if (cgot == 0)
ulog (LOG_ERROR, "Line disconnected");
else
ulog (LOG_ERROR, "read: %s", strerror (ierr));
}
return FALSE;
}
}
cwant -= cgot;
if ((size_t) cgot >= cmin)
cmin = 0;
else
cmin -= cgot;
zbuf += cgot;
*pclen += cgot;
if (cmin == 0)
{
fret = TRUE;
break;
}
#if HAVE_BSD_TTY
#if ! HAVE_SELECT
#error This code requires select; feel free to extend it
#endif
if (q->fterminal && cmin > 1 && cgot > 0)
{
int csleepchars;
int isleep;
if (cmin <= MAX_INPUT - 10)
csleepchars = cmin;
else
csleepchars = MAX_INPUT - 10;
isleep = (int) (((long) csleepchars * 10000L) / q->ibaud);
isleep -= 10;
if (isleep > 10)
{
struct timeval s;
s.tv_sec = isleep / 1000;
s.tv_usec = (isleep % 1000) * 1000;
(void) select (0, (pointer) NULL, (pointer) NULL,
(pointer) NULL, &s);
}
}
#endif
}
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
alarm (0);
usysdep_end_catch ();
return fret;
}
boolean
fsdouble_read (qconn, zbuf, pclen, cmin, ctimeout, freport)
struct sconnection *qconn;
char *zbuf;
size_t *pclen;
size_t cmin;
int ctimeout;
boolean freport;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
qsysdep->o = qsysdep->ord;
return fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport);
}
boolean
fsysdep_conn_write (qconn, zwrite, cwrite)
struct sconnection *qconn;
const char *zwrite;
size_t cwrite;
{
struct ssysdep_conn *q;
int czero;
q = (struct ssysdep_conn *) qconn->psysdep;
if (! fsblock (q, TRUE))
return FALSE;
czero = 0;
while (cwrite > 0)
{
int cdid;
while (TRUE)
{
if (FGOT_QUIT_SIGNAL ())
return FALSE;
#if HAVE_TLI
if (q->ftli)
{
cdid = t_snd (q->o, (char *) zwrite, cwrite, 0);
if (cdid < 0 && t_errno != TSYSERR)
{
ulog (LOG_ERROR, "t_snd: %s",
(t_errno >= 0 && t_errno < t_nerr
? t_errlist[t_errno]
: "unknown TLI error"));
return FALSE;
}
}
else
#endif
cdid = write (q->o, zwrite, cwrite);
if (cdid >= 0)
break;
if (errno != EINTR)
break;
ulog (LOG_ERROR, (const char *) NULL);
}
if (cdid < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA)
{
ulog (LOG_ERROR, "write: %s", strerror (errno));
return FALSE;
}
cdid = 0;
}
if (cdid == 0)
{
++czero;
if (czero >= 10)
{
ulog (LOG_ERROR, "Line disconnected");
return FALSE;
}
}
else
{
czero = 0;
cwrite -= cdid;
zwrite += cdid;
}
}
return TRUE;
}
boolean
fsdouble_write (qconn, zwrite, cwrite)
struct sconnection *qconn;
const char *zwrite;
size_t cwrite;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
qsysdep->o = qsysdep->ord;
if (! fsblock (qsysdep, TRUE))
return FALSE;
qsysdep->o = qsysdep->owr;
return fsysdep_conn_write (qconn, zwrite, cwrite);
}
boolean
fsysdep_conn_io (qconn, zwrite, pcwrite, zread, pcread)
struct sconnection *qconn;
const char *zwrite;
size_t *pcwrite;
char *zread;
size_t *pcread;
{
struct ssysdep_conn *q;
size_t cwrite, cread;
int czero;
q = (struct ssysdep_conn *) qconn->psysdep;
cwrite = *pcwrite;
*pcwrite = 0;
cread = *pcread;
*pcread = 0;
czero = 0;
while (TRUE)
{
int cgot, cdid;
size_t cdo;
if (q->ord >= 0)
q->o = q->ord;
if (! fsblock (q, FALSE))
return FALSE;
while (TRUE)
{
if (FGOT_QUIT_SIGNAL ())
return FALSE;
#if HAVE_TLI
if (q->ftli)
{
int iflags;
cgot = t_rcv (q->o, zread, cread, &iflags);
if (cgot < 0)
{
if (t_errno == TNODATA)
errno = EAGAIN;
else if (t_errno != TSYSERR)
{
ulog (LOG_ERROR, "t_rcv: %s",
(t_errno >= 0 && t_errno < t_nerr
? t_errlist[t_errno]
: "unknown TLI error"));
return FALSE;
}
}
}
else
#endif
cgot = read (q->o, zread, cread);
if (cgot >= 0)
break;
if (errno != EINTR)
break;
ulog (LOG_ERROR, (const char *) NULL);
}
if (cgot < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA)
{
ulog (LOG_ERROR, "read: %s", strerror (errno));
return FALSE;
}
cgot = 0;
}
cread -= cgot;
zread += cgot;
*pcread += cgot;
if (cread == 0 || cwrite == 0)
return TRUE;
cdo = cwrite;
#if ! HAVE_UNBLOCKED_WRITES
if (q->fterminal && cdo > SINGLE_WRITE)
cdo = SINGLE_WRITE;
#endif
if (q->owr >= 0)
q->o = q->owr;
while (TRUE)
{
if (FGOT_QUIT_SIGNAL ())
return FALSE;
#if HAVE_TLI
if (q->ftli)
{
cdid = t_snd (q->o, (char *) zwrite, cdo, 0);
if (cdid < 0)
{
if (t_errno == TFLOW)
errno = EAGAIN;
else if (t_errno != TSYSERR)
{
ulog (LOG_ERROR, "t_snd: %s",
(t_errno >= 0 && t_errno < t_nerr
? t_errlist[t_errno]
: "unknown TLI error"));
return FALSE;
}
}
}
else
#endif
cdid = write (q->o, zwrite, cdo);
if (cdid >= 0)
break;
if (errno != EINTR)
break;
ulog (LOG_ERROR, (const char *) NULL);
}
if (cdid < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA)
{
ulog (LOG_ERROR, "write: %s", strerror (errno));
return FALSE;
}
cdid = 0;
}
if (cdid > 0)
{
cwrite -= cdid;
zwrite += cdid;
*pcwrite += cdid;
if (cwrite == 0)
return TRUE;
czero = 0;
}
else
{
#if HAVE_SELECT
struct timeval stime;
#ifdef FD_ZERO
fd_set smask;
#else
int smask;
#endif
int c;
if (q->fterminal)
{
unsigned long cwait;
cwait = 1024;
if (cwait > cread)
cwait = cread;
stime.tv_sec = (cwait * 10) / q->ibaud;
stime.tv_usec = ((((cwait * 1000000) / q->ibaud) * 10)
% 1000000);
}
else
{
stime.tv_sec = 1;
stime.tv_usec = 0;
}
#ifdef FD_ZERO
FD_ZERO (&smask);
FD_SET (q->o, &smask);
#else
smask = 1 << q->o;
if (smask == 0)
ulog (LOG_FATAL, "fsysdep_conn_io: File descriptors too large");
#endif
if (FGOT_QUIT_SIGNAL ())
return FALSE;
DEBUG_MESSAGE0 (DEBUG_PORT, "fsysdep_conn_io: Calling select");
c = select (q->o + 1, (pointer) NULL, (pointer) &smask,
(pointer) NULL, &stime);
if (c < 0 && errno == EINTR)
{
ulog (LOG_ERROR, (const char *) NULL);
}
else if (c >= 0)
{
}
else
#endif
{
int ierr;
#if HAVE_RESTARTABLE_SYSCALLS
ulog (LOG_FATAL, "fsysdep_conn_io: Unsupported case; see code");
#endif
if (q->ord >= 0)
q->o = q->ord;
if (! fsblock (q, TRUE))
return FALSE;
DEBUG_MESSAGE0 (DEBUG_PORT, "fsysdep_conn_io: Blocking write");
if (q->owr >= 0)
q->o = q->owr;
if (FGOT_QUIT_SIGNAL ())
return FALSE;
usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
if (q->fterminal)
alarm ((int) ((long) 10240 / q->ibaud) + 1);
else
alarm (1);
#if HAVE_TLI
if (q->ftli)
{
cdid = t_snd (q->o, (char *) zwrite, 1, 0);
if (cdid < 0 && t_errno != TSYSERR)
{
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
alarm (0);
ulog (LOG_ERROR, "t_snd: %s",
(t_errno >= 0 && t_errno < t_nerr
? t_errlist[t_errno]
: "unknown TLI error"));
return FALSE;
}
}
else
#endif
cdid = write (q->o, zwrite, 1);
ierr = errno;
usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
alarm (0);
if (cdid < 0)
{
if (ierr == EINTR)
{
ulog (LOG_ERROR, (const char *) NULL);
}
else
{
ulog (LOG_ERROR, "write: %s", strerror (ierr));
return FALSE;
}
}
else if (cdid == 0)
{
++czero;
if (czero >= 10)
{
ulog (LOG_ERROR, "Line disconnected");
return FALSE;
}
}
else
{
cwrite -= cdid;
zwrite += cdid;
*pcwrite += cdid;
czero = 0;
}
}
}
}
}
static boolean
fsserial_break (qconn)
struct sconnection *qconn;
{
struct ssysdep_conn *q;
q = (struct ssysdep_conn *) qconn->psysdep;
#if HAVE_BSD_TTY
(void) ioctl (q->o, TIOCSBRK, 0);
sleep (2);
(void) ioctl (q->o, TIOCCBRK, 0);
return TRUE;
#endif
#if HAVE_SYSV_TERMIO
(void) ioctl (q->o, TCSBRK, 0);
return TRUE;
#endif
#if HAVE_POSIX_TERMIOS
return tcsendbreak (q->o, 0) == 0;
#endif
}
static boolean
fsstdin_break (qconn)
struct sconnection *qconn;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
qsysdep->o = qsysdep->owr;
return fsserial_break (qconn);
}
static boolean
fsserial_set (qconn, tparity, tstrip, txonxoff)
struct sconnection *qconn;
enum tparitysetting tparity;
enum tstripsetting tstrip;
enum txonxoffsetting txonxoff;
{
register struct ssysdep_conn *q;
boolean fchanged, fdo;
unsigned int iset = 0;
unsigned int iclear = 0;
q = (struct ssysdep_conn *) qconn->psysdep;
if (! q->fterminal)
return TRUE;
fchanged = FALSE;
#if HAVE_BSD_TTY
fdo = FALSE;
switch (tparity)
{
case PARITYSETTING_DEFAULT:
break;
case PARITYSETTING_NONE:
#if HAVE_PARITY_BUG
iset = 0;
iclear = ANYP;
#else
iset = ANYP;
iclear = 0;
#endif
fdo = TRUE;
break;
case PARITYSETTING_EVEN:
iset = EVENP;
iclear = ODDP;
fdo = TRUE;
break;
case PARITYSETTING_ODD:
iset = ODDP;
iclear = EVENP;
fdo = TRUE;
break;
case PARITYSETTING_MARK:
case PARITYSETTING_SPACE:
break;
}
if (fdo)
{
if ((q->snew.stty.sg_flags & iset) != iset
|| (q->snew.stty.sg_flags & iclear) != 0)
{
q->snew.stty.sg_flags |= iset;
q->snew.stty.sg_flags &=~ iclear;
fchanged = TRUE;
}
}
#else
fdo = FALSE;
switch (tparity)
{
case PARITYSETTING_DEFAULT:
break;
case PARITYSETTING_NONE:
iset = CS8;
iclear = PARENB | PARODD | (CSIZE &~ CS8);
fdo = TRUE;
break;
case PARITYSETTING_EVEN:
iset = PARENB | CS7;
iclear = PARODD | (CSIZE &~ CS7);
fdo = TRUE;
break;
case PARITYSETTING_ODD:
iset = PARENB | PARODD | CS7;
iclear = CSIZE &~ CS7;
fdo = TRUE;
break;
case PARITYSETTING_MARK:
case PARITYSETTING_SPACE:
break;
}
if (fdo)
{
if ((q->snew.c_cflag & iset) != iset
|| (q->snew.c_cflag & iclear) != 0)
{
q->snew.c_cflag |= iset;
q->snew.c_cflag &=~ iclear;
fchanged = TRUE;
}
}
#endif
#if HAVE_BSD_TTY
#ifdef LPASS8
{
int i;
i = LPASS8;
if (tstrip == STRIPSETTING_EIGHTBITS)
{
i = LPASS8;
(void) ioctl (q->o, TIOCLBIS, &i);
}
else if (tstrip == STRIPSETTING_SEVENBITS)
{
i = LPASS8;
(void) ioctl (q->o, TIOCLBIC, &i);
}
}
#endif
#else
fdo = FALSE;
switch (tstrip)
{
case STRIPSETTING_DEFAULT:
break;
case STRIPSETTING_EIGHTBITS:
iset = 0;
iclear = ISTRIP;
fdo = TRUE;
break;
case STRIPSETTING_SEVENBITS:
iset = ISTRIP;
iclear = 0;
fdo = TRUE;
break;
}
if (fdo)
{
if ((q->snew.c_iflag & iset) != iset
|| (q->snew.c_iflag & iclear) != 0)
{
q->snew.c_iflag |= iset;
q->snew.c_iflag &=~ iclear;
fchanged = TRUE;
}
}
#endif
#if HAVE_BSD_TTY
fdo = FALSE;
switch (txonxoff)
{
case XONXOFF_DEFAULT:
break;
case XONXOFF_OFF:
iset = RAW;
iclear = TANDEM | CBREAK;
fdo = TRUE;
break;
case XONXOFF_ON:
iset = CBREAK | TANDEM;
iclear = RAW;
fdo = TRUE;
break;
}
if (fdo)
{
if ((q->snew.stty.sg_flags & iset) != iset
|| (q->snew.stty.sg_flags & iclear) != 0)
{
q->snew.stty.sg_flags |= iset;
q->snew.stty.sg_flags &=~ iclear;
fchanged = TRUE;
}
}
#else
fdo = FALSE;
switch (txonxoff)
{
case XONXOFF_DEFAULT:
break;
case XONXOFF_OFF:
iset = 0;
iclear = IXON | IXOFF;
fdo = TRUE;
break;
case XONXOFF_ON:
#ifdef CRTSCTS
#if HAVE_POSIX_TERMIOS
if ((q->snew.c_cflag & CRTSCTS) != 0)
{
iset = IXON;
iclear = IXOFF;
fdo = TRUE;
break;
}
#endif
#endif
#ifdef CRTSFL
if ((q->snew.c_cflag & CRTSFL) != 0)
{
iset = IXON;
iclear = IXOFF;
#ifdef RTSFLOW
iclear |= RTSFLOW;
#endif
#ifdef CTSFLOW
iclear |= CTSFLOW;
#endif
fdo = TRUE;
break;
}
#endif
iset = IXON | IXOFF;
iclear = 0;
fdo = TRUE;
break;
}
if (fdo)
{
if ((q->snew.c_iflag & iset) != iset
|| (q->snew.c_iflag & iclear) != 0)
{
q->snew.c_iflag |= iset;
q->snew.c_iflag &=~ iclear;
fchanged = TRUE;
}
}
#endif
if (fchanged)
{
if (! fsetterminfodrain (q->o, &q->snew))
{
ulog (LOG_ERROR, "Can't change terminal settings: %s",
strerror (errno));
return FALSE;
}
}
#if HAVE_BSD_TTY
if (txonxoff == XONXOFF_ON
&& (q->snew.stty.sg_flags & ANYP) == ANYP)
{
int i;
#ifndef LLITOUT
#define LLITOUT 0
#endif
#ifndef LPASS8
#define LPASS8 0
#endif
#ifndef LAUTOFLOW
#define LAUTOFLOW 0
#endif
i = LLITOUT | LPASS8 | LAUTOFLOW;
(void) ioctl (q->o, TIOCLBIS, &i);
#if HAVE_STRIP_BUG
{
struct termio s;
if (ioctl (q->o, TCGETA, &s) >= 0)
{
s.c_iflag &=~ ISTRIP;
(void) ioctl (q->o, TCSETA, &s);
}
}
#endif
}
#endif
return TRUE;
}
static boolean
fsstdin_set (qconn, tparity, tstrip, txonxoff)
struct sconnection *qconn;
enum tparitysetting tparity;
enum tstripsetting tstrip;
enum txonxoffsetting txonxoff;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
qsysdep->o = qsysdep->ord;
return fsserial_set (qconn, tparity, tstrip, txonxoff);
}
static boolean
fsrun_chat (oread, owrite, pzprog)
int oread;
int owrite;
char **pzprog;
{
int aidescs[3];
FILE *e;
pid_t ipid;
char *z;
size_t c;
aidescs[0] = oread;
aidescs[1] = owrite;
aidescs[2] = SPAWN_READ_PIPE;
ipid = ixsspawn ((const char **) pzprog, aidescs, TRUE, TRUE,
(const char *) NULL, FALSE, TRUE, (const char *) NULL,
(const char *) NULL, (const char *) NULL);
if (ipid < 0)
{
ulog (LOG_ERROR, "ixsspawn (%s): %s", pzprog[0], strerror (errno));
return FALSE;
}
e = fdopen (aidescs[2], (char *) "r");
if (e == NULL)
{
ulog (LOG_ERROR, "fdopen: %s", strerror (errno));
(void) close (aidescs[2]);
(void) kill (ipid, SIGKILL);
(void) ixswait ((unsigned long) ipid, (const char *) NULL);
return FALSE;
}
z = NULL;
c = 0;
while (getline (&z, &c, e) > 0)
{
size_t clen;
clen = strlen (z);
if (z[clen - 1] == '\n')
z[clen - 1] = '\0';
if (*z != '\0')
ulog (LOG_NORMAL, "chat: %s", z);
}
xfree ((pointer) z);
(void) fclose (e);
return ixswait ((unsigned long) ipid, "Chat program") == 0;
}
boolean
fsdouble_chat (qconn, pzprog)
struct sconnection *qconn;
char **pzprog;
{
struct ssysdep_conn *qsysdep;
boolean fret;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
fret = fsrun_chat (qsysdep->ord, qsysdep->owr, pzprog);
if (qsysdep->fterminal)
(void) fgetterminfo (qsysdep->ord, &qsysdep->snew);
return fret;
}
boolean
fsysdep_conn_chat (qconn, pzprog)
struct sconnection *qconn;
char **pzprog;
{
struct ssysdep_conn *qsysdep;
boolean fret;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
fret = fsrun_chat (qsysdep->o, qsysdep->o, pzprog);
if (qsysdep->fterminal)
(void) fgetterminfo (qsysdep->o, &qsysdep->snew);
return fret;
}
static long
isserial_baud (qconn)
struct sconnection *qconn;
{
struct ssysdep_conn *qsysdep;
qsysdep = (struct ssysdep_conn *) qconn->psysdep;
return qsysdep->ibaud;
}