Re: A question on PTY

H.J. Lu (hjl@lucon.org)
Wed, 9 Dec 1998 21:14:33 -0800 (PST)


>
> From: hjl@lucon.org (H.J. Lu)
> Date: Wed, 9 Dec 1998 08:09:12 -0800 (PST)
>
> I got 2 failures in the POSIX test on PTY. One is the STOP char
> is not sent when the PTY is full. Although write blocks, read on
> the blocked PTY doesn't return the STOP char. The other one is
> when the PTY is put in back ground, output doesn't generate
> SIGTTOU. I can provide testcases if necessary.
>
> HJ,
>
> Can you provide test cases? Both of these cases are
> handled by the high-level tty code, and the last time I checked using
> the serial driver (which granted was a while ago), neither of these
> cases were a problem. So test cases would be most helpful. Thanks!!
>
> - Ted
>

Here is a testcase.

# gcc loop.c test19.c
# a.out
Wrote 8190 bytes
read 4222 bytes
after input queue filled to 4222 bytes, attempt, to read() STOP character(s) returned 0, expected >= 1

-- 
H.J. Lu (hjl@gnu.org)
----
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1998-12-09 21:12 PST by <hjl@ocean.lucon.org>.
# Source directory was `/home/hjl/bugs/glibc/termios'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#  12926 -rw-r--r-- loop.c
#   4666 -rw-r--r-- test19.c
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh10858; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= loop.c ==============
if test -f 'loop.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'loop.c' '(file already exists)'
else
  $echo 'x -' extracting 'loop.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'loop.c' &&
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <termios.h>
#include <string.h>
X
#define	WAITTIME	(5 * SPEEDFACTOR)
#define	COMBITS		11
#define TTY_SIDE	1
#define LOOP_SIDE	2
X
extern void setsigs (void (*func)());
X
int sig_recvd;
int sig_cont;
int sig_errno;
int sig_ret;
X
pid_t	child1;
pid_t	child2;
X
int	data_rate = 9600 / COMBITS;
X
cc_t PERASE = 0x7f;
cc_t PKILL  = 0x15;
cc_t PINTR  = 0x03;
cc_t PQUIT  = 0x1c;
cc_t PSTART = 0x11;
cc_t PSTOP  = 0x13;
cc_t PEOF   = 0x04;
cc_t PEOL   = 0x0b;
cc_t PSUSP  = 0x1a;
cc_t PMIN   = 0;
cc_t PTIME  = 10;
X
static int	tty_done = 0;
static struct termios tty_termios;
static int	loop_done = 0;
static struct termios loop_termios;
static int	tty_master_fd = -1;
static int	loop_master_fd = -1;
X
int
openctl(spec, oflag)
char *spec;
int  oflag;
{
X	/* Note: setsid() will have been called before this routine */
X
X	int fd;
X
X	/* allow for modem line delay if there has just been a close() */
X	(void) sleep(1);
X
X	fd = open(spec, oflag, 0);
X
X	/* Make sure failure is not just because the device cannot
X	   become the controlling terminal */
X	
X	if (fd < 0 && errno != EINTR)
X		fd = open(spec, oflag|O_NOCTTY, 0);
X
X	return fd;
}
X
int
openpty(master, slave, mfdp, sfdp, cntrl)
char *master;
char *slave;
int *mfdp;
int *sfdp;
int cntrl;
{
X	*mfdp = open(master, O_RDWR|O_NOCTTY);
X	if (*mfdp < 0 && errno != EBUSY)
X		return -1;
X
X	if (*mfdp >= 0)
X	{
X		char *newslave;
X		size_t len;
X		static struct { char *ptr; size_t len; } slavelen[2];
X		extern char *ptsname();
X
X		(void) grantpt(*mfdp);   /* change permission of slave */
X		(void) unlockpt(*mfdp);  /* unlock slave */
X		newslave=ptsname(*mfdp); /* get a slave */
X		if (slavelen[0].ptr == NULL)
X		{
X			/* remember length of original dummy name
X			   to be used if called again */
X			slavelen[0].ptr = slave;
X			slavelen[0].len = strlen(slave);
X		}
X		else if (slave != slavelen[0].ptr && slavelen[1].ptr == NULL)
X		{
X			/* remember length of original dummy name
X			   to be used if called again */
X			slavelen[1].ptr = slave;
X			slavelen[1].len = strlen(slave);
X		}
X		if (slave == slavelen[0].ptr)
X			len = slavelen[0].len;
X		else
X			len = slavelen[1].len;
X		if (strlen(newslave) > len)
X		{
X			(void) fprintf(stderr,
X				"dummy slave name too short to take \"%s\" in openpty()\n",
X				newslave);
X			return -1;
X		}
X		(void) strcpy(slave, newslave);
X	}
X
X	if (cntrl)
X		*sfdp = openctl(slave, O_RDWR);
X	else
X		*sfdp = open(slave, O_RDWR|O_NOCTTY);
X
X	if (*sfdp < 0)
X		return -1;
X
X	return 0;
}
X
int
ptygetattr(mfd, termios_p)
int mfd;
struct termios *termios_p;
{
X	return tcgetattr(mfd, termios_p);
}
X
static void
rdwr(fdin, fdout)
int fdin;
int fdout;
{
X	/* read from fdin and write to fdout - called in child process */
X
X	int n;
X	char buf[_POSIX_MAX_INPUT/2];
X
X	while ((n = read(fdin, buf, sizeof(buf))) >= 0)
X	{
X		/* On EOF write EOF character */
X		if (n == 0)
X		{
X			if (ptygetattr(fdout, &tty_termios) == 0)
X				buf[n++] = tty_termios.c_cc[VEOF];
X			else
X				buf[n++] = PEOF;
X		}
X
X		errno = 0;
X		if (write(fdout, buf, (unsigned int)n) != n)
X		{
X			/* die quietly on EIO */
X			if (errno != EIO)
X				perror ("write");
X			_exit(1);
X		}
X	}
X	/* die quietly on EIO */
X	if (errno != EIO)
X		perror ("read");
X	_exit(1);
}
X
static int
ptycon(tty_fildes, loop_fildes)
int tty_fildes;
int loop_fildes;
{
X	/* connect two master pty's so slaves can be used as loopback */
X
X	pid_t ppid;
X
X	switch (child1 = fork())
X	{
X		case -1:
X			perror ("fork");
X			exit (1);
X		
X		case 0:
X			setsigs(SIG_IGN);
X			(void) close(tty_fildes);
X			(void) close(loop_fildes);
X			rdwr(tty_master_fd, loop_master_fd);
X			_exit(0);
X	}
X
X	switch (child2 = fork())
X	{
X		case -1:
X			perror ("fork");
X			exit (1);
X		
X		case 0:
X			setsigs(SIG_IGN);
X			(void) close(tty_fildes);
X			(void) close(loop_fildes);
X			rdwr(loop_master_fd, tty_master_fd);
X			_exit(0);
X	}
X
X	/* rdwr processes may not be killed by tests, so this process
X	   checks for when the parent dies, and kills the rdwr's */
X
X	ppid = getpid();
X	switch (fork())
X	{
X
X		case -1:
X			perror ("fork");
X			exit (1);
X		
X		case 0:
X			setsigs(SIG_IGN);
X			(void) close(tty_fildes);
X			(void) close(loop_fildes);
X
X			/* When parent PID changes, parent has died */
X			while (ppid == getppid())
X				(void) sleep(2);
X
X			(void) kill(child1, SIGKILL);
X			(void) kill(child2, SIGKILL);
X
X			_exit(0);
X	}
X
X	return 0;
}
X
char *
xstrdup (const char *s)
{
X  char *p = malloc (strlen (s) + 1);
X  strcpy (p, s);
X  return p;
}
X
static int
do_prep(side, oflags, fildes_p, termios_p)
int side;
int oflags;
int *fildes_p;
struct termios *termios_p;
{
X	/* common function called by tty_prep() and loop_prep() */
X
X	char *termios_tty, *master_tty;
X	int flags, cntrl, *master_fdp;
X
X	*fildes_p = -1;
X
X	if (side == TTY_SIDE)
X		termios_tty = xstrdup ("/dev/pts/xxxxxxx");
X	else
X		termios_tty = xstrdup ("/dev/pts/xxxxxxx");
X
X	if (side == TTY_SIDE)
X	{
X		master_tty = "/dev/ptmx";
X		master_fdp = &tty_master_fd;
X	}
X	else
X	{
X		master_tty = "/dev/ptmx";
X		master_fdp = &loop_master_fd;
X	}
X
X	if (master_tty == NULL || *master_tty == '\0')
X	{
X		if (side == TTY_SIDE)
X			*fildes_p = openctl(termios_tty, oflags | O_NONBLOCK);
X		else
X			*fildes_p = open(termios_tty,
X				oflags | O_NONBLOCK | O_NOCTTY);
X		if(*fildes_p < 0)
X			return -1;
X	}
X	else
X	{
X		/* allow previous rdwr() processes to finish */
X		(void) sleep(3);
X
X		if (side == TTY_SIDE)
X			cntrl = (oflags & O_NOCTTY) == 0;
X		else
X			cntrl = 0;
X
X		/* Implementation-supplied pseudo-terminal opener */
X		if (openpty(master_tty, termios_tty,
X				master_fdp, fildes_p, cntrl) != 0)
X		{
X			fprintf (stderr, "openpty(\"%s\", \"%s\", mfdp, sfdp, %d) failed\n",
X				master_tty, termios_tty, cntrl);
X			if (*master_fdp >= 0)
X			{
X				(void) close(*master_fdp);
X				*master_fdp = -1;
X			}
X			return -1;
X		}
X	}
X
X	/* Initialize fields */
X	if (side == TTY_SIDE)
X	{
X		if (tty_init(*fildes_p, termios_p) != 0)
X			return -1;
X	}
X	else
X	{
X		if (loop_init(*fildes_p, termios_p) != 0)
X			return -1;
X	}
X	if (tcsetattr(*fildes_p, TCSANOW, termios_p) != 0)
X		return -1;
X
X	if ((oflags & O_NONBLOCK) == 0)
X	{
X		/* Clear O_NONBLOCK flag after opening */
X		if ((flags = fcntl (*fildes_p, F_GETFL)) == -1)
X			return -1;
X		if (fcntl (*fildes_p, F_SETFL, flags & ~O_NONBLOCK) == -1)
X			return -1;
X	}
X
X	return 0;
}
X
/*******************************
*  Prepare tty's
********************************/
int
terms_prep(tty_flags, tty_fildes_p, tty_termios_p,
X	   loop_flags, loop_fildes_p, loop_termios_p)
int tty_flags;
int *tty_fildes_p;
struct termios *tty_termios_p;
int loop_flags;
int *loop_fildes_p;
struct termios *loop_termios_p;
{
X	int ret;
X
X	*tty_fildes_p = *loop_fildes_p = -1;
X
X	/*
X	 * TERMIOS_TTY must be the next tty opened so that if the process
X	 * is a session leader it will become the controlling terminal
X	 */
X
X	ret = do_prep(TTY_SIDE, tty_flags, tty_fildes_p, tty_termios_p);
X	if (ret != 0)
X		return ret;
X
X	ret = do_prep(LOOP_SIDE, loop_flags, loop_fildes_p, loop_termios_p);
X	if (ret != 0)
X		return ret;
X	
X	/* If pseudo-terminal looback required, connect master sides */
X	if (tty_master_fd >= 0 && loop_master_fd >= 0)
X		ret = ptycon(*tty_fildes_p, *loop_fildes_p);
X
X	if (tty_master_fd >= 0)
X	{
X		(void) close(tty_master_fd);
X		tty_master_fd = -1;
X	}
X	if (loop_master_fd >= 0)
X	{
X		(void) close(loop_master_fd);
X		loop_master_fd = -1;
X	}
X	
X	return ret;
}
X
int
termios_prep (tty_fildes_p, tty_termios_p, loop_fildes_p, loop_termios_p)
int *tty_fildes_p;			/* TERMIOS_TTY file descriptor	*/
struct termios *tty_termios_p;		/* terminal attributes		*/
int *loop_fildes_p;			/* TERMIOS_LOOP file descriptor	*/
struct termios *loop_termios_p;		/* terminal attributes		*/
{
X	(void) setsid();
X
X	return terms_prep(O_RDWR, tty_fildes_p, tty_termios_p,
X			  O_RDWR, loop_fildes_p, loop_termios_p);
}
X
/*************************
* TERMIOS_TTY
*************************/
int
tty_prep (tty_fildes_p, tty_termios_p)
int *tty_fildes_p;			/* TERMIOS_TTY file descriptor	*/
struct termios *tty_termios_p;		/* terminal attributes		*/
{
X	return do_prep(TTY_SIDE, O_RDWR, tty_fildes_p, tty_termios_p);
}
X
/*************************
* TERMIOS_LOOP
*************************/
int
loop_prep (loop_fildes_p, loop_termios_p)
int *loop_fildes_p;			/* TERMIOS_LOOP file descriptor	*/
struct termios *loop_termios_p;		/* terminal attributes		*/
{
X	return do_prep(LOOP_SIDE, O_RDWR, loop_fildes_p, loop_termios_p);
}
X
/**********************************************
* Save the initial termios state
* Modify struct termios to VSX values
* Modify struct termios for test values
**********************************************/
int
tty_init(fildes, termios_p)
int fildes;
struct termios *termios_p;
{
X	/*************************
X	* save initial state
X	**************************/
X	if (!tty_done)
X	{
X		if (tcgetattr(fildes, &tty_termios) != 0)
X			return -1;
X	}
X	tty_done = 1;
X
X	*termios_p = tty_termios;	/* ANSI C structure assignment	*/
X
X	/*************************
X	* set to VSX values
X	**************************/
X	(termios_p)->c_iflag &= ~(PARMRK|INLCR|INPCK|IGNCR|IXOFF|BRKINT|IXON);
X	(termios_p)->c_iflag |= (IGNPAR|IGNBRK|ICRNL);
X	if ((termios_p->c_cflag & CSIZE) == CS8)
X		(termios_p)->c_iflag &= ~ISTRIP;
#ifdef _XOPEN_SOURCE
X	(termios_p)->c_iflag &= ~(IUCLC|IXANY);
#endif
X	(termios_p)->c_oflag &= ~OPOST;
X
X	(termios_p)->c_cflag |= (CLOCAL|CREAD);
X
X	(termios_p)->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|IEXTEN);
X	(termios_p)->c_lflag |= (ISIG|ICANON|NOFLSH);
#ifdef _XOPEN_SOURCE
X	(termios_p)->c_lflag &= ~XCASE;
#endif
X	(termios_p)->c_cc[VMIN] = PMIN;
X	(termios_p)->c_cc[VTIME] = PTIME;
X	(termios_p)->c_cc[VINTR] = PINTR;
X	(termios_p)->c_cc[VQUIT] = PQUIT;
X	(termios_p)->c_cc[VERASE] = PERASE;
X	(termios_p)->c_cc[VKILL] = PKILL;
X	(termios_p)->c_cc[VEOF] = PEOF;
X	(termios_p)->c_cc[VEOL] = PEOL;
X	(termios_p)->c_cc[VSUSP] = PSUSP;
X	(termios_p)->c_cc[VSTART] = PSTART;
X	(termios_p)->c_cc[VSTOP] = PSTOP;
X
X	if (cfsetispeed(termios_p,B9600) != 0 ||
X	    cfsetospeed(termios_p,B9600) != 0)
X		return -1;
X
X	return 0;
}
X
/**********************************
*  Initialize LOOP to noncanonical
***********************************/
int
loop_init(fildes, termios_p)
X
int fildes;
struct termios *termios_p;
{
X	static long NOTHING;		/* "disable" character		*/
X
X	/*************************
X	* save initial state
X	**************************/
X	if (!loop_done)
X	{
X		if (tcgetattr(fildes, &loop_termios) != 0)
X			return -1;
X		NOTHING = fpathconf(fildes, _PC_VDISABLE);
X		if (NOTHING == -1)
X			NOTHING = 0;
X	}
X	loop_done = 1;
X
X	*termios_p = loop_termios;	/* ANSI C structure assignment	*/
X
X	/*************************
X	* set VSX values
X	**************************/
X	(termios_p)->c_iflag &= ~(PARMRK|ICRNL|INLCR|INPCK|IGNCR|IXOFF|BRKINT|IXON);
X	(termios_p)->c_iflag |= (IGNPAR|IGNBRK);
#ifdef _XOPEN_SOURCE
X	(termios_p)->c_iflag &= ~(IUCLC|IXANY);
#endif
X	(termios_p)->c_oflag &= ~OPOST;
X
X	(termios_p)->c_cflag |= (CLOCAL|CREAD);
X	/* make sure LOOP has similar enough c_cflag values to TTY
X	   so they can talk to each other */
X	if (tty_done)
X	{
X		termios_p->c_cflag &= ~(CSIZE|CSTOPB|PARENB|PARODD);
X		termios_p->c_cflag |=
X			(tty_termios.c_cflag & (CSIZE|CSTOPB|PARENB|PARODD));
X	}
X
X	if ((termios_p->c_cflag & CSIZE) == CS8)
X		(termios_p)->c_iflag &= ~ISTRIP;
X
X	(termios_p)->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ISIG|ICANON|TOSTOP|IEXTEN);
X	(termios_p)->c_lflag |= (NOFLSH);
#ifdef _XOPEN_SOURCE
X	(termios_p)->c_lflag &= ~XCASE;
#endif
X	(termios_p)->c_cc[VINTR] = NOTHING;
X	(termios_p)->c_cc[VQUIT] = NOTHING;
X	(termios_p)->c_cc[VERASE] = NOTHING;
X	(termios_p)->c_cc[VKILL] = NOTHING;
X	(termios_p)->c_cc[VEOF] = NOTHING;
X	(termios_p)->c_cc[VEOL] = NOTHING;
X	(termios_p)->c_cc[VSUSP] = NOTHING;
X	(termios_p)->c_cc[VMIN] = PMIN;
X	(termios_p)->c_cc[VTIME] = PTIME;
X	(termios_p)->c_cc[VSTART] = PSTART;
X	(termios_p)->c_cc[VSTOP] = PSTOP;
X
X	if (cfsetispeed((termios_p),B9600) != 0 ||
X	    cfsetospeed((termios_p),B9600) != 0)
X		return -1;
X	
X	return 0;
}
X
#ifdef __hpux
#undef NSIG
#define NSIG 31
#endif
X
int
sig_init (int signum, sigset_t *sigmask, void (*sigfunc)(),
X	  int sigflags)
{
X  struct sigaction act;
X  act.sa_flags   = sigflags;
X  act.sa_mask    = *sigmask;
X  act.sa_handler = sigfunc;
X  return(sigaction(signum, &act, (struct sigaction *)NULL));
}
X
static sigset_t sig_empty;
X
void
do_signal (signum, sigfunc)
X  int signum;
X  void (*sigfunc)();
{
X  struct sigaction act;
X
X  act.sa_flags = 0;
X  act.sa_handler = sigfunc;
X  sigemptyset(&act.sa_mask);
X  if (sigaction(signum, &act, (struct sigaction *)NULL) < 0)
X    {
X      perror ("sigaction");
X      abort ();
X    }
}
X
void
sig_catch (sig)
X  int sig;
{
X  sig_recvd = sig;
}
X
void
sig_resume (sig)
X  int sig;
{
X  sig_cont = sig;
}
X
void
setsigs (func)
X  void (*func)();
{
X  int i;
X  for (i = 1; i < NSIG; i++)
X   {
X     if (i == SIGKILL || i == SIGSTOP || i == SIGCHLD)
X       continue;
X
X     do_signal (i, func);
X   }
}
X
int alrm_flag;
X     
void
alrm (int sig)
{
X	alrm_flag = 1;
}
SHAR_EOF
  $shar_touch -am 1207115798 'loop.c' &&
  chmod 0644 'loop.c' ||
  $echo 'restore of' 'loop.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'loop.c:' 'MD5 check failed'
