home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-09-11 | 34.8 KB | 1,283 lines |
- Path: uunet!rs
- From: rs@uunet.UU.NET (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v11i008: Getty on/off programs for 4.[23] BSD
- Message-ID: <898@uunet.UU.NET>
- Date: 13 Aug 87 22:41:13 GMT
- Organization: UUNET Communications Services, Arlington, VA
- Lines: 1272
- Approved: rs@uunet.UU.NET
-
- Submitted-by: tron@sc.nsc.com (Ronald S. Karr)
- Posting-number: Volume 11, Issue 8
- Archive-name: getty-enable
-
- [ Since UUNET is not about to give me write permission to /etc/ttys :-),
- I just compiled this and haven't run it. -r$ ]
-
- The 4.3BSD ttys file format increases versatility, but the simple act
- of enabling or disabling getty on a line is no longer easily writable
- as a quick shell script. Thus, I wrote this C program to do this for
- me. I modified it for 4.2BSD as well, just for fun.
-
- tron |-<=>-| ARPAnet: nsc!tron@sun.COM
- tron@sc.nsc.com UUCPnet: {amdahl,decwrl,hplabs,pyramid,sun}!nsc!tron
-
- #!/bin/sh
- #
- # This is a shell archive.
- # Send this to /bin/sh to create the following files:
- # README
- # Makefile
- # enable.man
- # enable.c
- # enable42.c
- #
-
- echo "Extracting from shar archive number 1..."
-
- if test ! -f "README"; then
- echo " file - \"README\""
- sed 's/^X//' > "README" <<\EOF
- XIn versions of UN*X before 4.3BSD, it was relatively simple to write
- Xa quick shell script to turn gettys on or off on tty lines. But under
- X4.3BSD the more complex /etc/ttys file format makes it more difficult
- Xto make a simple shell script for this, or at least one that can handle
- Xerrors in a reasonable fashion. Thus, I wrote this little program
- Xto handle the new format. Then, since I had written the program, anyway,
- Xand much of the code is independent of file format, I wrote a second
- Xversion to handle 4.2BSD consistently. The version for 4.2BSD may
- Xwork elsewhere if the code for AUTHGROUP and the rename(2) call are
- Xreplaced with something appropriate for a different environment.
- X
- X tron |-<=>-| ARPAnet: nsc!tron@sun.com
- X tron@sc.nsc.com UUCPnet: {amdahl,decwrl,hplabs,pyramid,sun}!nsc!tron
- EOF
- if test $? -ne 0; then
- echo " WARNING: status $? returned by sed"
- fi
- else
- echo " WARNING: \"README\" already exists, will not overwrite"
- fi
-
- if test ! -f "Makefile"; then
- echo " file - \"Makefile\""
- sed 's/^X//' > "Makefile" <<\EOF
- X#!/bin/make -f
- X# ENABLE/DISABLE TTYLINES (makefile)
- X#
- X# $Header: Makefile,v 1.1 87/03/06 20:44:14 tron Exp $
- X# $Log: Makefile,v $
- X# Revision 1.1 87/03/06 20:44:14 tron
- X# Remove system V comments.
- X#
- X# Revision 1.0 87/03/06 19:36:43 tron
- X# Initial revision
- X#
- X
- X# 4.3BSD systems use enable.c, which handles the new format
- X# /etc/ttys file. 4.2 systems use enable42.c which handles
- X# the older, less interesting, format.
- XSRC=enable.c
- X
- XDESTDIR=/etc
- XMANDIR=/usr/local/man/man8
- XMANSUFFIX=8
- X
- X# users in this group are authorized to use these programs
- XAUTHGROUP = "operator"
- X
- XCFLAGS=-O
- X
- Xall: enable
- X
- Xenable: ${SRC}
- X cc ${CFLAGS} -o enable ${SRC}
- X
- Xinstall: enable
- X rm -f ${DESTDIR}/disable
- X install -s -m 4755 -o root enable ${DESTDIR}/enable
- X ln ${DESTDIR}/enable ${DESTDIR}/disable
- X
- Xinstallman: enable.man
- X install -c enable.man ${MANDIR}/enable.${MANSUFFIX}
- X
- Xlint: ${SRC}
- X lint ${SRC}
- X
- Xclean:; rm *.o core a.out enable disable
- EOF
- if test $? -ne 0; then
- echo " WARNING: status $? returned by sed"
- fi
- else
- echo " WARNING: \"Makefile\" already exists, will not overwrite"
- fi
-
- if test ! -f "enable.man"; then
- echo " file - \"enable.man\""
- sed 's/^X//' > "enable.man" <<\EOF
- X'\"
- X'\" $Header: enable.man,v 1.1 87/03/06 20:16:01 tron Exp $
- X'\" $Log: enable.man,v $
- X'\" Revision 1.1 87/03/06 20:16:01 tron
- X'\" Added information about the "operator" group permissions in BSD.
- X'\"
- X'\" Revision 1.0 87/03/06 19:37:52 tron
- X'\" Initial revision
- X'\"
- X.TH ENABLE 8 "March 6, 1987"
- X.UC 4
- X.SH NAME
- Xenable, disable \- enable, disable getty on tty lines
- X.SH SYNOPSIS
- X.B enable
- Xtty ...
- X.br
- X.B disable
- Xtty ...
- X.br
- X.SH DESCRIPTION
- X.I Enable
- Xsets the getty enable flag in
- X.I /etc/ttys
- Xfor the specified tty lines.
- X.I Disable
- Xresets the getty enable flag.
- XBoth programs send the SIGHUP signal to init(8) when all changes
- Xhave been made so init can respond to the configuration changes.
- X.PP
- XUnder 4BSD systems,
- Xusers in the group "operator" can use
- X.I enable
- Xand
- X.I disable
- Xwithout needing to
- X.I su(1)
- Xto root.
- X.SH DIAGNOSTICS
- XComplains about anything that seems wrong,
- Xand does a reasonably large amount
- Xof checking.
- X.SH FILES
- X.ta 2i
- X/etc/ttys terminal information file
- X.br
- X/etc/nttys temp file for making changes
- X.br
- X/etc/ottys backup ttys file
- X.br
- X.SH AUTHOR
- XRonald S. Karr
- X.br
- Xtron@sc.nsc.com
- X.SH "SEE ALSO"
- Xinit(8), ttys(5), getty(8), su(1)
- X.SH BUGS
- XOn 4.3BSD systems, there must be on "on" or "off" string in the
- Xstatus field for this to work.
- X.I Enable
- Xand
- X.I Disable
- Xwill change this field,
- Xbut will not create it if it does not exist.
- EOF
- if test $? -ne 0; then
- echo " WARNING: status $? returned by sed"
- fi
- else
- echo " WARNING: \"enable.man\" already exists, will not overwrite"
- fi
-
- if test ! -f "enable.c"; then
- echo " file - \"enable.c\""
- sed 's/^X//' > "enable.c" <<\EOF
- X/*
- X * ENABLE/DISABLE -- turn on/off tty lines (4.2BSD version).
- X *
- X * $Header: enable.c,v 1.3 87/03/06 20:49:13 tron Exp $
- X * $Log: enable.c,v $
- X * Revision 1.3 87/03/06 20:49:13 tron
- X * Cleaned up panic exitcodes.
- X *
- X * Revision 1.2 87/03/06 19:48:57 tron
- X * cleanup, readied for release, added code for handling signals.
- X *
- X * Revision 1.1 87/03/06 05:13:43 tron
- X * found one bug, incorrectly computing basename of program.
- X *
- X * Revision 1.0 87/03/06 05:09:29 tron
- X * Initial revision
- X *
- X * usage:
- X * enable ttyline ...
- X * disable ttyline ...
- X *
- X * ttyline may be of the form "ttyname" or "/dev/ttyname" in which case
- X * "/dev/" is stripped off.
- X */
- X
- X#ifndef lint
- Xstatic char rcsid[] = "$Header: enable.c,v 1.3 87/03/06 20:49:13 tron Exp $";
- X#endif
- X
- X#include <stdio.h>
- X#include <sysexits.h>
- X#include <sys/types.h>
- X#include <sys/file.h>
- X#include <sys/param.h>
- X#include <sys/stat.h>
- X#include <signal.h>
- X#include <grp.h>
- X#include <strings.h>
- X#include <ctype.h>
- X
- Xchar dev_prefix[] = "/dev/"; /* prefix for tty special files */
- Xchar ttysfile[] = "/etc/ttys"; /* the file we are interested in */
- Xchar tempfile[] = "/etc/nttys"; /* lock file, temp file for changes */
- Xchar backup[] = "/etc/ottys"; /* in case of error, retain previous copy */
- X
- X#ifndef AUTHGROUP
- X# define AUTHGROUP "operator"
- X#endif AUTHGROUP
- X
- Xchar auth_group[] = AUTHGROUP; /* only root or users in this group are ok */
- X
- X/*
- X * advance to the next char in a string. If there is
- X * not enough space allocated in the string to advance to
- X * the next char, make it M_BUMP-chars larger.
- X * M_INIT is a reasonable initial malloc region.
- X *
- X * s - the string
- X * i - current index into string, range from 0 to a-1.
- X * a - current allocation size
- X *
- X * usage:
- X * NEXT(string,index,alloc_size) = new_character;
- X */
- X#define M_BUMP 64
- X#define NEXT(s,i,a) ((i)>=(a)?((a)+=M_BUMP,s=xrealloc((s),(a))):0),(s)[(i)++]
- X#define M_INIT (64-sizeof (int))
- X
- Xchar *program; /* name this program was run as */
- X
- Xchar *xmalloc(); /* malloc(3) with panic on error */
- Xchar *xrealloc(); /* realloc(3) with panic on error */
- Xchar *getfield(); /* get next field from the ttys file */
- Xvoid putfield(); /* write out field to the temp file */
- Xvoid panic(); /* write error message and quit */
- Xvoid security_check(); /* panic if user is not authorized for this */
- Xint interupt(); /* routine to handle signals */
- XFILE *temp = NULL; /* temp file; if open, unlink on error */
- X
- Xextern int errno; /* see intro(2) */
- Xextern int sys_nerr; /* number of known errors */
- Xextern char *sys_errlist[]; /* description of known errors */
- X
- Xvoid
- Xmain(argc, argv)
- X int argc; /* number of arguments */
- X char *argv[]; /* vector of arguments */
- X{
- X register char **ap; /* pointer to arguments list */
- X char *argmap; /* map of hits in tty file */
- X FILE *ttys; /* ttys file */
- X int fd; /* used for opening temp file */
- X struct stat statbuf; /* status on ttys file */
- X register char *field; /* current field from the ttys file */
- X int fieldno; /* field number in the ttys file */
- X int match; /* does the current line match anything? */
- X char *from; /* status field to change on matching line */
- X char *to; /* what to change that field to */
- X char *bl; /* hold blanks from getfield() */
- X int i; /* index */
- X
- X /* first things first, trap signals */
- X (void) signal(SIGHUP, interupt);
- X (void) signal(SIGINT, interupt);
- X (void) signal(SIGTERM, interupt);
- X
- X /* get basename of argv[0] for the name of the program */
- X program = rindex(*argv, '/');
- X if (program == NULL) {
- X program = *argv;
- X } else {
- X program++;
- X }
- X
- X /* make sure the user is authorized to do this */
- X security_check();
- X
- X /* what are we doing, anyway? */
- X if (strcmp(program, "enable") == 0) {
- X /* enable changes from off to on */
- X from = "off";
- X to = "on";
- X } else {
- X /* otherwise assume disable, changes from on to off */
- X from = "on";
- X to = "off";
- X }
- X argv++; /* we don't care about argv[0] anymore */
- X argc--;
- X
- X if (*argv == NULL) {
- X panic(EX_USAGE, "usage: %s ttyline ...\n", program);
- X /*NOTREACHED*/
- X }
- X
- X /* allocate ttys hit map and zero it out */
- X argmap = xmalloc((unsigned)argc);
- X (void) bzero(argmap, argc);
- X
- X /*
- X * cleanup the argument list
- X */
- X for (ap = argv; *ap; ap++) {
- X /*
- X * if in dev directory, get basename
- X */
- X if ((*ap)[0] == '/') {
- X if (strncmp(*ap, dev_prefix, sizeof(dev_prefix) - 1) != 0) {
- X panic(EX_DATAERR, "%s: illegal argument: %s\n", program, *ap);
- X /*NOTREACHED*/
- X }
- X *ap += sizeof(dev_prefix)-1;
- X }
- X }
- X
- X /*
- X * open the ttys file and get its mode
- X */
- X ttys = fopen(ttysfile, "r+");
- X if (ttys == NULL) {
- X panic(EX_OSFILE, "%s: could not open %s: %m\n", program, ttysfile);
- X /*NOTREACHED*/
- X }
- X if (fstat(fileno(ttys), &statbuf) < 0) {
- X panic(EX_OSFILE, "%s: could not stat %s: %m\n", program, ttysfile);
- X /*NOTREACHED*/
- X }
- X
- X /*
- X * open temp file exclusively to prevent simultaneous edits
- X */
- X fd = open(tempfile, O_WRONLY|O_CREAT|O_EXCL, statbuf.st_mode);
- X if (fd < 0) {
- X panic(EX_OSFILE, "%s: open failed on %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X temp = fdopen(fd, "w");
- X if (temp == NULL) {
- X panic(EX_OSERR, "%s: fdopen failed on %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X
- X /*
- X * loop through, grabbing fields from the ttys file and copying
- X * them to the temp file, altering the status fields as necessary
- X */
- X fieldno = 0; /* field number within the current line */
- X match = 0; /* is current line a match? */
- X
- X while (field = getfield(&bl, ttys)) {
- X if (field[0] == '\n' || field[0] == '#') {
- X /* end of a line, initialize for the next line */
- X fieldno = 0;
- X match = 0;
- X } else {
- X fieldno++;
- X if (fieldno == 1) {
- X /* does first field match one of the ttys to change */
- X for (ap = argv; *ap; ap++) {
- X if (strcmp(*ap, field) == 0) {
- X match = 1; /* yes */
- X if (argmap[ap-argv] > 0) {
- X (void)fprintf(stderr,
- X "%s: warning: %s in %s twice\n",
- X program, *ap, ttysfile);
- X }
- X argmap[ap-argv] = 1;
- X break;
- X }
- X }
- X } else if (fieldno > 3 && match) {
- X /* does status field match "on" or "off"? */
- X if (strcmp(from, field) == 0) {
- X field = to; /* change if so */
- X argmap[ap-argv] |= 2; /* signify that field found */
- X } else if (strcmp(to, field) == 0) {
- X (void) fprintf(stderr, "%s: %s is already %s\n",
- X program, *ap, to);
- X }
- X }
- X }
- X
- X /* output the field to the temp file */
- X putfield(bl, field, temp);
- X }
- X putfield(bl, "", temp); /* there could be stray spaces at the end */
- X
- X /*
- X * check map for anything that wasn't found or changed properly
- X */
- X for (i = 0; i < argc; i++) {
- X if (argmap[i] == 0) {
- X (void) fprintf(stderr, "%s: line %s not found in %s\n",
- X program, argv[i], ttysfile);
- X } else if ((argmap[i] & 2) == 0) {
- X (void) fprintf(stderr, "%s: line %s not turned %s\n",
- X program, argv[i], to);
- X }
- X }
- X
- X /* finish everything up */
- X (void) fclose(ttys); /* close files */
- X if (fclose(temp) == EOF) {
- X panic(EX_OSERR, "%s: could not close %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X (void) unlink(backup); /* remove previous backup */
- X if (link(ttysfile, backup) < 0) { /* create a new backup */
- X panic(EX_OSERR, "%s: link from %s to %s failed: %m\n",
- X program, ttysfile, backup);
- X /*NOTREACHED*/
- X } /* install new ttys file */
- X if (rename(tempfile, ttysfile)) {
- X panic(EX_OSERR, "%s: rename from %s to %s failed: %m\n",
- X program, tempfile, ttysfile);
- X /*NOTREACHED*/
- X }
- X if (kill(1, SIGHUP) < 0) { /* inform init of changes */
- X panic(EX_OSERR, "%s: failed to notify init: %m\n", program);
- X /*NOTREACHED*/
- X }
- X exit(0); /* all done, everything went okay (?) */
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * the user must either be root or be in the auth_group. Otherwise
- X * panic with a security violation.
- X */
- Xvoid
- Xsecurity_check()
- X{
- X register i; /* index */
- X register n; /* index limit */
- X int gidset[NGROUPS]; /* set of user's groups */
- X struct group *gr; /* group file entry */
- X register int gid; /* group we are searching for in gidset */
- X uid_t getuid(); /* let's make lint happy */
- X
- X /* are we root? */
- X if (getuid() == 0) return; /* yes */
- X
- X /* get id of authorized group */
- X gr = getgrnam(auth_group);
- X if (gr == NULL) {
- X panic(EX_SOFTWARE, "%s: no such group %s\n", program, auth_group);
- X /*NOTREACHED*/
- X }
- X gid = gr->gr_gid;
- X
- X n = getgroups(NGROUPS, gidset);
- X /* search for authorized group id */
- X for (i = 0; i < n; i++) {
- X if (gidset[i] == gid) return; /* user looks okay to me */
- X }
- X /* not found */
- X panic(EX_NOPERM, "%s: you are not allowed to run this program\n", program);
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * return the next field from the input. A field is defined
- X * as a sequence of non-space chars followed by a space char
- X * or the comment char '#'.
- X *
- X * a field beginning with # contains a comment which ends in newline.
- X * a field beginning with '\n' signals the end of an uncommented line.
- X * a return value of NULL signifies end of file.
- X * this routine panics on errors.
- X */
- Xchar *
- Xgetfield(bl, stream)
- X char **bl; /* return string of blanks here */
- X FILE *stream; /* file to take the fields from */
- X{
- X register int c; /* current input char */
- X register char *s; /* malloc region to store into */
- X static char *buf = NULL; /* retained between calls */
- X register int i = 0; /* index into s */
- X static unsigned a = M_INIT; /* allocation region */
- X int inquote = 0; /* set if we are inside of a quote */
- X int field; /* index to start of field */
- X
- X if (buf == NULL) {
- X s = buf = xmalloc(a); /* first time, get the initial region */
- X } else {
- X s = buf; /* get the region */
- X }
- X
- X /*
- X * advance to next non space or tab
- X */
- X while ((c = getc(stream)) != EOF && (c == ' ' || c == '\t')) {
- X NEXT(s,i,a) = c;
- X }
- X NEXT(s,i,a) = '\0'; /* terminate string of blanks */
- X
- X if (c == EOF) {
- X /* end of file */
- X *bl = buf = s; /* return blanks */
- X return NULL;
- X }
- X
- X field = i; /* field starts at next char */
- X NEXT(s,i,a) = c; /* all other cases will want this char */
- X
- X /*
- X * first handle trivial cases
- X */
- X if (c == '\n') {
- X /* end of line */
- X NEXT(s,i,a) = '\0'; /* terminate string */
- X *bl = s;
- X buf = s;
- X return s+field; /* return the newline */
- X }
- X if (c == '#') {
- X /* comment to end of line */
- X do {
- X NEXT(s,i,a) = c = getc(stream);
- X } while (c != '\n');
- X NEXT(s,i,a) = '\0'; /* terminate the string */
- X buf = s;
- X *bl = s;
- X return s+field; /* return the entire comment, plus newline */
- X }
- X /*
- X * now scan for the end of the field, as signified by a character
- X * which is a space, tab, newline or '#'. Characters inside double
- X * quotes don't count, and " can be escaped in a quote as \".
- X */
- X if (c == '"') {
- X inquote = 1; /* first char is a quote */
- X }
- X for (;;) {
- X c = getc(stream); /* get next character */
- X if (inquote) {
- X /* in a quote */
- X if (c == '"') {
- X inquote = 0; /* end of quote */
- X } else if (c == '\\') {
- X /* escape next char in quote */
- X NEXT(s,i,a) = c;
- X if ((c = getc(stream)) == EOF || c == '\n') {
- X break; /* though watch out for EOF and newline */
- X }
- X } else if (c == EOF || c == '\n') {
- X break; /* EOF or end of line in string */
- X }
- X } else {
- X /* not in a quote: EOF, space or comment ends field */
- X /* trap error on read */
- X if ((c == EOF && !ferror(stream)) || isspace(c) || c == '#') {
- X NEXT(s,i,a) = '\0';
- X (void) ungetc(c, stream); /* put character back */
- X buf = s;
- X *bl = s;
- X return s+field;
- X }
- X if (c == EOF) {
- X break; /* must have been a read error */
- X }
- X if (c == '"') {
- X inquote = 1;
- X }
- X }
- X NEXT(s,i,a) = c; /* put the char in the field */
- X }
- X
- X /*
- X * if we reach this point, then a quote was not terminated, or there was
- X * a read error.
- X */
- X if (ferror(stream)) {
- X panic(EX_IOERR, "%s: read error in %s: %m\n", program, ttysfile);
- X /*NOTREACHED*/
- X }
- X panic(EX_DATAERR, "%s: unterminated quote in %s\n", program, ttysfile);
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * write out the blanks, followed by the field, to the stream
- X */
- Xvoid
- Xputfield(bl, field, stream)
- X char *bl;
- X char *field;
- X register FILE *stream;
- X{
- X (void) fputs(bl, stream); /* blanks go first */
- X (void) fputs(field, stream);
- X if (ferror(stream)) {
- X panic(EX_IOERR, "%s: error writing to %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X}
- X
- X/*
- X * malloc and realloc with panics on error
- X */
- Xchar *
- Xxmalloc(size)
- X unsigned size;
- X{
- X register char *buf;
- X extern char *malloc();
- X
- X buf = malloc(size);
- X if (buf == NULL) {
- X panic(EX_OSERR, "%s: could not malloc: %m\n", program);
- X /*NOTREACHED*/
- X }
- X return buf;
- X}
- X
- Xchar *
- Xxrealloc(oldbuf, size)
- X char *oldbuf;
- X unsigned size;
- X{
- X register char *newbuf;
- X extern char *realloc();
- X
- X newbuf = realloc(oldbuf, size);
- X if (newbuf == NULL) {
- X panic(EX_OSERR, "%s: could not realloc: %m\n", program);
- X /*NOTREACHED*/
- X }
- X return newbuf;
- X}
- X
- X/*
- X * signals are sent here to generate an error.
- X */
- Xint
- Xinterupt()
- X{
- X panic(EX_SOFTWARE, "\nsignal\n");
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * process a panic string and exit with the given exit code
- X * format is as with printf, only there are no numeric codes
- X * within fields, and the %m field is replaced by a string
- X * representing the current error.
- X */
- X/*VARARGS2*/
- Xvoid
- Xpanic(exitstat, fmt, args)
- X int exitstat; /* call exit with this value */
- X char *fmt; /* printf-like format string */
- X char args; /* anchor for the rest of the args */
- X{
- X register int c; /* current format char */
- X register char *ap = &args; /* pointer to args */
- X register int i; /* index */
- X char *itoa();
- X
- X /*
- X * process the format string
- X */
- X while (c = *fmt++) if (c != '%') {
- X /* not a percent field, just output the current char */
- X (void) putc(c, stderr);
- X } else {
- X switch(*fmt++) {
- X case 'm': /* print the current error */
- X if (errno >= sys_nerr || errno < 0) {
- X /* outside of the range of known errors */
- X (void) fputs("Error ", stderr);
- X (void) fputs(itoa((unsigned)errno, 10), stderr);
- X } else {
- X (void) fputs(sys_errlist[errno], stderr);
- X }
- X break;
- X case 's': /* output a string */
- X (void) fputs(*(char **)ap, stderr);
- X ap += sizeof(char *);
- X break;
- X case 'c': /* output a single character */
- X (void) putc(*(int *)ap, stderr);
- X ap += sizeof(int);
- X break;
- X case 'd': /* output a signed decimal integer */
- X i = *(int *)ap;
- X if (i < 0) {
- X (void) putc('-', stderr);
- X i = -i;
- X }
- X (void) fputs(itoa((unsigned)i, 10), stderr);
- X ap += sizeof(int);
- X break;
- X case 'u': /* output an unsigned decimal interger */
- X (void) fputs(itoa(*(unsigned *)ap, 10), stderr);
- X ap += sizeof(unsigned);
- X break;
- X case 'o': /* an unsigned octal integer */
- X (void) fputs(itoa(*(unsigned *)ap, 8), stderr);
- X ap += sizeof(unsigned);
- X break;
- X case 'x': /* an unsigned hexadecimal integer */
- X (void) fputs(itoa(*(unsigned *)ap, 16), stderr);
- X ap += sizeof(char *);
- X break;
- X case '%': /* a percent sign */
- X (void) putc('%', stderr);
- X }
- X }
- X /*
- X * if temp file open, remove it.
- X */
- X if (temp) {
- X (void) fclose(temp);
- X (void) unlink(tempfile);
- X }
- X exit(exitstat);
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * convert an integer to a string representation in the given base
- X */
- Xchar *
- Xitoa(n, base)
- X register unsigned n; /* number to print */
- X register int base; /* base to print it in */
- X{
- X static char buf[64]; /* big enough to store any reasonable number */
- X register char *p; /* pointer to buf */
- X
- X p = buf+64; /* point to end, and work back */
- X *--p = '\0'; /* null terminate the string */
- X
- X /*
- X * fill in buf until we are done
- X */
- X while (n) {
- X *--p = "0123456789abcdefghijklmnopqrstuvwxyz"[n%base];
- X n /= base;
- X }
- X if (*p == '\0') {
- X *--p = '0'; /* handle case of the number 0 */
- X }
- X return p; /* return the value; */
- X}
- EOF
- if test $? -ne 0; then
- echo " WARNING: status $? returned by sed"
- fi
- else
- echo " WARNING: \"enable.c\" already exists, will not overwrite"
- fi
-
- if test ! -f "enable42.c"; then
- echo " file - \"enable42.c\""
- sed 's/^X//' > "enable42.c" <<\EOF
- X/*
- X * ENABLE/DISABLE -- turn on/off tty lines (non-4.3BSD version)
- X *
- X * $Header: enable42.c,v 1.3 87/03/06 21:14:23 tron Exp $
- X * $Log: enable42.c,v $
- X * Revision 1.3 87/03/06 21:14:23 tron
- X * no uid_t type in 4.2BSD.
- X *
- X * Revision 1.2 87/03/06 20:47:51 tron
- X * Cleaned up panic exitcodes.
- X *
- X * Revision 1.1 87/03/06 19:47:30 tron
- X * cleanup, readied for release, installed code to handle signals.
- X *
- X * Revision 1.0 87/03/06 18:24:48 tron
- X * 4.3BSD enable modified for other UNIX systems
- X *
- X * Revision 1.1 87/03/06 05:13:43 tron
- X * found one bug, incorrectly computing basename of program.
- X *
- X * Revision 1.0 87/03/06 05:09:29 tron
- X * Initial revision
- X *
- X * usage:
- X * enable ttyline ...
- X * disable ttyline ...
- X *
- X * ttyline may be of the form "ttyname" or "/dev/ttyname" in which case
- X * "/dev/" is stripped off.
- X */
- X
- X#ifndef lint
- Xstatic char rcsid[] = "$Header: enable42.c,v 1.3 87/03/06 21:14:23 tron Exp $";
- X#endif
- X
- X#include <stdio.h>
- X#include <sysexits.h>
- X#include <sys/types.h>
- X#include <sys/file.h>
- X#include <sys/param.h>
- X#include <sys/stat.h>
- X#include <signal.h>
- X#include <grp.h>
- X#include <strings.h>
- X#include <ctype.h>
- X
- Xchar dev_prefix[] = "/dev/"; /* prefix for tty special files */
- Xchar ttysfile[] = "/etc/ttys"; /* the file we are interested in */
- Xchar tempfile[] = "/etc/nttys"; /* lock file, temp file for changes */
- Xchar backup[] = "/etc/ottys"; /* in case of error, retain previous copy */
- X
- X#ifndef AUTHGROUP
- X# define AUTHGROUP "operator"
- X#endif AUTHGROUP
- X
- Xchar auth_group[] = AUTHGROUP; /* only root or users in this group are ok */
- X
- X#define MAXLINE 64 /* maximum size for a ttys file line */
- X
- Xchar *program; /* name this program was run as */
- X
- Xchar *xmalloc(); /* malloc(3) with panic on error */
- Xchar *getline(); /* get next line from the ttys file */
- Xvoid putline(); /* write out line to the temp file */
- Xvoid panic(); /* write error message and quit */
- Xvoid security_check(); /* panic if user is not authorized for this */
- Xint interupt(); /* routine to handle signals */
- XFILE *temp = NULL; /* temp file; if open, unlink on error */
- X
- Xextern int errno; /* see intro(2) */
- Xextern int sys_nerr; /* number of known errors */
- Xextern char *sys_errlist[]; /* description of known errors */
- X
- Xvoid
- Xmain(argc, argv)
- X int argc; /* number of arguments */
- X char *argv[]; /* vector of arguments */
- X{
- X register char **ap; /* pointer to arguments list */
- X char *argmap; /* map of hits in tty file */
- X FILE *ttys; /* ttys file */
- X int fd; /* used for opening temp file */
- X struct stat statbuf; /* status on ttys file */
- X register char *line; /* current line from the ttys file */
- X char to; /* how to set the status line */
- X int i; /* index */
- X
- X /* first things first, trap signals */
- X (void) signal(SIGHUP, interupt);
- X (void) signal(SIGINT, interupt);
- X (void) signal(SIGTERM, interupt);
- X
- X /* get basename of argv[0] for the name of the program */
- X program = rindex(*argv, '/');
- X if (program == NULL) {
- X program = *argv;
- X } else {
- X program++;
- X }
- X
- X /* make sure the user is authorized to do this */
- X security_check();
- X
- X /* what are we doing, anyway? */
- X if (strcmp(program, "enable") == 0) {
- X /* enable changes from off to on */
- X to = '1';
- X } else {
- X /* otherwise assume disable, changes from on to off */
- X to = '0';
- X }
- X argv++; /* we don't care about argv[0] anymore */
- X argc--;
- X
- X if (*argv == NULL) {
- X panic(EX_USAGE, "usage: %s ttyline ...\n", program);
- X /*NOTREACHED*/
- X }
- X
- X /* allocate ttys hit map and zero it out */
- X argmap = xmalloc((unsigned)argc);
- X (void) bzero(argmap, argc);
- X
- X /*
- X * cleanup the argument list
- X */
- X for (ap = argv; *ap; ap++) {
- X /*
- X * if in dev directory, get basename
- X */
- X if ((*ap)[0] == '/') {
- X if (strncmp(*ap, dev_prefix, sizeof(dev_prefix) - 1) != 0) {
- X panic(EX_DATAERR, "%s: illegal argument: %s\n", program, *ap);
- X /*NOTREACHED*/
- X }
- X *ap += sizeof(dev_prefix)-1;
- X }
- X }
- X
- X /*
- X * open the ttys file and get its mode
- X */
- X ttys = fopen(ttysfile, "r+");
- X if (ttys == NULL) {
- X panic(EX_OSFILE, "%s: could not open %s: %m\n", program, ttysfile);
- X /*NOTREACHED*/
- X }
- X if (fstat(fileno(ttys), &statbuf) < 0) {
- X panic(EX_OSFILE, "%s: could not stat %s: %m\n", program, ttysfile);
- X /*NOTREACHED*/
- X }
- X
- X /*
- X * open temp file exclusively to prevent simultaneous edits
- X */
- X fd = open(tempfile, O_WRONLY|O_CREAT|O_EXCL, statbuf.st_mode);
- X if (fd < 0) {
- X panic(EX_OSFILE, "%s: open failed on %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X temp = fdopen(fd, "w");
- X if (temp == NULL) {
- X panic(EX_OSERR, "%s: fdopen failed on %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X
- X /*
- X * loop through, grabbing fields from the ttys file and copying
- X * them to the temp file, altering the status fields as necessary
- X */
- X while (line = getline(ttys)) {
- X /* is this one of the lines we are interested in? */
- X for (ap = argv; *ap; ap++) {
- X if (strcmp(*ap, line+2) == 0) {
- X /* yes */
- X if (argmap[ap-argv] > 0) {
- X (void)fprintf(stderr,
- X "%s: warning: %s in %s twice\n",
- X program, *ap, ttysfile);
- X }
- X argmap[ap-argv] = 1;
- X if (line[0] == to) {
- X (void) fprintf(stderr, "%s: %s is already %s\n",
- X program, *ap, to=='1'? "on": "off");
- X }
- X line[0] = to;
- X break;
- X }
- X }
- X
- X /* output the field to the temp file */
- X putline(line, temp);
- X }
- X
- X /*
- X * check map for anything that wasn't found
- X */
- X for (i = 0; i < argc; i++) {
- X if (argmap[i] == 0) {
- X (void) fprintf(stderr, "%s: line %s not found in %s\n",
- X program, argv[i], ttysfile);
- X }
- X }
- X
- X /* finish everything up */
- X (void) fclose(ttys); /* close files */
- X if (fclose(temp) == EOF) {
- X panic(EX_OSERR, "%s: could not close %s: %m\n", program, temp);
- X /*NOTREACHED*/
- X }
- X (void) unlink(backup); /* remove previous backup */
- X if (link(ttysfile, backup) < 0) { /* create a new backup */
- X panic(EX_OSERR, "%s: link from %s to %s failed: %m\n",
- X program, ttysfile, backup);
- X /*NOTREACHED*/
- X } /* install new ttys file */
- X if (rename(tempfile, ttysfile)) {
- X panic(EX_OSERR, "%s: rename from %s to %s failed: %m\n",
- X program, tempfile, ttysfile);
- X /*NOTREACHED*/
- X }
- X if (kill(1, SIGHUP) < 0) { /* inform init of changes */
- X panic(EX_OSERR, "%s: failed to notify init: %m\n", program);
- X /*NOTREACHED*/
- X }
- X exit(0); /* all done, everything went okay (?) */
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * the user must either be root or be in the auth_group. Otherwise
- X * panic with a security violation.
- X */
- Xvoid
- Xsecurity_check()
- X{
- X register i; /* index */
- X register n; /* index limit */
- X int gidset[NGROUPS]; /* set of user's groups */
- X struct group *gr; /* group file entry */
- X register int gid; /* group we are searching for in gidset */
- X
- X /* are we root? */
- X if (getuid() == 0) return; /* yes */
- X
- X /* get id of authorized group */
- X gr = getgrnam(auth_group);
- X if (gr == NULL) {
- X panic(EX_SOFTWARE, "%s: no such group %s\n", program, auth_group);
- X /*NOTREACHED*/
- X }
- X gid = gr->gr_gid;
- X
- X n = getgroups(NGROUPS, gidset);
- X /* search for authorized group id */
- X for (i = 0; i < n; i++) {
- X if (gidset[i] == gid) return; /* user looks okay to me */
- X }
- X /* not found */
- X panic(EX_NOPERM, "%s: you are not allowed to run this program\n", program);
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * return the next line from the ttys file.
- X * panic if there is anything obviously wrong with it.
- X * return NULL on end of file.
- X */
- Xchar *
- Xgetline(stream)
- X FILE *stream; /* file to take the fields from */
- X{
- X static char buf[MAXLINE+1]; /* put the input line here */
- X register char *s; /* return value from fgets */
- X register int l;
- X
- X s = fgets(buf, MAXLINE, stream);
- X if (s == NULL && ferror(stream)) {
- X panic(EX_IOERR, "%s: read error in %s: %m\n", program, ttysfile);
- X /*NOTREACHED*/
- X }
- X if (s) {
- X l = strlen(s);
- X if (s[l-1] != '\n' || (l < 4 && (s[l-1] = '\0', 1))) {
- X panic(EX_DATAERR, "%s: illegal line in %s: %s\n",
- X program, ttysfile, s);
- X /*NOTREACHED*/
- X }
- X }
- X s[l-1] = '\0'; /* strip the newline */
- X return s;
- X}
- X
- X/*
- X * write out the given string to the file. panic on error.
- X */
- Xvoid
- Xputline(line, stream)
- X char *line;
- X FILE *stream;
- X{
- X (void) fputs(line, stream);
- X (void) putc('\n', stream);
- X if (ferror(stream)) {
- X panic(EX_IOERR, "%s: error writing to %s: %m\n", program, tempfile);
- X /*NOTREACHED*/
- X }
- X}
- X
- X/*
- X * malloc with panic on error
- X */
- Xchar *
- Xxmalloc(size)
- X unsigned size;
- X{
- X register char *buf;
- X extern char *malloc();
- X
- X buf = malloc(size);
- X if (buf == NULL) {
- X panic(EX_OSERR, "%s: could not malloc: %m\n", program);
- X /*NOTREACHED*/
- X }
- X return buf;
- X}
- X
- X/*
- X * signals are sent here to generate an error.
- X */
- Xint
- Xinterupt()
- X{
- X panic(EX_SOFTWARE, "\nsignal\n");
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * process a panic string and exit with the given exit code
- X * format is as with printf, only there are no numeric codes
- X * within fields, and the %m field is replaced by a string
- X * representing the current error.
- X */
- X/*VARARGS2*/
- Xvoid
- Xpanic(exitstat, fmt, args)
- X int exitstat; /* call exit with this value */
- X char *fmt; /* printf-like format string */
- X char args; /* anchor for the rest of the args */
- X{
- X register int c; /* current format char */
- X register char *ap = &args; /* pointer to args */
- X register int i; /* index */
- X char *itoa();
- X
- X /*
- X * process the format string
- X */
- X while (c = *fmt++) if (c != '%') {
- X /* not a percent field, just output the current char */
- X (void) putc(c, stderr);
- X } else {
- X switch(*fmt++) {
- X case 'm': /* print the current error */
- X if (errno >= sys_nerr || errno < 0) {
- X /* outside of the range of known errors */
- X (void) fputs("Error ", stderr);
- X (void) fputs(itoa((unsigned)errno, 10), stderr);
- X } else {
- X (void) fputs(sys_errlist[errno], stderr);
- X }
- X break;
- X case 's': /* output a string */
- X (void) fputs(*(char **)ap, stderr);
- X ap += sizeof(char *);
- X break;
- X case 'c': /* output a single character */
- X (void) putc(*(int *)ap, stderr);
- X ap += sizeof(int);
- X break;
- X case 'd': /* output a signed decimal integer */
- X i = *(int *)ap;
- X if (i < 0) {
- X (void) putc('-', stderr);
- X i = -i;
- X }
- X (void) fputs(itoa((unsigned)i, 10), stderr);
- X ap += sizeof(int);
- X break;
- X case 'u': /* output an unsigned decimal interger */
- X (void) fputs(itoa(*(unsigned *)ap, 10), stderr);
- X ap += sizeof(unsigned);
- X break;
- X case 'o': /* an unsigned octal integer */
- X (void) fputs(itoa(*(unsigned *)ap, 8), stderr);
- X ap += sizeof(unsigned);
- X break;
- X case 'x': /* an unsigned hexadecimal integer */
- X (void) fputs(itoa(*(unsigned *)ap, 16), stderr);
- X ap += sizeof(char *);
- X break;
- X case '%': /* a percent sign */
- X (void) putc('%', stderr);
- X }
- X }
- X /*
- X * if temp file open, remove it.
- X */
- X if (temp) {
- X (void) fclose(temp);
- X (void) unlink(tempfile);
- X }
- X exit(exitstat);
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * convert an integer to a string representation in the given base
- X */
- Xchar *
- Xitoa(n, base)
- X register unsigned n; /* number to print */
- X register int base; /* base to print it in */
- X{
- X static char buf[64]; /* big enough to store any reasonable number */
- X register char *p; /* pointer to buf */
- X
- X p = buf+64; /* point to end, and work back */
- X *--p = '\0'; /* null terminate the string */
- X
- X /*
- X * fill in buf until we are done
- X */
- X while (n) {
- X *--p = "0123456789abcdefghijklmnopqrstuvwxyz"[n%base];
- X n /= base;
- X }
- X if (*p == '\0') {
- X *--p = '0'; /* handle case of the number 0 */
- X }
- X return p; /* return the value; */
- X}
- EOF
- if test $? -ne 0; then
- echo " WARNING: status $? returned by sed"
- fi
- else
- echo " WARNING: \"enable42.c\" already exists, will not overwrite"
- fi
-
- echo "Inspecting for damage in transit..."
- ERROR=
- EXPECT=`wc -c "README" 2>&1 | awk '{print 0 + $1}'`
- if test $EXPECT -ne 812; then
- echo " ERROR: \"README\": expected 812 chars, got $EXPECT"
- ERROR=1
- fi
- EXPECT=`wc -c "Makefile" 2>&1 | awk '{print 0 + $1}'`
- if test $EXPECT -ne 928; then
- echo " ERROR: \"Makefile\": expected 928 chars, got $EXPECT"
- ERROR=1
- fi
- EXPECT=`wc -c "enable.man" 2>&1 | awk '{print 0 + $1}'`
- if test $EXPECT -ne 1356; then
- echo " ERROR: \"enable.man\": expected 1356 chars, got $EXPECT"
- ERROR=1
- fi
- EXPECT=`wc -c "enable.c" 2>&1 | awk '{print 0 + $1}'`
- if test $EXPECT -ne 16200; then
- echo " ERROR: \"enable.c\": expected 16200 chars, got $EXPECT"
- ERROR=1
- fi
- EXPECT=`wc -c "enable42.c" 2>&1 | awk '{print 0 + $1}'`
- if test $EXPECT -ne 11781; then
- echo " ERROR: \"enable42.c\": expected 11781 chars, got $EXPECT"
- ERROR=1
- fi
-
-
- if test -z "$ERROR"; then echo " No errors."; fi
-
- # end of last shar archive out of 1
-
- exit 0
-
- --
-
- Rich $alz
- Cronus Project, BBN Labs rsalz@bbn.com
- Moderator, comp.sources.unix sources@uunet.uu.net
-