[net.sources] New login-accounting utility, "sac".

gdw@ssl-macc.co.uk (Grenville Whelan) (08/22/86)

A login-accounting package similar to ac(8) follows. I've called it 'sac'. It
works fine on our VAX-750s and Suns running 4.2BSD. For more information, see
the manual page. As i am new to C, any *consructive*  comments or critiscisms
will be welcome; if you modify/improve it, please let me know, email  address
at EOF.

--------- CUT---------- CUT---------- CUT---------- CUT---------- CUT--------

#! /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:
#	Makefile
#	sac.c
#	getopt.c
#	sac.8
# This archive created: Fri Aug 22 11:53:56 1986
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'

sac:		getopt.o sac.c
		cc -O -o sac sac.c getopt.o

getopt.o:	getopt.c
		cc -O -c getopt.c

shar:		
		shar Makefile sac.c getopt.c sac.8 > sac.shar
SHAR_EOF
fi # end of overwriting check
if test -f 'sac.c'
then
	echo shar: will not over-write existing file "'sac.c'"
else
cat << \SHAR_EOF > 'sac.c'
/*
**
** Program:	sac.c
**
** Author:	Grenville Whelan Aug-1986, (gdw@uk.co.ssl-macc)
**
** Description:	Sac is  based off the  standard Unix command  ac(8), with
** 		some  enhancements. There were  two reasons for writing a
**		new utility; (1) We  like to  re-create  the 'wtmp' login
**		accounting file every-day, but once 'wtmp' is re-created,
**		existing log-ins are lost. Sac gives  the user the option
**		of automatically re-creating a 'wtmp' file, consisting of
**		only logged-in  users. (2) Ac also accounts for users who
**		have not logged out; sac only  accounts logged-out users.
**		The  options on  sac are the  same as ac, apart  from the
**		'-d' option, which we never use. There is also a new flag
**		'-n' which, if set, will re-create the wtmp file.
**
** Modifications:
**
*/

#include <sys/types.h>
#include <sys/file.h>
#include <sys/dir.h>
#include <stdio.h>

#define WTMP_FILE "/usr/adm/wtmp"		/* Default wtmp file */
#define MODE 422				/* Mode of new wtmp file */

#define MAXUSERS 1000				/* Max no. of users */
#define MAXTTYS 1000				/* Max no. of ttys */

struct wtmp_info {
			char w_tty[8];		/* tty name of login */
			char w_use[8];		/* user name of login */
			char w_hos[16];		/* host name of login */
			long w_log;		/* login time of login */
		};

struct user_info {
			char u_use[8];		/* user name */
			long u_tim;		/* secs logged in */
		};

main(argc,argv)

int argc;
char **argv;

