diff options
Diffstat (limited to 'gnu/libexec/uucp/libunix/cusub.c')
| -rw-r--r-- | gnu/libexec/uucp/libunix/cusub.c | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/libunix/cusub.c b/gnu/libexec/uucp/libunix/cusub.c new file mode 100644 index 000000000000..d1110fd5c7b4 --- /dev/null +++ b/gnu/libexec/uucp/libunix/cusub.c @@ -0,0 +1,1163 @@ +/* cusub.c + System dependent routines for cu. + + Copyright (C) 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 cusub_rcsid[] = "$Id: cusub.c,v 1.1 1993/08/05 18:23:44 conklin Exp $"; +#endif + +#include "uudefs.h" +#include "uuconf.h" +#include "sysdep.h" +#include "system.h" +#include "cu.h" +#include "conn.h" +#include "prot.h" + +#include <errno.h> + +/* 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 + +/* Local variables. */ + +/* The EOF character, as set by fsysdep_terminal_raw. */ +static char bSeof; + +/* The SUSP character, as set by fsysdep_terminal_raw. */ +static char bStstp; + +/* Local functions. */ + +static const char *zsport_line P((const struct uuconf_port *qport)); +static void uscu_child P((struct sconnection *qconn, int opipe)); +static RETSIGTYPE uscu_alarm P((int isig)); +static int cscu_escape P((char *pbcmd, const char *zlocalname)); +static RETSIGTYPE uscu_alarm_kill P((int isig)); + +/* Return the device name for a port, or NULL if none. */ + +static const char * +zsport_line (qport) + const struct uuconf_port *qport; +{ + const char *zline; + + if (qport == NULL) + return NULL; + + switch (qport->uuconf_ttype) + { + default: + case UUCONF_PORTTYPE_STDIN: + return NULL; + case UUCONF_PORTTYPE_MODEM: + zline = qport->uuconf_u.uuconf_smodem.uuconf_zdevice; + break; + case UUCONF_PORTTYPE_DIRECT: + zline = qport->uuconf_u.uuconf_sdirect.uuconf_zdevice; + break; + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + return NULL; + } + + if (zline == NULL) + zline = qport->uuconf_zname; + return zline; +} + +/* Check whether the user has legitimate access to a port. */ + +boolean +fsysdep_port_access (qport) + struct uuconf_port *qport; +{ + const char *zline; + char *zfree; + boolean fret; + + zline = zsport_line (qport); + if (zline == NULL) + return TRUE; + + zfree = NULL; + if (*zline != '/') + { + zfree = zbufalc (sizeof "/dev/" + strlen (zline)); + sprintf (zfree, "/dev/%s", zline); + zline = zfree; + } + + fret = access (zline, R_OK | W_OK) == 0; + ubuffree (zfree); + return fret; +} + +/* Return whether the given port is named by the given line. */ + +boolean +fsysdep_port_is_line (qport, zline) + struct uuconf_port *qport; + const char *zline; +{ + const char *zpline; + char *zfree1, *zfree2; + boolean fret; + + zpline = zsport_line (qport); + if (zpline == NULL) + return FALSE; + + if (strcmp (zline, zpline) == 0) + return TRUE; + + zfree1 = NULL; + zfree2 = NULL; + if (*zline != '/') + { + zfree1 = zbufalc (sizeof "/dev/" + strlen (zline)); + sprintf (zfree1, "/dev/%s", zline); + zline = zfree1; + } + if (*zpline != '/') + { + zfree2 = zbufalc (sizeof "/dev/" + strlen (zpline)); + sprintf (zfree2, "/dev/%s", zpline); + zpline = zfree2; + } + + fret = strcmp (zline, zpline) == 0; + ubuffree (zfree1); + ubuffree (zfree2); + return fret; +} + +/* The cu program wants the system dependent layer to handle the + details of copying data from the communications port to the + terminal. This copying need only be done while executing + fsysdep_cu. On Unix, however, we set up a subprocess to do it all + the time. This subprocess must be controllable via the + fsysdep_cu_copy function. + + We keep a pipe open to the subprocess. When we want it to stop we + send it a signal, and then wait for it to write a byte to us over + the pipe. */ + +/* The subprocess pid. */ +static volatile pid_t iSchild; + +/* The pipe from the subprocess. */ +static int oSpipe; + +/* When we tell the child to stop, it sends this. */ +#define CHILD_STOPPED ('S') + +/* When we tell the child to start, it sends this. */ +#define CHILD_STARTED ('G') + +/* Initialize the subprocess, and have it start copying data. */ + +boolean +fsysdep_cu_init (qconn) + struct sconnection *qconn; +{ + int ai[2]; + + /* Write out anything we may have buffered up during the chat + script. We do this before forking the child only to make it easy + to move the child into a separate executable. */ + while (iPrecend != iPrecstart) + { + char *z; + int c; + + z = abPrecbuf + iPrecstart; + if (iPrecend > iPrecstart) + c = iPrecend - iPrecstart; + else + c = CRECBUFLEN - iPrecstart; + + iPrecstart = (iPrecstart + c) % CRECBUFLEN; + + while (c > 0) + { + int cwrote; + + cwrote = write (1, z, c); + if (cwrote <= 0) + { + if (cwrote < 0) + ulog (LOG_ERROR, "write: %s", strerror (errno)); + else + ulog (LOG_ERROR, "Line disconnected"); + return FALSE; + } + c -= cwrote; + z += cwrote; + } + } + + if (pipe (ai) < 0) + { + ulog (LOG_ERROR, "pipe: %s", strerror (errno)); + return FALSE; + } + + iSchild = ixsfork (); + if (iSchild < 0) + { + ulog (LOG_ERROR, "fork: %s", strerror (errno)); + return FALSE; + } + + if (iSchild == 0) + { + (void) close (ai[0]); + uscu_child (qconn, ai[1]); + /*NOTREACHED*/ + } + + (void) close (ai[1]); + + oSpipe = ai[0]; + + return TRUE; +} + +/* Copy all data from the terminal to the communications port. If we + see an escape character following a newline character, read the + next character and return it. */ + +boolean +fsysdep_cu (qconn, pbcmd, zlocalname) + struct sconnection *qconn; + char *pbcmd; + const char *zlocalname; +{ + boolean fstart; + char b; + int c; + + fstart = TRUE; + + while (TRUE) + { + if (fsysdep_catch ()) + usysdep_start_catch (); + else + { + ulog (LOG_ERROR, (const char *) NULL); + return FALSE; + } + + c = read (0, &b, 1); + + usysdep_end_catch (); + + if (c <= 0) + break; + + if (fstart && b == *zCuvar_escape) + { + c = cscu_escape (pbcmd, zlocalname); + if (c <= 0) + break; + if (*pbcmd != b) + { + write (1, pbcmd, 1); + + /* For Unix, we let the eof character be the same as + '.', and we let the suspend character (if any) be the + same as 'z'. */ + if (*pbcmd == bSeof) + *pbcmd = '.'; + if (*pbcmd == bStstp) + *pbcmd = 'z'; + return TRUE; + } + } + if (! fconn_write (qconn, &b, (size_t) 1)) + return FALSE; + fstart = strchr (zCuvar_eol, b) != NULL; + } + + if (c < 0) + { + if (errno != EINTR) + ulog (LOG_ERROR, "read: %s", strerror (errno)); + else + ulog (LOG_ERROR, (const char *) NULL); + return FALSE; + } + + /* I'm not sure what's best in this case. */ + ulog (LOG_ERROR, "End of file on terminal"); + return FALSE; +} + +/* A SIGALRM handler that sets fScu_alarm and optionally longjmps. */ + +volatile sig_atomic_t fScu_alarm; + +static RETSIGTYPE +uscu_alarm (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_alarm); +#endif + + fScu_alarm = TRUE; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif +} + +/* We've just seen an escape character. We print the host name, + optionally after a 1 second delay. We read the next character from + the terminal and return it. The 1 second delay on the host name is + mostly to be fancy; it lets ~~ look smoother. */ + +static int +cscu_escape (pbcmd, zlocalname) + char *pbcmd; + const char *zlocalname; +{ + CATCH_PROTECT int c; + + write (1, zCuvar_escape, 1); + + fScu_alarm = FALSE; + usset_signal (SIGALRM, uscu_alarm, TRUE, (boolean *) NULL); + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + alarm (1); + } + + c = 0; + + while (TRUE) + { + if (fScu_alarm) + { + char b; + + fScu_alarm = FALSE; + b = '['; + write (1, &b, 1); + write (1, zlocalname, strlen (zlocalname)); + b = ']'; + write (1, &b, 1); + } + + if (c <= 0) + c = read (0, pbcmd, 1); + if (c >= 0 || errno != EINTR) + { + usysdep_end_catch (); + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + return c; + } + } +} + +/* A SIGALRM handler which does nothing but send a signal to the child + process and schedule another alarm. POSIX.1 permits kill and alarm + from a signal handler. The reference to static data may or may not + be permissible. */ + +static volatile sig_atomic_t iSsend_sig; + +static RETSIGTYPE +uscu_alarm_kill (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_alarm_kill); +#endif + + (void) kill (iSchild, iSsend_sig); + + alarm (1); +} + +/* Start or stop copying data from the communications port to the + terminal. We send a signal to the child process to tell it what to + do. Unfortunately, there are race conditions in the child, so we + keep sending it a signal once a second until it responds. We send + SIGUSR1 to make it start copying, and SIGUSR2 to make it stop. */ + +boolean +fsysdep_cu_copy (fcopy) + boolean fcopy; +{ + int ierr; + int c; + + usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); + if (fcopy) + iSsend_sig = SIGUSR1; + else + iSsend_sig = SIGUSR2; + + uscu_alarm_kill (SIGALRM); + + alarm (1); + + while (TRUE) + { + char b; + + c = read (oSpipe, &b, 1); + +#if DEBUG > 1 + if (c > 0) + DEBUG_MESSAGE1 (DEBUG_INCOMING, + "fsysdep_cu_copy: Got '%d'", b); +#endif + + if ((c < 0 && errno != EINTR) + || c == 0 + || (c > 0 && b == (fcopy ? CHILD_STARTED : CHILD_STOPPED))) + break; + + /* If none of the above conditions were true, then we either got + an EINTR error, in which case we probably timed out and the + SIGALRM handler resent the signal, or we read the wrong + character, in which case we will just read again from the + pipe. */ + } + + ierr = errno; + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + + if (c > 0) + return TRUE; + + if (c == 0) + ulog (LOG_ERROR, "EOF on child pipe"); + else + ulog (LOG_ERROR, "read: %s", strerror (ierr)); + + return FALSE; +} + +/* Shut down cu by killing the child process. */ + +boolean +fsysdep_cu_finish () +{ + (void) close (oSpipe); + + /* We hit the child with SIGTERM, give it two seconds to die, and + then send a SIGKILL. */ + if (kill (iSchild, SIGTERM) < 0) + { + /* Don't give an error if the child has already died. */ + if (errno != ESRCH) + ulog (LOG_ERROR, "kill: %s", strerror (errno)); + } + + usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL); + iSsend_sig = SIGKILL; + alarm (2); + + (void) ixswait ((unsigned long) iSchild, "child"); + + usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL); + alarm (0); + + return TRUE; +} + +/* Code for the child process. */ + +/* This signal handler just records the signal. In this case we only + care about which signal we received most recently. */ + +static volatile sig_atomic_t iSchild_sig; + +static RETSIGTYPE +uscu_child_handler (isig) + int isig; +{ +#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET + (void) signal (isig, uscu_child_handler); +#endif + + iSchild_sig = isig; + +#if HAVE_RESTARTABLE_SYSCALLS + if (fSjmp) + longjmp (sSjmp_buf, 1); +#endif /* HAVE_RESTARTABLE_SYSCALLS */ +} + +/* The child process. This copies the port to the terminal, except + when it is stopped by a signal. It would be reasonable to write a + separate program for this, probably passing it the port on stdin. + This would reduce the memory requirements, since we wouldn't need a + second process holding all the configuration stuff, and also let it + work reasonably on 680x0 versions of MINIX. */ + +static void +uscu_child (qconn, opipe) + struct sconnection *qconn; + int opipe; +{ + CATCH_PROTECT int oport; + CATCH_PROTECT boolean fstopped, fgot; + CATCH_PROTECT int cwrite; + CATCH_PROTECT char abbuf[1024]; + + /* It would be nice if we could just use fsserial_read, but that + will log signals that we don't want logged. There should be a + generic way to extract the file descriptor from the port. */ + if (qconn->qport == NULL) + oport = 0; + else + { + switch (qconn->qport->uuconf_ttype) + { +#if DEBUG > 0 + default: + ulog (LOG_FATAL, "uscu_child: Can't happen"); + oport = -1; + break; +#endif + case UUCONF_PORTTYPE_STDIN: + oport = 0; + break; + case UUCONF_PORTTYPE_MODEM: + case UUCONF_PORTTYPE_DIRECT: + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + oport = ((struct ssysdep_conn *) qconn->psysdep)->o; + break; + } + } + + usset_signal (SIGUSR1, uscu_child_handler, TRUE, (boolean *) NULL); + usset_signal (SIGUSR2, uscu_child_handler, TRUE, (boolean *) NULL); + usset_signal (SIGINT, SIG_IGN, TRUE, (boolean *) NULL); + usset_signal (SIGQUIT, SIG_IGN, TRUE, (boolean *) NULL); + usset_signal (SIGPIPE, SIG_DFL, TRUE, (boolean *) NULL); + usset_signal (SIGTERM, uscu_child_handler, TRUE, (boolean *) NULL); + + fstopped = FALSE; + fgot = FALSE; + iSchild_sig = 0; + cwrite = 0; + + if (fsysdep_catch ()) + usysdep_start_catch (); + + while (TRUE) + { + int isig; + int c; + + /* There is a race condition here between checking the signal + and receiving a new and possibly different one. This is + solved by having the parent resend the signal until it gets a + response. */ + isig = iSchild_sig; + iSchild_sig = 0; + if (isig != 0) + { + char b; + + if (isig == SIGTERM) + exit (EXIT_SUCCESS); + + if (isig == SIGUSR1) + { + fstopped = FALSE; + b = CHILD_STARTED; + } + else + { + fstopped = TRUE; + b = CHILD_STOPPED; + cwrite = 0; + } + + c = write (opipe, &b, 1); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) + c = 0; + + if (c <= 0) + { + /* Should we give an error message here? */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_FAILURE); + } + } + + if (fstopped) + pause (); + else if (cwrite > 0) + { + char *zbuf; + + zbuf = abbuf; + while (cwrite > 0) + { + c = write (1, zbuf, cwrite); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN + || errno == EWOULDBLOCK + || errno == ENODATA)) + c = 0; + + if (c < 0 && errno == EINTR) + break; + if (c <= 0) + { + /* Should we give an error message here? */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_FAILURE); + } + cwrite -= c; + zbuf += c; + } + } + else + { + /* On some systems apparently read will return 0 until + something has been written to the port. We therefore + accept a 0 return until after we have managed to read + something. Setting errno to 0 apparently avoids a + problem on Coherent. */ + errno = 0; + c = read (oport, abbuf, sizeof abbuf); + + /* Apparently on some systems we can get EAGAIN here. */ + if (c < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)) + c = 0; + + if ((c == 0 && fgot) + || (c < 0 && errno != EINTR)) + { + /* This can be a normal way to exit, depending on just + how the connection is dropped. */ + (void) kill (getppid (), SIGHUP); + exit (EXIT_SUCCESS); + } + if (c > 0) + { + fgot = TRUE; + cwrite = c; + } + } + } +} + +/* Terminal control routines. */ + +/* Whether file descriptor 0 is attached to a terminal or not. */ +static boolean fSterm; + +/* Whether we are doing local echoing. */ +static boolean fSlocalecho; + +/* The original state of the terminal. */ +static sterminal sSterm_orig; + +/* The new state of the terminal. */ +static sterminal sSterm_new; + +#if ! HAVE_BSD_TTY +#ifdef SIGTSTP +/* Whether SIGTSTP is being ignored. */ +static boolean fStstp_ignored; +#endif +#endif + +/* Set the terminal into raw mode. */ + +boolean +fsysdep_terminal_raw (flocalecho) + boolean flocalecho; +{ + fSlocalecho = flocalecho; + + /* This defaults may be overriden below. */ + bSeof = '\004'; + bStstp = '\032'; + + if (! fgetterminfo (0, &sSterm_orig)) + { + fSterm = FALSE; + return TRUE; + } + + fSterm = TRUE; + + sSterm_new = sSterm_orig; + +#if HAVE_BSD_TTY + + /* We use CBREAK mode rather than RAW mode, because RAW mode turns + off all output processing, which we don't want to do. This means + that we have to disable the interrupt characters, which we do by + setting them to -1. */ + bSeof = sSterm_orig.stchars.t_eofc; + + sSterm_new.stchars.t_intrc = -1; + sSterm_new.stchars.t_quitc = -1; + sSterm_new.stchars.t_startc = -1; + sSterm_new.stchars.t_stopc = -1; + sSterm_new.stchars.t_eofc = -1; + sSterm_new.stchars.t_brkc = -1; + + bStstp = sSterm_orig.sltchars.t_suspc; + + sSterm_new.sltchars.t_suspc = -1; + sSterm_new.sltchars.t_dsuspc = -1; + sSterm_new.sltchars.t_rprntc = -1; + sSterm_new.sltchars.t_flushc = -1; + sSterm_new.sltchars.t_werasc = -1; + sSterm_new.sltchars.t_lnextc = -1; + + if (! flocalecho) + { + sSterm_new.stty.sg_flags |= (CBREAK | ANYP); + sSterm_new.stty.sg_flags &=~ (ECHO | CRMOD | TANDEM); + } + else + { + sSterm_new.stty.sg_flags |= (CBREAK | ANYP | ECHO); + sSterm_new.stty.sg_flags &=~ (CRMOD | TANDEM); + } + +#endif /* HAVE_BSD_TTY */ + +#if HAVE_SYSV_TERMIO + + bSeof = sSterm_new.c_cc[VEOF]; + if (! flocalecho) + sSterm_new.c_lflag &=~ (ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHONL); + else + sSterm_new.c_lflag &=~ (ICANON | ISIG); + sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL); + sSterm_new.c_oflag &=~ (OPOST); + sSterm_new.c_cc[VMIN] = 1; + sSterm_new.c_cc[VTIME] = 0; + +#endif /* HAVE_SYSV_TERMIO */ + +#if HAVE_POSIX_TERMIOS + + bSeof = sSterm_new.c_cc[VEOF]; + bStstp = sSterm_new.c_cc[VSUSP]; + if (! flocalecho) + sSterm_new.c_lflag &=~ + (ICANON | IEXTEN | ISIG | ECHO | ECHOE | ECHOK | ECHONL); + else + sSterm_new.c_lflag &=~ (ICANON | IEXTEN | ISIG); + sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL); + sSterm_new.c_oflag &=~ (OPOST); + sSterm_new.c_cc[VMIN] = 1; + sSterm_new.c_cc[VTIME] = 0; + +#endif /* HAVE_POSIX_TERMIOS */ + + if (! fsetterminfo (0, &sSterm_new)) + { + ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Restore the terminal to its original setting. */ + +boolean +fsysdep_terminal_restore () +{ + if (! fSterm) + return TRUE; + + if (! fsetterminfo (0, &sSterm_orig)) + { + ulog (LOG_ERROR, "Can't restore terminal: %s", strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Read a line from the terminal. This will be called after + fsysdep_terminal_raw has been called. */ + +char * +zsysdep_terminal_line (zprompt) + const char *zprompt; +{ + CATCH_PROTECT size_t cbuf = 0; + CATCH_PROTECT char *zbuf = NULL; + CATCH_PROTECT size_t cgot = 0; + + if (zprompt != NULL && *zprompt != '\0') + (void) write (1, zprompt, strlen (zprompt)); + + /* Forgot about any previous SIGINT or SIGQUIT signals we may have + received. We don't worry about the race condition here, since we + can't get these signals from the terminal at the moment and it's + not too likely that somebody else will be sending them to us. */ + afSignal[INDEXSIG_SIGINT] = 0; + afSignal[INDEXSIG_SIGQUIT] = 0; + + if (! fsysdep_terminal_restore ()) + return NULL; + + if (fsysdep_catch ()) + { + usysdep_start_catch (); + cbuf = 0; + zbuf = NULL; + cgot = 0; + } + + while (TRUE) + { + char b; + int c; + + if (afSignal[INDEXSIG_SIGINT] + || afSignal[INDEXSIG_SIGQUIT]) + { + usysdep_end_catch (); + /* Make sure the signal is logged. */ + ulog (LOG_ERROR, (const char *) NULL); + /* Return an empty string. */ + cgot = 0; + break; + } + + /* There's a race here between checking the signals and calling + read. It just means that the user will have to hit ^C more + than once. */ + + c = read (0, &b, 1); + if (c < 0) + { + if (errno == EINTR) + continue; + usysdep_end_catch (); + ulog (LOG_ERROR, "read: %s", strerror (errno)); + (void) fsysdep_terminal_raw (fSlocalecho); + return NULL; + } + if (c == 0) + { + /* I'm not quite sure what to do here. */ + usysdep_end_catch (); + ulog (LOG_ERROR, "EOF on terminal"); + (void) fsysdep_terminal_raw (fSlocalecho); + return NULL; + } + + if (cgot >= cbuf) + { + char *znew; + + cbuf += 64; + znew = zbufalc (cbuf); + if (zbuf != NULL) + { + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + } + zbuf = znew; + } + + zbuf[cgot] = b; + + ++cgot; + + if (b == '\n') + { + usysdep_end_catch (); + break; + } + } + + if (cgot >= cbuf) + { + char *znew; + + ++cbuf; + znew = zbufalc (cbuf); + if (zbuf != NULL) + { + memcpy (znew, zbuf, cgot); + ubuffree (zbuf); + } + zbuf = znew; + } + + zbuf[cgot] = '\0'; + + if (! fsysdep_terminal_raw (fSlocalecho)) + return NULL; + + return zbuf; +} + +/* Write a line to the terminal with a trailing newline. */ + +boolean +fsysdep_terminal_puts (zline) + const char *zline; +{ + char *zalc, *zprint; + size_t clen; + + if (zline == NULL) + { + zalc = zbufalc (2); + clen = 0; + } + else + { + clen = strlen (zline); + zalc = zbufalc (clen + 2); + memcpy (zalc, zline, clen); + } + + if (fSterm) + { + zalc[clen] = '\r'; + ++clen; + } + zalc[clen] = '\n'; + ++clen; + + zprint = zalc; + while (clen > 0) + { + int c; + + c = write (1, zprint, clen); + if (c <= 0) + { + ubuffree (zalc); + ulog (LOG_ERROR, "write: %s", strerror (errno)); + return FALSE; + } + clen -= c; + zprint += c; + } + + ubuffree (zalc); + + return TRUE; +} + +/* Allow or disallow signals from the terminal. */ + +boolean +fsysdep_terminal_signals (faccept) + boolean faccept; +{ +#if HAVE_BSD_TTY + + if (faccept) + { + sSterm_new.stchars.t_intrc = sSterm_orig.stchars.t_intrc; + sSterm_new.stchars.t_quitc = sSterm_orig.stchars.t_quitc; + } + else + { + sSterm_new.stchars.t_intrc = -1; + sSterm_new.stchars.t_quitc = -1; + } + +#else /* ! HAVE_BSD_TTY */ + + if (faccept) + sSterm_new.c_lflag |= ISIG; + else + sSterm_new.c_lflag &=~ ISIG; + +#ifdef SIGTSTP + /* We only want to get SIGINT and SIGQUIT, not SIGTSTP. This + function will be called with faccept TRUE before it is called + with faccept FALSE, so fStstp_ignored will be correctly + initialized. */ + if (faccept) + usset_signal (SIGTSTP, SIG_IGN, FALSE, &fStstp_ignored); + else if (! fStstp_ignored) + usset_signal (SIGTSTP, SIG_DFL, TRUE, (boolean *) NULL); +#endif + +#endif /* ! HAVE_BSD_TTY */ + + if (! fsetterminfo (0, &sSterm_new)) + { + ulog (LOG_ERROR, "Can't set terminal: %s", strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/* Start up a command, or possibly just a shell. Optionally attach + stdin or stdout to the port. We attach directly to the port, + rather than copying the data ourselves. */ + +boolean +fsysdep_shell (qconn, zcmd, tcmd) + struct sconnection *qconn; + const char *zcmd; + enum tshell_cmd tcmd; +{ + const char *azargs[4]; + int oread, owrite; + int aidescs[3]; + pid_t ipid; + + azargs[0] = "/bin/sh"; + if (zcmd == NULL || *zcmd == '\0') + azargs[1] = NULL; + else + { + azargs[1] = "-c"; + azargs[2] = zcmd; + azargs[3] = NULL; + } + + if (qconn->qport == NULL) + { + oread = 0; + owrite = 1; + } + else + { + switch (qconn->qport->uuconf_ttype) + { + default: + oread = owrite = -1; + break; + case UUCONF_PORTTYPE_STDIN: + oread = 0; + owrite = 1; + break; + case UUCONF_PORTTYPE_MODEM: + case UUCONF_PORTTYPE_DIRECT: + case UUCONF_PORTTYPE_TCP: + case UUCONF_PORTTYPE_TLI: + oread = owrite = ((struct ssysdep_conn *) qconn->psysdep)->o; + break; + } + } + + aidescs[0] = 0; + aidescs[1] = 1; + aidescs[2] = 2; + + if (tcmd == SHELL_STDIN_FROM_PORT || tcmd == SHELL_STDIO_ON_PORT) + aidescs[0] = oread; + if (tcmd == SHELL_STDOUT_TO_PORT || tcmd == SHELL_STDIO_ON_PORT) + aidescs[1] = owrite; + + ipid = ixsspawn (azargs, aidescs, FALSE, TRUE, (const char *) NULL, + FALSE, FALSE, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + if (ipid < 0) + { + ulog (LOG_ERROR, "ixsspawn (/bin/sh): %s", strerror (errno)); + return FALSE; + } + + return ixswait ((unsigned long) ipid, "shell") == 0; +} + +/* Change directories. */ + +boolean +fsysdep_chdir (zdir) + const char *zdir; +{ + if (zdir == NULL || *zdir == '\0') + { + zdir = getenv ("HOME"); + if (zdir == NULL) + { + ulog (LOG_ERROR, "HOME not defined"); + return FALSE; + } + } + if (chdir (zdir) < 0) + { + ulog (LOG_ERROR, "chdir (%s): %s", zdir, strerror (errno)); + return FALSE; + } + return TRUE; +} + +/* Suspend the current process. */ + +boolean +fsysdep_suspend () +{ +#ifndef SIGTSTP + return fsysdep_terminal_puts ("[process suspension not supported]"); +#else + return kill (getpid (), SIGTSTP) == 0; +#endif +} |
