[comp.sources.unix] v15i083: A "shell" for CU, Kermit, etc.

rsalz@uunet.uu.net (Rich Salz) (07/07/88)

Submitted-by: bsmith@csuchico.EDU (Bennett Smith)
Posting-number: Volume 15, Issue 83
Archive-name: cu-shell

This program attempts to solve some of the problems with having kermit
cu, and uucp sharing the same lines.  It also keeps a log of all connections
that are made.  Access to costly resources such as phone lines may be
restricted by way of a Call-sys file.  It will not allow the use of cu or
kermit directly, thus a user cannot specify a phone number.

Hope this helps.  Chico State University has been using it for about one
month, and it seems to solve most of their problems.  Have Fun.

				Bennett (bsmith@csuchico.EDU)

# 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:
#	Makefile
#	call.c
#	call.1L
#	Call-sys
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#  	Copyright (c) 1988 Cascade Research
# 	  All Rights Reserved              
# 
#  	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF CASCADE RESEARCH
# 	The copyright notice above does not evidence any actual or     
# 	intended publication of such source code                       
# 
# 	@(#)Makefile	1.2 */
# 
#  WHO	MODIFICATION				DATE DONE
#  ---  -------------------------------------   ---------
#  cbs	Creation of Makefile for call.c		12-Apr-88

# Comment out if you don't have HonneyDANBER lockfiles.
HDB		= 1

# Full path to file containing list of valid systems for call.
CALLFILE	= /usr/lib/uucp/Call-sys

# Full path to call log file.
LOGFILE		= /usr/lib/uucp/Call-log

# Sprintf string for pathname of lockfile.
LOCKFILE	= /usr/spool/locks/LCK..%s

# Full path to kermit executable.
KERMIT		= /usr/local/bin/kermit

# Full path to cu executable.
CU		= /usr/bin/cu

# User and group id for uucp (used to chown the device back after call).
UIDUUCP		= 5
GIDUUCP		= 5

# Mode of device for kermit and uucp/cu (used to chmod the device during call). 
MODKER		= 00600
MODUUCP		= 00622

DEFS		= -DHDB=\"$(HDB)\" -DCALLFILE=\"$(CALLFILE)\" \
		  -DLOGFILE=\"$(LOGFILE)\" -DLOCKFILE=\"$(LOCKFILE)\" \
		  -DKERMIT=\"$(KERMIT)\" -DCU=\"$(CU)\" \
		  -DUIDUUCP=\"$(UIDUUCP)\" -DGIDUUCP=\"$(GIDUUCP)\" \
		  -DMODKER=\"$(MODKER)\" -DMODUUCP=\"$(MODUUCP)\"

# Directory for call executable
BINDIR		= /usr/local/bin

# Directory for call manual page ([nt]roff form)
MANDIR		= /usr/man/l_man/man1

############################################################################

SRC		= call.c
OBJ		= call.o
MAN		= call.1L
CFLAGS		= -O ${DEFS}
LFLAGS		=
LINTFLAGS	= ${DEFS}
ROFF		= psroff -man
LP		= remsh bugs rlp pluto

call:		$(OBJ)
		cc $(LFLAGS) $(OBJ) -o call

man:
		$(ROFF) $(MAN)
print:
		pr Makefile call.c | $(LP)
		$(ROFF) $(MAN) | $(LP)
clean:	
		-rm -f $(OBJ) call

install:
		-cp call $(BINDIR)
		-cp Call-sys $(CALLFILE)
		-cp call.1 $(MANDIR)
		-chmod 4555 $(BINDIR)/call
		-chown root $(BINDIR)/call
		-chgrp bin $(BINDIR)/call
		-chmod 600 $(CALLFILE)
		-chown root $(CALLFILE)
		-chgrp bin $(CALLFILE)
		-chmod 444 $(MANDIR)/call.1
SHAR_EOF
fi # end of overwriting check
if test -f 'call.c'
then
	echo shar: will not over-write existing file "'call.c'"