{
	struct wtmp_info wt;
	struct user_info us[MAXUSERS];
	struct wtmp_info lo[MAXTTYS];

	extern int optind;
	extern char *optarg;

	int fd;				/* file descriptor */
	int d_flag=0;			/* gen. purpose flag */
	int i=0, j=0;			/* gen. purpose counters */
	int num_u=0;			/* number of different users */
	int num_t=0;			/* number of different ttys */
	int e_tty=0;			/* tty being dealt with */
	int bytes_read=0;		/* no. bytes read from file */
	int RC_FLAG=0;			/* if flag set, create new wtmp */
	int PU_FLAG=0;			/* if flag set, print all users */

	float time=0;			/* login hours */
	float total=0;			/* total login hours */

	char dum[36];			/* dummy string */
	char WTMP[MAXNAMLEN];		/* wtmp file name */
	char *gen_ptr;			/* gen. purpose pointer */

	/*
	** Make wtmp file default of WTMP_FILE
	*/

	strcpy(WTMP,WTMP_FILE);

	/* 
	** Get command arguments
	*/

	while((i=getopt(argc,argv,"npw:"))!=EOF)
		switch(i) {
			case 'n':
				RC_FLAG=1;
				break;
			case 'p':
				PU_FLAG=1;
				break;
			case 'w':
				strcpy(WTMP,optarg);
				break;
			case '?':
				usage(*argv);
				break;
			default:
				break;
			}

	/*
	** Attempt to open wtmp file
	*/

	if((fd=open(WTMP,O_RDONLY))==EOF) {
		fprintf(stderr,"%s: cannot open %s\n",*argv,WTMP);
		exit(-1); }

	/*
	** Read file until bytes_read != 0, ie EOF or corrupt file
	*/

	while((bytes_read=read(fd,dum,36))==36)
	{
		strncpy(wt.w_tty,dum,8);
		strncpy(wt.w_use,&dum[8],8);
		strncpy(wt.w_hos,&dum[16],16);

		/*
		** Convert char login time to long
		*/

		gen_ptr=(char *)&(wt.w_log);
		for(i=32; i<=35; ++i)
			*gen_ptr++=dum[i];

		d_flag=0;

		/* Pseudo code to build array of username
		***************************************************************
		**
		** if(not first user and not NULL)
		**	set flag if user already in array;
		**
		** if(flag not set and user not NULL)
		**	add user to array;
		**
		**************************************************************/

		if((num_u)&&(strlen(wt.w_use)))
			for(i=0; i<num_u; ++i)
				if(!strcmp(us[i].u_use,wt.w_use))
					d_flag=1;

		if((d_flag==0)&&(strlen(wt.w_use)))
			strcpy(us[num_u++].u_use,wt.w_use);

		d_flag=0;
		e_tty=0;

		/* Pseudo code to build up array of ttys
		***************************************************************
		**
		** if(not first tty) {
		**	set flag if tty already in array;
		**	set variable to keep track of tty; }
		**
		** if(flag set) {
		**	add tty to array;
		**	set variable to keep track of tty; }
		**
		**************************************************************/

		if(num_t)
			for(i=0; i<num_t; ++i)
				if(!strcmp(lo[i].w_tty,wt.w_tty)) {
					d_flag=1;
					e_tty=i; }

		if(d_flag==0) {
			strcpy(lo[num_t++].w_tty,wt.w_tty);
			e_tty=(num_t-1); }


		/* Pseudo Code to compile users login times
		***************************************************************
		**
		** if(this is a login)
		** {
		**	if(user already logged in on this tty)
		**	{
		**		find user;
		**		set user's logout on tty to this users log-in;
		**	}
		**
		**	Copy username to user new user of tty;
		**	Copy users' login time to login of tty
		** }
		** else(is a log-out)
		**	{
		**		find user logged in to tty;
		**		add login length of this tty to user total;
		**		Make username of tty NULL;
		**		Make login time of tty 0;
		**	}
		**
		**************************************************************/

		if(strlen(wt.w_use))
		{
			if(lo[e_tty].w_log)
				for(i=0; i<num_u; ++i)
					if(!strcmp(lo[e_tty].w_use,us[i].u_use)) {
						us[i].u_tim += (wt.w_log - lo[e_tty].w_log);
						strcpy(lo[e_tty].w_use,"\0");
						lo[e_tty].w_log=0; }

			strcpy(lo[e_tty].w_use,wt.w_use);
			strcpy(lo[e_tty].w_hos,wt.w_hos);
			lo[e_tty].w_log=wt.w_log; }
		else
			for(i=0; i<num_u; ++i)
				if(!strcmp(lo[e_tty].w_use,us[i].u_use))
				{
					us[i].u_tim += (wt.w_log - lo[e_tty].w_log);
					strcpy(lo[e_tty].w_use,"\0");
					strcpy(lo[e_tty].w_hos,"\0");
					lo[e_tty].w_log=0;
				}
	}

	/*
	** If not end of file, wtmp file corrupt
	*/

	if(bytes_read!=0) {
		fprintf(stderr,"%s: fatal error, %s corrupt\n",*argv,WTMP);
		close(fd);
		exit(-1); }

	close(fd);

	/* Pseudo Code to total up and output results
	***********************************************************************
	**
	** for(count = 1 to number of users)
	** {
	**	set flag to 0;
	**		if(any people arguments)
	**			if(user in people args)
	**				set flag;
	**		else
	**			set flag;
	**	if(flag set)
	**		calc time and add to total;
	**
	**	if(flag set and print all flag set)
	**		output user + users total;
	**	}
	**	output total;
	**
	*********************************************************************/

	for(i=0; i<num_u; ++i)
	{
		d_flag=0;

		if(argc>=(optind+1)) {
			for(j=optind; j<argc; ++j)
				if(!strcmp(*(argv+j),us[i].u_use))
					d_flag=1; }
		else
			d_flag=1;

		if(d_flag) {
			time=us[i].u_tim;
			total += time; }

		if((PU_FLAG)&&(d_flag))
			printf("\t%-8s%6.2f\n",us[i].u_use,(time/3600));
	}

		printf("\ttotal%9.2f\n",(total/3600));

	/* Pseudo code to Recreate wtmp file
	***********************************************************************
	**	
	** if(recreate wtmp flag is set)
	** {
	**	if(can't open wtmp for writing)
	**		error and exit;
	**
	**	for(count = 1 to number of ttys)
	**		if(this tty not logged out)
	**		{
	**			Convert login time of tty back to char;
	**			Recreate string with all wtmp data;
	**
	**		write this string to file;
	**		if(write failed)
	**			error and exit;
	**
	**		close file
	**		}
	** }
	**
	**********************************************************************/

	if(RC_FLAG)
	{

		if((fd=open(WTMP,(O_TRUNC|O_CREAT|O_WRONLY),MODE))==EOF) {
			fprintf(stderr,"%s:cannot open %s\n",*argv,WTMP);
			exit(-1); }

		for(i=0; i<num_t; ++i)
			if(lo[i].w_log!=0) {
				for(j=0; j<=35; ++j)
					strcpy(&dum[j],"\0");

				strncpy(dum,lo[i].w_tty,8);
				strncpy(&dum[8],lo[i].w_use,8);
				strncpy(&dum[16],lo[i].w_hos,16);

				gen_ptr=(char *)&(lo[i].w_log);

				for(j=32; j<=35; ++j)
					dum[j]= *gen_ptr++;

				if(write(fd,dum,36)!=36) {
					fprintf(stderr,"%s: fatal error, %s corrupt\n",*argv,WTMP);
					close(fd);
					exit(-1); }
			}

		close(fd);
	}

}

