[net.sources] totty: execute process on specified tty

sewilco@mecc.UUCP (Scot E. Wilcoxon) (03/04/86)

This program executes a command on a specified tty port.  I'd like to
get it to work on a port which has a getty inactive on it, but haven't
yet figured out how to do that easily.  In the meantime it is being
extremely useful for running reports at night (SMC seems to think that
human intervention should be needed for all business reports).

I do prefer mod.sources, but I'll start this program here since it is
both small and of limited interest.  Suggestions for improvements are
welcome.

Scot E. Wilcoxon  Minn. Ed. Comp. Corp.            quest!mecc!sewilco
45 03 N / 93 08 W   (612)481-3507  {ihnp4,mgnetp}!dicome!mecc!sewilco

-------------------------nibble here --------------------------------
#! /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:
#	totty.c
# This archive created: Mon Mar  3 16:37:36 1986
export PATH; PATH=/bin:$PATH
if test -f 'totty.c'
then
	echo shar: will not over-write existing file "'totty.c'"
else
cat << \SHAR_EOF > 'totty.c'
/*	totty.c - assign a process to an unused tty
**	Author:	Scot E. Wilcoxon, MECC (ihnp4!mecc!sewilco)	86/01/15
**
**	Format of Call:
**
**	totty device-pathname command [args]
**
**		device-pathname - pathname of device to assign to
**		command - command to execute
**		[args] - optional arguments to command
**
*/

/*	This program causes a command to be executed under control
 *	of a specified device (usually a tty port).
 *
 *	Applications:
 *
 *		Any program which requires use of /dev/tty.
 *		To allow crontab entries for SMC BASIC, which requires /dev/tty
 *			(and doesn't support standard in/out..sigh).
 *		A program to shutdown when temperature or power (UPS) problem
 *		occurs uses the Lisa mouse button, which requires /dev/tty.
 *
 *	How it works:
 *
 *	1. Close standard in/out/error so as to not inherit them.
 *	2. Make an orphan process.
 *	3. Declare the orphan to be leader of the process group.
 *	4. Assign the desired device as standard in/out/error.
 *	5. Execute the desired command as an orphan which does not mind
 *	   the parent disappearing.
 *	
 *	Step 1 avoids the orphan from being considered part of the original
 *		process group.
 *	Step 2 isolates the new process from the previous process grouping.
 *	Steps 3 and 4 are what define the tty group owner by meeting the
 *		kernel's requirements when the orphan opens standard input.
 *	Step 5 tries to keep things clean by having an orphan do the real work.
 *
 *	System:
 *		XENIX 3.0 (Priam) on Apple Lisa
 *
 *	Bugs:
 *	
 *	Only works when called by a process which is not a member of a
 *	tty group.  These are identified by "?" in the TTY column of
 *	"ps -ef".  Known to work in /etc/rc and crontab.
 *
 *	Does not work when the device has processes assigned to it,
 *	specifically getty.  Known to work on a tty port with getty
 *	disabled.
 *
 *	The device name must be a pathname.  Should allow (or assume)
 *	implied "/dev/" prefix.
 *
 *	No formal man page.  Author does not have XENIX Text Development
 *	System, thus no nroff/troff.
 */

#define	DAEMON	/* if DAEMON, don't give up easily */

#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>

extern	errno;

#ifndef	void
typedef	int	void;	/* XENIX 3.0 only has a system void */
#endif

main(argc, argv)
int	argc;
char	**argv;
{
	int	forkvalue;	/* value of fork */

	if ( argc < 3 ) {
		printf("usage: %s tty_device command [args]\n",argv[0]);
		exit(1);
	}

	if ( fclose(stdin) )
		perror("totty: stdin fclose");
	if ( fclose(stdout) )
		perror("totty: stdout fclose");
	if ( fclose(stderr) )
		perror("totty: stderr fclose");

	if ( close(0) )
		perror("totty: STDIN close");
	if ( close(1) )
		perror("totty: STDOUT close");
	if ( close(2) )
		perror("totty: STDERR close");

#ifdef	DAEMON
	while ( (forkvalue=fork()) == -1 ) sleep(60);	/* keep trying until forked */
#else
	if ( (forkvalue=fork()) == -1 ) exit(1);	/* die if can't fork */
#endif

	if ( forkvalue != 0 ) exit(0);	/* if parent, die. */

	(void)setpgrp();	/* become process group leader */

	if ( open(argv[1],O_RDONLY|O_NDELAY) < 0 )
		perror("totty: stdin open");
	if ( open(argv[1],O_WRONLY) < 0 )
		perror("totty: stdout open");
	if ( open(argv[1],O_WRONLY) < 0 )
		perror("totty: stderr open");

	if ( fopen(argv[1],"r") == NULL )
		perror("totty: stdin open");
	if ( fopen(argv[1],"w") == NULL )
		perror("totty: stdout open");
	if ( fopen(argv[1],"w") == NULL )
		perror("totty: stderr open");

	(void)signal(SIGHUP,SIG_IGN);	/* have child ignore death of
						tty group leader */

#ifdef	DAEMON
	while ( (forkvalue=fork()) == -1 ) sleep(60);	/* keep trying until forked */
#else
	if ( (forkvalue=fork()) == -1 ) exit(1);	/* die if can't fork */
#endif

	if ( forkvalue != 0 ) exit(0);	/* if parent, die. */

	(void)execvp( argv[2], &argv[2] ); /* execute desired command */
	exit(1);
}

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

Scot E. Wilcoxon  Minn. Ed. Comp. Corp.            quest!mecc!sewilco
45 03 N / 93 15 W   (612)481-3507  {ihnp4,mgnetp}!dicome!mecc!sewilco