ac.c revision 126750
11558Srgrimes/* 21558Srgrimes * Copyright (c) 1994 Christopher G. Demetriou. 31558Srgrimes * @(#)Copyright (c) 1994, Simon J. Gerraty. 41558Srgrimes * 51558Srgrimes * This is free software. It comes with NO WARRANTY. 61558Srgrimes * Permission to use, modify and distribute this source code 71558Srgrimes * is granted subject to the following conditions. 81558Srgrimes * 1/ that the above copyright notice and this notice 91558Srgrimes * are preserved in all copies and that due credit be given 101558Srgrimes * to the author. 111558Srgrimes * 2/ that any changes to this code are clearly commented 121558Srgrimes * as such so that the author does not get blamed for bugs 131558Srgrimes * other than his own. 141558Srgrimes */ 151558Srgrimes 161558Srgrimes#include <sys/cdefs.h> 171558Srgrimes__FBSDID("$FreeBSD: head/usr.sbin/ac/ac.c 126750 2004-03-08 19:20:06Z gad $"); 181558Srgrimes 191558Srgrimes#include <sys/types.h> 201558Srgrimes#include <sys/time.h> 211558Srgrimes#include <err.h> 221558Srgrimes#include <errno.h> 231558Srgrimes#include <langinfo.h> 241558Srgrimes#include <locale.h> 251558Srgrimes#include <stdio.h> 261558Srgrimes#include <stdlib.h> 271558Srgrimes#include <string.h> 281558Srgrimes#include <timeconv.h> 291558Srgrimes#include <unistd.h> 301558Srgrimes#include <utmp.h> 311558Srgrimes 321558Srgrimes/* 331558Srgrimes * this is for our list of currently logged in sessions 341558Srgrimes */ 3538040Scharnierstruct utmp_list { 361558Srgrimes struct utmp_list *next; 371558Srgrimes struct utmp usr; 381558Srgrimes}; 391558Srgrimes 401558Srgrimes/* 4138040Scharnier * this is for our list of users that are accumulating time. 421558Srgrimes */ 4338040Scharnierstruct user_list { 4438040Scharnier struct user_list *next; 4550476Speter char name[UT_NAMESIZE+1]; 461558Srgrimes time_t secs; 471558Srgrimes}; 481558Srgrimes 491558Srgrimes/* 501558Srgrimes * this is for chosing whether to ignore a login 511558Srgrimes */ 5242873Sluoqistruct tty_list { 5396478Sphk struct tty_list *next; 541558Srgrimes char name[UT_LINESIZE+3]; 551558Srgrimes size_t len; 561558Srgrimes int ret; 5742873Sluoqi}; 581558Srgrimes 591558Srgrimes/* 601558Srgrimes * globals - yes yuk 611558Srgrimes */ 6238040Scharnier#ifdef CONSOLE_TTY 631558Srgrimesstatic char *Console = CONSOLE_TTY; 641558Srgrimes#endif 6558047Ssheldonhstatic time_t Total = 0; 661558Srgrimesstatic time_t FirstTime = 0; 671558Srgrimesstatic int Flags = 0; 681558Srgrimesstatic struct user_list *Users = NULL; 691558Srgrimesstatic struct tty_list *Ttys = NULL; 701558Srgrimes 711558Srgrimes#define NEW(type) (type *)malloc(sizeof (type)) 721558Srgrimes 731558Srgrimes#define AC_W 1 /* not _PATH_WTMP */ 741558Srgrimes#define AC_D 2 /* daily totals (ignore -p) */ 751558Srgrimes#define AC_P 4 /* per-user totals */ 761558Srgrimes#define AC_U 8 /* specified users only */ 771558Srgrimes#define AC_T 16 /* specified ttys only */ 781558Srgrimes 791558Srgrimes#ifdef DEBUG 8092883Simpstatic int Debug = 0; 8192883Simp#endif 8292883Simp 8392883Simpint main(int, char **); 8492883Simpint ac(FILE *); 8592883Simpstruct tty_list *add_tty(char *); 861558Srgrimes#ifdef DEBUG 871558Srgrimesconst char *debug_pfx(const struct utmp *, const struct utmp *); 881558Srgrimes#endif 891558Srgrimesint do_tty(char *); 901558SrgrimesFILE *file(const char *); 911558Srgrimesstruct utmp_list *log_in(struct utmp_list *, struct utmp *); 9279750Sddstruct utmp_list *log_out(struct utmp_list *, struct utmp *); 9379750Sddint on_console(struct utmp_list *); 941558Srgrimesvoid show(const char *, time_t); 9542873Sluoqivoid show_today(struct user_list *, struct utmp_list *, 9675377Smckusick time_t); 9775377Smckusickvoid show_users(struct user_list *); 9875377Smckusickstruct user_list *update_user(struct user_list *, char *, time_t); 9975377Smckusickvoid usage(void); 10069829Scharnier 1011558Srgrimes/* 10279750Sdd * open wtmp or die 10379750Sdd */ 10442873SluoqiFILE * 10542873Sluoqifile(const char *name) 10669314Scharnier{ 1071558Srgrimes FILE *fp; 10869314Scharnier 10969314Scharnier if ((fp = fopen(name, "r")) == NULL) 11069314Scharnier err(1, "%s", name); 11175377Smckusick /* in case we want to discriminate */ 11269314Scharnier if (strcmp(_PATH_WTMP, name)) 11369314Scharnier Flags |= AC_W; 11469314Scharnier return fp; 11569314Scharnier} 11669314Scharnier 11769314Scharnierstruct tty_list * 11869314Scharnieradd_tty(char *name) 11969314Scharnier{ 12069829Scharnier struct tty_list *tp; 12169829Scharnier char *rcp; 12269314Scharnier 12369829Scharnier Flags |= AC_T; 12469314Scharnier 12569314Scharnier if ((tp = NEW(struct tty_list)) == NULL) 12669314Scharnier errx(1, "malloc failed"); 12769314Scharnier tp->len = 0; /* full match */ 12869829Scharnier tp->ret = 1; /* do if match */ 12969829Scharnier if (*name == '!') { /* don't do if match */ 13069314Scharnier tp->ret = 0; 13169314Scharnier name++; 13269314Scharnier } 13369314Scharnier strlcpy(tp->name, name, sizeof (tp->name)); 13469829Scharnier if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */ 13569829Scharnier *rcp = '\0'; 13669314Scharnier tp->len = strlen(tp->name); /* match len bytes only */ 13769829Scharnier } 13869314Scharnier tp->next = Ttys; 13975377Smckusick Ttys = tp; 14075377Smckusick return Ttys; 14175377Smckusick} 14275377Smckusick 14375377Smckusick/* 14475377Smckusick * should we process the named tty? 14575377Smckusick */ 14675377Smckusickint 14769314Scharnierdo_tty(char *name) 14869314Scharnier{ 14969314Scharnier struct tty_list *tp; 15069829Scharnier int def_ret = 0; 15169829Scharnier 15269314Scharnier for (tp = Ttys; tp != NULL; tp = tp->next) { 15369829Scharnier if (tp->ret == 0) /* specific don't */ 15469314Scharnier def_ret = 1; /* default do */ 15569314Scharnier if (tp->len != 0) { 15669314Scharnier if (strncmp(name, tp->name, tp->len) == 0) 15769314Scharnier return tp->ret; 15869829Scharnier } else { 15969829Scharnier if (strncmp(name, tp->name, sizeof (tp->name)) == 0) 16069314Scharnier return tp->ret; 16169314Scharnier } 16269314Scharnier } 16369829Scharnier return def_ret; 16469314Scharnier} 16569314Scharnier 16669314Scharnier#ifdef CONSOLE_TTY 16769314Scharnier/* 16869314Scharnier * is someone logged in on Console? 16969314Scharnier */ 17069314Scharnierint 17169829Scharnieron_console(struct utmp_list *head) 17269314Scharnier{ 17369829Scharnier struct utmp_list *up; 17469314Scharnier 17569314Scharnier for (up = head; up; up = up->next) { 1761558Srgrimes if (strncmp(up->usr.ut_line, Console, 17769829Scharnier sizeof (up->usr.ut_line)) == 0) 17869314Scharnier return 1; 17969314Scharnier } 18071790Sben return 0; 18169829Scharnier} 18269829Scharnier#endif 18375377Smckusick 18475377Smckusick/* 18575377Smckusick * update user's login time 18675377Smckusick */ 18775377Smckusickstruct user_list * 18875377Smckusickupdate_user(struct user_list *head, char *name, time_t secs) 18975377Smckusick{ 19075377Smckusick struct user_list *up; 19169314Scharnier 19269314Scharnier for (up = head; up != NULL; up = up->next) { 19369314Scharnier if (strncmp(up->name, name, UT_NAMESIZE) == 0) { 19469314Scharnier up->secs += secs; 19569314Scharnier Total += secs; 1961558Srgrimes return head; 19769314Scharnier } 19869314Scharnier } 19969314Scharnier /* 20069829Scharnier * not found so add new user unless specified users only 20169829Scharnier */ 20269829Scharnier if (Flags & AC_U) 20369829Scharnier return head; 20469829Scharnier 20569829Scharnier if ((up = NEW(struct user_list)) == NULL) 20669829Scharnier errx(1, "malloc failed"); 20769829Scharnier up->next = head; 20869829Scharnier strlcpy(up->name, name, sizeof (up->name)); 20969829Scharnier up->secs = secs; 21069829Scharnier Total += secs; 21169829Scharnier return up; 21269829Scharnier} 21369829Scharnier 21484166Siedowse#ifdef DEBUG 21580277Skris/* 21669829Scharnier * Create a string which is the standard prefix for a debug line. It 21769829Scharnier * includes a timestamp (perhaps with year), device-name, and user-name. 21869829Scharnier */ 21969829Scharnierconst char * 22069829Scharnierdebug_pfx(const struct utmp *event_up, const struct utmp *userinf_up) 22184166Siedowse{ 22284166Siedowse static char str_result[40+UT_LINESIZE+UT_NAMESIZE]; 22369829Scharnier static char thisyear[5]; 22469829Scharnier size_t maxcopy; 22569829Scharnier time_t ut_timecopy; 22669829Scharnier 22769829Scharnier if (thisyear[0] == '\0') { 22869829Scharnier /* Figure out what "this year" is. */ 22969829Scharnier time(&ut_timecopy); 23069829Scharnier strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result)); 23169829Scharnier strlcpy(thisyear, &str_result[20], sizeof(thisyear)); 23269829Scharnier } 23369829Scharnier 23469829Scharnier if (event_up->ut_time == 0) 23569829Scharnier strlcpy(str_result, "*ZeroTime* --:--:-- ", sizeof(str_result)); 23669829Scharnier else { 23769829Scharnier /* 23869829Scharnier * The type of utmp.ut_time is not necessary type time_t, as 23969829Scharnier * it is explicitly defined as type int32_t. Copy the value 24069829Scharnier * for platforms where sizeof(time_t) != sizeof(int32_t). 24169829Scharnier */ 24269829Scharnier ut_timecopy = _time32_to_time(event_up->ut_time); 24369829Scharnier strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result)); 24469829Scharnier /* 24569829Scharnier * Include the year, if it is not the same year as "now". 24669829Scharnier */ 24769829Scharnier if (strncmp(&str_result[20], thisyear, 4) == 0) 24869829Scharnier str_result[20] = '\0'; 24969829Scharnier else { 25069829Scharnier str_result[24] = ' '; /* Replace a '\n' */ 25169829Scharnier str_result[25] = '\0'; 25269829Scharnier } 25369829Scharnier } 25469829Scharnier 25569829Scharnier if (userinf_up->ut_line[0] == '\0') 25669829Scharnier strlcat(str_result, "NoDev", sizeof(str_result)); 25769829Scharnier else { 25869829Scharnier /* ut_line is not necessarily null-terminated. */ 25969829Scharnier maxcopy = strlen(str_result) + UT_LINESIZE + 1; 26069829Scharnier if (maxcopy > sizeof(str_result)) 26169829Scharnier maxcopy = sizeof(str_result); 26275377Smckusick strlcat(str_result, userinf_up->ut_line, maxcopy); 26375377Smckusick } 26475377Smckusick strlcat(str_result, ": ", sizeof(str_result)); 26575377Smckusick 26675377Smckusick if (userinf_up->ut_name[0] == '\0') 26775377Smckusick strlcat(str_result, "LogOff", sizeof(str_result)); 26875377Smckusick else { 26975377Smckusick /* ut_name is not necessarily null-terminated. */ 27075377Smckusick maxcopy = strlen(str_result) + UT_NAMESIZE + 1; 27175377Smckusick if (maxcopy > sizeof(str_result)) 27275377Smckusick maxcopy = sizeof(str_result); 27369829Scharnier strlcat(str_result, userinf_up->ut_name, maxcopy); 27469829Scharnier } 27569829Scharnier 27669829Scharnier return (str_result); 27769829Scharnier} 27869829Scharnier#endif 27969829Scharnier 28069829Scharnierint 28169829Scharniermain(int argc, char *argv[]) 28269829Scharnier{ 28369829Scharnier FILE *fp; 28469829Scharnier int c; 28569829Scharnier 28669829Scharnier (void) setlocale(LC_TIME, ""); 28769829Scharnier 28869829Scharnier fp = NULL; 28969829Scharnier while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) { 29069829Scharnier switch (c) { 29175498Smckusick#ifdef DEBUG 29269829Scharnier case 'D': 29375498Smckusick Debug++; 29475498Smckusick break; 29575498Smckusick#endif 29669829Scharnier case 'c': 29769829Scharnier#ifdef CONSOLE_TTY 29869829Scharnier Console = optarg; 29969829Scharnier#else 30069829Scharnier usage(); /* XXX */ 30169829Scharnier#endif 30269829Scharnier break; 30369829Scharnier case 'd': 30469829Scharnier Flags |= AC_D; 30569829Scharnier break; 30669829Scharnier case 'p': 30769829Scharnier Flags |= AC_P; 30869829Scharnier break; 30969829Scharnier case 't': /* only do specified ttys */ 31069829Scharnier add_tty(optarg); 31169829Scharnier break; 31269829Scharnier case 'w': 31369829Scharnier fp = file(optarg); 31469829Scharnier break; 31569829Scharnier case '?': 31669829Scharnier default: 31769829Scharnier usage(); 31869829Scharnier break; 31969829Scharnier } 32069829Scharnier } 32169829Scharnier if (optind < argc) { 32269829Scharnier /* 32369829Scharnier * initialize user list 32469829Scharnier */ 32569829Scharnier for (; optind < argc; optind++) { 32669829Scharnier Users = update_user(Users, argv[optind], (time_t)0); 32769829Scharnier } 32875377Smckusick Flags |= AC_U; /* freeze user list */ 32975377Smckusick } 33075377Smckusick if (Flags & AC_D) 33175377Smckusick Flags &= ~AC_P; 33275377Smckusick if (fp == NULL) { 33375377Smckusick /* 33475377Smckusick * if _PATH_WTMP does not exist, exit quietly 33575377Smckusick */ 33675377Smckusick if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT) 33775377Smckusick return 0; 33875377Smckusick 33969829Scharnier fp = file(_PATH_WTMP); 34058047Ssheldonh } 34142873Sluoqi ac(fp); 34242873Sluoqi 34342873Sluoqi return 0; 34442873Sluoqi} 34542873Sluoqi 34642873Sluoqi/* 34742873Sluoqi * print login time in decimal hours 3481558Srgrimes */ 3491558Srgrimesvoid 3501558Srgrimesshow(const char *name, time_t secs) 3511558Srgrimes{ 3521558Srgrimes (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name, 3531558Srgrimes ((double)secs / 3600)); 35438040Scharnier} 35575377Smckusick 35675377Smckusickvoid 35775377Smckusickshow_users(struct user_list *list) 3581558Srgrimes{ 3591558Srgrimes struct user_list *lp; 3601558Srgrimes 3611558Srgrimes for (lp = list; lp; lp = lp->next) 3621558Srgrimes show(lp->name, lp->secs); 36379750Sdd} 36479750Sdd 3651558Srgrimes/* 3661558Srgrimes * print total login time for 24hr period in decimal hours 36758047Ssheldonh */ 3681558Srgrimesvoid 3691558Srgrimesshow_today(struct user_list *users, struct utmp_list *logins, time_t secs) 3701558Srgrimes{ 3711558Srgrimes struct user_list *up; 3721558Srgrimes struct utmp_list *lp; 37384166Siedowse char date[64]; 3741558Srgrimes time_t yesterday = secs - 1; 3751558Srgrimes static int d_first = -1; 3761558Srgrimes 3771558Srgrimes if (d_first < 0) 37858047Ssheldonh d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 37979750Sdd (void)strftime(date, sizeof (date), 38079750Sdd d_first ? "%e %b total" : "%b %e total", 38158047Ssheldonh localtime(&yesterday)); 38258047Ssheldonh 38358047Ssheldonh /* restore the missing second */ 38458047Ssheldonh yesterday++; 38558047Ssheldonh 38658047Ssheldonh for (lp = logins; lp != NULL; lp = lp->next) { 38758047Ssheldonh secs = yesterday - lp->usr.ut_time; 38858047Ssheldonh Users = update_user(Users, lp->usr.ut_name, secs); 38958047Ssheldonh lp->usr.ut_time = yesterday; /* as if they just logged in */ 39058047Ssheldonh } 39158047Ssheldonh secs = 0; 39258047Ssheldonh for (up = users; up != NULL; up = up->next) { 39358047Ssheldonh secs += up->secs; 39458047Ssheldonh up->secs = 0; /* for next day */ 39579750Sdd } 39658047Ssheldonh if (secs) 39758047Ssheldonh (void)printf("%s %11.2f\n", date, ((double)secs / 3600)); 39858047Ssheldonh} 39979750Sdd 40058047Ssheldonh/* 40158047Ssheldonh * log a user out and update their times. 40258047Ssheldonh * if ut_line is "~", we log all users out as the system has 40358047Ssheldonh * been shut down. 4049315Sjoerg */ 4059315Sjoergstruct utmp_list * 40634266Sjulianlog_out(struct utmp_list *head, struct utmp *up) 40734266Sjulian{ 4089315Sjoerg struct utmp_list *lp, *lp2, *tlp; 4099315Sjoerg time_t secs; 4109315Sjoerg 4119315Sjoerg for (lp = head, lp2 = NULL; lp != NULL; ) 4129315Sjoerg if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line, 4139315Sjoerg sizeof (up->ut_line)) == 0) { 41475377Smckusick secs = up->ut_time - lp->usr.ut_time; 41575377Smckusick Users = update_user(Users, lp->usr.ut_name, secs); 41675377Smckusick#ifdef DEBUG 41775377Smckusick if (Debug) 4189315Sjoerg printf("%s logged out (%2d:%02d:%02d)\n", 4199315Sjoerg debug_pfx(up, &lp->usr), (int)(secs / 3600), 4209315Sjoerg (int)((secs % 3600) / 60), 4219315Sjoerg (int)(secs % 60)); 4229315Sjoerg#endif 4239315Sjoerg /* 4249315Sjoerg * now lose it 4259315Sjoerg */ 4269315Sjoerg tlp = lp; 4279315Sjoerg lp = lp->next; 4289315Sjoerg if (tlp == head) 4299315Sjoerg head = lp; 4309315Sjoerg else if (lp2 != NULL) 4311558Srgrimes lp2->next = lp; 4321558Srgrimes free(tlp); 43379750Sdd } else { 4341558Srgrimes lp2 = lp; 4351558Srgrimes lp = lp->next; 4361558Srgrimes } 4371558Srgrimes return head; 4381558Srgrimes} 4391558Srgrimes 4401558Srgrimes 4411558Srgrimes/* 4421558Srgrimes * if do_tty says ok, login a user 4431558Srgrimes */ 4441558Srgrimesstruct utmp_list * 4451558Srgrimeslog_in(struct utmp_list *head, struct utmp *up) 4461558Srgrimes{ 4471558Srgrimes struct utmp_list *lp; 4481558Srgrimes 4491558Srgrimes /* 4501558Srgrimes * this could be a login. if we're not dealing with 4511558Srgrimes * the console name, say it is. 4521558Srgrimes * 4531558Srgrimes * If we are, and if ut_host==":0.0" we know that it 4541558Srgrimes * isn't a real login. _But_ if we have not yet recorded 4551558Srgrimes * someone being logged in on Console - due to the wtmp 4561558Srgrimes * file starting after they logged in, we'll pretend they 4571558Srgrimes * logged in, at the start of the wtmp file. 4581558Srgrimes */ 4591558Srgrimes 460#ifdef CONSOLE_TTY 461 if (up->ut_host[0] == ':') { 462 /* 463 * SunOS 4.0.2 does not treat ":0.0" as special but we 464 * do. 465 */ 466 if (on_console(head)) 467 return head; 468 /* 469 * ok, no recorded login, so they were here when wtmp 470 * started! Adjust ut_time! 471 */ 472 up->ut_time = FirstTime; 473 /* 474 * this allows us to pick the right logout 475 */ 476 strlcpy(up->ut_line, Console, sizeof (up->ut_line)); 477 } 478#endif 479 /* 480 * If we are doing specified ttys only, we ignore 481 * anything else. 482 */ 483 if (Flags & AC_T) 484 if (!do_tty(up->ut_line)) 485 return head; 486 487 /* 488 * go ahead and log them in 489 */ 490 if ((lp = NEW(struct utmp_list)) == NULL) 491 errx(1, "malloc failed"); 492 lp->next = head; 493 head = lp; 494 memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp)); 495#ifdef DEBUG 496 if (Debug) { 497 printf("%s logged in", debug_pfx(&lp->usr, up)); 498 if (*up->ut_host) 499 printf(" (%-.*s)", (int)sizeof(up->ut_host), 500 up->ut_host); 501 putchar('\n'); 502 } 503#endif 504 return head; 505} 506 507int 508ac(FILE *fp) 509{ 510 struct utmp_list *lp, *head = NULL; 511 struct utmp usr; 512 struct tm *ltm; 513 time_t secs; 514 int day = -1; 515 516 while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) { 517 if (!FirstTime) 518 FirstTime = usr.ut_time; 519 if (Flags & AC_D) { 520 time_t t = _int_to_time(usr.ut_time); 521 ltm = localtime(&t); 522 if (day >= 0 && day != ltm->tm_yday) { 523 day = ltm->tm_yday; 524 /* 525 * print yesterday's total 526 */ 527 secs = usr.ut_time; 528 secs -= ltm->tm_sec; 529 secs -= 60 * ltm->tm_min; 530 secs -= 3600 * ltm->tm_hour; 531 show_today(Users, head, secs); 532 } else 533 day = ltm->tm_yday; 534 } 535 switch(*usr.ut_line) { 536 case '|': 537 secs = usr.ut_time; 538 break; 539 case '{': 540 secs -= usr.ut_time; 541 /* 542 * adjust time for those logged in 543 */ 544 for (lp = head; lp != NULL; lp = lp->next) 545 lp->usr.ut_time -= secs; 546 break; 547 case '~': /* reboot or shutdown */ 548 head = log_out(head, &usr); 549 FirstTime = usr.ut_time; /* shouldn't be needed */ 550 break; 551 default: 552 /* 553 * if they came in on tty[p-sP-S]*, then it is only 554 * a login session if the ut_host field is non-empty 555 */ 556 if (*usr.ut_name) { 557 if (strncmp(usr.ut_line, "tty", 3) == 0 || 558 strchr("pqrsPQRS", usr.ut_line[3]) != 0 || 559 *usr.ut_host != '\0') 560 head = log_in(head, &usr); 561#ifdef DEBUG 562 else if (Debug > 1) 563 /* Things such as 'screen' sessions. */ 564 printf("%s - record ignored\n", 565 debug_pfx(&usr, &usr)); 566#endif 567 } else 568 head = log_out(head, &usr); 569 break; 570 } 571 } 572 (void)fclose(fp); 573 if (!(Flags & AC_W)) 574 usr.ut_time = time((time_t *)0); 575 (void)strcpy(usr.ut_line, "~"); 576 577 if (Flags & AC_D) { 578 time_t t = _int_to_time(usr.ut_time); 579 ltm = localtime(&t); 580 if (day >= 0 && day != ltm->tm_yday) { 581 /* 582 * print yesterday's total 583 */ 584 secs = usr.ut_time; 585 secs -= ltm->tm_sec; 586 secs -= 60 * ltm->tm_min; 587 secs -= 3600 * ltm->tm_hour; 588 show_today(Users, head, secs); 589 } 590 } 591 /* 592 * anyone still logged in gets time up to now 593 */ 594 head = log_out(head, &usr); 595 596 if (Flags & AC_D) 597 show_today(Users, head, time((time_t *)0)); 598 else { 599 if (Flags & AC_P) 600 show_users(Users); 601 show("total", Total); 602 } 603 return 0; 604} 605 606void 607usage(void) 608{ 609 (void)fprintf(stderr, 610#ifdef CONSOLE_TTY 611 "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n"); 612#else 613 "ac [-dp] [-t tty] [-w wtmp] [users ...]\n"); 614#endif 615 exit(1); 616} 617