larry@jc3b21.UUCP (Lawrence F. Strickland) (12/10/85)
The following "C" program is a "User Accounting" program designed specifically for the 3B2 series of computers. We (like others) found that System V Release 2.2 for the 3B2 seems to have 'lost' several parts of UNIX (:-) that were standard previously. One of the worst losses as far as we were concerned was the system accounting. It is necessary for us as an educational institution to keep track of who has been on the system and when. The following program, 'ua', takes care of this. * CAVEATS * Ua reads all of wtmp (basically) into memory as a binary sort tree. As a result, it expects relatively short wtmp files (we clear ours out each night). If the tree overflows memory, the program will die with a message, but not save and/or print any part of the tree. Although the program has been tested, there are no guarantees that it is entirely bugless. Please send flames/questions/complaints to me at the address below. It works for the 3b2, anything else is anybodys guess. There are NO ifdef's for other system. Hope this is somewhat useful! Larry Strickland UUCP: ...peora!ucf-cs!usfvax2!3b2bame!jc3b21!larry USMAIL: Larry Strickland 12866 80th Avenue North Seminole, FL 33542 ---- cut here ----- cut here ----- cut here ----- cut here ----- cut here ---- # # To unbundle, sh this file with /bin/sh, NOT /bin/csh # echo Unbundling ua.c 1>&2 sed "s/^X//" >ua.c <<\'End of ua.c\' X/* X * ua -- trace user logins (user accounting) X * for 3b2 systems ONLY. Others may not work. X * Lawrence Strickland, St. Petersburg Jr. College X * ...usfvax2!3b2bame!jc3b21!larry X * NOTICE: This program may be copied and used as desired X * but NOT sold or used for commercial gain! X */ X X#include <stdio.h> X#include <sys/types.h> X#include <utmp.h> X#include <time.h> X#include <sys/utsname.h> X X#define FALSE 0 X#define TRUE 1 X#define MAXPORTS 24 X#define LINECNT 18 X#define PORTSIZE 128 X#define MODEMCNT 3 X X/***** structure definitions *****/ X Xstruct ports X { X char *name; X int port; X } ; X Xstruct ports tline[MAXPORTS+1] = { "console", 0, "syscon", 0, "contty", 1, X "tty11", 2, "tty12", 3, "tty13", 4, "tty14", 5, X "tty21", 6, "tty22", 7, "tty23", 8, "tty24", 9, X "tty31", 10, "tty32", 11, "tty33", 12, "tty34", 13, X "tty41", 14, "tty42", 15, "tty43", 16, "tty44", 17, X "", -1, "", -1, "", -1, "", -1, "", -1, "", -1 }; X Xstruct linerec X { X char line[12]; X time_t on, off; X struct linerec *link; X }; X Xstruct idnode X { X char uid[8]; X int uses; X time_t ttime; X struct idnode *left, *right; X struct linerec *link; X }; X Xstruct X { X char uid[8]; X char line[12]; X time_t on; X int login; X int uses; X } modem[MAXPORTS]; X X/***** global variables *****/ X Xchar *prog; Xstruct idnode *logoff(); Xlong TotUses=0L, TotUsage=0L; Xint linecnt=LINECNT; Xchar *modemfile = "/etc/modemlines"; X X/***** option variables *****/ X Xint detail=FALSE; /* do detail for users */ Xint modemonly=FALSE; /* do only modem lines */ Xint summary=TRUE; /* do normal listing */ Xint termsum=FALSE; /* do a terminal summary */ Xint procsum=FALSE; /* do a process summary */ Xint emptflg=FALSE; /* accrue empty processes */ Xint runflg=FALSE; /* accrue run-time processes */ Xint bootflg=FALSE; /* accrue boot processes */ Xint timeflg=FALSE; /* accrue old/new time processes */ Xint initflg=FALSE; /* accrue init processes */ Xint logflg=FALSE; /* accrue LOGIN processes */ Xint acctflg=FALSE; /* accrue ACCOUNTING processes */ X X/***** start of main program *****/ X Xmain(argc,argv) Xint argc; Xchar **argv; X{ XFILE *fp; Xlong time(), time_tmp; Xstruct utsname sname; Xstruct utmp who; Xstruct idnode *root = NULL; Xint ml,c,p; Xextern char *optarg; Xextern int optind; Xchar *wtmp=WTMP_FILE, *rtype, *rport, *ctime(), *mfind(), *malloc(), *fgets(); Xstatic int pcount[] = {0,0,0,0,0,0,0,0,0,0}; Xstatic char *pname[] = {"Empty","Run Level","Boot Time","Old Time","New Time", X "Init Process","Login Process","User Process","Dead Process", X "Accounting"}; X Xprog = argv[0]; X Xfor(ml = 0; ml < MAXPORTS; ml++) X { X modem[ml].login = FALSE; X modem[ml].uses = 0; X } X Xwhile((c = getopt(argc,argv,"admptxABEILM:RT")) != EOF) X switch(c) X { X case 'a': emptflg = runflg = bootflg = timeflg = TRUE; X initflg = logflg = acctflg = TRUE; X break; X case 'd': detail = TRUE; X break; X case 'm': modemonly = TRUE; X break; X case 'p': procsum = TRUE; X break; X case 't': termsum = TRUE; X break; X case 'x': summary = FALSE; X break; X case 'A': acctflg = TRUE; X break; X case 'B': bootflg = TRUE; X break; X case 'E': emptflg = TRUE; X break; X case 'I': initflg = TRUE; X break; X case 'L': logflg = TRUE; X break; X case 'M': modemfile = optarg; X modemonly = TRUE; X break; X case 'R': runflg = TRUE; X break; X case 'T': timeflg = TRUE; X break; X } X Xif(optind < argc) wtmp = argv[optind]; X Xif(modemonly) X { X if((fp = fopen(modemfile,"r")) == NULL) X fprintf(stderr,"%s: can't open modem file '%s'\n",prog,modemfile), X exit(1); X for(linecnt = 0; linecnt < MAXPORTS; linecnt++) X { X if((tline[linecnt].name = malloc(PORTSIZE)) == NULL) X fprintf(stderr,"%s: out of memory getting lines.\n",prog), exit(1); X if(fgets(tline[linecnt].name,PORTSIZE,fp) == NULL) X break; X tline[linecnt].name[strlen(tline[linecnt].name)-1] = '\0'; X if(tline[linecnt].name[0] == '#') X { X free(tline[linecnt].name); X linecnt--; X continue; X } X tline[linecnt].port = linecnt; X } X tline[linecnt].name = ""; X tline[linecnt].port = -1; X fclose(fp); X } X Xif((fp = fopen(wtmp,"r")) == NULL) X fprintf(stderr,"%s: can't open '%s'.\n",prog,wtmp), exit(1); X Xwhile(fread(&who,sizeof(who),1,fp) > 0) X { X if((who.ut_type >= EMPTY) && (who.ut_type <= ACCOUNTING)) X pcount[who.ut_type]++; X if((ml = getml(who.ut_line)) < 0) X continue; /* this was not a legal port */ X if(legalprocess(who.ut_type)) X { X if(modem[ml].login) X root = logoff(&who,ml,root); X logon(&who,ml); X } X else if((who.ut_type == DEAD_PROCESS) && (modem[ml].login)) X { X modem[ml].login = FALSE; X root = logoff(&who,ml,root); X } X } X Xfclose(fp); X Xuname(&sname); Xrtype = detail ? "Detailed" : " "; Xrport = modemonly ? "Modem" : " User"; Xtime_tmp = time(0L); X Xif(summary) X { X printf("\n\n%-9.9s(%-9.9s) ",sname.sysname,sname.nodename); X printf("%-8.8s %-5.5s Profile %s\n",rtype,rport,ctime(&time_tmp)); X X if(detail) X printf("User Port Start End "); X else X printf("User ID Logins"); X printf(" Time Used\n"); X printf("---------------------------------------"); X printf("---------------------------------------\n"); X printtree(root); X printf("---------------------------------------"); X printf("---------------------------------------\n"); X printf("Total Uses/Time %8ld",TotUses); X puttime(TotUsage); X printf("\n"); X } X Xif(procsum) X { X printf("\n\nProcess Summary\n\n"); X printf(" Process Count "); X printf(" Process Count "); X printf(" Process Count\n"); X printf(" --------------------------"); X printf("--------------------------"); X printf("-------------------------\n"); X for(ml = 0; ml <= ACCOUNTING; ml++) X { X printf(" %-15.15s%8d",pname[ml],pcount[ml]); X if((ml % 3) == 2) X printf("\n"); X else X printf(" "); X } X if((ml % 3) != 2) X printf("\n"); X } X Xif(termsum) X { X printf("\n\nTerminal Summary\n\n"); X printf("Terminal Uses Logged On "); X printf(" Terminal Uses Logged On\n"); X printf("-----------------------------------------"); X printf("-------------------------------------\n"); X for(ml = 0; ml < linecnt; ml++) X { X printf("%-12.12s %8d %8.8s",mfind(ml),modem[ml].uses, X ((modem[ml].login)?modem[ml].uid:"")); X if((ml % 2) == 0) X printf(" "); X else X printf("\n"); X } X if((ml % 2) != 0) X printf("\n"); X } X Xreturn(0); X} X Xgetml(s) Xchar *s; X{ Xint i; X Xfor(i = 0; *(tline[i].name) != '\0'; i++) X if(strcmp(tline[i].name,s) == 0) break; Xreturn(tline[i].port); X} X Xlogon(who,ml) Xstruct utmp *who; Xint ml; X{ Xmodem[ml].login = TRUE; Xstrcpy(modem[ml].uid, who->ut_user); Xstrcpy(modem[ml].line, who->ut_line); Xmodem[ml].on = who->ut_time; X} X Xstruct idnode *logoff(who,ml,root) Xstruct utmp *who; Xint ml; Xstruct idnode *root; X{ Xchar *malloc(); Xstruct linerec *newline; Xint cmpval; X Xif(root == NULL) X { X root = (struct idnode *)malloc(sizeof(struct idnode)); X if(root == NULL) X fprintf(stderr,"%s: out of memory building tree.\n",prog), exit(1); X strcpy(root->uid, modem[ml].uid); X root->left = root->right = NULL; X root->link = (struct linerec *)malloc(sizeof(struct linerec)); X if(root->link == NULL) X fprintf(stderr,"%s: out of memory creating list.\n",prog), exit(1); X strcpy((root->link)->line, modem[ml].line); X (root->link)->on = modem[ml].on; X (root->link)->off = who->ut_time; X (root->link)->link = NULL; X root->uses = 1; X root->ttime = ((root->link)->off) - ((root->link)->on); X TotUses++; X TotUsage += root->ttime; X modem[ml].uses++; X } Xelse if((cmpval = strcmp(modem[ml].uid,root->uid)) > 0) X root->right = logoff(who,ml,root->right); Xelse if(cmpval < 0) X root->left = logoff(who,ml,root->left); Xelse /* this user already in tree */ X { X newline = (struct linerec *)malloc(sizeof(struct linerec)); X if(newline == NULL) X fprintf(stderr,"%s: out of memory adding line.\n",prog), exit(1); X strcpy(newline->line, modem[ml].line); X newline->on = modem[ml].on; X newline->off = who->ut_time; X newline->link = root->link; X root->link = newline; X root->uses += 1; X root->ttime += ((root->link)->off) - ((root->link)->on); X TotUses++; X TotUsage += ((root->link)->off) - ((root->link)->on); X modem[ml].uses++; X } X Xreturn(root); X} X Xprinttree(root) Xstruct idnode *root; X{ Xstruct linerec *p; Xint cmpval; X Xif(root != NULL) X { X printtree(root->left); X if(detail) X { X p = root->link; X printf("\n%-8.8s %-8.8s",root->uid,p->line); X dotime(p->on); X dotime(p->off); X puttime(p->off - p->on); X printf("\n"); X p = p->link; X while(p) X { X printf(" %-8.8s",p->line); X dotime(p->on); X dotime(p->off); X puttime(p->off - p->on); X printf("\n"); X p = p->link; X } X } X if(detail) X printf(" %-8.8s %8d","Total",root->uses); X else X printf("%-8.8s %8d",root->uid,root->uses); X puttime(root->ttime); X printf("\n"); X printtree(root->right); X } X} X Xdotime(val) Xlong val; X{ Xstruct tm *localtime(), *tval; X Xtval = localtime(&val); X Xprintf(" %02d:%02d:%02d",tval->tm_hour,tval->tm_min,tval->tm_sec); X} X Xchar *mfind(ml) Xint ml; X{ Xint i; X Xfor(i = 0; i < linecnt; i++) X if((*(tline[i].name) == NULL) || (tline[i].port == ml)) break; Xreturn(tline[i].name); X} X Xlegalprocess(t) Xint t; X{ Xint val=FALSE; X Xif(t == USER_PROCESS) X val = TRUE; Xelse if((t == EMPTY) && emptflg) X val = TRUE; Xelse if((t == RUN_LVL) && runflg) X val = TRUE; Xelse if((t == BOOT_TIME) && bootflg) X val = TRUE; Xelse if((t == OLD_TIME) && timeflg) X val = TRUE; Xelse if((t == NEW_TIME) && timeflg) X val = TRUE; Xelse if((t == INIT_PROCESS) && initflg) X val = TRUE; Xelse if((t == LOGIN_PROCESS) && logflg) X val = TRUE; Xelse if((t == ACCOUNTING) && acctflg) X val = TRUE; X Xreturn(val); X} End of ua.c echo Unbundling puttime.c 1>&2 sed "s/^X//" >puttime.c <<\'End of puttime.c\' Xputtime(val) Xlong val; X{ Xstatic long spm = 60L; /* seconds per minute */ Xstatic long sph = 3600L; /* seconds per hour */ Xlong htemp, mtemp; X Xhtemp = val/sph; Xif (val < 0L) X { X printf(" *** Negative time value ***"); X return; X } X Xif (htemp == 0) X printf(" "); Xelse if (htemp == 1) X printf(" %9ld Hour ",htemp); Xelse X printf(" %9ld Hours",htemp); Xval %= sph; X Xmtemp = val/spm; Xif ( (mtemp == 0) && (htemp == 0) ) X printf(" "); Xelse if (mtemp == 1) X printf(" %2ld Minute ",mtemp); Xelse X printf(" %2ld Minutes",mtemp); Xval %= spm; X Xif (val == 1) X printf(" %2ld Second",val); Xelse X printf(" %2ld Seconds",val); X} End of puttime.c echo Unbundling ua.1l 1>&2 sed "s/^X//" >ua.1l <<\'End of ua.1l\' X.TH UA 1 LOCAL X.SH NAME Xua -- produce a user/process profile of the system X.SH SYNOPSIS X.I ua X[-dmptx] [-aABDEILRT] [-M line_file] wtmp-like-file X.SH DESCRIPTION X.P X.I Ua Xproduces a report detailing number of times logged in and total user Xtime for each user plus totals. It may also produce a summary of all Xprocesses; a summary of terminal usage; a modem only summary; or a Xdetailed summary. X.P XIf a file name is given on the command line, it must be similar in structure Xto X.B /etc/wtmp Xor errors will occur. If no filename is given, X.B /etc/wtmp Xis used by default. The global options are: X.TP 10 X-d Xproduce a X.B detailed Xsummary including times on and off for each use plus totals. X.TP 10 X-m Xproduce a X.B modem only Xsummary. Either \fB/etc/lines\fP or the specified file (see -M) Xwill be read for a list of ports to be summarized. X.TP 10 X-p Xproduce a X.B process Xsummary. X.TP 10 X-t Xproduce a X.B terminal Xsummary. X.TP 10 X-x X.B exclude Xthe normal user summary. X.P XUnder normal circumstances, only X.I USER Xprocesses are included in the user summary. All other process types are Xincluded in the process summary but excluded elsewhere. The following options Xwill include these process types both in the user summary and the terminal Xsummary: X.TP 10 X-A Xinclude X.B Accounting Xprocesses X.TP 10 X-B Xinclude X.B Boot Time Xprocesses X.TP 10 X-E Xinclude X.B Empty Xprocesses X.TP 10 X-I Xinclude X.B Init Xprocesses X.TP 10 X-L Xinclude X.B Login Xprocesses X.TP 10 X-R Xinclude X.B Run Level Xprocesses X.TP 10 X-T Xinclude X.B New Time and Old Time Xprocesses X.TP 10 X-a Xinclude all of the above processes X.P XNormally, all ports available to the system are checked for activity. If Xthe -m (modem) option is chosen, only the ports specified in the X\fB/etc/modemlines\fP file are checked. Typically, this will be for modem Xline usage. If desired, a separate file may be used by including the: X.TP 10 X-M Xinclude X.B Xmodem lines Xfrom the selected file name. Each line should be one of the port names to Xbe found in \fB/etc/wtmp\fP. Lines starting with a '\#' are considered Xcomment lines. Lines must be no more than 128 characters long. The -M option Ximplies the -m option. X.SH FILES X.P X/etc/wtmp, /etc/modemlines X.SH SEE ALSO Xwho(1) X.SH BUGS X.P XCertain combinations of time changes may result in incorrect or even Xnegative times for some processes. Negative times are trapped and not Xprinted, but incorrect times are not. It is best if time changes are done Xonly in single-user mode. XThe run-level process should include the new run level, it does not. XNo paging of the output is done. End of ua.1l echo Unbundling ua.help 1>&2 sed "s/^X//" >ua.help <<\'End of ua.help\' X X X X UUUUAAAA((((1111)))) UUUUNNNNIIIIXXXX 5555....0000 ((((LLLLOOOOCCCCAAAALLLL)))) UUUUAAAA((((1111)))) X X X X NNNNAAAAMMMMEEEE X ua -- produce a user/process profile of the system X X SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS X _u_a [-dmptx] [-aABDEILRT] [-M line_file] wtmp-like-file X X DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN X _U_a produces a report detailing number of times logged in and X total user time for each user plus totals. It may also X produce a summary of all processes; a summary of terminal X usage; a modem only summary; or a detailed summary. X X If a file name is given on the command line, it must be X similar in structure to ////eeeettttcccc////wwwwttttmmmmpppp or errors will occur. If X no filename is given, ////eeeettttcccc////wwwwttttmmmmpppp is used by default. The X global options are: X X -d produce a ddddeeeettttaaaaiiiilllleeeedddd summary including times on and X off for each use plus totals. X X -m produce a mmmmooooddddeeeemmmm oooonnnnllllyyyy summary. Either ////eeeettttcccc////lllliiiinnnneeeessss X or the specified file (see -M) will be read for a X list of ports to be summarized. X X -p produce a pppprrrroooocccceeeessssssss summary. X X -t produce a tttteeeerrrrmmmmiiiinnnnaaaallll summary. X X -x eeeexxxxcccclllluuuuddddeeee the normal user summary. X X Under normal circumstances, only _U_S_E_R processes are included X in the user summary. All other process types are included X in the process summary but excluded elsewhere. The X following options will include these process types both in X the user summary and the terminal summary: X X -A include AAAAccccccccoooouuuunnnnttttiiiinnnngggg processes X X -B include BBBBooooooootttt TTTTiiiimmmmeeee processes X X -E include EEEEmmmmppppttttyyyy processes X X -I include IIIInnnniiiitttt processes X X -L include LLLLooooggggiiiinnnn processes X X -R include RRRRuuuunnnn LLLLeeeevvvveeeellll processes X X -T include NNNNeeeewwww TTTTiiiimmmmeeee aaaannnndddd OOOOlllldddd TTTTiiiimmmmeeee processes X X -a include all of the above processes X X X X X Page 1 (printed 9/4/85) X X X X X X X UUUUAAAA((((1111)))) UUUUNNNNIIIIXXXX 5555....0000 ((((LLLLOOOOCCCCAAAALLLL)))) UUUUAAAA((((1111)))) X X X X Normally, all ports available to the system are checked for X activity. If the -m (modem) option is chosen, only the X ports specified in the ////eeeettttcccc////mmmmooooddddeeeemmmmlllliiiinnnneeeessss file are checked. X Typically, this will be for modem line usage. If desired, a X separate file may be used by including the: X X -M include mmmmooooddddeeeemmmm lllliiiinnnneeeessss from the selected file name. X Each line should be one of the port names to be X found in ////eeeettttcccc////wwwwttttmmmmpppp. Lines starting with a '#' are X considered comment lines. Lines must be no more X than 128 characters long. The -M option implies X the -m option. X X FFFFIIIILLLLEEEESSSS X /etc/wtmp, /etc/modemlines X X SSSSEEEEEEEE AAAALLLLSSSSOOOO X who(1) X X BBBBUUUUGGGGSSSS X Certain combinations of time changes may result in incorrect X or even negative times for some processes. Negative times X are trapped and not printed, but incorrect times are not. X It is best if time changes are done only in single-user X mode. The run-level process should include the new run X level, it does not. No paging of the output is done. X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X Page 2 (printed 9/4/85) X X X End of ua.help