1ff9c3a32SDavid Greenman /* 2ff9c3a32SDavid Greenman * Copyright (c) 1994 Christopher G. Demetriou. 3ff9c3a32SDavid Greenman * @(#)Copyright (c) 1994, Simon J. Gerraty. 4ff9c3a32SDavid Greenman * 5ff9c3a32SDavid Greenman * This is free software. It comes with NO WARRANTY. 6ff9c3a32SDavid Greenman * Permission to use, modify and distribute this source code 7ff9c3a32SDavid Greenman * is granted subject to the following conditions. 8ff9c3a32SDavid Greenman * 1/ that the above copyright notice and this notice 9ff9c3a32SDavid Greenman * are preserved in all copies and that due credit be given 10ff9c3a32SDavid Greenman * to the author. 11ff9c3a32SDavid Greenman * 2/ that any changes to this code are clearly commented 12ff9c3a32SDavid Greenman * as such so that the author does not get blamed for bugs 13ff9c3a32SDavid Greenman * other than his own. 14ff9c3a32SDavid Greenman */ 15ff9c3a32SDavid Greenman 16b51547cfSPhilippe Charnier #include <sys/cdefs.h> 17b51547cfSPhilippe Charnier __FBSDID("$FreeBSD$"); 18ff9c3a32SDavid Greenman 19*b42c08d3SEd Schouten #include <sys/queue.h> 20ff9c3a32SDavid Greenman #include <sys/time.h> 21*b42c08d3SEd Schouten 22ff9c3a32SDavid Greenman #include <err.h> 23ff9c3a32SDavid Greenman #include <errno.h> 24497c7558SAndrey A. Chernov #include <langinfo.h> 253efa2f58SPhilippe Charnier #include <locale.h> 26ff9c3a32SDavid Greenman #include <stdio.h> 27ff9c3a32SDavid Greenman #include <stdlib.h> 28ff9c3a32SDavid Greenman #include <string.h> 29db774d60SBruce Evans #include <timeconv.h> 30ff9c3a32SDavid Greenman #include <unistd.h> 31cf4d4e15SEd Schouten #include <utmpx.h> 32ff9c3a32SDavid Greenman 33ff9c3a32SDavid Greenman /* 34ff9c3a32SDavid Greenman * this is for our list of currently logged in sessions 35ff9c3a32SDavid Greenman */ 36*b42c08d3SEd Schouten struct utmpx_entry { 37*b42c08d3SEd Schouten SLIST_ENTRY(utmpx_entry) next; 38*b42c08d3SEd Schouten char user[sizeof(((struct utmpx *)0)->ut_user)]; 39*b42c08d3SEd Schouten char id[sizeof(((struct utmpx *)0)->ut_id)]; 40*b42c08d3SEd Schouten #ifdef CONSOLE_TTY 41*b42c08d3SEd Schouten char line[sizeof(((struct utmpx *)0)->ut_line)]; 42*b42c08d3SEd Schouten #endif 43*b42c08d3SEd Schouten struct timeval time; 44ff9c3a32SDavid Greenman }; 45ff9c3a32SDavid Greenman 46ff9c3a32SDavid Greenman /* 47ff9c3a32SDavid Greenman * this is for our list of users that are accumulating time. 48ff9c3a32SDavid Greenman */ 49*b42c08d3SEd Schouten struct user_entry { 50*b42c08d3SEd Schouten SLIST_ENTRY(user_entry) next; 51*b42c08d3SEd Schouten char user[sizeof(((struct utmpx *)0)->ut_user)]; 52*b42c08d3SEd Schouten struct timeval time; 53ff9c3a32SDavid Greenman }; 54ff9c3a32SDavid Greenman 55ff9c3a32SDavid Greenman /* 56ff9c3a32SDavid Greenman * this is for chosing whether to ignore a login 57ff9c3a32SDavid Greenman */ 58*b42c08d3SEd Schouten struct tty_entry { 59*b42c08d3SEd Schouten SLIST_ENTRY(tty_entry) next; 60*b42c08d3SEd Schouten char line[sizeof(((struct utmpx *)0)->ut_line) + 2]; 61b51547cfSPhilippe Charnier size_t len; 62ff9c3a32SDavid Greenman int ret; 63ff9c3a32SDavid Greenman }; 64ff9c3a32SDavid Greenman 65ff9c3a32SDavid Greenman /* 66ff9c3a32SDavid Greenman * globals - yes yuk 67ff9c3a32SDavid Greenman */ 68ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 69*b42c08d3SEd Schouten static const char *Console = CONSOLE_TTY; 70ff9c3a32SDavid Greenman #endif 71*b42c08d3SEd Schouten static struct timeval Total = { 0, 0 }; 72*b42c08d3SEd Schouten static struct timeval FirstTime = { 0, 0 }; 73ff9c3a32SDavid Greenman static int Flags = 0; 74*b42c08d3SEd Schouten static SLIST_HEAD(, utmpx_entry) CurUtmpx = SLIST_HEAD_INITIALIZER(CurUtmpx); 75*b42c08d3SEd Schouten static SLIST_HEAD(, user_entry) Users = SLIST_HEAD_INITIALIZER(Users); 76*b42c08d3SEd Schouten static SLIST_HEAD(, tty_entry) Ttys = SLIST_HEAD_INITIALIZER(Ttys); 77ff9c3a32SDavid Greenman 78ff9c3a32SDavid Greenman #define AC_W 1 /* not _PATH_WTMP */ 79ff9c3a32SDavid Greenman #define AC_D 2 /* daily totals (ignore -p) */ 80ff9c3a32SDavid Greenman #define AC_P 4 /* per-user totals */ 81ff9c3a32SDavid Greenman #define AC_U 8 /* specified users only */ 82ff9c3a32SDavid Greenman #define AC_T 16 /* specified ttys only */ 83ff9c3a32SDavid Greenman 84*b42c08d3SEd Schouten static void ac(const char *); 85*b42c08d3SEd Schouten static void usage(void); 86ff9c3a32SDavid Greenman 87*b42c08d3SEd Schouten static void 88*b42c08d3SEd Schouten add_tty(const char *line) 89ff9c3a32SDavid Greenman { 90*b42c08d3SEd Schouten struct tty_entry *tp; 912adaffb0SGarance A Drosehn char *rcp; 92ff9c3a32SDavid Greenman 93ff9c3a32SDavid Greenman Flags |= AC_T; 94ff9c3a32SDavid Greenman 95*b42c08d3SEd Schouten if ((tp = malloc(sizeof(*tp))) == NULL) 96c3737d34SPhilippe Charnier errx(1, "malloc failed"); 97ff9c3a32SDavid Greenman tp->len = 0; /* full match */ 98ff9c3a32SDavid Greenman tp->ret = 1; /* do if match */ 99*b42c08d3SEd Schouten if (*line == '!') { /* don't do if match */ 100ff9c3a32SDavid Greenman tp->ret = 0; 101*b42c08d3SEd Schouten line++; 102ff9c3a32SDavid Greenman } 103*b42c08d3SEd Schouten strlcpy(tp->line, line, sizeof(tp->line)); 104*b42c08d3SEd Schouten /* Wildcard. */ 105*b42c08d3SEd Schouten if ((rcp = strchr(tp->line, '*')) != NULL) { 106ff9c3a32SDavid Greenman *rcp = '\0'; 107*b42c08d3SEd Schouten /* Match len bytes only. */ 108*b42c08d3SEd Schouten tp->len = strlen(tp->line); 109ff9c3a32SDavid Greenman } 110*b42c08d3SEd Schouten SLIST_INSERT_HEAD(&Ttys, tp, next); 111ff9c3a32SDavid Greenman } 112ff9c3a32SDavid Greenman 113ff9c3a32SDavid Greenman /* 114ff9c3a32SDavid Greenman * should we process the named tty? 115ff9c3a32SDavid Greenman */ 116*b42c08d3SEd Schouten static int 117*b42c08d3SEd Schouten do_tty(const char *line) 118ff9c3a32SDavid Greenman { 119*b42c08d3SEd Schouten struct tty_entry *tp; 120ff9c3a32SDavid Greenman int def_ret = 0; 121ff9c3a32SDavid Greenman 122*b42c08d3SEd Schouten SLIST_FOREACH(tp, &Ttys, next) { 123ff9c3a32SDavid Greenman if (tp->ret == 0) /* specific don't */ 124ff9c3a32SDavid Greenman def_ret = 1; /* default do */ 125ff9c3a32SDavid Greenman if (tp->len != 0) { 126*b42c08d3SEd Schouten if (strncmp(line, tp->line, tp->len) == 0) 127ff9c3a32SDavid Greenman return tp->ret; 128ff9c3a32SDavid Greenman } else { 129*b42c08d3SEd Schouten if (strncmp(line, tp->line, sizeof(tp->line)) == 0) 130ff9c3a32SDavid Greenman return tp->ret; 131ff9c3a32SDavid Greenman } 132ff9c3a32SDavid Greenman } 133*b42c08d3SEd Schouten return (def_ret); 134ff9c3a32SDavid Greenman } 135ff9c3a32SDavid Greenman 136ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 137ff9c3a32SDavid Greenman /* 138ff9c3a32SDavid Greenman * is someone logged in on Console? 139ff9c3a32SDavid Greenman */ 140*b42c08d3SEd Schouten static int 141*b42c08d3SEd Schouten on_console(void) 142ff9c3a32SDavid Greenman { 143*b42c08d3SEd Schouten struct utmpx_entry *up; 144ff9c3a32SDavid Greenman 145*b42c08d3SEd Schouten SLIST_FOREACH(up, &CurUtmpx, next) 146*b42c08d3SEd Schouten if (strcmp(up->line, Console) == 0) 147*b42c08d3SEd Schouten return (1); 148*b42c08d3SEd Schouten return (0); 149ff9c3a32SDavid Greenman } 150ff9c3a32SDavid Greenman #endif 151ff9c3a32SDavid Greenman 152ff9c3a32SDavid Greenman /* 153*b42c08d3SEd Schouten * Update user's login time. 154*b42c08d3SEd Schouten * If no entry for this user is found, a new entry is inserted into the 155*b42c08d3SEd Schouten * list alphabetically. 156ff9c3a32SDavid Greenman */ 157*b42c08d3SEd Schouten static void 158*b42c08d3SEd Schouten update_user(const char *user, struct timeval secs) 159ff9c3a32SDavid Greenman { 160*b42c08d3SEd Schouten struct user_entry *up, *aup; 161*b42c08d3SEd Schouten int c; 162ff9c3a32SDavid Greenman 163*b42c08d3SEd Schouten aup = NULL; 164*b42c08d3SEd Schouten SLIST_FOREACH(up, &Users, next) { 165*b42c08d3SEd Schouten c = strcmp(up->user, user); 166*b42c08d3SEd Schouten if (c == 0) { 167*b42c08d3SEd Schouten timeradd(&up->time, &secs, &up->time); 168*b42c08d3SEd Schouten timeradd(&Total, &secs, &Total); 169*b42c08d3SEd Schouten return; 170*b42c08d3SEd Schouten } else if (c > 0) 171*b42c08d3SEd Schouten break; 172*b42c08d3SEd Schouten aup = up; 173ff9c3a32SDavid Greenman } 174ff9c3a32SDavid Greenman /* 175ff9c3a32SDavid Greenman * not found so add new user unless specified users only 176ff9c3a32SDavid Greenman */ 177ff9c3a32SDavid Greenman if (Flags & AC_U) 178*b42c08d3SEd Schouten return; 179ff9c3a32SDavid Greenman 180*b42c08d3SEd Schouten if ((up = malloc(sizeof(*up))) == NULL) 181c3737d34SPhilippe Charnier errx(1, "malloc failed"); 182*b42c08d3SEd Schouten if (aup == NULL) 183*b42c08d3SEd Schouten SLIST_INSERT_HEAD(&Users, up, next); 184*b42c08d3SEd Schouten else 185*b42c08d3SEd Schouten SLIST_INSERT_AFTER(aup, up, next); 186*b42c08d3SEd Schouten strlcpy(up->user, user, sizeof(up->user)); 187*b42c08d3SEd Schouten up->time = secs; 188*b42c08d3SEd Schouten timeradd(&Total, &secs, &Total); 189ff9c3a32SDavid Greenman } 190ff9c3a32SDavid Greenman 191ff9c3a32SDavid Greenman int 1927f761c52SGarance A Drosehn main(int argc, char *argv[]) 193ff9c3a32SDavid Greenman { 194cf4d4e15SEd Schouten const char *wtmpf = NULL; 195ff9c3a32SDavid Greenman int c; 196ff9c3a32SDavid Greenman 1975877948cSAndrey A. Chernov (void) setlocale(LC_TIME, ""); 1985877948cSAndrey A. Chernov 199*b42c08d3SEd Schouten while ((c = getopt(argc, argv, "c:dpt:w:")) != -1) { 200ff9c3a32SDavid Greenman switch (c) { 201ff9c3a32SDavid Greenman case 'c': 202ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 203ff9c3a32SDavid Greenman Console = optarg; 204ff9c3a32SDavid Greenman #else 205ff9c3a32SDavid Greenman usage(); /* XXX */ 206ff9c3a32SDavid Greenman #endif 207ff9c3a32SDavid Greenman break; 208ff9c3a32SDavid Greenman case 'd': 209ff9c3a32SDavid Greenman Flags |= AC_D; 210ff9c3a32SDavid Greenman break; 211ff9c3a32SDavid Greenman case 'p': 212ff9c3a32SDavid Greenman Flags |= AC_P; 213ff9c3a32SDavid Greenman break; 214ff9c3a32SDavid Greenman case 't': /* only do specified ttys */ 215ff9c3a32SDavid Greenman add_tty(optarg); 216ff9c3a32SDavid Greenman break; 217ff9c3a32SDavid Greenman case 'w': 218cf4d4e15SEd Schouten Flags |= AC_W; 219cf4d4e15SEd Schouten wtmpf = optarg; 220ff9c3a32SDavid Greenman break; 221ff9c3a32SDavid Greenman case '?': 222ff9c3a32SDavid Greenman default: 223ff9c3a32SDavid Greenman usage(); 224ff9c3a32SDavid Greenman break; 225ff9c3a32SDavid Greenman } 226ff9c3a32SDavid Greenman } 227ff9c3a32SDavid Greenman if (optind < argc) { 228ff9c3a32SDavid Greenman /* 229ff9c3a32SDavid Greenman * initialize user list 230ff9c3a32SDavid Greenman */ 231ff9c3a32SDavid Greenman for (; optind < argc; optind++) { 232*b42c08d3SEd Schouten update_user(argv[optind], (struct timeval){ 0, 0 }); 233ff9c3a32SDavid Greenman } 234ff9c3a32SDavid Greenman Flags |= AC_U; /* freeze user list */ 235ff9c3a32SDavid Greenman } 236ff9c3a32SDavid Greenman if (Flags & AC_D) 237ff9c3a32SDavid Greenman Flags &= ~AC_P; 238cf4d4e15SEd Schouten ac(wtmpf); 239ff9c3a32SDavid Greenman 240*b42c08d3SEd Schouten return (0); 241ff9c3a32SDavid Greenman } 242ff9c3a32SDavid Greenman 243ff9c3a32SDavid Greenman /* 244ff9c3a32SDavid Greenman * print login time in decimal hours 245ff9c3a32SDavid Greenman */ 246*b42c08d3SEd Schouten static void 247*b42c08d3SEd Schouten show(const char *user, struct timeval secs) 248ff9c3a32SDavid Greenman { 249cf4d4e15SEd Schouten (void)printf("\t%-*s %8.2f\n", 250*b42c08d3SEd Schouten (int)sizeof(((struct user_entry *)0)->user), user, 251*b42c08d3SEd Schouten (double)secs.tv_sec / 3600); 252ff9c3a32SDavid Greenman } 253ff9c3a32SDavid Greenman 254*b42c08d3SEd Schouten static void 255*b42c08d3SEd Schouten show_users(void) 256ff9c3a32SDavid Greenman { 257*b42c08d3SEd Schouten struct user_entry *lp; 258ff9c3a32SDavid Greenman 259*b42c08d3SEd Schouten SLIST_FOREACH(lp, &Users, next) 260*b42c08d3SEd Schouten show(lp->user, lp->time); 261ff9c3a32SDavid Greenman } 262ff9c3a32SDavid Greenman 263ff9c3a32SDavid Greenman /* 264ff9c3a32SDavid Greenman * print total login time for 24hr period in decimal hours 265ff9c3a32SDavid Greenman */ 266*b42c08d3SEd Schouten static void 267*b42c08d3SEd Schouten show_today(struct timeval today) 268ff9c3a32SDavid Greenman { 269*b42c08d3SEd Schouten struct user_entry *up; 270*b42c08d3SEd Schouten struct utmpx_entry *lp; 271ff9c3a32SDavid Greenman char date[64]; 272*b42c08d3SEd Schouten struct timeval usec = { 0, 1 }; 273*b42c08d3SEd Schouten struct timeval yesterday; 274497c7558SAndrey A. Chernov static int d_first = -1; 275ff9c3a32SDavid Greenman 276497c7558SAndrey A. Chernov if (d_first < 0) 277497c7558SAndrey A. Chernov d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 278*b42c08d3SEd Schouten timersub(&today, &usec, &yesterday); 279497c7558SAndrey A. Chernov (void)strftime(date, sizeof(date), 280497c7558SAndrey A. Chernov d_first ? "%e %b total" : "%b %e total", 281*b42c08d3SEd Schouten localtime(&yesterday.tv_sec)); 282ff9c3a32SDavid Greenman 283ff9c3a32SDavid Greenman /* restore the missing second */ 284*b42c08d3SEd Schouten timeradd(&today, &usec, &yesterday); 285ff9c3a32SDavid Greenman 286*b42c08d3SEd Schouten SLIST_FOREACH(lp, &CurUtmpx, next) { 287*b42c08d3SEd Schouten timersub(&yesterday, &lp->time, &today); 288*b42c08d3SEd Schouten update_user(lp->user, today); 289*b42c08d3SEd Schouten /* As if they just logged in. */ 290*b42c08d3SEd Schouten lp->time = yesterday; 291ff9c3a32SDavid Greenman } 292*b42c08d3SEd Schouten timerclear(&today); 293*b42c08d3SEd Schouten SLIST_FOREACH(up, &Users, next) { 294*b42c08d3SEd Schouten timeradd(&today, &up->time, &today); 295*b42c08d3SEd Schouten /* For next day. */ 296*b42c08d3SEd Schouten timerclear(&up->time); 297ff9c3a32SDavid Greenman } 298*b42c08d3SEd Schouten if (timerisset(&today)) 299*b42c08d3SEd Schouten (void)printf("%s %11.2f\n", date, (double)today.tv_sec / 3600); 300ff9c3a32SDavid Greenman } 301ff9c3a32SDavid Greenman 302ff9c3a32SDavid Greenman /* 303*b42c08d3SEd Schouten * Log a user out and update their times. 304*b42c08d3SEd Schouten * If ut_type is BOOT_TIME or SHUTDOWN_TIME, we log all users out as the 305*b42c08d3SEd Schouten * system has been shut down. 306ff9c3a32SDavid Greenman */ 307*b42c08d3SEd Schouten static void 308*b42c08d3SEd Schouten log_out(const struct utmpx *up) 309ff9c3a32SDavid Greenman { 310*b42c08d3SEd Schouten struct utmpx_entry *lp, *lp2, *tlp; 311*b42c08d3SEd Schouten struct timeval secs; 312ff9c3a32SDavid Greenman 313*b42c08d3SEd Schouten for (lp = SLIST_FIRST(&CurUtmpx), lp2 = NULL; lp != NULL;) 314cf4d4e15SEd Schouten if (up->ut_type == BOOT_TIME || up->ut_type == SHUTDOWN_TIME || 315cf4d4e15SEd Schouten (up->ut_type == DEAD_PROCESS && 316*b42c08d3SEd Schouten memcmp(lp->id, up->ut_id, sizeof(up->ut_id)) == 0)) { 317*b42c08d3SEd Schouten timersub(&up->ut_tv, &lp->time, &secs); 318*b42c08d3SEd Schouten update_user(lp->user, secs); 319ff9c3a32SDavid Greenman /* 320ff9c3a32SDavid Greenman * now lose it 321ff9c3a32SDavid Greenman */ 322ff9c3a32SDavid Greenman tlp = lp; 323*b42c08d3SEd Schouten lp = SLIST_NEXT(lp, next); 324*b42c08d3SEd Schouten if (lp2 == NULL) 325*b42c08d3SEd Schouten SLIST_REMOVE_HEAD(&CurUtmpx, next); 326*b42c08d3SEd Schouten else 327*b42c08d3SEd Schouten SLIST_REMOVE_AFTER(lp2, next); 328ff9c3a32SDavid Greenman free(tlp); 329ff9c3a32SDavid Greenman } else { 330ff9c3a32SDavid Greenman lp2 = lp; 331*b42c08d3SEd Schouten lp = SLIST_NEXT(lp, next); 332ff9c3a32SDavid Greenman } 333ff9c3a32SDavid Greenman } 334ff9c3a32SDavid Greenman 335ff9c3a32SDavid Greenman /* 336ff9c3a32SDavid Greenman * if do_tty says ok, login a user 337ff9c3a32SDavid Greenman */ 338*b42c08d3SEd Schouten static void 339*b42c08d3SEd Schouten log_in(struct utmpx *up) 340ff9c3a32SDavid Greenman { 341*b42c08d3SEd Schouten struct utmpx_entry *lp; 342ff9c3a32SDavid Greenman 343ff9c3a32SDavid Greenman /* 344ff9c3a32SDavid Greenman * this could be a login. if we're not dealing with 345ff9c3a32SDavid Greenman * the console name, say it is. 346ff9c3a32SDavid Greenman * 347ff9c3a32SDavid Greenman * If we are, and if ut_host==":0.0" we know that it 348ff9c3a32SDavid Greenman * isn't a real login. _But_ if we have not yet recorded 349ff9c3a32SDavid Greenman * someone being logged in on Console - due to the wtmp 350ff9c3a32SDavid Greenman * file starting after they logged in, we'll pretend they 351ff9c3a32SDavid Greenman * logged in, at the start of the wtmp file. 352ff9c3a32SDavid Greenman */ 353ff9c3a32SDavid Greenman 354ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 355ff9c3a32SDavid Greenman if (up->ut_host[0] == ':') { 356ff9c3a32SDavid Greenman /* 357ff9c3a32SDavid Greenman * SunOS 4.0.2 does not treat ":0.0" as special but we 358ff9c3a32SDavid Greenman * do. 359ff9c3a32SDavid Greenman */ 360*b42c08d3SEd Schouten if (on_console()) 361*b42c08d3SEd Schouten return; 362ff9c3a32SDavid Greenman /* 363ff9c3a32SDavid Greenman * ok, no recorded login, so they were here when wtmp 364ff9c3a32SDavid Greenman * started! Adjust ut_time! 365ff9c3a32SDavid Greenman */ 366*b42c08d3SEd Schouten up->ut_tv = FirstTime; 367ff9c3a32SDavid Greenman /* 368ff9c3a32SDavid Greenman * this allows us to pick the right logout 369ff9c3a32SDavid Greenman */ 370fd96447aSKris Kennaway strlcpy(up->ut_line, Console, sizeof(up->ut_line)); 371ff9c3a32SDavid Greenman } 372ff9c3a32SDavid Greenman #endif 373ff9c3a32SDavid Greenman /* 374ff9c3a32SDavid Greenman * If we are doing specified ttys only, we ignore 375ff9c3a32SDavid Greenman * anything else. 376ff9c3a32SDavid Greenman */ 377*b42c08d3SEd Schouten if (Flags & AC_T && !do_tty(up->ut_line)) 378*b42c08d3SEd Schouten return; 379ff9c3a32SDavid Greenman 380ff9c3a32SDavid Greenman /* 381ff9c3a32SDavid Greenman * go ahead and log them in 382ff9c3a32SDavid Greenman */ 383*b42c08d3SEd Schouten if ((lp = malloc(sizeof(*lp))) == NULL) 384c3737d34SPhilippe Charnier errx(1, "malloc failed"); 385*b42c08d3SEd Schouten SLIST_INSERT_HEAD(&CurUtmpx, lp, next); 386*b42c08d3SEd Schouten strlcpy(lp->user, up->ut_user, sizeof(lp->user)); 387*b42c08d3SEd Schouten memcpy(lp->id, up->ut_id, sizeof(lp->id)); 388*b42c08d3SEd Schouten #ifdef CONSOLE_TTY 389*b42c08d3SEd Schouten memcpy(lp->line, up->ut_line, sizeof(lp->line)); 390ff9c3a32SDavid Greenman #endif 391*b42c08d3SEd Schouten lp->time = up->ut_tv; 392ff9c3a32SDavid Greenman } 393ff9c3a32SDavid Greenman 394*b42c08d3SEd Schouten static void 395cf4d4e15SEd Schouten ac(const char *file) 396ff9c3a32SDavid Greenman { 397*b42c08d3SEd Schouten struct utmpx_entry *lp; 398cf4d4e15SEd Schouten struct utmpx *usr, usht; 399ff9c3a32SDavid Greenman struct tm *ltm; 400*b42c08d3SEd Schouten struct timeval prev_secs, ut_timecopy, secs, clock_shift, now; 401*b42c08d3SEd Schouten int day, rfound; 402ff9c3a32SDavid Greenman 403e3b218cdSGarance A Drosehn day = -1; 404*b42c08d3SEd Schouten timerclear(&prev_secs); /* Minimum acceptable date == 1970. */ 405*b42c08d3SEd Schouten timerclear(&secs); 406*b42c08d3SEd Schouten timerclear(&clock_shift); 407*b42c08d3SEd Schouten rfound = 0; 408cf4d4e15SEd Schouten if (setutxdb(UTXDB_LOG, file) != 0) 409cf4d4e15SEd Schouten err(1, "%s", file); 410cf4d4e15SEd Schouten while ((usr = getutxent()) != NULL) { 411e3b218cdSGarance A Drosehn rfound++; 412*b42c08d3SEd Schouten ut_timecopy = usr->ut_tv; 413*b42c08d3SEd Schouten /* Don't let the time run backwards. */ 414*b42c08d3SEd Schouten if (timercmp(&ut_timecopy, &prev_secs, <)) 415*b42c08d3SEd Schouten ut_timecopy = prev_secs; 416e3b218cdSGarance A Drosehn prev_secs = ut_timecopy; 417e3b218cdSGarance A Drosehn 418*b42c08d3SEd Schouten if (!timerisset(&FirstTime)) 419e3b218cdSGarance A Drosehn FirstTime = ut_timecopy; 420ff9c3a32SDavid Greenman if (Flags & AC_D) { 421*b42c08d3SEd Schouten ltm = localtime(&ut_timecopy.tv_sec); 422ff9c3a32SDavid Greenman if (day >= 0 && day != ltm->tm_yday) { 423ff9c3a32SDavid Greenman day = ltm->tm_yday; 424ff9c3a32SDavid Greenman /* 425ff9c3a32SDavid Greenman * print yesterday's total 426ff9c3a32SDavid Greenman */ 427e3b218cdSGarance A Drosehn secs = ut_timecopy; 428*b42c08d3SEd Schouten secs.tv_sec -= ltm->tm_sec; 429*b42c08d3SEd Schouten secs.tv_sec -= 60 * ltm->tm_min; 430*b42c08d3SEd Schouten secs.tv_sec -= 3600 * ltm->tm_hour; 431*b42c08d3SEd Schouten secs.tv_usec = 0; 432*b42c08d3SEd Schouten show_today(secs); 433ff9c3a32SDavid Greenman } else 434ff9c3a32SDavid Greenman day = ltm->tm_yday; 435ff9c3a32SDavid Greenman } 436cf4d4e15SEd Schouten switch(usr->ut_type) { 437cf4d4e15SEd Schouten case OLD_TIME: 438*b42c08d3SEd Schouten clock_shift = ut_timecopy; 439ff9c3a32SDavid Greenman break; 440cf4d4e15SEd Schouten case NEW_TIME: 441*b42c08d3SEd Schouten timersub(&clock_shift, &ut_timecopy, &clock_shift); 442ff9c3a32SDavid Greenman /* 443ff9c3a32SDavid Greenman * adjust time for those logged in 444ff9c3a32SDavid Greenman */ 445*b42c08d3SEd Schouten SLIST_FOREACH(lp, &CurUtmpx, next) 446*b42c08d3SEd Schouten timersub(&lp->time, &clock_shift, &lp->time); 447ff9c3a32SDavid Greenman break; 448cf4d4e15SEd Schouten case BOOT_TIME: 449cf4d4e15SEd Schouten case SHUTDOWN_TIME: 450*b42c08d3SEd Schouten log_out(usr); 451e3b218cdSGarance A Drosehn FirstTime = ut_timecopy; /* shouldn't be needed */ 452ff9c3a32SDavid Greenman break; 453cf4d4e15SEd Schouten case USER_PROCESS: 454ff9c3a32SDavid Greenman /* 455*b42c08d3SEd Schouten * If they came in on pts/..., then it is only 456*b42c08d3SEd Schouten * a login session if the ut_host field is non-empty. 457ff9c3a32SDavid Greenman */ 458*b42c08d3SEd Schouten if (strncmp(usr->ut_line, "pts/", 4) != 0 || 459cf4d4e15SEd Schouten *usr->ut_host != '\0') 460*b42c08d3SEd Schouten log_in(usr); 461cf4d4e15SEd Schouten break; 462cf4d4e15SEd Schouten case DEAD_PROCESS: 463*b42c08d3SEd Schouten log_out(usr); 464ff9c3a32SDavid Greenman break; 465ff9c3a32SDavid Greenman } 466ff9c3a32SDavid Greenman } 467cf4d4e15SEd Schouten endutxent(); 468*b42c08d3SEd Schouten (void)gettimeofday(&now, NULL); 469*b42c08d3SEd Schouten if (Flags & AC_W) 470*b42c08d3SEd Schouten usht.ut_tv = ut_timecopy; 4719c8d0b96SEd Schouten else 472*b42c08d3SEd Schouten usht.ut_tv = now; 473cf4d4e15SEd Schouten usht.ut_type = SHUTDOWN_TIME; 474ff9c3a32SDavid Greenman 475ff9c3a32SDavid Greenman if (Flags & AC_D) { 476*b42c08d3SEd Schouten ltm = localtime(&ut_timecopy.tv_sec); 477ff9c3a32SDavid Greenman if (day >= 0 && day != ltm->tm_yday) { 478ff9c3a32SDavid Greenman /* 479ff9c3a32SDavid Greenman * print yesterday's total 480ff9c3a32SDavid Greenman */ 481e3b218cdSGarance A Drosehn secs = ut_timecopy; 482*b42c08d3SEd Schouten secs.tv_sec -= ltm->tm_sec; 483*b42c08d3SEd Schouten secs.tv_sec -= 60 * ltm->tm_min; 484*b42c08d3SEd Schouten secs.tv_sec -= 3600 * ltm->tm_hour; 485*b42c08d3SEd Schouten secs.tv_usec = 0; 486*b42c08d3SEd Schouten show_today(secs); 487ff9c3a32SDavid Greenman } 488ff9c3a32SDavid Greenman } 489ff9c3a32SDavid Greenman /* 490ff9c3a32SDavid Greenman * anyone still logged in gets time up to now 491ff9c3a32SDavid Greenman */ 492*b42c08d3SEd Schouten log_out(&usht); 493ff9c3a32SDavid Greenman 494ff9c3a32SDavid Greenman if (Flags & AC_D) 495*b42c08d3SEd Schouten show_today(now); 496ff9c3a32SDavid Greenman else { 497ff9c3a32SDavid Greenman if (Flags & AC_P) 498*b42c08d3SEd Schouten show_users(); 499ff9c3a32SDavid Greenman show("total", Total); 500ff9c3a32SDavid Greenman } 501ff9c3a32SDavid Greenman } 502ff9c3a32SDavid Greenman 503*b42c08d3SEd Schouten static void 5047f761c52SGarance A Drosehn usage(void) 505ff9c3a32SDavid Greenman { 506ff9c3a32SDavid Greenman (void)fprintf(stderr, 507ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 508ff9c3a32SDavid Greenman "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n"); 509ff9c3a32SDavid Greenman #else 510ff9c3a32SDavid Greenman "ac [-dp] [-t tty] [-w wtmp] [users ...]\n"); 511ff9c3a32SDavid Greenman #endif 512ff9c3a32SDavid Greenman exit(1); 513ff9c3a32SDavid Greenman } 514