else
cat << \SHAR_EOF > 'call.c'

/* 	Copyright (c) 1988 Cascade Research */
/*	  All Rights Reserved               */

/* 	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF CASCADE RESEARCH */
/*	The copyright notice above does not evidence any actual or      */
/*	intended publication of such source code                        */

/*	@(#)call.c	1.2 */

/* WHO	MODIFICATION				DATE DONE */
/* ---  -------------------------------------   --------- */
/* cbs	Initial creation of call.c program.	05-Apr-88 */
/* cbs	Added code for the -d option.		07-Apr-88 */
/* cbs	Added -l with no machine name to show	07-Apr-88 */
/*	entire contents of log file.			  */
/* cbs	Added code to set the HUPCL value for	12-Apr-88 */
/*	the device to be connected to.			  */

/* Call allows the invocation of cu or kermit in a more   */
/* restrictive environment.  It allows for the monitoring */
/* of callers, and the duration of their connections to   */
/* various machines.  Call gets a list of available       */
/* machines from the Call-devices file which is located   */
/* in the /usr/lib/uucp directory.  The log file of all   */
/* actions is in /usr/adm/Call-log.                       */

/* Call must run setuid(root).  It respects the HDB type  */
/* lock files as well as the v7 ones.  It will set the    */
/* permissions and ownerships of the appropriate device   */
/* for the duration of the call.                          */

#include <fcntl.h>
#include <termio.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

/* The following are the defaults.  If you wish to make changes, do so */
/* in the Makefile, not here.					       */

#ifndef CALLFILE
#define	CALLFILE	"/usr/lib/uucp/Call-sys"
#endif
#ifndef LOGFILE
#define LOGFILE		"/usr/lib/uucp/Call-log"
#endif
#ifndef LOCKFILE
#define LOCKFILE	"/usr/spool/locks/LCK..%s"
#endif
#ifndef KERMIT
#define KERMIT		"/usr/local/bin/kermit"
#endif
#ifndef CU
#define CU		"/usr/bin/cu"
#endif
#ifndef UIDUUCP
#define UIDUUCP	5
#endif
#ifndef GIDUUCP
#define GIDUUCP	5
#endif
#ifndef MODKER
#define MODKER	00600
#endif
#ifndef MODUUCP
#define MODUUCP 00622
#endif

struct calldev {
	char *name;		/* name of system */
	char *dev;		/* device attached to */
	char *baud;		/* baud rate to connect at */
	char *mdmtype;		/* modem type */
	char *telno;		/* telephone number */
	struct calldev *next;	/* pointer to next available device */
};

struct calldev *callhead, *callcur, *match, *getcall();
char *pname, *getlogin(), typecall[50], whocall[50];

struct termio *devparam;
int fd;

int errflg,kflg,cflg,lflg,sflg,dflg;

