[alt.sources] Duplicate output to multiple terminals

psfales@cbnewsc.ATT.COM (Peter Fales) (11/10/89)

In article <256@telxon.UUCP>, dank@telxon.UUCP (Dan Kelley) writes:
> 
> I am looking for an idea to duplicate output on different terminals.
> Obviously, the choice at first is easy, just use the "tee" program
> in this manner:
> 	program | tee /dev/tty???
> 	
> Well, I have done this and am still having problems getting everything
> to the device(s).

The trick I saw to do this recently really made me sit up and take
notice...

The "teacher" executes the following command:

	cu XXXX	| tee /tmp/tmpfile

where XXX  is a phone number or machine name for the computer to
be demonstrated (this may result in logging in to the same machine 
running the cu command, but not necessarily).  

The "students" run 

	tail -f /tmp/tmpfile

I don't totally understand why this works, but it does.  You can even
see all the "teacher's" key strokes in real time.

-- 
Peter Fales			AT&T, Room 5B-420
N9IYJ				2000 N. Naperville Rd.
UUCP:	...att!peter.fales	Naperville, IL 60566
Domain: peter.fales@att.com	work:	(312) 979-8031

cpcahil@virtech.uucp (Conor P. Cahill) (11/10/89)

In article <4647@cbnewsc.ATT.COM>, psfales@cbnewsc.ATT.COM (Peter Fales) writes:
> 	cu XXXX	| tee /tmp/tmpfile
> 
> [text deleted...] 
> 
> 	tail -f /tmp/tmpfile
> 
> I don't totally understand why this works, but it does.  You can even
> see all the "teacher's" key strokes in real time.

The reason that this works is that cu starts up two programs, one that 
takes all of you're keyboard input and writes it out the communications port and
one that reads all of the data comming in the comm port and writes it to stdout.

In your example stdout is the tee, which copies the data to the logfile and
writes it to the terminal.  Since the data is being written to the log file,
the students that read the log file will see all data comming from the comm
port.

You don't see all of the teacher's keystrokes, you see all of the data ouput
from the system to the terminal.  Many of the teacher's keystrokes are echoed
back by the system so that is why you see most of them.
-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

tom@ismdqa.intel.com (Tom Soukup ~) (11/12/89)

In article <4647@cbnewsc.ATT.COM> psfales@cbnewsc.ATT.COM (Peter Fales) writes:
>In article <256@telxon.UUCP>, dank@telxon.UUCP (Dan Kelley) writes:
>> I am looking for an idea to duplicate output on different terminals.
>The "teacher" executes the following command:
>	cu XXXX	| tee /tmp/tmpfile
>The "students" run 
>	tail -f /tmp/tmpfile
>I don't totally understand why this works, but it does.  You can even
>see all the "teacher's" key strokes in real time.

This works because tail -f writes the contents of the file to the standard
output (the screen) and then waits.  As additional characters are put into
the file, tail -f continues writing them to the screen.  It will continue
waiting forever (I think) unless interupted.  So the cu | tee copies
everything that the cu echos into tmpfile and the tail prints everything
that goes into tmpfile on thr students screen.  Good idea, I wish I'd
thought of it :-).

>Peter Fales			AT&T, Room 5B-420

Tom
________________________________________________________________________________
DISCLAMER:  Intel doesn't agree with much of anything that I say.
UUCP:	{amdcad,decwrl,hplabs,oliveb,pur-ee,qantel}!intelca!mipos3!ismdqa!tom
ARPA:	tom%ismdqa.intel.com@relay.cs.net       CSNET:	tom@ismdqa.intel.com

mercer@ncrcce.StPaul.NCR.COM (Dan Mercer) (11/13/89)

In article <353@bilver.UUCP> bill@bilver.UUCP (Bill Vermillion) writes:
:In article <256@telxon.UUCP-> dank@telxon.UUCP (Dan Kelley) writes:
:->
:->I am looking for an idea to duplicate output on different terminals.
:->Obviously, the choice at first is easy, just use the "tee" program
:->in this manner:
:->	program | tee /dev/tty???
:->	
:->Well, I have done this and am still having problems getting everything
:->to the device(s).  Seems when I run something like a database package,
:->I do not get the duplication as I should.  In fact, I do not get the
:->proper I/O from the program on my own tty.  Seems "tee" is not dealing
:->with I/O properly and there also may be a buffering problem.
: 
:How about a hardware solution, and take the output going to terminal A and put
:it to terminal B through a diode.  
:
:I had a serial board fail in the mode.  So that anthing on A went to A & B,
:but anything directed to B stayed at B.   
:
:   
:
:-- 
:Bill Vermillion - UUCP: {uiucuxc,hoptoad,petsd}!peora!tarpit!bilver!bill
:                      : bill@bilver.UUCP

