[comp.sources.misc] program to force commands on another terminal

allbery@ncoast.UUCP (05/29/87)

This program lets a super-user enter commands or data on a terminal other
than his own.  Handy for logging people off -- just
	force ttyB4 logout
instead of 'ps auxt' and 'kill -HUP'.  Also more fun :-).  It is known to
run on 4.3 BSD; it will definitely run on 4.2 BSD; it may run on V7 or SIII
or even S5 -- basically, if you have TIOCSTI, you can use this.
I found this program in our public sources directory; it has no authorship
in the comments, which means it probably belongs to ATT :-), but it's simple
and short enough that it looks like it was done locally -- just way, way
before my time.

It was called 'type.c', but because it matches the function of a program in
my old Alpha-Micro days called "force", I call it that here.  The usage
message thinks it's called "type", so you can call it that, too, if you want.
Actually I don't care what you call it.

----------------------------cut here for force.c---------------------------
#include <sgtty.h>
#include <sys/file.h>
#include <stdio.h>
#include <sys/param.h>
#include <strings.h>

/*
 * type - make the system think that a command was entered on
 * another user's terminal.
 *
 * type [-n] <terminal> <command>
 *
 *	<terminal> is the line that the command should be entered on.
 *		examples: "/dev/ttyj4" ; "ttyj4" ; "j4"
 *
 *	<command> is the command that should be entered on that terminal.
 *		examples: "logout" ; "jobs > /dev/console"
 *
 * a newline will be appended to <command> unless type is given a "-n"
 * option.
 *
 * type can only be used on the terminal you are on, unless you are the
 * superuser, in which case you can send commands to any terminal.
 */

static int fd;		/* file descriptor for terminal line */
extern int wakeup();	/* routine to wakeup if open of terminal line hangs */

main(argc, argv, environ)
int argc;
char *argv[], *environ[];
{
	int flag_n;		/* do not append newline to <command> */

	if ( argc <= 1 ) {
		usage();
	}

	if ( argv[1][0] == '-' ) {
		switch ( argv[1][1] ) {
			case 'n' :
				flag_n++;
				break;
			case '-' :
				break;
			default:
				usage();
				break;
		}
		argc--, argv++;
	}
		
	if ( argc < 3 ) {
		usage();
	}

	if ( index( argv[1], '/' ) == NULL ) {
		char terminal[MAXPATHLEN+1];

		/*
		 * since a full pathname was not given, assume the
		 * file is in the "/dev" directory, and if only 2
		 * characters were given, assume that means a "tty".
		 */
		strcpy( terminal, "/dev/" );
		if (strlen(argv[1]) == 2)
			strcat( terminal, "tty" );
		argv[1] = strcat( terminal, argv[1] );
	}

	/*
	 * O_NDELAY does not work (?), so set a timer to
	 * wake up if the open hangs.
	 */
	
	signal( SIGALRM, wakeup );
	alarm( 30 );

	if ( (fd = open(argv[1], O_WRONLY|O_NDELAY, 0)) < 0 ) {
		perror( "open" );
		exit( 2 );
	} else {
		char *cp = argv[2];

		alarm( 0 );

		while ( *cp != '\0' ) {
			send( *cp );
			cp++;
		}

		if ( ! flag_n ) send( '\n' );
	}
}

usage()
{
	fprintf( stderr, "usage: type [-n] <terminal> <command>\n" );
	exit( 1 );
}

send( c )
char c;
{
	register int status;

	status = ioctl( fd, TIOCSTI, &c );

	if ( status != 0 ) {
		perror( "ioctl" );
		exit( 3 );
	}

	return status;
}

wakeup()
{
	fprintf( stderr, "open hung\n" );
	exit( 4 );
}
-----------------------------------cut here--------------------------------
-- 
Brandon S. Allbery	{decvax,cbatt,cbosgd}!cwruecmp!ncoast!allbery
Tridelta Industries	{ames,mit-eddie,talcott}!necntc!ncoast!allbery
7350 Corporate Blvd.	necntc!ncoast!allbery@harvard.HARVARD.EDU
Mentor, OH 44060	+01 216 255 1080	(also eddie.MIT.EDU)

allbery@ncoast.UUCP (06/02/87)

From: jjg@linus.UUCP (Jeff Glass)

In article <8705291811.AA23508@ubvax.ub.com> allbery@ncoast.UUCP (Brandon S. Allbery) writes:
[no, he doesn't; but I've commented on this before.  ++bsa]
> This program lets a super-user enter commands or data on a terminal other
> than his own.  Handy for logging people off -- just
> 	force ttyB4 logout
> instead of 'ps auxt' and 'kill -HUP'.  Also more fun :-).  It is known to
> run on 4.3 BSD; it will definitely run on 4.2 BSD; it may run on V7 or SIII
> or even S5 -- basically, if you have TIOCSTI, you can use this.
> I found this program in our public sources directory; it has no authorship
> in the comments, which means it probably belongs to ATT :-), but it's simple
> and short enough that it looks like it was done locally -- just way, way
> before my time.

I wrote this program a while ago.  Here's the latest version, which allows
you to use '\' (e.g., '\037' and '\n') and '^' (e.g., '^S') escape sequences:

