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