/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctcon [-l file] [-o file] #include #include #include "acctdef.h" #include #include #include #include #include #include #include int a_tsize = A_TSIZE; int tsize = -1; /* highest index of used slot in tbuf table */ static int csize; struct utmpx wb; /* record structure read into */ struct ctmp cb; /* record structure written out of */ struct tacct tb; double timet, timei; struct tbuf { char tline[LSZ]; /* /dev/... */ char tname[NSZ]; /* user name */ time_t ttime; /* start time */ dev_t tdev; /* device */ int tlsess; /* # complete sessions */ int tlon; /* # times on (ut_type of 7) */ int tloff; /* # times off (ut_type != 7) */ long ttotal; /* total time used on this line */ } *tbuf; struct ctab { uid_t ct_uid; char ct_name[NSZ]; long ct_con[2]; ushort_t ct_sess; } *pctab; int nsys; struct sys { char sname[LSZ]; /* reasons for ACCOUNTING records */ char snum; /* number of times encountered */ } sy[NSYS]; static char time_buf[50]; time_t datetime; /* old time if date changed, otherwise 0 */ time_t firstime; time_t lastime; int ndates; /* number of times date changed */ int exitcode; char *report = NULL; char *replin = NULL; uid_t namtouid(); dev_t lintodev(); static int valid(void); static void fixup(FILE *); static void loop(void); static void bootshut(void); static int iline(void); static void upall(void); static void update(struct tbuf *); static void printrep(void); static void printlin(void); static int tcmp(struct tbuf *, struct tbuf *); static int node_compare(const void *, const void *); static void enter(struct ctmp *); static void print_node(const void *, VISIT, int); static void output(void); extern char *optarg; extern int optind; void **root = NULL; int main(int argc, char **argv) { int c; (void) setlocale(LC_ALL, ""); while ((c = getopt(argc, argv, "l:o:")) != EOF) switch (c) { case 'l': replin = optarg; break; case 'o': report = optarg; break; case '?': fprintf(stderr, "usage: %s [-l lineuse] " "[-o reboot]\n", argv[0]); exit(1); } if ((tbuf = (struct tbuf *)calloc(a_tsize, sizeof (struct tbuf))) == NULL) { fprintf(stderr, "acctcon: Cannot allocate memory\n"); exit(3); } /* * XXX - fixme - need a good way of getting the fd that getutxent would * use to access wtmpx, so we can convert this read of stdin to use * the APIs and remove the dependence on the existence of the file. */ while (fread(&wb, sizeof (wb), 1, stdin) == 1) { if (firstime == 0) firstime = wb.ut_xtime; if (valid()) loop(); else fixup(stderr); } wb.ut_name[0] = '\0'; strcpy(wb.ut_line, "acctcon"); wb.ut_type = ACCOUNTING; wb.ut_xtime = lastime; loop(); output(); if (report != NULL) printrep(); if (replin != NULL) printlin(); exit(exitcode); } /* * valid: check input wtmpx record, return 1 if looks OK */ static int valid() { int i, c; /* XPG say that user names should not start with a "-" */ if ((c = wb.ut_name[0]) == '-') return (0); for (i = 0; i < NSZ; i++) { c = wb.ut_name[i]; if (isalnum(c) || c == '$' || c == ' ' || c == '.' || c == '_' || c == '-') continue; else if (c == '\0') break; else return (0); } if ((wb.ut_type >= EMPTY) && (wb.ut_type <= UTMAXTYPE)) return (1); return (0); } static void fixup(FILE *stream) { fprintf(stream, "bad wtmpx: offset %lu.\n", ftell(stdin)-sizeof (wb)); fprintf(stream, "bad record is: %.*s\t%.*s\t%lu", sizeof (wb.ut_line), wb.ut_line, sizeof (wb.ut_name), wb.ut_name, wb.ut_xtime); cftime(time_buf, DATE_FMT, &wb.ut_xtime); fprintf(stream, "\t%s", time_buf); exitcode = 1; } static void loop() { int timediff; struct tbuf *tp; if (wb.ut_line[0] == '\0') /* It's an init admin process */ return; /* no connect accounting data here */ switch (wb.ut_type) { case OLD_TIME: datetime = wb.ut_xtime; return; case NEW_TIME: if (datetime == 0) return; timediff = wb.ut_xtime - datetime; for (tp = tbuf; tp <= &tbuf[tsize]; tp++) tp->ttime += timediff; datetime = 0; ndates++; return; case DOWN_TIME: return; case BOOT_TIME: upall(); /* FALLTHROUGH */ case ACCOUNTING: case RUN_LVL: lastime = wb.ut_xtime; bootshut(); return; case USER_PROCESS: case LOGIN_PROCESS: case INIT_PROCESS: case DEAD_PROCESS: /* WHCC mod 3/86 */ update(&tbuf[iline()]); return; case EMPTY: return; default: cftime(time_buf, DATE_FMT, &wb.ut_xtime); fprintf(stderr, "acctcon: invalid type %d for %s %s %s", wb.ut_type, wb.ut_name, wb.ut_line, time_buf); } } /* * bootshut: record reboot (or shutdown) * bump count, looking up wb.ut_line in sy table */ static void bootshut() { int i; for (i = 0; i < nsys && !EQN(wb.ut_line, sy[i].sname); i++) ; if (i >= nsys) { if (++nsys > NSYS) { fprintf(stderr, "acctcon: recompile with larger NSYS\n"); nsys = NSYS; return; } CPYN(sy[i].sname, wb.ut_line); } sy[i].snum++; } /* * iline: look up/enter current line name in tbuf, return index * (used to avoid system dependencies on naming) */ static int iline() { int i; for (i = 0; i <= tsize; i++) if (EQN(wb.ut_line, tbuf[i].tline)) return (i); if (++tsize >= a_tsize) { a_tsize = a_tsize + A_TSIZE; if ((tbuf = (struct tbuf *)realloc(tbuf, a_tsize * sizeof (struct tbuf))) == NULL) { fprintf(stderr, "acctcon: Cannot reallocate memory\n"); exit(2); } } CPYN(tbuf[tsize].tline, wb.ut_line); tbuf[tsize].tdev = lintodev(wb.ut_line); return (tsize); } static void upall() { struct tbuf *tp; wb.ut_type = DEAD_PROCESS; /* fudge a logoff for reboot record. */ for (tp = tbuf; tp <= &tbuf[tsize]; tp++) update(tp); } /* * update tbuf with new time, write ctmp record for end of session */ static void update(struct tbuf *tp) { time_t told, /* last time for tbuf record */ tnew; /* time of this record */ /* Difference is connect time */ told = tp->ttime; tnew = wb.ut_xtime; if (told > tnew) { cftime(time_buf, DATE_FMT, &told); fprintf(stderr, "acctcon: bad times: old: %s", time_buf); cftime(time_buf, DATE_FMT, &tnew); fprintf(stderr, "new: %s", time_buf); exitcode = 1; tp->ttime = tnew; return; } tp->ttime = tnew; switch (wb.ut_type) { case USER_PROCESS: tp->tlsess++; /* * Someone logged in without logging off. Put out record. */ if (tp->tname[0] != '\0') { cb.ct_tty = tp->tdev; CPYN(cb.ct_name, tp->tname); cb.ct_uid = namtouid(cb.ct_name); cb.ct_start = told; if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told), cb.ct_con) == 0) { fprintf(stderr, "acctcon: could not calculate " "prime/non-prime hours\n"); exit(1); } enter(&cb); tp->ttotal += tnew-told; } else /* Someone just logged in */ tp->tlon++; CPYN(tp->tname, wb.ut_name); break; case DEAD_PROCESS: tp->tloff++; if (tp->tname[0] != '\0') { /* Someone logged off */ /* Set up and print ctmp record */ cb.ct_tty = tp->tdev; CPYN(cb.ct_name, tp->tname); cb.ct_uid = namtouid(cb.ct_name); cb.ct_start = told; if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told), cb.ct_con) == 0) { fprintf(stderr, "acctcon: could not calculate " "prime/non-prime hours\n"); exit(1); } enter(&cb); tp->ttotal += tnew-told; tp->tname[0] = '\0'; } } } static void printrep() { int i; freopen(report, "w", stdout); cftime(time_buf, DATE_FMT, &firstime); printf("from %s", time_buf); cftime(time_buf, DATE_FMT, &lastime); printf("to %s", time_buf); if (ndates) printf("%d\tdate change%c\n", ndates, (ndates > 1 ? 's' : '\0')); for (i = 0; i < nsys; i++) printf("%d\t%.*s\n", sy[i].snum, sizeof (sy[i].sname), sy[i].sname); } /* * print summary of line usage * accuracy only guaranteed for wtmpx file started fresh */ static void printlin() { struct tbuf *tp; double ttime; int tsess, ton, toff; freopen(replin, "w", stdout); ttime = 0.0; tsess = ton = toff = 0; timet = MINS(lastime-firstime); printf("TOTAL DURATION IS %.0f MINUTES\n", timet); printf("LINE MINUTES PERCENT # SESS # ON # OFF\n"); qsort((char *)tbuf, tsize + 1, sizeof (tbuf[0]), (int (*)(const void *, const void *))tcmp); for (tp = tbuf; tp <= &tbuf[tsize]; tp++) { timei = MINS(tp->ttotal); ttime += timei; tsess += tp->tlsess; ton += tp->tlon; toff += tp->tloff; printf("%-*.*s %-7.0f %-7.0f %-6d %-4d %-5d\n", OUTPUT_LSZ, OUTPUT_LSZ, tp->tline, timei, (timet > 0.)? 100*timei/timet : 0., tp->tlsess, tp->tlon, tp->tloff); } printf("TOTALS %-7.0f -- %-6d %-4d %-5d\n", ttime, tsess, ton, toff); } static int tcmp(struct tbuf *t1, struct tbuf *t2) { return (strncmp(t1->tline, t2->tline, LSZ)); } static int node_compare(const void *node1, const void *node2) { if (((const struct ctab *)node1)->ct_uid > ((const struct ctab *)node2)->ct_uid) return (1); else if (((const struct ctab *)node1)->ct_uid < ((const struct ctab *)node2)->ct_uid) return (-1); else return (0); } static void enter(struct ctmp *c) { unsigned i; int j; struct ctab **pt; if ((pctab = (struct ctab *)malloc(sizeof (struct ctab))) == NULL) { fprintf(stderr, "acctcon: malloc fail!\n"); exit(2); } pctab->ct_uid = c->ct_uid; CPYN(pctab->ct_name, c->ct_name); pctab->ct_con[0] = c->ct_con[0]; pctab->ct_con[1] = c->ct_con[1]; pctab->ct_sess = 1; if (*(pt = (struct ctab **)tsearch((void *)pctab, (void **)&root, \ node_compare)) == NULL) { fprintf(stderr, "Not enough space available to build tree\n"); exit(1); } if (*pt != pctab) { (*pt)->ct_con[0] += c->ct_con[0]; (*pt)->ct_con[1] += c->ct_con[1]; (*pt)->ct_sess++; free(pctab); } } static void print_node(const void *node, VISIT order, int level) { if (order == postorder || order == leaf) { tb.ta_uid = (*(struct ctab **)node)->ct_uid; CPYN(tb.ta_name, (*(struct ctab **)node)->ct_name); tb.ta_con[0] = ((*(struct ctab **)node)->ct_con[0]) / 60.0; tb.ta_con[1] = ((*(struct ctab **)node)->ct_con[1]) / 60.0; tb.ta_sc = (*(struct ctab **)node)->ct_sess; fwrite(&tb, sizeof (tb), 1, stdout); } } static void output() { twalk((struct ctab *)root, print_node); }