main(argc,argv)
int argc;
char *argv[];
{
	extern char *optarg;
	extern int optind,opterr;
	int c;
	char *callsys, cmd[100];
	FILE *cf, *fopen();

	devparam = (struct termio *)malloc(sizeof(struct termio));

	pname=(char *)malloc(sizeof(argv[0])+1);
	strcpy(pname,argv[0]);
	strcpy(typecall,"?????");
	strcpy(whocall,"?????");
	dflg=sflg=errflg=kflg=lflg=0;
	cflg=1;

	while ((c=getopt(argc,argv,"dckls"))!=EOF)
		switch (c) {
			case 'd':	dflg++;
					break;	
			case 'c':	cflg++;
					if (kflg) kflg=0;
					break;
			case 'k':	kflg++;
					if (cflg) cflg=0;
					break;
			case 'l':	lflg++;
					break;
			case 's':	sflg++;
					break;
			case '?':	errflg++;
		}

	if (errflg || argc < 2) {
		fprintf(stderr,"usage: %s [-d] [-c|-k|-l] system | [-s]\n",pname);
		exit (2);
	}

	callsys = argv[optind];
		
	if ((cf=fopen(CALLFILE,"r"))==(FILE *)NULL) {
		fprintf(stderr,"%s: cannot open %s\n",pname,CALLFILE);
		exit (2);
	}
	callhead = getcall(cf);	/* load the call structure from the CALLFILE */

	if (sflg) {
		strcpy(typecall,"SYSTEM LIST");
		for (callcur=callhead; callcur!=(struct calldev *)NULL; callcur=callcur->next)
			printf("%s\n",callcur->name);  
		exit(0);
	}

	if (lflg && (optind==argc)) {
		sprintf(cmd,"/bin/cat %s 2>/dev/null",LOGFILE);
		system(cmd);
		exit(0);
	}

	match=(struct calldev *)NULL;
	for (callcur=callhead; callcur!=(struct calldev *)NULL; callcur=callcur->next)
		if (strcmp(callsys,callcur->name)==0) match=callcur;
	if (match==(struct calldev *)NULL) {
		if (!lflg) 
			fprintf(stderr,"%s: %s is not a valid system\n",pname,callsys);
		else
			fprintf(stderr,"%s: must specify system for log viewing\n",pname);
		exit (2);
	}
	strcpy(whocall,match->name);
	if (!lflg) {
		exit (kflg?dokermit(match):docu(match));
	} else if (lflg)
		exit (showlog(match));
	exit(0);
}

/* showlog() - display the log for the specified match to CALLFILE */

int showlog(where)
struct calldev *where;
{
	char cmd[100];
	sprintf(cmd,"/bin/grep %s %s 2>/dev/null",where->name,LOGFILE);
	system(cmd);
	return (0);
}

/* dokermit() - invoke KERMIT for the specified match to CALLFILE */

int dokermit(where)
struct calldev *where;
{
	char line[35],rmlck[70];

	strcpy(typecall,"KERMIT");
	fprintf(stderr,"Calling with kermit\n");
	if(checklock(where->dev)==0) {
		sprintf(rmlck,"rm -f ");
		sprintf(line,LOCKFILE,where->dev);
		strcat(rmlck,line);
		strcat(rmlck," 2>&1 1>/dev/null");
		if (dflg)
			fprintf(stderr,"%s: removing invalid lock file [%s]\n",pname,line);
		system(rmlck);
		sprintf(line,"/dev/%s",where->dev);
		if (chown(line,getuid(),getgid())<0) {
			fprintf(stderr,"%s: cannot chown %s for kermit\n",pname,line);
			writelog("CHOWN FAILED");
			exit(2);
		}
		if (chmod(line,MODKER)<0) {
			fprintf(stderr,"%s: cannot chmod %s for kermit\n",pname,line);
			writelog("CHMOD FAILED");
			exit(2);
		}
		ioctl((fd=open(line,O_RDONLY)),devparam,TCGETA);
		devparam->c_cflag=devparam->c_cflag||HUPCL;
		ioctl(fd,devparam,TCSETA);
		close(fd);
		writelog("CONNECT");
		if (fork() == 0) {
			setuid(getuid());
			if (dflg)
				execlp(KERMIT,"kermit","-d","-l",line,"-b",where->baud,(char *)0);
			else
				execlp(KERMIT,"kermit","-l",line,"-b",where->baud,(char *)0);
			fprintf(stderr,"%s: cannot execute kermit\n",pname);
			exit(2);
		}
		wait((int *)0);
		writelog("DISCONNECT");
		if (chown(line,UIDUUCP,GIDUUCP)<0) {
			fprintf(stderr,"%s: cannot reset %s\n",pname,line);
			writelog("CHOWN FAILED");
			exit(2);
		}
		if (chmod(line,MODUUCP)<0) {
			fprintf(stderr,"%s: cannot reset %s\n",pname,line);
			writelog("CHMOD FAILED");
			exit(2);
		}
		return (0);
	} else {
		fprintf(stderr,"%s: line busy - try later\n",pname);
		return (1);
	}
}

