4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
31 * acctcon [-l file] [-o file] <wtmpx-file
32 * -l file causes output of line usage summary
33 * -o file causes first/last/reboots report to be written to file
34 * reads input (normally /var/log/wtmpx), produces
35 * list of sessions, sorted by ending time in tacct.h format
39 #include <sys/types.h>
40 #include <sys/param.h>
50 int a_tsize
= A_TSIZE
;
51 int tsize
= -1; /* highest index of used slot in tbuf table */
53 struct utmpx wb
; /* record structure read into */
54 struct ctmp cb
; /* record structure written out of */
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 */
78 char sname
[LSZ
]; /* reasons for ACCOUNTING records */
79 char snum
; /* number of times encountered */
82 static char time_buf
[50];
83 time_t datetime
; /* old time if date changed, otherwise 0 */
86 int ndates
; /* number of times date changed */
93 static int valid(void);
94 static void fixup(FILE *);
95 static void loop(void);
96 static void bootshut(void);
97 static int iline(void);
98 static void upall(void);
99 static void update(struct tbuf
*);
100 static void printrep(void);
101 static void printlin(void);
102 static int tcmp(struct tbuf
*, struct tbuf
*);
103 static int node_compare(const void *, const void *);
104 static void enter(struct ctmp
*);
105 static void print_node(const void *, VISIT
, int);
106 static void output(void);
114 main(int argc
, char **argv
)
118 (void) setlocale(LC_ALL
, "");
119 while ((c
= getopt(argc
, argv
, "l:o:")) != EOF
)
128 fprintf(stderr
, "usage: %s [-l lineuse] "
129 "[-o reboot]\n", argv
[0]);
133 if ((tbuf
= (struct tbuf
*)calloc(a_tsize
,
134 sizeof (struct tbuf
))) == NULL
) {
135 fprintf(stderr
, "acctcon: Cannot allocate memory\n");
140 * XXX - fixme - need a good way of getting the fd that getutxent would
141 * use to access wtmpx, so we can convert this read of stdin to use
142 * the APIs and remove the dependence on the existence of the file.
144 while (fread(&wb
, sizeof (wb
), 1, stdin
) == 1) {
146 firstime
= wb
.ut_xtime
;
152 wb
.ut_name
[0] = '\0';
153 strcpy(wb
.ut_line
, "acctcon");
154 wb
.ut_type
= ACCOUNTING
;
155 wb
.ut_xtime
= lastime
;
170 * valid: check input wtmpx record, return 1 if looks OK
177 /* XPG say that user names should not start with a "-" */
178 if ((c
= wb
.ut_name
[0]) == '-')
181 for (i
= 0; i
< NSZ
; i
++) {
183 if (isalnum(c
) || c
== '$' || c
== ' ' || c
== '.' ||
184 c
== '_' || c
== '-')
192 if ((wb
.ut_type
>= EMPTY
) && (wb
.ut_type
<= UTMAXTYPE
))
201 fprintf(stream
, "bad wtmpx: offset %lu.\n", ftell(stdin
)-sizeof (wb
));
202 fprintf(stream
, "bad record is: %.*s\t%.*s\t%lu",
208 cftime(time_buf
, DATE_FMT
, &wb
.ut_xtime
);
209 fprintf(stream
, "\t%s", time_buf
);
219 if (wb
.ut_line
[0] == '\0') /* It's an init admin process */
220 return; /* no connect accounting data here */
221 switch (wb
.ut_type
) {
223 datetime
= wb
.ut_xtime
;
228 timediff
= wb
.ut_xtime
- datetime
;
229 for (tp
= tbuf
; tp
<= &tbuf
[tsize
]; tp
++)
230 tp
->ttime
+= timediff
;
241 lastime
= wb
.ut_xtime
;
247 case DEAD_PROCESS
: /* WHCC mod 3/86 */
248 update(&tbuf
[iline()]);
253 cftime(time_buf
, DATE_FMT
, &wb
.ut_xtime
);
254 fprintf(stderr
, "acctcon: invalid type %d for %s %s %s",
263 * bootshut: record reboot (or shutdown)
264 * bump count, looking up wb.ut_line in sy table
271 for (i
= 0; i
< nsys
&& !EQN(wb
.ut_line
, sy
[i
].sname
); i
++)
276 "acctcon: recompile with larger NSYS\n");
280 CPYN(sy
[i
].sname
, wb
.ut_line
);
286 * iline: look up/enter current line name in tbuf, return index
287 * (used to avoid system dependencies on naming)
294 for (i
= 0; i
<= tsize
; i
++)
295 if (EQN(wb
.ut_line
, tbuf
[i
].tline
))
297 if (++tsize
>= a_tsize
) {
298 a_tsize
= a_tsize
+ A_TSIZE
;
299 if ((tbuf
= reallocarray(tbuf
, a_tsize
,
300 sizeof (struct tbuf
))) == NULL
) {
301 fprintf(stderr
, "acctcon: Cannot reallocate memory\n");
306 CPYN(tbuf
[tsize
].tline
, wb
.ut_line
);
307 tbuf
[tsize
].tdev
= lintodev(wb
.ut_line
);
316 wb
.ut_type
= DEAD_PROCESS
; /* fudge a logoff for reboot record. */
317 for (tp
= tbuf
; tp
<= &tbuf
[tsize
]; tp
++)
322 * update tbuf with new time, write ctmp record for end of session
325 update(struct tbuf
*tp
)
327 time_t told
, /* last time for tbuf record */
328 tnew
; /* time of this record */
329 /* Difference is connect time */
334 cftime(time_buf
, DATE_FMT
, &told
);
335 fprintf(stderr
, "acctcon: bad times: old: %s", time_buf
);
336 cftime(time_buf
, DATE_FMT
, &tnew
);
337 fprintf(stderr
, "new: %s", time_buf
);
343 switch (wb
.ut_type
) {
347 * Someone logged in without logging off. Put out record.
349 if (tp
->tname
[0] != '\0') {
350 cb
.ct_tty
= tp
->tdev
;
351 CPYN(cb
.ct_name
, tp
->tname
);
352 cb
.ct_uid
= namtouid(cb
.ct_name
);
354 if (pnpsplit(cb
.ct_start
, (ulong_t
)(tnew
-told
),
356 fprintf(stderr
, "acctcon: could not calculate "
357 "prime/non-prime hours\n");
361 tp
->ttotal
+= tnew
-told
;
362 } else /* Someone just logged in */
364 CPYN(tp
->tname
, wb
.ut_name
);
368 if (tp
->tname
[0] != '\0') { /* Someone logged off */
369 /* Set up and print ctmp record */
370 cb
.ct_tty
= tp
->tdev
;
371 CPYN(cb
.ct_name
, tp
->tname
);
372 cb
.ct_uid
= namtouid(cb
.ct_name
);
374 if (pnpsplit(cb
.ct_start
, (ulong_t
)(tnew
-told
),
376 fprintf(stderr
, "acctcon: could not calculate "
377 "prime/non-prime hours\n");
381 tp
->ttotal
+= tnew
-told
;
392 freopen(report
, "w", stdout
);
393 cftime(time_buf
, DATE_FMT
, &firstime
);
394 printf("from %s", time_buf
);
395 cftime(time_buf
, DATE_FMT
, &lastime
);
396 printf("to %s", time_buf
);
398 printf("%d\tdate change%c\n", ndates
, (ndates
> 1 ? 's' :
400 for (i
= 0; i
< nsys
; i
++)
401 printf("%d\t%.*s\n", sy
[i
].snum
,
402 sizeof (sy
[i
].sname
), sy
[i
].sname
);
407 * print summary of line usage
408 * accuracy only guaranteed for wtmpx file started fresh
415 int tsess
, ton
, toff
;
417 freopen(replin
, "w", stdout
);
419 tsess
= ton
= toff
= 0;
420 timet
= MINS(lastime
-firstime
);
421 printf("TOTAL DURATION IS %.0f MINUTES\n", timet
);
422 printf("LINE MINUTES PERCENT # SESS # ON # OFF\n");
423 qsort((char *)tbuf
, tsize
+ 1, sizeof (tbuf
[0]),
424 (int (*)(const void *, const void *))tcmp
);
425 for (tp
= tbuf
; tp
<= &tbuf
[tsize
]; tp
++) {
426 timei
= MINS(tp
->ttotal
);
431 printf("%-*.*s %-7.0f %-7.0f %-6d %-4d %-5d\n",
436 (timet
> 0.)? 100*timei
/timet
: 0.,
441 printf("TOTALS %-7.0f -- %-6d %-4d %-5d\n",
442 ttime
, tsess
, ton
, toff
);
446 tcmp(struct tbuf
*t1
, struct tbuf
*t2
)
448 return (strncmp(t1
->tline
, t2
->tline
, LSZ
));
452 node_compare(const void *node1
, const void *node2
)
454 if (((const struct ctab
*)node1
)->ct_uid
>
455 ((const struct ctab
*)node2
)->ct_uid
)
457 else if (((const struct ctab
*)node1
)->ct_uid
<
458 ((const struct ctab
*)node2
)->ct_uid
)
465 enter(struct ctmp
*c
)
471 if ((pctab
= (struct ctab
*)malloc(sizeof (struct ctab
))) == NULL
) {
472 fprintf(stderr
, "acctcon: malloc fail!\n");
476 pctab
->ct_uid
= c
->ct_uid
;
477 CPYN(pctab
->ct_name
, c
->ct_name
);
478 pctab
->ct_con
[0] = c
->ct_con
[0];
479 pctab
->ct_con
[1] = c
->ct_con
[1];
482 if (*(pt
= (struct ctab
**)tsearch((void *)pctab
, (void **)&root
, \
483 node_compare
)) == NULL
) {
484 fprintf(stderr
, "Not enough space available to build tree\n");
489 (*pt
)->ct_con
[0] += c
->ct_con
[0];
490 (*pt
)->ct_con
[1] += c
->ct_con
[1];
498 print_node(const void *node
, VISIT order
, int level
)
500 if (order
== postorder
|| order
== leaf
) {
501 tb
.ta_uid
= (*(struct ctab
**)node
)->ct_uid
;
502 CPYN(tb
.ta_name
, (*(struct ctab
**)node
)->ct_name
);
503 tb
.ta_con
[0] = ((*(struct ctab
**)node
)->ct_con
[0]) / 60.0;
504 tb
.ta_con
[1] = ((*(struct ctab
**)node
)->ct_con
[1]) / 60.0;
505 tb
.ta_sc
= (*(struct ctab
**)node
)->ct_sess
;
506 fwrite(&tb
, sizeof (tb
), 1, stdout
);
513 twalk((struct ctab
*)root
, print_node
);