Home | History | Annotate | Download | only in acct
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     23 /*	  All Rights Reserved  	*/
     24 
     25 
     26 /*
     27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     28  * Use is subject to license terms.
     29  */
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 /*
     32  *	acctcon1 [-p] [-t] [-l file] [-o file] <wtmpx-file >ctmp-file
     33  *	-p	print input only, no processing
     34  *	-t	test mode: use latest time found in input, rather than
     35  *		current time when computing times of lines still on
     36  *		(only way to get repeatable data from old files)
     37  *	-l file	causes output of line usage summary
     38  *	-o file	causes first/last/reboots report to be written to file
     39  *	reads input (normally /var/adm/wtmpx), produces
     40  *	list of sessions, sorted by ending time in ctmp.h/ascii format
     41  *	A_TSIZE is max # distinct ttys
     42  */
     43 
     44 #include <sys/types.h>
     45 #include "acctdef.h"
     46 #include <stdio.h>
     47 #include <ctype.h>
     48 #include <time.h>
     49 #include <utmpx.h>
     50 #include <locale.h>
     51 #include <stdlib.h>
     52 
     53 int	a_tsize	= A_TSIZE;
     54 int	tsize	= -1;	/* used slots in tbuf table */
     55 struct  utmpx	wb;	/* record structure read into */
     56 struct	ctmp	cb;	/* record structure written out of */
     57 
     58 struct tbuf {
     59 	char	tline[LSZ];	/* /dev/...  */
     60 	char	tname[NSZ];	/* user name */
     61 	time_t	ttime;		/* start time */
     62 	dev_t	tdev;		/* device */
     63 	int	tlsess;		/* # complete sessions */
     64 	int	tlon;		/* # times on (ut_type of 7) */
     65 	int	tloff;		/* # times off (ut_type != 7) */
     66 	long	ttotal;		/* total time used on this line */
     67 } * tbuf;
     68 
     69 #define DATE_FMT	"%a %b %e %H:%M:%S %Y\n"
     70 int	nsys;
     71 struct sys {
     72 	char	sname[LSZ];	/* reasons for ACCOUNTING records */
     73 	char	snum;		/* number of times encountered */
     74 } sy[NSYS];
     75 
     76 time_t	datetime;	/* old time if date changed, otherwise 0 */
     77 time_t	firstime;
     78 time_t	lastime;
     79 int	ndates;		/* number of times date changed */
     80 int	exitcode;
     81 char	*report	= NULL;
     82 char	*replin = NULL;
     83 int	printonly;
     84 int	tflag;
     85 
     86 static char time_buf[50];
     87 uid_t	namtouid();
     88 dev_t	lintodev();
     89 static size_t wread(void);
     90 static int valid(void);
     91 static void fixup(FILE *);
     92 static void loop(void);
     93 static void bootshut(void);
     94 static int iline(void);
     95 static void upall(void);
     96 static void update(struct tbuf *);
     97 static void printrep(void);
     98 static void printlin(void);
     99 static void prctmp(struct ctmp *);
    100 
    101 int
    102 main(int argc, char **argv)
    103 {
    104 	char *prog = argv[0];
    105 
    106 	(void)setlocale(LC_ALL, "");
    107 	while (--argc > 0 && **++argv == '-')
    108 		switch(*++*argv) {
    109 		case 'l':
    110 			if (--argc > 0)
    111 				replin = *++argv;
    112 			continue;
    113 		case 'o':
    114 			if (--argc > 0)
    115 				report = *++argv;
    116 			continue;
    117 		case 'p':
    118 			printonly++;
    119 			continue;
    120 		case 't':
    121 			tflag++;
    122 			continue;
    123 		default:
    124 			fprintf(stderr, "usage: %s [-p] [-t] [-l lineuse] [-o reboot]\n", prog);
    125 			exit(1);
    126 
    127 		}
    128 
    129 	if ((tbuf = (struct tbuf *) calloc(a_tsize,
    130 		sizeof (struct tbuf))) == NULL) {
    131 		fprintf(stderr, "acctcon1: Cannot allocate memory\n");
    132 		exit(3);
    133 	}
    134 
    135 	if (printonly) {
    136 		while (wread()) {
    137 			if (valid()) {
    138 				printf("%.*s\t%.*s\t%lu",
    139 				    sizeof (wb.ut_line),
    140 				    wb.ut_line,
    141 				    sizeof (wb.ut_name),
    142 				    wb.ut_name,
    143 				    wb.ut_xtime);
    144 				cftime(time_buf, DATE_FMT, &wb.ut_xtime);
    145 				printf("\t%s", time_buf);
    146 			} else
    147 				fixup(stdout);
    148 
    149 		}
    150 		exit(exitcode);
    151 	}
    152 
    153 	while (wread()) {
    154 		if (firstime == 0)
    155 			firstime = wb.ut_xtime;
    156 		if (valid())
    157 			loop();
    158 		else
    159 			fixup(stderr);
    160 	}
    161 	wb.ut_name[0] = '\0';
    162 	strcpy(wb.ut_line, "acctcon1");
    163 	wb.ut_type = ACCOUNTING;
    164 	if (tflag)
    165 		wb.ut_xtime = lastime;
    166 	else
    167 		time(&wb.ut_xtime);
    168 	loop();
    169 	if (report != NULL)
    170 		printrep();
    171 	if (replin != NULL)
    172 		printlin();
    173 	exit(exitcode);
    174 }
    175 
    176 static size_t
    177 wread()
    178 {
    179 	return (fread(&wb, sizeof(wb), 1, stdin) == 1);
    180 
    181 }
    182 
    183 /*
    184  * valid: check input wtmp record, return 1 if looks OK
    185  */
    186 static int
    187 valid()
    188 {
    189 	int i, c;
    190 
    191 	/* XPG say that user names should not start with a "-". */
    192         if ((c = wb.ut_name[0]) == '-')
    193 		return(0);
    194 
    195 	for (i = 0; i < NSZ; i++) {
    196 		c = wb.ut_name[i];
    197 		if (isalnum(c) || c == '$' || c == ' ' || c == '_' || c == '-')
    198 			continue;
    199 		else if (c == '\0')
    200 			break;
    201 		else
    202 			return(0);
    203 	}
    204 
    205 	if((wb.ut_type >= EMPTY) && (wb.ut_type <= UTMAXTYPE))
    206 		return(1);
    207 
    208 	return(0);
    209 }
    210 
    211 /*
    212  *	fixup assumes that V6 wtmp (16 bytes long) is mixed in with
    213  *	V7 records (20 bytes each)
    214  *
    215  *	Starting with Release 5.0 of UNIX, this routine will no
    216  *	longer reset the read pointer.  This has a snowball effect
    217  *	On the following records until the offset corrects itself.
    218  *	If a message is printed from here, it should be regarded as
    219  *	a bad record and not as a V6 record.
    220  */
    221 static void
    222 fixup(FILE *stream)
    223 {
    224 	fprintf(stream, "bad wtmpx: offset %lu.\n", ftell(stdin)-sizeof(wb));
    225 	fprintf(stream, "bad record is:  %.*s\t%.*s\t%lu",
    226 	    sizeof (wb.ut_line),
    227 	    wb.ut_line,
    228 	    sizeof (wb.ut_name),
    229 	    wb.ut_name,
    230 	    wb.ut_xtime);
    231 	cftime(time_buf, DATE_FMT, &wb.ut_xtime);
    232 	fprintf(stream, "\t%s", time_buf);
    233 #ifdef	V6
    234 	fseek(stdin, (long)-4, 1);
    235 #endif
    236 	exitcode = 1;
    237 }
    238 
    239 static void
    240 loop()
    241 {
    242 	int timediff;
    243 	struct tbuf *tp;
    244 
    245 	if(wb.ut_line[0] == '\0' )	/* It's an init admin process */
    246 		return;			/* no connect accounting data here */
    247 	switch(wb.ut_type) {
    248 	case OLD_TIME:
    249 		datetime = wb.ut_xtime;
    250 		return;
    251 	case NEW_TIME:
    252 		if(datetime == 0)
    253 			return;
    254 		timediff = wb.ut_xtime - datetime;
    255 		for (tp = tbuf; tp <= &tbuf[tsize]; tp++)
    256 			tp->ttime += timediff;
    257 		datetime = 0;
    258 		ndates++;
    259 		return;
    260 	case BOOT_TIME:
    261 		upall();
    262 	case ACCOUNTING:
    263 	case RUN_LVL:
    264 		lastime = wb.ut_xtime;
    265 		bootshut();
    266 		return;
    267 	case USER_PROCESS:
    268 	case LOGIN_PROCESS:
    269 	case INIT_PROCESS:
    270 	case DEAD_PROCESS:
    271 		update(&tbuf[iline()]);
    272 		return;
    273 	case EMPTY:
    274 		return;
    275 	default:
    276 		cftime(time_buf, DATE_FMT, &wb.ut_xtime);
    277 		fprintf(stderr, "acctcon1: invalid type %d for %s %s %s",
    278 		    wb.ut_type,
    279 		    wb.ut_name,
    280 		    wb.ut_line,
    281 		    time_buf);
    282 	}
    283 }
    284 
    285 /*
    286  * bootshut: record reboot (or shutdown)
    287  * bump count, looking up wb.ut_line in sy table
    288  */
    289 static void
    290 bootshut()
    291 {
    292 	int i;
    293 
    294 	for (i = 0; i < nsys && !EQN(wb.ut_line, sy[i].sname); i++)
    295 		;
    296 	if (i >= nsys) {
    297 		if (++nsys > NSYS) {
    298 			fprintf(stderr,
    299 				"acctcon1: recompile with larger NSYS\n");
    300 			nsys = NSYS;
    301 			return;
    302 		}
    303 		CPYN(sy[i].sname, wb.ut_line);
    304 	}
    305 	sy[i].snum++;
    306 }
    307 
    308 /*
    309  * iline: look up/enter current line name in tbuf, return index
    310  * (used to avoid system dependencies on naming)
    311  */
    312 static int
    313 iline()
    314 {
    315 	int i;
    316 
    317 	for (i = 0; i <= tsize; i++)
    318 		if (EQN(wb.ut_line, tbuf[i].tline))
    319 			return(i);
    320 	if (++tsize >= a_tsize) {
    321 		a_tsize = a_tsize + A_TSIZE;
    322 		if ((tbuf = (struct tbuf *) realloc(tbuf, a_tsize *
    323 			sizeof (struct tbuf))) == NULL) {
    324 			fprintf(stderr, "acctcon1: Cannot reallocate memory\n");
    325 			exit(2);
    326 		}
    327 	}
    328 
    329 	CPYN(tbuf[tsize].tline, wb.ut_line);
    330 	tbuf[tsize].tdev = lintodev(wb.ut_line);
    331 	return(tsize);
    332 }
    333 
    334 static void
    335 upall()
    336 {
    337 	struct tbuf *tp;
    338 
    339 	wb.ut_type = INIT_PROCESS;	/* fudge a logoff for reboot record */
    340 	for (tp = tbuf; tp <= &tbuf[tsize]; tp++)
    341 		update(tp);
    342 }
    343 
    344 /*
    345  * update tbuf with new time, write ctmp record for end of session
    346  */
    347 static void
    348 update(struct tbuf *tp)
    349 {
    350 	time_t	told,	/* last time for tbuf record */
    351 		tnew;	/* time of this record */
    352 			/* Difference is connect time */
    353 
    354 	told = tp->ttime;
    355 	tnew = wb.ut_xtime;
    356 	cftime(time_buf, DATE_FMT, &told);
    357 	fprintf(stderr, "The old time is: %s", time_buf);
    358 	cftime(time_buf, DATE_FMT, &tnew);
    359 	fprintf(stderr, "the new time is: %s", time_buf);
    360 	if (told > tnew) {
    361 		cftime(time_buf, DATE_FMT, &told);
    362 		fprintf(stderr, "acctcon1: bad times: old: %s", time_buf);
    363 		cftime(time_buf, DATE_FMT, &tnew);
    364 		fprintf(stderr, "new: %s", time_buf);
    365 		exitcode = 1;
    366 		tp->ttime = tnew;
    367 		return;
    368 	}
    369 	tp->ttime = tnew;
    370 	switch(wb.ut_type) {
    371 	case USER_PROCESS:
    372 		tp->tlsess++;
    373 		if(tp->tname[0] != '\0') { /* Someone logged in without */
    374 					   /* logging off. Put out record. */
    375 			cb.ct_tty = tp->tdev;
    376 			CPYN(cb.ct_name, tp->tname);
    377 			cb.ct_uid = namtouid(cb.ct_name);
    378 			cb.ct_start = told;
    379 			if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told),
    380 			    cb.ct_con) == 0) {
    381 				fprintf(stderr, "acctcon1: could not calculate prime/non-prime hours\n");
    382 
    383 				exit(1);
    384 			}
    385 			prctmp(&cb);
    386 			tp->ttotal += tnew-told;
    387 		}
    388 		else	/* Someone just logged in */
    389 			tp->tlon++;
    390 		CPYN(tp->tname, wb.ut_name);
    391 		break;
    392 	case INIT_PROCESS:
    393 	case LOGIN_PROCESS:
    394 	case DEAD_PROCESS:
    395 		tp->tloff++;
    396 		if(tp->tname[0] != '\0') { /* Someone logged off */
    397 			/* Set up and print ctmp record */
    398 			cb.ct_tty = tp->tdev;
    399 			CPYN(cb.ct_name, tp->tname);
    400 			cb.ct_uid = namtouid(cb.ct_name);
    401 			cb.ct_start = told;
    402 			if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told),
    403 			    cb.ct_con) == 0) {
    404 				fprintf(stderr, "acctcon1: could not calculate prime/non-prime hours\n");
    405 				exit(1);
    406 			}
    407 			prctmp(&cb);
    408 			tp->ttotal += tnew-told;
    409 			tp->tname[0] = '\0';
    410 		}
    411 	}
    412 }
    413 
    414 static void
    415 printrep()
    416 {
    417 	int i;
    418 
    419 	freopen(report, "w", stdout);
    420 	cftime(time_buf, DATE_FMT, &firstime);
    421 	printf("from %s", time_buf);
    422 	cftime(time_buf, DATE_FMT, &lastime);
    423 	printf("to   %s", time_buf);
    424 	if (ndates)
    425 		printf("%d\tdate change%c\n",ndates,(ndates>1 ? 's' : '\0'));
    426 	for (i = 0; i < nsys; i++)
    427 		printf("%d\t%.*s\n", sy[i].snum,
    428 		    sizeof (sy[i].sname), sy[i].sname);
    429 }
    430 
    431 /*
    432  *	print summary of line usage
    433  *	accuracy only guaranteed for wtmpx file started fresh
    434  */
    435 static void
    436 printlin()
    437 {
    438 	struct tbuf *tp;
    439 	double timet, timei;
    440 	double ttime;
    441 	int tsess, ton, toff;
    442 
    443 	freopen(replin, "w", stdout);
    444 	ttime = 0.0;
    445 	tsess = ton = toff = 0;
    446 	timet = MINS(lastime-firstime);
    447 	printf("TOTAL DURATION IS %.0f MINUTES\n", timet);
    448 	printf("LINE         MINUTES  PERCENT  # SESS  # ON  # OFF\n");
    449 	for (tp = tbuf; tp <= &tbuf[tsize]; tp++) {
    450 		timei = MINS(tp->ttotal);
    451 		ttime += timei;
    452 		tsess += tp->tlsess;
    453 		ton += tp->tlon;
    454 		toff += tp->tloff;
    455 		printf("%-*.*s %-7.0f  %-7.0f  %-6d  %-4d  %-5d\n",
    456 		    OUTPUT_LSZ,
    457 		    OUTPUT_LSZ,
    458 		    tp->tline,
    459 		    timei,
    460 		    (timet > 0.)? 100*timei/timet : 0.,
    461 		    tp->tlsess,
    462 		    tp->tlon,
    463 		    tp->tloff);
    464 	}
    465 	printf("TOTALS       %-7.0f  --       %-6d  %-4d  %-5d\n",
    466 	    ttime, tsess, ton, toff);
    467 }
    468 
    469 static void
    470 prctmp(struct ctmp *t)
    471 {
    472 
    473 	printf("%u\t%ld\t%.*s\t%lu\t%lu\t%lu",
    474 	    t->ct_tty,
    475 	    t->ct_uid,
    476 	    OUTPUT_NSZ,
    477 	    t->ct_name,
    478 	    t->ct_con[0],
    479 	    t->ct_con[1],
    480 	    t->ct_start);
    481 	cftime(time_buf, DATE_FMT, &t->ct_start);
    482 	printf("\t%s", time_buf);
    483 }
    484