/* docu() - invoke CU for the specified match to CALLFILE */

int docu(where)
struct calldev *where;
{
	char line[35];

	strcpy(typecall,"CU");
	fprintf(stderr,"Calling with cu\n");
	if(checklock(where->dev)==0) {
		sprintf(line,LOCKFILE,where->dev);
		ioctl((fd=open(line,O_RDONLY)),devparam,TCGETA);
		devparam->c_cflag=devparam->c_cflag||HUPCL;
		ioctl(fd,devparam,TCSETA);
		close(fd);
		writelog("CONNECT");
		if (fork() == 0) {
			if (dflg)
				execlp(CU,"cu","-d",where->name,(char *)0); /* run cu */
			else
				execlp(CU,"cu",where->name,(char *)0); /* run cu */
			fprintf(stderr,"%s: cannot execute cu\n",pname);
			exit(2);
		}
		wait((int *)0); /* wait for cu to disconnect */
		writelog("DISCONNECT");
		return (0);
	} else {
		fprintf(stderr,"%s: line busy - try later\n",pname);
		return (1);
	}
}

/* writelog() - write a line into the call logfile */

int writelog(desc)
char *desc;
{
	FILE *fopen(), *log;
	long clock;
	
	if ((log=fopen(LOGFILE,"a"))==(FILE *)NULL) {
		fprintf(stderr,"%s: could not open log file\n",pname);
		exit(2);
	}
	clock=time((long *)0);
	if (dflg)
		fprintf(stderr,"%-10s %-10s %-10s %-20s %s",whocall,getlogin(),typecall,desc,asctime(localtime(&clock)));
	fprintf(log,"%-10s %-10s %-10s %-20s %s",whocall,getlogin(),typecall,desc,asctime(localtime(&clock)));
	fclose(log);
	return (0);
}

/* checklock() - check that the device which we want is available */
/*               return: 0=ok 1=device in use                     */

int checklock(dev)
char *dev;
{
	char lckfile[100];
	int lkpid;
	FILE *lf;

	sprintf(lckfile,LOCKFILE,dev);
	if (access(lckfile,0)==0) { /* check if lockfile exists */
#ifdef HDB
		if ((lf=fopen(lckfile,"r"))!=(FILE *)NULL) {
			fscanf(lf,"%d",&lkpid);
			if (kill(lkpid,0)==(-1))  /* check if line avail */
				return(0); /* pid is invalid, line avail */
			else return (1); /* valid pid, line busy */
		}
#endif
		return(1);	
	}
	return(0);
}

/* getcall() - read and parse the CALLFILE and setup the linked list */
/*             of allowed connections                                */

