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