[comp.unix.xenix] Tapping a tty for data

ivar@acc.uu.no (Ivar Hosteng) (07/16/89)

I have written a small utility that makes it possible to log the activity
of a tty. This program is useful for dial in lines if you want to know
what the user is doing. I hope somone have use for it.

READ the notes in tapserial.c file!



#! /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 the files:
#	tapserial.c
#	getpterminal.c
#	bstring.h
#	kernel.patch
#	makefile
# This archive created: Sat Jul 15 20:18:44 1989
export PATH; PATH=/bin:$PATH
if test -f 'tapserial.c'
then
	echo shar: will not over-write existing file "'tapserial.c'"
else
cat << \SHAR_EOF > 'tapserial.c'
/*
 **                          tapserial.c
 ** Written by Ivar E. Hosteng, Oslo, Norway
 ** 
 ** This program is public domain, you cannot sell this program for profit.
 ** if you do, I'l send you virus.com to do some terrible things to
 ** you system.
 **
 ** If you make kodifications to this program please send me a note so that
 ** i can update my version. I'l soon make a versin tats compatible with 
 ** the new getty to allow it to be used on dial in/out lines also. 
 ** 
 ** To use this program you must have ptys and have at least one enabled
 ** for each tapserial task. You must also have a working select call.
 ** Xenix 2.3.2 have a bug in the select(S) call that makes it unusable
 ** for this program. To make it usable you must do some patching in the
 ** libsys.a file in /usr/sys/sys. Se the file kernel.patch for how to
 ** do this
 **
 ** My E-mail address is
 ** Ivar E. Hosteng,	Advanced Computer Consultans, Oslo, Norway
 ** Internet:		ivar@acc.uu.no
 ** UUCP:			...!{uunet,mcvax,ifi}!acc.uu.no!ivar
 **/

#include <stdio.h>
#include <termio.h>
#include "bstring.h"
#include <sys/types.h>
#include <sys/select.h>
#include <signal.h>
#include <fcntl.h>

#define LOGDIR "/xtra/log"

#define wr_out(fd,string) write((fd),(string),strlen((string)))
main(argc,argv)
     int argc;
     char **argv;
     
{
  int master, slave;
  char *filename;
  char device[20];
  char *getpterminal(int *, int *, int);
  char tempfilename[80];
  
  int i,fd,slutt,count,logfile;
  struct termio serial;
  
  
  if (argc!=2) 
    {
      fprintf(stderr,"Usage %s tty to log\n",argv[0]);
      exit(1);
    }

  sprintf(device,"/dev/%s",argv[1]);
  printf("Tapserial tapping %s to %s%s#[1-3].log\n",argv[1],LOGDIR,argv[1]);
  for (i=1;i<NSIG;i++) signal(i,SIG_IGN);
  count=1;
  
  while (1)
    {
      setpgrp();
      close(0);
      close(1);
      close(2);
      
      if ((fd=open(device,O_RDWR))==-1) continue;
      (void) ioctl(fd,TCGETA,&serial);
      serial.c_cflag = (CLOCAL | B9600 | CREAD | CS8 | HUPCL);
      serial.c_iflag = 0; 
      serial.c_lflag = 0; 
      serial.c_oflag = 0;
      serial.c_cc[VMIN]=1;
      serial.c_cc[VTIME]=0;
      (void) ioctl(fd,TCSETA,&serial);
      if ((filename=getpterminal(&master,&slave,1))==NULL)
	{
	  wr_out(fd,"\nsorry no pty's availvable, Try again laiter!\n");
	  close(fd);
	  continue;
	}
      slutt=1;
      sprintf(tempfilename,"%s%s#%d.log",LOGDIR,argv[1],count++);
      unlink(tempfilename);
      if (count>3) count=1;
      if((logfile=open(tempfilename,( O_CREAT|O_WRONLY|O_TRUNC ), 0400))==-1)
	{
	  wr_out(fd,"Sorry logg full. Try again laiter\n");
	  close(fd);
	  continue;
	}
      while (slutt)
	{
	  int buf[BUFSIZ],n;
	  fd_set rd,wr,ex;
	  
	  FD_ZERO(&rd);
	  FD_ZERO(&wr);
	  FD_ZERO(&ex);
	  FD_SET(fd,&rd);
	  FD_SET(master,&rd);
	  (void) select(master+10,&rd,&wr,&ex,0);
	  if (FD_ISSET(fd,&rd))
	    {
	      n=read(fd,buf,BUFSIZ);
	      write(master,buf,n);
	    }
	  if (FD_ISSET(master,&rd))
	    {
	      n=read(master,buf,BUFSIZ);
	      if (n==0) 
		{
		  slutt=0;
		  continue;
		}
	      write(logfile,buf,n);
	      write(fd,buf,n);
	    }
	}
      close(master);
      close(fd);
      close(logfile);
      sleep(3);
    }
}

