Writing to multiple terminals (BSD)

Rich Salz rsalz at bbn.com
Fri Dec 8 04:10:11 AEST 1989


Someone wanted a program that would write to multiple terminals at the
same time.  I had hacked the BSD "script" program to do that; here's the
source.  Compile with -DTUTOR.  Usage:
	tutor /dev/tty3 tty4 /dev/pty2 ...
(If you don't put / or . in front of the pathname, /dev is assumed.)

It's a useful hack for when you want to run through a demo and show
everyone in a class what's going on.
	/r$

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  script.c
# Wrapped by rsalz at bbn.com on Thu Dec  7 12:08:12 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'script.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'script.c'\"
else
echo shar: Extracting \"'script.c'\" \(7226 characters\)
sed "s/^X//" >'script.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifdef	TUTOR
X/*
X**  Hacked on by Rich $alz <rsalz at bbn.com> so that it writes to multiple
X**  devices at the same time.  Compile with -DTUTOR to get that effect.
X**	cc -DTUTOR -o tutor script.c
X**  Usage:
X**	tutor console tty3 /dev/ttyp2 ...
X*/
X#endif	/* TUTOR */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)script.c	5.6 (Berkeley) 6/29/88";
X#endif /* not lint */
X
X/*
X * script
X */
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <stdio.h>
X#include <signal.h>
X
Xchar	*shell;
X#ifndef	TUTOR
XFILE	*fscript;
X#else	/* TUTOR */
Xint ttys[50];
Xint count;
X#endif	/* TUTOR */
Xint	master;
Xint	slave;
Xint	child;
Xint	subchild;
X#ifndef	TUTOR
Xchar	*fname;
X#endif	/* TUTOR */
X
Xstruct	sgttyb b;
Xstruct	tchars tc;
Xstruct	ltchars lc;
Xstruct	winsize win;
Xint	lb;
Xint	l;
Xchar	*line = "/dev/ptyXX";
Xint	aflg;
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	extern char *optarg;
X	extern int optind;
X	int ch;
X	int finish();
X	char *getenv();
X
X	while ((ch = getopt(argc, argv, "a")) != EOF)
X		switch((char)ch) {
X		case 'a':
X			aflg++;
X			break;
X		case '?':
X		default:
X#ifndef	TUTOR
X			fprintf(stderr, "usage: script [-a] [file]\n");
X#else	/* TUTOR */
X			fprintf(stderr, "usage: script [-a] ttys...\n");
X#endif	/* TUTOR */
X			exit(1);
X		}
X	argc -= optind;
X	argv += optind;
X
X#ifndef	TUTOR
X	if (argc > 0)
X		fname = argv[0];
X	else
X		fname = "typescript";
X	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
X		perror(fname);
X		fail();
X#else	/* TUTOR */
X	if (argc == 0) {
X		fprintf(stderr, "usage: script [-a] ttys...\n");
X		exit(1);
X	}
X	openthem(argv);
X#endif	/* TUTOR */
X
X	shell = getenv("SHELL");
X	if (shell == NULL)
X		shell = "/bin/sh";
X
X	getmaster();
X#ifndef	TUTOR
X	printf("Script started, file is %s\n", fname);
X#else
X	printf("Script started.\n");
X#endif	/* TUTOR */
X	fixtty();
X
X	(void) signal(SIGCHLD, finish);
X	child = fork();
X	if (child < 0) {
X		perror("fork");
X		fail();
X	}
X	if (child == 0) {
X		subchild = child = fork();
X		if (child < 0) {
X			perror("fork");
X			fail();
X		}
X		if (child)
X			dooutput();
X		else
X			doshell();
X	}
X	doinput();
X}
X
Xdoinput()
X{
X	register int cc;
X	char ibuf[BUFSIZ];
X
X#ifndef	TUTOR
X	(void) fclose(fscript);
X#else	/* TUTOR */
X	closethem();
X#endif	/* TUTOR */
X	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
X		(void) write(master, ibuf, cc);
X	done();
X}
X
X#include <sys/wait.h>
X
Xfinish()
X{
X	union wait status;
X	register int pid;
X	register int die = 0;
X
X	while ((pid = wait3(&status, WNOHANG, 0)) > 0)
X		if (pid == child)
X			die = 1;
X
X	if (die)
X		done();
X}
X
Xdooutput()
X{
X	register int cc;
X	time_t tvec, time();
X	char obuf[BUFSIZ], *ctime();
X
X	(void) close(0);
X	tvec = time((time_t *)NULL);
X#ifndef	TUTOR
X	fprintf(fscript, "Script started on %s", ctime(&tvec));
X#else	/* TUTOR */
X	(void) sprintf(obuf, "Script started on %s", ctime(&tvec));
X	writethem(obuf, strlen(obuf));
X#endif	/* TUTOR */
X	for (;;) {
X		cc = read(master, obuf, sizeof (obuf));
X		if (cc <= 0)
X			break;
X		(void) write(1, obuf, cc);
X#ifndef	TUTOR
X		(void) fwrite(obuf, 1, cc, fscript);
X#else	/* TUTOR */
X		writethem(obuf, cc);
X#endif	/* TUTOR */
X	}
X	done();
X}
X
Xdoshell()
X{
X	int t;
X
X	t = open("/dev/tty", O_RDWR);
X	if (t >= 0) {
X		(void) ioctl(t, TIOCNOTTY, (char *)0);
X		(void) close(t);
X	}
X	getslave();
X	(void) close(master);
X#ifndef	TUTOR
X	(void) fclose(fscript);
X#else	/* TUTOR */
X	closethem();
X#endif	/* TUTOR */
X	(void) dup2(slave, 0);
X	(void) dup2(slave, 1);
X	(void) dup2(slave, 2);
X	(void) close(slave);
X	execl(shell, "sh", "-i", 0);
X	perror(shell);
X	fail();
X}
X
Xfixtty()
X{
X	struct sgttyb sbuf;
X
X	sbuf = b;
X	sbuf.sg_flags |= RAW;
X	sbuf.sg_flags &= ~ECHO;
X	(void) ioctl(0, TIOCSETP, (char *)&sbuf);
X}
X
Xfail()
X{
X
X	(void) kill(0, SIGTERM);
X	done();
X}
X
Xdone()
X{
X	time_t tvec, time();
X#ifndef	TUTOR
X	char *ctime();
X#else	/* TUTOR */
X	char *ctime(), buff[128];
X#endif	/* TUTOR */
X
X	if (subchild) {
X		tvec = time((time_t *)NULL);
X#ifndef	TUTOR
X		fprintf(fscript,"\nscript done on %s", ctime(&tvec));
X		(void) fclose(fscript);
X#else	/* TUTOR */
X		(void) sprintf(buff,"\nscript done on %s", ctime(&tvec));
X		writethem(buff, strlen(buff));
X		closethem();
X#endif	/* TUTOR */
X		(void) close(master);
X	} else {
X		(void) ioctl(0, TIOCSETP, (char *)&b);
X#ifndef	TUTOR
X		printf("Script done, file is %s\n", fname);
X#else	/* TUTOR */
X		printf("Script done.\n");
X#endif	/* TUTOR */
X	}
X	exit(0);
X}
X
Xgetmaster()
X{
X	char *pty, *bank, *cp;
X	struct stat stb;
X
X	pty = &line[strlen("/dev/ptyp")];
X	for (bank = "pqrs"; *bank; bank++) {
X		line[strlen("/dev/pty")] = *bank;
X		*pty = '0';
X		if (stat(line, &stb) < 0)
X			break;
X		for (cp = "0123456789abcdef"; *cp; cp++) {
X			*pty = *cp;
X			master = open(line, O_RDWR);
X			if (master >= 0) {
X				char *tp = &line[strlen("/dev/")];
X				int ok;
X
X				/* verify slave side is usable */
X				*tp = 't';
X				ok = access(line, R_OK|W_OK) == 0;
X				*tp = 'p';
X				if (ok) {
X				    (void) ioctl(0, TIOCGETP, (char *)&b);
X				    (void) ioctl(0, TIOCGETC, (char *)&tc);
X				    (void) ioctl(0, TIOCGETD, (char *)&l);
X				    (void) ioctl(0, TIOCGLTC, (char *)&lc);
X				    (void) ioctl(0, TIOCLGET, (char *)&lb);
X				    (void) ioctl(0, TIOCGWINSZ, (char *)&win);
X					return;
X				}
X				(void) close(master);
X			}
X		}
X	}
X	fprintf(stderr, "Out of pty's\n");
X	fail();
X}
X
Xgetslave()
X{
X
X	line[strlen("/dev/")] = 't';
X	slave = open(line, O_RDWR);
X	if (slave < 0) {
X		perror(line);
X		fail();
X	}
X	(void) ioctl(slave, TIOCSETP, (char *)&b);
X	(void) ioctl(slave, TIOCSETC, (char *)&tc);
X	(void) ioctl(slave, TIOCSLTC, (char *)&lc);
X	(void) ioctl(slave, TIOCLSET, (char *)&lb);
X	(void) ioctl(slave, TIOCSETD, (char *)&l);
X	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
X}
X
X
X#ifdef	TUTOR
Xopenthem(argv)
X	char **argv;
X{
X	char buff[BUFSIZ];
X
X	for ( ; *argv; argv++) {
X		/* If path doesn't start with . or / assume it's in /dev */
X		if (**argv != '.' && **argv != '/') {
X			(void) sprintf(buff, "/dev/%s", *argv);
X			*argv = buff;
X		}
X		ttys[count] = open(*argv, O_WRONLY | O_NDELAY);
X		if (ttys[count] < 0)
X			perror(*argv);
X		else
X			count++;
X	}
X
X	if (count == 0) {
X		fprintf(stderr, "No devices open.\n");
X		exit(1);
X	}
X}
X
Xwritethem(p, i)
X	char *p;
X	int i;
X{
X	int j;
X
X	for (j = 0; j < count; j++)
X		(void) write(ttys[j], p, i);
X}
X
Xclosethem()
X{
X	while (--count >= 0)
X		(void)close(ttys[count]);
X}
X#endif	/* TUTOR */
END_OF_FILE
if test 7226 -ne `wc -c <'script.c'`; then
    echo shar: \"'script.c'\" unpacked with wrong size!
fi
# end of 'script.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.unix.questions mailing list