[alt.sources] sm v3

jfh@rpp386.cactus.org (John F Haugh II) (01/04/91)

sm du jour -

this version uses non-blocking reads from the master side of the
pty to improve performance.  also, the labeled session feature of
the shell layer manager has been added - you may refer to sessions
by name as well as number.

credit for putting in non-blocking reads is due to pat myrto who
has been very motivational.  adding the shell layer features was
prompted by axel fischer.

unshar and enjoy!
--
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	sm.c
# This archive created: Fri Jan  4 09:49:03 1991
# By:	John F Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'sm.c'" '(18474 characters)'
if test -f 'sm.c'
then
	echo shar: "will not over-write existing file 'sm.c'"
else
sed 's/^X//' << \SHAR_EOF > 'sm.c'
X/*
X * This code is in the public domain.  THIS CODE IS PROVIDED ON AN AS-IS
X * BASIS.  THE USER ACCEPTS ALL RISKS ASSOCIATED WITH USING THIS CODE AND
X * IS SOLELY RESPONSIBLE FOR CORRECTING ANY SOFTWARE ERRORS OR DAMAGE
X * CAUSED BY SOFTWARE ERRORS OR MISUSE.
X *
X * Written By: John F Haugh II, 12/21/90
X *
X * Modified to include suggestions made by Pat Myrto, Dan Bernstein,
X * and Axel Fischer.
X */
X
X#include <sys/types.h>
X#include <sys/termio.h>
X#include <sys/stat.h>
X#include <string.h>
X#include <stdio.h>
X#include <signal.h>
X#include <time.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <ctype.h>
X#include <pwd.h>
X
X/*
X * MAXSESSIONS is the number of sessions which a single user can
X * manage with this program at a single time.  16 is plenty.  4 or
X * 5 might be a better idea if pty's are a scarce resource.  8
X * should be a nice compromise.
X */
X
X#define	MAXSESSIONS	8
X
Xint	childpids[MAXSESSIONS];		/* Process ID of each session leader */
Xchar	*labels[MAXSESSIONS];		/* Names of sessions                 */
Xint	writepid;			/* Process ID of PTY writing process */
Xint	pspid;				/* Obfuscation is my life            */
Xint	masters[MAXSESSIONS];		/* File descriptor for PTY master    */
Xint	nsessions;			/* High-water mark for session count */
Xint	current = -1;			/* Currently active session          */
Xint	last = -1;			/* Previously active session         */
Xint	caught = 0;			/* Some signal was caught            */
X
Xstruct	termio	sanetty;		/* Initial TTY modes on entry        */
Xstruct	termio	rawtty;			/* Modes used when session is active */
X
Xvoid	exit ();
Xvoid	_exit ();
Xchar	*getlogin ();
Xchar	*getenv ();
Xchar	*malloc ();
Xstruct	passwd	*getpwuid ();
Xextern	char	**environ;
X
X/*
X * parse - see if "s" and "pat" smell alike
X */
X
Xchar *
Xparse (s, pat)
Xchar	*s;
Xchar	*pat;
X{
X	int	match = 0;
X	int	star = 0;
X
X	/*
X	 * Match all of the characters which are identical.  The '*'
X	 * character is used to denote the end of the unique suffix
X	 * for a pattern.  Everything after that is optional, but
X	 * must be matched exactly if given.
X	 */
X
X	while (*s && *pat) {
X		if (*s == *pat && *s) {
X			s++, pat++;
X			continue;
X		}
X		if (*pat == '*') {
X			star++;
X			pat++;
X			continue;
X		}
X		if ((*s == ' ' || *s == '\t') && star)
X			return s;
X		else
X			return 0;
X	}
X
X	/*
X	 * The pattern has been used up - see if whitespace
X	 * follows, or if the input string is also finished.
X	 */
X
X	if (! *pat && (*s == '\0' || *s == ' ' || *s == '\t'))
X		return s;
X
X	/*
X	 * The input string has been used up.  The unique
X	 * prefix must have been matched.
X	 */
X
X	if (! *s && (star || *pat == '*'))
X		return s;
X
X	return 0;
X}
X
X/*
X * murder - reap a single child process
X */
X
Xvoid
Xmurder (sig)
Xint	sig;
X{
X	int	pid;
X	int	i;
X
X	pid = wait ((int *) 0);
X
X	/*
X	 * See what children have died recently.
X	 */
X
X	for (i = 0;pid != -1 && i < nsessions;i++) {
X
X		/*
X		 * Close their master sides and mark the
X		 * session as "available".
X		 */
X
X		if (pid == childpids[i]) {
X			childpids[i] = -1;
X			if (masters[i] != -1) {
X				close (masters[i]);
X				if (writepid != -1)
X					kill (writepid, SIGTERM);
X
X				masters[i] = -1;
X			}
X			if (labels[i]) {
X				free (labels[i]);
X				labels[i] = 0;
X			}
X			break;
X		}
X	}
X	if (writepid != -1 && pid == writepid)
X		writepid = -1;
X
X	if (pspid != -1 && pid == pspid)
X		pspid = -1;
X
X	signal (sig, murder);
X}
X
X/*
X * catch - catch a signal and set a flag
X */
X
Xvoid
Xcatch (sig)
Xint	sig;
X{
X	caught = 1;
X	signal (sig, catch);
X}
X
X/*
X * reader - read characters from the pty and write to the screen
X */
X
Xint
Xreader (fd)
Xint	fd;
X{
X	char	buf[16];	/* do reads in 16 byte bursts */
X	int	cnt;		/* how many bytes were read */
X	int	wanted = sizeof buf; /* how may bytes to try reading */
X	int	flags;		/* fcntl flags */
X
X	/*
X	 * Ignore the SIGINT and SIGQUIT signals.
X	 */
X
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X
X	flags = fcntl (fd, F_GETFL, 0);
X	fcntl (fd, F_SETFL, flags|O_NDELAY);
X
X	while (1) {
X		if ((cnt = read (fd, buf, wanted)) == -1) {
X			if (errno != EINTR)
X				return -1;
X
X			if (caught)
X				return 0;
X			else
X				continue;
X		}
X		if (cnt == 0) {
X			wanted = 1;
X			fcntl (fd, F_SETFL, flags & ~O_NDELAY);
X			continue;
X		}
X		if (wanted == 1)
X			flags = fcntl (fd, F_GETFL, flags|O_NDELAY);
X
X		wanted = sizeof buf;
X		write (1, buf, cnt);
X	}
X}
X
X/*
X * writer - write characters read from the keyboard down the pty
X */
X
Xwriter (fd)
Xint	fd;
X{
X	char	c;
X	int	cnt;
X	int	zflg = 0;
X
X	signal (SIGINT, SIG_IGN);
X	signal (SIGQUIT, SIG_IGN);
X	signal (SIGHUP, _exit);
X
X	/*
X	 * Read characters until an error is returned or ^Z is seen
X	 * followed by a non-^Z character.
X	 */
X
X	while (1) {
X		errno = 0;
X		if ((cnt = read (0, &c, 1)) == 0)
X			continue;
X
X		/*
X		 * Some signal may have occured, so retry
X		 * the read.
X		 */
X
X		if (cnt == -1) {
X			if (errno == EINTR && caught)
X				continue;
X			else
X				exit (0);
X		}
X
X		/*
X		 * Process a ^Z.  If one was not seen earlier, 
X		 * set a flag and go read another character.
X		 */
X
X		if (c == ('z' & 037)) {
X			if (! zflg++)
X				continue;
X		}
X		
X		/*
X		 * See if a ^Z was seen before.  If so, signal
X		 * the master and exit.
X		 */
X
X		else if (zflg) {
X			kill (getppid (), SIGUSR1);
X			exit (0);
X		}
X
X		/*
X		 * Just output the character as is.
X		 */
X
X		zflg = 0;
X		if (write (fd, &c, 1) != 1)
X			break;
X	}
X	exit (0);
X}
X
X/*
X * usage - command line syntax
X */
X
Xusage ()
X{
X	fprintf (stderr, "usage: sm\n");
X	exit (1);
X}
X
X/*
X * help - built-in command syntax
X */
X
Xhelp ()
X{
X	fprintf (stderr, "Valid commands are:\n");
X	fprintf (stderr, "\tconnect [ # ]\t(connects to session)\n");
X	fprintf (stderr, "\tcreate\t\t(sets up a new pty session)\n");
X	fprintf (stderr, "\tcurrent\t\t(shows current session number)\n");
X	fprintf (stderr, "\tdelete #\t(deletes session)\n");
X	fprintf (stderr, "\thelp\t\tdisplay this message\n");
X	fprintf (stderr, "\tjobs\t\t(shows a ps listing of current session)\n");
X	fprintf (stderr, "\tquit\tor exit (terminate session manager)\n");
X	fprintf (stderr, "\tset #\t\t(# is 0-15 - selets current session)\n");
X	fprintf (stderr, "\ttoggle\t\t(switch to previous session)\n\n");
X	fprintf (stderr, "Commands may be abbreviated to a unique prefix\n\n");
X	fprintf (stderr, "Note - to exit a session back into sm so one can\n");
X	fprintf (stderr, " select another session, type a ^Z and a return\n");
X	fprintf (stderr, " To send ^Z to the shell, type two ^Z chars\n\n");
X}
X
X/*
X * session - create a new session on a pty
X */
X
Xint
Xsession (prompt, init)
Xchar	*prompt;
Xint	init;
X{
X	char	mastername[BUFSIZ];
X	char	slavename[BUFSIZ];
X	char	newprompt[32];
X	char	newshell[32];
X	char	*digits = "0123456789abcdef";
X	char	*letters = "pqrs";
X	char	*shell;
X	char	*arg;
X	int	oumask;
X	int	i;
X	int	pty;
X	int	ptys = 64;
X	struct	stat	sb;
X	struct	passwd	*pwd;
X
X	/*
X	 * Find the number of the new session.  An error will be
X	 * given if no sessions are available.
X	 */
X
X	for (i = 0;i < nsessions && masters[i] != -1;i++)
X		;
X
X	if (i == MAXSESSIONS) {
X		printf ("out of sessions\n");
X		return 0;
X	}
X	if (i == nsessions)
X		nsessions++;
X
X	/*
X	 * Save the previous sesssion number.  This is so the
X	 * "toggle" command will work after a "create".
X	 */
X
X	if (current != -1)
X		last = current;
X
X	current = i;
X
X	/*
X	 * Go find the master side of a PTY to use.  Masters are
X	 * found by trying to open them.  Each PTY master is an
X	 * exclusive access device.  If every pty is tried but no
X	 * available ones are found, scream.
X	 */
X
X	for (pty = 0;pty < ptys;pty++) {
X		sprintf (mastername, "/dev/pty%c%c",
X			letters[pty >> 4], digits[pty & 0xf]);
X		if ((masters[i] = open (mastername, O_RDWR)) != -1)
X			break;
X	}
X	if (masters[i] == -1) {
X		printf ("out of ptys\n");
X		return 0;
X	}
X
X	/*
X	 * Let's make a child process.
X	 */
X
X	switch (childpids[i] = fork ()) {
X		case -1:
X			printf ("out of processes\n");
X			return 0;
X		case 0:
X
X			/*
X			 * Disassociate from the parent process group
X			 * and tty's.
X			 */
X
X			close (0);
X			close (1);
X			for (i = 0;i < nsessions;i++)
X				close (masters[i]);
X
X			setpgrp ();
X
X			/*
X			 * Reset any signals that have been upset.
X			 */
X
X			signal (SIGINT, SIG_DFL);
X			signal (SIGQUIT, SIG_DFL);
X			signal (SIGCLD, SIG_DFL);
X			signal (SIGHUP, SIG_DFL);
X			signal (SIGUSR1, SIG_DFL);
X
X			/*
X			 * Make up the name of the slave side of the
X			 * PTY and open it.  It will be opened as stdin.
X			 */
X
X			sprintf (slavename, "/dev/tty%c%c",
X				letters[pty >> 4], digits[pty & 0xf]);
X
X			if (open (slavename, O_RDWR) == -1) {
X				fprintf (stderr, "can't open %s\n", slavename);
X				_exit (-1);
X			}
X
X			/*
X			 * Try to change the owner of the master and slave
X			 * side of the PTY.  This will only work if the
X			 * invoker has an effective UID of 0.  Change the
X			 * mode of the slave to be the same as the parent
X			 * tty.
X			 */
X
X			(void) chown (mastername, getuid (), getgid ());
X			(void) chown (slavename, getuid (), getgid ());
X			if (fstat (2, &sb) == 0)
X				(void) chmod (slavename, sb.st_mode & 0777);
X
X			/*
X			 * Close the last open file descriptor and make
X			 * the new stdout and stderr descriptors.  Copy
X			 * the tty modes from the parent tty to the slave
X			 * pty.
X			 */
X
X			close (2);
X			dup (0);
X			dup (0);
X			ioctl (0, TCSETAF, &sanetty);
X
X			/*
X			 * See if the invoker has a shell in their
X			 * environment and use the default value if
X			 * not.
X			 */
X
X			if (! (shell = getenv ("SHELL")))
X				shell = "/bin/sh";
X
X			/*
X			 * Set the PS1 variable to "prompt " if
X			 * "prompt" looks reasonable.
X			 */
X
X			if (! isalpha (*prompt))
X				prompt = strrchr (slavename, '/') + 1;
X
X			sprintf (newprompt, "PS1=%.16s%c ",
X					prompt, getuid () ? '$':'#');
X				
X			for (i = 0;environ[i];i++)
X				if (! strncmp (environ[i], "PS1=", 4))
X					break;
X
X			if (environ[i])
X				environ[i] = newprompt;
X
X			/*
X			 * See about changing current directory
X			 */
X
X			if (init && (pwd = getpwuid (getuid ())))
X				chdir (pwd->pw_dir);
X
X			endpwent ();
X
X			/*
X			 * Undo any set-UID or set-GID bits on the
X			 * executable.
X			 */
X
X			setgid (getgid ());
X			setuid (getuid ());
X
X			/*
X			 * Start off the new session.
X			 */
X
X			if (arg = strrchr (shell, '/'))
X				arg++;
X			else
X				arg = shell;
X
X			if (init)
X				sprintf (newshell, "-%s", arg), arg = newshell;
X
X			execl (shell, arg, 0);
X			_exit (-1);
X	}
X	i = fcntl (masters[current], F_GETFL, 0);
X	fcntl (masters[current], F_SETFL, i|O_NDELAY);
X	return 1;
X}
X
X/*
X * quit - kill all active sessions
X */
X
Xquit (sig)
Xint	sig;
X{
X	int	i;
X
X	for (i = 0;i < nsessions;i++) {
X		if (masters[i] != -1) {
X			close (masters[i]);
X			masters[i] = -1;
X
X			if (childpids[i] != -1)
X				kill (- childpids[i], SIGHUP);
X		}
X	}
X	exit (sig);
X}
X
X/*
X * label - manipulate session labels
X *
X *	label() will either create a new label (if 'sess' != -1) or
X *	return the session number which matches 'name'.
X */
X
Xint
Xlabel (sess, name)
Xint	sess;
Xchar	*name;
X{
X	char	buf[16];
X	int	i;
X	char	*cp;
X
X	/*
X	 * Make "name" into something with no leading
X	 * whitespace that consists of characters in
X	 * [a-zA-Z0-9].
X	 */
X
X	for (cp = name;isspace (*cp);cp++)
X		;
X
X	for (i = 0;cp[i] && i < (sizeof buf - 1);i++) {
X		if (isalnum (cp[i]))
X			buf[i] = cp[i];
X		else
X			return -1;
X	}
X	buf[i] = '\0';
X	cp = buf;
X
X	/*
X	 * Look-up the session named "name"
X	 */
X
X	if (sess == -1) {
X
X		/*
X		 * See if 'name' is a label
X		 */
X
X		for (i = 0;i < nsessions;i++)
X			if (strcmp (labels[i], cp) == 0)
X				return i;
X
X		/*
X		 * Perhaps 'name' is a number?!?
X		 */
X
X		i = strtol (name, &cp, 10);
X		if (*cp)
X			return -1;
X		else
X			return i;
X	}
X	
X	/*
X	 * Add a new name, can't be a number.
X	 */
X
X	if (isdigit (*cp))
X		return -1;
X
X	/*
X	 * Add a new session named "name"
X	 */
X
X	if (labels[sess])
X		free (labels[sess]);
X
X	if (! (labels[sess] = malloc (strlen (cp) + 1)))
X		return -1;
X
X	strcpy (labels[sess], cp);
X	return sess;
X}
X
X/*
X * sm - manage pty sessions
X */
X
Xmain (argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	char	buf[BUFSIZ];
X	char	*cp;
X	int	i;
X	int	pid;
X	FILE	*fp;
X
X	/*
X	 * No arguments are allowed on the command line
X	 */
X
X	if (argc > 1)
X		usage ();
X
X	/*
X	 * Set up all the file descriptors and process IDs
X	 */
X
X	for (i = 0;i < MAXSESSIONS;i++) {
X		childpids[i] = -1;
X		masters[i] = -1;
X	}
X
X	/*
X	 * Get the current tty settings, and make a copy that can
X	 * be tinkered with.  The sane values are used while getting
X	 * commands.  The raw values are used while sessions are
X	 * active.  New sessions are set to have the same tty values
X	 * as the sane values.
X	 */
X
X	ioctl (0, TCGETA, &sanetty);
X	rawtty = sanetty;
X
X	rawtty.c_oflag &= ~OPOST;
X	rawtty.c_lflag = 0;
X	rawtty.c_cc[VMIN] = 1;
X	rawtty.c_cc[VTIME] = 1;
X
X	/*
X	 * SIGCLG is caught to detect when a session has died or when
X	 * the writer for the session has exited.  SIGUSR1 is used to
X	 * signal that a ^Z has been seen.
X	 */
X
X	signal (SIGCLD, murder);
X	signal (SIGUSR1, catch);
X
X	/*
X	 * The file $HOME/.smrc is read for initializing commands.
X	 */
X
X	if (cp = getenv ("HOME")) {
X		sprintf (buf, "%s/.smrc", cp);
X		if (access (buf, 04) != 0 || ! (fp = fopen (buf, "r")))
X			fp = stdin;
X	}
X
X	/*
X	 * This is the main loop.  A line is read and executed.  If
X	 * EOF is read, the loop is exited (except if input is the
X	 * .smrc file).
X	 */
X
X	while (1) {
X
X		/*
X		 * Keyboard signals cause an exit.
X		 */
X
X		signal (SIGINT, quit);
X		signal (SIGQUIT, quit);
X
X		/*
X		 * Prompt for input only when it is not coming from
X		 * the .smrc file.  A single line will be read and
X		 * executed.  The read is retried if an interrupt
X		 * has been seen.
X		 */
X
X		if (fp == stdin) {
X			printf ("pty-> ");
X			fflush (stdout);
X		}
X		while (errno = 0, fgets (buf, sizeof buf, fp) == 0) {
X			if (errno == EINTR) {
X				continue;
X			} else if (fp != stdin) {
X				fclose (fp);
X				fp = stdin;
X				buf[0] = '\0';
X				break;
X			} else {
X				strcpy (buf, "quit");
X				break;
X			}
X		}
X		if (cp = strchr (buf, '\n'))
X			*cp = '\0';
X
X		if (! buf[0])
X			continue;
X
X		/*
X		 * Parse the command.  Each command consists of a
X		 * verb, with some commands accepting a session ID
X		 * to act on.  The command will be accepted if a
X		 * unique prefix of the command is entered.
X		 */
X
X		if (parse (buf, "q*uit") || parse (buf, "e*xit")) {
X
X			/*
X			 * Just give up.
X			 */
X
X			quit (0);
X		} else if (cp = parse (buf, "cr*eate")) {
X			int	init = 0;
X
X			/*
X			 * Create a new session and make it current
X			 */
X
X			while (*cp == ' ' || *cp == '\t')
X				cp++;
X
X			if (*cp == '-')
X				init++, cp++;
X
X			if (session (cp, init))
X				label (current, cp);
X
X			continue;
X		} else if (parse (buf, "cu*rrent")) {
X
X			/*
X			 * Give the session ID of the current session.
X			 */
X
X			if (current != -1) {
X				if (labels[current])
X					printf ("current session is \"%s\"\n",
X						labels[current]);
X				else
X					printf ("current session is %d\n",
X						current);
X			} else
X				printf ("no current session\n");
X
X			continue;
X		} else if (cp = parse (buf, "s*et")) {
X
X			/*
X			 * Set the current session ID to #
X			 */
X
X			if (*cp) {
X				if ((i = label (-1, cp)) != -1) {
X					last = current;
X					current = i;
X					continue;
X				}
X				/* FALLTHROUGH */
X			}
X			printf ("eh?\n");
X			continue;
X		} else if (parse (buf, "a*ctive")) {
X
X			/*
X			 * List the session IDs of all active sessions
X			 */
X
X			for (i = 0;i < nsessions;i++) {
X				if (masters[i] != -1) {
X					if (labels[i])
X						printf ("%s ", labels[i]);
X					else
X						printf ("%d ", i);
X
X				}
X			}
X			putchar ('\n');
X			continue;
X		} else if (cp = parse (buf, "j*obs")) {
X			int	pids = 0;
X
X			/*
X			 * Look for a "-" flag
X			 */
X
X			while (isspace (*cp))
X				cp++;
X
X			if (*cp != '-') {
X				for (pids = i = 0;i < nsessions;i++) {
X					if (childpids[i] == -1)
X						continue;
X					else
X						pids++;
X
X					if (labels[i])
X						printf ("%s (%d)\n", labels[i],
X							childpids[i]);
X					else
X						printf ("#%d (%d)\n", i,
X							childpids[i]);
X				}
X				if (! pids)
X					printf ("no jobs\n");
X
X				continue;
X			}
X
X			/*
X			 * Give a "ps" listing of all active sessions
X			 */
X
X			buf[0] = '\0';
X			for (i = 0;i < nsessions;i++) {
X				if (childpids[i] != -1) {
X					if (pids++)
X						strcat (buf, ",");
X
X					sprintf (buf + strlen (buf), "%d",
X						childpids[i]);
X				}
X			}
X			if (pids) {
X				if (! (pspid = fork ())) {
X					setgid (getgid ());
X					setuid (getuid ());
X					execl ("/bin/ps", "ps", "-fp", buf, 0);
X					_exit (1);
X				}
X				while (pspid != -1)
X					pause ();
X			} else {
X				printf ("no jobs\n");
X			}
X			continue;
X		} else if (cp = parse (buf, "co*nnect")) {
X
X			/*
X			 * Connect to the current or named session.
X			 */
X
X			if (*cp) {
X				if ((i = label (-1, cp)) != -1) {
X					last = current;
X					current = i;
X					/* FALLTHROUGH */
X				} else {
X					printf ("eh?\n");
X					continue;
X				}
X			}
X		} else if (parse (buf, "t*oggle")) {
X
X			/*
X			 * Toggle between the previous and current session
X			 */
X
X			i = current;
X			current = last;
X			last = i;
X			/* FALLTHROUGH */
X		} else if (cp = parse (buf, "d*elete")) {
X
X			/*
X			 * Delete the named session
X			 */
X
X			if (*cp) {
X				i = label (-1, cp);
X				if (i >= 0 && i < MAXSESSIONS) {
X					if (masters[i] != -1) {
X						close (masters[i]);
X						masters[i] = -1;
X						if (childpids[i] != -1)
X							kill (- childpids[i], SIGHUP);
X
X						continue;
X					}
X				}
X				/* FALLTHROUGH */
X			}
X			printf ("eh?\n");
X			continue;
X		} else if ((i = label (-1, buf)) != -1) {
X			if (i >= 0 && i < MAXSESSIONS && masters[i]) {
X				last = current;
X				current = i;
X			} else {
X				printf ("eh?\n");
X				continue;
X			}
X		} else {
X
X			/*
X			 * The command was not recognized
X			 */
X
X			help ();
X			continue;
X		}
X
X		/*
X		 * Validate the session number.  It must be in the
X		 * range 0 .. (MAXSESSIONS-1) to be valid.  The current
X		 * session must also be associated with an open PTY.
X		 */
X
X		if (current < 0 || current >= MAXSESSIONS)
X			current = -1;
X
X		if (current == -1 || masters[current] == -1) {
X			printf ("no current session\n");
X			current = -1;
X			continue;
X		}
X
X		/*
X		 * Let's make a process to read from the child ...
X		 */
X
X		switch (writepid = fork ()) {
X			case -1:
X				if (childpids[current] != -1)
X					kill (childpids[current], SIGKILL);
X
X				perror ("fork");
X				break;
X			case 0:
X				writer (masters[current]);
X				exit (1);
X		}
X
X		/*
X		 * Set up the raw TTY modes and start writing to
X		 * the child.
X		 */
X
X		ioctl (0, TCSETAF, &rawtty);
X
X		if (reader (masters[current]) == -1) {
X			close (masters[current]);
X			masters[current] = -1;
X			childpids[current] = -1;
X			current = -1;
X			if (writepid > 0)
X				kill (writepid, SIGTERM);
X		}
X
X		/*
X		 * Reset the tty modes to resume the command loop.
X		 */
X
X		ioctl (0, TCSETA, &sanetty);
X	}
X	exit (0);
X}
SHAR_EOF
if test 18474 -ne "`wc -c < 'sm.c'`"
then
	echo shar: "error transmitting 'sm.c'" '(should have been 18474 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                             UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 832-8832                           Domain: jfh@rpp386.cactus.org
"While you are here, your wives and girlfriends are dating handsome American
 movie and TV stars. Stars like Tom Selleck, Bruce Willis, and Bart Simpson."