SHAR_EOF
fi # end of overwriting check
if test -f 'getpterminal.c'
then
	echo shar: will not over-write existing file "'getpterminal.c'"
else
cat << \SHAR_EOF > 'getpterminal.c'
/*
**	getpterminal(master_fh,slave_fh,login) opens and returns a file handle
**	to a psuedo terminal in the two file descriptors. If the search for a 
**	psuedo terminal fails, getpterminal returns NULL and set file handle 
**	to NULL. If getpterminal sucseeds it returns the filename for the 
**      slave pty. If login is non-zero the pty must be enabled for logins,
**      and getpterminal returns the filename for the MASTER pty!
*/

#include <stdio.h>
#include <fcntl.h>

char *getpterminal(master_fh, slave_fh,login)
int *master_fh, *slave_fh;
{
  char master_pty_filename[sizeof("/dev/ptypXXX")];
  static char slave_pty_filename[sizeof("/dev/ptypXXX")];
  int n,i,found_pty;
  char *fname;
  
  *master_fh= (int) NULL;
  *slave_fh= (int) NULL;
  fname=(char *) NULL;
  
  found_pty=0;
  
  for (i=0; i < 16 && !found_pty; ++i) 
    {
      (void) sprintf(master_pty_filename, "/dev/ptyp%d", i);
      if ((n=open(master_pty_filename, O_RDWR)) != -1)
	{
	  (void) sprintf(slave_pty_filename, "/dev/ttyp%d",i);
	  *master_fh=n;
	  if ((n=open(slave_pty_filename, O_RDWR)) == -1) 
	    {
	      if (!login) 
		{
		  close(*master_fh);
		  *master_fh= (int) NULL;
		}
	      else 
		{
		  found_pty=1;
		  *slave_fh=-1;
		}
	    }
	  else 
	    {
	      if (login)
		{
		  found_pty=0;
		  close(*master_fh);
		}
	      else
		{
		  *slave_fh=n;
		  found_pty=1;
		}
	    }
	}
    }
  if (found_pty==1)
    {
      fname=(char *) malloc(sizeof("/dev/ptypxxx"));
      if (login) strcpy(fname,master_pty_filename);
        else strcpy(fname,slave_pty_filename);
      return(fname);
    }
  else
    return( (char *) NULL);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'bstring.h'
then
	echo shar: will not over-write existing file "'bstring.h'"
else
cat << \SHAR_EOF > 'bstring.h'
#define bcopy(a,b,s)	memcpy(b,a,s)
#define bzero(a,s)	memset(a,0,s)
#define bcmp		memcmp
SHAR_EOF
fi # end of overwriting check
if test -f 'kernel.patch'
then
	echo shar: will not over-write existing file "'kernel.patch'"
else
cat << \SHAR_EOF > 'kernel.patch'
From: csch@netcs.UUCP (Clemens Schrimpe)
Newsgroups: comp.unix.xenix
Subject: Re: Select(S) in Xenix 386 2.3.2
Keywords: select, serial, pipes
Message-ID: <172@prmmbx.UUCP>
Date: 23 Jun 89 23:35:30 GMT
References: <117@accsys.acc.uu.no>
Reply-To: csch@netcs.UUCP (Clemens Schrimpe)
Organization: netCS Kommunikationstechnik GmbH, Berlin
Lines: 63
Posted: Sat Jun 24 00:35:30 1989

ivar@acc.uu.no (Ivar Hosteng) writes:
<> I have experienced some problems using the select call in Xenix 386 V2.3.2.
<> It does not seems to detect when a pipe gets ready to been read from.
This is, because there is no provision to select on pipes!
Why? The stuff is almost totally ported 1:1 from the Berkeley code and
in BSD pipes should consist of AF_UNIX sockets, on which you can naturally
select.
I was very angry, when I found this out after hours of digging with adb in
the kernel. But I also tried the same on a SUN under SunOS 4.0 and it doesn't
work either ... seems to be a common illness ???
(I wonder, because the code for that is very simple ... ??? ...)

<> I also 
<> have trouble using select on a serial port.  When I do that the input
<> turns into garbage.  This does not occur when I use select on the
<> multiscreen ttys (tty01-tty12).
Hehe - we had just the same!
Here is the solution (thanks to my colleague Stefan Koehler, who took one
look at my screen, into which I had starred for hours, to find it ...)

Select is implemented by an undocumented ioctl
	(0xFFFF == IOC_SELECT -> [sys/slect.h])
which is handled by ttiocom() for all devices using the standard
SYS-V linediscipline!

The ioctl-routine for the serial devices [sioioctl()] just calls 
ttiocom() [after some undefinable VPIX stuff ???] and
if it returns NON-ZERO it calls sioparam(), which adjusts certain
parameters and garbles the output!
OK so far. Now: The Bug lies in the ttiocom-code within the check
for IOC_SELECT. After detecting the IOC_SELECT, the ttiocom calls
the select-code and returns NOTHING, which means that if EAX is
non-zero (randomly) sioparam() is called and garbles the output.

The Fix: (quick and dirty)
Write a routine called "ttiocom", which might look like this:

ttiocom(ttyp, com, arg, flag)
struct tty *ttyp;
int com, arg, flag;	/* there should be better types for this :-) */
{
	if (com == IOC_SELECT)
	{
		ttselect(ttyp, flag);
		return(0);	/*** THIS IS IMPORTANT ***/
	}
	return(Ttiocom(ttyp, com ,arg, flag));
}

Compile something like this, then use whatever you have (GNU-Emacs is
great in patching strings in binaries) to patch /usr/sys/sys/libsys.a
to change the original ttiocom into Ttiocom !
Link in your code and -by some magic reason- experience a full blown
select on your System V / Xenix machine!!!

Have fun playing around with it -

	Clemens Schrimpe, netCS Informationstechnik GmbH Berlin
--
UUCP:		csch@netcs		BITNET:	csch@db0tui6.BITNET
ARPA/NSF:	csch@garp.mit.edu	PSI: PSI%45300033047::CSCH
PHONE:		+49-30-24 42 37		FAX: +49-30-24 38 00
BTX:		0303325016-0003		TELEX: 186672 net d

SHAR_EOF
fi # end of overwriting check
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
cat << \SHAR_EOF > 'makefile'
tapserial:	tapserial.o getpterminal.o
	cc -o tapserial tapserial.o getpterminal.o

tapserial.o:	tapserial.c
	cc -c tapserial.c

getpterminal.o:	getpterminal.c
	cc -c getpterminal.c

SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

-- 
Ivar E. Hosteng,	Advanced Computer Consultans, Oslo, Norway
Internet:		ivar@acc.uu.no
UUCP:			...!{uunet,mcvax,ifi}!acc.uu.no!ivar
'Just what do you think you are doing Dave?'	-HAL9000