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 19ff9c3a32SDavid Greenman #include <sys/types.h> 20ff9c3a32SDavid Greenman #include <sys/time.h> 21ff9c3a32SDavid Greenman #include <err.h> 22ff9c3a32SDavid Greenman #include <errno.h> 23497c7558SAndrey A. Chernov #include <langinfo.h> 243efa2f58SPhilippe Charnier #include <locale.h> 25ff9c3a32SDavid Greenman #include <stdio.h> 26ff9c3a32SDavid Greenman #include <stdlib.h> 27ff9c3a32SDavid Greenman #include <string.h> 28db774d60SBruce Evans #include <timeconv.h> 29ff9c3a32SDavid Greenman #include <unistd.h> 30cf4d4e15SEd Schouten #include <utmpx.h> 31ff9c3a32SDavid Greenman 32ff9c3a32SDavid Greenman /* 33ff9c3a32SDavid Greenman * this is for our list of currently logged in sessions 34ff9c3a32SDavid Greenman */ 35ff9c3a32SDavid Greenman struct utmp_list { 36ff9c3a32SDavid Greenman struct utmp_list *next; 37cf4d4e15SEd Schouten struct utmpx usr; 38ff9c3a32SDavid Greenman }; 39ff9c3a32SDavid Greenman 40ff9c3a32SDavid Greenman /* 41ff9c3a32SDavid Greenman * this is for our list of users that are accumulating time. 42ff9c3a32SDavid Greenman */ 43ff9c3a32SDavid Greenman struct user_list { 44ff9c3a32SDavid Greenman struct user_list *next; 45cf4d4e15SEd Schouten char name[sizeof(((struct utmpx *)0)->ut_user)]; 46ff9c3a32SDavid Greenman time_t secs; 47ff9c3a32SDavid Greenman }; 48ff9c3a32SDavid Greenman 49ff9c3a32SDavid Greenman /* 50ff9c3a32SDavid Greenman * this is for chosing whether to ignore a login 51ff9c3a32SDavid Greenman */ 52ff9c3a32SDavid Greenman struct tty_list { 53ff9c3a32SDavid Greenman struct tty_list *next; 54cf4d4e15SEd Schouten char name[sizeof(((struct utmpx *)0)->ut_host) + 2]; 55b51547cfSPhilippe Charnier size_t len; 56ff9c3a32SDavid Greenman int ret; 57ff9c3a32SDavid Greenman }; 58ff9c3a32SDavid Greenman 59ff9c3a32SDavid Greenman /* 60ff9c3a32SDavid Greenman * globals - yes yuk 61ff9c3a32SDavid Greenman */ 62ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 63ff9c3a32SDavid Greenman static char *Console = CONSOLE_TTY; 64ff9c3a32SDavid Greenman #endif 65ff9c3a32SDavid Greenman static time_t Total = 0; 66ff9c3a32SDavid Greenman static time_t FirstTime = 0; 67ff9c3a32SDavid Greenman static int Flags = 0; 68ff9c3a32SDavid Greenman static struct user_list *Users = NULL; 69ff9c3a32SDavid Greenman static struct tty_list *Ttys = NULL; 70ff9c3a32SDavid Greenman 71ff9c3a32SDavid Greenman #define NEW(type) (type *)malloc(sizeof (type)) 72ff9c3a32SDavid Greenman 73ff9c3a32SDavid Greenman #define AC_W 1 /* not _PATH_WTMP */ 74ff9c3a32SDavid Greenman #define AC_D 2 /* daily totals (ignore -p) */ 75ff9c3a32SDavid Greenman #define AC_P 4 /* per-user totals */ 76ff9c3a32SDavid Greenman #define AC_U 8 /* specified users only */ 77ff9c3a32SDavid Greenman #define AC_T 16 /* specified ttys only */ 78ff9c3a32SDavid Greenman 79ff9c3a32SDavid Greenman #ifdef DEBUG 80ff9c3a32SDavid Greenman static int Debug = 0; 81ff9c3a32SDavid Greenman #endif 82ff9c3a32SDavid Greenman 83d89167b4SAlfred Perlstein int main(int, char **); 84cf4d4e15SEd Schouten int ac(const char *); 85d89167b4SAlfred Perlstein struct tty_list *add_tty(char *); 86b5a06c25SGarance A Drosehn #ifdef DEBUG 87cf4d4e15SEd Schouten const char *debug_pfx(const struct utmpx *, const struct utmpx *); 88b5a06c25SGarance A Drosehn #endif 89d89167b4SAlfred Perlstein int do_tty(char *); 90cf4d4e15SEd Schouten struct utmp_list *log_in(struct utmp_list *, struct utmpx *); 91cf4d4e15SEd Schouten struct utmp_list *log_out(struct utmp_list *, struct utmpx *); 92d89167b4SAlfred Perlstein int on_console(struct utmp_list *); 93d89167b4SAlfred Perlstein void show(const char *, time_t); 9454b60d7eSAlfred Perlstein void show_today(struct user_list *, struct utmp_list *, 9554b60d7eSAlfred Perlstein time_t); 96d89167b4SAlfred Perlstein void show_users(struct user_list *); 97d89167b4SAlfred Perlstein struct user_list *update_user(struct user_list *, char *, time_t); 98d89167b4SAlfred Perlstein void usage(void); 99ff9c3a32SDavid Greenman 100ff9c3a32SDavid Greenman struct tty_list * 1017f761c52SGarance A Drosehn add_tty(char *name) 102ff9c3a32SDavid Greenman { 103ff9c3a32SDavid Greenman struct tty_list *tp; 1042adaffb0SGarance A Drosehn char *rcp; 105ff9c3a32SDavid Greenman 106ff9c3a32SDavid Greenman Flags |= AC_T; 107ff9c3a32SDavid Greenman 108ff9c3a32SDavid Greenman if ((tp = NEW(struct tty_list)) == NULL) 109c3737d34SPhilippe Charnier errx(1, "malloc failed"); 110ff9c3a32SDavid Greenman tp->len = 0; /* full match */ 111ff9c3a32SDavid Greenman tp->ret = 1; /* do if match */ 112ff9c3a32SDavid Greenman if (*name == '!') { /* don't do if match */ 113ff9c3a32SDavid Greenman tp->ret = 0; 114ff9c3a32SDavid Greenman name++; 115ff9c3a32SDavid Greenman } 116fd96447aSKris Kennaway strlcpy(tp->name, name, sizeof (tp->name)); 117ff9c3a32SDavid Greenman if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */ 118ff9c3a32SDavid Greenman *rcp = '\0'; 119ff9c3a32SDavid Greenman tp->len = strlen(tp->name); /* match len bytes only */ 120ff9c3a32SDavid Greenman } 121ff9c3a32SDavid Greenman tp->next = Ttys; 122ff9c3a32SDavid Greenman Ttys = tp; 123ff9c3a32SDavid Greenman return Ttys; 124ff9c3a32SDavid Greenman } 125ff9c3a32SDavid Greenman 126ff9c3a32SDavid Greenman /* 127ff9c3a32SDavid Greenman * should we process the named tty? 128ff9c3a32SDavid Greenman */ 129ff9c3a32SDavid Greenman int 1307f761c52SGarance A Drosehn do_tty(char *name) 131ff9c3a32SDavid Greenman { 132ff9c3a32SDavid Greenman struct tty_list *tp; 133ff9c3a32SDavid Greenman int def_ret = 0; 134ff9c3a32SDavid Greenman 135ff9c3a32SDavid Greenman for (tp = Ttys; tp != NULL; tp = tp->next) { 136ff9c3a32SDavid Greenman if (tp->ret == 0) /* specific don't */ 137ff9c3a32SDavid Greenman def_ret = 1; /* default do */ 138ff9c3a32SDavid Greenman if (tp->len != 0) { 139ff9c3a32SDavid Greenman if (strncmp(name, tp->name, tp->len) == 0) 140ff9c3a32SDavid Greenman return tp->ret; 141ff9c3a32SDavid Greenman } else { 142ff9c3a32SDavid Greenman if (strncmp(name, tp->name, sizeof (tp->name)) == 0) 143ff9c3a32SDavid Greenman return tp->ret; 144ff9c3a32SDavid Greenman } 145ff9c3a32SDavid Greenman } 146ff9c3a32SDavid Greenman return def_ret; 147ff9c3a32SDavid Greenman } 148ff9c3a32SDavid Greenman 149ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 150ff9c3a32SDavid Greenman /* 151ff9c3a32SDavid Greenman * is someone logged in on Console? 152ff9c3a32SDavid Greenman */ 153ff9c3a32SDavid Greenman int 1547f761c52SGarance A Drosehn on_console(struct utmp_list *head) 155ff9c3a32SDavid Greenman { 156ff9c3a32SDavid Greenman struct utmp_list *up; 157ff9c3a32SDavid Greenman 158ff9c3a32SDavid Greenman for (up = head; up; up = up->next) { 159cf4d4e15SEd Schouten if (strcmp(up->usr.ut_line, Console) == 0) 160ff9c3a32SDavid Greenman return 1; 161ff9c3a32SDavid Greenman } 162ff9c3a32SDavid Greenman return 0; 163ff9c3a32SDavid Greenman } 164ff9c3a32SDavid Greenman #endif 165ff9c3a32SDavid Greenman 166ff9c3a32SDavid Greenman /* 167ff9c3a32SDavid Greenman * update user's login time 168ff9c3a32SDavid Greenman */ 169ff9c3a32SDavid Greenman struct user_list * 1707f761c52SGarance A Drosehn update_user(struct user_list *head, char *name, time_t secs) 171ff9c3a32SDavid Greenman { 172ff9c3a32SDavid Greenman struct user_list *up; 173ff9c3a32SDavid Greenman 174ff9c3a32SDavid Greenman for (up = head; up != NULL; up = up->next) { 175cf4d4e15SEd Schouten if (strcmp(up->name, name) == 0) { 176ff9c3a32SDavid Greenman up->secs += secs; 177ff9c3a32SDavid Greenman Total += secs; 178ff9c3a32SDavid Greenman return head; 179ff9c3a32SDavid Greenman } 180ff9c3a32SDavid Greenman } 181ff9c3a32SDavid Greenman /* 182ff9c3a32SDavid Greenman * not found so add new user unless specified users only 183ff9c3a32SDavid Greenman */ 184ff9c3a32SDavid Greenman if (Flags & AC_U) 185ff9c3a32SDavid Greenman return head; 186ff9c3a32SDavid Greenman 187ff9c3a32SDavid Greenman if ((up = NEW(struct user_list)) == NULL) 188c3737d34SPhilippe Charnier errx(1, "malloc failed"); 189ff9c3a32SDavid Greenman up->next = head; 190fd96447aSKris Kennaway strlcpy(up->name, name, sizeof (up->name)); 191ff9c3a32SDavid Greenman up->secs = secs; 192ff9c3a32SDavid Greenman Total += secs; 193ff9c3a32SDavid Greenman return up; 194ff9c3a32SDavid Greenman } 195ff9c3a32SDavid Greenman 196b5a06c25SGarance A Drosehn #ifdef DEBUG 197b5a06c25SGarance A Drosehn /* 198b5a06c25SGarance A Drosehn * Create a string which is the standard prefix for a debug line. It 199b5a06c25SGarance A Drosehn * includes a timestamp (perhaps with year), device-name, and user-name. 200b5a06c25SGarance A Drosehn */ 201b5a06c25SGarance A Drosehn const char * 202cf4d4e15SEd Schouten debug_pfx(const struct utmpx *event_up, const struct utmpx *userinf_up) 203b5a06c25SGarance A Drosehn { 204cf4d4e15SEd Schouten static char str_result[40 + sizeof(userinf_up->ut_line) + 205cf4d4e15SEd Schouten sizeof(userinf_up->ut_user)]; 206b5a06c25SGarance A Drosehn static char thisyear[5]; 207b5a06c25SGarance A Drosehn size_t maxcopy; 208b5a06c25SGarance A Drosehn time_t ut_timecopy; 209b5a06c25SGarance A Drosehn 210b5a06c25SGarance A Drosehn if (thisyear[0] == '\0') { 211b5a06c25SGarance A Drosehn /* Figure out what "this year" is. */ 212b5a06c25SGarance A Drosehn time(&ut_timecopy); 213b5a06c25SGarance A Drosehn strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result)); 214b5a06c25SGarance A Drosehn strlcpy(thisyear, &str_result[20], sizeof(thisyear)); 215b5a06c25SGarance A Drosehn } 216b5a06c25SGarance A Drosehn 217cf4d4e15SEd Schouten if (event_up->ut_tv.tv_sec == 0) 218b5a06c25SGarance A Drosehn strlcpy(str_result, "*ZeroTime* --:--:-- ", sizeof(str_result)); 219b5a06c25SGarance A Drosehn else { 220cf4d4e15SEd Schouten ut_timecopy = event_up->ut_tv.tv_sec; 221b5a06c25SGarance A Drosehn strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result)); 222b5a06c25SGarance A Drosehn /* 223b5a06c25SGarance A Drosehn * Include the year, if it is not the same year as "now". 224b5a06c25SGarance A Drosehn */ 225b5a06c25SGarance A Drosehn if (strncmp(&str_result[20], thisyear, 4) == 0) 226b5a06c25SGarance A Drosehn str_result[20] = '\0'; 227b5a06c25SGarance A Drosehn else { 228b5a06c25SGarance A Drosehn str_result[24] = ' '; /* Replace a '\n' */ 229b5a06c25SGarance A Drosehn str_result[25] = '\0'; 230b5a06c25SGarance A Drosehn } 231b5a06c25SGarance A Drosehn } 232b5a06c25SGarance A Drosehn 233b5a06c25SGarance A Drosehn if (userinf_up->ut_line[0] == '\0') 234b5a06c25SGarance A Drosehn strlcat(str_result, "NoDev", sizeof(str_result)); 235b5a06c25SGarance A Drosehn else { 236cf4d4e15SEd Schouten maxcopy = strlen(str_result) + sizeof(userinf_up->ut_line); 237b5a06c25SGarance A Drosehn if (maxcopy > sizeof(str_result)) 238b5a06c25SGarance A Drosehn maxcopy = sizeof(str_result); 239b5a06c25SGarance A Drosehn strlcat(str_result, userinf_up->ut_line, maxcopy); 240b5a06c25SGarance A Drosehn } 241b5a06c25SGarance A Drosehn strlcat(str_result, ": ", sizeof(str_result)); 242b5a06c25SGarance A Drosehn 243cf4d4e15SEd Schouten if (userinf_up->ut_user[0] == '\0') 244b5a06c25SGarance A Drosehn strlcat(str_result, "LogOff", sizeof(str_result)); 245b5a06c25SGarance A Drosehn else { 246cf4d4e15SEd Schouten maxcopy = strlen(str_result) + sizeof(userinf_up->ut_user); 247b5a06c25SGarance A Drosehn if (maxcopy > sizeof(str_result)) 248b5a06c25SGarance A Drosehn maxcopy = sizeof(str_result); 249cf4d4e15SEd Schouten strlcat(str_result, userinf_up->ut_user, maxcopy); 250b5a06c25SGarance A Drosehn } 251b5a06c25SGarance A Drosehn 252b5a06c25SGarance A Drosehn return (str_result); 253b5a06c25SGarance A Drosehn } 254b5a06c25SGarance A Drosehn #endif 255b5a06c25SGarance A Drosehn 256ff9c3a32SDavid Greenman int 2577f761c52SGarance A Drosehn main(int argc, char *argv[]) 258ff9c3a32SDavid Greenman { 259cf4d4e15SEd Schouten const char *wtmpf = NULL; 260ff9c3a32SDavid Greenman int c; 261ff9c3a32SDavid Greenman 2625877948cSAndrey A. Chernov (void) setlocale(LC_TIME, ""); 2635877948cSAndrey A. Chernov 2646c3f552aSWarner Losh while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) { 265ff9c3a32SDavid Greenman switch (c) { 266ff9c3a32SDavid Greenman #ifdef DEBUG 267ff9c3a32SDavid Greenman case 'D': 268ff9c3a32SDavid Greenman Debug++; 269ff9c3a32SDavid Greenman break; 270ff9c3a32SDavid Greenman #endif 271ff9c3a32SDavid Greenman case 'c': 272ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 273ff9c3a32SDavid Greenman Console = optarg; 274ff9c3a32SDavid Greenman #else 275ff9c3a32SDavid Greenman usage(); /* XXX */ 276ff9c3a32SDavid Greenman #endif 277ff9c3a32SDavid Greenman break; 278ff9c3a32SDavid Greenman case 'd': 279ff9c3a32SDavid Greenman Flags |= AC_D; 280ff9c3a32SDavid Greenman break; 281ff9c3a32SDavid Greenman case 'p': 282ff9c3a32SDavid Greenman Flags |= AC_P; 283ff9c3a32SDavid Greenman break; 284ff9c3a32SDavid Greenman case 't': /* only do specified ttys */ 285ff9c3a32SDavid Greenman add_tty(optarg); 286ff9c3a32SDavid Greenman break; 287ff9c3a32SDavid Greenman case 'w': 288cf4d4e15SEd Schouten Flags |= AC_W; 289cf4d4e15SEd Schouten wtmpf = optarg; 290ff9c3a32SDavid Greenman break; 291ff9c3a32SDavid Greenman case '?': 292ff9c3a32SDavid Greenman default: 293ff9c3a32SDavid Greenman usage(); 294ff9c3a32SDavid Greenman break; 295ff9c3a32SDavid Greenman } 296ff9c3a32SDavid Greenman } 297ff9c3a32SDavid Greenman if (optind < argc) { 298ff9c3a32SDavid Greenman /* 299ff9c3a32SDavid Greenman * initialize user list 300ff9c3a32SDavid Greenman */ 301ff9c3a32SDavid Greenman for (; optind < argc; optind++) { 302b51547cfSPhilippe Charnier Users = update_user(Users, argv[optind], (time_t)0); 303ff9c3a32SDavid Greenman } 304ff9c3a32SDavid Greenman Flags |= AC_U; /* freeze user list */ 305ff9c3a32SDavid Greenman } 306ff9c3a32SDavid Greenman if (Flags & AC_D) 307ff9c3a32SDavid Greenman Flags &= ~AC_P; 308cf4d4e15SEd Schouten ac(wtmpf); 309ff9c3a32SDavid Greenman 310ff9c3a32SDavid Greenman return 0; 311ff9c3a32SDavid Greenman } 312ff9c3a32SDavid Greenman 313ff9c3a32SDavid Greenman /* 314ff9c3a32SDavid Greenman * print login time in decimal hours 315ff9c3a32SDavid Greenman */ 316ff9c3a32SDavid Greenman void 3177f761c52SGarance A Drosehn show(const char *name, time_t secs) 318ff9c3a32SDavid Greenman { 319cf4d4e15SEd Schouten (void)printf("\t%-*s %8.2f\n", 320cf4d4e15SEd Schouten (int)sizeof(((struct utmpx *)0)->ut_user), name, 321ff9c3a32SDavid Greenman ((double)secs / 3600)); 322ff9c3a32SDavid Greenman } 323ff9c3a32SDavid Greenman 324ff9c3a32SDavid Greenman void 3257f761c52SGarance A Drosehn show_users(struct user_list *list) 326ff9c3a32SDavid Greenman { 327ff9c3a32SDavid Greenman struct user_list *lp; 328ff9c3a32SDavid Greenman 329ff9c3a32SDavid Greenman for (lp = list; lp; lp = lp->next) 330ff9c3a32SDavid Greenman show(lp->name, lp->secs); 331ff9c3a32SDavid Greenman } 332ff9c3a32SDavid Greenman 333ff9c3a32SDavid Greenman /* 334ff9c3a32SDavid Greenman * print total login time for 24hr period in decimal hours 335ff9c3a32SDavid Greenman */ 336ff9c3a32SDavid Greenman void 3377f761c52SGarance A Drosehn show_today(struct user_list *users, struct utmp_list *logins, time_t secs) 338ff9c3a32SDavid Greenman { 339ff9c3a32SDavid Greenman struct user_list *up; 340ff9c3a32SDavid Greenman struct utmp_list *lp; 341ff9c3a32SDavid Greenman char date[64]; 342ff9c3a32SDavid Greenman time_t yesterday = secs - 1; 343497c7558SAndrey A. Chernov static int d_first = -1; 344ff9c3a32SDavid Greenman 345497c7558SAndrey A. Chernov if (d_first < 0) 346497c7558SAndrey A. Chernov d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 347497c7558SAndrey A. Chernov (void)strftime(date, sizeof (date), 348497c7558SAndrey A. Chernov d_first ? "%e %b total" : "%b %e total", 349ff9c3a32SDavid Greenman localtime(&yesterday)); 350ff9c3a32SDavid Greenman 351ff9c3a32SDavid Greenman /* restore the missing second */ 352ff9c3a32SDavid Greenman yesterday++; 353ff9c3a32SDavid Greenman 354ff9c3a32SDavid Greenman for (lp = logins; lp != NULL; lp = lp->next) { 355cf4d4e15SEd Schouten secs = yesterday - lp->usr.ut_tv.tv_sec; 356cf4d4e15SEd Schouten Users = update_user(Users, lp->usr.ut_user, secs); 357cf4d4e15SEd Schouten lp->usr.ut_tv.tv_sec = yesterday; /* as if they just logged in */ 358ff9c3a32SDavid Greenman } 359ff9c3a32SDavid Greenman secs = 0; 360ff9c3a32SDavid Greenman for (up = users; up != NULL; up = up->next) { 361ff9c3a32SDavid Greenman secs += up->secs; 362ff9c3a32SDavid Greenman up->secs = 0; /* for next day */ 363ff9c3a32SDavid Greenman } 364ff9c3a32SDavid Greenman if (secs) 365ff9c3a32SDavid Greenman (void)printf("%s %11.2f\n", date, ((double)secs / 3600)); 366ff9c3a32SDavid Greenman } 367ff9c3a32SDavid Greenman 368ff9c3a32SDavid Greenman /* 369ff9c3a32SDavid Greenman * log a user out and update their times. 370ff9c3a32SDavid Greenman * if ut_line is "~", we log all users out as the system has 371ff9c3a32SDavid Greenman * been shut down. 372ff9c3a32SDavid Greenman */ 373ff9c3a32SDavid Greenman struct utmp_list * 374cf4d4e15SEd Schouten log_out(struct utmp_list *head, struct utmpx *up) 375ff9c3a32SDavid Greenman { 376ff9c3a32SDavid Greenman struct utmp_list *lp, *lp2, *tlp; 377ff9c3a32SDavid Greenman time_t secs; 378ff9c3a32SDavid Greenman 379ff9c3a32SDavid Greenman for (lp = head, lp2 = NULL; lp != NULL; ) 380cf4d4e15SEd Schouten if (up->ut_type == BOOT_TIME || up->ut_type == SHUTDOWN_TIME || 381cf4d4e15SEd Schouten (up->ut_type == DEAD_PROCESS && 382cf4d4e15SEd Schouten memcmp(lp->usr.ut_id, up->ut_id, sizeof up->ut_id) == 0)) { 383cf4d4e15SEd Schouten secs = up->ut_tv.tv_sec - lp->usr.ut_tv.tv_sec; 384cf4d4e15SEd Schouten Users = update_user(Users, lp->usr.ut_user, secs); 385ff9c3a32SDavid Greenman #ifdef DEBUG 386ff9c3a32SDavid Greenman if (Debug) 387b5a06c25SGarance A Drosehn printf("%s logged out (%2d:%02d:%02d)\n", 388b5a06c25SGarance A Drosehn debug_pfx(up, &lp->usr), (int)(secs / 3600), 389b5a06c25SGarance A Drosehn (int)((secs % 3600) / 60), 390b5a06c25SGarance A Drosehn (int)(secs % 60)); 391ff9c3a32SDavid Greenman #endif 392ff9c3a32SDavid Greenman /* 393ff9c3a32SDavid Greenman * now lose it 394ff9c3a32SDavid Greenman */ 395ff9c3a32SDavid Greenman tlp = lp; 396ff9c3a32SDavid Greenman lp = lp->next; 397ff9c3a32SDavid Greenman if (tlp == head) 398ff9c3a32SDavid Greenman head = lp; 399ff9c3a32SDavid Greenman else if (lp2 != NULL) 400ff9c3a32SDavid Greenman lp2->next = lp; 401ff9c3a32SDavid Greenman free(tlp); 402ff9c3a32SDavid Greenman } else { 403ff9c3a32SDavid Greenman lp2 = lp; 404ff9c3a32SDavid Greenman lp = lp->next; 405ff9c3a32SDavid Greenman } 406ff9c3a32SDavid Greenman return head; 407ff9c3a32SDavid Greenman } 408ff9c3a32SDavid Greenman 409ff9c3a32SDavid Greenman 410ff9c3a32SDavid Greenman /* 411ff9c3a32SDavid Greenman * if do_tty says ok, login a user 412ff9c3a32SDavid Greenman */ 413ff9c3a32SDavid Greenman struct utmp_list * 414cf4d4e15SEd Schouten log_in(struct utmp_list *head, struct utmpx *up) 415ff9c3a32SDavid Greenman { 416ff9c3a32SDavid Greenman struct utmp_list *lp; 417ff9c3a32SDavid Greenman 418ff9c3a32SDavid Greenman /* 419ff9c3a32SDavid Greenman * this could be a login. if we're not dealing with 420ff9c3a32SDavid Greenman * the console name, say it is. 421ff9c3a32SDavid Greenman * 422ff9c3a32SDavid Greenman * If we are, and if ut_host==":0.0" we know that it 423ff9c3a32SDavid Greenman * isn't a real login. _But_ if we have not yet recorded 424ff9c3a32SDavid Greenman * someone being logged in on Console - due to the wtmp 425ff9c3a32SDavid Greenman * file starting after they logged in, we'll pretend they 426ff9c3a32SDavid Greenman * logged in, at the start of the wtmp file. 427ff9c3a32SDavid Greenman */ 428ff9c3a32SDavid Greenman 429ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 430ff9c3a32SDavid Greenman if (up->ut_host[0] == ':') { 431ff9c3a32SDavid Greenman /* 432ff9c3a32SDavid Greenman * SunOS 4.0.2 does not treat ":0.0" as special but we 433ff9c3a32SDavid Greenman * do. 434ff9c3a32SDavid Greenman */ 435ff9c3a32SDavid Greenman if (on_console(head)) 436ff9c3a32SDavid Greenman return head; 437ff9c3a32SDavid Greenman /* 438ff9c3a32SDavid Greenman * ok, no recorded login, so they were here when wtmp 439ff9c3a32SDavid Greenman * started! Adjust ut_time! 440ff9c3a32SDavid Greenman */ 441ff9c3a32SDavid Greenman up->ut_time = FirstTime; 442ff9c3a32SDavid Greenman /* 443ff9c3a32SDavid Greenman * this allows us to pick the right logout 444ff9c3a32SDavid Greenman */ 445fd96447aSKris Kennaway strlcpy(up->ut_line, Console, sizeof (up->ut_line)); 446ff9c3a32SDavid Greenman } 447ff9c3a32SDavid Greenman #endif 448ff9c3a32SDavid Greenman /* 449ff9c3a32SDavid Greenman * If we are doing specified ttys only, we ignore 450ff9c3a32SDavid Greenman * anything else. 451ff9c3a32SDavid Greenman */ 452ff9c3a32SDavid Greenman if (Flags & AC_T) 453ff9c3a32SDavid Greenman if (!do_tty(up->ut_line)) 454ff9c3a32SDavid Greenman return head; 455ff9c3a32SDavid Greenman 456ff9c3a32SDavid Greenman /* 457ff9c3a32SDavid Greenman * go ahead and log them in 458ff9c3a32SDavid Greenman */ 459ff9c3a32SDavid Greenman if ((lp = NEW(struct utmp_list)) == NULL) 460c3737d34SPhilippe Charnier errx(1, "malloc failed"); 461ff9c3a32SDavid Greenman lp->next = head; 462ff9c3a32SDavid Greenman head = lp; 463cf4d4e15SEd Schouten memmove(&lp->usr, up, sizeof *up); 464ff9c3a32SDavid Greenman #ifdef DEBUG 465ff9c3a32SDavid Greenman if (Debug) { 466b5a06c25SGarance A Drosehn printf("%s logged in", debug_pfx(&lp->usr, up)); 467ff9c3a32SDavid Greenman if (*up->ut_host) 468b5a06c25SGarance A Drosehn printf(" (%-.*s)", (int)sizeof(up->ut_host), 469b5a06c25SGarance A Drosehn up->ut_host); 470ff9c3a32SDavid Greenman putchar('\n'); 471ff9c3a32SDavid Greenman } 472ff9c3a32SDavid Greenman #endif 473ff9c3a32SDavid Greenman return head; 474ff9c3a32SDavid Greenman } 475ff9c3a32SDavid Greenman 476ff9c3a32SDavid Greenman int 477cf4d4e15SEd Schouten ac(const char *file) 478ff9c3a32SDavid Greenman { 479ff9c3a32SDavid Greenman struct utmp_list *lp, *head = NULL; 480cf4d4e15SEd Schouten struct utmpx *usr, usht; 481ff9c3a32SDavid Greenman struct tm *ltm; 482e3b218cdSGarance A Drosehn time_t prev_secs, secs, ut_timecopy; 483e3b218cdSGarance A Drosehn int day, rfound, tchanged, tskipped; 484ff9c3a32SDavid Greenman 485e3b218cdSGarance A Drosehn day = -1; 486e3b218cdSGarance A Drosehn prev_secs = 1; /* Minimum acceptable date == 1970 */ 487e3b218cdSGarance A Drosehn rfound = tchanged = tskipped = 0; 488e3b218cdSGarance A Drosehn secs = 0; 489cf4d4e15SEd Schouten if (setutxdb(UTXDB_LOG, file) != 0) 490cf4d4e15SEd Schouten err(1, "%s", file); 491cf4d4e15SEd Schouten while ((usr = getutxent()) != NULL) { 492e3b218cdSGarance A Drosehn rfound++; 493cf4d4e15SEd Schouten ut_timecopy = usr->ut_tv.tv_sec; 494e3b218cdSGarance A Drosehn /* 495e3b218cdSGarance A Drosehn * With sparc64 using 64-bit time_t's, there is some system 496e3b218cdSGarance A Drosehn * routine which sets ut_time==0 (the high-order word of a 497e3b218cdSGarance A Drosehn * 64-bit time) instead of a 32-bit time value. For those 498e3b218cdSGarance A Drosehn * wtmp files, it is "more-accurate" to substitute the most- 499e3b218cdSGarance A Drosehn * recent time found, instead of throwing away the entire 500e3b218cdSGarance A Drosehn * record. While it is still just a guess, it is a better 501e3b218cdSGarance A Drosehn * guess than throwing away a log-off record and therefore 502e3b218cdSGarance A Drosehn * counting a session as if it continued to the end of the 503e3b218cdSGarance A Drosehn * month, or the next system-reboot. 504e3b218cdSGarance A Drosehn */ 505e3b218cdSGarance A Drosehn if (ut_timecopy == 0 && prev_secs > 1) { 506e3b218cdSGarance A Drosehn #ifdef DEBUG 507e3b218cdSGarance A Drosehn if (Debug) 508e3b218cdSGarance A Drosehn printf("%s - date changed to: %s", 509cf4d4e15SEd Schouten debug_pfx(usr, usr), ctime(&prev_secs)); 510e3b218cdSGarance A Drosehn #endif 511e3b218cdSGarance A Drosehn tchanged++; 512cf4d4e15SEd Schouten usr->ut_tv.tv_sec = ut_timecopy = prev_secs; 513e3b218cdSGarance A Drosehn } 514e3b218cdSGarance A Drosehn /* 515e3b218cdSGarance A Drosehn * Skip records where the time goes backwards. 516e3b218cdSGarance A Drosehn */ 517e3b218cdSGarance A Drosehn if (ut_timecopy < prev_secs) { 518e3b218cdSGarance A Drosehn #ifdef DEBUG 519e3b218cdSGarance A Drosehn if (Debug) 520e3b218cdSGarance A Drosehn printf("%s - bad date, record skipped\n", 521cf4d4e15SEd Schouten debug_pfx(usr, usr)); 522e3b218cdSGarance A Drosehn #endif 523e3b218cdSGarance A Drosehn tskipped++; 524e3b218cdSGarance A Drosehn continue; /* Skip this invalid record. */ 525e3b218cdSGarance A Drosehn } 526e3b218cdSGarance A Drosehn prev_secs = ut_timecopy; 527e3b218cdSGarance A Drosehn 528ff9c3a32SDavid Greenman if (!FirstTime) 529e3b218cdSGarance A Drosehn FirstTime = ut_timecopy; 530ff9c3a32SDavid Greenman if (Flags & AC_D) { 531e3b218cdSGarance A Drosehn ltm = localtime(&ut_timecopy); 532ff9c3a32SDavid Greenman if (day >= 0 && day != ltm->tm_yday) { 533ff9c3a32SDavid Greenman day = ltm->tm_yday; 534ff9c3a32SDavid Greenman /* 535ff9c3a32SDavid Greenman * print yesterday's total 536ff9c3a32SDavid Greenman */ 537e3b218cdSGarance A Drosehn secs = ut_timecopy; 538ff9c3a32SDavid Greenman secs -= ltm->tm_sec; 539ff9c3a32SDavid Greenman secs -= 60 * ltm->tm_min; 540ff9c3a32SDavid Greenman secs -= 3600 * ltm->tm_hour; 541ff9c3a32SDavid Greenman show_today(Users, head, secs); 542ff9c3a32SDavid Greenman } else 543ff9c3a32SDavid Greenman day = ltm->tm_yday; 544ff9c3a32SDavid Greenman } 545cf4d4e15SEd Schouten switch(usr->ut_type) { 546cf4d4e15SEd Schouten case OLD_TIME: 547e3b218cdSGarance A Drosehn secs = ut_timecopy; 548ff9c3a32SDavid Greenman break; 549cf4d4e15SEd Schouten case NEW_TIME: 550e3b218cdSGarance A Drosehn secs -= ut_timecopy; 551ff9c3a32SDavid Greenman /* 552ff9c3a32SDavid Greenman * adjust time for those logged in 553ff9c3a32SDavid Greenman */ 554ff9c3a32SDavid Greenman for (lp = head; lp != NULL; lp = lp->next) 555cf4d4e15SEd Schouten lp->usr.ut_tv.tv_sec -= secs; 556ff9c3a32SDavid Greenman break; 557cf4d4e15SEd Schouten case BOOT_TIME: 558cf4d4e15SEd Schouten case SHUTDOWN_TIME: 559cf4d4e15SEd Schouten head = log_out(head, usr); 560e3b218cdSGarance A Drosehn FirstTime = ut_timecopy; /* shouldn't be needed */ 561ff9c3a32SDavid Greenman break; 562cf4d4e15SEd Schouten case USER_PROCESS: 563ff9c3a32SDavid Greenman /* 5641ce46cefSPoul-Henning Kamp * if they came in on tty[p-sP-S]*, then it is only 565ff9c3a32SDavid Greenman * a login session if the ut_host field is non-empty 566ff9c3a32SDavid Greenman */ 567cf4d4e15SEd Schouten if (strncmp(usr->ut_line, "tty", 3) != 0 || 568cf4d4e15SEd Schouten strchr("pqrsPQRS", usr->ut_line[3]) == NULL || 569cf4d4e15SEd Schouten *usr->ut_host != '\0') 570cf4d4e15SEd Schouten head = log_in(head, usr); 571b5a06c25SGarance A Drosehn #ifdef DEBUG 572b5a06c25SGarance A Drosehn else if (Debug > 1) 573b5a06c25SGarance A Drosehn /* Things such as 'screen' sessions. */ 574b5a06c25SGarance A Drosehn printf("%s - record ignored\n", 575cf4d4e15SEd Schouten debug_pfx(usr, usr)); 576b5a06c25SGarance A Drosehn #endif 577cf4d4e15SEd Schouten break; 578cf4d4e15SEd Schouten case DEAD_PROCESS: 579cf4d4e15SEd Schouten head = log_out(head, usr); 580ff9c3a32SDavid Greenman break; 581ff9c3a32SDavid Greenman } 582ff9c3a32SDavid Greenman } 583cf4d4e15SEd Schouten endutxent(); 5842a30154fSSteve Price if (!(Flags & AC_W)) 585cf4d4e15SEd Schouten usht.ut_tv.tv_sec = time(NULL); 586cf4d4e15SEd Schouten usht.ut_type = SHUTDOWN_TIME; 587ff9c3a32SDavid Greenman 588ff9c3a32SDavid Greenman if (Flags & AC_D) { 589cf4d4e15SEd Schouten ut_timecopy = usht.ut_tv.tv_sec; 590e3b218cdSGarance A Drosehn ltm = localtime(&ut_timecopy); 591ff9c3a32SDavid Greenman if (day >= 0 && day != ltm->tm_yday) { 592ff9c3a32SDavid Greenman /* 593ff9c3a32SDavid Greenman * print yesterday's total 594ff9c3a32SDavid Greenman */ 595e3b218cdSGarance A Drosehn secs = ut_timecopy; 596ff9c3a32SDavid Greenman secs -= ltm->tm_sec; 597ff9c3a32SDavid Greenman secs -= 60 * ltm->tm_min; 598ff9c3a32SDavid Greenman secs -= 3600 * ltm->tm_hour; 599ff9c3a32SDavid Greenman show_today(Users, head, secs); 600ff9c3a32SDavid Greenman } 601ff9c3a32SDavid Greenman } 602ff9c3a32SDavid Greenman /* 603ff9c3a32SDavid Greenman * anyone still logged in gets time up to now 604ff9c3a32SDavid Greenman */ 605cf4d4e15SEd Schouten head = log_out(head, &usht); 606ff9c3a32SDavid Greenman 607ff9c3a32SDavid Greenman if (Flags & AC_D) 608ff9c3a32SDavid Greenman show_today(Users, head, time((time_t *)0)); 609ff9c3a32SDavid Greenman else { 610ff9c3a32SDavid Greenman if (Flags & AC_P) 611ff9c3a32SDavid Greenman show_users(Users); 612ff9c3a32SDavid Greenman show("total", Total); 613ff9c3a32SDavid Greenman } 614e3b218cdSGarance A Drosehn 615e3b218cdSGarance A Drosehn if (tskipped > 0) 616e3b218cdSGarance A Drosehn printf("(Skipped %d of %d records due to invalid time values)\n", 617e3b218cdSGarance A Drosehn tskipped, rfound); 618e3b218cdSGarance A Drosehn if (tchanged > 0) 619e3b218cdSGarance A Drosehn printf("(Changed %d of %d records to have a more likely time value)\n", 620e3b218cdSGarance A Drosehn tchanged, rfound); 621e3b218cdSGarance A Drosehn 622ff9c3a32SDavid Greenman return 0; 623ff9c3a32SDavid Greenman } 624ff9c3a32SDavid Greenman 625ff9c3a32SDavid Greenman void 6267f761c52SGarance A Drosehn usage(void) 627ff9c3a32SDavid Greenman { 628ff9c3a32SDavid Greenman (void)fprintf(stderr, 629ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY 630ff9c3a32SDavid Greenman "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n"); 631ff9c3a32SDavid Greenman #else 632ff9c3a32SDavid Greenman "ac [-dp] [-t tty] [-w wtmp] [users ...]\n"); 633ff9c3a32SDavid Greenman #endif 634ff9c3a32SDavid Greenman exit(1); 635ff9c3a32SDavid Greenman } 636