1/* cusub.c
2   System dependent routines for cu.
3
4   Copyright (C) 1992, 1993, 1995, 2002 Ian Lance Taylor
5
6   This file is part of the Taylor UUCP package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21
22   The author of the program may be contacted at ian@airs.com.
23   */
24
25#include "uucp.h"
26
27#if USE_RCS_ID
28const char cusub_rcsid[] = "$Id: cusub.c,v 1.27 2002/03/05 19:10:42 ian Rel $";
29#endif
30
31#include "uudefs.h"
32#include "uuconf.h"
33#include "sysdep.h"
34#include "system.h"
35#include "cu.h"
36#include "conn.h"
37#include "prot.h"
38
39#if HAVE_FCNTL_H
40#include <fcntl.h>
41#else
42#if HAVE_SYS_FILE_H
43#include <sys/file.h>
44#endif
45#endif
46
47/* Get definitions for both O_NONBLOCK and O_NDELAY.  */
48#ifndef O_NDELAY
49#ifdef FNDELAY
50#define O_NDELAY FNDELAY
51#else /* ! defined (FNDELAY) */
52#define O_NDELAY 0
53#endif /* ! defined (FNDELAY) */
54#endif /* ! defined (O_NDELAY) */
55
56#ifndef O_NONBLOCK
57#ifdef FNBLOCK
58#define O_NONBLOCK FNBLOCK
59#else /* ! defined (FNBLOCK) */
60#define O_NONBLOCK 0
61#endif /* ! defined (FNBLOCK) */
62#endif /* ! defined (O_NONBLOCK) */
63
64#include <errno.h>
65
66/* 4.2 systems don't define SIGUSR2.  This should work for them.  On
67   systems which are missing SIGUSR1, or SIGURG, you must find two
68   signals which you can safely use.  */
69#ifndef SIGUSR2
70#define SIGUSR2 SIGURG
71#endif
72
73/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA.  */
74#ifndef EAGAIN
75#ifndef EWOULDBLOCK
76#define EAGAIN (-1)
77#define EWOULDBLOCK (-1)
78#else /* defined (EWOULDBLOCK) */
79#define EAGAIN EWOULDBLOCK
80#endif /* defined (EWOULDBLOCK) */
81#else /* defined (EAGAIN) */
82#ifndef EWOULDBLOCK
83#define EWOULDBLOCK EAGAIN
84#endif /* ! defined (EWOULDBLOCK) */
85#endif /* defined (EAGAIN) */
86
87#ifndef ENODATA
88#define ENODATA EAGAIN
89#endif
90
91/* Local variables.  */
92
93/* The EOF character, as set by fsysdep_terminal_raw.  */
94static char bSeof;
95
96/* The SUSP character, as set by fsysdep_terminal_raw.  */
97static char bStstp;
98
99/* Local functions.  */
100
101static const char *zsport_line P((const struct uuconf_port *qport));
102static void uscu_child P((struct sconnection *qconn, int opipe));
103static RETSIGTYPE uscu_child_handler P((int isig));
104static RETSIGTYPE uscu_alarm P((int isig));
105static int cscu_escape P((char *pbcmd, const char *zlocalname));
106static RETSIGTYPE uscu_alarm_kill P((int isig));
107
108/* Return the device name for a port, or NULL if none.  */
109
110static const char *
111zsport_line (qport)
112     const struct uuconf_port *qport;
113{
114  const char *zline;
115
116  if (qport == NULL)
117    return NULL;
118
119  switch (qport->uuconf_ttype)
120    {
121    default:
122    case UUCONF_PORTTYPE_STDIN:
123      return NULL;
124    case UUCONF_PORTTYPE_MODEM:
125      zline = qport->uuconf_u.uuconf_smodem.uuconf_zdevice;
126      break;
127    case UUCONF_PORTTYPE_DIRECT:
128      zline = qport->uuconf_u.uuconf_sdirect.uuconf_zdevice;
129      break;
130    case UUCONF_PORTTYPE_TCP:
131    case UUCONF_PORTTYPE_TLI:
132    case UUCONF_PORTTYPE_PIPE:
133      return NULL;
134    }
135
136  if (zline == NULL)
137    zline = qport->uuconf_zname;
138  return zline;
139}
140
141/* Check whether the user has legitimate access to a port.  */
142
143boolean
144fsysdep_port_access (qport)
145     struct uuconf_port *qport;
146{
147  const char *zline;
148  char *zfree;
149  boolean fret;
150
151  zline = zsport_line (qport);
152  if (zline == NULL)
153    return TRUE;
154
155  zfree = NULL;
156  if (*zline != '/')
157    {
158      zfree = zbufalc (sizeof "/dev/" + strlen (zline));
159      sprintf (zfree, "/dev/%s", zline);
160      zline = zfree;
161    }
162
163  fret = access (zline, R_OK | W_OK) == 0;
164  ubuffree (zfree);
165  return fret;
166}
167
168/* Return whether the given port is named by the given line.  */
169
170boolean
171fsysdep_port_is_line (qport, zline)
172     struct uuconf_port *qport;
173     const char *zline;
174{
175  const char *zpline;
176  char *zfree1, *zfree2;
177  boolean fret;
178
179  zpline = zsport_line (qport);
180  if (zpline == NULL)
181    return FALSE;
182
183  if (strcmp (zline, zpline) == 0)
184    return TRUE;
185
186  zfree1 = NULL;
187  zfree2 = NULL;
188  if (*zline != '/')
189    {
190      zfree1 = zbufalc (sizeof "/dev/" + strlen (zline));
191      sprintf (zfree1, "/dev/%s", zline);
192      zline = zfree1;
193    }
194  if (*zpline != '/')
195    {
196      zfree2 = zbufalc (sizeof "/dev/" + strlen (zpline));
197      sprintf (zfree2, "/dev/%s", zpline);
198      zpline = zfree2;
199    }
200
201  fret = strcmp (zline, zpline) == 0;
202  ubuffree (zfree1);
203  ubuffree (zfree2);
204  return fret;
205}
206
207/* The cu program wants the system dependent layer to handle the
208   details of copying data from the communications port to the
209   terminal.  This copying need only be done while executing
210   fsysdep_cu.  On Unix, however, we set up a subprocess to do it all
211   the time.  This subprocess must be controllable via the
212   fsysdep_cu_copy function.
213
214   We keep a pipe open to the subprocess.  When we want it to stop we
215   send it a signal, and then wait for it to write a byte to us over
216   the pipe.  */
217
218/* The subprocess pid.  */
219static volatile pid_t iSchild;
220
221/* The pipe from the subprocess.  */
222static int oSpipe;
223
224/* When we tell the child to stop, it sends this.  */
225#define CHILD_STOPPED ('S')
226
227/* When we tell the child to start, it sends this.  */
228#define CHILD_STARTED ('G')
229
230/* Initialize the subprocess, and have it start copying data.  */
231
232boolean
233fsysdep_cu_init (qconn)
234     struct sconnection *qconn;
235{
236  int ai[2];
237
238  /* Write out anything we may have buffered up during the chat
239     script.  We do this before forking the child only to make it easy
240     to move the child into a separate executable.  */
241  while (iPrecend != iPrecstart)
242    {
243      char *z;
244      int c;
245
246      z = abPrecbuf + iPrecstart;
247      if (iPrecend > iPrecstart)
248	c = iPrecend - iPrecstart;
249      else
250	c = CRECBUFLEN - iPrecstart;
251
252      iPrecstart = (iPrecstart + c) % CRECBUFLEN;
253
254      while (c > 0)
255	{
256	  int cwrote;
257
258	  cwrote = write (1, z, c);
259	  if (cwrote <= 0)
260	    {
261	      if (cwrote < 0)
262		ulog (LOG_ERROR, "write: %s", strerror (errno));
263	      else
264		ulog (LOG_ERROR, "Line disconnected");
265	      return FALSE;
266	    }
267	  c -= cwrote;
268	  z += cwrote;
269	}
270    }
271
272  if (pipe (ai) < 0)
273    {
274      ulog (LOG_ERROR, "pipe: %s", strerror (errno));
275      return FALSE;
276    }
277
278  iSchild = ixsfork ();
279  if (iSchild < 0)
280    {
281      ulog (LOG_ERROR, "fork: %s", strerror (errno));
282      return FALSE;
283    }
284
285  if (iSchild == 0)
286    {
287      (void) close (ai[0]);
288      uscu_child (qconn, ai[1]);
289      /*NOTREACHED*/
290    }
291
292  (void) close (ai[1]);
293
294  oSpipe = ai[0];
295
296  return TRUE;
297}
298
299/* Copy all data from the terminal to the communications port.  If we
300   see an escape character following a newline character, read the
301   next character and return it.  */
302
303boolean
304fsysdep_cu (qconn, pbcmd, zlocalname)
305     struct sconnection *qconn;
306     char *pbcmd;
307     const char *zlocalname;
308{
309  boolean fstart;
310  char b;
311  int c;
312
313  fstart = TRUE;
314
315  while (TRUE)
316    {
317      if (fsysdep_catch ())
318	usysdep_start_catch ();
319      else
320	{
321	  ulog (LOG_ERROR, (const char *) NULL);
322	  return FALSE;
323	}
324
325      c = read (0, &b, 1);
326
327      usysdep_end_catch ();
328
329      if (c <= 0)
330	break;
331
332      if (fstart && b == *zCuvar_escape && b != '\0')
333	{
334	  c = cscu_escape (pbcmd, zlocalname);
335	  if (c <= 0)
336	    break;
337	  if (*pbcmd != b)
338	    {
339	      write (1, pbcmd, 1);
340
341	      /* For Unix, we let the eof character be the same as
342		 '.', and we let the suspend character (if any) be the
343		 same as 'z'.  */
344	      if (*pbcmd == bSeof)
345		*pbcmd = '.';
346	      if (*pbcmd == bStstp)
347		*pbcmd = 'z';
348	      return TRUE;
349	    }
350	}
351      if (! fconn_write (qconn, &b, (size_t) 1))
352	return FALSE;
353      fstart = strchr (zCuvar_eol, b) != NULL;
354    }
355
356  if (c < 0)
357    {
358      if (errno != EINTR)
359	ulog (LOG_ERROR, "read: %s", strerror (errno));
360      else
361	ulog (LOG_ERROR, (const char *) NULL);
362      return FALSE;
363    }
364
365  /* I'm not sure what's best in this case.  */
366  ulog (LOG_ERROR, "End of file on terminal");
367  return FALSE;
368}
369
370/* A SIGALRM handler that sets fScu_alarm and optionally longjmps.  */
371
372volatile sig_atomic_t fScu_alarm;
373
374static RETSIGTYPE
375uscu_alarm (isig)
376     int isig ATTRIBUTE_UNUSED;
377{
378#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET
379  (void) signal (isig, uscu_alarm);
380#endif
381
382  fScu_alarm = TRUE;
383
384#if HAVE_RESTARTABLE_SYSCALLS
385  if (fSjmp)
386    longjmp (sSjmp_buf, 1);
387#endif
388}
389
390/* We've just seen an escape character.  We print the host name,
391   optionally after a 1 second delay.  We read the next character from
392   the terminal and return it.  The 1 second delay on the host name is
393   mostly to be fancy; it lets ~~ look smoother.  */
394
395static int
396cscu_escape (pbcmd, zlocalname)
397     char *pbcmd;
398     const char *zlocalname;
399{
400  CATCH_PROTECT int c;
401
402  write (1, zCuvar_escape, 1);
403
404  fScu_alarm = FALSE;
405  usset_signal (SIGALRM, uscu_alarm, TRUE, (boolean *) NULL);
406
407  if (fsysdep_catch ())
408    {
409      usysdep_start_catch ();
410      alarm (1);
411    }
412
413  c = 0;
414
415  while (TRUE)
416    {
417      if (fScu_alarm)
418	{
419	  char b;
420
421	  fScu_alarm = FALSE;
422	  b = '[';
423	  write (1, &b, 1);
424	  write (1, zlocalname, strlen (zlocalname));
425	  b = ']';
426	  write (1, &b, 1);
427	}
428
429      if (c <= 0)
430	c = read (0, pbcmd, 1);
431      if (c >= 0 || errno != EINTR)
432	{
433	  usysdep_end_catch ();
434	  usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
435	  alarm (0);
436	  return c;
437	}
438    }
439}
440
441/* A SIGALRM handler which does nothing but send a signal to the child
442   process and schedule another alarm.  POSIX.1 permits kill and alarm
443   from a signal handler.  The reference to static data may or may not
444   be permissible.  */
445
446static volatile sig_atomic_t iSsend_sig;
447
448static RETSIGTYPE
449uscu_alarm_kill (isig)
450     int isig ATTRIBUTE_UNUSED;
451{
452#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET
453  (void) signal (isig, uscu_alarm_kill);
454#endif
455
456  (void) kill (iSchild, iSsend_sig);
457
458  alarm (1);
459}
460
461/* Start or stop copying data from the communications port to the
462   terminal.  We send a signal to the child process to tell it what to
463   do.  Unfortunately, there are race conditions in the child, so we
464   keep sending it a signal once a second until it responds.  We send
465   SIGUSR1 to make it start copying, and SIGUSR2 to make it stop.  */
466
467boolean
468fsysdep_cu_copy (fcopy)
469     boolean fcopy;
470{
471  int ierr;
472  int c;
473
474  usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL);
475  if (fcopy)
476    iSsend_sig = SIGUSR1;
477  else
478    iSsend_sig = SIGUSR2;
479
480  uscu_alarm_kill (SIGALRM);
481
482  alarm (1);
483
484  while (TRUE)
485    {
486      char b;
487
488      c = read (oSpipe, &b, 1);
489
490#if DEBUG > 1
491      if (c > 0)
492	DEBUG_MESSAGE1 (DEBUG_INCOMING,
493			"fsysdep_cu_copy: Got '%d'", b);
494#endif
495
496      if ((c < 0 && errno != EINTR)
497	  || c == 0
498	  || (c > 0 && b == (fcopy ? CHILD_STARTED : CHILD_STOPPED)))
499	break;
500
501      /* If none of the above conditions were true, then we either got
502	 an EINTR error, in which case we probably timed out and the
503	 SIGALRM handler resent the signal, or we read the wrong
504	 character, in which case we will just read again from the
505	 pipe.  */
506    }
507
508  ierr = errno;
509
510  usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
511  alarm (0);
512
513  if (c > 0)
514    return TRUE;
515
516  if (c == 0)
517    ulog (LOG_ERROR, "EOF on child pipe");
518  else
519    ulog (LOG_ERROR, "read: %s", strerror (ierr));
520
521  return FALSE;
522}
523
524/* Shut down cu by killing the child process.  */
525
526boolean
527fsysdep_cu_finish ()
528{
529  (void) close (oSpipe);
530
531  /* We hit the child with SIGTERM, give it two seconds to die, and
532     then send a SIGKILL.  */
533  if (kill (iSchild, SIGTERM) < 0)
534    {
535      /* Don't give an error if the child has already died.  */
536      if (errno != ESRCH)
537	ulog (LOG_ERROR, "kill: %s", strerror (errno));
538    }
539
540  usset_signal (SIGALRM, uscu_alarm_kill, TRUE, (boolean *) NULL);
541  iSsend_sig = SIGKILL;
542  alarm (2);
543
544  (void) ixswait ((unsigned long) iSchild, "child");
545
546  usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
547  alarm (0);
548
549  return TRUE;
550}
551
552/* Code for the child process.  */
553
554/* This signal handler just records the signal.  In this case we only
555   care about which signal we received most recently.  */
556
557static volatile sig_atomic_t iSchild_sig;
558
559static RETSIGTYPE
560uscu_child_handler (isig)
561     int isig;
562{
563#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET
564  (void) signal (isig, uscu_child_handler);
565#endif
566
567  iSchild_sig = isig;
568
569#if HAVE_RESTARTABLE_SYSCALLS
570  if (fSjmp)
571    longjmp (sSjmp_buf, 1);
572#endif /* HAVE_RESTARTABLE_SYSCALLS */
573}
574
575/* The child process.  This copies the port to the terminal, except
576   when it is stopped by a signal.  It would be reasonable to write a
577   separate program for this, probably passing it the port on stdin.
578   This would reduce the memory requirements, since we wouldn't need a
579   second process holding all the configuration stuff, and also let it
580   work reasonably on 680x0 versions of MINIX.  */
581
582static void
583uscu_child (qconn, opipe)
584     struct sconnection *qconn;
585     int opipe;
586{
587  CATCH_PROTECT int oport;
588  CATCH_PROTECT boolean fstopped, fgot;
589  CATCH_PROTECT int cwrite;
590  CATCH_PROTECT char abbuf[1024];
591
592  fgot = FALSE;
593
594  /* It would be nice if we could just use fsysdep_conn_read, but that
595     will log signals that we don't want logged.  There should be a
596     generic way to extract the file descriptor from the port.  */
597  if (qconn->qport == NULL)
598    oport = 0;
599  else
600    {
601      switch (qconn->qport->uuconf_ttype)
602	{
603#if DEBUG > 0
604	default:
605	  ulog (LOG_FATAL, "uscu_child: Can't happen");
606	  oport = -1;
607	  break;
608#endif
609	case UUCONF_PORTTYPE_PIPE:
610	  /* A read of 0 on a pipe always means EOF (see below).  */
611	  fgot = TRUE;
612	  /* Fall through.  */
613	case UUCONF_PORTTYPE_STDIN:
614	  oport = ((struct ssysdep_conn *) qconn->psysdep)->ord;
615	  break;
616	case UUCONF_PORTTYPE_MODEM:
617	case UUCONF_PORTTYPE_DIRECT:
618	case UUCONF_PORTTYPE_TCP:
619	case UUCONF_PORTTYPE_TLI:
620	  oport = ((struct ssysdep_conn *) qconn->psysdep)->o;
621	  break;
622	}
623    }
624
625  /* Force the descriptor into blocking mode.  */
626  (void) fcntl (oport, F_SETFL,
627		fcntl (oport, F_GETFL, 0) &~ (O_NDELAY | O_NONBLOCK));
628
629  usset_signal (SIGUSR1, uscu_child_handler, TRUE, (boolean *) NULL);
630  usset_signal (SIGUSR2, uscu_child_handler, TRUE, (boolean *) NULL);
631  usset_signal (SIGINT, SIG_IGN, TRUE, (boolean *) NULL);
632  usset_signal (SIGQUIT, SIG_IGN, TRUE, (boolean *) NULL);
633  usset_signal (SIGPIPE, SIG_DFL, TRUE, (boolean *) NULL);
634  usset_signal (SIGTERM, uscu_child_handler, TRUE, (boolean *) NULL);
635
636  fstopped = FALSE;
637  iSchild_sig = 0;
638  cwrite = 0;
639
640  if (fsysdep_catch ())
641    {
642      usysdep_start_catch ();
643    }
644
645  while (TRUE)
646    {
647      int isig;
648      int c;
649
650      /* There is a race condition here between checking the signal
651	 and receiving a new and possibly different one.  This is
652	 solved by having the parent resend the signal until it gets a
653	 response.  */
654      isig = iSchild_sig;
655      iSchild_sig = 0;
656      if (isig != 0)
657	{
658	  char b;
659
660	  if (isig == SIGTERM)
661	    exit (EXIT_SUCCESS);
662
663	  if (isig == SIGUSR1)
664	    {
665	      fstopped = FALSE;
666	      b = CHILD_STARTED;
667	    }
668	  else
669	    {
670	      fstopped = TRUE;
671	      b = CHILD_STOPPED;
672	      cwrite = 0;
673	    }
674
675	  c = write (opipe, &b, 1);
676
677	  /* Apparently on some systems we can get EAGAIN here.  */
678	  if (c < 0 &&
679	      (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA))
680	    c = 0;
681
682	  if (c <= 0)
683	    {
684	      /* Should we give an error message here?  */
685	      (void) kill (getppid (), SIGHUP);
686	      exit (EXIT_FAILURE);
687	    }
688	}
689
690      if (fstopped)
691	pause ();
692      else if (cwrite > 0)
693	{
694	  char *zbuf;
695
696	  zbuf = abbuf;
697	  while (cwrite > 0)
698	    {
699	      c = write (1, zbuf, cwrite);
700
701	      /* Apparently on some systems we can get EAGAIN here.  */
702	      if (c < 0 &&
703		  (errno == EAGAIN
704		   || errno == EWOULDBLOCK
705		   || errno == ENODATA))
706		c = 0;
707
708	      if (c < 0 && errno == EINTR)
709		break;
710	      if (c <= 0)
711		{
712		  /* Should we give an error message here?  */
713		  (void) kill (getppid (), SIGHUP);
714		  exit (EXIT_FAILURE);
715		}
716	      cwrite -= c;
717	      zbuf += c;
718	    }
719	}
720      else
721	{
722	  /* On some systems apparently read will return 0 until
723	     something has been written to the port.  We therefore
724	     accept a 0 return until after we have managed to read
725	     something.  Setting errno to 0 apparently avoids a
726	     problem on Coherent.  */
727	  errno = 0;
728	  c = read (oport, abbuf, sizeof abbuf);
729
730	  /* Apparently on some systems we can get EAGAIN here.  */
731	  if (c < 0 &&
732	      (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA))
733	    c = 0;
734
735	  if ((c == 0 && fgot)
736	      || (c < 0 && errno != EINTR))
737	    {
738	      /* This can be a normal way to exit, depending on just
739		 how the connection is dropped.  */
740	      (void) kill (getppid (), SIGHUP);
741	      exit (EXIT_SUCCESS);
742	    }
743	  if (c > 0)
744	    {
745	      fgot = TRUE;
746	      cwrite = c;
747	    }
748	}
749    }
750}
751
752/* Terminal control routines.  */
753
754/* Whether file descriptor 0 is attached to a terminal or not.  */
755static boolean fSterm;
756
757/* Whether we are doing local echoing.  */
758static boolean fSlocalecho;
759
760/* The original state of the terminal.  */
761static sterminal sSterm_orig;
762
763/* The new state of the terminal.  */
764static sterminal sSterm_new;
765
766#if ! HAVE_BSD_TTY
767#ifdef SIGTSTP
768/* Whether SIGTSTP is being ignored.  */
769static boolean fStstp_ignored;
770#endif
771#endif
772
773/* Set the terminal into raw mode.  */
774
775boolean
776fsysdep_terminal_raw (flocalecho)
777     boolean flocalecho;
778{
779  fSlocalecho = flocalecho;
780
781  /* This defaults may be overriden below.  */
782  bSeof = '\004';
783  bStstp = '\032';
784
785  if (! fgetterminfo (0, &sSterm_orig))
786    {
787      fSterm = FALSE;
788      return TRUE;
789    }
790
791  fSterm = TRUE;
792
793  sSterm_new = sSterm_orig;
794
795#if HAVE_BSD_TTY
796
797  /* We use CBREAK mode rather than RAW mode, because RAW mode turns
798     off all output processing, which we don't want to do.  This means
799     that we have to disable the interrupt characters, which we do by
800     setting them to -1.  */
801  bSeof = sSterm_orig.stchars.t_eofc;
802
803  sSterm_new.stchars.t_intrc = -1;
804  sSterm_new.stchars.t_quitc = -1;
805  sSterm_new.stchars.t_startc = -1;
806  sSterm_new.stchars.t_stopc = -1;
807  sSterm_new.stchars.t_eofc = -1;
808  sSterm_new.stchars.t_brkc = -1;
809
810  bStstp = sSterm_orig.sltchars.t_suspc;
811
812  sSterm_new.sltchars.t_suspc = -1;
813  sSterm_new.sltchars.t_dsuspc = -1;
814  sSterm_new.sltchars.t_rprntc = -1;
815  sSterm_new.sltchars.t_flushc = -1;
816  sSterm_new.sltchars.t_werasc = -1;
817  sSterm_new.sltchars.t_lnextc = -1;
818
819  if (! flocalecho)
820    {
821      sSterm_new.stty.sg_flags |= (CBREAK | ANYP);
822      sSterm_new.stty.sg_flags &=~ (ECHO | CRMOD | TANDEM);
823    }
824  else
825    {
826      sSterm_new.stty.sg_flags |= (CBREAK | ANYP | ECHO);
827      sSterm_new.stty.sg_flags &=~ (CRMOD | TANDEM);
828    }
829
830#endif /* HAVE_BSD_TTY */
831
832#if HAVE_SYSV_TERMIO
833
834  bSeof = sSterm_new.c_cc[VEOF];
835  if (! flocalecho)
836    sSterm_new.c_lflag &=~ (ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHONL);
837  else
838    sSterm_new.c_lflag &=~ (ICANON | ISIG);
839  sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
840  sSterm_new.c_oflag &=~ (OPOST);
841  sSterm_new.c_cc[VMIN] = 1;
842  sSterm_new.c_cc[VTIME] = 0;
843
844#endif /* HAVE_SYSV_TERMIO */
845
846#if HAVE_POSIX_TERMIOS
847
848  bSeof = sSterm_new.c_cc[VEOF];
849  bStstp = sSterm_new.c_cc[VSUSP];
850  if (! flocalecho)
851    sSterm_new.c_lflag &=~
852      (ICANON | IEXTEN | ISIG | ECHO | ECHOE | ECHOK | ECHONL);
853  else
854    sSterm_new.c_lflag &=~ (ICANON | IEXTEN | ISIG);
855  sSterm_new.c_iflag &=~ (INLCR | IGNCR | ICRNL | IXON | IXOFF);
856  sSterm_new.c_oflag &=~ (OPOST);
857  sSterm_new.c_cc[VMIN] = 1;
858  sSterm_new.c_cc[VTIME] = 0;
859
860#endif /* HAVE_POSIX_TERMIOS */
861
862  if (! fsetterminfo (0, &sSterm_new))
863    {
864      ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno));
865      return FALSE;
866    }
867
868  return TRUE;
869}
870
871/* Restore the terminal to its original setting.  */
872
873boolean
874fsysdep_terminal_restore ()
875{
876  if (! fSterm)
877    return TRUE;
878
879  if (! fsetterminfo (0, &sSterm_orig))
880    {
881      ulog (LOG_ERROR, "Can't restore terminal: %s", strerror (errno));
882      return FALSE;
883    }
884  return TRUE;
885}
886
887/* Read a line from the terminal.  This will be called after
888   fsysdep_terminal_raw has been called.  */
889
890char *
891zsysdep_terminal_line (zprompt)
892     const char *zprompt;
893{
894  CATCH_PROTECT size_t cbuf = 0;
895  CATCH_PROTECT char *zbuf = NULL;
896  CATCH_PROTECT size_t cgot = 0;
897
898  if (zprompt != NULL && *zprompt != '\0')
899    (void) write (1, zprompt, strlen (zprompt));
900
901  /* Forgot about any previous SIGINT or SIGQUIT signals we may have
902     received.  We don't worry about the race condition here, since we
903     can't get these signals from the terminal at the moment and it's
904     not too likely that somebody else will be sending them to us.  */
905  afSignal[INDEXSIG_SIGINT] = 0;
906  afSignal[INDEXSIG_SIGQUIT] = 0;
907
908  if (! fsysdep_terminal_restore ())
909    return NULL;
910
911  if (fsysdep_catch ())
912    {
913      usysdep_start_catch ();
914      cbuf = 0;
915      zbuf = NULL;
916      cgot = 0;
917    }
918
919  while (TRUE)
920    {
921      char b;
922      int c;
923
924      if (afSignal[INDEXSIG_SIGINT]
925	  || afSignal[INDEXSIG_SIGQUIT])
926	{
927	  usysdep_end_catch ();
928	  /* Make sure the signal is logged.  */
929	  ulog (LOG_ERROR, (const char *) NULL);
930	  /* Return an empty string.  */
931	  cgot = 0;
932	  break;
933	}
934
935      /* There's a race here between checking the signals and calling
936	 read.  It just means that the user will have to hit ^C more
937	 than once.  */
938
939      c = read (0, &b, 1);
940      if (c < 0)
941	{
942	  if (errno == EINTR)
943	    continue;
944	  usysdep_end_catch ();
945	  ulog (LOG_ERROR, "read: %s", strerror (errno));
946	  (void) fsysdep_terminal_raw (fSlocalecho);
947	  return NULL;
948	}
949      if (c == 0)
950	{
951	  /* I'm not quite sure what to do here.  */
952	  usysdep_end_catch ();
953	  ulog (LOG_ERROR, "EOF on terminal");
954	  (void) fsysdep_terminal_raw (fSlocalecho);
955	  return NULL;
956	}
957
958      if (cgot >= cbuf)
959	{
960	  char *znew;
961
962	  cbuf += 64;
963	  znew = zbufalc (cbuf);
964	  if (zbuf != NULL)
965	    {
966	      memcpy (znew, zbuf, cgot);
967	      ubuffree (zbuf);
968	    }
969	  zbuf = znew;
970	}
971
972      zbuf[cgot] = b;
973
974      ++cgot;
975
976      if (b == '\n')
977	{
978	  usysdep_end_catch ();
979	  break;
980	}
981    }
982
983  if (cgot >= cbuf)
984    {
985      char *znew;
986
987      ++cbuf;
988      znew = zbufalc (cbuf);
989      if (zbuf != NULL)
990	{
991	  memcpy (znew, zbuf, cgot);
992	  ubuffree (zbuf);
993	}
994      zbuf = znew;
995    }
996
997  zbuf[cgot] = '\0';
998
999  if (! fsysdep_terminal_raw (fSlocalecho))
1000    return NULL;
1001
1002  return zbuf;
1003}
1004
1005/* Write a line to the terminal with a trailing newline.  */
1006
1007boolean
1008fsysdep_terminal_puts (zline)
1009     const char *zline;
1010{
1011  char *zalc, *zprint;
1012  size_t clen;
1013
1014  if (zline == NULL)
1015    {
1016      zalc = zbufalc (2);
1017      clen = 0;
1018    }
1019  else
1020    {
1021      clen = strlen (zline);
1022      zalc = zbufalc (clen + 2);
1023      memcpy (zalc, zline, clen);
1024    }
1025
1026  if (fSterm)
1027    {
1028      zalc[clen] = '\r';
1029      ++clen;
1030    }
1031  zalc[clen] = '\n';
1032  ++clen;
1033
1034  zprint = zalc;
1035  while (clen > 0)
1036    {
1037      int c;
1038
1039      c = write (1, zprint, clen);
1040      if (c <= 0)
1041	{
1042	  ubuffree (zalc);
1043	  ulog (LOG_ERROR, "write: %s", strerror (errno));
1044	  return FALSE;
1045	}
1046      clen -= c;
1047      zprint += c;
1048    }
1049
1050  ubuffree (zalc);
1051
1052  return TRUE;
1053}
1054
1055/* Allow or disallow signals from the terminal.  */
1056
1057boolean
1058fsysdep_terminal_signals (faccept)
1059     boolean faccept;
1060{
1061#if HAVE_BSD_TTY
1062
1063  if (faccept)
1064    {
1065      sSterm_new.stchars.t_intrc = sSterm_orig.stchars.t_intrc;
1066      sSterm_new.stchars.t_quitc = sSterm_orig.stchars.t_quitc;
1067    }
1068  else
1069    {
1070      sSterm_new.stchars.t_intrc = -1;
1071      sSterm_new.stchars.t_quitc = -1;
1072    }
1073
1074#else /* ! HAVE_BSD_TTY */
1075
1076  if (faccept)
1077    sSterm_new.c_lflag |= ISIG;
1078  else
1079    sSterm_new.c_lflag &=~ ISIG;
1080
1081#ifdef SIGTSTP
1082  /* We only want to get SIGINT and SIGQUIT, not SIGTSTP.  This
1083     function will be called with faccept TRUE before it is called
1084     with faccept FALSE, so fStstp_ignored will be correctly
1085     initialized.  */
1086  if (faccept)
1087    usset_signal (SIGTSTP, SIG_IGN, FALSE, &fStstp_ignored);
1088  else if (! fStstp_ignored)
1089    usset_signal (SIGTSTP, SIG_DFL, TRUE, (boolean *) NULL);
1090#endif
1091
1092#endif /* ! HAVE_BSD_TTY */
1093
1094  if (! fsetterminfo (0, &sSterm_new))
1095    {
1096      ulog (LOG_ERROR, "Can't set terminal: %s", strerror (errno));
1097      return FALSE;
1098    }
1099
1100  return TRUE;
1101}
1102
1103/* Start up a command, or possibly just a shell.  Optionally attach
1104   stdin or stdout to the port.  We attach directly to the port,
1105   rather than copying the data ourselves.  */
1106
1107boolean
1108fsysdep_shell (qconn, zcmd, tcmd)
1109     struct sconnection *qconn;
1110     const char *zcmd;
1111     enum tshell_cmd tcmd;
1112{
1113  const char *azargs[4];
1114  int oread, owrite;
1115  int aidescs[3];
1116  pid_t ipid;
1117
1118  if (tcmd != SHELL_NORMAL)
1119    azargs[0] = "/bin/sh";
1120  else
1121    {
1122      azargs[0] = getenv ("SHELL");
1123      if (azargs[0] == NULL)
1124	azargs[0] = "/bin/sh";
1125    }
1126  if (zcmd == NULL || *zcmd == '\0')
1127    azargs[1] = NULL;
1128  else
1129    {
1130      azargs[1] = "-c";
1131      azargs[2] = zcmd;
1132      azargs[3] = NULL;
1133    }
1134
1135  if (qconn->qport == NULL)
1136    {
1137      oread = 0;
1138      owrite = 1;
1139    }
1140  else
1141    {
1142      switch (qconn->qport->uuconf_ttype)
1143	{
1144	default:
1145	  oread = owrite = -1;
1146	  break;
1147	case UUCONF_PORTTYPE_STDIN:
1148	case UUCONF_PORTTYPE_PIPE:
1149	  oread = ((struct ssysdep_conn *) qconn->psysdep)->ord;
1150	  owrite = ((struct ssysdep_conn *) qconn->psysdep)->owr;
1151	  break;
1152	case UUCONF_PORTTYPE_MODEM:
1153	case UUCONF_PORTTYPE_DIRECT:
1154	case UUCONF_PORTTYPE_TCP:
1155	case UUCONF_PORTTYPE_TLI:
1156	  oread = owrite = ((struct ssysdep_conn *) qconn->psysdep)->o;
1157	  break;
1158	}
1159    }
1160
1161  aidescs[0] = 0;
1162  aidescs[1] = 1;
1163  aidescs[2] = 2;
1164
1165  if (tcmd == SHELL_STDIN_FROM_PORT || tcmd == SHELL_STDIO_ON_PORT)
1166    aidescs[0] = oread;
1167  if (tcmd == SHELL_STDOUT_TO_PORT || tcmd == SHELL_STDIO_ON_PORT)
1168    aidescs[1] = owrite;
1169
1170  ipid = ixsspawn (azargs, aidescs, FALSE, TRUE, (const char *) NULL,
1171		   FALSE, FALSE, (const char *) NULL,
1172		   (const char *) NULL, (const char *) NULL);
1173  if (ipid < 0)
1174    {
1175      ulog (LOG_ERROR, "ixsspawn (/bin/sh): %s", strerror (errno));
1176      return FALSE;
1177    }
1178
1179  return ixswait ((unsigned long) ipid, "shell") == 0;
1180}
1181
1182/* Change directories.  */
1183
1184boolean
1185fsysdep_chdir (zdir)
1186     const char *zdir;
1187{
1188  if (zdir == NULL || *zdir == '\0')
1189    {
1190      zdir = getenv ("HOME");
1191      if (zdir == NULL)
1192	{
1193	  ulog (LOG_ERROR, "HOME not defined");
1194	  return FALSE;
1195	}
1196    }
1197  if (chdir (zdir) < 0)
1198    {
1199      ulog (LOG_ERROR, "chdir (%s): %s", zdir, strerror (errno));
1200      return FALSE;
1201    }
1202  return TRUE;
1203}
1204
1205/* Suspend the current process.  */
1206
1207boolean
1208fsysdep_suspend ()
1209{
1210#ifndef SIGTSTP
1211  return fsysdep_terminal_puts ("[process suspension not supported]");
1212#else
1213  return kill (getpid (), SIGTSTP) == 0;
1214#endif
1215}
1216