[unix-pc.sources] REPOST

ignatz@chinet.chi.il.us (Dave Ihnat) (12/19/88)

As I said in the group unix-pc.general, I goofed.
Last minute upgrades == last minute goofs.  Anyway, it's for the better--this
has some extensions and modifications that improve it (in my view.)

It's small, so it's a total repost; just discard the last version, if my cancel
didn't get to it before you saved it.

	-Dave Ihnat
===================== Hier Schneiden ============================
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Ifile.sh Makefile pwcntl.1 pwcntl.4 pwcntl.c pwcntl.h
#   pwcntl.notes
# Wrapped by ignatz@chinet on Sun Dec 18 13:33:11 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Ifile.sh -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Ifile.sh\"
else
echo shar: Extracting \"Ifile.sh\" \(705 characters\)
sed "s/^X//" >Ifile.sh <<'END_OF_Ifile.sh'
Xex - $1 << EOF
X1,\$s/^PC/xPC/
X1,\$s/^BC/xBC/
X1,\$s/^UP/xUP/
X1,\$s/^ospeed/xospeed/
X1,\$s/^LINES/xLINES/
X1,\$s/^COLS/xCOLS/
X1,\$s/^tgetflag/xtgetflag/
X1,\$s/^tgetent/xtgetent/
X1,\$s/^tgetstr/xtgetstr/
X1,\$s/^tgetnum/xtgetnum/
X1,\$s/^tgoto/xtgoto/
X1,\$s/^tputs/xtputs/
X1,\$s/^wrefresh/xwrefresh/
X1,\$s/^initscr/xinitscr/
X1,\$s/^cbreak/xcbreak/
X1,\$s/^nl/xnl/
X1,\$s/^flushinp/xflushinp/
X1,\$s/^noecho/xnoecho/
X1,\$s/^savetty/xsavetty/
X1,\$s/^resetty/xresetty/
X1,\$s/^echo/xecho/
X1,\$s/^nocbreak/xnocbreak/
X1,\$s/^nonl/xnonl/
X1,\$s/^keypad/xkeypad/
X1,\$s/^endwin/xendwin/
X1,\$s/^printw/xprintw/
X1,\$s/^fixterm/xfixterm/
X1,\$s/^resetterm/xresetterm/
X1,\$s/^setterm/xsetterm/
X1,\$s/^baudrate/xbaudrate/
Xw
Xq
XEOF
END_OF_Ifile.sh
if test 705 -ne `wc -c <Ifile.sh`; then
    echo shar: \"Ifile.sh\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(636 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X# @(#)Makefile	1.1 12/18/88
