[mod.computers.masscomp] turn your getty on and off with this!

Stan@soma.UUCP (08/08/86)

Following is a shar archive of some code I promised to write at the
April MUS meeting to allow users to turn on and off getty without
being the superuser and without editing the inittab file directly.
I have been running it here at some for about 3 weeks. It is not
as pretty as I would like, but it does seem to work. 
Please mail bugs to me for further enhancements.

Good Luck!
Stan Barber
-------------------------------------------------------------------
#! /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:
#	READ_ME
#	Makefile
#	acucntrl.c
# This archive created: Fri Aug  8 00:26:36 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'READ_ME'" '(2170 characters)'
if test -f 'READ_ME'
then
	echo shar: will not over-write existing file "'READ_ME'"
else
cat << \SHAR_EOF > 'READ_ME'
READ_ME for acucntrl for Masscomps
8/8/86
Stan Barber

Acucntrl allows you to toggle getty on or off on a specified terminal
line. This is very useful if you only have one modem which you want
to use for both incoming and outgoing traffic.

Basically, set up your inittab as you usually would to have getty running
on all the line you'd like it to run on, including the modem line.

Then compile and install acucntrl. Do this in this manner:
become root with su or login as root and move to the directory that
contains acucntrl.c
type:
ucb cc acucntrl.c -o acucntrl
cp acucntrl /usr/lib/uucp
chown root /usr/lib/uucp/acucntrl
chmod 4711 /usr/lib/uucp/acucntrl


Now, when you want to toggle the getty off, enter
/usr/lib/uucp/acucntrl disable ttyX 
where X is the line you want off like tty2 to disable the "remote" port
on a Masscomp 5500 system. After a few seconds, you run a ps and 
you will (hopefully) notice that there is no getty on the line. You can
then run cu or kermit on the line and dial out through the modem if you
like.

To turn the getty back on, enter
/usr/lib/uucp/acucntrl enable ttyX
where X is the same as described above. When you run the ps this time,
you should see a line that shows "/etc/getty ttyX 1200" or something like
that.

Use with uucp is a little more complex and is left as an exercise for 
the user. Basically, you need to rename uucico to something else (say,
uucicox) and build a shell script that will be called uucico to
invoke acucntrl and then call uucico with the correct arguements.

If you build one of these that you'd like to share, send it to me for
mod.computers.masscomp (soon to be called comp.hardw.masscomp, or
some such).

IMPORTANT NOTES:

In /etc/inittab, form of the line that starts the getty must look like this:
00:2:respawn:/etc/getty tty2 1200
that is, the name of getty MUST be /etc/getty and there must be no
more than one space between "/etc/getty" and the name of the terminal line
"tty2" in this case.

