[net.sources] switch modem line in/out

steve@txsil.UUCP (10/31/86)

Here is a program which we use locally to timeshare a single modem line
between incoming (login) and outgoing calls.  We run a 680x0 port of 4.2BSD,
but the program should be easy to fix for other versions of Unix.  We place
the program in /etc, and it must run setuid to root.

Stephen McConnel                   USENET: ...!convex!smu!txsil!steve
Summer Institute of Linguistics    CSNET:  txsil!steve@smu
7500 W. Camp Wisdom Road           ARPA:   txsil!steve%smu@csnet-relay.ARPA
Dallas, TX 75236
214-298-3331 (x238,x274)

----------------------------------cut 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:
#	dial.1
#	dial-lines
#	dial.c
# This archive created: Thu Oct 30 15:45:13 1986
export PATH; PATH=/bin:$PATH
if test -f 'dial.1'
then
	echo shar: over-writing existing file "'dial.1'"
fi
cat << \SHAR_EOF > 'dial.1'
.TH DIAL 1L
.SH NAME
dial \- switch a tty line between incoming and outgoing
.SH SYNOPSIS
.B /etc/dial in|out [ ttyxx ]
.SH DESCRIPTION
.I Dial
switches the designated tty line from an incoming (login) line to an
outgoing (nonlogin) line, or vice versa.  This can useful, for example,
when a single modem must serve both as a login line, and as a dialout
line.
.sp
.I Dial
takes either one or two arguments.  The first argument is either the
word "in" or the word "out", to designate whether the tty line is to be
incoming or outgoing.  The second (optional) argument is the name of the
tty line, such as "ttyd0" or "ttyie".  If given, the tty name is checked
against those in the file "/etc/dial-lines" for permission.  If the
second argument is omitted, the tty given on the first line of that file
is used.
.sp
.I Dial
must be setuid to root for the actions which it must take.  It is
recommended that /etc/dial-lines be owned by root also.
.SH FILES
/etc/dial-lines
.br
This file lists the ttys which can be affected by
.I dial.
The format is one tty name per line.
.SH AUTHORS
.nf
Gertjan Vinkesteyn
.sp
Stephen McConnel
Summer Institute of Linguistics
7500 W. Camp Wisdom Road
Dallas, Texas 75236
.fi
.SH DIAGNOSTICS
.nf
cannot open `/etc/dial-lines'
/etc/dial-lines is empty
invalid tty line requested
/usr/spool/uucp/LCK..ttyxx already exists
root or uucp does not own /dev/ttyxx, somebody might be logged on
.fi
SHAR_EOF
if test -f 'dial-lines'
then
	echo shar: over-writing existing file "'dial-lines'"
fi
cat << \SHAR_EOF > 'dial-lines'
ttyde
ttyh6
ttyi2
SHAR_EOF
if test -f 'dial.c'
then
	echo shar: over-writing existing file "'dial.c'"
fi
cat << \SHAR_EOF > 'dial.c'
/* dial.c -- "dial in|out [ttyxx]" for bidirectional line use
 *
 * originally written (I believe) by Gertjan Vinkesteyn
 * 30-Oct-86 Steve McConnel	add option to specify tty line, and use of
 *				/etc/dial-lines
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>

extern char *strcpy(), *strcat(), *fgets();

#define ROOT_UID 0
#define UUCP_UID 5

char *documentation[] = {
"Usage:  /etc/dial in|out [ttyxx]",
"",
"    /etc/dial in         changes /dev/ttyxx to an incoming (login) line",
"    /etc/dial out        changes /dev/ttyxx to an outgoing line",
"    /etc/dial in ttyi3   changes /dev/ttyi3 to an incoming (login) line",
"    /etc/dial out ttyh4  changes /dev/ttyh4 to an outgoing line",
NULL };

/************************************************************************
 * NAME
 *    usage
 * DESCRIPTION
 *    Print the online documentation on how to use this program.
 */
usage()
{
register char **p;
for (p = documentation ; *p ; ++p)
    fprintf(stderr,"%s\n", *p);
exit(0);
}

/************************************************************************
 * NAME
 *    fgetss
 * DESCRIPTION
 *    Read a line from a file like fgets(), but strip the trailing '\n'
 *    like gets().
 * ARGUMENTS
 *    buf  - address of input buffer
 *    size - size of input buffer
 *    fp   - input FILE pointer
 * RETURN VALUE
 *    original value of `buf' if successful, NULL if failure (such as EOF)
 */
char *fgetss(buf, size, fp)
char *buf;
int size;
FILE *fp;
{
register char *p;

if (fgets(buf,size,fp) == NULL)
    return(NULL);
p = buf + strlen(buf);
if (*--p == '\n')
    *p = '\0';
return(buf);
}

/************************************************************************
 * NAME
 *    fputss
 * DESCRIPTION
 *    Write a line to a file like fputs(), but add a trailing '\n' like
 *    puts().
 * ARGUMENTS
 *    buf  - address of output buffer
 *    fp   - output FILE pointer
 */