struct calldev *getcall(cf)
FILE *cf;
{
	struct calldev *head, *cur;
	int c; 
	char tname[25], tdev[25], tbaud[25], tmdmtype[25], ttelno[25];

	cur=head=(struct calldev *)NULL;
	while ((c=fgetc(cf))!=EOF)
		if (c=='#' || c==' ' || c=='\t' || c=='\n')
			while ((c=fgetc(cf))!='\n');
		else {
			ungetc(c,cf);
			fscanf(cf,"%s %s %s %s %s\n",tname,tdev,tbaud,tmdmtype,ttelno);
			if (cur==(struct calldev *)NULL) {
				head=(struct calldev *)malloc(sizeof(struct calldev));
				cur=head;
			} else {
				cur->next=(struct calldev *)malloc(sizeof(struct calldev));
				cur=cur->next;
			}
			cur->name=(char *)malloc(strlen(tname)+1);
			strcpy(cur->name,tname);
			cur->dev=(char *)malloc(strlen(tdev)+1);
			strcpy(cur->dev,tdev);
			cur->baud=(char *)malloc(strlen(tbaud)+1);
			strcpy(cur->baud,tbaud);
			cur->mdmtype=(char *)malloc(strlen(tmdmtype)+1);
			strcpy(cur->mdmtype,tmdmtype);
			cur->telno=(char *)malloc(strlen(ttelno)+1);
			strcpy(cur->telno,ttelno);
		}
	fclose(cf);
	return (head);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'call.1L'
then
	echo shar: will not over-write existing file "'call.1L'"
else
cat << \SHAR_EOF > 'call.1L'
.TH CALL 1L local
.SH NAME
call \- make a connection to an available machine via cu or kermit
.SH SYNOPSIS
.B call "[ -d ]" "[ -ck ]" "system"
.P
.B call "[ -d ]" -l "[ system ]"
.P
.B call "[ -d ]" "-s"
.SH DESCRIPTION
.I Call
provides a means to connect to another UNIX system, a terminal, or possibly
a non-UNIX system.  It restricts access to the actual calling programs,
.I cu
and
.IR kermit ,
in an effort to limit access to expensive resources (ie. phone lines).
.I Call
maintains a log of all activity on the available lines.  It will only allow
access to
.I cu
with a valid system name, thus preventing direct dialing.
.P
.I Call
allows for the peaceful co-existence of
.I kermit
with 
.I cu
and
.IR uucp ,
because it will not execute
.I kermit
unless the specified line is currently available.  It respects HoneyDANBER
lockfiles with the pid of the active process, and the older lockfiles which
simply indicate that the line is used.
.TP
.B call
accepts the following options and arguments.
.TP
-c
Invokes
.B call
with
.I cu
as the calling program.  This is the default calling program, if neither
.B "-c"
or
.B "-k"
is present.
.TP
-k
Invokes
.B call
with 
.I kermit
as the calling program.  It will supply
.I kermit
with the device name and baud rate.  Calling with
.I kermit
is not premitted over a dialed line.  Only "direct" connections are
supported with this option.
.TP
system
A valid system name obtained from the
.B /usr/lib/uucp/Call-sys
file.  To list the currently available system names, see the
.B -s
option below.
.TP
-d
Turn on the debugging mode for the calling program.  This is useful
when connecting over an unreliable line since the progression of the
connection may be monitored.  
.I Cu
will display the debugging information on the screen whereas
.I kermit
will send all debugging information to the file
.B debug.log
in the current directory.
.TP
-s
Display a list of available systems. 
.TP
-l
List the log file for the specified system name.  If no system is
specified, the entire log is displayed, sorted by machine and then
by time.
.SH FILES
.P
/usr/lib/uucp/Call-sys
.P
/usr/lib/uucp/Call-log
/usr/spool/uucp/LCK..(tty-device)
.SH SEE ALSO
.P
cu(1C), uucp(1C), kermit(1L)
.SH DIAGNOSTICS
.P
Exit code is zero for normal exit, non-zero (various values) otherwise.
.SH BUGS
As yet, none have been reported.
.SH AUTHORS
.P
Bennett Smith
.br
Jim Garlick
SHAR_EOF
fi # end of overwriting check
if test -f 'Call-sys'
then
	echo shar: will not over-write existing file "'Call-sys'"
else
cat << \SHAR_EOF > 'Call-sys'
# Connections contains a list of all valid connections for the call
# program.
#
# Line syntax:
#    system \t device \t baud \t modemtype \t telno
#
# If an argument does not apply, use a dash '-' as a place holder for it.
# There must be 5 arguments per line, and comments are NOT allowed to
# follow the arguments.  Comments are OK on other lines, and start with
# a hatch '#' character.
#
# The system and device fields are always necessary, but the baud, modemtype,
# and telno fields are only used by kermit.
#
# examples -
#	bugs tty106 9600 - -
#	bunky tty021 9600 - -
#
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Cascade Research                        | uucp:     ihnp4!csun!csuchico!bsmith
Bennett Smith, Software Engineer        | voice:    (916) 343-5731

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.