This software is provided without warrenty of any kind. Stan Barber
and Columbia University cannot be held liable for any use or abuse that
may be made of this software or the results of that use.
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(256 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
DEST=/usr/lib/uucp

acucntrl:acucntrl.c
	ucb cc acucntrl.c  -o acucntrl

install: acucntrl
	@echo "you must be the superuser to install acucntrl"
	cp acucntrl $(DEST)
	chown root $(DEST)/acucntrl
	chmod 4711 $(DEST)/acucntrl
	@echo "installation complete"
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'acucntrl.c'" '(13228 characters)'
if test -f 'acucntrl.c'
then
	echo shar: will not over-write existing file "'acucntrl.c'"
else
cat << \SHAR_EOF > 'acucntrl.c'
#ifndef lint
static char sccsid[] = "@(#)$Header: acucntrl.c,v 1.6 86/08/08 00:10:03 sob Exp $";
#endif

/*  acucntrl - turn around tty line between dialin and dialout
 * 
 * Usage:	acucntrl {enable,disable} /dev/ttydX
 *
 *      $Log:	acucntrl.c,v $
 * Revision 1.6  86/08/08  00:10:03  sob
 * Add copyright for release to real world.
 * Stan
 * 
 * Revision 1.5  86/08/07  23:55:33  sob
 * Added better lock checking to catch deadlocks
 * Stan
 * 
 * Revision 1.4  86/07/23  17:24:05  sob
 * Added more checking to be sure that line is not in use.
 * Stan
 * 
 * Revision 1.3  86/07/21  18:38:51  sob
 * More streamlining
 * 
 * Revision 1.2  86/07/21  18:12:08  sob
 * streamlined a bit
 * 
 * Revision 1.1  86/07/21  17:15:13  sob
 * Initial revision
 * 
 */
/* this code inspired by acucntrl found in 4.3 BSD uucp */
/* This code is Copyright 1986 Stan Barber.
 * Permission is granted to any individual or institution to use, copy, or
 * redistribute this software so long as it is not sold for profit, 
 *provided this copyright notice is retained. 
 *This software is provided without warrenty of any kind. Stan Barber
 *and Columbia University cannot be held liable for any use or abuse that
 *may be made of this software or the results of that use.
 *This sentence ends the copyright notice.
 */
/* this code created at Baylor College of Medicine, Houston, Texas
 * in the Cellular Neurophysiology Laboratory, Neurology Department
 */
/* compile this code in the ucb universe -- eg.
 * ucb cc acucntrl.c -o acucntrl
 */
/* this code should be adaptable to generic system V, but I have not
 * tried it yet - sob
 */
#include <sys/types.h>
#include <sgtty.h>
#include <utmp.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/file.h>
#include <signal.h>
#include <sys/stat.h>
#include <pwd.h>
#include <errno.h>

#define ENABLE	1
#define DISABLE	0

char Etcutmp[] = "/etc/utmp";
char Etcinit[] = "/etc/inittab";
#ifdef DEBUG
char Etcnewinit[] = "/tmp/inittab.new";
#else
char Etcnewinit[] = "/etc/inittab.new";
#endif
FILE *inittab, *ninittab;

extern long ftell();

char Devhome[] = "/dev";

char usage[] = "Usage: acucntrl {dis|en}able ttyX\n";
int hasLock;
char flfnam[100];
struct utmp *utmp;
char resettty;
off_t ttyslnbeg;


main(argc, argv)
int argc; char *argv[];
{
	register char *p;
	register int i;
	int enable ;
	char *device;
	int devfile;
	int uid, gid;
	struct stat statb;
	struct utmp *getutent();
	char *rindex();
	extern int errno;
	extern char *sys_errlist[];

	/* check input arguments */
	if (argc!=3) {
		fprintf(stderr, usage);
		exit(1);
	}

	/* interpret command type */
	if (prefix(argv[1], "disable")  || strcmp(argv[1], "dialout")==0)
		enable = 0;
	else if (prefix(argv[1], "enable")  || strcmp(argv[1], "dialin")==0)
		enable = 1;
	else {
		fprintf(stderr, usage);
		exit(1);
	}

	device = rindex(argv[2], '/');
	device = (device == NULL) ? argv[2]: device+1;

	/* only recognize devices of the form ttyX */
	if (strncmp(device, "tty", 3)!=0) {
		fprintf(stderr, "Bad Device Name %s", device);
		exit(1);
	}

	for (i=0;i<100;i++){
		if(setlock() == 0 ) break;
		sleep(1);
	}

	if (hasLock == 0 ){
			fprintf(stderr,"Cannot create aculock file\n");
			acuexit(1);
	}

	opninittab(device);

	/* Chdir to /dev */
	if(chdir(Devhome) < 0) {
		fprintf(stderr, "Cannot chdir to %s: %s\r\n",
			Devhome, sys_errlist[errno]);
		acuexit(1);
	}

	/* Get uid information */
	uid = getuid();
	gid = getgid();

	/* get device status information */
	if(stat(device, &statb) < 0) {
		fprintf(stderr, "%s stat: %s\n", device, sys_errlist[errno]);
		acuexit(2);
	}

	if((statb.st_mode&S_IFMT) != S_IFCHR) {
		fprintf(stderr, "%s is not a character device.\n",device);
		acuexit(2);
	}
	
	if((statb.st_uid != 0 && statb.st_uid != uid)){
		fprintf(stderr,"%s is in use by %s\n",device,
		(getpwuid(statb.st_uid))->pw_name);
		acuexit(2);
	}



	/* check to see if line is being used */
	setutent();
	while ((utmp = getutent()) != NULL){
		if (utmp->ut_name[0] != '\0'
			 && !strcmp(utmp->ut_line,device)) {
			fprintf(stderr, "%s in use by %s\n",
						 device, utmp->ut_name);
 			acuexit(2);
		 }
	}
	endutent();
	if (enable) {
		if((devfile = open(device, O_WRONLY|O_NDELAY)) < 0) {
			fprintf(stderr, "On open of %s: %s\n",
				device, sys_errlist[errno]);
			acuexit(1);
		}
/* these will have to be rewritten if this program is used with
 * generic SYSV -- sob
 */
#ifndef DEBUG
		/* Try one last time to hang up */
		if (ioctl(devfile, (int)TIOCCDTR, (char *)0) < 0)
			fprintf(stderr, "On TIOCCDTR ioctl: %s\n",
				sys_errlist[errno]);

		if (ioctl(devfile, (int)TIOCNXCL, (char *)0) < 0)
			fprintf(stderr,
			    "Cannot clear Exclusive Use on %s: %s\n",
				device, sys_errlist[errno]);

		if (ioctl(devfile, (int)TIOCHPCL, (char *)0) < 0)
			fprintf(stderr,
			    "Cannot set hangup on close on %s: %s\n",
				device, sys_errlist[errno]);

#endif
		/* set uid to root */

		setuid(0);
		if (setinit(ENABLE)) {
			fprintf(stderr, "%s already enabled\n", device);
		} else {
			pokeinit(device,  enable);
		}

	} else {
#if defined(TIOCMGET) && defined(SENSECARRIER)
		if (uid!=0) {
			int linestat = 0;

			/* check for presence of carrier */
			sleep(2); /* need time after modem control turnoff */

			if((devfile = open(device, 1)) < 0) {
				fprintf(stderr, "On open of %s: %s\n",
					device, sys_errlist[errno]);
				acuexit(1);
			}

			(void)ioctl(devfile, TIOCMGET, &linestat);

			if (linestat&TIOCM_CAR) {
				fprintf(stderr, "%s is in use (Carrier On)\n",
					device);
				acuexit(2);
			}
			(void)close(devfile);
		}
#endif TIOCMGET
		/* chown device */
		if(chown(device, uid, gid) < 0)
			fprintf(stderr, "Cannot chown %s: %s\n",
				device, sys_errlist[errno]);


		/* chmod device */
		if(chmod(device,0660) < 0)
			fprintf(stderr, "Cannot chmod %s: %s\n",
				device, sys_errlist[errno]);


		/* poke init */
		if(setinit(DISABLE)) {
			fprintf(stderr, "%s already disabled\n", device);
		} else {
			pokeinit(device, enable);
		}
#ifndef DEBUG
		if((devfile = open(device, O_RDWR|O_NDELAY)) < 0) {
			fprintf(stderr, "On %s open: %s\n",
				device, sys_errlist[errno]);
		} else {
			if(ioctl(devfile, (int)TIOCSDTR, (char *)0) < 0)
				fprintf(stderr,
				    "Cannot set DTR on %s: %s\n",
					device, sys_errlist[errno]);
		}
#endif
	}

	acuexit(0);
}


/* poke process 1 and wait for it to do its thing */
pokeinit(device, enable)
char  *device; int enable;
{
	struct utmp utmp;
	FILE	*fin, *popen();
	char	buf[BUFSIZ];
	char    *ps,getty[60];
	register int i;


	/* poke init */
	if (system("/etc/init q")) { 
		fprintf(stderr,
		    "Cannot send hangup to init process: %s\n",
			sys_errlist[errno]);
		(void)setinit(resettty);
		acuexit(1);
	}


	/* wait till init has responded */
        ps= "ps -uroot";
	strcpy(getty, "/etc/getty ");
	strcat(getty,device);

tryagain:
	if ((fin = popen(ps, "r")) == NULL)
	{
		fprintf(stderr, "acucntrl: can't run %s\n", ps);
		acuexit(1);
	}
	fgets(buf, sizeof buf, fin);	/* get header line from ps */
	while (fgets(buf, sizeof buf, fin) != NULL)
		{
		if (enable && strindex(buf, getty) >=0) return;
			else sleep(1);
		}
	pclose(fin);
	if(enable)  goto tryagain;
}

prefix(s1, s2)
	register char *s1, *s2;
{
	register char c;

	while ((c = *s1++) == *s2++)
		if (c == '\0')
			return (1);
	return (c == '\0');
}

/* identify terminal line in ttys */
opninittab(device)
char *device;
{
	char p[BUFSIZ];
	char *index();
	char linebuf[BUFSIZ];

	inittab = NULL;
	do {
		if (inittab != NULL) {
			fclose(inittab);
			sleep(5);
		}
		inittab = fopen(Etcinit, "r");
		if(inittab == NULL) {
			fprintf(stderr, "Cannot open %s: %s\n", Etcinit,
				sys_errlist[errno]);
			acuexit(1);
		}
	} while (flock(fileno(inittab), LOCK_NB|LOCK_EX) < 0);
	ninittab = fopen(Etcnewinit, "w");
	if(ninittab == NULL || (flock(fileno(ninittab),LOCK_NB|LOCK_EX) < 0)) {
		fprintf(stderr, "Cannot open %s: %s\n", Etcinit,
			sys_errlist[errno]);
		acuexit(1);
	}
	
	while(fgets(linebuf, sizeof(linebuf) - 1, inittab) != NULL) {

#ifdef DEBUG
		fprintf(stderr,"ttyslnbeg = %d\n",ttyslnbeg);
#endif
		sprintf(p,"/etc/getty %s",device);
		if(strindex(linebuf,p) != -1)
			return;
		ttyslnbeg += strlen(linebuf);
		if (fputs(linebuf, ninittab) == NULL) {
			fprintf(stderr, "On %s write: %s\n",
				Etcinit, sys_errlist[errno]);
			acuexit(1);
		}
		
	}
	fprintf(stderr, "%s not found in %s\n", device, Etcinit);
	acuexit(1);
}

/* modify appropriate line in /etc/inittab to turn on/off the device */
setinit(enable)
int enable;
{
	register char *cp, *cp2,*cp3,*cp4;
	char lbuf[BUFSIZ];
	int i;
	char c1, c2;

	(void) fseek(inittab, ttyslnbeg, 0);
	if(fgets(lbuf, BUFSIZ, inittab) == NULL) {
		fprintf(stderr, "On %s read: %s\n",
			Etcinit, sys_errlist[errno]);
		acuexit(1);
	}
	/* format is now */
	/* xx:rstate:stuff:command */
	cp = lbuf;
	for (i=0;*cp && i<3;i++) {
		while (*cp && (*cp != ':'))
			cp++;
		switch(i){
			case 0: cp2=cp;break; /* rstate */
			case 1: cp3=cp;break; /* action */
			case 2: cp4=cp;break; /* process */
			}
		cp++;
	}
	if (*cp == '\0') {
		fprintf(stderr,"Badly formatted line in /etc/inittab:\n%s", lbuf);
		acuexit(1);
	}

	*cp2='\0';cp2++;
	*cp3='\0';cp3++;
	*cp4='\0';cp4++;
#ifdef DEBUG
	fprintf(stderr,"field 1 = %s\nfield 2 = %s\nfield 3 = %s\nfield 4 = %s\n",lbuf,cp2,cp3,cp4);
#endif
	resettty = strcmp("respawn", cp3) != 0;
#ifdef DEBUG
	fprintf(stderr,"resettty = %d\n", resettty);
#endif

	fprintf(ninittab,"%s:%s:%s:%s", lbuf,cp2,enable ? "respawn" : "off", cp4);
	if (ferror(ninittab)) {
		fprintf(stderr, "On %s fprintf: %s\n",
			Etcnewinit, sys_errlist[errno]);
		acuexit(1);
	}
	while(fgets(lbuf, sizeof(lbuf) - 1, inittab) != NULL) {
		if (fputs(lbuf, ninittab) == NULL) {
			fprintf(stderr, "On %s write: %s\n",
				Etcnewinit, sys_errlist[errno]);
			acuexit(1);
		}
	}
		
#ifndef DEBUG
	if (enable^resettty)
		(void) unlink(Etcnewinit);
	else {
		struct stat statb;
		if (stat(Etcinit, &statb) == 0) {
			fchmod(fileno(ninittab) ,statb.st_mode);
			fchown(fileno(ninittab), statb.st_uid, statb.st_gid);
		}
		(void) rename(Etcnewinit, Etcinit);
	}
#endif
	(void) fclose(ninittab);
	(void) fclose(inittab);
	return enable^resettty;
}
strindex(s,t) /* return index of t in s, -1 if none ...like instr in MBASIC */
	char *s, *t;
{
	int i, n;

	n=strlen(t);
	for (i=0; s[i] != '\0'; i++)
		if (strncmp(s+i, t, n) == 0)
			return i;
	return -1;
}
acuexit(x)
int x;
{
	rmlock();
	exit(x);
}



/* this code taken from C-Kermit Version 4D
 * thanks to Columbia for making it available
 * and to H. Fisher for writing it.
 * This section of the code is copyrighted by Columbia.
 Copyright (C) 1985, Trustees of Columbia University in the City of New York.
 Permission is granted to any individual or institution to use, copy, or
 redistribute this software so long as it is not sold for profit,
 provided this copyright notice is retained. 
 * modifications copyright 1986 by Stan Barber
 */
look4lk(name) char *name; {
    char lockfil[50];			/* Max length for lock file name */
#ifdef ISIII
    char *lockdir = "/etc/locks";
#else
#ifdef ATT3BX
    char *lockdir = "/usr/spool/locks";
#else
#ifdef NEWUUCP
    char *lockdir = "/usr/spool/uucp/LCK";
#else
    char *lockdir = "/usr/spool/uucp";
#endif
#endif
#endif


#ifdef ISIII
    (void) strcpy( lockfil, name );
#else
    strcat(strcpy( lockfil, "LCK.." ), name );
#endif

    if (access( lockdir, 04 ) < 0) {	/* read access denied on lock dir */
	fprintf(stderr,"Warning, read access to lock directory denied\n");
	return( 1 );			/* cannot check or set lock file */
    }
	
    strcat(strcat(strcpy(flfnam,lockdir),"/"), lockfil);
/* begin modifications by Stan Barber */
    if (access(flfnam,02) == 0) /* we can read the file */
	{
		int fd;
		fd = open(flfnam,0);
		if (fd >= 0 )
		{
		int ret,pid;
			ret = read(fd,&pid,sizeof pid);
			close(fd);
			if (ret == sizeof pid &&
			      (kill(pid, 0) == 0 || errno != ESRCH))
				return(-1); /* process still running */
		}
		fprintf(stderr,"Warning, dead lock found\n");
		unlink(flfnam); /* attempt to remove it */
	}					
/* end modifications by Stan Barber */
    if ( access( lockdir, 02 ) < 0 ) {	/* lock file cannot be written */
	fprintf(stderr,"Warning, write access to lock directory denied\n");
	return( 1 );
    }
    return( 0 );			/* okay to go ahead and lock */
}



setlock() {		/* lock uucp if possible */
#ifdef ATT3BX
    FILE *lck_fild;
#endif
    int lck_fil, l4l;
    int pid_buf = getpid();		/* pid to save in lock file */
	
    hasLock = 0;			/* not locked yet */
    l4l = look4lk("ACUCNTRL");
    if (l4l < 0) return (-1);		/* already locked */
    if (l4l == 1) return (0);		/* can't read/write lock directory */
    lck_fil = creat(flfnam, 0444);	/* create lock file ... */
    if (lck_fil < 0) return (-1);	/* create of lockfile failed */
		/* creat leaves file handle open for writing -- hf */
#ifdef ATT3BX
    fprintf((lck_fild = fdopen(lck_fil, "w")), "%10d\n", pid_buf);
    fflush(lck_fild);
#else
    write (lck_fil, &pid_buf, sizeof(pid_buf) ); /* uucp expects int in file */
#endif
    close (lck_fil);
    hasLock = 1;			/* now is locked */
    return(0);
}


rmlock() {				/* kill uucp lock if possible */
    if (hasLock) return( unlink( flfnam ) );
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0