---------- cut here ---------
#!/bin/sh
sed 's/^X//' << 'END' > type.c
X#include <sgtty.h>
X#include <sys/file.h>
X#include <stdio.h>
X#include <sys/param.h>
X#include <strings.h>
X#include <ctype.h>
X
X/*
X * Jeff Glass, The MITRE Corporation, 5/14/87
X *
X * type - make the system think that a command was entered on
X * another user's terminal.
X *
X * type [-n] <terminal> <command> ...
X *
X *	<terminal> is the line that the command should be entered on.
X *		examples: "/dev/ttyj4" ; "ttyj4" ; "j4"
X *
X *	<command> is the command that should be entered on that terminal.
X *		examples: "logout" ; "jobs > /dev/console"
X *
X * a newline will be appended to <command> unless type is given a "-n"
X * option.  a space will be appended after each command except the last.
X * command can have '\nnn' and '\[tbnr]' and '^[SQC]' escapes in it.
X *
X * type can only be used on the terminal you are on, unless you are the
X * superuser, in which case you can send commands to any terminal.
X */
X
Xstatic int fd;		/* file descriptor for terminal line */
Xstatic char *name;	/* argv[0] */
Xextern int wakeup();	/* routine to wakeup if open of terminal line hangs */
X
Xmain(argc, argv, environ)
Xint argc;
Xchar *argv[], *environ[];
X{
X	int flag_n;		/* do not append newline to <command> */
X	int opt;
X	extern int optind;
X
X	name = argv[0];
X	while ( (opt = getopt(argc,argv,"n"))  != EOF ) {
X		switch ( opt ) {
X			case 'n' :
X				flag_n++;
X				break;
X			default:
X				usage();
X				break;
X		}
X	}
X	if ( (optind+2) > argc ) {
X		usage();
X	}
X
X	if ( index( argv[optind], '/' ) == NULL ) {
X		char terminal[MAXPATHLEN+1];
X
X		/*
X		 * since a full pathname was not given, assume the
X		 * file is in the "/dev" directory, and if only 2
X		 * characters were given, assume that means a "tty".
X		 */
X		strcpy( terminal, "/dev/" );
X		if (strlen(argv[optind]) == 2)
X			strcat( terminal, "tty" );
X		argv[optind] = strcat( terminal, argv[optind] );
X	}
X
X	/*
X	 * O_NDELAY does not work (?), so set a timer to
X	 * wake up if the open hangs.
X	 */
X	
X	signal( SIGALRM, wakeup );
X	alarm( 30 );
X
X	if ( (fd = open(argv[optind], O_RDONLY|O_NDELAY, 0)) < 0 ) {
X		perror( "open" );
X		exit( 2 );
X	} else {
X		int i;
X
X		alarm( 0 );
X
X		for (i = optind+1; i < argc; i++) {
X			if (sendstr(argv[i]) != 0)
X				exit(3);
X			if (i != (argc-1))
X				if (send(' ') != 0)
X					exit(3);
X		}
X
X		if ( ! flag_n )
X			if (send('\n'))
X				exit(3);
X	}
X}
X
Xusage()
X{
X	fprintf( stderr, "%s: usage: %s [-n] <terminal> <command> ...\n",
X		 name,
X		 name );
X	exit( 1 );
X}
X
Xsend( c )
Xchar c;
X{
X	register int status;
X
X	if ( (status = ioctl(fd,TIOCSTI,&c)) != 0 )
X		perror( "ioctl" );
X
X	return status;
X}
X
X/*
X * send a string to the terminal, but do some translation (such as '\012'
X * or '\n' for newline, or '^S' for CTRL-S).
X */
Xsendstr(cp)
Xchar *cp;
X{
X	short	ch, in_bslash, in_carat;
X
X	ch = in_bslash = in_carat = 0;
X
X	/*
X	 * this is a FSM (don't laugh!), where the states are :
X	 *	in_bslash == 1
X	 *	in_bslash > 1
X	 *	in_carat == 1
X	 *	in_bslash == 0 && in_carat == 0
X	 *
X	 * whenever the FSM is in the state (in_bslash == 0 && in_carat == 0),
X	 * it prints out the character held in ch.
X	 */
X	while (*cp != '\0' ) {
X		if (in_bslash) {
X			if (isascii(*cp) && isdigit(*cp)) {
X				ch = ch * 8 + *cp - '0';
X				if (in_bslash < 3)
X					in_bslash++;
X				else
X					in_bslash = 0;
X			} else if (in_bslash == 1) {
X				switch (*cp) {
X				case 'b' : ch = '\b'; break;
X				case 'f' : ch = '\f'; break;
X				case 'n' : ch = '\n'; break;
X				case 'r' : ch = '\r'; break;
X				case 't' : ch = '\t'; break;
X				default  : ch = *cp ; break;
X				}
X				in_bslash = 0;
X			} else {
X				/*
X				 * this character terminated a '\nn',
X				 * so back up cp so this character is
X				 * seen again.
X				 */
X				cp--;
X				in_bslash = 0;
X			}
X		} else if (in_carat) {
X			if (*cp == '?')
X				ch = '\177';
X			else
X				ch = *cp & 037;
X			in_carat = 0;
X		} else {
X			switch (*cp) {
X			case '\\' :
X				in_bslash = 1;
X				ch = 0;
X				break;
X			case '^' :
X				in_carat = 1;
X				break;
X			default :
X				ch = *cp;
X				break;
X			}
X		} /*else*/
X
X		if (!(in_bslash || in_carat))
X			if (send(ch) != 0)
X				return 1;
X		cp++;
X				
X	} /*while*/
X
X	/*
X	 * take care of anything left at the end of the string.
X	 */
X	if (in_bslash) {
X		if (send(ch) != 0)
X			return 1;
X	} else if (in_carat) {
X		if (send('^') != 0)
X			return 1;
X	}
X	
X	return 0;
X}
X
Xwakeup()
X{
X	fprintf( stderr, "open hung\n" );
X	exit( 4 );
X}
END