ac.c revision 48378
1/* 2 * Copyright (c) 1994 Christopher G. Demetriou. 3 * @(#)Copyright (c) 1994, Simon J. Gerraty. 4 * 5 * This is free software. It comes with NO WARRANTY. 6 * Permission to use, modify and distribute this source code 7 * is granted subject to the following conditions. 8 * 1/ that the above copyright notice and this notice 9 * are preserved in all copies and that due credit be given 10 * to the author. 11 * 2/ that any changes to this code are clearly commented 12 * as such so that the author does not get blamed for bugs 13 * other than his own. 14 */ 15 16#ifndef lint 17static const char rcsid[] = 18 "$Id: ac.c,v 1.10 1998/07/02 05:34:08 phk Exp $"; 19#endif /* not lint */ 20 21#include <sys/types.h> 22#include <sys/file.h> 23#include <sys/time.h> 24#include <err.h> 25#include <errno.h> 26#include <locale.h> 27#include <pwd.h> 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <unistd.h> 32#include <utmp.h> 33 34/* 35 * this is for our list of currently logged in sessions 36 */ 37struct utmp_list { 38 struct utmp_list *next; 39 struct utmp usr; 40}; 41 42/* 43 * this is for our list of users that are accumulating time. 44 */ 45struct user_list { 46 struct user_list *next; 47 char name[UT_NAMESIZE+1]; 48 time_t secs; 49}; 50 51/* 52 * this is for chosing whether to ignore a login 53 */ 54struct tty_list { 55 struct tty_list *next; 56 char name[UT_LINESIZE+3]; 57 int len; 58 int ret; 59}; 60 61/* 62 * globals - yes yuk 63 */ 64#ifdef CONSOLE_TTY 65static char *Console = CONSOLE_TTY; 66#endif 67static time_t Total = 0; 68static time_t FirstTime = 0; 69static int Flags = 0; 70static struct user_list *Users = NULL; 71static struct tty_list *Ttys = NULL; 72 73#define NEW(type) (type *)malloc(sizeof (type)) 74 75#define AC_W 1 /* not _PATH_WTMP */ 76#define AC_D 2 /* daily totals (ignore -p) */ 77#define AC_P 4 /* per-user totals */ 78#define AC_U 8 /* specified users only */ 79#define AC_T 16 /* specified ttys only */ 80 81#ifdef DEBUG 82static int Debug = 0; 83#endif 84 85int main __P((int, char **)); 86int ac __P((FILE *)); 87struct tty_list *add_tty __P((char *)); 88int do_tty __P((char *)); 89FILE *file __P((char *)); 90struct utmp_list *log_in __P((struct utmp_list *, struct utmp *)); 91struct utmp_list *log_out __P((struct utmp_list *, struct utmp *)); 92int on_console __P((struct utmp_list *)); 93void show __P((char *, time_t)); 94void show_today __P((struct user_list *, struct utmp_list *, 95 time_t)); 96void show_users __P((struct user_list *)); 97struct user_list *update_user __P((struct user_list *, char *, time_t)); 98void usage __P((void)); 99 100/* 101 * open wtmp or die 102 */ 103FILE * 104file(name) 105 char *name; 106{ 107 FILE *fp; 108 109 /* 110 * Added by GAW... 111 */ 112 if (strcmp("-", name) == 0) { 113 Flags |= AC_W; 114 return (stdin); 115 } else { 116 if ((fp = fopen(name, "r")) == NULL) 117 err(1, "%s", name); 118 /* in case we want to discriminate */ 119 if (strcmp(_PATH_WTMP, name)) 120 Flags |= AC_W; 121 return fp; 122 } 123} 124 125struct tty_list * 126add_tty(name) 127 char *name; 128{ 129 struct tty_list *tp; 130 register char *rcp; 131 132 Flags |= AC_T; 133 134 if ((tp = NEW(struct tty_list)) == NULL) 135 err(1, "malloc"); 136 tp->len = 0; /* full match */ 137 tp->ret = 1; /* do if match */ 138 if (*name == '!') { /* don't do if match */ 139 tp->ret = 0; 140 name++; 141 } 142 (void)strncpy(tp->name, name, sizeof (tp->name) - 1); 143 tp->name[sizeof (tp->name) - 1] = '\0'; 144 if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */ 145 *rcp = '\0'; 146 tp->len = strlen(tp->name); /* match len bytes only */ 147 } 148 tp->next = Ttys; 149 Ttys = tp; 150 return Ttys; 151} 152 153/* 154 * should we process the named tty? 155 */ 156int 157do_tty(name) 158 char *name; 159{ 160 struct tty_list *tp; 161 int def_ret = 0; 162 163 for (tp = Ttys; tp != NULL; tp = tp->next) { 164 if (tp->ret == 0) /* specific don't */ 165 def_ret = 1; /* default do */ 166 if (tp->len != 0) { 167 if (strncmp(name, tp->name, tp->len) == 0) 168 return tp->ret; 169 } else { 170 if (strncmp(name, tp->name, sizeof (tp->name)) == 0) 171 return tp->ret; 172 } 173 } 174 return def_ret; 175} 176 177#ifdef CONSOLE_TTY 178/* 179 * is someone logged in on Console? 180 */ 181int 182on_console(head) 183 struct utmp_list *head; 184{ 185 struct utmp_list *up; 186 187 for (up = head; up; up = up->next) { 188 if (strncmp(up->usr.ut_line, Console, 189 sizeof (up->usr.ut_line)) == 0) 190 return 1; 191 } 192 return 0; 193} 194#endif 195 196/* 197 * update user's login time 198 */ 199struct user_list * 200update_user(head, name, secs) 201 struct user_list *head; 202 char *name; 203 time_t secs; 204{ 205 struct user_list *up; 206 207 for (up = head; up != NULL; up = up->next) { 208 if (strncmp(up->name, name, UT_NAMESIZE) == 0) { 209 up->secs += secs; 210 Total += secs; 211 return head; 212 } 213 } 214 /* 215 * not found so add new user unless specified users only 216 */ 217 if (Flags & AC_U) 218 return head; 219 220 if ((up = NEW(struct user_list)) == NULL) 221 err(1, "malloc"); 222 up->next = head; 223 (void)strncpy(up->name, name, sizeof (up->name) - 1); 224 up->name[sizeof (up->name) - 1] = '\0'; /* paranoid! */ 225 up->secs = secs; 226 Total += secs; 227 return up; 228} 229 230int 231main(argc, argv) 232 int argc; 233 char **argv; 234{ 235 FILE *fp; 236 int c; 237 238 (void) setlocale(LC_TIME, ""); 239 240 fp = NULL; 241 while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) { 242 switch (c) { 243#ifdef DEBUG 244 case 'D': 245 Debug++; 246 break; 247#endif 248 case 'c': 249#ifdef CONSOLE_TTY 250 Console = optarg; 251#else 252 usage(); /* XXX */ 253#endif 254 break; 255 case 'd': 256 Flags |= AC_D; 257 break; 258 case 'p': 259 Flags |= AC_P; 260 break; 261 case 't': /* only do specified ttys */ 262 add_tty(optarg); 263 break; 264 case 'w': 265 fp = file(optarg); 266 break; 267 case '?': 268 default: 269 usage(); 270 break; 271 } 272 } 273 if (optind < argc) { 274 /* 275 * initialize user list 276 */ 277 for (; optind < argc; optind++) { 278 Users = update_user(Users, argv[optind], 0L); 279 } 280 Flags |= AC_U; /* freeze user list */ 281 } 282 if (Flags & AC_D) 283 Flags &= ~AC_P; 284 if (fp == NULL) { 285 /* 286 * if _PATH_WTMP does not exist, exit quietly 287 */ 288 if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT) 289 return 0; 290 291 fp = file(_PATH_WTMP); 292 } 293 ac(fp); 294 295 return 0; 296} 297 298/* 299 * print login time in decimal hours 300 */ 301void 302show(name, secs) 303 char *name; 304 time_t secs; 305{ 306 (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name, 307 ((double)secs / 3600)); 308} 309 310void 311show_users(list) 312 struct user_list *list; 313{ 314 struct user_list *lp; 315 316 for (lp = list; lp; lp = lp->next) 317 show(lp->name, lp->secs); 318} 319 320/* 321 * print total login time for 24hr period in decimal hours 322 */ 323void 324show_today(users, logins, secs) 325 struct user_list *users; 326 struct utmp_list *logins; 327 time_t secs; 328{ 329 struct user_list *up; 330 struct utmp_list *lp; 331 char date[64]; 332 time_t yesterday = secs - 1; 333 334 (void)strftime(date, sizeof (date), "%b %e total", 335 localtime(&yesterday)); 336 337 /* restore the missing second */ 338 yesterday++; 339 340 for (lp = logins; lp != NULL; lp = lp->next) { 341 secs = yesterday - lp->usr.ut_time; 342 Users = update_user(Users, lp->usr.ut_name, secs); 343 lp->usr.ut_time = yesterday; /* as if they just logged in */ 344 } 345 secs = 0; 346 for (up = users; up != NULL; up = up->next) { 347 secs += up->secs; 348 up->secs = 0; /* for next day */ 349 } 350 if (secs) 351 (void)printf("%s %11.2f\n", date, ((double)secs / 3600)); 352} 353 354/* 355 * log a user out and update their times. 356 * if ut_line is "~", we log all users out as the system has 357 * been shut down. 358 */ 359struct utmp_list * 360log_out(head, up) 361 struct utmp_list *head; 362 struct utmp *up; 363{ 364 struct utmp_list *lp, *lp2, *tlp; 365 time_t secs; 366 367 for (lp = head, lp2 = NULL; lp != NULL; ) 368 if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line, 369 sizeof (up->ut_line)) == 0) { 370 secs = up->ut_time - lp->usr.ut_time; 371 Users = update_user(Users, lp->usr.ut_name, secs); 372#ifdef DEBUG 373 if (Debug) 374 printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n", 375 19, ctime(&up->ut_time), 376 sizeof (lp->usr.ut_line), lp->usr.ut_line, 377 sizeof (lp->usr.ut_name), lp->usr.ut_name, 378 secs / 3600, (secs % 3600) / 60, secs % 60); 379#endif 380 /* 381 * now lose it 382 */ 383 tlp = lp; 384 lp = lp->next; 385 if (tlp == head) 386 head = lp; 387 else if (lp2 != NULL) 388 lp2->next = lp; 389 free(tlp); 390 } else { 391 lp2 = lp; 392 lp = lp->next; 393 } 394 return head; 395} 396 397 398/* 399 * if do_tty says ok, login a user 400 */ 401struct utmp_list * 402log_in(head, up) 403 struct utmp_list *head; 404 struct utmp *up; 405{ 406 struct utmp_list *lp; 407 408 /* 409 * this could be a login. if we're not dealing with 410 * the console name, say it is. 411 * 412 * If we are, and if ut_host==":0.0" we know that it 413 * isn't a real login. _But_ if we have not yet recorded 414 * someone being logged in on Console - due to the wtmp 415 * file starting after they logged in, we'll pretend they 416 * logged in, at the start of the wtmp file. 417 */ 418 419#ifdef CONSOLE_TTY 420 if (up->ut_host[0] == ':') { 421 /* 422 * SunOS 4.0.2 does not treat ":0.0" as special but we 423 * do. 424 */ 425 if (on_console(head)) 426 return head; 427 /* 428 * ok, no recorded login, so they were here when wtmp 429 * started! Adjust ut_time! 430 */ 431 up->ut_time = FirstTime; 432 /* 433 * this allows us to pick the right logout 434 */ 435 (void)strncpy(up->ut_line, Console, sizeof (up->ut_line) - 1); 436 up->ut_line[sizeof (up->ut_line) - 1] = '\0'; /* paranoid! */ 437 } 438#endif 439 /* 440 * If we are doing specified ttys only, we ignore 441 * anything else. 442 */ 443 if (Flags & AC_T) 444 if (!do_tty(up->ut_line)) 445 return head; 446 447 /* 448 * go ahead and log them in 449 */ 450 if ((lp = NEW(struct utmp_list)) == NULL) 451 err(1, "malloc"); 452 lp->next = head; 453 head = lp; 454 memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp)); 455#ifdef DEBUG 456 if (Debug) { 457 printf("%-.*s %-.*s: %-.*s logged in", 19, 458 ctime(&lp->usr.ut_time), sizeof (up->ut_line), 459 up->ut_line, sizeof (up->ut_name), up->ut_name); 460 if (*up->ut_host) 461 printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host); 462 putchar('\n'); 463 } 464#endif 465 return head; 466} 467 468int 469ac(fp) 470 FILE *fp; 471{ 472 struct utmp_list *lp, *head = NULL; 473 struct utmp usr; 474 struct tm *ltm; 475 time_t secs; 476 int day = -1; 477 478 while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) { 479 if (!FirstTime) 480 FirstTime = usr.ut_time; 481 if (Flags & AC_D) { 482 ltm = localtime(&usr.ut_time); 483 if (day >= 0 && day != ltm->tm_yday) { 484 day = ltm->tm_yday; 485 /* 486 * print yesterday's total 487 */ 488 secs = usr.ut_time; 489 secs -= ltm->tm_sec; 490 secs -= 60 * ltm->tm_min; 491 secs -= 3600 * ltm->tm_hour; 492 show_today(Users, head, secs); 493 } else 494 day = ltm->tm_yday; 495 } 496 switch(*usr.ut_line) { 497 case '|': 498 secs = usr.ut_time; 499 break; 500 case '{': 501 secs -= usr.ut_time; 502 /* 503 * adjust time for those logged in 504 */ 505 for (lp = head; lp != NULL; lp = lp->next) 506 lp->usr.ut_time -= secs; 507 break; 508 case '~': /* reboot or shutdown */ 509 head = log_out(head, &usr); 510 FirstTime = usr.ut_time; /* shouldn't be needed */ 511 break; 512 default: 513 /* 514 * if they came in on tty[p-sP-S]*, then it is only 515 * a login session if the ut_host field is non-empty 516 */ 517 if (*usr.ut_name) { 518 if (strncmp(usr.ut_line, "tty", 3) != 0 || 519 strchr("pqrsPQRS", usr.ut_line[3]) == 0 || 520 *usr.ut_host != '\0') 521 head = log_in(head, &usr); 522 } else 523 head = log_out(head, &usr); 524 break; 525 } 526 } 527 (void)fclose(fp); 528 if (!(Flags & AC_W)) 529 usr.ut_time = time((time_t *)0); 530 (void)strcpy(usr.ut_line, "~"); 531 532 if (Flags & AC_D) { 533 ltm = localtime(&usr.ut_time); 534 if (day >= 0 && day != ltm->tm_yday) { 535 /* 536 * print yesterday's total 537 */ 538 secs = usr.ut_time; 539 secs -= ltm->tm_sec; 540 secs -= 60 * ltm->tm_min; 541 secs -= 3600 * ltm->tm_hour; 542 show_today(Users, head, secs); 543 } 544 } 545 /* 546 * anyone still logged in gets time up to now 547 */ 548 head = log_out(head, &usr); 549 550 if (Flags & AC_D) 551 show_today(Users, head, time((time_t *)0)); 552 else { 553 if (Flags & AC_P) 554 show_users(Users); 555 show("total", Total); 556 } 557 return 0; 558} 559 560void 561usage() 562{ 563 (void)fprintf(stderr, 564#ifdef CONSOLE_TTY 565 "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n"); 566#else 567 "ac [-dp] [-t tty] [-w wtmp] [users ...]\n"); 568#endif 569 exit(1); 570} 571