Newsgroups: comp.sources.unix From: lytras@avalon.unizh.ch (Apostolos Lytras) Subject: v28i083: freeks-1.33 - extended login accounting, V1.33, Part01/01 Message-id: <1.773910551.9561@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: lytras@avalon.unizh.ch (Apostolos Lytras) Posting-Number: Volume 28, Issue 83 Archive-Name: freeks-1.33/part01 Here's a revised version of 'freeks'. I have received some patches for NetBSD and BSD/386 as well as a fix for a bug in gecos.c. This time even the shar and the Makefile should work correctly ;-). - Apostolos # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # ./freeks-1.33 # ./freeks-1.33/Makefile # ./freeks-1.33/README # ./freeks-1.33/configfile-example # ./freeks-1.33/freeks.6 # ./freeks-1.33/freeks.c # ./freeks-1.33/freeks.cat # ./freeks-1.33/freeks.h # ./freeks-1.33/gecos.c # echo c - ./freeks-1.33 mkdir ./freeks-1.33 > /dev/null 2>&1 echo x - ./freeks-1.33/Makefile sed 's/^X//' >./freeks-1.33/Makefile << 'END-of-./freeks-1.33/Makefile' X# Makefile for freeks 1.33 X X# Configure this for your system (default is {SunOS,Ultrix} with gcc) X# X# NOTE: if your system has far more (or far less) users than 1021, you X# may wish to change the value for HASHTABLESIZE. That value must X# be prime. You can change it in freeks.c, or here by adding X# -DHASHTABLESIZE=2003 (for about 2000 users for example) to CFLAGS. X# If you have a lot of users and you want to improve performance a bit, X# it's probably best to test a prime number around (1.5*number_of_users) X# which should cause less searching for free positions in the hash X# table (the searching is linear!) when the table gets full. X# X# NOTE FOR SYSTEM V: System V doesn't log shutdowns by default. If you X# want accurate information about system uptime then create a X# shutdown user whose login will indicate that the system was shut X# down. Then define CONFIGFILE in freeks.c (or call the account X# "shutdown") and create the configfile in the appropriate X# place. X X# Some general Variables X# Xprefix = /usr/local Xpostfix = 6 XMANPATH = ${prefix}/man/man${postfix} XCP = install -c -m XMODE1 = 755 XMODE2 = 644 XOBJS = freeks.o gecos.o X X# BSD/386 1.0 with gcc X# X#CC = cc X#CFLAGS = -O X X# SunOS 4.1.3 / Ultrix 4.0 with gcc X# XCC = gcc XCFLAGS = -Wall -W -Wno-implicit -O X X# SunOS 4.1.3 with Sun C compiler X# X#CC = cc X#CFLAGS = -O X X# Slowaris 2.3 with gcc X# X#CC = gcc X#CFLAGS = -Wall -W -O -DSYSV X X# HP/UX 9.0x. X# X# CC = cc X# CFLAGS = -Aa -O X X# Nextstep 3.x with NeXT's (g)cc X# X# CC = cc X# CFLAGS = -Wall -W -Dnextstep3 -O X X# more flags X# DEBUGFLAGS = -DDEBUG -g -ansi -pedantic -DWANTGECOS XDEBUGFLAGS = X# LDFLAGS = -s XLDFLAGS = X X# -------- from here on you shouldn't need to change anything --- X Xall: freeks X Xfreeks: ${OBJS} X ${CC} ${CFLAGS} ${DEBUGFLAGS} ${LDFLAGS} -o $@ ${OBJS} X Xfreeks.o: X ${CC} ${CFLAGS} ${DEBUGFLAGS} -c $*.c X Xgecos.o: X ${CC} ${CFLAGS} ${DEBUGFLAGS} -c $*.c X Xinstall: all X ${CP} ${MODE1} freeks ${prefix}/bin; \ X ${CP} ${MODE2} freeks.6 ${MANPATH}/freeks.${postfix} X Xclean: X rm -f freeks *~ core *.o END-of-./freeks-1.33/Makefile echo x - ./freeks-1.33/README sed 's/^X//' >./freeks-1.33/README << 'END-of-./freeks-1.33/README' XFirst of all read the manual page... X(e.g. with 'nroff -man freeks.6 | more') X XINSTALLATION: X X1) Edit the Makefile to match your system. X2) Check if you have to change anything in 'freeks.h' X X########### NOTE : ############# X XIf your system has _more_ than HASHTABLESIZE users then you _must_ set XHASHTABLESIZE to a higher value (preferably a prime) in freeks.h, or Xyou will get an error (Hash table full) when you run freeks. X X################################ X X3) 'make' X4) Test-run freeks. X5) 'make install' X6) 'make clean' X XPORTABILITY: X XThis has been run on the following systems, I say ;-): XMachine Type Operating System Compiler XDecsystem 5200 Ultrix 4.0 gcc 2.2.2 XNextstation m68k Nextstep 3.2 cc (Next's Version of gcc 2.2.2) XSparcstation 1 SunOS 4.1.3 gcc 2.5.8 XSparcClassic SunOS 5.3 (Solaris 2.3) gcc 2.5.8 XIntel 486 NetBSD 0.9B gcc 2.4.5 XIntel x86 BSD/386 ?? X XTHANKS: X Xbruce@slc.com (Bruce Schuchardt) sent me some patches to make freeks Xrun on HP/UX 9.0x with HP's cc. I've built them into the source. He said Xthat there are a couple of minor compilation warnings which could be Xignored. X XWietse Venema for his feedback on SysV peculiarities... X XTerry Kennedy and Mark Delany X for their patches for BSD/386 and NetBSD. X XEnjoy! X- Apostolos Lytras (lytras@avalon.unizh.ch) END-of-./freeks-1.33/README echo x - ./freeks-1.33/configfile-example sed 's/^X//' >./freeks-1.33/configfile-example << 'END-of-./freeks-1.33/configfile-example' Xhalt Xarmageddon Xshutdown Xpowerdown END-of-./freeks-1.33/configfile-example echo x - ./freeks-1.33/freeks.6 sed 's/^X//' >./freeks-1.33/freeks.6 << 'END-of-./freeks-1.33/freeks.6' X.TH freeks 6 "July 10, 1994" "Apostolos Lytras" "Freeks 1.33 Manual" X.SH NAME Xfreeks \- extended login accounting X.SH SYNOPSIS Xfreeks [\-w X.I wtmp X] [\-t] [\-c X.I configfile X] X X.SH DESCRIPTION X.B Freeks Xproduces a report on system usage based on the contents of X.I wtmp Xand sorted by the actual time different users have spent on the Xsystem. XThe first few lines contain general information about Xthe system. X X X.SH OPTIONS XThe default for X.I wtmp Xis "/usr/adm/wtmp". XYou may specify an alternate X.I wtmp Xfile on the command line. X XWith the 't' option the user statistics are modified, showing the X\'rank\', the name, the time the user was actually logged in and Xthe number of logins. X XWith the \'c\' option you can specify a X.I configfile Xcontaining the names of alternative 'shutdown' users (cf. TODO), which Xis probably interesting to SysV machine owners only. X X.SH BUGS X.B Freeks Xis (almost) limited to BSD style X.I wtmp Xfiles (cf. TODO below). It does not have a lot of fancy options. XFreeks does make some mistakes, e.g. when a reboot entry in X.I wtmp Xhappened without a shutdown before it. If the last entry happened more Xthan a day before the reboot entry, freeks presumes that the machine Xcrashed, and will use the time of the last entry before the crash as Xtime of shutdown. This may lead to marginal errors in 'uptime' and the Xlogin times of users still logged in at that time. X XAnother bug happens when time is changed in single user mode which Xmeans it doesn't get logged. Don't do that, or delete the X.I wtmp Xfile afterwards. X X.SH TODO XSystem V has changed a lot in X.I wtmp Xbut it should be possible to run the program, if you rarely shut Xyour machine down or have a dedicated 'shutdown' user that logs in Xwhen you shut down. If this user is called 'shutdown' you needn't Xdo nothing, if his login name is something else, you can specify that in X.I configfile X(if, of course, freeks had been compiled with CONFIGFILE defined...). X X X.SH "SEE ALSO" X.BR geeks(5) X.BR geeks(6) X.BR ac(8) X.BR wtmp(5) X.BR wtmpfix(8) X.BR login(1) X.BR init(8) X X.SH FILES X.PD 0 X.TP 20 X.B /usr/adm/wtmp Xwtmp file X X.SH AUTHOR XCopyright (C) 1994 by Apostolos Lytras (lytras@avalon.unizh.ch). X XThanks to Dave P. Gymer (dpg@cs.nott.ac.uk) and everyone that helped Xhim write geeks(6). It has been an important inspiration and a reason Xto write this program (because it was just too slow, geeks! ;-). X XThanks also to Wietse Venema for his information on SVR4's wtmp. X XSome of the credit must also go to the University of California, XBerkeley, for their programs ac(8), last(1) and uptime(1), which Xevaluate /usr/adm/wtmp as well. These programs were inspiring as well, Xbecause they couldn't do everything real geeks needed them to. X XThis is free software; you can redistribute it and/or modify it under Xthe terms of the GNU General Public License as published by the Free XSoftware Foundation; either version 2 of the License, or (at your Xoption) any later version. X XThe GNU General Public License is not included in the original Xdistrubution of freeks. If you don't have a copy of it already, write Xto the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, XUSA. X XThis program is distributed in the hope that it will be useful, but XWITHOUT ANY WARRANTY; without even the implied warranty of XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the XGNU General Public License for more details. X END-of-./freeks-1.33/freeks.6 echo x - ./freeks-1.33/freeks.c sed 's/^X//' >./freeks-1.33/freeks.c << 'END-of-./freeks-1.33/freeks.c' X/********************************************************************/ X/* */ X/* freeks.c - login accounting */ X/* Copyright (C) 1994 by Apostolos Lytras (lytras@avalon.unizh.ch) */ X/* Version 1.33, July 10, 1994 */ X/* */ X/* 1.33 NetBSD, BSD/386 patches from Mark Delany and */ X/* Terry Kennedy, who also found and fixed a bug */ X/* in gecos.c */ X/* 1.32 SysV support almost in. fixed more bugs. */ X/* 1.31 time handling still not okay. */ X/* finally got some SysV help from Wietse Venema */ X/* 1.30 first attempt to include SysV support */ X/* 1.22 included hints for HP/UX 9.0x with */ X/* CLASSIC_ID_TYPES */ X/* suggested by bruce@slc.com (Bruce Schuchardt) */ X/* 1.21 included bugfix for J_hash suggested by */ X/* awick@csugrad.cs.vt.edu (Andy Wick) */ X/* posted to alt.sources March 30, 1994 */ X/* 1.20 first edition, posted to alt.sources */ X/* March 29, 1994 */ X/* */ X/* */ X/* See the manual page (freeks.6) distributed with this file, for */ X/* restriction, warranty, distribution and copyright information. */ X/* */ X/********************************************************************/ X X#include "freeks.h" X X#ifdef NO_STRDUP Xchar * Xstrdup(char *s) X{ X char *t = (char *) malloc(strlen(s) + 1); X X return t ? strcpy(t, s) : t; X} X#else Xextern char *strdup(); X#endif X X XUSER userlist[HASHTABLESIZE]; XTTY *ttylist; X Xlong s_uptime; Xlong s_start; Xlong s_corr = 0; Xlong t_corr = 0; Xlong s_stop; Xunsigned int s_reboots = 0; Xunsigned int s_shutdowns = 0; Xunsigned int s_crashes = 0; Xunsigned int s_logouts = 0; Xunsigned int s_logins = 0; Xunsigned int toptenmode = 0; Xlong now = 0; Xstatic int shutd_state = FALSE; X X#ifdef __STDC__ Xvoid J_init_all(void) X#else XJ_init_all() X#endif X{ int i; X for ( i = 0 ; i <= HASHTABLESIZE ; i++) X { X if (userlist[i].name != NULL) X { X userlist[i].name = NULL; X userlist[i].current_logins = 0; X userlist[i].max_conc_logins = 0; X userlist[i].logins = 0; X userlist[i].logintimebuf = 0; X userlist[i].needs_timeset = FALSE; X userlist[i].time = 0; X userlist[i].total = 0; X userlist[i].max_time = 0; X } X } X ttylist = NULL; X s_uptime = 0; X s_start = 0; X s_corr = 0; X t_corr =0; X s_stop = 0; X s_reboots = 0; X s_shutdowns = 0; X s_crashes = 0; X s_logouts = 0; X s_logins = 0; X shutd_state = FALSE; X now = 0; X} X X#ifdef CONFIGFILE X XPRIVILEGED *shutdownusers; X XPRIVILEGED *config_init(filename) Xchar *filename; X{ X FILE *fp; X PRIVILEGED *tmp = (PRIVILEGED *)NULL; X char priv_user[UT_NAMESIZE]; X X fp = (FILE *)fopen(filename,"r"); X if (fp == NULL) { X fprintf(stderr,"Warning: Could not open config file (%s)\n", X filename ); return((PRIVILEGED *)NULL); X } X while (fscanf(fp,"%s",&priv_user) != EOF) { X priv_user[UT_NAMESIZE] = '\0'; X if ((tmp = (PRIVILEGED *)malloc(sizeof(PRIVILEGED))) == (PRIVILEGED *)NULL) X fprintf(stderr,("Fatal: malloc failed in config\n")); X else { X tmp->next = shutdownusers; X tmp->user = (char *) strdup (priv_user); X shutdownusers = tmp; X } X } X if (fclose(fp) == 0) X { X return(tmp); X } X else X { X fprintf(stderr, "Fatal: couldn't close %s\n", filename); X exit(1); X } X} X Xint privilege_check(username,list) Xchar *username; XPRIVILEGED *list; X{ X PRIVILEGED *T; X X if (list != NULL) { X for (T=list ; T != NULL ; T = T->next) { X#ifdef DEBUG X fprintf(stderr,"Debug: privileges test %s for %s\n",list->user, username); X#endif /* DEBUG */ X if (!strncmp(T->user,username,UT_NAMESIZE)) return TRUE; X } X }; X return FALSE; X} X X#endif /* CONFIGFILE */ X Xstatic int XSedgeHash(arg) Xchar *arg; X{ X int h; X X for (h = 0; *arg != '\0'; arg++) X { X h = (64 * h + *arg) % HASHTABLESIZE; X } X return h; X} X Xstatic int XJ_hash(arg) Xchar *arg; X{ X int Index, i, count=0; X X Index = (int) SedgeHash(arg); X X if (userlist[Index].name == NULL) X { X return Index; X } X for (i = Index; userlist[i].name != NULL; i++) X { X count++; X if (i == HASHTABLESIZE) X { X i = 0; X if (userlist[i].name == NULL) X return i; X } X else if (count == HASHTABLESIZE) X { X fprintf(stderr, "Fatal: Hash table is full.\n"); X exit(2); X }; X if (strcmp(userlist[i].name, arg) == 0) X return i; X } X return i; X} X Xint Xucompare(i, j) XUSER *i, *j; X{ X if (i->time > j->time) X return -1; X else if (i->time < j->time) X return 1; X return 0; X} X Xstatic TTY * XJ_gettty(thetty) Xchar *thetty; X{ X register TTY *cur, *T; X X if (ttylist != NULL) X { X for (T = ttylist; T != NULL; T = T->next) X { X if (strncmp(thetty, T->tty, UT_LINESIZE) == 0) X { X return (T); X }; X } X }; X if ((cur = (TTY *) malloc(sizeof(TTY))) == NULL) X { X fprintf(stderr, "Fatal: malloc failed !\n"); X exit(1); X } X cur->next = ttylist; X cur->tty = (char *) strdup(thetty); X ttylist = cur; X return (cur); X} X Xstatic int XJ_countlogins(uhash) Xint uhash; X{ X register TTY *curt; X int count = 0; X X if (ttylist != NULL) X { X for (curt = ttylist; curt != NULL; curt = curt->next) X { X if ((curt->used) && (uhash == curt->user_hash)) X { X count++; X } X else X continue; X } X }; X return count; X} X Xstatic void XJ_logout(logouttime, cur) Xlong logouttime; XTTY *cur; X{ X long stayed; X long definite; X int current; X X if (cur->used == 0) X { X return; X } X else X { X cur->used = 0; X if ((logouttime != now) || (now == 0)) X /* avoid counting users still logged in */ X { X s_logouts++; X } X current = J_countlogins(cur->user_hash); X userlist[cur->user_hash].current_logins = current; X stayed = logouttime - cur->time_in; X if (stayed < 0) X { X fprintf(stderr,"Error: user %s has a negative session time.\n", X userlist[cur->user_hash].name); X }; X if (stayed > userlist[cur->user_hash].max_time) X { X userlist[cur->user_hash].max_time = stayed; X }; X userlist[cur->user_hash].total += stayed; X if (current == 0) X { X definite = logouttime - userlist[cur->user_hash].logintimebuf; X userlist[cur->user_hash].time += definite; X }; X } X} X Xstatic void XJ_login(inname, logintime, tty) Xchar *inname, *tty; Xlong logintime; X{ X int uhash; X register TTY *thistty; X X X uhash = J_hash(inname); X if (userlist[uhash].name == NULL) X { X userlist[uhash].name = (char *) strdup(inname); X } X else X { X while (strncmp(userlist[uhash].name, inname, sizeof(inname)) != 0) X { X fprintf(stderr, "Fatal: could not avoid hash collision at %d: %s %s\n", X uhash, inname, userlist[uhash].name); X exit(2); X } X } X thistty = J_gettty(tty); X if (thistty->used) X { /* the tty is already in use */ X if (strcmp(userlist[thistty->user_hash].name,userlist[uhash].name) == 0) X { /* the newly logged in user is already logged in */ X return; X } X else X { X J_logout(logintime, thistty); X X#ifdef DEBUG X fprintf(stderr, "Debug: %s did not log out. Replaced by %s...\n", X userlist[thistty->user_hash].name, X userlist[uhash].name); X#endif X } X }; X s_logins++; X userlist[uhash].logins++; X if (userlist[uhash].current_logins < 0) X { X userlist[uhash].current_logins = 1; X } X else X { X userlist[uhash].current_logins++; X } X if (userlist[uhash].current_logins == 1) X { X userlist[uhash].logintimebuf = logintime; X }; X if (userlist[uhash].max_conc_logins < userlist[uhash].current_logins) X { X userlist[uhash].max_conc_logins = userlist[uhash].current_logins; X }; X thistty->time_in = logintime; X thistty->user_hash = uhash; X thistty->used = 1; X} X X#if __STDC__ Xstatic void XJ_timeprep(void) X#else Xstatic void XJ_timeprep() X#endif X{ X register TTY *cur; X X if (ttylist != NULL) X { X for (cur = ttylist; cur != NULL; cur = cur->next) X { X if (cur->used == 1) X { X userlist[cur->user_hash].needs_timeset = TRUE; X }; X } X }; X} X Xstatic void XJ_timeset(oldtime, newtime) Xlong oldtime, newtime; X{ X long diff; X register TTY *cur; X X diff = newtime - oldtime; X if (ttylist != NULL) X { X for (cur = ttylist; cur != NULL; cur = cur->next) X { X if (cur->used == 1) X { X cur->time_in += diff; X if (userlist[cur->user_hash].needs_timeset == TRUE) X { X userlist[cur->user_hash].logintimebuf += diff; X userlist[cur->user_hash].needs_timeset = FALSE; X }; X }; X } X }; X s_corr += diff; X t_corr += diff; X#ifdef DEBUG X fprintf(stderr, "Debug: date got set. Difference is: %ld\n", diff); X#endif X} X Xvoid XJ_cleanup(endtime) Xlong endtime; X{ X register TTY *cur; X X if (ttylist != NULL) X { X for (cur = ttylist; cur != NULL; cur = cur->next) X { X if (cur->used) X { X J_logout(endtime, cur); X } X else X continue; X } X }; X} X Xstatic void XJ_reboot(sometime, reboottime, shutdowntime) Xlong sometime, reboottime, shutdowntime; X{ X long thetime, timediff; X X if (shutd_state == TRUE) X { X timediff = reboottime - shutdowntime; X s_corr += timediff; X if (timediff < 0) X { X thetime = reboottime; X } X else X { X thetime = shutdowntime; X } X s_reboots++; X } X else X { X if (((reboottime - sometime) > 86400) || (sometime > shutdowntime)) X { X fprintf(stderr, "Warning: You have had a serious crash, I suppose\n"); X thetime = sometime; X timediff = reboottime - sometime; X s_corr += timediff; X s_crashes++; X } X else X { X thetime = reboottime; X s_shutdowns++; X s_reboots++; X } X } X J_cleanup(thetime); X} X Xstatic void XJ_print(list) XUSER *list; X{ X int i,rank=0; X long localv; X X localv = now - (s_start + t_corr); X if (localv < 0) { X fprintf(stderr,"Warning: negative time covered!\n"); X }; X X s_uptime = now - (s_start + s_corr); X if (s_uptime < 0) { X fprintf(stderr,"Warning: negative uptime!\n"); X }; X X printf("--- System statistics---\n"); X printf("Start at: %s", ctime((time_t *)&s_start)); X printf("End at: %s", ctime((time_t *)&now)); X printf("Time covered:"); X timefmt(localv); X printf("\n"); X printf("Uptime: "); X timefmt(s_uptime); X printf(" (%.1f %% of total time)\n", X (((float) s_uptime / (float) localv) * 100.0)); X printf("Booted: %d times", s_reboots); X printf(" (shut down %d times)\n", s_shutdowns); X printf("Crashed: %d times\n", s_crashes); X printf("Logins: %d\n", s_logins); X printf("Logouts: %d\n", s_logouts); X printf("\n--- User statistics---\n"); X qsort((char *) ((USER *) list), (size_t) HASHTABLESIZE, (size_t) sizeof(struct anUser), ucompare); X if(toptenmode == 1) { X for (i = 0; i < HASHTABLESIZE; i++) X { X if (list[i].name != NULL) X { X rank++; X printf("%4d %-22s", X rank, X (char *)J_gecos_parse(list[i].name)); X timefmt(list[i].time); X printf(" (%6d logins)\n",list[i].logins); X }; X } X } X else { X#ifdef __bsdi__ /* Actually an SPC-ism */ X printf("user logins ttys real total longest average %%uptime\n"); X#else X printf("user logins ttys real total longest average %%uptime\n"); X#endif X X for (i = 0; i < HASHTABLESIZE; i++) X { X if (list[i].name != NULL) X { X#ifdef __bsdi__ /* Actually an SPC-ism */ X printf("%-13s %6d %4d", list[i].name, list[i].logins, X#else X printf("%-9s %6d %4d", list[i].name, list[i].logins, X#endif X list[i].max_conc_logins); X timefmt(list[i].time); X timefmt(list[i].total); X#ifdef DEBUG X if (list[i].time > list[i].total) X { X fprintf(stderr,"Debug: total time greater than real time: %s\n", X list[i].name); X } X#endif X timefmt(list[i].max_time); X timefmt((list[i].total / list[i].logins)); X printf(" %7.2f\n", (((float) list[i].time / (float) s_uptime) * 100)); X }; X } X } X} X Xvoid XJ_read_wtmp(fp, def) XFILE *fp; Xint def; X{ X struct utmp logbuf, *uptr; X int first = TRUE; X long timebuf = 0; X char curuser[UT_NAMESIZE+1]; X char curline[UT_LINESIZE+1]; X long tob = 0, dot = 0; X X while (fread((char *) &logbuf, 1, sizeof(logbuf), fp) == sizeof(logbuf)) X { X s_stop = logbuf.ut_time; X if (first == TRUE) X { X first = FALSE; X tob = s_stop; X s_start = s_stop; X }; X if ( s_stop < s_start ) X { X fprintf(stderr,"Error: time scope vulneration... resetting\n"); X J_init_all(); X }; X#ifndef SYSV X if (!strncmp("reboot", logbuf.ut_name, 6)) X#else X if (logbuf.ut_type == BOOT_TIME) X#endif X { X J_reboot(tob, s_stop, dot); X shutd_state = FALSE; X continue; X } X else if (!strncmp("shutdown", logbuf.ut_name, UT_NAMESIZE) X#ifndef SYSV X || logbuf.ut_line[0] == '~' X#endif X || shutd_state == TRUE X#ifdef CONFIGFILE X || (privilege_check(logbuf.ut_name,shutdownusers) == TRUE) X#endif X ) X { X tob = s_stop; X if (shutd_state == FALSE) X { X shutd_state = TRUE; X s_shutdowns++; X dot = logbuf.ut_time; X continue; X } X else X continue; X } X#ifndef SYSV X else if (logbuf.ut_line[0] == '|' && !logbuf.ut_line[1]) X#else X else if (logbuf.ut_type == OLD_TIME) X#endif X { X timebuf = logbuf.ut_time; X J_timeprep(); X tob = s_stop; X continue; X } X#ifndef SYSV X else if (logbuf.ut_line[0] == '{' && !logbuf.ut_line[1]) X#else X else if (logbuf.ut_type == NEW_TIME) X#endif X { X tob = s_stop; X if (timebuf != 0) X { X#ifdef DEBUG X fprintf(stderr,"Debug: timesetting %ld\n",(logbuf.ut_time - timebuf)); X#endif X J_timeset(timebuf, logbuf.ut_time); X continue; X } X else X continue; X } X else if ((!strncmp("ftp", logbuf.ut_line, 3)) || X (!strncmp("uucp", logbuf.ut_line, 4))) X { X shutd_state = FALSE; X tob = s_stop; X continue; X } X#ifndef SYSV X else if (isalnum(logbuf.ut_name[0]) != 0) X#else X else if (logbuf.ut_type == USER_PROCESS) X#endif X { X shutd_state = FALSE; X uptr = &logbuf; X curuser[UT_NAMESIZE] = '\0'; X curline[UT_LINESIZE] = '\0'; X X strncpy(curuser, uptr->ut_name, UT_NAMESIZE); X strncpy(curline, uptr->ut_line, UT_LINESIZE); X J_login(curuser, uptr->ut_time, curline); X tob = s_stop; X continue; X } X#ifndef SYSV X else if (logbuf.ut_line != NULL) X#else X else if (logbuf.ut_type == DEAD_PROCESS) X#endif X { X curline[UT_LINESIZE] = '\0'; X strncpy(curline, logbuf.ut_line, UT_LINESIZE); X J_logout(logbuf.ut_time, J_gettty(curline)); X tob = s_stop; X continue; X } X else X { X#ifndef SYSV X fprintf(stderr, "Warning: Strange entry at %s\n%s %s\n", X ctime((time_t *)&logbuf.ut_time), X logbuf.ut_name, logbuf.ut_line); X#endif X tob = s_stop; X continue; X } X } X if (def == 0) X { X now = s_stop; X } X else X { X now = (long) time((time_t *) 0); X } X J_cleanup(now); X J_print(userlist); X} X Xvoid Xmain(argc, argv) Xint argc; Xchar **argv; X{ X FILE *fd; X#ifdef WTMP_FILE X char *wtmp = WTMP_FILE; X#else X#ifdef _PATH_WTMP X char *wtmp = _PATH_WTMP; X#else X char *wtmp = "/usr/adm/wtmp"; X#endif X#endif X#ifdef CONFIGFILE X char *configfile = CONFIGFILE; X#endif X int def = 1; X X while (--argc > 0 && **++argv == '-') X switch (*++*argv) X { X case 'w': X if (--argc > 0) X { X wtmp = *++argv; X def--; X } X continue; X case 't': X toptenmode++; X continue; X#ifdef CONFIGFILE X case 'c': X if (--argc > 0) X { X configfile = *++argv; X } X continue; X#endif X } X#ifdef CONFIGFILE X shutdownusers = config_init(configfile); X#endif X X fd = (FILE *) fopen(wtmp, "r"); X if (fd == NULL) X { X fprintf(stderr, "Fatal: %s ... no such file\n", wtmp); X exit(1); X } X J_read_wtmp(fd, def); X if (fclose(fd) == 0) X { X exit(0); X } X else X { X fprintf(stderr, "Fatal: couldn't close %s\n", wtmp); X exit(1); X } X} END-of-./freeks-1.33/freeks.c echo x - ./freeks-1.33/freeks.cat sed 's/^X//' >./freeks-1.33/freeks.cat << 'END-of-./freeks-1.33/freeks.cat' X X X Xfreeks(6) Freeks 1.33 Manual freeks(6) X X X XNAME X freeks - extended login accounting X XSYNOPSIS X freeks [-w wtmp ] [-t] [-c configfile ] X X XDESCRIPTION X Freeks produces a report on system usage based on the con- X tents of X wtmp and sorted by the actual time different users have X spent on the system. The first few lines contain general X information about the system. X X X XOPTIONS X The default for X wtmp is "/usr/adm/wtmp". You may specify an alternate X wtmp file on the command line. X X With the 't' option the user statistics are modified, show- X ing the 'rank', the name, the time the user was actually X logged in and the number of logins. X X With the 'c' option you can specify a X configfile containing the names of alternative 'shutdown' X users (cf. TODO), which is probably interesting to SysV X machine owners only. X X XBUGS X Freeks is (almost) limited to BSD style X wtmp files (cf. TODO below). It does not have a lot of X fancy options. Freeks does make some mistakes, e.g. when a X reboot entry in X wtmp happened without a shutdown before it. If the last X entry happened more than a day before the reboot entry, X freeks presumes that the machine crashed, and will use the X time of the last entry before the crash as time of shutdown. X This may lead to marginal errors in 'uptime' and the login X times of users still logged in at that time. X X Another bug happens when time is changed in single user mode X which means it doesn't get logged. Don't do that, or delete X the wtmp file afterwards. X X X X X X XApostolos Lytras Last change: July 10, 1994 1 X X X X X X Xfreeks(6) Freeks 1.33 Manual freeks(6) X X X XTODO X System V has changed a lot in X wtmp but it should be possible to run the program, if you X rarely shut your machine down or have a dedicated 'shutdown' X user that logs in when you shut down. If this user is called X 'shutdown' you needn't do nothing, if his login name is X something else, you can specify that in X configfile (if, of course, freeks had been compiled with X CONFIGFILE defined...). X X X XSEE ALSO X geeks(5) geeks(6) ac(8) wtmp(5) wtmpfix(8) login(1) init(8) X X XFILES X /usr/adm/wtmp wtmp file X XAUTHOR X Copyright (C) 1994 by Apostolos Lytras X (lytras@avalon.unizh.ch). X X Thanks to Dave P. Gymer (dpg@cs.nott.ac.uk) and everyone X that helped him write geeks(6). It has been an important X inspiration and a reason to write this program (because it X was just too slow, geeks! ;-). X X Thanks also to Wietse Venema for his information on SVR4's X wtmp. X X Some of the credit must also go to the University of Cali- X fornia, Berkeley, for their programs ac(8), last(1) and X uptime(1), which evaluate /usr/adm/wtmp as well. These pro- X grams were inspiring as well, because they couldn't do X everything real geeks needed them to. X X This is free software; you can redistribute it and/or modify X it under the terms of the GNU General Public License as pub- X lished by the Free Software Foundation; either version 2 of X the License, or (at your option) any later version. X X The GNU General Public License is not included in the origi- X nal distrubution of freeks. If you don't have a copy of it X already, write to the Free Software Foundation, 675 Mass X Ave, Cambridge, MA 02139, USA. X X This program is distributed in the hope that it will be X useful, but WITHOUT ANY WARRANTY; without even the implied X warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR X PURPOSE. See the GNU General Public License for more X details. X X X XApostolos Lytras Last change: July 10, 1994 2 X X END-of-./freeks-1.33/freeks.cat echo x - ./freeks-1.33/freeks.h sed 's/^X//' >./freeks-1.33/freeks.h << 'END-of-./freeks-1.33/freeks.h' X/* --- start of configurable section --- */ X/* X * HASHTABLESIZE must be prime, such as 61,113,251,509,1021 To save memory X * choose a value close to the number of users on your system. X * X * Note: X * This is not a full-fledged hashing library, just some code stolen from X * "Algorithms in C" by Robert Sedgewick which has been refined to avoid hash X * collisions. The algorithm which does this is very simple and probably not X * worth a lot in more complex programs. (It does not handle removal from the X * list, because that won't happen in this application.) X */ X#ifndef HASHTABLESIZE X#define HASHTABLESIZE 1021 X#endif X X/* NO_STRDUP: X * if your system doesn't have the strdup routine, then uncomment the X * definition below. X */ X/* #define NO_STRDUP */ X X/* WANTGECOS: X * If you want to use GECOS entries ("real names") in 'freeks -t' output X * define this X */ X#ifndef WANTGECOS X#define WANTGECOS X#endif X X/* DEBUG: X * If you want to get more debugging output on standard error, then X * uncomment this here or look at the debugflags in the Makefile X */ X/* #define DEBUG */ X X/* CONFIGFILE */ X/* System V users might want to create a configfile containing the */ X/* names of additional usernames that create a shutdown, like `halt' */ X/* or `powerdown'. Each username must appear separately on a line in */ X/* the file */ X/* #define CONFIGFILE "/usr/local/lib/freeks/config" */ X X/* --- you shouldn't need to change much below this --- */ X X#if defined(__hpux) X#ifndef SYSV X#define _CLASSIC_ID_TYPES X#endif X#endif X X#include X#include X#include X#include X#include X#ifndef SYSV X#include X#include X#else X#include X#include X#ifndef ut_name X#define ut_name ut_user X#endif X#endif X X#ifdef WANTGECOS X#include X/***************************************************************/ X/* MAX_GECOS_LEN is the maximum length of a gecos entry in the */ X/* password database for use in topten ( option '-t' ) mode */ X/* Default: 20 */ X/***************************************************************/ X#ifdef __bsdi__ X#define MAX_GECOS_LEN 32 X#else X#define MAX_GECOS_LEN 20 X#endif X#define minimum(a,b) (a < b) ? a : b X#endif /* WANTGECOS */ X Xstruct utmp utmp_dummy; X#ifndef UT_NAMESIZE X#define UT_NAMESIZE sizeof (utmp_dummy.ut_name) X#endif X#ifndef UT_LINESIZE X#define UT_LINESIZE sizeof (utmp_dummy.ut_line) X#endif X X#define TRUE 1 X#define FALSE 0 X X#define timefmt(time) \ Xprintf(" %4ld:%02ld:%02ld", (time) / 3600, ((time) / 60) % 60, (time) % 60) X X#ifndef NO_STRDUP X#if (defined(__ultrix__) || defined(nextstep3)) X#define NO_STRDUP X#endif /* ultrix or nextstep */ X#endif X Xtypedef struct anUser X{ X char *name; X int current_logins; X int max_conc_logins; X int logins; X long logintimebuf; X int needs_timeset; X long time; X long total; X long max_time; X} USER; X Xtypedef struct aTty X{ X char *tty; X int used; X int user_hash; X long time_in; X struct aTty *next; X} TTY; X X#ifdef CONFIGFILE Xtypedef struct priv_list { X char *user; X struct priv_list *next; X} PRIVILEGED ; X#endif X END-of-./freeks-1.33/freeks.h echo x - ./freeks-1.33/gecos.c sed 's/^X//' >./freeks-1.33/gecos.c << 'END-of-./freeks-1.33/gecos.c' X/* gecos.c */ X/* Apostolos Lytras. June 1994 */ X/* This little program gets a user name on its command line for which */ X/* it searches /etc/passwd's gecos entry and strips everything but the */ X/* first of its (comma delimited) fields... */ X/* BUGS: maybe the first field is not the user's real name... ?! */ X X#include "freeks.h" X X#ifndef WANTGECOS X Xchar *J_gecos_parse(string) Xchar *string; X{ X return string; /* dummy function */ X} X X#else X Xchar *J_gecos_parse(namen) Xchar *namen; X{ X char out[MAX_GECOS_LEN], *output, *input; X int len, i; X struct passwd *dastruct; X X dastruct = (struct passwd *) malloc (sizeof(struct passwd *)); X if ((dastruct = getpwnam(namen)) == (struct passwd *)NULL ) X { X#ifdef DEBUG X fprintf(stderr,"Debug: user %s is not in password file.\n",namen); X#endif X return namen; X }; X if ((input = (char *)strdup(dastruct->pw_gecos)) == (char *)NULL) X { X fprintf(stderr,"Fatal: allocation for GECOS failed.\n"); X exit(-2); X }; X/* This starts at 0, so we want -2 (0-based, less 1 for terminator) */ X len = minimum((MAX_GECOS_LEN - 2),strlen(input)); X for ( i = 0; (i <= len) && (input[i] != ',') ; i++, out[i] = '\0') X { X out[i] = input[i]; X } X output = (char *)strdup(out); X return output; X} X X#endif END-of-./freeks-1.33/gecos.c exit