9a85b08a341019e2ab49c64fad7f8d80  loop.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'loop.c'`"
    test 12926 -eq "$shar_count" ||
    $echo 'loop.c:' 'original size' '12926,' 'current size' "$shar_count!"
  fi
fi
# ============= test19.c ==============
if test -f 'test19.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'test19.c' '(file already exists)'
else
  $echo 'x -' extracting 'test19.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'test19.c' &&
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <termios.h>
#include <string.h>
X
extern void alrm (int);
X
extern int data_rate;
X
#define Strlen          (sizeof(string)-1)
#define NAP             2
X
static	long	max_input;
static	int	tty_fildes, loop_fildes;
static	struct termios tty_termios, loop_termios,
X		*loop_termios_p = &loop_termios,
X		*tty_termios_p = &tty_termios;
static char	string[]  = "And they thwam and they thwam all over the dam!";
static char	inbuf[512];
static char	Stop       = '\023';		/* ASCII DC3		*/
static char	Start      = '\021';		/* ASCII DC1		*/
extern int alrm_flag;
X
void
test19()
{
X	int ret, err;
X	unsigned capacity, total, nbytes, wrote;
X
X	errno = 0;
X	max_input = fpathconf(tty_fildes, _PC_MAX_INPUT);
X	if (max_input < 0)
X	{
X		perror ("fpathconf");
X		return;
X	}
X
X        if (loop_init(loop_fildes, loop_termios_p) != 0)
X	{
X		return;
X	}
X
X	loop_termios_p->c_lflag &= ~ICANON;
X	loop_termios_p->c_iflag |= IXON;
X	loop_termios_p->c_cc[VMIN] = 0;
X	loop_termios_p->c_cc[VTIME] = 10;
X	if (tcsetattr(loop_fildes, TCSANOW, loop_termios_p) != 0)
X	{
X		perror ("tcsetattr");
X		return;
X	}
X
X        if (tty_init(tty_fildes, tty_termios_p) != 0)
X	{
X		return;
X	}
X
X	tty_termios_p->c_lflag &= ~ICANON;
X	tty_termios_p->c_cc[VMIN] = 0;
X	tty_termios_p->c_cc[VTIME] = 10;
X	tty_termios_p->c_iflag |= IXOFF;
X	if (tcsetattr(tty_fildes, TCSANOW, tty_termios_p) != 0)
X	{
X		perror ("tcsetattr");
X		return;
X	}
X
X	alrm_flag = 0;
X	wrote = 0;
X	while (alrm_flag == 0)
X	{
X		do_signal (SIGALRM, alrm);
X		alarm (10);
X		ret = write(loop_fildes, string, Strlen);
X		alarm (0);
X		err = errno;
X		
X		if (ret < 0)
X			break;
X		wrote += ret;
X	}
X
X	if (alrm_flag == 0)
X	{
X		fprintf (stderr, "no alarm\n");
X		perror ("write");
X		return;
X	}
X
X	fprintf (stderr, "Wrote %d bytes\n", wrote);
X
X	if (tcflush(loop_fildes, TCOFLUSH) != 0)
X	{
X		perror ("tcflush");
X		return;
X	}
X
X	capacity = 0;
X	while ((ret = read(tty_fildes, inbuf, sizeof(inbuf))) > 0)
X		capacity += ret;
X	if (ret < 0)
X	{
X		perror ("read");
X		return;
X	}
X
X	fprintf (stderr, "read %d bytes\n", capacity);
X
X	/*
X	 * Now switch off flow control and fill the input queue
X	 */
X	
X	loop_termios_p->c_iflag &= ~IXON;
X	if (tcsetattr(loop_fildes, TCSANOW, loop_termios_p) != 0)
X	{
X		perror ("tcsetattr");
X		return;
X	}
X
X	if (capacity < max_input)
X		capacity = max_input;
X
X	total = 0;
X	do {
X		nbytes = capacity - total;
X		if (nbytes > Strlen)
X			nbytes = Strlen;
X
X		errno = 0;
X		ret = write(loop_fildes, string, nbytes);
X
X		if (ret != nbytes)
X		{
X			perror ("write");
X			return;
X		} else {
X			total += ret;
X		}
X
X	} while (total < capacity);
X
X	(void) sleep((unsigned)(capacity/data_rate)+NAP);
X
X	/* Read STOP character(s) */
X
X	ret = read(loop_fildes, inbuf, sizeof(inbuf));
X	if (ret < 1)
X	{
X		err = errno;
X		fprintf (stderr, "after input queue filled to %u bytes, attempt, to read() STOP character(s) returned %d, expected >= 1\n",
X			capacity, ret);
X		if (ret < 0)
X			fprintf (stderr, "errno was set to %d\n", err);
X		return;
X	}
X	else if (memchr((void *)inbuf, Stop, (size_t)ret) == 0)
X	{
X		fprintf (stderr, "system did not transmit STOP when input queue filled to %u bytes\n",
X			capacity);
X		return;
X	}
X
X	/* read input to cause START to be sent, and check no data was lost */
X
X	total = 0;
X	do {
X		ret = read(tty_fildes, inbuf, sizeof(inbuf));
X
X	} while (ret > 0 && (total += ret) < capacity);
X
X	if (total != capacity)
X	{
X		err = errno;
X		fprintf (stderr, "second read() loop gave %u bytes, expected %u\n",
X			total, capacity);
X		if (ret < 0)
X			fprintf (stderr, "errno was set to %d\n", err);
X	}
X
X	(void) sleep(NAP);
X
X	ret = read(loop_fildes, inbuf, sizeof(inbuf));
X	if (ret < 1)
X	{
X		err = errno;
X		fprintf (stderr, "attempt to read() START character returned %d, expected >= 1\n", ret);
X		if (ret < 0)
X			fprintf (stderr, "errno was set to %d\n", err);
X	}
X	else if (memchr((void *)inbuf, Start, (size_t)ret) == 0)
X	{
X		fprintf (stderr, "system did not transmit START after input queue emptied\n");
X	}
}
X
void
prepare_tests()
{
X	switch (termios_prep (&tty_fildes, tty_termios_p,
X			      &loop_fildes, loop_termios_p) )
X	{
X	case 0:
X		break;
X	case 1:
X		fprintf (stderr, "Unsupported\n");
X		return;
X		break;
X	default:
X		fprintf (stderr, "Unsupported\n");
X		return;
X		break;
X	}
X
X	tty_termios_p->c_lflag &= ~ICANON;
X	tty_termios_p->c_cc[VMIN] = 0;
X	tty_termios_p->c_cc[VTIME] = 30;
X
X	if (tcsetattr(tty_fildes, TCSANOW, tty_termios_p) != 0)
X	{
X		perror ("tcsetattr");
X		return;
X	}
}
X
void
clean_tests()
{
X
X        (void) close(tty_fildes);
X        (void) close(loop_fildes);
}
main ()
{
X  prepare_tests();
X  test19 ();
X  clean_tests ();
}
SHAR_EOF
  $shar_touch -am 1209211198 'test19.c' &&
  chmod 0644 'test19.c' ||
  $echo 'restore of' 'test19.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'test19.c:' 'MD5 check failed'
2e7db6dd2d7cd45a268dd40afad60486  test19.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'test19.c'`"
    test 4666 -eq "$shar_count" ||
    $echo 'test19.c:' 'original size' '4666,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh10858
exit 0

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/