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.
29 #pragma ident "%Z%%M% %I% %E% SMI"
33 * acctcon [-l file] [-o file] <wtmpx-file
34 * -l file causes output of line usage summary
35 * -o file causes first/last/reboots report to be written to file
36 * reads input (normally /var/adm/wtmpx), produces
37 * list of sessions, sorted by ending time in tacct.h format
41 #include <sys/types.h>
42 #include <sys/param.h>
52 int a_tsize
= A_TSIZE
;
53 int tsize
= -1; /* highest index of used slot in tbuf table */
55 struct utmpx wb
; /* record structure read into */
56 struct ctmp cb
; /* record structure written out of */
61 char tline
[LSZ
]; /* /dev/... */
62 char tname
[NSZ
]; /* user name */
63 time_t ttime
; /* start time */
64 dev_t tdev
; /* device */
65 int tlsess
; /* # complete sessions */
66 int tlon
; /* # times on (ut_type of 7) */
67 int tloff
; /* # times off (ut_type != 7) */
68 long ttotal
; /* total time used on this line */
80 char sname
[LSZ
]; /* reasons for ACCOUNTING records */
81 char snum
; /* number of times encountered */
84 static char time_buf
[50];
85 time_t datetime
; /* old time if date changed, otherwise 0 */
88 int ndates
; /* number of times date changed */
95 static int valid(void);
96 static void fixup(FILE *);
97 static void loop(void);
98 static void bootshut(void);
99 static int iline(void);
100 static void upall(void);
101 static void update(struct tbuf
*);
102 static void printrep(void);
103 static void printlin(void);
104 static int tcmp(struct tbuf
*, struct tbuf
*);
105 static int node_compare(const void *, const void *);
106 static void enter(struct ctmp
*);
107 static void print_node(const void *, VISIT
, int);
108 static void output(void);
116 main(int argc
, char **argv
)
120 (void) setlocale(LC_ALL
, "");
121 while ((c
= getopt(argc
, argv
, "l:o:")) != EOF
)
130 fprintf(stderr
, "usage: %s [-l lineuse] "
131 "[-o reboot]\n", argv
[0]);
135 if ((tbuf
= (struct tbuf
*)calloc(a_tsize
,
136 sizeof (struct tbuf
))) == NULL
) {
137 fprintf(stderr
, "acctcon: Cannot allocate memory\n");
142 * XXX - fixme - need a good way of getting the fd that getutxent would
143 * use to access wtmpx, so we can convert this read of stdin to use
144 * the APIs and remove the dependence on the existence of the file.
146 while (fread(&wb
, sizeof (wb
), 1, stdin
) == 1) {
148 firstime
= wb
.ut_xtime
;
154 wb
.ut_name
[0] = '\0';
155 strcpy(wb
.ut_line
, "acctcon");
156 wb
.ut_type
= ACCOUNTING
;
157 wb
.ut_xtime
= lastime
;
172 * valid: check input wtmpx record, return 1 if looks OK
179 /* XPG say that user names should not start with a "-" */
180 if ((c
= wb
.ut_name
[0]) == '-')
183 for (i
= 0; i
< NSZ
; i
++) {
185 if (isalnum(c
) || c
== '$' || c
== ' ' || c
== '.' ||
186 c
== '_' || c
== '-')
194 if ((wb
.ut_type
>= EMPTY
) && (wb
.ut_type
<= UTMAXTYPE
))
203 fprintf(stream
, "bad wtmpx: offset %lu.\n", ftell(stdin
)-sizeof (wb
));
204 fprintf(stream
, "bad record is: %.*s\t%.*s\t%lu",
210 cftime(time_buf
, DATE_FMT
, &wb
.ut_xtime
);
211 fprintf(stream
, "\t%s", time_buf
);
221 if (wb
.ut_line
[0] == '\0') /* It's an init admin process */
222 return; /* no connect accounting data here */
223 switch (wb
.ut_type
) {
225 datetime
= wb
.ut_xtime
;
230 timediff
= wb
.ut_xtime
- datetime
;
231 for (tp
= tbuf
; tp
<= &tbuf
[tsize
]; tp
++)
232 tp
->ttime
+= timediff
;
242 lastime
= wb
.ut_xtime
;
248 case DEAD_PROCESS
: /* WHCC mod 3/86 */
249 update(&tbuf
[iline()]);
254 cftime(time_buf
, DATE_FMT
, &wb
.ut_xtime
);
255 fprintf(stderr
, "acctcon: invalid type %d for %s %s %s",
264 * bootshut: record reboot (or shutdown)
265 * bump count, looking up wb.ut_line in sy table
272 for (i
= 0; i
< nsys
&& !EQN(wb
.ut_line
, sy
[i
].sname
); i
++)
277 "acctcon: recompile with larger NSYS\n");
281 CPYN(sy
[i
].sname
, wb
.ut_line
);
287 * iline: look up/enter current line name in tbuf, return index
288 * (used to avoid system dependencies on naming)
295 for (i
= 0; i
<= tsize
; i
++)
296 if (EQN(wb
.ut_line
, tbuf
[i
].tline
))
298 if (++tsize
>= a_tsize
) {
299 a_tsize
= a_tsize
+ A_TSIZE
;
300 if ((tbuf
= reallocarray(tbuf
, a_tsize
,
301 sizeof (struct tbuf
))) == NULL
) {
302 fprintf(stderr
, "acctcon: Cannot reallocate memory\n");
307 CPYN(tbuf
[tsize
].tline
, wb
.ut_line
);
308 tbuf
[tsize
].tdev
= lintodev(wb
.ut_line
);
317 wb
.ut_type
= DEAD_PROCESS
; /* fudge a logoff for reboot record. */
318 for (tp
= tbuf
; tp
<= &tbuf
[tsize
]; tp
++)
323 * update tbuf with new time, write ctmp record for end of session
326 update(struct tbuf
*tp
)
328 time_t told
, /* last time for tbuf record */
329 tnew
; /* time of this record */
330 /* Difference is connect time */
335 cftime(time_buf
, DATE_FMT
, &told
);
336 fprintf(stderr
, "acctcon: bad times: old: %s", time_buf
);
337 cftime(time_buf
, DATE_FMT
, &tnew
);
338 fprintf(stderr
, "new: %s", time_buf
);
344 switch (wb
.ut_type
) {
348 * Someone logged in without logging off. Put out record.
350 if (tp
->tname
[0] != '\0') {
351 cb
.ct_tty
= tp
->tdev
;
352 CPYN(cb
.ct_name
, tp
->tname
);
353 cb
.ct_uid
= namtouid(cb
.ct_name
);
355 if (pnpsplit(cb
.ct_start
, (ulong_t
)(tnew
-told
),
357 fprintf(stderr
, "acctcon: could not calculate "
358 "prime/non-prime hours\n");
362 tp
->ttotal
+= tnew
-told
;
363 } else /* Someone just logged in */
365 CPYN(tp
->tname
, wb
.ut_name
);
369 if (tp
->tname
[0] != '\0') { /* Someone logged off */
370 /* Set up and print ctmp record */
371 cb
.ct_tty
= tp
->tdev
;
372 CPYN(cb
.ct_name
, tp
->tname
);
373 cb
.ct_uid
= namtouid(cb
.ct_name
);
375 if (pnpsplit(cb
.ct_start
, (ulong_t
)(tnew
-told
),
377 fprintf(stderr
, "acctcon: could not calculate "
378 "prime/non-prime hours\n");
382 tp
->ttotal
+= tnew
-told
;
393 freopen(report
, "w", stdout
);
394 cftime(time_buf
, DATE_FMT
, &firstime
);
395 printf("from %s", time_buf
);
396 cftime(time_buf
, DATE_FMT
, &lastime
);
397 printf("to %s", time_buf
);
399 printf("%d\tdate change%c\n", ndates
, (ndates
> 1 ? 's' :
401 for (i
= 0; i
< nsys
; i
++)
402 printf("%d\t%.*s\n", sy
[i
].snum
,
403 sizeof (sy
[i
].sname
), sy
[i
].sname
);
408 * print summary of line usage
409 * accuracy only guaranteed for wtmpx file started fresh
416 int tsess
, ton
, toff
;
418 freopen(replin
, "w", stdout
);
420 tsess
= ton
= toff
= 0;
421 timet
= MINS(lastime
-firstime
);
422 printf("TOTAL DURATION IS %.0f MINUTES\n", timet
);
423 printf("LINE MINUTES PERCENT # SESS # ON # OFF\n");
424 qsort((char *)tbuf
, tsize
+ 1, sizeof (tbuf
[0]),
425 (int (*)(const void *, const void *))tcmp
);
426 for (tp
= tbuf
; tp
<= &tbuf
[tsize
]; tp
++) {
427 timei
= MINS(tp
->ttotal
);
432 printf("%-*.*s %-7.0f %-7.0f %-6d %-4d %-5d\n",
437 (timet
> 0.)? 100*timei
/timet
: 0.,
442 printf("TOTALS %-7.0f -- %-6d %-4d %-5d\n",
443 ttime
, tsess
, ton
, toff
);
447 tcmp(struct tbuf
*t1
, struct tbuf
*t2
)
449 return (strncmp(t1
->tline
, t2
->tline
, LSZ
));
453 node_compare(const void *node1
, const void *node2
)
455 if (((const struct ctab
*)node1
)->ct_uid
>
456 ((const struct ctab
*)node2
)->ct_uid
)
458 else if (((const struct ctab
*)node1
)->ct_uid
<
459 ((const struct ctab
*)node2
)->ct_uid
)
466 enter(struct ctmp
*c
)
472 if ((pctab
= (struct ctab
*)malloc(sizeof (struct ctab
))) == NULL
) {
473 fprintf(stderr
, "acctcon: malloc fail!\n");
477 pctab
->ct_uid
= c
->ct_uid
;
478 CPYN(pctab
->ct_name
, c
->ct_name
);
479 pctab
->ct_con
[0] = c
->ct_con
[0];
480 pctab
->ct_con
[1] = c
->ct_con
[1];
483 if (*(pt
= (struct ctab
**)tsearch((void *)pctab
, (void **)&root
, \
484 node_compare
)) == NULL
) {
485 fprintf(stderr
, "Not enough space available to build tree\n");
490 (*pt
)->ct_con
[0] += c
->ct_con
[0];
491 (*pt
)->ct_con
[1] += c
->ct_con
[1];
499 print_node(const void *node
, VISIT order
, int level
)
501 if (order
== postorder
|| order
== leaf
) {
502 tb
.ta_uid
= (*(struct ctab
**)node
)->ct_uid
;
503 CPYN(tb
.ta_name
, (*(struct ctab
**)node
)->ct_name
);
504 tb
.ta_con
[0] = ((*(struct ctab
**)node
)->ct_con
[0]) / 60.0;
505 tb
.ta_con
[1] = ((*(struct ctab
**)node
)->ct_con
[1]) / 60.0;
506 tb
.ta_sc
= (*(struct ctab
**)node
)->ct_sess
;
507 fwrite(&tb
, sizeof (tb
), 1, stdout
);
514 twalk((struct ctab
*)root
, print_node
);