fputss(buf, fp)
char *buf;
FILE *fp;
{
fputs(buf,fp);
fputs("\n",fp);
}

/************************************************************************
 * NAME
 *    Abort
 * DESCRIPTION
 *    print an error message and die.
 * RETURN VALUE
 *    does not return to program, returns to shell with -1 value
 */
Abort(msg)
char *msg;
{
fprintf(stderr, "dial abort:  %s\n", msg);
exit(-1);
}

/************************************************************************
 * NAME
 *    main
 * DESCRIPTION
 *    This program switches a tty line between incoming and outgoing.
 *    See dial(8L) for details (or read the code below!).
 * ARGUMENTS
 *    argc - number of command line arguments
 *    argv - array of command line arguments
 */
main(argc, argv)
int argc;
char *argv[];
{
register char *p;
char line[100];		/* working buffer large enough for any need */
char ttyline[12];	/* tty name (excluding "/dev/") */
char lck[32];		/* constructed name of lock file */
char tty[16];		/* constructed tty filename (includes "/dev/") */
FILE *Ti;		/* for input from /etc/dial-lines and /etc/ttys */
FILE *To;		/* for output to /etc/ttysnew */
struct stat statbuf;	/* for checking owner of /dev/ttyxx */
/*
 *  check for valid command line arguments
 */
if ((argc < 2) || (argc > 3))
    usage();
if ((strcmp(argv[1], "in") != 0) && (strcmp(argv[1],"out") != 0))
    usage();
if (argc==3)
    {
    if (strncmp(argv[2],"tty",3) == 0)
	strcpy(ttyline,argv[2]);
    else
	usage();
    }
else
    strcpy(ttyline,"");
/*
 *  get/check a valid tty line from /etc/dial-lines
 */
if ((Ti = fopen("/etc/dial-lines", "r")) == NULL)
    Abort("cannot open `/etc/dial-lines'");
if (fgetss(line,sizeof(line),Ti) == NULL)
    Abort("/etc/dial-lines is empty");
if (ttyline[0] == '\0')
    strcpy(ttyline,line);
else
    {
    while (strcmp(ttyline,line) != 0)
	{
	if (fgetss(line,sizeof(line),Ti) == NULL)
	    Abort("invalid tty line requested");
	}
    }
fclose(Ti);
strcat( strcpy(lck,"/usr/spool/uucp/LCK.."), ttyline );
strcat( strcpy(tty,"/dev/"), ttyline );
/*
 *  check that the line isn't already being used
 */
if (access(lck,F_OK) == 0)
    Abort( strcat(strcpy(line,lck)," already exists") );
if (stat(tty, &statbuf) < 0)
    Abort("stat failed");
if ((statbuf.st_uid != ROOT_UID) && (statbuf.st_uid != UUCP_UID))
    Abort( strcat(strcat(strcpy(line,"root or uucp does not own "), tty),
				     ", somebody might be logged on") );
/*
 *  get set to modify /etc/ttys to change the tty line's mode
 */
if ((Ti = fopen("/etc/ttys", "r")) == NULL)
    Abort("cannot open `/etc/ttys'");
if ((To = fopen("/etc/ttysnew", "w")) == NULL)
    Abort("cannot create `/etc/ttysnew'");
/*
 *  scan through /etc/ttys to find the desired line, writing to /etc/ttysnew
 */
while ((p = fgetss(line, sizeof(line), Ti)) != NULL)
    {
    if (strcmp(&line[2], ttyline) == 0)
	{
	if (strcmp(argv[1],"in") == 0)
	    *p = '1';
	else
	    {
	    *p = '0';
	    if (chmod(tty, 0666) != 0)
		fprintf(stderr, "dial: cannot chmod `%s'\n", tty);
	    else if (chown(tty, UUCP_UID, 1) != 0)
		fprintf(stderr, "dial: cannot chown `%s'\n", tty);
	    }
	}
    fputss(line, To);
    }
fclose(To);
fclose(Ti);
/*
 *  replace /etc/ttys with /etc/ttysnew
 */
if (unlink("/etc/ttys") == -1)
    Abort("cannot unlink /etc/ttys");
if (link("/etc/ttysnew", "/etc/ttys") == -1)
    {
    fprintf(stderr,
	  "dial abort:  link(\"/etc/ttysnew\",\"/etc/ttys\") failed\n");
    Abort("FATAL--NOW WE DON'T HAVE A /etc/ttys FILE ANYMORE!!");
    }
if (unlink("/etc/ttysnew") == -1)
    Abort("cannot unlink /etc/ttysnew");
/*
 *  get the system to recognize the new /etc/ttys
 */
if (kill (1, 1) != 0)
    Abort("cannot \"kill -1 1\"");
}
SHAR_EOF
#	End of shell archive
exit 0