X
X# Normal distribution
XCFLAGS = -O
XLDFLAGS = -s
XCRT= /lib/crt0s.o
XLIBC= ./shlib_c.ifile
X
X# Debug version
X#CFLAGS = -g -DSHELL
X#LDFLAGS = -lg
X#CRT=/lib/crt0.o
X#LIBC= /lib/libc.a
X
XOBJECTS = 	pwcntl.o \
X		setvbuf.o \
X		doprnt.o
X
Xpwcntl:	$(OBJECTS) shlib_c.ifile
X	$(LD) $(LDFLAGS) -o pwcntl $(OBJECTS) -lcurses $(CRT) $(LIBC)
X
Xsetvbuf.o : /lib/libc.a
X	ar xv /lib/libc.a setvbuf.o
X
Xdoprnt.o : /lib/libc.a
X	ar xv /lib/libc.a doprnt.o
X
X# Thanks to Emmet Gray for his ifile shellscript and scheme
Xshlib_c.ifile:
X	cp /lib/shlib.ifile shlib_c.ifile
X	sh Ifile.sh shlib_c.ifile
X
Xclean:
X	- rm -f pwcntl shlib_c.ifile *.o
END_OF_Makefile
if test 636 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pwcntl.1 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pwcntl.1\"
else
echo shar: Extracting \"pwcntl.1\" \(8024 characters\)
sed "s/^X//" >pwcntl.1 <<'END_OF_pwcntl.1'
X.\" @(#)pwcntl.1	1.3 12/18/88
X.TH PWCNTL 1 " Public Domain Utilities"
X.SH NAME
Xpwcntl \- dump and administer the Unix PC/7300/3B1 /etc/pwcntl file
X.SH SYNOPSIS
X\fBpwcntl -d\fP [ \fB-i\fP][ \fB-s\fP [\fBname\fP|\fBlast\fP]] [ \fB-c\fP][ \fB-v\fP][ \fB-f\fP filename]
X.br
X\fBpwcntl\fP [ \fB-e\fP | \fB-u\fP][ \fB-c\fP][ \fB-v\fP][\fB-f\fP filename]
X.SH DESCRIPTION
XThere is a file on the AT&T Unix PC, or 7300, or 3B1--take your pick--which
Xis maintained by the normal system login (and/or user administration)
Xprograms which provides certain information about login attempts.  This
Xprogram provides a means of examining and maintaining this file--a
Xcapability not provided by AT&T (which isn't surprising, as they didn't
Xdocument it, either.)
X.PP
XThe program has two main modes of operation.  One mode provides for a
Xnon-interactive line-oriented dump of the information in the file, suitable
Xfor printing (or framing, I suppose, if you're so inclined.) Of course,
Xit has several modifying options to tailor the output.
XThe second mode provides for an interactive perusal of the file, with a
Xfull-screen display.  When in this mode with the update option
Xspecified, the user may perform file maintenance functions, including
Xthe deletion of records (presumably to clean up dead users or bogus
Xlogin information), correction of the space indication, etc.
X.PP
XWith either mode, or simply by itself, there is an option to force
Xrecalculation of space usage for every valid login recorded.  (Note that
Xspace calculations are only for the login's home directory; any directory
Xstructures or files that belong to that login, but are resident elsewhere,
Xwill not enter into the calculation.)  The provision exists at program
Xgeneration to exempt certain logins from the space calculation--for
Xinstance, \fIroot\fP would appear to own the whole system, since its home
Xdirectory is usually recorded as \fB/\fP.  Similarly, uucp access logins
Xcommonly have the directory \fB/usr/spool/uucppublic\fP as the home directory;
Xcalculating the space used would be meaningless.
X.PP
XA number of checks are made to assure and indicate validity of the login
Xinformation in the file.  As all login attempts--including those for
Xnon-existent logins--are logged, there can be a potentially large number of
Xthese records.  Normally they only indicate mistyped valid login attempts,
Xbut this does provide some intruder detection.
X.SS Options
XThe space calculation and file options are valid for either the dump or
Xinteractive modes of operation; all others are valid with only one or the
Xother, as indicated.  Options may be provided in any order.
X.TP
X\fB-d\fP
X.br
XDump.  80-column output is to stdout.
X.TP
X\fB-e\fP
X.br
XExamine.  Allows interactive examination on a per-record full-screen basis.
X.TP
X\fB-u\fP
X.br
XUpdate.  Allows changes to the file data as indicated on the screen.  Valid only
Xwith interactive mode (i.e., no \fB-d\fP option.)
X.TP
X\fB-i\fP
X.br
XInvalid logins only (Only with 'd' option).  Only those file records identified as bad logins (\fBL\fP flag at the end of the line), or with User ID values that
Xdon't match the current value in the password file (\fBI\fP flag) are dumped.
X.TP
X\fB-c\fP
X.br
XCalculate space.  Either standalone, or before further processing for either
Xdumps or interactive sessions, it scans the file and performs space calculations
Xfor only the valid records, updating the file upon completion of the scan.
X(This can take some time.)
X.TP
X\fB-v\fP
X.br
XVerbose mode.  Provides more detailed informative messages, particularly when
Xperforming long background tasks such as global space recalculate.
X.TP
X\fB-s\fP X
X.br
XSort.  Must have either X=name, for by Name order, or X=last, for Lastlog order.
X(Only with 'd' option.)  Actually, the flags may be shortened to only \fIn\fP
Xor \fIl\fP.
X.TP
X\fB-f\fP filename
X.br
XUse alternate filename; default is /etc/pwcntl.  It may be preferable, while
Xlearning how to use the program, to copy /etc/pwcntl to another file and test
Xon that.
X.SS Screen
XThe interactive screen is quite self-explanatory.  The commands that perform
Xmodifications on the file are not honored unless the update option is selected;
Xthis is indicated by offering them on the menu.
X.PP
XCommands always honored are:
X.TP
X\fBN\fP
XNext record
X.TP
X\fBP\fP
XPrevious record
X.TP
X\fBT\fP
XTop of file
X.TP
X\fBB\fP
XBottom of file
X.TP
X\fBR\fP
XRefresh screen
X.TP
X\fBQ\fP
XQuit.  Any changes will be recorded in the file.  To abort changes, send an
Xinterrupt or quit signal.
X.PP
XModification commands are:
X.TP
X\fBD\fP
XDelete.  Flagged only; takes effect on exit.
X.TP
X\fBU
XUndelete.  Restores record for rewrite on close.
X.TP
X\fP\fBE\fP
XExpert toggle.  Toggles the state of the Expert flag.
X.TP
X\fBS\fP
XSpace calculation.  Calculates space used in the home directory of this user
Xonly.
X.TP
X\fBI\fP
XUser ID correction.  If the user ID in the password file has changed since this
Xrecord was logged, the recorded ID will be incorrect.  This command updates the
Xrecorded user ID from the password file information.
X.SS Output
XThe output from the dump option is fairly self-explanatory; it is simply a
Xformatted dump of the information contained in the /etc/pwcntl file.  One
Xexception is the flag field.  If the record is abnormal--either a bad login
Xattempt, or a mismatched user ID--then a one-character flag will be appended to
Xthe end of the line.  An \fBL\fP indicates a bad login attempt, i.e., the login
Xname recorded doesn't exist in the password file.  An \fBI\fP indicates a bad
Xuser id for a valid login.
X.PP
XOutput was designed to fit on an 80-column page.  However, two conditions may
Xcause this to overwrap.  First, a user may type anything at the login prompt;
Xthus, an excessively long name, or a name containing control characters, may
Xcause the output to exceed, or fall short, of its intended format length.
XSecondly, an excessive amount of disk space allocation--for instance, if root
Xowns a disproportionately large number of files, or you've a very large source
Xdatabase owned by a caretaker--may cause the space allocation field to expand
Xpast its bounds.  These effects are, however, purely cosmetic, and are the
Xexception rather than the norm.
X.SH FILES
X/etc/pwcntl
X.SH SEE ALSO
Xpwcntl(4)
X.SH WARNINGS
XThis program should \fBnot\fP be run setuid or setgid.  Only execute from an
Xaccount with appropriate permissions.  Normal users should not be permitted to
Xeven peruse the file, since often passwords may be mistyped instead of logins.
X.PP
XNotice that the program has the same name as the file it is supposed to manage.
XThis is deliberate--it's easy to remember, and if you just bury this in /etc,
Xyou stand a chance of losing it whenever you have to move to a new upgrade, or
Xhave to recover your disk.  If you don't like that, feel free to rename it
Xlocally.  Whatever--\fBdon't\fP blindly copy this into /etc!
X.SH DIAGNOSTICS
XA number of informative text messages are emitted in case of exceptions.
X.SH CAVEATS
XChanges during an update or space calculation session may be aborted by an
Xinterrupt or quit signal, and file integrity is assured.  Once the normal
Xprogram exit is taken, however, the changes are permanent.  No backup is
Xtaken of the file by the program.
X.PP
XWhile running the program for update or space calculation, normal logging is
Xprohibited.
X.PP
XI deliberately didn't allow creation of records.  This will happen with the
Xfirst login of an unknown valid user, anyway.  In a similar manner, I didn't
Xallow unrestricted editing of information in the file--it's logging information,
Xand shouldn't be fudged.
X.PP
XThis program hasn't yet undergone extensive testing-by-use.  Please notify me
Xat ignatz@homebru.chi.il.us of any bugs (and fixes, if you have them),
Xenhancements, etc.  (Attaboys are always welcome, too...)
X.SH CREDITS
XThanks to Mike Ditto and Dave Wexelblat (and anyone else I may
Xforget--sorry) for responding to my initial query as to the structure of
Xthe /etc/pwcntl file.  This saved me a couple of hours of poking about
Xthat I didn't really want to spend.
END_OF_pwcntl.1
if test 8024 -ne `wc -c <pwcntl.1`; then
    echo shar: \"pwcntl.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pwcntl.4 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pwcntl.4\"
else
echo shar: Extracting \"pwcntl.4\" \(1129 characters\)
sed "s/^X//" >pwcntl.4 <<'END_OF_pwcntl.4'
X.\" @(#)pwcntl.4	1.1 12/18/88
X.TH PWCNTL 4 "#Public Domain Utilities"
X.SH NAME
Xpwcntl \- Unix PC/7300/3B1 /etc/pwcntl file format
X.SH SYNOPSIS
X\fB#include <sys/pwcntl.h>\fP
X.SH DESCRIPTION
XThe file \fB/etc/pwcntl\fP holds login information gleaned by \fIlogin\fP,
Xand maintained by the UA utility \fIUlogin\fP.  The structure, as defined by
Xthe file \fIpwcntl.h\fP, is as follows:
X.nf
X
X	struct  pwrec {
X		char logon[8];	/* user name from login arg1 */
X		int uid;	/* uid from /etc/passwd */
X		char expert;	/* Y/N flag for expert mode */
X		char flg;	/* unused (pad) */
X		time_t timeon;	/* time of most recent login */
X		time_t timecr;	/* time of first login */
X		long space;	/* disk space used in blocks,
X				 * only used in Ulogin after Compute Space
X				 * has been selected.
X				 */
X	};
X
X.fi
X.SH FILES
X/etc/pwcntl
X.SH SEE ALSO
Xpwcntl(1)
X.SH CAVEATS
XThis file is not distributed with the Unix PC/7300/3B1 software, but rather
Xis derived from perusal of the file itself.
X.SH CREDITS
XThanks to Mike Ditto and Dave Wexelblat (and anyone else I may
Xforget--sorry) for responding to my initial query as to the structure of
Xthis file.
END_OF_pwcntl.4
if test 1129 -ne `wc -c <pwcntl.4`; then
    echo shar: \"pwcntl.4\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pwcntl.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pwcntl.c\"
else
echo shar: Extracting \"pwcntl.c\" \(23716 characters\)
sed "s/^X//" >pwcntl.c <<'END_OF_pwcntl.c'
X/* @(#)pwcntl.c	1.2 12/18/88 */
X/*
X * pwcntl - AT&T 7300/3B1 /etc/pwcntl file manager utilty
X *
X * Author:	David M. Ihnat
X *		ignatz@homebru.chi.il.us
X *		(312) 784-4544
X *
X *		December 11, 1988
X *
X * This is a work donated to the Public Domain.  As such, it may be used in any
X * way desired.  I do request that you at least allow the credit (or blame) to
X * remain mine.
X *
X * See the manual page for detailed discussion of the features and modes of
X * operation; important assumptions and design approach information should
X * be kept here in the source file.
X *
X * The major assumption made is that the file will always be small enough
X * to be kept in memory.  This is not unreasonable, since each record in
X * the file is only 26 bytes in length, and on the 7300/3B1--a small box--
X * even, say, 1000 records--an outrageous number!--would only take 26000
X * bytes to hold in memory.  Even a 2000 record file would be much less
X * than 64K.  However, the first draft of the program was implemented using
X * tempfiles, and the conversion to a memory-based model took 30 minutes.
X * Going the other way would be similarly easy, with the exception of the
X * dump sorting.
X *
X * Protection from unauthorized modification of the file is provided only
X * by normal Unix file permissions; the program must be run by an
X * appropriately privileged user.  DON'T run it setuid/setgid; that would
X * be tantamount to leaving /etc/pwcntl 666.
X *
X * When enabled for update or space calculation, the file is locked.  This
X * effectively prevents update during execution, and maintains a consistent
X * view of the file from the maintainer's point of view, but it will
X * interfere with normal logging of user access attempts during the program
X * execution.
X * 
X * The information returned from getpwnam is assumed to always be correct,
X * and is used to indicate bad login attempts or user IDs.
X *
X * It is assumed that a time of last login of 0L indicates NEVER LOGGED IN.
X * should this be false, please correct it and notify me by email.
X * 
X * The ifile provided is for 3.51.  If you're running 3.50, it should work;
X * if it doesn't, the changes are fairly simple--it's the old curses thing.
X * Just compare your /lib/shlib.ifile with this one, and make the changes
X * for your system. (Even though the program is only for the 3B1--so
X * portability isn't an issue--I decided I didn't want to play with TAM
X * directly.)
X * 
X * Outside of the above, the program is quite straightforward.  Please mail any
X * bug reports, comments, or corrections to me, and I'll attempt to keep a
X * current version.
X *
X * CREDITS
X * Thanks to Mike Ditto and Dave Wexelblat (and anyone else I may
X * forget--sorry) for responding to my initial query as to the structure of
X * the /etc/pwcntl file.  This saved me a couple of hours of poking about
X * that I didn't really want to spend.
X */
X
X#define	PATCHLEVEL	0
X
X#include <sys/types.h>
X#include <string.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <sys/stat.h>
X#include <time.h>
X#include <curses.h>
X#include <sys/signal.h>
X#include <memory.h>
X#include <pwd.h>
X#include <errno.h>
X#include "pwcntl.h"
X
X/* Some system calls and functions used in multiple places */
Xextern void exit();
Xextern void perror();
Xextern unsigned sleep();
X
Xtypedef struct {
X	int y;
X	int x;
X	char *label;
X} SCRFRM;
X
Xtypedef struct {
X	int y;
X	int x;
X	char *fmt;
X} SCRDATA;
X
X/* Following are the fixed screen form definitions */
X#define UPDCMDS	9
X
XSCRFRM sform[] = {
X	{  0,  0, "** pwcntl data **        Total records: " },
X	{  1,  0, "Current record: " },
X	{  2,  0, "logon: " },
X	{  2, 40,"Uid: "},
X	{  2, 60,"Expert: " },
X	{  4,  0, "Login created: " },
X	{  4, 40,"Last Login: " },
X	{  6,  0, "Space Used (blocks): " },
X	{  8,  0, "Commands: (N)ext, (P)revious, (T)op, (B)ottom, (R)efresh, (Q)uit" },
X	{  9,  0, "          (D)elete, (U)ndelete, (E)xpert toggle, (S)pace calculate, (I)uId fix" },
X	{ 21,  0, "Status: " },
X	{ 23,  0, "Command: " },
X	{ -1, -1,(char *)NULL }
X};
X
X#define	CURREC		0
X#define	LOGIN		CURREC+1
X#define	RECCNT		LOGIN+1
X#define	UID		RECCNT+1
X#define	EXPERT		UID+1
X#define	CREATE		EXPERT+1
X#define LASTLOG		CREATE+1
X#define	SPACE_USED	LASTLOG+1
X#define	DELETED		SPACE_USED+1
X
X#define	BAD_LOGIN	DELETED+1
X#define	BAD_UID		BAD_LOGIN+1
X
X/* Some positions we need... */
X#define CMDIN_Y		23
X#define	CMDIN_X		9
X
X#define STATUS_Y	21
X#define STATUS_X	8
X
X#define	RECSTAT_Y	3
X#define	RECSTAT_X	0
X
X#define CURREC_Y	1
X#define CURREC_X	16
X
X/* Data Elements */
XSCRDATA data[] = {
X	{  1, 16, "%5.5d" },
X	{  2,  7, "%-20.20s" },		/* Login ID (recorded) */
X	{  0, 40, "%5.5d" },		/* Record count (permanent) */
X	{  2, 45, "%3.3d" },		/* UID (recorded) */
X	{  2, 68, "%c" },		/* Expert Flag */
X	{  4, 15, "%24.24s" },		/* Creation Date */
X	{  4, 52, "%24.24s" },		/* Last login date */
X	{  6, 21, "%6.6ld" },		/* Space Used */
X	{  7,  0, "%7.7s" },		/* DELETED flag */
X
X	/* OPTIONAL DATA FIELDS */
X	{  3, 0, "**BAD LOGON**" },
X	{  3, 40, "**BAD UID (should be %d)**" }
X};
X
X/* Sort information */
Xint (*comp_routine)() = NULL;
X
X/* File access status indicators and data */
X#define	F_NORM	0
X#define	F_TOP	1
X#define F_EOF	2
X
X/* Data disposition on termination or dump to file */
X#define	KEEP	0
X#define	FREE	1
X
Xint fstatus;		/* This is the current status of the 'tempfile' */
Xchar *f_statext[] = {
X	"                                                     ",
X	"File at TOP",
X	"File at EOF",
X};
X
X/* Used for record validation, and space calculation */
Xstruct passwd *passwdrec;
Xstruct passwd *getpwnam();
X
X/*
X * Exception list.  On a per-site basis, there may be some users or login
X * name conventions for which it makes no sense to do a space calculation.
X * In this event, include either the full login name, or the initial unique 
X * portion of the name, in this list at compile time.  Their space usage will
X * be set to zero on any space recalculation.
X */
Xchar *nospace_list[] = {
X	"root",		/* Owns everything */
X	(char *)NULL	/* TERMINATOR  --  ALWAYS LAST */
X};
X
X/* To mark deleted entries, a rather unlikely size */
X#define DELREC	0x696969L
X
X/* The star of the show */
X#define	PWCNTL	"/etc/pwcntl"
Xchar *pw_fname = PWCNTL;
X
X#define	VERSION	"1.2 12/18/88"
X
Xstatic int updateflag = 0;
Xstatic int spaceflag  = 0;
Xstatic int dumpflag   = 0;
Xstatic int examflag   = 0;
Xstatic int invflag    = 0;
Xstatic int verbflag   = 0;
Xstatic int errflag    = 0;
X
Xstatic char interactive= 0;	/* Set as byproduct of update, or no dumpflag */
X
XFILE *oldfdes = (FILE *)NULL;
X
X/* File statistic and manipulation routines. */
Xint reccnt;		/* Count of valid records */
Xint currec;		/* Current record in file (index into datarec) */
X
Xstruct pwrec *data_recs;	/* Data structure holding the file data */
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X	register int ch;
X	extern char *optarg;
X
X	void dospace();
X	extern int by_name();
X	extern int by_login();
X
X	while((ch = getopt(argc,argv,"eicduvs:f:")) != EOF)
X		switch(ch)
X		{
X			case 'd':	/* Dump */
X				dumpflag = 1;
X				break;
X
X			case 'e':	/* Examine interactively */
X				examflag++;
X				break;
X
X			case 's':	/* Sorting */
X				if(*optarg == 'n' || *optarg == 'N')
X					comp_routine = by_name;
X
X				else
X				if(*optarg == 'l' || *optarg == 'L')
X					comp_routine = by_login;
X
X				else
X					errflag++;
X
X				break;
X
X			case 'i':	/* Invalid list only */
X				invflag++;
X				break;
X
X			case 'c':	/* Calculate space for all */
X				spaceflag++;
X				break;
X
X			case 'u':	/* Update */
X				updateflag = 1;
X				break;
X
X			case 'v':	/* Verbose (non-interactive */
X				verbflag++;
X				break;
X
X			case 'f':	/* Alternate file */
X				pw_fname = optarg;
X				break;
X			
X			case '?':	/* Huh? */
X				errflag++;
X		}
X		
X	/* A series of consistency checks */
X	if(updateflag)
X		examflag++;
X
X	if(!dumpflag && !examflag && !spaceflag)
X		errflag++;
X
X	if(updateflag && dumpflag)
X		errflag++;
X
X	if((comp_routine != NULL) && !dumpflag)
X		errflag++;
X
X	if(interactive && invflag)
X		errflag++;
X
X
X	if(errflag)
X		usage();
X
X	if((!dumpflag && !updateflag && !spaceflag) || updateflag )
X		interactive++;
X		
X	/* Only show update commands if in update mode */
X	if(!updateflag)
X		sform[UPDCMDS].label = "";
X
X	(void)printf("pwcntl Version %s\n",VERSION);
X
X	if(spaceflag)
X	{
X		dospace();
X		spaceflag = 0;	/* For exit processing */
X	}
X
X	if(dumpflag)
X		dumpit();
X	
X	if(examflag)
X		screenit();
X
X	return(0);
X}
X/**/
Xvoid
Xdospace()
X{
X	struct pwrec *pw;
X	register int index;
X
X	struct pwrec *getrec();
X	long spacerec();
X	void purge_data();
X	void load_file();
X	extern int end_proc();
X	extern int (*signal())();
X
X	if(verbflag)
X		printf("Doing space recalculation for all valid recorded logins.\n");
X	/*
X	 * As for the normal startup code.  Notice that we neither save, nor
X	 * restore, the signal values, even though they might get reset later
X	 * if this is in conjunction with an update.  No problem--no settings
X	 * are needful of being saved or restored in any case.
X	 */
X	for(index = 1; index <= SIGUSR2; index++)
X		(void)signal(index,end_proc);
X
X	updateflag +=2;	/* If in update, just increments; if not, are now */
X	load_file();	/* Will fail horribly and not return, if necessary */
X
X	pw = getrec();
X	while(fstatus == F_NORM || fstatus == F_TOP)
X	{
X		if(verbflag)
X		{
X			printf("%s...",pw->logon);
X			fflush(stdout);
X		}
X
X		passwdrec = getpwnam(pw->logon);
X
X		if(passwdrec == (struct passwd *)NULL)
X		{
X			if(verbflag)
X				printf("INVALID LOGIN.  Skipping.\n");
X
X			continue;	/* Don't process bad records */
X		}else
X			if(passwdrec->pw_uid != pw->uid)
X				if(verbflag)
X				{
X					printf("WARNING: UID MISMATCH.  Processing...");
X					fflush(stdout);
X				}
X
X
X		pw->space = spacerec();
X		if(pw->space < 0L)
X		{
X			if(verbflag)
X				printf("EXEMPTED USER.  Set to zero.\n");
X
X			pw->space = 0L;
X		}else
X			if(verbflag)
X				printf("%ld blocks.\n",pw->space);
X
X		pw = getrec();
X
X	}
X
X	if(fstatus != F_EOF)
X	{
X		(void)fprintf(stderr,"FATAL ERROR: Failure on space calculate(%d)\n",
X			fstatus);
X		(void)fprintf(stderr,"Updates lost; file intact.\n");
X		exit(-1);
X	}
X
X	/* If not in update, will close & release all; else, will hold file */
X	updateflag -= 2;
X	if(updateflag == 1)
X		purge_data(KEEP);
X	else
X		purge_data(FREE);
X
X	if(verbflag)
X		printf("\n**Done with recalculation.**\n");
X
X	return;
X}
X/**/
Xdumpit()
X{
X	struct pwrec *pw;
X	register int rec_sts;
X
X	void qsort();
X	void load_file();
X
X	load_file();
X
X	if((comp_routine != NULL))
X		qsort((char *)data_recs,reccnt,sizeof(struct pwrec),comp_routine);
X
X	(void)printf("User     Uid  Exp. First                     Last                      Disk\n");
X	(void)printf("              Flg  Login                     Logged in                 Used\n");
X	(void)printf("------------------------------------------------------------------------------\n");
X
X	pw = getrec();
X	while(fstatus == F_NORM || fstatus == F_TOP)
X	{
X		rec_sts = ' ';
X
X		passwdrec = getpwnam(pw->logon);
X		if(passwdrec == (struct passwd *)NULL)
X			rec_sts = 'L';
X		else
X
X			if((passwdrec->pw_uid != pw->uid) && rec_sts == ' ')
X				rec_sts = 'U';
X
X		if(rec_sts == ' ' && invflag)
X			continue;
X
X		(void)printf("%-8.8s %4.4d %c   %24.24s  %24.24s  %5.5ld %c\n", 
X			pw->logon,pw->uid,pw->expert,
X			ctime(&pw->timecr),
X			((pw->timecr!=pw->timeon)?ctime(&pw->timeon):"** NEVER LOGGED ON **"),
X			pw->space,rec_sts);
X
X		pw = getrec();
X	}
X
X	(void)printf("\nTotal: %d users recorded.\n",reccnt);
X
X	return(0);
X}
X/**/
Xscreenit()
X{
X	struct pwrec *pw;
X	register int cmd_ch;
X	register int index;
X	register int readit = 0;
X
X	struct pwrec *getrec();
X	long atol();
X	long spacerec();
X	void load_data();
X	void purge_data();
X	void startup();
X
X	/* Ok, let's see how startup works.  We won't return on fatal errors. */
X	startup();
X
X	/* Display the initial record count (set in startup) */
X	mvprintw(STATUS_Y,STATUS_X,"%d records",reccnt);
X	refresh();
X
X	/* Wow--it all worked!  Now enter the record loop */
X	for(readit++;;)
X	{
X		if(readit)
X			pw = getrec();
X
X		readit = 0;
X
X		if((fstatus != F_EOF)||(fstatus == F_NORM)||(fstatus == F_TOP))
X			load_data(pw);	/* Stuff the data into the data areas */
X		else
X		{
X			move(STATUS_Y,STATUS_X);
X			clrtoeol();
X
X			mvprintw(STATUS_Y,STATUS_X,"%s",f_statext[fstatus]);
X			refresh();
X		};
X
X		cmd_ch = getch();
X
X		switch(cmd_ch)
X		{
X			case 't':	/* Top of file */
X				fstatus = F_TOP;
X				currec = -1;
X				readit++;
X				break;
X
X			case 'b':	/* Bottom of file */
X				
X				currec = reccnt - 2;
X				fstatus = F_NORM;
X				readit++;
X				break;
X
X			case 'p':	/* Previous record */
X				if(currec <= 0)
X				{
X					fstatus = F_TOP;
X					break;
X				};
X
X				readit++;
X				currec -= 2;
X
X				break;
X
X			case 'n':	/* Next record */
X			case '\n':
X				readit++;
X				break;
X
X			case 'q':
X				purge_data(FREE);
X
X				end_proc(0);
X				break;
X
X			case 'r':	/* Screen refresh */
X				clear();
X				for(index = 0; sform[index].x >= 0;index++)
X					mvprintw(sform[index].y,sform[index].x,"%s",sform[index].label);
X				refresh();
X				break;
X
X#ifdef SHELL
X				/* Diagnostic, and not all that clean. */
X			case '!':	/* Shell */
X				if(!fork())
X				{
X					(void)setuid(getuid());
X					(void)setgid(getgid());
X
X					noraw();
X					echo();
X					clear();
X					refresh();
X
X					/* Don't care what happens... */
X					(void)system("/bin/ksh");
X
X					raw();
X					noecho();
X					clear();
X					for(index = 0; sform[index].x >= 0;index++)
X						mvprintw(sform[index].y,sform[index].x,"%s",sform[index].label);
X					refresh();
X				}else
X					(void)wait((int *)0);
X
X				break;
X#endif
X
X			case 'd':	/* Delete current record */
X				if(!updateflag)
X					break;
X
X				pw->space = DELREC;
X				updateflag++;
X
X				break;
X
X			case 'u':	/* Undelete current record */
X
X				if(!updateflag || (pw->space != DELREC))
X					break;
X
X				pw->space = 0L;
X				updateflag++;
X
X				break;
X
X			case 'e':	/* Expert toggle */
X				if(!updateflag)
X					break;
X
X				pw->expert = (pw->expert=='Y'?'N':'Y');
X				updateflag++;
X
X				break;
X
X			case 'i':	/* Uid fix */
X				if(!updateflag)
X					break;
X
X				pw->uid = passwdrec->pw_uid;
X				updateflag++;
X
X				break;
X
X			case 's':	/* Space calculate */
X				if(!updateflag)
X					break;
X				
X				mvprintw(STATUS_Y,STATUS_X,"Be patient...");
X				refresh();
X				pw->space = spacerec();
X				if(pw->space < 0L)
X				{
X
X					mvprintw(STATUS_Y,STATUS_X,"Exempted user.");
X					pw->space = 0L;
X				}else
X					mvprintw(STATUS_Y,STATUS_X,"Space recalculation done!");
X
X				refresh();
X				(void)sleep(1);
X
X				updateflag++;
X
X				break;
X		}
X	}
X}
X/**/
Xlong
Xspacerec()
X{
X	/*
X	 * The routine assumes that the passwd record contains the
X	 * valid password field for the current record.
X	 */
X	FILE *pipfdes;
X	char *cmdbuf;
X	register char *tmpptr;
X	char **listptr;
X	long space_ret;
X
X	extern char *malloc();
X	extern void free();
X
X	/* See if the user is an exception.  If so, return negative flag. */
X	for(listptr = nospace_list;*listptr != (char *)NULL;listptr++)
X		if(!strncmp(*listptr,passwdrec->pw_name,strlen(*listptr)))
X			return(-1L);
X
X	/* '/bin/du -s ' + user's dirpath + null */
X	cmdbuf = malloc((unsigned)(11+strlen(passwdrec->pw_dir)+1));
X	if(cmdbuf == (char *)NULL)
X	{
X		perror("spacerec (malloc)"); /* Not fatal */
X		return(0L);
X	}
X
X	(void)strcpy(cmdbuf,"/bin/du -s ");
X	(void)strcat(cmdbuf,passwdrec->pw_dir);
X	pipfdes = popen(cmdbuf,"r");
X
X	/* It'll fit here, and we know it's at least this long */
X	(void)fgets(cmdbuf,12,pipfdes);
X
X	for(tmpptr = cmdbuf;
X	   ((*tmpptr >= '0')&&(*tmpptr <= '9'));
X	   tmpptr++);
X
X	*tmpptr = '\0';
X
X	space_ret = atol(cmdbuf);
X
X	(void)pclose(pipfdes);
X	(void)free(cmdbuf);
X
X	return(space_ret);
X}
Xvoid
Xload_data(recptr)
Xstruct pwrec *recptr;
X{
X	/*
X	 * Load the data into the screen and force an update.
X	 * Notice that there is an actual check for the login ID and the
X	 * UID in the system password file for each entry passed!
X	 */
X	register SCRDATA *curelt;
X
X	/* Data is highlighted */
X	standout();
X
X	/* Always clear out the error status line. */
X	move(RECSTAT_Y,RECSTAT_X);
X	clrtoeol();
X	
X	/* Load the current record number (one-based) */
X	curelt = &data[CURREC];
X	mvprintw(curelt->y,curelt->x,curelt->fmt,(currec+1));
X	
X	/* Load the login name. */
X	curelt = &data[LOGIN];
X	mvprintw(curelt->y,curelt->x,curelt->fmt,recptr->logon);
X
X	/* On to the UID */
X	curelt = &data[UID];
X	mvprintw(curelt->y,curelt->x,curelt->fmt,recptr->uid);
X
X	/* Are the UID and the logon name good? */
X	passwdrec = getpwnam(recptr->logon);
X
X	if(passwdrec == (struct passwd *)NULL)
X		mvprintw(data[BAD_LOGIN].y,data[BAD_LOGIN].x,data[BAD_LOGIN].fmt);
X	else
X
X		if(passwdrec->pw_uid != recptr->uid)
X			mvprintw(data[BAD_UID].y,data[BAD_UID].x,data[BAD_UID].fmt,passwdrec->pw_uid);
X
X	/* Next, the Expert flag */
X	curelt = &data[EXPERT];
X	mvprintw(curelt->y,curelt->x,curelt->fmt,recptr->expert);
X
X	/* And the Creation and Last Login Dates */
X	curelt = &data[CREATE];
X	mvprintw(curelt->y,curelt->x,curelt->fmt,ctime(&recptr->timecr));
X
X	curelt = &data[LASTLOG];
X	if(recptr->timeon != 0L)
X		mvprintw(curelt->y,curelt->x,curelt->fmt,ctime(&recptr->timeon));
X	else
X		mvprintw(curelt->y,curelt->x,curelt->fmt,"** NEVER LOGGED IN **");
X
X	/* Current space used value, or deleted */
X	if(recptr->space == DELREC)
X	{
X		curelt = &data[SPACE_USED];
X		mvprintw(curelt->y,curelt->x,curelt->fmt,0L);
X		curelt = &data[DELETED];
X		mvprintw(curelt->y,curelt->x,curelt->fmt,"DELETED");
X	
X	}else
X	{
X		curelt = &data[SPACE_USED];
X		mvprintw(curelt->y,curelt->x,curelt->fmt,recptr->space);
X		curelt = &data[DELETED];
X		standend();
X		mvprintw(curelt->y,curelt->x,curelt->fmt,"       ");
X	}
X
X	/* End the highlight mode, if not done above */
X	standend();
X
X	/* Load the status data, after clearing it */
X	move(STATUS_Y,STATUS_X);
X	clrtoeol();
X
X	mvprintw(STATUS_Y,STATUS_X,"%s",f_statext[fstatus]);
X
X	/* Go to get ready for the input */
X	move(CMDIN_Y,CMDIN_X);
X
X	refresh();
X}
X/**/
Xvoid
Xstartup()
X{
X	register int index;
X
X	void load_file();
X	extern int end_proc();
X	extern int (*signal())();
X
X	/* Get the file data */
X
X	load_file();
X	/*
X	 * Ok, grab all signals that could zonk us.  We know that
X	 * there are some signals in this range we can't catch, but
X	 * running through them won't hurt, and simplifies the code.
X	 */
X	for(index = 1; index <= SIGUSR2; index++)
X		(void)signal(index,end_proc);
X
X	/* Initialize screen handling */
X	initscr();
X	raw();
X	noecho();
X	clear();
X	refresh();
X
X	/* Display the initial screen form */
X	for(index = 0; sform[index].x >= 0;index++)
X		mvprintw(sform[index].y,sform[index].x,"%s",sform[index].label);
X
X	/* Load the record count */
X	mvprintw(data[RECCNT].y,data[RECCNT].x,data[RECCNT].fmt,reccnt);
X	refresh();
X
X	return;
X}
X/**/
Xvoid
Xload_file()
X{
X	struct stat statbuf;
X
X	extern char *calloc();
X
X	/*
X	 * If the file is already loaded, no allocation work--just go to
X	 * the "top".
X	 */
X	if(oldfdes != (FILE *)NULL)
X	{
X		currec = -1;
X		fstatus = F_TOP;
X		return;
X	}
X
X	/* Can we get the file?  The philosophy here is to lock the
X	 * file to prevent others from accessing it--this should block
X	 * logins for the time that we're operative, but it shouldn't
X	 * be prohibitive.  If so, you figure out how to handle changed file
X	 * records... of course, locks are only applied for update mode.
X	 */
X	if((oldfdes = fopen(pw_fname,"r")) == (FILE *)NULL)
X	{
X		perror(pw_fname);
X		exit(-1);
X	}else
X		if(updateflag)
X		{
X			/* Let them know now, if it's not writable */
X			if(access(pw_fname,02))
X			{
X				(void)fprintf(stderr,"No write permission on %s.\n",pw_fname);
X				(void)fprintf(stderr,"ABORT\n");
X				exit(-1);
X			}
X
X			if(locking(fileno(oldfdes),1,0L) < 0)
X			{
X				perror(pw_fname);
X				exit(errno);
X			}
X		}
X
X	/* Good, have it open. Figure out how big an array to allocate.
X	 * As mentioned elsewhere, the assumption is that, with each record
X	 * only 26 bytes, the file will always easily fit in memory.
X	 *  This makes later manipulation--sorting, etc--trivial.  The
X	 * structure, however, will be easy to convert to using a tempfile,
X	 * if anyone really wants to...the first cut of this program did so.
X	 */
X	if(fstat(fileno(oldfdes),&statbuf) < 0)
X	{
X		perror(pw_fname);
X		exit(errno);
X	}
X
X	reccnt = (int)statbuf.st_size / sizeof(struct pwrec);
X
X	/* Size sanity check */
X	if((off_t)(reccnt * sizeof(struct pwrec)) != statbuf.st_size)
X	{
X		(void)fprintf(stderr,
X			"CORRUPTED %s!  File size not an integral number of records!\n",pw_fname);
X		exit(-1);
X	}
X
X	data_recs = (struct pwrec *)calloc((unsigned)reccnt,(unsigned)sizeof(struct pwrec));
X
X	if(data_recs == (struct pwrec *)NULL)
X	{
X		(void)fprintf(stderr,"Couldn't get memory!\n");
X		exit(-1);
X	}
X
X	/*
X	 * Load the array with the actual file data. Check the
X	 * record count, too.  Note that, while this keeps our view of the
X	 * file consistent, the real file may change if we're just viewing it.
X	 */
X	for(currec = 0; currec < reccnt;currec++)
X		if(fread(&data_recs[currec],sizeof(struct pwrec),1,oldfdes) != 1)
X		{
X			if(feof(oldfdes))
X				(void)fprintf(stderr,
X					"Unexpected early EOF on %s!\n",pw_fname);
X			else
X				(void)fprintf(stderr,
X					"Unknown error on %s read!\n",pw_fname);
X
X			exit(-1);
X		}
X
X	/* Just another sanity check */
X	{
X		struct pwrec pw;
X		if((fread(&pw,sizeof(struct pwrec),1,oldfdes) == 1) ||
X			!feof(oldfdes))
X		{
X			(void)fprintf(stderr,"Unexpected extra data in %s!\n",pw_fname);
X			exit(-1);
X		}
X	}
X
X	/* All data loaded; leave input file ready for update */
X	if(updateflag)
X		rewind(oldfdes);
X	else
X		(void)fclose(oldfdes);
X
X
X	/* Initialize the current record number  and status */
X	currec = -1;
X	fstatus = F_TOP;
X}
X/**/
Xstruct pwrec *
Xgetrec()
X{
X	/* "Get" a record from the array. */
X
X	fstatus = F_NORM;
X
X
X	if(currec == (reccnt-1))
X		fstatus = F_EOF;
X	else
X		currec++;
X
X	if(currec == 0)
X		fstatus = F_TOP;
X
X	return(&data_recs[currec]);
X}
X/**/
Xvoid
Xpurge_data(disp)
Xint disp;
X{
X	/*
X	 * If it's been determined that the data was modified.  
X	 * Truncate the data file, and then transfer ONLY valid
X	 * (undeleted) records.  In all cases, release the array memory.
X	 */
X	extern void free();
X
X	if(updateflag <= 1)
X	{
X		free(data_recs);
X		return;
X	}
X
X	currec = 0;	/* Start at the array first element */
X
X	if((oldfdes = freopen(pw_fname,"w",oldfdes)) == (FILE *)NULL)
X	{
X		(void)fprintf(stderr,"COULDN'T TRUNCATE %s\n",pw_fname);
X		(void)fprintf(stderr,"ALL UPDATES LOST!");
X
X		end_proc(0);
X	};
X
X	/* Move those records, but ignore deleted ones */
X	for(currec = 0; currec < reccnt;currec++)
X		if(data_recs[currec].space != DELREC)
X			if(fwrite(&data_recs[currec],sizeof(struct pwrec),1,oldfdes) != 1)
X			{
X				perror("real file write");
X				exit(errno);
X			}
X
X	if(disp == FREE)
X	{
X		(void)fclose(oldfdes);
X		free(data_recs);
X	}
X}
X/**/
Xend_proc(sig)
Xint sig;
X{
X
X	if(!spaceflag)
X	{
X		noraw();
X		echo();
X		clear();
X		endwin();
X	};
X
X	if(sig != 0)
X	{
X		(void)fprintf(stderr,"Caught signal %d\n",sig);
X		if(updateflag)
X			(void)fprintf(stderr,"Changes lost!\n");
X
X		(void)sleep(2);
X	}
X
X	exit(sig);
X}
X/**/
Xint
Xby_name(n1,n2)
Xchar *n1,*n2;
X{
X	return(strcmp(n1,n2));
X}
X/**/
Xby_login(l1,l2)
Xlong l1,l2;
X{
X	return((int)l1 - l2);
X}
X/**/
Xusage()
X{
X	(void)fprintf(stderr,"Usage: pwcntl -d [ -i][ -s [n|l]] [ -c][ -v][ -f filename]\n");
X	(void)fprintf(stderr,"       pwcntl [-e | -u][ -c][ -v][-f filename]\n");
X	(void)fprintf(stderr," where:\n");
X	(void)fprintf(stderr,"   -u  :  Update.  Interactive only.\n\n");
X	(void)fprintf(stderr,"   -d  :  Dump mode.\n\n");
X	(void)fprintf(stderr,"   -e  :  Examine interactively.\n\n");
X	(void)fprintf(stderr,"   -i  :  Invalid logins only (Only with 'd' option)\n\n");
X	(void)fprintf(stderr,"   -c  :  Calculate space.  Either standalone, or before further processing.\n\n");
X	(void)fprintf(stderr,"   -s X:  Sort.  Must have either X=n, for by Name order, or X=l, for Lastlog\n");
X	(void)fprintf(stderr,"          order. (Only with 'd' option)\n\n");
X	(void)fprintf(stderr,"   -v  :  Verbose mode.\n\n");
X	(void)fprintf(stderr,"   -f  :  Use alternate filename; default /etc/pwcntl\n\n");
X
X	exit(1);
X};
END_OF_pwcntl.c
if test 23716 -ne `wc -c <pwcntl.c`; then
    echo shar: \"pwcntl.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pwcntl.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pwcntl.h\"