/*
** Define command usage
*/

usage(command)

char *command;
{
	fprintf(stderr,"usage: %s [-np] [-w file] people...\n",command);
	exit();
}
SHAR_EOF
fi # end of overwriting check
if test -f 'getopt.c'
then
	echo shar: will not over-write existing file "'getopt.c'"
else
cat << \SHAR_EOF > 'getopt.c'
/* got this off net.sources */
#include <stdio.h>

/*
 * get option letter from argument vector
 */
int	opterr = 1,		/* useless, never set or used */
	optind = 1,		/* index into parent argv vector */
	optopt;			/* character checked for validity */
char	*optarg;		/* argument associated with option */

#define BADCH	(int)'?'
#define EMSG	""
#define tell(s)	fputs(*nargv,stderr);fputs(s,stderr); \
		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);

getopt(nargc,nargv,ostr)
int	nargc;
char	**nargv,
	*ostr;
{
	static char	*place = EMSG;	/* option letter processing */
	register char	*oli;		/* option letter list index */
	char	*index();

	if(!*place) {			/* update scanning pointer */
		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
		if (*place == '-') {	/* found "--" */
			++optind;
			return(EOF);
		}
	}				/* option letter okay? */
	if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
		if(!*place) ++optind;
		tell(": illegal option -- ");
	}
	if (*++oli != ':') {		/* don't need argument */
		optarg = NULL;
		if (!*place) ++optind;
	}
	else {				/* need an argument */
		if (*place) optarg = place;	/* no white space */
		else if (nargc <= ++optind) {	/* no arg */
			place = EMSG;
			tell(": option requires an argument -- ");
		}
	 	else optarg = nargv[optind];	/* white space */
		place = EMSG;
		++optind;
	}
	return(optopt);			/* dump back option letter */
}
SHAR_EOF
fi # end of overwriting check
if test -f 'sac.8'
then
	echo shar: will not over-write existing file "'sac.8'"
else
cat << \SHAR_EOF > 'sac.8'
.TH SAC LOCALTOOLS "August 21 1986"
.UC 4
.SH NAME
sac \- login accounting
.SH SYNOPSIS
.B sac
[
.B \-w
wtmp ] [
.B \-p
] [
.B \-d
] [
.B \-n
] [ people ] ...
.SH DESCRIPTION
.I Sac
produces a printout giving
connect time
for each user who has logged in and out
during the life of the current
.I wtmp
file.
Only logged-out processes are accounted.
A total is also produced.
.B \-w
is used to specify an alternate
.IR wtmp ""
file.
.B \-p
prints individual totals; without this option,
only totals are printed.
.B \-n
will attempt to recreate the wtmp file, so that all logged-out processes
are discarded.
Any
.I people
will limit the
printout to only the specified login names.
If no
.IR wtmp ""
file is given,
.I /usr/adm/wtmp
is used.
.PP
The accounting file
.I /usr/adm/wtmp
is maintained by
.I init
and
.I login.
Neither of these programs creates the file,
so if it does not exist
no connect-time accounting is done.
To start accounting, it should be created with length 0.
On the other hand if the file is left undisturbed it will
grow without bound, so periodically any information
desired should be collected and the file truncated
using the '-n' option.
.PP
Sac's format is very similar to that of ac(8).
.PP
.SH AUTHOR
Grenville Whelan, (gdw@uk.co.ssl-macc).
.SH BUGS
None, (I hope).
.SH FILES
/usr/adm/wtmp
.SH "SEE ALSO"
init(8), sa(8), login(1), utmp(5), ac(8).
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
       +----------------------------+----------------------------------+
       | Grenville Whelan,          | Tel   - +44 625 29241            |
       | Software Sciences Ltd,     |                                  |
       | London & Manchester House, |                                  |
       | Park Street,               | EMAIL - gdw@ssl-macc.co.uk       |
       | Macclesfield, UK.          | UUCP  - ...!mcvax!ukc!sslvax!gdw |
       +----------------------------+----------------------------------+

           "I didn't get where I am today by writing silly quotes in
            my .signature file"  -- C.J.