In order for tee to work,  the program must use stdout and stderr for
output.  Vi and many other programs require a terminal,  so thet open
/dev/tty directly.  If they are using rawmode input and output,  they
frequently will do ioctl against stdin,  stdout or stderr.  If this
is a pipe,  it will fail (but few people have the sense to check for
the failure)  and your machines termio will be all screwed up.

I had to demo a programming environment that used a rawmode menuing
system laid over vi.  Consequently,  tee would not work.  Cu could
not handle the high speed of the lines and the large number of rawmode
transactions.  So I rolled my own,  called demo2tty.  I can mail it
to you if you like.  Standard disclaimers apply (SysVr1).

To all the net.policeman objecting to this type of entry,  may I
remind you that management looks askance at any alt groups appearing
on the net,  and many of us do not have alt.sources.d.

-- 

Dan Mercer
Reply-To: mercer@ncrcce.StPaul.NCR.COM (Dan Mercer)

mercer@ncrcce.StPaul.NCR.COM (Dan Mercer) (11/13/89)

I forgot to add to the last article,  the use of /dev/tty by many
programs makes programs that log out users logged in but not working
almost impossible to implement.

We're using one on our news machine that has timed me out several times
while transferring files using kermit and once while editting a long
article in vi.  (that was the worst,  cause vi didn't hang up and
when I logged back on I got into vi when the machine was supposed to
be asking for the dialup password.

The times for /dev/tty get updated,  of course,  not the times for
/dev/ttyxx.

-- 

Dan Mercer
Reply-To: mercer@ncrcce.StPaul.NCR.COM (Dan Mercer)

drw@schubert.math.mit.edu (Dale R. Worley) (11/14/89)

Here's a program I use that uses interprocessor communications.  It
seems to work fine.  It was hacked up from the Emacs client/server
code.

To run the master program, do:

	program | broadcast

Then just:

	receiver

to run a slave.  broadcast and receiver have to be run in the same
account.  These could be hacked up to use Internet IPC, etc. to make
them more useful, but they do what I needed done.

Dale
---------------------------------------- broadcast.c
/* Broadcast stdin to everybody who wants to listen */

/* Communication subprocess for GNU Emacs acting as server.
   Copyright (C) 1986, 1987 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is distributed in the hope that it will be useful,
but without any warranty.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.

Everyone is granted permission to copy, modify and redistribute
GNU Emacs, but only under the conditions described in the
document "GNU Emacs copying permission notice".   An exact copy
of the document is supposed to have been given to you along with
GNU Emacs so that you can know how you may redistribute it all.
It should be in a file named COPYING.  Among other things, the
copyright notice and this notice must be preserved on all copies.  */


/* The GNU Emacs edit server process is run as a subprocess of Emacs
   under control of the file lisp/server.el.
   This program accepts communication from client (program emacsclient.c)
   and passes their commands (consisting of keyboard characters)
   up to the Emacs which then executes them.  */

/* This code is BSD only. */

#include <sys/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/un.h>
#include <stdio.h>
#include <errno.h>

#include <fcntl.h>

extern int errno;

main ()
{
  int s, infd, fromlen;
  struct sockaddr_un server, fromunix;
  char *homedir;
  char *str, string[BUFSIZ], code[BUFSIZ];
  FILE *infile;
  FILE **openfiles;
  int openfiles_size;
  int i;

  char *getenv ();

  openfiles_size = 20;
  openfiles = (FILE **) malloc (openfiles_size * sizeof (FILE *));
  if (openfiles == 0)
    abort ();
  for (i = 0; i < openfiles_size; i++)
    openfiles[i] == NULL;

  /* 
   * Open up an AF_UNIX socket in this person's home directory
   */

  if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
    {
      perror ("socket");
      exit (1);
    }
  server.sun_family = AF_UNIX;
  if ((homedir = getenv ("HOME")) == NULL)
    {
      fprintf (stderr,"No home directory\n");
      exit (1);
    }
  strcpy (server.sun_path, homedir);
  strcat (server.sun_path, "/.broadcast");
  unlink(server.sun_path);
  if (bind (s, &server, strlen (server.sun_path) + 2) < 0)
    {
      perror ("bind");
      exit (1);
    }
  /*
   * Now, just wait for everything to come in..
   */
  if (listen (s, 5) < 0)
    {
      perror ("listen");
      exit (1);
    }

  fprintf(stderr, "[Waiting for clients.]\n");

  /* Disable sigpipes in case luser kills client... */
  signal (SIGPIPE, SIG_IGN);

  /* Make stdin non-blocking */
  fcntl(stdin->_file, F_SETFL, FNDELAY);

  for (;;)
    {
      int rmask = (1 << s) + 1;

      /* wait for input from new client or stdin */
      if (select (s + 1, &rmask, 0, 0, 0) < 0)
	perror ("select");
      if (rmask & (1 << s))	/* client sends list of filenames */
	{
	  /* new client calling */
	  fromlen = sizeof (fromunix);
	  fromunix.sun_family = AF_UNIX;
	  infd = accept (s, &fromunix, &fromlen); /* open socket fd */
	  if (infd < 0)
	    {
	      if (errno == EMFILE || errno == ENFILE)
		fprintf (stderr, "Too many clients.\n");
	      else
		perror ("accept");
	      continue;
	    }

	  while (infd >= openfiles_size)
	    {
	      int i;

	      openfiles_size *= 2;
	      openfiles = (FILE **) realloc (openfiles,
					     openfiles_size * sizeof (FILE *));
	      if (openfiles == 0)
		abort ();
	      for (i = openfiles_size/2; i < openfiles_size; i++)
		openfiles[i] == NULL;
	    }

	  infile = fdopen (infd, "r+"); /* open stream */
	  if (infile == NULL)
	    {
	      fprintf (stderr, "Too many clients.\n");
	      write (infd, "Too many clients.\n", 18);
	      close (infd);		/* Prevent descriptor leak.. */
	      continue;
	    }
	  openfiles[infd] = infile;
	  fprintf (stderr, "[Client: %d]\n", infd);
	  fflush (stderr);
	  continue;
	}
      else if (rmask & 1)
	{
	  /* text from stdin -- broadcast it to all clients */
	  /* Read from stdin and copy to the streams until we get 'wouldblock'
	   * or EOF. */
	  while (1)
	    {
	      int c;
	      int i;
	      int retcode;
#define BUFSIZE 100
	      char buffer[BUFSIZE];

	      /* Read some input. */
	      errno = 0;
	      retcode = read(stdin->_file, buffer, BUFSIZE);

	      /* Handle errors and EOF. */
	      if (retcode == -1 || retcode == 0)
		{
		  if (errno == EWOULDBLOCK)
		    /* wouldblock -- go back to waiting */
		    break;
		  else
		    {
		      /* EOF on input -- exit */
		      fprintf(stderr, "[Exiting]\n");
		      exit(0);
		    }
		}

	      /* Write characters to all open streams */
	      for (i = 0; i < openfiles_size; i++)
		{
		  if (openfiles[i] != NULL)
		    write (openfiles[i]->_file, buffer, retcode);
		}
	      /* Copy to stdout */
	      write (stdout->_file, buffer, retcode);
	    }
	  continue;
	} 
    }
}
---------------------------------------- receiver.c
/* Client process that communicates with GNU Emacs acting as server.
   Copyright (C) 1986, 1987 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is distributed in the hope that it will be useful,
but without any warranty.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.

Everyone is granted permission to copy, modify and redistribute
GNU Emacs, but only under the conditions described in the
document "GNU Emacs copying permission notice".   An exact copy
of the document is supposed to have been given to you along with
GNU Emacs so that you can know how you may redistribute it all.
It should be in a file named COPYING.  Among other things, the
copyright notice and this notice must be preserved on all copies.  */

/* This code is BSD only. */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>

main (argc, argv)
     int argc;
     char **argv;
{
  int s, n, i;
  FILE *out;
  struct sockaddr_un server;
  char *homedir, *cwd, *str;
  char string[BUFSIZ];
  int c;

  char *getenv (), *getwd ();

  /* 
   * Open up an AF_UNIX socket in this person's home directory
   */

  if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
    {
      perror ("socket");
      exit (1);
    }
  server.sun_family = AF_UNIX;
  if ((homedir = getenv ("HOME")) == NULL)
    {
      fprintf (stderr, "No home directory\n");
      exit (1);
    }
  strcpy (server.sun_path, homedir);
  strcat (server.sun_path, "/.broadcast");
  if (connect (s, &server, strlen (server.sun_path) + 2) < 0)
    {
      perror ("connect");
      exit (1);
    }
  if ((out = fdopen (s, "r+")) == NULL)
    {
      perror ("fdopen");
      exit (1);
    }

  cwd = getwd (string);
  if (cwd == 0)
    abort ();

  fprintf(stderr, "[Connected.]\n");

  setbuf(stdout, NULL);

  while (1)
    {
      int retcode;
#define BUFSIZE 100
      char buffer[BUFSIZE];

      /* Read some input. */
      retcode = read(s, buffer, BUFSIZE);

      /* Handle errors and EOF. */
      if (retcode == -1 || retcode == 0)
	break;

      /* Copy to stdout */
      write (stdout->_file, buffer, retcode);
    }
  printf("\n[Disconnected]\n");
}