else
echo shar: Extracting \"pwcntl.h\" \(470 characters\)
sed "s/^X//" >pwcntl.h <<'END_OF_pwcntl.h'
X/* @(#)pwcntl.h	1.1 12/18/88 */
X/*
X * pwcntl.h - /etc/pwcntl file structure
X */
Xstruct  pwrec {
X	char logon[8];	/* user name from login arg1 */
X	int uid;	/* uid from /etc/passwd */
X	char expert;	/* Y/N flag for expert mode */
X	char flg;	/* unused (pad) */
X	time_t timeon;	/* time of most recent login */
X	time_t timecr;	/* time of first login */
X	long space;	/* disk space used in blocks,
X			 * only used in Ulogin after Compute Space
X			 * has been selected.
X			 */
X};
END_OF_pwcntl.h
if test 470 -ne `wc -c <pwcntl.h`; then
    echo shar: \"pwcntl.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f pwcntl.notes -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"pwcntl.notes\"
else
echo shar: Extracting \"pwcntl.notes\" \(7919 characters\)
sed "s/^X//" >pwcntl.notes <<'END_OF_pwcntl.notes'
XFrom uucp Sun Dec 11 00:50 CST 1988
X>From ignatz  Sun Dec 11 00:50:33 1988 remote from chinet
XReceived: by homebru.UUCP (smail2.5)
X	id AA13442; 11 Dec 88 00:50:33 CST (Sun)
XReceived: by chinet.chi.il.us (smail2.5)
X	id AA09165; 10 Dec 88 23:47:16 CST (Sat)
XSubject: pwcntl.update
XTo: homebru!ignatz
XDate: Sat, 10 Dec 88 23:47:12 CST
XFrom: Dave Ihnat <ignatz@chinet.chi.il.us>
XX-Mailer: Elm [version 2.1 PL1]
XMessage-Id: <8812102347.AA09165@chinet.chi.il.us>
X
XArticle 3509 of unix-pc.general:
XPath: chinet!mcdchg!ethos!att!mtuxo!mtgzy!mtgzz!dwex
XFrom: dwex@mtgzz.att.com (d.e.wexelblat)
XNewsgroups: unix-pc.general
XSubject: Re: /etc/pwcntl on the 3B1 (3.51), anyone?
XKeywords: pwcntl login lastlog
XMessage-ID: <4736@mtgzz.att.com>
XDate: 7 Dec 88 13:29:49 GMT
XReferences: <7059@chinet.chi.il.us> <5439@cbmvax.UUCP>
XReply-To: dwex@mtgzz.UUCP (d.e.wexelblat)
XOrganization: AT&T, Middletown NJ
XLines: 57
X
XIn article <5439@cbmvax.UUCP> ditto@cbmvax.UUCP (Michael "Ford" Ditto) writes:
X>In article <7059@chinet.chi.il.us> ignatz@chinet.chi.il.us (Dave Ihnat) writes:
X>>Before I waste my time re-inventing the wheel, I guess I'll ask here.
X> [ ... ]
X>>Has someone else taken this file apart?  If not, it shouldn't be too
X>>outrageous, but I'd prefer not to duplicate effort. 
X>
X [ ... ]
X>
X>	struct pwcntl
X>	{
X>	    char name[8];	/* name entered at login: */
X>	    int uid;		/* seems to be garbage for failed logins */
X>	    char flag;		/* always 'Y' or 'N', what does it mean? */
X>	    /* char pad; */
X>	    long last_login;	/* last login attempt */
X>	    long first_login;	/* time when this entry was created */
X>	    long unknown;	/* always zero, what does it mean? */
X>	}; /* 26 bytes total */
X>
X>Has anyone ever seen this file on anything other than a Unix PC?
X>Anyone with further observations/conclusions, please post!
X>-- 
X>					-=] Ford [=-
X>
X>"The number of Unix installations	(In Real Life:  Mike Ditto)
X>has grown to 10, with more expected."	ford@kenobi.cts.com
X>- The Unix Programmer's Manual,		...!sdcsvax!crash!elgar!ford
X>  2nd Edition, June, 1972.		ditto@cbmvax.commodore.com
X
X
XThis file is used by the 'install' user's 'add user' (or whatever it's
Xcalled) command.  The flag field is for the 'EXPERT' flag (shows up on
Xthe menu.  The first_login field will be zero if the user is created through
Xthe menu, and gets set when the user first logs in.  The unknown field
Xis (I think) the disk space used by that user, and gets filled in when you
Xtell UA to run whatever to find disk usage (this also shows up on the
Xadd user menu).  I decoded this file a while ago, since I needed to write
Xa script to create a bunch of users, but still have things look OK to UA.
XIf UA isn't used, I don't think this file is necessary, or relevant.
X
X
X--David Wexelblat			dwex@mtgzz.att.com
X  AT&T Bell Laboratories		...!att!mtgzz!dwex
X  200 Laurel Ave - 4B-421
X  Middletown, NJ 07748
X
X
Xstupid
Xinews
Xfodder
Xstupid
Xinews
Xfodder
Xstupid
Xinews
Xfodder
X
X
XArticle 3516 of unix-pc.general:
XPath: chinet!att!ucbvax!ucsd!rutgers!tut.cis.ohio-state.edu!unmvax!pprg.unm.edu!hc!lll-winken!lll-tis!pacbell!ctnews!mitisft!bms
XFrom: bms@mitisft.Convergent.COM (Bruce Schlobohm)
XNewsgroups: unix-pc.general
XSubject: Re: /etc/pwcntl on the 3B1 (3.51)
XKeywords: pwcntl login Ulogin
XMessage-ID: <531@mitisft.Convergent.COM>
XDate: 9 Dec 88 05:42:06 GMT
XReferences: <7059@chinet.chi.il.us> <5439@cbmvax.UUCP>
XOrganization: Convergent Technologies, San Jose, CA
XLines: 46
X
XIn article <5439@cbmvax.UUCP>, ditto@cbmvax.UUCP (Michael "Ford" Ditto) writes:
X> 
X> When I first looked at this file a long time ago, I was under the
X> impression that it only recorded failed login attempts, but since it
X> was definately modified when I logged in just now, I guess I was
X> wrong.  It definitely does record unsuccessful attempts, though;
X> even unknown login names.
X> [...]
X> Has anyone ever seen this file on anything other than a Unix PC?
X> Anyone with further observations/conclusions, please post!
X
XInfo on pwcntl as I see it:
X
Xstruct  pwrec {
X	char logon[8];		/* user name from login arg1 */
X	int uid;		/* uid from /etc/passwd */
X	char expert;		/* Y/N flag for expert mode */
X	char flg;		/* unused (pad) */
X	time_t timeon;		/* time of most recent login */
X	time_t timecr;		/* time of first login */
X	long space;		/* disk space used in blocks,
X				 * only used in Ulogin after Compute Space
X				 * has been selected.
X				 */
X};
X
Xlogin writes records, Ulogin reads them and updates the expert flag,
Xupon request, but never writes the disk space info back to the file.
XThe information you see in the menu "User Login Interface" (Ulogin),
Xcomes from pwcntl.  In my brief search, I didn't find any other programs
Xwhich use of the file, and this is definitely not a feature from CTIX.
X
XThe purpose of the file seems to be the groundwork for the ua to control
Xwho can be an "expert", but this feature doesn't seem to be fully implemented,
Xat least not on my UNIXPC.  (Changing Expert= in ~/Environment seems to be
Xall that ua needs to turn on/off Expert Mode.)
X
XAs far as failed logins, this seems to be more of a quirk than an feature.
XIf you type a nonexistent login name at getty, hit return for the password,
Xthen log in with a valid login name/passwd, a record is written with
Xthe nonexistent login name, as though it were a valid (new) user.  However,
Xif you fail the login completely, and let login timeout (60 seconds),
Xno record is written to pwcntl.  So this isn't a reliable way of tracking
Xfailed login attempts.
X-- 
XBruce Schlobohm
Xbms@Convergent.COM -or- {pyramid,sri-unix,pacbell}!ctnews!bms
X
X
X
XFrom uucp Wed Dec  7 12:20 CST 1988
X>From ignatz  Wed Dec  7 12:20:47 1988 remote from chinet
XReceived: by homebru.UUCP (smail2.5)
X	id AA00697; 7 Dec 88 12:20:47 CST (Wed)
XReceived: by chinet.chi.il.us (smail2.5)
X	id AA20892; 7 Dec 88 11:07:56 CST (Wed)
XSubject: pwdctl.note
XTo: homebru!ignatz
XDate: Wed, 7 Dec 88 11:07:55 CST
XFrom: Dave Ihnat <ignatz@chinet.chi.il.us>
XX-Mailer: Elm [version 2.1 PL1]
XMessage-Id: <8812071107.AA20892@chinet.chi.il.us>
X
XArticle 3483 of unix-pc.general:
XPath: chinet!att!rutgers!cbmvax!ditto
XFrom: ditto@cbmvax.UUCP (Michael "Ford" Ditto)
XNewsgroups: unix-pc.general
XSubject: Re: /etc/pwcntl on the 3B1 (3.51), anyone?
XSummary: priliminary guess at format
XKeywords: pwcntl login lastlog
XMessage-ID: <5439@cbmvax.UUCP>
XDate: 6 Dec 88 00:41:18 GMT
XReferences: <7059@chinet.chi.il.us>
XReply-To: ditto@cbmvax.UUCP (Michael "Ford" Ditto)
XOrganization: Commodore Technology, West Chester, PA
XLines: 34
X
XIn article <7059@chinet.chi.il.us> ignatz@chinet.chi.il.us (Dave Ihnat) writes:
X>Before I waste my time re-inventing the wheel, I guess I'll ask here.
X [ ... ]
X>Has someone else taken this file apart?  If not, it shouldn't be too
X>outrageous, but I'd prefer not to duplicate effort. 
X
XWhen I first looked at this file a long time ago, I was under the
Ximpression that it only recorded failed login attempts, but since it
Xwas definately modified when I logged in just now, I guess I was
Xwrong.  It definitely does record unsuccessful attempts, though;
Xeven unknown login names.
X
XHere's what I've been able to figure out from the file itself:
X
X	struct pwcntl
X	{
X	    char name[8];	/* name entered at login: */
X	    int uid;		/* seems to be garbage for failed logins */
X	    char flag;		/* always 'Y' or 'N', what does it mean? */
X	    /* char pad; */
X	    long last_login;	/* last login attempt */
X	    long first_login;	/* time when this entry was created */
X	    long unknown;	/* always zero, what does it mean? */
X	}; /* 26 bytes total */
X
XHas anyone ever seen this file on anything other than a Unix PC?
XAnyone with further observations/conclusions, please post!
X-- 
X					-=] Ford [=-
X
X"The number of Unix installations	(In Real Life:  Mike Ditto)
Xhas grown to 10, with more expected."	ford@kenobi.cts.com
X- The Unix Programmer's Manual,		...!sdcsvax!crash!elgar!ford
X  2nd Edition, June, 1972.		ditto@cbmvax.commodore.com
X
X
X
END_OF_pwcntl.notes
if test 7919 -ne `wc -c <pwcntl.notes`; then
    echo shar: \"pwcntl.notes\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0