18f67c5bcSEd Schouten /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 48f67c5bcSEd Schouten * Copyright (c) 1994 Christopher G. Demetriou 58f67c5bcSEd Schouten * Copyright (c) 1994 Simon J. Gerraty 68f67c5bcSEd Schouten * Copyright (c) 2012 Ed Schouten <ed@FreeBSD.org> 78f67c5bcSEd Schouten * All rights reserved. 8ff9c3a32SDavid Greenman * 98f67c5bcSEd Schouten * Redistribution and use in source and binary forms, with or without 108f67c5bcSEd Schouten * modification, are permitted provided that the following conditions 118f67c5bcSEd Schouten * are met: 128f67c5bcSEd Schouten * 1. Redistributions of source code must retain the above copyright 138f67c5bcSEd Schouten * notice, this list of conditions and the following disclaimer. 148f67c5bcSEd Schouten * 2. Redistributions in binary form must reproduce the above copyright 158f67c5bcSEd Schouten * notice, this list of conditions and the following disclaimer in the 168f67c5bcSEd Schouten * documentation and/or other materials provided with the distribution. 178f67c5bcSEd Schouten * 188f67c5bcSEd Schouten * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 198f67c5bcSEd Schouten * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 208f67c5bcSEd Schouten * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 218f67c5bcSEd Schouten * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 228f67c5bcSEd Schouten * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 238f67c5bcSEd Schouten * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 248f67c5bcSEd Schouten * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 258f67c5bcSEd Schouten * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 268f67c5bcSEd Schouten * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 278f67c5bcSEd Schouten * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 288f67c5bcSEd Schouten * SUCH DAMAGE. 29ff9c3a32SDavid Greenman */ 30ff9c3a32SDavid Greenman 31b51547cfSPhilippe Charnier #include <sys/cdefs.h> 32b51547cfSPhilippe Charnier __FBSDID("$FreeBSD$"); 33ff9c3a32SDavid Greenman 34b42c08d3SEd Schouten #include <sys/queue.h> 35ff9c3a32SDavid Greenman #include <sys/time.h> 36b42c08d3SEd Schouten 37ff9c3a32SDavid Greenman #include <err.h> 38ff9c3a32SDavid Greenman #include <errno.h> 39497c7558SAndrey A. Chernov #include <langinfo.h> 403efa2f58SPhilippe Charnier #include <locale.h> 41ff9c3a32SDavid Greenman #include <stdio.h> 42ff9c3a32SDavid Greenman #include <stdlib.h> 43ff9c3a32SDavid Greenman #include <string.h> 44db774d60SBruce Evans #include <timeconv.h> 45ff9c3a32SDavid Greenman #include <unistd.h> 46cf4d4e15SEd Schouten #include <utmpx.h> 47ff9c3a32SDavid Greenman 48ff9c3a32SDavid Greenman /* 49ff9c3a32SDavid Greenman * this is for our list of currently logged in sessions 50ff9c3a32SDavid Greenman */ 51b42c08d3SEd Schouten struct utmpx_entry { 52b42c08d3SEd Schouten SLIST_ENTRY(utmpx_entry) next; 53b42c08d3SEd Schouten char user[sizeof(((struct utmpx *)0)->ut_user)]; 54b42c08d3SEd Schouten char id[sizeof(((struct utmpx *)0)->ut_id)]; 55b42c08d3SEd Schouten #ifdef CONSOLE_TTY 56b42c08d3SEd Schouten char line[sizeof(((struct utmpx *)0)->ut_line)]; 57b42c08d3SEd Schouten #endif 58b42c08d3SEd Schouten struct timeval time; 59ff9c3a32SDavid Greenman }; 60ff9c3a32SDavid Greenman 61ff9c3a32SDavid Greenman /* 62ff9c3a32SDavid Greenman * this is for our list of users that are accumulating time. 63ff9c3a32SDavid Greenman */ 64b42c08d3SEd Schouten struct user_entry { 65b42c08d3SEd Schouten SLIST_ENTRY(user_entry) next; 66b42c08d3SEd Schouten char user[sizeof(((struct utmpx *)0)->ut_user)]; 67b42c08d3SEd Schouten struct timeval time; 68ff9c3a32SDavid Greenman }; 69ff9c3a32SDavid Greenman 70ff9c3a32SDavid Greenman /* 7110924c4fSGordon Bergling * this is for choosing whether to ignore a login 72ff9c3a32SDavid Greenman */ 73b42c08d3SEd Schouten struct tty_entry { 74b42c08d3SEd Schouten SLIST_ENTRY(tty_entry) next; 75b42c08d3SEd Schouten char line[sizeof(((struct utmpx *)0)->ut_line) + 2]; 76b51547cfSPhilippe Charnier size_t len; 77ff9c3a32SDavid Greenman int ret; 78ff9c3a32SDavid Greenman }; 79ff9c3a32SDavid Greenman 80ff9c3a32SDavid Greenman /* 81ff9c3a32SDavid Greenman * globals - yes yuk 82ff9c3a32SDavid Greenman */ 83ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 84b42c08d3SEd Schouten static const char *Console = CONSOLE_TTY; 85ff9c3a32SDavid Greenman #endif 86b42c08d3SEd Schouten static struct timeval Total = { 0, 0 }; 87b42c08d3SEd Schouten static struct timeval FirstTime = { 0, 0 }; 88ff9c3a32SDavid Greenman static int Flags = 0; 89b42c08d3SEd Schouten static SLIST_HEAD(, utmpx_entry) CurUtmpx = SLIST_HEAD_INITIALIZER(CurUtmpx); 90b42c08d3SEd Schouten static SLIST_HEAD(, user_entry) Users = SLIST_HEAD_INITIALIZER(Users); 91b42c08d3SEd Schouten static SLIST_HEAD(, tty_entry) Ttys = SLIST_HEAD_INITIALIZER(Ttys); 92ff9c3a32SDavid Greenman 93ff9c3a32SDavid Greenman #define AC_W 1 /* not _PATH_WTMP */ 94ff9c3a32SDavid Greenman #define AC_D 2 /* daily totals (ignore -p) */ 95ff9c3a32SDavid Greenman #define AC_P 4 /* per-user totals */ 96ff9c3a32SDavid Greenman #define AC_U 8 /* specified users only */ 97ff9c3a32SDavid Greenman #define AC_T 16 /* specified ttys only */ 98ff9c3a32SDavid Greenman 99b42c08d3SEd Schouten static void ac(const char *); 100b42c08d3SEd Schouten static void usage(void); 101ff9c3a32SDavid Greenman 102b42c08d3SEd Schouten static void 103b42c08d3SEd Schouten add_tty(const char *line) 104ff9c3a32SDavid Greenman { 105b42c08d3SEd Schouten struct tty_entry *tp; 1062adaffb0SGarance A Drosehn char *rcp; 107ff9c3a32SDavid Greenman 108ff9c3a32SDavid Greenman Flags |= AC_T; 109ff9c3a32SDavid Greenman 110b42c08d3SEd Schouten if ((tp = malloc(sizeof(*tp))) == NULL) 111c3737d34SPhilippe Charnier errx(1, "malloc failed"); 112ff9c3a32SDavid Greenman tp->len = 0; /* full match */ 113ff9c3a32SDavid Greenman tp->ret = 1; /* do if match */ 114b42c08d3SEd Schouten if (*line == '!') { /* don't do if match */ 115ff9c3a32SDavid Greenman tp->ret = 0; 116b42c08d3SEd Schouten line++; 117ff9c3a32SDavid Greenman } 118b42c08d3SEd Schouten strlcpy(tp->line, line, sizeof(tp->line)); 119b42c08d3SEd Schouten /* Wildcard. */ 120b42c08d3SEd Schouten if ((rcp = strchr(tp->line, '*')) != NULL) { 121ff9c3a32SDavid Greenman *rcp = '\0'; 122b42c08d3SEd Schouten /* Match len bytes only. */ 123b42c08d3SEd Schouten tp->len = strlen(tp->line); 124ff9c3a32SDavid Greenman } 125b42c08d3SEd Schouten SLIST_INSERT_HEAD(&Ttys, tp, next); 126ff9c3a32SDavid Greenman } 127ff9c3a32SDavid Greenman 128ff9c3a32SDavid Greenman /* 129ff9c3a32SDavid Greenman * should we process the named tty? 130ff9c3a32SDavid Greenman */ 131b42c08d3SEd Schouten static int 132b42c08d3SEd Schouten do_tty(const char *line) 133ff9c3a32SDavid Greenman { 134b42c08d3SEd Schouten struct tty_entry *tp; 135ff9c3a32SDavid Greenman int def_ret = 0; 136ff9c3a32SDavid Greenman 137b42c08d3SEd Schouten SLIST_FOREACH(tp, &Ttys, next) { 138ff9c3a32SDavid Greenman if (tp->ret == 0) /* specific don't */ 139ff9c3a32SDavid Greenman def_ret = 1; /* default do */ 140ff9c3a32SDavid Greenman if (tp->len != 0) { 141b42c08d3SEd Schouten if (strncmp(line, tp->line, tp->len) == 0) 142ff9c3a32SDavid Greenman return tp->ret; 143ff9c3a32SDavid Greenman } else { 144b42c08d3SEd Schouten if (strncmp(line, tp->line, sizeof(tp->line)) == 0) 145ff9c3a32SDavid Greenman return tp->ret; 146ff9c3a32SDavid Greenman } 147ff9c3a32SDavid Greenman } 148b42c08d3SEd Schouten return (def_ret); 149ff9c3a32SDavid Greenman } 150ff9c3a32SDavid Greenman 151ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 152ff9c3a32SDavid Greenman /* 153ff9c3a32SDavid Greenman * is someone logged in on Console? 154ff9c3a32SDavid Greenman */ 155b42c08d3SEd Schouten static int 156b42c08d3SEd Schouten on_console(void) 157ff9c3a32SDavid Greenman { 158b42c08d3SEd Schouten struct utmpx_entry *up; 159ff9c3a32SDavid Greenman 160b42c08d3SEd Schouten SLIST_FOREACH(up, &CurUtmpx, next) 161b42c08d3SEd Schouten if (strcmp(up->line, Console) == 0) 162b42c08d3SEd Schouten return (1); 163b42c08d3SEd Schouten return (0); 164ff9c3a32SDavid Greenman } 165ff9c3a32SDavid Greenman #endif 166ff9c3a32SDavid Greenman 167ff9c3a32SDavid Greenman /* 168b42c08d3SEd Schouten * Update user's login time. 169b42c08d3SEd Schouten * If no entry for this user is found, a new entry is inserted into the 170b42c08d3SEd Schouten * list alphabetically. 171ff9c3a32SDavid Greenman */ 172b42c08d3SEd Schouten static void 173b42c08d3SEd Schouten update_user(const char *user, struct timeval secs) 174ff9c3a32SDavid Greenman { 175b42c08d3SEd Schouten struct user_entry *up, *aup; 176b42c08d3SEd Schouten int c; 177ff9c3a32SDavid Greenman 178b42c08d3SEd Schouten aup = NULL; 179b42c08d3SEd Schouten SLIST_FOREACH(up, &Users, next) { 180b42c08d3SEd Schouten c = strcmp(up->user, user); 181b42c08d3SEd Schouten if (c == 0) { 182b42c08d3SEd Schouten timeradd(&up->time, &secs, &up->time); 183b42c08d3SEd Schouten timeradd(&Total, &secs, &Total); 184b42c08d3SEd Schouten return; 185b42c08d3SEd Schouten } else if (c > 0) 186b42c08d3SEd Schouten break; 187b42c08d3SEd Schouten aup = up; 188ff9c3a32SDavid Greenman } 189ff9c3a32SDavid Greenman /* 190ff9c3a32SDavid Greenman * not found so add new user unless specified users only 191ff9c3a32SDavid Greenman */ 192ff9c3a32SDavid Greenman if (Flags & AC_U) 193b42c08d3SEd Schouten return; 194ff9c3a32SDavid Greenman 195b42c08d3SEd Schouten if ((up = malloc(sizeof(*up))) == NULL) 196c3737d34SPhilippe Charnier errx(1, "malloc failed"); 197b42c08d3SEd Schouten if (aup == NULL) 198b42c08d3SEd Schouten SLIST_INSERT_HEAD(&Users, up, next); 199b42c08d3SEd Schouten else 200b42c08d3SEd Schouten SLIST_INSERT_AFTER(aup, up, next); 201b42c08d3SEd Schouten strlcpy(up->user, user, sizeof(up->user)); 202b42c08d3SEd Schouten up->time = secs; 203b42c08d3SEd Schouten timeradd(&Total, &secs, &Total); 204ff9c3a32SDavid Greenman } 205ff9c3a32SDavid Greenman 206ff9c3a32SDavid Greenman int 2077f761c52SGarance A Drosehn main(int argc, char *argv[]) 208ff9c3a32SDavid Greenman { 209cf4d4e15SEd Schouten const char *wtmpf = NULL; 210ff9c3a32SDavid Greenman int c; 211ff9c3a32SDavid Greenman 2125877948cSAndrey A. Chernov (void) setlocale(LC_TIME, ""); 2135877948cSAndrey A. Chernov 214b42c08d3SEd Schouten while ((c = getopt(argc, argv, "c:dpt:w:")) != -1) { 215ff9c3a32SDavid Greenman switch (c) { 216ff9c3a32SDavid Greenman case 'c': 217ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 218ff9c3a32SDavid Greenman Console = optarg; 219ff9c3a32SDavid Greenman #else 220ff9c3a32SDavid Greenman usage(); /* XXX */ 221ff9c3a32SDavid Greenman #endif 222ff9c3a32SDavid Greenman break; 223ff9c3a32SDavid Greenman case 'd': 224ff9c3a32SDavid Greenman Flags |= AC_D; 225ff9c3a32SDavid Greenman break; 226ff9c3a32SDavid Greenman case 'p': 227ff9c3a32SDavid Greenman Flags |= AC_P; 228ff9c3a32SDavid Greenman break; 229ff9c3a32SDavid Greenman case 't': /* only do specified ttys */ 230ff9c3a32SDavid Greenman add_tty(optarg); 231ff9c3a32SDavid Greenman break; 232ff9c3a32SDavid Greenman case 'w': 233cf4d4e15SEd Schouten Flags |= AC_W; 234cf4d4e15SEd Schouten wtmpf = optarg; 235ff9c3a32SDavid Greenman break; 236ff9c3a32SDavid Greenman case '?': 237ff9c3a32SDavid Greenman default: 238ff9c3a32SDavid Greenman usage(); 239ff9c3a32SDavid Greenman break; 240ff9c3a32SDavid Greenman } 241ff9c3a32SDavid Greenman } 242ff9c3a32SDavid Greenman if (optind < argc) { 243ff9c3a32SDavid Greenman /* 244ff9c3a32SDavid Greenman * initialize user list 245ff9c3a32SDavid Greenman */ 246ff9c3a32SDavid Greenman for (; optind < argc; optind++) { 247b42c08d3SEd Schouten update_user(argv[optind], (struct timeval){ 0, 0 }); 248ff9c3a32SDavid Greenman } 249ff9c3a32SDavid Greenman Flags |= AC_U; /* freeze user list */ 250ff9c3a32SDavid Greenman } 251ff9c3a32SDavid Greenman if (Flags & AC_D) 252ff9c3a32SDavid Greenman Flags &= ~AC_P; 253cf4d4e15SEd Schouten ac(wtmpf); 254ff9c3a32SDavid Greenman 255b42c08d3SEd Schouten return (0); 256ff9c3a32SDavid Greenman } 257ff9c3a32SDavid Greenman 258ff9c3a32SDavid Greenman /* 259ff9c3a32SDavid Greenman * print login time in decimal hours 260ff9c3a32SDavid Greenman */ 261b42c08d3SEd Schouten static void 262b42c08d3SEd Schouten show(const char *user, struct timeval secs) 263ff9c3a32SDavid Greenman { 264cf4d4e15SEd Schouten (void)printf("\t%-*s %8.2f\n", 265b42c08d3SEd Schouten (int)sizeof(((struct user_entry *)0)->user), user, 266b42c08d3SEd Schouten (double)secs.tv_sec / 3600); 267ff9c3a32SDavid Greenman } 268ff9c3a32SDavid Greenman 269b42c08d3SEd Schouten static void 270b42c08d3SEd Schouten show_users(void) 271ff9c3a32SDavid Greenman { 272b42c08d3SEd Schouten struct user_entry *lp; 273ff9c3a32SDavid Greenman 274b42c08d3SEd Schouten SLIST_FOREACH(lp, &Users, next) 275b42c08d3SEd Schouten show(lp->user, lp->time); 276ff9c3a32SDavid Greenman } 277ff9c3a32SDavid Greenman 278ff9c3a32SDavid Greenman /* 279ff9c3a32SDavid Greenman * print total login time for 24hr period in decimal hours 280ff9c3a32SDavid Greenman */ 281b42c08d3SEd Schouten static void 282b42c08d3SEd Schouten show_today(struct timeval today) 283ff9c3a32SDavid Greenman { 284b42c08d3SEd Schouten struct user_entry *up; 285b42c08d3SEd Schouten struct utmpx_entry *lp; 286ff9c3a32SDavid Greenman char date[64]; 28707df15ddSEd Schouten struct timeval diff, total = { 0, 0 }, usec = { 0, 1 }, yesterday; 288497c7558SAndrey A. Chernov static int d_first = -1; 289ff9c3a32SDavid Greenman 290497c7558SAndrey A. Chernov if (d_first < 0) 291497c7558SAndrey A. Chernov d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 292b42c08d3SEd Schouten timersub(&today, &usec, &yesterday); 293497c7558SAndrey A. Chernov (void)strftime(date, sizeof(date), 294497c7558SAndrey A. Chernov d_first ? "%e %b total" : "%b %e total", 295b42c08d3SEd Schouten localtime(&yesterday.tv_sec)); 296ff9c3a32SDavid Greenman 297b42c08d3SEd Schouten SLIST_FOREACH(lp, &CurUtmpx, next) { 29807df15ddSEd Schouten timersub(&today, &lp->time, &diff); 29907df15ddSEd Schouten update_user(lp->user, diff); 300b42c08d3SEd Schouten /* As if they just logged in. */ 30107df15ddSEd Schouten lp->time = today; 302ff9c3a32SDavid Greenman } 303b42c08d3SEd Schouten SLIST_FOREACH(up, &Users, next) { 30407df15ddSEd Schouten timeradd(&total, &up->time, &total); 305b42c08d3SEd Schouten /* For next day. */ 306b42c08d3SEd Schouten timerclear(&up->time); 307ff9c3a32SDavid Greenman } 30807df15ddSEd Schouten if (timerisset(&total)) 30907df15ddSEd Schouten (void)printf("%s %11.2f\n", date, (double)total.tv_sec / 3600); 310ff9c3a32SDavid Greenman } 311ff9c3a32SDavid Greenman 312ff9c3a32SDavid Greenman /* 313b42c08d3SEd Schouten * Log a user out and update their times. 314b42c08d3SEd Schouten * If ut_type is BOOT_TIME or SHUTDOWN_TIME, we log all users out as the 315b42c08d3SEd Schouten * system has been shut down. 316ff9c3a32SDavid Greenman */ 317b42c08d3SEd Schouten static void 318b42c08d3SEd Schouten log_out(const struct utmpx *up) 319ff9c3a32SDavid Greenman { 320b42c08d3SEd Schouten struct utmpx_entry *lp, *lp2, *tlp; 321b42c08d3SEd Schouten struct timeval secs; 322ff9c3a32SDavid Greenman 323b42c08d3SEd Schouten for (lp = SLIST_FIRST(&CurUtmpx), lp2 = NULL; lp != NULL;) 324cf4d4e15SEd Schouten if (up->ut_type == BOOT_TIME || up->ut_type == SHUTDOWN_TIME || 325cf4d4e15SEd Schouten (up->ut_type == DEAD_PROCESS && 326b42c08d3SEd Schouten memcmp(lp->id, up->ut_id, sizeof(up->ut_id)) == 0)) { 327b42c08d3SEd Schouten timersub(&up->ut_tv, &lp->time, &secs); 328b42c08d3SEd Schouten update_user(lp->user, secs); 329ff9c3a32SDavid Greenman /* 330ff9c3a32SDavid Greenman * now lose it 331ff9c3a32SDavid Greenman */ 332ff9c3a32SDavid Greenman tlp = lp; 333b42c08d3SEd Schouten lp = SLIST_NEXT(lp, next); 334b42c08d3SEd Schouten if (lp2 == NULL) 335b42c08d3SEd Schouten SLIST_REMOVE_HEAD(&CurUtmpx, next); 336b42c08d3SEd Schouten else 337b42c08d3SEd Schouten SLIST_REMOVE_AFTER(lp2, next); 338ff9c3a32SDavid Greenman free(tlp); 339ff9c3a32SDavid Greenman } else { 340ff9c3a32SDavid Greenman lp2 = lp; 341b42c08d3SEd Schouten lp = SLIST_NEXT(lp, next); 342ff9c3a32SDavid Greenman } 343ff9c3a32SDavid Greenman } 344ff9c3a32SDavid Greenman 345ff9c3a32SDavid Greenman /* 346ff9c3a32SDavid Greenman * if do_tty says ok, login a user 347ff9c3a32SDavid Greenman */ 348b42c08d3SEd Schouten static void 349b42c08d3SEd Schouten log_in(struct utmpx *up) 350ff9c3a32SDavid Greenman { 351b42c08d3SEd Schouten struct utmpx_entry *lp; 352ff9c3a32SDavid Greenman 353ff9c3a32SDavid Greenman /* 354ff9c3a32SDavid Greenman * this could be a login. if we're not dealing with 355ff9c3a32SDavid Greenman * the console name, say it is. 356ff9c3a32SDavid Greenman * 357ff9c3a32SDavid Greenman * If we are, and if ut_host==":0.0" we know that it 358ff9c3a32SDavid Greenman * isn't a real login. _But_ if we have not yet recorded 359ff9c3a32SDavid Greenman * someone being logged in on Console - due to the wtmp 360ff9c3a32SDavid Greenman * file starting after they logged in, we'll pretend they 361ff9c3a32SDavid Greenman * logged in, at the start of the wtmp file. 362ff9c3a32SDavid Greenman */ 363ff9c3a32SDavid Greenman 364ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 365ff9c3a32SDavid Greenman if (up->ut_host[0] == ':') { 366ff9c3a32SDavid Greenman /* 367ff9c3a32SDavid Greenman * SunOS 4.0.2 does not treat ":0.0" as special but we 368ff9c3a32SDavid Greenman * do. 369ff9c3a32SDavid Greenman */ 370b42c08d3SEd Schouten if (on_console()) 371b42c08d3SEd Schouten return; 372ff9c3a32SDavid Greenman /* 373ff9c3a32SDavid Greenman * ok, no recorded login, so they were here when wtmp 374ff9c3a32SDavid Greenman * started! Adjust ut_time! 375ff9c3a32SDavid Greenman */ 376b42c08d3SEd Schouten up->ut_tv = FirstTime; 377ff9c3a32SDavid Greenman /* 378ff9c3a32SDavid Greenman * this allows us to pick the right logout 379ff9c3a32SDavid Greenman */ 380fd96447aSKris Kennaway strlcpy(up->ut_line, Console, sizeof(up->ut_line)); 381ff9c3a32SDavid Greenman } 382ff9c3a32SDavid Greenman #endif 383ff9c3a32SDavid Greenman /* 384ff9c3a32SDavid Greenman * If we are doing specified ttys only, we ignore 385ff9c3a32SDavid Greenman * anything else. 386ff9c3a32SDavid Greenman */ 387b42c08d3SEd Schouten if (Flags & AC_T && !do_tty(up->ut_line)) 388b42c08d3SEd Schouten return; 389ff9c3a32SDavid Greenman 390ff9c3a32SDavid Greenman /* 391ff9c3a32SDavid Greenman * go ahead and log them in 392ff9c3a32SDavid Greenman */ 393b42c08d3SEd Schouten if ((lp = malloc(sizeof(*lp))) == NULL) 394c3737d34SPhilippe Charnier errx(1, "malloc failed"); 395b42c08d3SEd Schouten SLIST_INSERT_HEAD(&CurUtmpx, lp, next); 396b42c08d3SEd Schouten strlcpy(lp->user, up->ut_user, sizeof(lp->user)); 397b42c08d3SEd Schouten memcpy(lp->id, up->ut_id, sizeof(lp->id)); 398b42c08d3SEd Schouten #ifdef CONSOLE_TTY 399b42c08d3SEd Schouten memcpy(lp->line, up->ut_line, sizeof(lp->line)); 400ff9c3a32SDavid Greenman #endif 401b42c08d3SEd Schouten lp->time = up->ut_tv; 402ff9c3a32SDavid Greenman } 403ff9c3a32SDavid Greenman 404b42c08d3SEd Schouten static void 405cf4d4e15SEd Schouten ac(const char *file) 406ff9c3a32SDavid Greenman { 407b42c08d3SEd Schouten struct utmpx_entry *lp; 408cf4d4e15SEd Schouten struct utmpx *usr, usht; 409ff9c3a32SDavid Greenman struct tm *ltm; 410b42c08d3SEd Schouten struct timeval prev_secs, ut_timecopy, secs, clock_shift, now; 411*def653cdSJohn Baldwin int day; 412ff9c3a32SDavid Greenman 413e3b218cdSGarance A Drosehn day = -1; 414b42c08d3SEd Schouten timerclear(&prev_secs); /* Minimum acceptable date == 1970. */ 415b42c08d3SEd Schouten timerclear(&secs); 416b42c08d3SEd Schouten timerclear(&clock_shift); 417cf4d4e15SEd Schouten if (setutxdb(UTXDB_LOG, file) != 0) 418cf4d4e15SEd Schouten err(1, "%s", file); 419cf4d4e15SEd Schouten while ((usr = getutxent()) != NULL) { 420b42c08d3SEd Schouten ut_timecopy = usr->ut_tv; 421b42c08d3SEd Schouten /* Don't let the time run backwards. */ 422b42c08d3SEd Schouten if (timercmp(&ut_timecopy, &prev_secs, <)) 423b42c08d3SEd Schouten ut_timecopy = prev_secs; 424e3b218cdSGarance A Drosehn prev_secs = ut_timecopy; 425e3b218cdSGarance A Drosehn 426b42c08d3SEd Schouten if (!timerisset(&FirstTime)) 427e3b218cdSGarance A Drosehn FirstTime = ut_timecopy; 428ff9c3a32SDavid Greenman if (Flags & AC_D) { 429b42c08d3SEd Schouten ltm = localtime(&ut_timecopy.tv_sec); 430ff9c3a32SDavid Greenman if (day >= 0 && day != ltm->tm_yday) { 431ff9c3a32SDavid Greenman day = ltm->tm_yday; 432ff9c3a32SDavid Greenman /* 433ff9c3a32SDavid Greenman * print yesterday's total 434ff9c3a32SDavid Greenman */ 435e3b218cdSGarance A Drosehn secs = ut_timecopy; 436b42c08d3SEd Schouten secs.tv_sec -= ltm->tm_sec; 437b42c08d3SEd Schouten secs.tv_sec -= 60 * ltm->tm_min; 438b42c08d3SEd Schouten secs.tv_sec -= 3600 * ltm->tm_hour; 439b42c08d3SEd Schouten secs.tv_usec = 0; 440b42c08d3SEd Schouten show_today(secs); 441ff9c3a32SDavid Greenman } else 442ff9c3a32SDavid Greenman day = ltm->tm_yday; 443ff9c3a32SDavid Greenman } 444cf4d4e15SEd Schouten switch(usr->ut_type) { 445cf4d4e15SEd Schouten case OLD_TIME: 446b42c08d3SEd Schouten clock_shift = ut_timecopy; 447ff9c3a32SDavid Greenman break; 448cf4d4e15SEd Schouten case NEW_TIME: 449b42c08d3SEd Schouten timersub(&clock_shift, &ut_timecopy, &clock_shift); 450ff9c3a32SDavid Greenman /* 451ff9c3a32SDavid Greenman * adjust time for those logged in 452ff9c3a32SDavid Greenman */ 453b42c08d3SEd Schouten SLIST_FOREACH(lp, &CurUtmpx, next) 454b42c08d3SEd Schouten timersub(&lp->time, &clock_shift, &lp->time); 455ff9c3a32SDavid Greenman break; 456cf4d4e15SEd Schouten case BOOT_TIME: 457cf4d4e15SEd Schouten case SHUTDOWN_TIME: 458b42c08d3SEd Schouten log_out(usr); 459e3b218cdSGarance A Drosehn FirstTime = ut_timecopy; /* shouldn't be needed */ 460ff9c3a32SDavid Greenman break; 461cf4d4e15SEd Schouten case USER_PROCESS: 462ff9c3a32SDavid Greenman /* 463b42c08d3SEd Schouten * If they came in on pts/..., then it is only 464b42c08d3SEd Schouten * a login session if the ut_host field is non-empty. 465ff9c3a32SDavid Greenman */ 466b42c08d3SEd Schouten if (strncmp(usr->ut_line, "pts/", 4) != 0 || 467cf4d4e15SEd Schouten *usr->ut_host != '\0') 468b42c08d3SEd Schouten log_in(usr); 469cf4d4e15SEd Schouten break; 470cf4d4e15SEd Schouten case DEAD_PROCESS: 471b42c08d3SEd Schouten log_out(usr); 472ff9c3a32SDavid Greenman break; 473ff9c3a32SDavid Greenman } 474ff9c3a32SDavid Greenman } 475cf4d4e15SEd Schouten endutxent(); 476b42c08d3SEd Schouten (void)gettimeofday(&now, NULL); 477b42c08d3SEd Schouten if (Flags & AC_W) 478b42c08d3SEd Schouten usht.ut_tv = ut_timecopy; 4799c8d0b96SEd Schouten else 480b42c08d3SEd Schouten usht.ut_tv = now; 481cf4d4e15SEd Schouten usht.ut_type = SHUTDOWN_TIME; 482ff9c3a32SDavid Greenman 483ff9c3a32SDavid Greenman if (Flags & AC_D) { 484b42c08d3SEd Schouten ltm = localtime(&ut_timecopy.tv_sec); 485ff9c3a32SDavid Greenman if (day >= 0 && day != ltm->tm_yday) { 486ff9c3a32SDavid Greenman /* 487ff9c3a32SDavid Greenman * print yesterday's total 488ff9c3a32SDavid Greenman */ 489e3b218cdSGarance A Drosehn secs = ut_timecopy; 490b42c08d3SEd Schouten secs.tv_sec -= ltm->tm_sec; 491b42c08d3SEd Schouten secs.tv_sec -= 60 * ltm->tm_min; 492b42c08d3SEd Schouten secs.tv_sec -= 3600 * ltm->tm_hour; 493b42c08d3SEd Schouten secs.tv_usec = 0; 494b42c08d3SEd Schouten show_today(secs); 495ff9c3a32SDavid Greenman } 496ff9c3a32SDavid Greenman } 497ff9c3a32SDavid Greenman /* 498ff9c3a32SDavid Greenman * anyone still logged in gets time up to now 499ff9c3a32SDavid Greenman */ 500b42c08d3SEd Schouten log_out(&usht); 501ff9c3a32SDavid Greenman 502ff9c3a32SDavid Greenman if (Flags & AC_D) 503b42c08d3SEd Schouten show_today(now); 504ff9c3a32SDavid Greenman else { 505ff9c3a32SDavid Greenman if (Flags & AC_P) 506b42c08d3SEd Schouten show_users(); 507ff9c3a32SDavid Greenman show("total", Total); 508ff9c3a32SDavid Greenman } 509ff9c3a32SDavid Greenman } 510ff9c3a32SDavid Greenman 511b42c08d3SEd Schouten static void 5127f761c52SGarance A Drosehn usage(void) 513ff9c3a32SDavid Greenman { 514ff9c3a32SDavid Greenman (void)fprintf(stderr, 515ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 516ff9c3a32SDavid Greenman "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n"); 517ff9c3a32SDavid Greenman #else 518ff9c3a32SDavid Greenman "ac [-dp] [-t tty] [-w wtmp] [users ...]\n"); 519ff9c3a32SDavid Greenman #endif 520ff9c3a32SDavid Greenman exit(1); 521ff9c3a32SDavid Greenman } 522