ac.c revision 81156
12088Ssos/* 25536Ssos * Copyright (c) 1994 Christopher G. Demetriou. 32088Ssos * @(#)Copyright (c) 1994, Simon J. Gerraty. 42088Ssos * 52088Ssos * This is free software. It comes with NO WARRANTY. 62088Ssos * Permission to use, modify and distribute this source code 72088Ssos * is granted subject to the following conditions. 82088Ssos * 1/ that the above copyright notice and this notice 95994Ssos * are preserved in all copies and that due credit be given 105994Ssos * to the author. 112088Ssos * 2/ that any changes to this code are clearly commented 122088Ssos * as such so that the author does not get blamed for bugs 132088Ssos * other than his own. 142088Ssos */ 1597748Sschweikh 162088Ssos#ifndef lint 172088Ssosstatic const char rcsid[] = 182088Ssos "$FreeBSD: head/usr.sbin/ac/ac.c 81156 2001-08-05 09:41:20Z jon $"; 192088Ssos#endif /* not lint */ 202088Ssos 212088Ssos#include <sys/types.h> 222088Ssos#include <sys/file.h> 232088Ssos#include <sys/time.h> 242088Ssos#include <err.h> 252088Ssos#include <errno.h> 262088Ssos#include <langinfo.h> 272088Ssos#include <locale.h> 282088Ssos#include <pwd.h> 29114601Sobrien#include <stdio.h> 30114601Sobrien#include <stdlib.h> 3129603Scharnier#include <string.h> 322088Ssos#include <unistd.h> 3329603Scharnier#include <utmp.h> 342088Ssos 3529603Scharnier/* 363864Sswallace * this is for our list of currently logged in sessions 3729603Scharnier */ 3842505Syokotastruct utmp_list { 3966834Sphk struct utmp_list *next; 4066834Sphk struct utmp usr; 412088Ssos}; 422088Ssos 432088Ssos/* 4476643Simp * this is for our list of users that are accumulating time. 4590394Sru */ 4690394Srustruct user_list { 4776643Simp struct user_list *next; 4890394Sru char name[UT_NAMESIZE+1]; 4990394Sru time_t secs; 5090394Sru}; 5190394Sru 5290394Sru/* 5390394Sru * this is for chosing whether to ignore a login 5476643Simp */ 5576643Simpstruct tty_list { 5676643Simp struct tty_list *next; 5776643Simp char name[UT_LINESIZE+3]; 58196500Sed int len; 59196500Sed int ret; 602088Ssos}; 618857Srgrimes 622088Ssos/* 632088Ssos * globals - yes yuk 6438139Syokota */ 652088Ssos#ifdef CONSOLE_TTY 662088Ssosstatic char *Console = CONSOLE_TTY; 6732316Syokota#endif 6832316Syokotastatic time_t Total = 0; 6932316Syokotastatic time_t FirstTime = 0; 7032316Syokotastatic int Flags = 0; 7132316Syokotastatic struct user_list *Users = NULL; 7232316Syokotastatic struct tty_list *Ttys = NULL; 7332316Syokota 7432316Syokota#define NEW(type) (type *)malloc(sizeof (type)) 7532316Syokota 7632316Syokota#define AC_W 1 /* not _PATH_WTMP */ 7732316Syokota#define AC_D 2 /* daily totals (ignore -p) */ 7832316Syokota#define AC_P 4 /* per-user totals */ 795994Ssos#define AC_U 8 /* specified users only */ 805994Ssos#define AC_T 16 /* specified ttys only */ 815994Ssos 825994Ssos#ifdef DEBUG 835994Ssosstatic int Debug = 0; 845994Ssos#endif 855994Ssos 865994Ssosint main __P((int, char **)); 875994Ssosint ac __P((FILE *)); 885994Ssosstruct tty_list *add_tty __P((char *)); 895994Ssosint do_tty __P((char *)); 905994SsosFILE *file __P((const char *)); 919202Srgrimesstruct utmp_list *log_in __P((struct utmp_list *, struct utmp *)); 925994Ssosstruct utmp_list *log_out __P((struct utmp_list *, struct utmp *)); 935994Ssosint on_console __P((struct utmp_list *)); 945994Ssosvoid show __P((const char *, time_t)); 959202Srgrimesvoid show_today __P((struct user_list *, struct utmp_list *, 965994Ssos time_t)); 975994Ssosvoid show_users __P((struct user_list *)); 985994Ssosstruct user_list *update_user __P((struct user_list *, char *, time_t)); 995994Ssosvoid usage __P((void)); 1005994Ssos 1015994Ssos/* 1025994Ssos * open wtmp or die 1035994Ssos */ 1042088SsosFILE * 1052088Ssosfile(name) 10646761Syokota const char *name; 10746761Syokota{ 10846761Syokota FILE *fp; 10946761Syokota 11046761Syokota if ((fp = fopen(name, "r")) == NULL) 11146761Syokota err(1, "%s", name); 11246761Syokota /* in case we want to discriminate */ 1132088Ssos if (strcmp(_PATH_WTMP, name)) 1146046Ssos Flags |= AC_W; 1152088Ssos return fp; 11632316Syokota} 1172088Ssos 11899816Salfredstruct tty_list * 11999816Salfredadd_tty(name) 12099816Salfred char *name; 12199814Salfred{ 12299816Salfred struct tty_list *tp; 12399814Salfred register char *rcp; 12499816Salfred 12599816Salfred Flags |= AC_T; 12699816Salfred 12799816Salfred if ((tp = NEW(struct tty_list)) == NULL) 12899816Salfred errx(1, "malloc failed"); 12999816Salfred tp->len = 0; /* full match */ 13099816Salfred tp->ret = 1; /* do if match */ 13199816Salfred if (*name == '!') { /* don't do if match */ 13299816Salfred tp->ret = 0; 13399816Salfred name++; 13499816Salfred } 135162327Semax strlcpy(tp->name, name, sizeof (tp->name)); 13699816Salfred if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */ 13799816Salfred *rcp = '\0'; 13899816Salfred tp->len = strlen(tp->name); /* match len bytes only */ 13999816Salfred } 14099816Salfred tp->next = Ttys; 14199816Salfred Ttys = tp; 1422088Ssos return Ttys; 1432088Ssos} 1442088Ssos 1452088Ssos/* 1462088Ssos * should we process the named tty? 1472088Ssos */ 14829603Scharnierint 1492088Ssosdo_tty(name) 1502088Ssos char *name; 1512088Ssos{ 1522088Ssos struct tty_list *tp; 1532088Ssos int def_ret = 0; 1542088Ssos 1552088Ssos for (tp = Ttys; tp != NULL; tp = tp->next) { 1565536Ssos if (tp->ret == 0) /* specific don't */ 1575536Ssos def_ret = 1; /* default do */ 1585536Ssos if (tp->len != 0) { 1592088Ssos if (strncmp(name, tp->name, tp->len) == 0) 1602088Ssos return tp->ret; 16177394Ssobomax } else { 1622088Ssos if (strncmp(name, tp->name, sizeof (tp->name)) == 0) 1632088Ssos return tp->ret; 1642088Ssos } 1652088Ssos } 16677394Ssobomax return def_ret; 1672088Ssos} 1682088Ssos 1692088Ssos#ifdef CONSOLE_TTY 1702088Ssos/* 1712088Ssos * is someone logged in on Console? 1722088Ssos */ 1732088Ssosint 1742088Ssoson_console(head) 1752088Ssos struct utmp_list *head; 1762088Ssos{ 1772088Ssos struct utmp_list *up; 1782088Ssos 1792088Ssos for (up = head; up; up = up->next) { 1802088Ssos if (strncmp(up->usr.ut_line, Console, 18199816Salfred sizeof (up->usr.ut_line)) == 0) 1822088Ssos return 1; 18332316Syokota } 1842088Ssos return 0; 185196500Sed} 1862088Ssos#endif 187196500Sed 1882088Ssos/* 189196500Sed * update user's login time 1902088Ssos */ 191196500Sedstruct user_list * 1922088Ssosupdate_user(head, name, secs) 193196500Sed struct user_list *head; 1942088Ssos char *name; 195196500Sed time_t secs; 1962088Ssos{ 197196500Sed struct user_list *up; 1982088Ssos 199196500Sed for (up = head; up != NULL; up = up->next) { 2002088Ssos if (strncmp(up->name, name, UT_NAMESIZE) == 0) { 201196500Sed up->secs += secs; 2022088Ssos Total += secs; 203196500Sed return head; 20448105Syokota } 205196500Sed } 2062088Ssos /* 207196500Sed * not found so add new user unless specified users only 2082088Ssos */ 209196500Sed if (Flags & AC_U) 2102088Ssos return head; 211196500Sed 2122088Ssos if ((up = NEW(struct user_list)) == NULL) 213196500Sed errx(1, "malloc failed"); 2142088Ssos up->next = head; 215196500Sed strlcpy(up->name, name, sizeof (up->name)); 2162088Ssos up->secs = secs; 217196500Sed Total += secs; 2182088Ssos return up; 219196500Sed} 2205994Ssos 221196500Sedint 22238053Syokotamain(argc, argv) 223196500Sed int argc; 22454380Syokota char **argv; 225196500Sed{ 22654380Syokota FILE *fp; 227196500Sed int c; 22854380Syokota 229196500Sed (void) setlocale(LC_TIME, ""); 23054380Syokota 231196500Sed fp = NULL; 23254380Syokota while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) { 233196500Sed switch (c) { 23454380Syokota#ifdef DEBUG 235196500Sed case 'D': 23654380Syokota Debug++; 237196500Sed break; 23865759Sdwmalone#endif 239196500Sed case 'c': 24065759Sdwmalone#ifdef CONSOLE_TTY 241196500Sed Console = optarg; 24274118Sache#else 243196500Sed usage(); /* XXX */ 24432316Syokota#endif 24532316Syokota break; 24632316Syokota case 'd': 247196500Sed Flags |= AC_D; 2482088Ssos break; 2492088Ssos case 'p': 2502088Ssos Flags |= AC_P; 251196500Sed break; 2522088Ssos case 't': /* only do specified ttys */ 2532088Ssos add_tty(optarg); 2542088Ssos break; 255196500Sed case 'w': 2562088Ssos fp = file(optarg); 2572088Ssos break; 2582088Ssos case '?': 259197330Sed default: 2602088Ssos usage(); 2612088Ssos break; 2622088Ssos } 2632088Ssos } 2642088Ssos if (optind < argc) { 2652088Ssos /* 2662088Ssos * initialize user list 26777394Ssobomax */ 26832316Syokota for (; optind < argc; optind++) { 2692088Ssos Users = update_user(Users, argv[optind], 0L); 27032316Syokota } 2712088Ssos Flags |= AC_U; /* freeze user list */ 2722088Ssos } 2732088Ssos if (Flags & AC_D) 27432316Syokota Flags &= ~AC_P; 27532316Syokota if (fp == NULL) { 27632316Syokota /* 27732316Syokota * if _PATH_WTMP does not exist, exit quietly 27832316Syokota */ 27932316Syokota if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT) 28032316Syokota return 0; 28132316Syokota 28232316Syokota fp = file(_PATH_WTMP); 28332316Syokota } 28432316Syokota ac(fp); 28532316Syokota 28632316Syokota return 0; 28732316Syokota} 28832316Syokota 28932316Syokota/* 29032316Syokota * print login time in decimal hours 29132316Syokota */ 29232316Syokotavoid 2932088Ssosshow(name, secs) 29432316Syokota const char *name; 29532316Syokota time_t secs; 29632316Syokota{ 29732316Syokota (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name, 29832316Syokota ((double)secs / 3600)); 29932316Syokota} 30032316Syokota 30132316Syokotavoid 30232316Syokotashow_users(list) 30332316Syokota struct user_list *list; 30432316Syokota{ 30532316Syokota struct user_list *lp; 3062088Ssos 3072088Ssos for (lp = list; lp; lp = lp->next) 3082088Ssos show(lp->name, lp->secs); 3092088Ssos} 3102088Ssos 3112088Ssos/* 3122088Ssos * print total login time for 24hr period in decimal hours 3132088Ssos */ 3142088Ssosvoid 315196500Sedshow_today(users, logins, secs) 3162088Ssos struct user_list *users; 317196500Sed struct utmp_list *logins; 3182088Ssos time_t secs; 3192088Ssos{ 32032316Syokota struct user_list *up; 3212088Ssos struct utmp_list *lp; 3222088Ssos char date[64]; 32332316Syokota time_t yesterday = secs - 1; 32432316Syokota static int d_first = -1; 3252088Ssos 3262088Ssos if (d_first < 0) 32732316Syokota d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 32832316Syokota (void)strftime(date, sizeof (date), 32932316Syokota d_first ? "%e %b total" : "%b %e total", 33032316Syokota localtime(&yesterday)); 33132316Syokota 33232316Syokota /* restore the missing second */ 3332088Ssos yesterday++; 33432316Syokota 33532316Syokota for (lp = logins; lp != NULL; lp = lp->next) { 33632316Syokota secs = yesterday - lp->usr.ut_time; 33732316Syokota Users = update_user(Users, lp->usr.ut_name, secs); 33832316Syokota lp->usr.ut_time = yesterday; /* as if they just logged in */ 33932316Syokota } 34032316Syokota secs = 0; 34132316Syokota for (up = users; up != NULL; up = up->next) { 34232316Syokota secs += up->secs; 34332316Syokota up->secs = 0; /* for next day */ 34432316Syokota } 34532316Syokota if (secs) 34632316Syokota (void)printf("%s %11.2f\n", date, ((double)secs / 3600)); 34732316Syokota} 34832316Syokota 34932316Syokota/* 35032316Syokota * log a user out and update their times. 35132316Syokota * if ut_line is "~", we log all users out as the system has 35232316Syokota * been shut down. 35332316Syokota */ 35432316Syokotastruct utmp_list * 35532316Syokotalog_out(head, up) 35632316Syokota struct utmp_list *head; 35732316Syokota struct utmp *up; 35832316Syokota{ 35932316Syokota struct utmp_list *lp, *lp2, *tlp; 36032316Syokota time_t secs; 36132316Syokota 36232316Syokota for (lp = head, lp2 = NULL; lp != NULL; ) 36332316Syokota if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line, 36432316Syokota sizeof (up->ut_line)) == 0) { 36532316Syokota secs = up->ut_time - lp->usr.ut_time; 36632316Syokota Users = update_user(Users, lp->usr.ut_name, secs); 36732316Syokota#ifdef DEBUG 36832316Syokota if (Debug) 36932316Syokota printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n", 37032316Syokota 19, ctime(&up->ut_time), 37132316Syokota sizeof (lp->usr.ut_line), lp->usr.ut_line, 37232316Syokota sizeof (lp->usr.ut_name), lp->usr.ut_name, 37332316Syokota secs / 3600, (secs % 3600) / 60, secs % 60); 37432316Syokota#endif 37532316Syokota /* 37632316Syokota * now lose it 37732316Syokota */ 37832316Syokota tlp = lp; 37932316Syokota lp = lp->next; 38032316Syokota if (tlp == head) 38132316Syokota head = lp; 38232316Syokota else if (lp2 != NULL) 38332316Syokota lp2->next = lp; 38432316Syokota free(tlp); 38532316Syokota } else { 38632316Syokota lp2 = lp; 38732316Syokota lp = lp->next; 38829603Scharnier } 3892088Ssos return head; 3902088Ssos} 391196500Sed 3922088Ssos 3932088Ssos/* 394196500Sed * if do_tty says ok, login a user 3958857Srgrimes */ 3962088Ssosstruct utmp_list * 397196500Sedlog_in(head, up) 3982088Ssos struct utmp_list *head; 3992088Ssos struct utmp *up; 400196500Sed{ 4012088Ssos struct utmp_list *lp; 4022088Ssos 403196500Sed /* 4042088Ssos * this could be a login. if we're not dealing with 4052088Ssos * the console name, say it is. 406196500Sed * 4072088Ssos * If we are, and if ut_host==":0.0" we know that it 4082088Ssos * isn't a real login. _But_ if we have not yet recorded 409196500Sed * someone being logged in on Console - due to the wtmp 4102088Ssos * file starting after they logged in, we'll pretend they 4112088Ssos * logged in, at the start of the wtmp file. 412196500Sed */ 4132088Ssos 4142088Ssos#ifdef CONSOLE_TTY 415196500Sed if (up->ut_host[0] == ':') { 4162088Ssos /* 4172088Ssos * SunOS 4.0.2 does not treat ":0.0" as special but we 418196500Sed * do. 4192088Ssos */ 4202088Ssos if (on_console(head)) 421196500Sed return head; 4222088Ssos /* 4232088Ssos * ok, no recorded login, so they were here when wtmp 424196500Sed * started! Adjust ut_time! 42548105Syokota */ 42648105Syokota up->ut_time = FirstTime; 427196500Sed /* 4282088Ssos * this allows us to pick the right logout 4292088Ssos */ 430196500Sed strlcpy(up->ut_line, Console, sizeof (up->ut_line)); 4312088Ssos } 4322088Ssos#endif 433196500Sed /* 4342088Ssos * If we are doing specified ttys only, we ignore 4352088Ssos * anything else. 436196500Sed */ 4372088Ssos if (Flags & AC_T) 4382088Ssos if (!do_tty(up->ut_line)) 439196500Sed return head; 4402088Ssos 4412088Ssos /* 442196500Sed * go ahead and log them in 4432088Ssos */ 4442088Ssos if ((lp = NEW(struct utmp_list)) == NULL) 445196500Sed errx(1, "malloc failed"); 4462088Ssos lp->next = head; 4472088Ssos head = lp; 448196500Sed memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp)); 44932316Syokota#ifdef DEBUG 45032316Syokota if (Debug) { 451196500Sed printf("%-.*s %-.*s: %-.*s logged in", 19, 45238053Syokota ctime(&lp->usr.ut_time), sizeof (up->ut_line), 45338053Syokota up->ut_line, sizeof (up->ut_name), up->ut_name); 454196500Sed if (*up->ut_host) 45554380Syokota printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host); 45654380Syokota putchar('\n'); 457196500Sed } 45854380Syokota#endif 45954380Syokota return head; 460196500Sed} 46154380Syokota 46254380Syokotaint 463196500Sedac(fp) 46454380Syokota FILE *fp; 46554380Syokota{ 466196500Sed struct utmp_list *lp, *head = NULL; 46754380Syokota struct utmp usr; 46854380Syokota struct tm *ltm; 469196500Sed time_t secs; 47054380Syokota int day = -1; 47154380Syokota 472196500Sed while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) { 47354380Syokota if (!FirstTime) 47454380Syokota FirstTime = usr.ut_time; 475196500Sed if (Flags & AC_D) { 47665759Sdwmalone ltm = localtime(&usr.ut_time); 47765759Sdwmalone if (day >= 0 && day != ltm->tm_yday) { 478196500Sed day = ltm->tm_yday; 47965759Sdwmalone /* 48065759Sdwmalone * print yesterday's total 481196500Sed */ 48274118Sache secs = usr.ut_time; 48374118Sache secs -= ltm->tm_sec; 4842088Ssos secs -= 60 * ltm->tm_min; 485196500Sed secs -= 3600 * ltm->tm_hour; 4868857Srgrimes show_today(Users, head, secs); 4872088Ssos } else 4888857Srgrimes day = ltm->tm_yday; 4892088Ssos } 49032316Syokota switch(*usr.ut_line) { 49132316Syokota case '|': 4922088Ssos secs = usr.ut_time; 4938857Srgrimes break; 4942088Ssos case '{': 49532316Syokota secs -= usr.ut_time; 4962088Ssos /* 4972088Ssos * adjust time for those logged in 4982088Ssos */ 4998857Srgrimes for (lp = head; lp != NULL; lp = lp->next) 5002088Ssos lp->usr.ut_time -= secs; 5018857Srgrimes break; 5029202Srgrimes case '~': /* reboot or shutdown */ 5038857Srgrimes head = log_out(head, &usr); 5042088Ssos FirstTime = usr.ut_time; /* shouldn't be needed */ 5058857Srgrimes break; 5062088Ssos default: 5078857Srgrimes /* 5082088Ssos * if they came in on tty[p-sP-S]*, then it is only 5092088Ssos * a login session if the ut_host field is non-empty 5102088Ssos */ 5112088Ssos if (*usr.ut_name) { 5122088Ssos if (strncmp(usr.ut_line, "tty", 3) == 0 || 51342505Syokota strchr("pqrsPQRS", usr.ut_line[3]) != 0 || 5142088Ssos *usr.ut_host != '\0') 51529603Scharnier head = log_in(head, &usr); 5162088Ssos } else 5172088Ssos head = log_out(head, &usr); 5182088Ssos break; 5192088Ssos } 5202088Ssos } 5212088Ssos (void)fclose(fp); 5222088Ssos if (!(Flags & AC_W)) 5232088Ssos usr.ut_time = time((time_t *)0); 5242088Ssos (void)strcpy(usr.ut_line, "~"); 5252088Ssos 526196500Sed if (Flags & AC_D) { 5272088Ssos ltm = localtime(&usr.ut_time); 5288857Srgrimes if (day >= 0 && day != ltm->tm_yday) { 5292088Ssos /* 5302088Ssos * print yesterday's total 5312088Ssos */ 5322088Ssos secs = usr.ut_time; 5332088Ssos secs -= ltm->tm_sec; 5342088Ssos secs -= 60 * ltm->tm_min; 5352088Ssos secs -= 3600 * ltm->tm_hour; 5362088Ssos show_today(Users, head, secs); 5372088Ssos } 5382088Ssos } 5392088Ssos /* 5402088Ssos * anyone still logged in gets time up to now 5412088Ssos */ 5426046Ssos head = log_out(head, &usr); 5436046Ssos 5446046Ssos if (Flags & AC_D) 5458857Srgrimes show_today(Users, head, time((time_t *)0)); 5462088Ssos else { 5472088Ssos if (Flags & AC_P) 54832316Syokota show_users(Users); 54932316Syokota show("total", Total); 55032316Syokota } 55132316Syokota return 0; 55232316Syokota} 5532088Ssos 55432316Syokotavoid 55532316Syokotausage() 55632316Syokota{ 55732316Syokota (void)fprintf(stderr, 55832316Syokota#ifdef CONSOLE_TTY 55932316Syokota "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n"); 56032316Syokota#else 56132316Syokota "ac [-dp] [-t tty] [-w wtmp] [users ...]\n"); 56232316Syokota#endif 56332316Syokota exit(1); 56432316Syokota} 56532316Syokota