aboutsummaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/libunix/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/libunix/serial.c')
-rw-r--r--gnu/libexec/uucp/libunix/serial.c2991
1 files changed, 2991 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/libunix/serial.c b/gnu/libexec/uucp/libunix/serial.c
new file mode 100644
index 000000000000..cee90fcc7bb3
--- /dev/null
+++ b/gnu/libexec/uucp/libunix/serial.c
@@ -0,0 +1,2991 @@
+/* serial.c
+ The serial port communication routines for Unix.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char serial_rcsid[] = "$Id: serial.c,v 1.3 1994/02/07 23:47:51 ache Exp $";
+#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 /* ! HAVE_TIUSER_H */
+#if HAVE_XTI_H
+#include <xti.h>
+#endif /* HAVE_XTI_H */
+#endif /* ! HAVE_TIUSER_H */
+#endif /* HAVE_TLI */
+
+#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
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_BSD_TTY
+#include <sys/time.h>
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+
+#if HAVE_TIME_H
+#if HAVE_SYS_TIME_AND_TIME_H || ! HAVE_BSD_TTY
+#include <time.h>
+#endif
+#endif
+
+#if HAVE_STRIP_BUG && HAVE_BSD_TTY
+#include <termio.h>
+#endif
+
+#if HAVE_SVR4_LOCKFILES
+/* Get the right definitions for major and minor. */
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#endif /* MAJOR_IN_MKDEV */
+#if MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif /* MAJOR_IN_SYSMACROS */
+#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 /* ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS */
+#endif /* HAVE_SVR4_LOCKFILES */
+
+/* Get definitions for both O_NONBLOCK and O_NDELAY. */
+#ifndef O_NDELAY
+#ifdef FNDELAY
+#define O_NDELAY FNDELAY
+#else /* ! defined (FNDELAY) */
+#define O_NDELAY 0
+#endif /* ! defined (FNDELAY) */
+#endif /* ! defined (O_NDELAY) */
+
+#ifndef O_NONBLOCK
+#ifdef FNBLOCK
+#define O_NONBLOCK FNBLOCK
+#else /* ! defined (FNBLOCK) */
+#define O_NONBLOCK 0
+#endif /* ! defined (FNBLOCK) */
+#endif /* ! defined (O_NONBLOCK) */
+
+#if O_NDELAY == 0 && O_NONBLOCK == 0
+ #error No way to do nonblocking I/O
+#endif
+
+/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */
+#ifndef EAGAIN
+#ifndef EWOULDBLOCK
+#define EAGAIN (-1)
+#define EWOULDBLOCK (-1)
+#else /* defined (EWOULDBLOCK) */
+#define EAGAIN EWOULDBLOCK
+#endif /* defined (EWOULDBLOCK) */
+#else /* defined (EAGAIN) */
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif /* ! defined (EWOULDBLOCK) */
+#endif /* defined (EAGAIN) */
+
+#ifndef ENODATA
+#define ENODATA EAGAIN
+#endif
+
+/* Make sure we have a definition for MAX_INPUT. */
+#ifndef MAX_INPUT
+#define MAX_INPUT (256)
+#endif
+
+/* If we have the TIOCSINUSE ioctl call, we use it to lock a terminal.
+ Otherwise, if we have the TIOCEXCL ioctl call, we have to open the
+ terminal before we know that it is unlocked. */
+#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
+
+/* Determine bits to clear for the various terminal control fields for
+ HAVE_SYSV_TERMIO and HAVE_POSIX_TERMIOS. */
+#if HAVE_SYSV_TERMIO
+#define ICLEAR_IFLAG (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK \
+ | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC \
+ | IXON | IXANY | IXOFF)
+#define ICLEAR_OFLAG (OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET \
+ | OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY \
+ | VTDLY | FFDLY)
+#define ICLEAR_CFLAG (CBAUD | CLOCAL | CSIZE | PARENB | PARODD)
+#define ISET_CFLAG (CS8 | CREAD | HUPCL)
+#define ICLEAR_LFLAG (ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK \
+ | ECHONL | NOFLSH)
+#endif
+#if HAVE_POSIX_TERMIOS
+#ifdef IMAXBEL
+#define CI_ADD1 IMAXBEL
+#else
+#define CI_ADD1 0
+#endif
+#define ICLEAR_IFLAG (BRKINT | ICRNL | IGNBRK | IGNCR | IGNPAR \
+ | INLCR | INPCK | ISTRIP | IXOFF | IXON \
+ | PARMRK | CI_ADD1)
+#define ICLEAR_OFLAG (OPOST)
+#define ICLEAR_CFLAG (CLOCAL | CSIZE | PARENB | PARODD)
+#define ISET_CFLAG (CS8 | CREAD | HUPCL)
+#ifdef PENDIN
+#define CL_ADD1 PENDIN
+#else
+#define CL_ADD1 0
+#endif
+#define ICLEAR_LFLAG (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN \
+ | ISIG | NOFLSH | TOSTOP | CL_ADD1)
+#endif
+
+/* Local functions. */
+
+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));
+static boolean fsserial_unlock P((struct sconnection *qconn));
+static boolean fsserial_open P((struct sconnection *qconn, long ibaud,
+ boolean fwait));
+static boolean fsstdin_open P((struct sconnection *qconn, long ibaud,
+ boolean fwait));
+static boolean fsmodem_open P((struct sconnection *qconn, long ibaud,
+ boolean fwait));
+static boolean fsdirect_open P((struct sconnection *qconn, long ibaud,
+ boolean fwait));
+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_reset P((struct sconnection *qconn));
+static boolean fsstdin_reset P((struct sconnection *qconn));
+static boolean fsstdin_read P((struct sconnection *qconn,
+ char *zbuf, size_t *pclen, size_t cmin,
+ int ctimeout, boolean freport));
+static boolean fsstdin_write P((struct sconnection *qconn,
+ const char *zwrite, size_t cwrite));
+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 fsrun_chat P((int oread, int owrite, char **pzprog));
+static boolean fsstdin_chat P((struct sconnection *qconn,
+ char **pzprog));
+static long isserial_baud P((struct sconnection *qconn));
+
+/* The command table for standard input ports. */
+
+static const struct sconncmds sstdincmds =
+{
+ usserial_free,
+ NULL, /* pflock */
+ NULL, /* pfunlock */
+ fsstdin_open,
+ fsstdin_close,
+ fsstdin_reset,
+ NULL, /* pfdial */
+ fsstdin_read,
+ fsstdin_write,
+ fsysdep_conn_io,
+ fsstdin_break,
+ fsstdin_set,
+ NULL, /* pfcarrier */
+ fsstdin_chat,
+ isserial_baud
+};
+
+/* The command table for modem ports. */
+
+static const struct sconncmds smodemcmds =
+{
+ usserial_free,
+ fsserial_lock,
+ fsserial_unlock,
+ fsmodem_open,
+ fsmodem_close,
+ fsserial_reset,
+ fmodem_dial,
+ fsysdep_conn_read,
+ fsysdep_conn_write,
+ fsysdep_conn_io,
+ fsserial_break,
+ fsserial_set,
+ fsmodem_carrier,
+ fsysdep_conn_chat,
+ isserial_baud
+};
+
+/* The command table for direct ports. */
+
+static const struct sconncmds sdirectcmds =
+{
+ usserial_free,
+ fsserial_lock,
+ fsserial_unlock,
+ fsdirect_open,
+ fsdirect_close,
+ fsserial_reset,
+ NULL, /* pfdial */
+ fsysdep_conn_read,
+ fsysdep_conn_write,
+ fsysdep_conn_io,
+ fsserial_break,
+ fsserial_set,
+ NULL, /* pfcarrier */
+ fsysdep_conn_chat,
+ isserial_baud
+};
+
+/* If the system will let us set both O_NDELAY and O_NONBLOCK, we do
+ so. This is because some ancient drivers on some systems appear to
+ look for one but not the other. Some other systems will give an
+ EINVAL error if we attempt to set both, so we use a static global
+ to hold the value we want to set. If we get EINVAL, we change the
+ global and try again (if some system gives an error other than
+ EINVAL, the code will have to be modified). */
+static int iSunblock = O_NDELAY | O_NONBLOCK;
+
+/* This code handles SIGALRM. See the discussion above
+ fsysdep_conn_read. Normally we ignore SIGALRM, but the handler
+ will temporarily be set to this function, which should set fSalarm
+ and then either longjmp or schedule another SIGALRM. fSalarm is
+ never referred to outside of this file, but we don't make it static
+ to try to fool compilers which don't understand volatile. */
+
+volatile sig_atomic_t fSalarm;
+
+static RETSIGTYPE
+usalarm (isig)
+ int isig;
+{
+#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
+}
+
+/* We need a simple routine to block SIGINT, SIGQUIT, SIGTERM and
+ SIGPIPE and another to restore the original state. When these
+ functions are called (in fsysdep_modem_close) SIGHUP is being
+ ignored. The routines are isblocksigs, which returns a value of
+ type HELD_SIG_MASK and usunblocksigs which takes a single argument
+ of type HELD_SIG_MASK. */
+
+#if HAVE_SIGPROCMASK
+
+/* Use the POSIX sigprocmask call. */
+
+#define HELD_SIG_MASK sigset_t
+
+static sigset_t isblocksigs P((void));
+
+static sigset_t
+isblocksigs ()
+{
+ sigset_t sblock, sold;
+
+ /* These expressions need an extra set of parentheses to avoid a bug
+ in SCO 3.2.2. */
+ (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 /* ! HAVE_SIGPROCMASK */
+#if HAVE_SIGBLOCK
+
+/* Use the BSD sigblock and sigsetmask calls. */
+
+#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 /* ! HAVE_SIGBLOCK */
+
+#if HAVE_SIGHOLD
+
+/* Use the SVR3 sighold and sigrelse calls. */
+
+#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));
+
+/*ARGSUSED*/
+static void
+usunblocksigs (i)
+ int i;
+{
+ sigrelse (SIGINT);
+ sigrelse (SIGQUIT);
+ sigrelse (SIGTERM);
+ sigrelse (SIGPIPE);
+}
+
+#else /* ! HAVE_SIGHOLD */
+
+/* We have no way to block signals. This system will suffer from a
+ race condition in fsysdep_modem_close. */
+
+#define HELD_SIG_MASK int
+
+#define isblocksigs() 0
+
+#define usunblocksigs(i)
+
+#endif /* ! HAVE_SIGHOLD */
+#endif /* ! HAVE_SIGBLOCK */
+#endif /* ! HAVE_SIGPROCMASK */
+
+/* Initialize a connection for use on a serial port. */
+
+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->ftli = FALSE;
+ qconn->psysdep = (pointer) q;
+ qconn->qcmds = qcmds;
+ return TRUE;
+}
+
+/* Initialize a connection for use on standard input. */
+
+boolean
+fsysdep_stdin_init (qconn)
+ struct sconnection *qconn;
+{
+ return fsserial_init (qconn, &sstdincmds, (const char *) NULL);
+}
+
+/* Initialize a connection for use on a modem port. */
+
+boolean
+fsysdep_modem_init (qconn)
+ struct sconnection *qconn;
+{
+ return fsserial_init (qconn, &smodemcmds,
+ qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdevice);
+}
+
+/* Initialize a connection for use on a direct port. */
+
+boolean
+fsysdep_direct_init (qconn)
+ struct sconnection *qconn;
+{
+ return fsserial_init (qconn, &sdirectcmds,
+ qconn->qport->uuconf_u.uuconf_sdirect.uuconf_zdevice);
+}
+
+/* Free up a serial port. */
+
+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;
+}
+
+/* This routine is used for both locking and unlocking. It is the
+ only routine which knows how to translate a device name into the
+ name of a lock file. If it can't figure out a name, it does
+ nothing and returns TRUE. */
+
+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_SVR4_LOCKFILES
+ {
+ const char *zbase;
+ size_t clen;
+
+ zbase = strrchr (qsysdep->zdevice, '/') + 1;
+ clen = strlen (zbase);
+ zalc = zbufalc (sizeof "LCK.." + clen);
+ memcpy (zalc, "LCK..", sizeof "LCK.." - 1);
+ memcpy (zalc + sizeof "LCK.." - 1, zbase, clen + 1);
+#if HAVE_SCO_LOCKFILES
+ {
+ char *zl;
+
+ for (zl = zalc + sizeof "LCK.." - 1; *zl != '\0'; zl++)
+ if (isupper (*zl))
+ *zl = tolower (*zl);
+ }
+#endif
+ z = zalc;
+ }
+#else /* ! HAVE_SVR4_LOCKFILES */
+#if HAVE_SVR4_LOCKFILES
+ {
+ 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.123.123.123");
+ sprintf (zalc, "LK.%03d.%03d.%03d", major (s.st_dev),
+ major (s.st_rdev), minor (s.st_rdev));
+ z = zalc;
+ }
+#else /* ! HAVE_SVR4_LOCKFILES */
+ z = strrchr (qsysdep->zdevice, '/') + 1;
+#endif /* ! HAVE_SVR4_LOCKFILES */
+#endif /* ! HAVE_SVR4_LOCKFILES */
+ }
+
+ 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))
+ {
+ ulog (LOG_NORMAL, "%s: port already locked", z+5);
+ fret = FALSE;
+ }
+ else
+ fret = !(fscoherent_disable_tty (z, &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 /* HAVE_COHERENT_LOCKFILES */
+
+ ubuffree (zalc);
+ return fret;
+}
+
+/* If we can mark a modem line in use, then when we lock a port we
+ must open it and mark it in use. We can't wait until the actual
+ open because we can't fail out if it is locked then. */
+
+static boolean
+fsserial_lock (qconn, fin)
+ struct sconnection *qconn;
+ boolean fin;
+{
+ if (! fsserial_lockfile (TRUE, qconn))
+ return FALSE;
+
+#if HAVE_TIOCSINUSE || HAVE_TIOCEXCL
+ /* Open the line and try to mark it in use. */
+ {
+ struct ssysdep_conn *qsysdep;
+ int iflag;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+
+ if (fin)
+ iflag = 0;
+ else
+ iflag = iSunblock;
+
+ 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)
+ {
+ if (errno != EBUSY)
+ ulog (LOG_ERROR, "open (%s): %s", qsysdep->zdevice,
+ strerror (errno));
+ (void) fsserial_lockfile (FALSE, qconn);
+ return FALSE;
+ }
+ }
+
+#if HAVE_TIOCSINUSE
+ /* If we can't mark it in use, return FALSE to indicate that the
+ lock failed. */
+ 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 (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;
+ }
+
+#ifdef TIOCSCTTY
+ /* On BSD 4.4, make it our controlling terminal. */
+ (void) ioctl (qsysdep->o, TIOCSCTTY, 0);
+#endif
+ }
+#endif /* HAVE_TIOCSINUSE || HAVE_TIOCEXCL */
+
+ return TRUE;
+}
+
+/* Unlock a modem or direct port. */
+
+static boolean
+fsserial_unlock (qconn)
+ struct sconnection *qconn;
+{
+ boolean fret;
+ struct ssysdep_conn *qsysdep;
+
+ fret = TRUE;
+
+ /* The file may have been opened by fsserial_lock, so close it here
+ if necessary. */
+ 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;
+}
+
+/* Open a serial line. This sets the terminal settings. We begin in
+ seven bit mode and let the protocol change if necessary. */
+
+#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 /* ! defined (B19200) */
+#ifdef EXTA
+ { EXTA, 19200 },
+#endif /* EXTA */
+#endif /* ! defined (B19200) */
+#ifdef B38400
+ { B38400, 38400 },
+#else /* ! defined (B38400) */
+#ifdef EXTB
+ { EXTB, 38400 },
+#endif /* EXTB */
+#endif /* ! defined (B38400) */
+#ifdef B57600
+ { B57600, 57600 },
+#endif
+#ifdef B76800
+ { B76800, 76800 },
+#endif
+#ifdef B115200
+ { B115200, 115200 },
+#endif
+ { B0, 0 }
+};
+
+#define CBAUD_TABLE (sizeof asSbaud_table / sizeof asSbaud_table[0])
+
+#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
+/* Hold the MIN value for the terminal to avoid setting it
+ unnecessarily. */
+static int cSmin;
+#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
+
+static boolean
+fsserial_open (qconn, ibaud, fwait)
+ struct sconnection *qconn;
+ long ibaud;
+ boolean fwait;
+{
+ struct ssysdep_conn *q;
+ baud_code ib;
+
+ q = (struct ssysdep_conn *) qconn->psysdep;
+
+ if (q->zdevice != NULL)
+ ulog_device (strrchr (q->zdevice, '/') + 1);
+ 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)
+ {
+ int 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;
+ }
+
+ /* The port may have already been opened by the locking routine. */
+ if (q->o < 0)
+ {
+ int iflag;
+
+ if (fwait)
+ iflag = 0;
+ else
+ iflag = iSunblock;
+
+ 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)
+ {
+ ulog (LOG_ERROR, "open (%s): %s", q->zdevice,
+ strerror (errno));
+ 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;
+ }
+ }
+
+ /* Get the port flags, and make sure the ports are blocking. */
+
+ q->iflags = fcntl (q->o, F_GETFL, 0);
+ if (q->iflags < 0)
+ {
+ ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
+ return FALSE;
+ }
+ q->istdout_flags = -1;
+
+ 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;
+ }
+
+ /* We don't want to receive any interrupt characters. */
+ 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
+ /* We want to use the ``new'' terminal driver so that we can use the
+ local mode bits to control XON/XOFF. */
+ {
+ int iparam;
+
+ if (ioctl (q->o, TIOCGETD, &iparam) >= 0
+ && iparam != NTTYDISC)
+ {
+ iparam = NTTYDISC;
+ (void) ioctl (q->o, TIOCSETD, &iparam);
+ }
+ }
+#endif
+
+#ifdef TIOCHPCL
+ /* When the file is closed, hang up the line. This is a safety
+ measure in case the program crashes. */
+ (void) ioctl (q->o, TIOCHPCL, 0);
+#endif
+
+#ifdef TIOCFLUSH
+ {
+ int iparam;
+
+ /* Flush pending input. */
+#ifdef FREAD
+ iparam = FREAD;
+#else
+ iparam = 0;
+#endif
+ (void) ioctl (q->o, TIOCFLUSH, &iparam);
+ }
+#endif /* TIOCFLUSH */
+
+#endif /* HAVE_BSD_TTY */
+
+#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;
+ if (!fwait)
+ q->snew.c_cflag |= CLOCAL;
+ 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] = 0;
+
+#ifdef TCFLSH
+ /* Flush pending input. */
+ (void) ioctl (q->o, TCFLSH, 0);
+#endif
+
+#endif /* HAVE_SYSV_TERMIO */
+
+#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;
+ if (!fwait)
+ q->snew.c_cflag |= CLOCAL;
+ 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] = 0;
+
+ (void) cfsetospeed (&q->snew, ib);
+ (void) cfsetispeed (&q->snew, ib);
+
+ /* Flush pending input. */
+ (void) tcflush (q->o, TCIFLUSH);
+
+#endif /* HAVE_POSIX_TERMIOS */
+
+ if (! fsetterminfo (q->o, &q->snew))
+ {
+ ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno));
+ return FALSE;
+ }
+
+#ifdef TIOCSCTTY
+ /* On BSD 4.4, make it our controlling terminal. */
+ (void) ioctl (q->o, TIOCSCTTY, 0);
+#endif
+
+ if (ibaud != 0)
+ q->ibaud = ibaud;
+ else
+ {
+ int i;
+
+ q->ibaud = (long) 1200;
+ for (i = 0; i < CBAUD_TABLE; i++)
+ {
+ if (asSbaud_table[i].icode == ib)
+ {
+ q->ibaud = asSbaud_table[i].ibaud;
+ break;
+ }
+ }
+
+ DEBUG_MESSAGE1 (DEBUG_PORT,
+ "fsserial_open: Baud rate is %ld", q->ibaud);
+ }
+
+ return TRUE;
+}
+
+/* Open a standard input port. The code alternates q->o between 0 and
+ 1 as appropriate. It is always 0 before any call to fsblock. */
+
+static boolean
+fsstdin_open (qconn, ibaud, fwait)
+ struct sconnection *qconn;
+ long ibaud;
+ boolean fwait;
+{
+ struct ssysdep_conn *q;
+
+ q = (struct ssysdep_conn *) qconn->psysdep;
+ q->o = 0;
+ if (! fsserial_open (qconn, ibaud, fwait))
+ return FALSE;
+ q->istdout_flags = fcntl (1, F_GETFL, 0);
+ if (q->istdout_flags < 0)
+ {
+ ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Open a modem port. */
+
+static boolean
+fsmodem_open (qconn, ibaud, fwait)
+ struct sconnection *qconn;
+ long ibaud;
+ boolean fwait;
+{
+ if (ibaud == (long) 0)
+ ibaud = qconn->qport->uuconf_u.uuconf_smodem.uuconf_ibaud;
+ return fsserial_open (qconn, ibaud, fwait);
+}
+
+/* Open a direct port. */
+
+static boolean
+fsdirect_open (qconn, ibaud, fwait)
+ struct sconnection *qconn;
+ long ibaud;
+ boolean fwait;
+{
+ if (ibaud == (long) 0)
+ ibaud = qconn->qport->uuconf_u.uuconf_sdirect.uuconf_ibaud;
+ return fsserial_open (qconn, ibaud, fwait);
+}
+
+/* Change the blocking status of the port. We keep track of the
+ current blocking status to avoid calling fcntl unnecessarily; fcntl
+ turns out to be surprisingly expensive, at least on Ultrix. */
+
+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->istdout_flags >= 0)
+ {
+ if (fblock)
+ iwant = qs->istdout_flags &~ (O_NDELAY | O_NONBLOCK);
+ else
+ iwant = qs->istdout_flags | iSunblock;
+
+ if (fcntl (1, F_SETFL, iwant) < 0)
+ {
+ /* We don't bother to fix up iSunblock here, since we
+ succeeded above. */
+ ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
+ return FALSE;
+ }
+
+ qs->istdout_flags = iwant;
+ }
+
+ return TRUE;
+}
+
+/* Close a serial port. */
+
+static boolean
+fsserial_close (q)
+ struct ssysdep_conn *q;
+{
+ if (q->o >= 0)
+ {
+ /* Use a 30 second timeout to avoid hanging while draining
+ output. */
+ 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 we timed out, use the non draining call. Hopefully
+ this can't hang. */
+ if (fSalarm)
+ (void) fsetterminfo (q->o, &q->sorig);
+ }
+
+#ifdef TIOCNOTTY
+ /* We don't want this as our controlling terminal any more, so
+ get rid of it. This is necessary because we don't want to
+ open /dev/tty, since that can confuse the serial port locking
+ on some computers. */
+ (void) ioctl (q->o, TIOCNOTTY, (char *) NULL);
+#endif
+
+ (void) close (q->o);
+ q->o = -1;
+
+ /* Sleep to give the terminal a chance to settle, in case we are
+ about to call out again. */
+ sleep (2);
+ }
+
+ return TRUE;
+}
+
+/* Close a stdin port. */
+
+/*ARGSUSED*/
+static boolean
+fsstdin_close (qconn, puuconf, qdialer, fsuccess)
+ struct sconnection *qconn;
+ pointer puuconf;
+ struct uuconf_dialer *qdialer;
+ boolean fsuccess;
+{
+ struct ssysdep_conn *qsysdep;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+ (void) close (1);
+ (void) close (2);
+ qsysdep->o = 0;
+ return fsserial_close (qsysdep);
+}
+
+/* Close a modem port. */
+
+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;
+
+ /* Figure out the dialer so that we can run the complete or abort
+ chat scripts. */
+ 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;
+ }
+
+ /* Get the complete or abort chat script to use. */
+ 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];
+
+ /* We're no longer interested in carrier. */
+ (void) fsmodem_carrier (qconn, FALSE);
+
+ /* The port I/O routines check whether any signal has been
+ received, and abort if one has. While we are closing down
+ the modem, we don't care if we received a signal in the past,
+ but we do care if we receive a new signal (otherwise it would
+ be difficult to kill a uucico which was closing down a
+ modem). We never care if we get SIGHUP at this point. So we
+ turn off SIGHUP, remember what signals we've already seen,
+ and clear our notion of what signals we've seen. We have to
+ block the signals while we remember and clear the array,
+ since we might otherwise miss a signal which occurred between
+ the copy and the clear (old systems can't block signals; they
+ will just have to suffer the race). */
+ 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;
+
+ /* Restore the old signal array and the SIGHUP handler. It is
+ not necessary to block signals here, since all we are doing
+ is exactly what the signal handler itself would do if the
+ signal occurred. */
+ 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
+ /* Reset the terminal to make sure we drop DTR. It should be
+ dropped when we close the descriptor, but that doesn't seem to
+ happen on some systems. Use a 30 second timeout to avoid hanging
+ while draining output. */
+ 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 ();
+
+ /* Let the port settle. */
+ sleep (2);
+ }
+#endif /* ! HAVE_RESET_BUG */
+
+ if (! fsserial_close (qsysdep))
+ fret = FALSE;
+
+ return fret;
+}
+
+/* Close a direct port. */
+
+/*ARGSUSED*/
+static boolean
+fsdirect_close (qconn, puuconf, qdialer, fsuccess)
+ struct sconnection *qconn;
+ pointer puuconf;
+ struct uuconf_dialer *qdialer;
+ boolean fsuccess;
+{
+ return fsserial_close ((struct ssysdep_conn *) qconn->psysdep);
+}
+
+/* Reset a serial port by hanging up. */
+
+static boolean
+fsserial_reset (qconn)
+ struct sconnection *qconn;
+{
+ struct ssysdep_conn *q;
+ sterminal sbaud;
+
+ q = (struct ssysdep_conn *) qconn->psysdep;
+
+ if (! q->fterminal)
+ return TRUE;
+
+ sbaud = q->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
+ if (cfsetospeed (&sbaud, B0) < 0)
+ {
+ ulog (LOG_ERROR, "Can't set baud rate: %s", strerror (errno));
+ return FALSE;
+ }
+#endif
+
+ if (! fsetterminfodrain (q->o, &sbaud))
+ {
+ ulog (LOG_ERROR, "Can't hangup terminal: %s", strerror (errno));
+ return FALSE;
+ }
+
+ /* Give the terminal a chance to settle. */
+ sleep (2);
+
+ if (! fsetterminfo (q->o, &q->snew))
+ {
+ ulog (LOG_ERROR, "Can't reopen terminal: %s", strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Reset a standard input port. */
+
+static boolean
+fsstdin_reset (qconn)
+ struct sconnection *qconn;
+{
+ struct ssysdep_conn *qsysdep;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+ qsysdep->o = 0;
+ return fsserial_reset (qconn);
+}
+
+/* Begin dialing out on a modem port. This opens the dialer device if
+ there is one. */
+
+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
+ /* If we can tell the modem to obey modem control, do so. */
+ {
+ int iperm;
+
+ iperm = 0;
+ (void) ioctl (qsysdep->o, TIOCMODEM, &iperm);
+ }
+#endif /* TIOCMODEM */
+
+ /* If we supposed to toggle DTR, do so. */
+
+ if (qdial->uuconf_fdtr_toggle)
+ {
+#ifdef TIOCCDTR
+ (void) ioctl (qsysdep->o, TIOCCDTR, 0);
+ sleep (2);
+ (void) ioctl (qsysdep->o, TIOCSDTR, 0);
+#else /* ! defined (TIOCCDTR) */
+ (void) fconn_reset (qconn);
+#endif /* ! defined (TIOCCDTR) */
+
+ if (qdial->uuconf_fdtr_toggle_wait)
+ sleep (2);
+ }
+
+ if (! fsmodem_carrier (qconn, FALSE))
+ return FALSE;
+
+ /* Open the dial device if there is one. */
+ 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;
+}
+
+/* Tell the port to require or not require carrier. On BSD this uses
+ TIOCCAR and TIOCNCAR, which I assume are generally supported (it
+ can also use the LNOMDM bit supported by IS68K Unix). On System V
+ it resets or sets CLOCAL. We only require carrier if the port
+ supports it. This will only be called with fcarrier TRUE if the
+ dialer supports carrier. */
+
+static boolean
+fsmodem_carrier (qconn, fcarrier)
+ struct sconnection *qconn;
+ boolean fcarrier;
+{
+ register struct ssysdep_conn *q;
+
+ q = (struct ssysdep_conn *) qconn->psysdep;
+
+ if (! q->fterminal)
+ return TRUE;
+
+ if (fcarrier)
+ {
+ if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier)
+ {
+#ifdef TIOCCAR
+ /* Tell the modem to pay attention to carrier. */
+ if (ioctl (q->o, TIOCCAR, 0) < 0)
+ {
+ ulog (LOG_ERROR, "ioctl (TIOCCAR): %s", strerror (errno));
+ return FALSE;
+ }
+#endif /* TIOCCAR */
+
+#if HAVE_BSD_TTY
+#ifdef LNOMDM
+ /* IS68K Unix uses a local LNOMDM bit. */
+ {
+ int iparam;
+
+ iparam = LNOMDM;
+ if (ioctl (q->o, TIOCLBIC, &iparam) < 0)
+ {
+ ulog (LOG_ERROR, "ioctl (TIOCLBIC, LNOMDM): %s",
+ strerror (errno));
+ return FALSE;
+ }
+ }
+#endif /* LNOMDM */
+#endif /* HAVE_BSD_TTY */
+
+#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
+ /* Put the modem into nonlocal mode. */
+ q->snew.c_cflag &=~ CLOCAL;
+ if (! fsetterminfo (q->o, &q->snew))
+ {
+ ulog (LOG_ERROR, "Can't clear CLOCAL: %s", strerror (errno));
+ return FALSE;
+ }
+#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
+ }
+ }
+ else
+ {
+#ifdef TIOCNCAR
+ /* Tell the modem to ignore carrier. */
+ if (ioctl (q->o, TIOCNCAR, 0) < 0)
+ {
+ ulog (LOG_ERROR, "ioctl (TIOCNCAR): %s", strerror (errno));
+ return FALSE;
+ }
+#endif /* TIOCNCAR */
+
+#if HAVE_BSD_TTY
+#ifdef LNOMDM
+ /* IS68K Unix uses a local LNOMDM bit. */
+ {
+ int iparam;
+
+ iparam = LNOMDM;
+ if (ioctl (q->o, TIOCLBIS, &iparam) < 0)
+ {
+ ulog (LOG_ERROR, "ioctl (TIOCLBIS, LNOMDM): %s",
+ strerror (errno));
+ return FALSE;
+ }
+ }
+#endif /* LNOMDM */
+#endif /* HAVE_BSD_TTY */
+
+#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
+ /* Put the modem into local mode (ignore carrier) to start the chat
+ script. */
+ 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
+ /* On SCO and AT&T UNIX PC you have to reopen the port. */
+ {
+ 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 /* HAVE_CLOCAL_BUG */
+
+#endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
+ }
+
+ return TRUE;
+}
+
+/* Finish dialing out on a modem by closing any dialer device and waiting
+ for carrier. */
+
+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)
+ {
+ /* Tell the port that we need carrier. */
+ if (! fsmodem_carrier (qconn, TRUE))
+ return FALSE;
+
+#ifdef TIOCWONLINE
+
+ /* We know how to wait for carrier, so do so. */
+
+ /* If we already got a signal, just quit now. */
+ if (FGOT_QUIT_SIGNAL ())
+ return FALSE;
+
+ /* This bit of code handles signals just like fsysdep_conn_read
+ does. See that function for a longer explanation. */
+
+ /* Use fsysdep_catch to handle a longjmp from the signal
+ handler. */
+
+ fSalarm = FALSE;
+
+ if (fsysdep_catch ())
+ {
+ /* Start catching SIGALRM; normally we ignore it. */
+ usysdep_start_catch ();
+ usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
+ (void) alarm (qdial->uuconf_ccarrier_wait);
+
+ /* We really don't care if we get an error, since that will
+ probably just mean that TIOCWONLINE isn't supported in
+ which case there's nothing we can do anyhow. If we get
+ SIGINT we want to keep waiting for carrier, because
+ SIGINT just means don't start any new sessions. We don't
+ handle SIGINT correctly if we do a longjmp in the signal
+ handler; too bad. */
+ while (ioctl (q->o, TIOCWONLINE, 0) < 0
+ && errno == EINTR)
+ {
+ /* Log the signal. */
+ ulog (LOG_ERROR, (const char *) NULL);
+ if (FGOT_QUIT_SIGNAL () || fSalarm)
+ break;
+ }
+ }
+
+ /* Turn off the pending SIGALRM and ignore SIGALARM again. */
+ usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
+ (void) alarm (0);
+ usysdep_end_catch ();
+
+ /* If we got a random signal, just return FALSE. */
+ if (FGOT_QUIT_SIGNAL ())
+ return FALSE;
+
+ /* If we timed out, give an error. */
+ if (fSalarm)
+ {
+ ulog (LOG_ERROR, "Timed out waiting for carrier");
+ return FALSE;
+ }
+
+#endif /* TIOCWONLINE */
+ }
+
+ return TRUE;
+}
+
+/* Read data from a connection, with a timeout. This routine handles
+ all types of connections, including TLI.
+
+ This function should return when we have read cmin characters or
+ the timeout has occurred. We have to work a bit to get Unix to do
+ this efficiently on a terminal. The simple implementation
+ schedules a SIGALRM signal and then calls read; if there is a
+ single character available, the call to read will return
+ immediately, so there must be a loop which terminates when the
+ SIGALRM is delivered or the correct number of characters has been
+ read. This can be very inefficient with a fast CPU or a low baud
+ rate (or both!), since each call to read may return only one or two
+ characters.
+
+ Under POSIX or System V, we can specify a minimum number of
+ characters to read, so there is no serious trouble.
+
+ Under BSD, we figure out how many characters we have left to read,
+ how long it will take for them to arrive at the current baud rate,
+ and sleep that long.
+
+ Doing this with a timeout and avoiding all possible race conditions
+ get very hairy, though. Basically, we're going to schedule a
+ SIGALRM for when the timeout expires. I don't really want to do a
+ longjmp in the SIGALRM handler, though, because that may lose data.
+ Therefore, I have the signal handler set a variable. However, this
+ means that there will be a span of time between the time the code
+ checks the variable and the time it calls the read system call; if
+ the SIGALRM occurs during that time, the read might hang forever.
+ To avoid this, the SIGALRM handler not only sets a global variable,
+ it also schedules another SIGALRM for one second in the future
+ (POSIX specifies that a signal handler is permitted to safely call
+ alarm). To avoid getting a continual sequence of SIGALRM
+ interrupts, we change the signal handler to ignore SIGALRM when
+ we're about to exit the function. This means that every time we
+ execute fsysdep_conn_read we make at least five system calls. It's
+ the best I've been able to come up with, though.
+
+ When fsysdep_conn_read finishes, there will be no SIGALRM scheduled
+ and SIGALRM will be ignored. */
+
+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;
+
+ cwant = *pclen;
+ *pclen = 0;
+
+ /* Guard against a bad timeout. We return TRUE when a timeout
+ expires. It is possible to get a negative timeout here because
+ the calling code does not check user supplied timeouts for
+ plausibility. */
+ if (ctimeout <= 0)
+ return TRUE;
+
+ /* We want to do a blocking read. */
+ if (! fsblock (q, TRUE))
+ return FALSE;
+
+ fSalarm = FALSE;
+
+ /* We're going to set up an alarm signal to last for the entire
+ read. If the read system call cannot be interrupted, the signal
+ handler will do a longjmp causing fsysdep_catch (a macro) to
+ return FALSE. We handle that here. If read can be interrupted,
+ fsysdep_catch will be defined to TRUE. */
+ if (fsysdep_catch ())
+ {
+ /* Prepare to catch SIGALRM and schedule the signal. */
+ usysdep_start_catch ();
+ usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
+ alarm (ctimeout);
+ }
+ else
+ {
+ /* We caught a signal. We don't actually have to do anything,
+ as all the appropriate checks are made at the start of the
+ following loop. */
+ }
+
+ fret = FALSE;
+
+ while (TRUE)
+ {
+ int cgot;
+
+#if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
+ /* If we can tell the terminal not to return until we have a
+ certain number of characters, do so. */
+ if (q->fterminal)
+ {
+ int csetmin;
+
+ /* I'm not that confident about setting MIN to values larger
+ than 127, although up to 255 would probably work. */
+ 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;
+
+ /* We turn off the signal before reporting the
+ error to minimize any problems with
+ interrupted system calls. */
+ 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 /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
+
+ /* If we've received a signal, get out now. */
+ if (FGOT_QUIT_SIGNAL ())
+ break;
+
+ /* If we've already gotten a SIGALRM, get out with whatever
+ we've accumulated. */
+ if (fSalarm)
+ {
+ fret = TRUE;
+ break;
+ }
+
+ /* Right here is the race condition which we avoid by having the
+ SIGALRM handler schedule another SIGALRM. */
+#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 the read returned an error, check for signals. */
+ if (cgot < 0)
+ {
+ if (errno == EINTR)
+ {
+ /* Log the signal. */
+ ulog (LOG_ERROR, (const char *) NULL);
+ }
+ if (fSalarm)
+ {
+ fret = TRUE;
+ break;
+ }
+ if (FGOT_QUIT_SIGNAL ())
+ break;
+ }
+
+ /* If read returned an error, get out. We just ignore EINTR
+ here, since it must be from some signal we don't care about.
+ If the read returned 0 then the line must have been hung up
+ (normally we would have received SIGHUP, but we can't count
+ on that). We turn off the signals before calling ulog to
+ reduce problems with interrupted system calls. */
+ if (cgot <= 0)
+ {
+ if (cgot < 0 && errno == EINTR)
+ 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 (cgot >= cmin)
+ cmin = 0;
+ else
+ cmin -= cgot;
+ zbuf += cgot;
+ *pclen += cgot;
+
+ /* If we have enough data, get out now. */
+ if (cmin == 0)
+ {
+ fret = TRUE;
+ break;
+ }
+
+#if HAVE_BSD_TTY
+ /* We still want more data, so sleep long enough for the rest of
+ it to arrive. We don't this for System V or POSIX because
+ setting MIN is good enough (we can't sleep longer than it
+ takes to get MAX_INPUT characters anyhow).
+
+ The baud rate is approximately 10 times the number of
+ characters which will arrive in one second, so the number of
+ milliseconds to sleep ==
+ characters * (milliseconds / character) ==
+ characters * (1000 * (seconds / character)) ==
+ characters * (1000 * (1 / (baud / 10))) ==
+ characters * (10000 / baud)
+
+ We arbitrarily reduce the sleep amount by 10 milliseconds to
+ attempt to account for the amount of time it takes to set up
+ the sleep. This is how long it takes to get half a character
+ at 19200 baud. We then don't bother to sleep for less than
+ 10 milliseconds. We don't sleep if the read was interrupted.
+
+ We use select to sleep. It would be easy to use poll as
+ well, but it's unlikely that any system with BSD ttys would
+ have poll but not select. Using select avoids hassles with
+ the pending SIGALRM; if it hits the select will be
+ interrupted, and otherwise the select will not affect it. */
+
+#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;
+
+ /* We don't try to read all the way up to MAX_INPUT,
+ since that might drop a character. */
+ 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;
+
+ /* Some versions of select take a pointer to an int,
+ while some take a pointer to an fd_set. I just cast
+ the arguments to a generic pointer, and assume that
+ any machine which distinguishes int * from fd_set *
+ (I would be amazed if there are any such machines)
+ have an appropriate prototype somewhere or other. */
+ (void) select (0, (pointer) NULL, (pointer) NULL,
+ (pointer) NULL, &s);
+
+ /* Here either the select finished sleeping or we got a
+ SIGALRM. If the latter occurred, fSalarm was set to
+ TRUE; it will be checked at the top of the loop. */
+ }
+ }
+#endif /* HAVE_BSD_TTY */
+ }
+
+ /* Turn off the pending SIGALRM and return. */
+
+ usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
+ alarm (0);
+ usysdep_end_catch ();
+
+ return fret;
+}
+
+/* Read from a stdin port. */
+
+static boolean
+fsstdin_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 = 0;
+ return fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport);
+}
+
+/* Write data to a connection. This routine handles all types of
+ connections, including TLI. */
+
+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;
+
+ /* We want blocking writes here. */
+ if (! fsblock (q, TRUE))
+ return FALSE;
+
+ czero = 0;
+
+ while (cwrite > 0)
+ {
+ int cdid;
+
+ /* Loop until we don't get an interrupt. */
+ while (TRUE)
+ {
+ /* If we've received a signal, don't continue. */
+ if (FGOT_QUIT_SIGNAL ())
+ return FALSE;
+
+#if HAVE_TLI
+ if (q->ftli)
+ {
+ cdid = t_snd (q->o, 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;
+
+ /* We were interrupted by a signal. Log it. */
+ 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)
+ {
+ /* On some systems write will return 0 if carrier is lost.
+ If we fail to write anything ten times in a row, we
+ assume that this has happened. This is hacked in like
+ this because there seems to be no reliable way to tell
+ exactly why the write returned 0. */
+ ++czero;
+ if (czero >= 10)
+ {
+ ulog (LOG_ERROR, "Line disconnected");
+ return FALSE;
+ }
+ }
+ else
+ {
+ czero = 0;
+
+ cwrite -= cdid;
+ zwrite += cdid;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Write to a stdin port. */
+
+static boolean
+fsstdin_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 = 0;
+ if (! fsblock (qsysdep, TRUE))
+ return FALSE;
+ qsysdep->o = 1;
+ return fsysdep_conn_write (qconn, zwrite, cwrite);
+}
+
+/* The fsysdep_conn_io routine is supposed to both read and write data
+ until it has either filled its read buffer or written out all the
+ data it was given. This lets us write out large packets without
+ losing incoming data. It handles all types of connections,
+ including TLI. */
+
+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;
+
+ /* This used to always use nonblocking writes, but it turns out
+ that some systems don't support them on terminals.
+
+ The current algorithm is:
+ loop:
+ unblocked read
+ if read buffer full, return
+ if nothing to write, return
+ if HAVE_UNBLOCKED_WRITES
+ write all data
+ else
+ write up to SINGLE_WRITE bytes
+ if all data written, return
+ if no data written
+ blocked write of up to SINGLE_WRITE bytes
+
+ This algorithm should work whether the system supports
+ unblocked writes on terminals or not. If the system supports
+ unblocked writes but HAVE_UNBLOCKED_WRITES is 0, then it will
+ call write more often than it needs to. If the system does
+ not support unblocked writes but HAVE_UNBLOCKED_WRITES is 1,
+ then the write may hang so long that incoming data is lost.
+ This is actually possible at high baud rates on any system
+ when a blocking write is done; there is no solution, except
+ hardware handshaking. */
+
+ /* If we are running on standard input, we switch the file
+ descriptors by hand. */
+ if (q->istdout_flags >= 0)
+ q->o = 0;
+
+ /* Do an unblocked read. */
+ if (! fsblock (q, FALSE))
+ return FALSE;
+
+ /* Loop until we get something (error or data) other than an
+ acceptable EINTR. */
+ while (TRUE)
+ {
+ /* If we've received a signal, don't continue. */
+ 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;
+
+ /* We got interrupted by a signal. Log it. */
+ 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 we've filled the read buffer, or we have nothing left to
+ write, return out. */
+ if (cread == 0 || cwrite == 0)
+ return TRUE;
+
+ /* The port is currently unblocked. Do a write. */
+ cdo = cwrite;
+
+#if ! HAVE_UNBLOCKED_WRITES
+ if (q->fterminal && cdo > SINGLE_WRITE)
+ cdo = SINGLE_WRITE;
+#endif
+
+ if (q->istdout_flags >= 0)
+ q->o = 1;
+
+ /* Loop until we get something besides EINTR. */
+ while (TRUE)
+ {
+ /* If we've received a signal, don't continue. */
+ if (FGOT_QUIT_SIGNAL ())
+ return FALSE;
+
+#if HAVE_TLI
+ if (q->ftli)
+ {
+ cdid = t_snd (q->o, 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;
+
+ /* We got interrupted by a signal. Log it. */
+ 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)
+ {
+ /* We wrote some data. If we wrote everything, return out.
+ Otherwise loop around and do another read. */
+ cwrite -= cdid;
+ zwrite += cdid;
+ *pcwrite += cdid;
+
+ if (cwrite == 0)
+ return TRUE;
+
+ czero = 0;
+ }
+ else
+ {
+ /* We didn't write any data. Do a blocking write. */
+
+ if (q->istdout_flags >= 0)
+ q->o = 0;
+
+ if (! fsblock (q, TRUE))
+ return FALSE;
+
+ cdo = cwrite;
+ if (cdo > SINGLE_WRITE)
+ cdo = SINGLE_WRITE;
+
+ DEBUG_MESSAGE1 (DEBUG_PORT,
+ "fsysdep_conn_io: Blocking write of %lud",
+ (unsigned long) cdo);
+
+ if (q->istdout_flags >= 0)
+ q->o = 1;
+
+ /* Loop until we get something besides EINTR. */
+ while (TRUE)
+ {
+ /* If we've received a signal, don't continue. */
+ if (FGOT_QUIT_SIGNAL ())
+ return FALSE;
+
+#if HAVE_TLI
+ if (q->ftli)
+ {
+ cdid = t_snd (q->o, zwrite, cdo, 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, cdo);
+
+ if (cdid >= 0)
+ break;
+ if (errno != EINTR)
+ break;
+
+ /* We got interrupted by a signal. Log it. */
+ ulog (LOG_ERROR, (const char *) NULL);
+ }
+
+ if (cdid < 0)
+ {
+ ulog (LOG_ERROR, "write: %s", strerror (errno));
+ return FALSE;
+ }
+
+ if (cdid == 0)
+ {
+ /* On some systems write will return 0 if carrier is
+ lost. If we fail to write anything ten times in a
+ row, we assume that this has happened. This is
+ hacked in like this because there seems to be no
+ reliable way to tell exactly why the write returned
+ 0. */
+ ++czero;
+ if (czero >= 10)
+ {
+ ulog (LOG_ERROR, "Line disconnected");
+ return FALSE;
+ }
+ }
+ else
+ {
+ cwrite -= cdid;
+ zwrite += cdid;
+ *pcwrite += cdid;
+ czero = 0;
+ }
+ }
+ }
+}
+
+/* Send a break character to a serial port. */
+
+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 /* HAVE_BSD_TTY */
+#if HAVE_SYSV_TERMIO
+ (void) ioctl (q->o, TCSBRK, 0);
+ return TRUE;
+#endif /* HAVE_SYSV_TERMIO */
+#if HAVE_POSIX_TERMIOS
+ return tcsendbreak (q->o, 0) == 0;
+#endif /* HAVE_POSIX_TERMIOS */
+}
+
+/* Send a break character to a stdin port. */
+
+static boolean
+fsstdin_break (qconn)
+ struct sconnection *qconn;
+{
+ struct ssysdep_conn *qsysdep;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+ qsysdep->o = 1;
+ return fsserial_break (qconn);
+}
+
+/* Change the setting of a serial port. */
+
+/*ARGSUSED*/
+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;
+ int iset = 0;
+ int iclear = 0;
+
+ q = (struct ssysdep_conn *) qconn->psysdep;
+
+ if (! q->fterminal)
+ return TRUE;
+
+ fchanged = FALSE;
+
+ /* Set the parity for output characters. */
+
+#if HAVE_BSD_TTY
+
+ /* This will also cause parity detection on input characters. */
+
+ fdo = FALSE;
+ switch (tparity)
+ {
+ case PARITYSETTING_DEFAULT:
+ break;
+ case PARITYSETTING_NONE:
+#if HAVE_PARITY_BUG
+ /* The Sony NEWS mishandles this for some reason. */
+ 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:
+ /* Not supported. */
+ 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 /* ! HAVE_BSD_TTY */
+
+ 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:
+ /* Not supported. */
+ 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 /* ! HAVE_BSD_TTY */
+
+ /* Set whether input characters are stripped to seven bits. */
+
+#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 /* ! HAVE_BSD_TTY */
+
+ 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 /* ! HAVE_BSD_TTY */
+
+ /* Set XON/XOFF handshaking. */
+
+#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 /* ! HAVE_BSD_TTY */
+
+ 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
+ /* This is system dependent, but I haven't figured out a good
+ way around it yet. If we are doing hardware flow control, we
+ don't send XON/XOFF characters but we do recognize them. */
+ if ((q->snew.c_cflag & CRTSCTS) != 0)
+ {
+ iset = IXON;
+ iclear = IXOFF;
+ fdo = TRUE;
+ break;
+ }
+#endif /* HAVE_POSIX_TERMIOS */
+#endif /* defined (CRTSCTS) */
+ 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 /* ! HAVE_BSD_TTY */
+
+ 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;
+
+ /* At least on Ultrix, we seem to have to set LLITOUT and
+ LPASS8. This shouldn't foul things up anywhere else. As far
+ as I can tell, this has to be done after setting the terminal
+ into cbreak mode, not before. */
+#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
+ /* Ultrix 4.0 has a peculiar problem: setting CBREAK always
+ causes input characters to be stripped. I hope this does not
+ apply to other BSD systems. It is possible to work around
+ this by using the termio call. I wish this sort of stuff was
+ not necessary!!! */
+ {
+ struct termio s;
+
+ if (ioctl (q->o, TCGETA, &s) >= 0)
+ {
+ s.c_iflag &=~ ISTRIP;
+ (void) ioctl (q->o, TCSETA, &s);
+ }
+ }
+#endif /* HAVE_STRIP_BUG */
+ }
+#endif /* HAVE_BSD_TTY */
+
+ return TRUE;
+}
+
+/* Change settings of a stdin port. */
+
+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 = 0;
+ return fsserial_set (qconn, tparity, tstrip, txonxoff);
+}
+
+/* Run a chat program. */
+
+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;
+
+ /* Pass fkeepuid, fkeepenv and fshell as TRUE. This puts the
+ responsibility of maintaing security on the chat program. */
+ 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;
+ }
+
+ /* The FILE e now is attached to stderr of the program. Forward
+ every line the program outputs to the log file. */
+ 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;
+}
+
+/* Run a chat program on a stdin port. */
+
+/*ARGSUSED*/
+static boolean
+fsstdin_chat (qconn, pzprog)
+ struct sconnection *qconn;
+ char **pzprog;
+{
+ return fsrun_chat (0, 1, pzprog);
+}
+
+/* Run a chat program on any general type of connection. */
+
+boolean
+fsysdep_conn_chat (qconn, pzprog)
+ struct sconnection *qconn;
+ char **pzprog;
+{
+ struct ssysdep_conn *qsysdep;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+ return fsrun_chat (qsysdep->o, qsysdep->o, pzprog);
+}
+
+/* Return baud rate of a serial port. */
+
+static long
+isserial_baud (qconn)
+ struct sconnection *qconn;
+{
+ struct ssysdep_conn *qsysdep;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+ return qsysdep->ibaud;
+}