No empty .Rs/.Re
[netbsd-mini2440.git] / usr.sbin / ac / ac.c
blobdf5bbde8752b69af7ee452020bc569951186f821
1 /* $NetBSD: ac.c,v 1.22 2006/05/23 01:16:33 christos Exp $ */
3 /*
4 * Copyright (c) 1994 Christopher G. Demetriou
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the
18 * NetBSD Project. See http://www.NetBSD.org/ for
19 * information about NetBSD.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
37 * @(#)Copyright (c) 1994, Simon J. Gerraty.
39 * This is free software. It comes with NO WARRANTY.
40 * Permission to use, modify and distribute this source code
41 * is granted subject to the following conditions.
42 * 1/ that the above copyright notice and this notice
43 * are preserved in all copies and that due credit be given
44 * to the author.
45 * 2/ that any changes to this code are clearly commented
46 * as such so that the author does not get blamed for bugs
47 * other than his own.
50 #include <sys/cdefs.h>
51 #ifndef lint
52 __RCSID("$NetBSD: ac.c,v 1.22 2006/05/23 01:16:33 christos Exp $");
53 #endif
55 #include <sys/types.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <pwd.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <time.h>
65 #include <utmp.h>
66 #include <ttyent.h>
69 * this is for our list of currently logged in sessions
71 struct utmp_list {
72 struct utmp_list *next;
73 struct utmp usr;
77 * this is for our list of users that are accumulating time.
79 struct user_list {
80 struct user_list *next;
81 char name[UT_NAMESIZE+1];
82 time_t secs;
86 * this is for chosing whether to ignore a login
88 struct tty_list {
89 struct tty_list *next;
90 char name[UT_LINESIZE+3];
91 int len;
92 int ret;
96 * globals - yes yuk
98 static time_t Total = 0;
99 static time_t FirstTime = 0;
100 static int Flags = 0;
101 static struct user_list *Users = NULL;
102 static struct tty_list *Ttys = NULL;
103 static int Maxcon = 0, Ncon = 0;
104 static char (*Con)[UT_LINESIZE] = NULL;
106 #define NEW(type) (type *)malloc(sizeof (type))
108 #define is_login_tty(line) \
109 (bsearch(line, Con, Ncon, sizeof(Con[0]), compare) != NULL)
111 #define AC_W 1 /* not _PATH_WTMP */
112 #define AC_D 2 /* daily totals (ignore -p) */
113 #define AC_P 4 /* per-user totals */
114 #define AC_U 8 /* specified users only */
115 #define AC_T 16 /* specified ttys only */
117 #ifdef DEBUG
118 static int Debug = 0;
119 #endif
121 static int ac(FILE *);
122 static struct tty_list *add_tty(char *);
123 static int do_tty(char *);
124 static FILE *file(const char *);
125 static struct utmp_list *log_in(struct utmp_list *, struct utmp *);
126 static struct utmp_list *log_out(struct utmp_list *, struct utmp *);
127 #ifdef notdef
128 static int on_console(struct utmp_list *);
129 #endif
130 static void find_login_ttys(void);
131 static void show(const char *, time_t);
132 static void show_today(struct user_list *, struct utmp_list *,
133 time_t);
134 static void show_users(struct user_list *);
135 static struct user_list *update_user(struct user_list *, char *, time_t);
136 static int compare(const void *, const void *);
137 static void usage(void);
140 * open wtmp or die
142 static FILE *
143 file(const char *name)
145 FILE *fp;
147 if (strcmp(name, "-") == 0)
148 fp = stdin;
149 else if ((fp = fopen(name, "r")) == NULL)
150 err(1, "%s", name);
151 /* in case we want to discriminate */
152 if (strcmp(_PATH_WTMP, name))
153 Flags |= AC_W;
154 return fp;
157 static struct tty_list *
158 add_tty(char *name)
160 struct tty_list *tp;
161 char *rcp;
163 Flags |= AC_T;
165 if ((tp = NEW(struct tty_list)) == NULL)
166 err(1, "malloc");
167 tp->len = 0; /* full match */
168 tp->ret = 1; /* do if match */
169 if (*name == '!') { /* don't do if match */
170 tp->ret = 0;
171 name++;
173 (void)strlcpy(tp->name, name, sizeof (tp->name));
174 if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */
175 *rcp = '\0';
176 tp->len = strlen(tp->name); /* match len bytes only */
178 tp->next = Ttys;
179 Ttys = tp;
180 return Ttys;
184 * should we process the named tty?
186 static int
187 do_tty(char *name)
189 struct tty_list *tp;
190 int def_ret = 0;
192 for (tp = Ttys; tp != NULL; tp = tp->next) {
193 if (tp->ret == 0) /* specific don't */
194 def_ret = 1; /* default do */
195 if (tp->len != 0) {
196 if (strncmp(name, tp->name, tp->len) == 0)
197 return tp->ret;
198 } else {
199 if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
200 return tp->ret;
203 return def_ret;
206 static int
207 compare(const void *a, const void *b)
209 return strncmp(a, b, UT_LINESIZE);
213 * Deal correctly with multiple virtual consoles/login ttys.
214 * We read the ttyent's from /etc/ttys and classify as login
215 * ttys ones that are running getty and they are turned on.
217 static void
218 find_login_ttys(void)
220 struct ttyent *tty;
221 char (*nCon)[UT_LINESIZE];
223 if ((Con = malloc((Maxcon = 10) * sizeof(Con[0]))) == NULL)
224 err(1, "malloc");
226 setttyent();
227 while ((tty = getttyent()) != NULL)
228 if ((tty->ty_status & TTY_ON) != 0 &&
229 strstr(tty->ty_getty, "getty") != NULL) {
230 if (Ncon == Maxcon) {
231 if ((nCon = realloc(Con, (Maxcon + 10) *
232 sizeof(Con[0]))) == NULL)
233 err(1, "malloc");
234 Con = nCon;
235 Maxcon += 10;
237 (void)strncpy(Con[Ncon++], tty->ty_name, UT_LINESIZE);
239 endttyent();
240 qsort(Con, Ncon, sizeof(Con[0]), compare);
243 #ifdef notdef
245 * is someone logged in on Console/login tty?
247 static int
248 on_console(struct utmp_list *head)
250 struct utmp_list *up;
252 for (up = head; up; up = up->next)
253 if (is_login_tty(up->usr.ut_line))
254 return 1;
256 return 0;
258 #endif
261 * update user's login time
263 static struct user_list *
264 update_user(struct user_list *head, char *name, time_t secs)
266 struct user_list *up;
268 for (up = head; up != NULL; up = up->next) {
269 if (strncmp(up->name, name, sizeof (up->name) - 1) == 0) {
270 up->secs += secs;
271 Total += secs;
272 return head;
276 * not found so add new user unless specified users only
278 if (Flags & AC_U)
279 return head;
281 if ((up = NEW(struct user_list)) == NULL)
282 err(1, "malloc");
283 up->next = head;
284 (void)strlcpy(up->name, name, sizeof (up->name));
285 up->secs = secs;
286 Total += secs;
287 return up;
291 main(int argc, char **argv)
293 FILE *fp;
294 int c;
296 fp = NULL;
297 while ((c = getopt(argc, argv, "Ddpt:w:")) != -1) {
298 switch (c) {
299 #ifdef DEBUG
300 case 'D':
301 Debug++;
302 break;
303 #endif
304 case 'd':
305 Flags |= AC_D;
306 break;
307 case 'p':
308 Flags |= AC_P;
309 break;
310 case 't': /* only do specified ttys */
311 add_tty(optarg);
312 break;
313 case 'w':
314 fp = file(optarg);
315 break;
316 case '?':
317 default:
318 usage();
322 find_login_ttys();
324 if (optind < argc) {
326 * initialize user list
328 for (; optind < argc; optind++) {
329 Users = update_user(Users, argv[optind], 0L);
331 Flags |= AC_U; /* freeze user list */
333 if (Flags & AC_D)
334 Flags &= ~AC_P;
335 if (fp == NULL) {
337 * if _PATH_WTMP does not exist, exit quietly
339 if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT)
340 return 0;
342 fp = file(_PATH_WTMP);
344 ac(fp);
346 return 0;
350 * print login time in decimal hours
352 static void
353 show(const char *name, time_t secs)
355 (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
356 ((double)secs / 3600));
359 static void
360 show_users(struct user_list *list)
362 struct user_list *lp;
364 for (lp = list; lp; lp = lp->next)
365 show(lp->name, lp->secs);
369 * print total login time for 24hr period in decimal hours
371 static void
372 show_today(struct user_list *users, struct utmp_list *logins, time_t secs)
374 struct user_list *up;
375 struct utmp_list *lp;
376 char date[64];
377 time_t yesterday = secs - 1;
379 (void)strftime(date, sizeof (date), "%b %e total",
380 localtime(&yesterday));
382 /* restore the missing second */
383 yesterday++;
385 for (lp = logins; lp != NULL; lp = lp->next) {
386 secs = yesterday - lp->usr.ut_time;
387 Users = update_user(Users, lp->usr.ut_name, secs);
388 lp->usr.ut_time = yesterday; /* as if they just logged in */
390 secs = 0;
391 for (up = users; up != NULL; up = up->next) {
392 secs += up->secs;
393 up->secs = 0; /* for next day */
395 if (secs)
396 (void)printf("%s %11.2f\n", date, ((double)secs / 3600));
400 * log a user out and update their times.
401 * if ut_line is "~", we log all users out as the system has
402 * been shut down.
404 static struct utmp_list *
405 log_out(struct utmp_list *head, struct utmp *up)
407 struct utmp_list *lp, *lp2, *tlp;
408 time_t secs;
410 for (lp = head, lp2 = NULL; lp != NULL; )
411 if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line,
412 sizeof (up->ut_line)) == 0) {
413 secs = up->ut_time - lp->usr.ut_time;
414 Users = update_user(Users, lp->usr.ut_name, secs);
415 #ifdef DEBUG
416 if (Debug)
417 printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
418 19, ctime(&up->ut_time),
419 sizeof (lp->usr.ut_line), lp->usr.ut_line,
420 sizeof (lp->usr.ut_name), lp->usr.ut_name,
421 secs / 3600, (secs % 3600) / 60, secs % 60);
422 #endif
424 * now lose it
426 tlp = lp;
427 lp = lp->next;
428 if (tlp == head) {
429 head = lp;
430 } else if (lp2 != NULL)
431 lp2->next = lp;
432 free(tlp);
433 } else {
434 lp2 = lp;
435 lp = lp->next;
437 return head;
442 * if do_tty says ok, login a user
444 struct utmp_list *
445 log_in(struct utmp_list *head, struct utmp *up)
447 struct utmp_list *lp;
450 * If we are doing specified ttys only, we ignore
451 * anything else.
453 if (Flags & AC_T)
454 if (!do_tty(up->ut_line))
455 return head;
458 * go ahead and log them in
460 if ((lp = NEW(struct utmp_list)) == NULL)
461 err(1, "malloc");
462 lp->next = head;
463 head = lp;
464 memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
465 #ifdef DEBUG
466 if (Debug) {
467 printf("%-.*s %-.*s: %-.*s logged in", 19,
468 ctime(&lp->usr.ut_time), sizeof (up->ut_line),
469 up->ut_line, sizeof (up->ut_name), up->ut_name);
470 if (*up->ut_host)
471 printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host);
472 putchar('\n');
474 #endif
475 return head;
478 static int
479 ac(FILE *fp)
481 struct utmp_list *lp, *head = NULL;
482 struct utmp usr;
483 struct tm *ltm;
484 time_t secs = 0;
485 int day = -1;
487 while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
488 if (!FirstTime)
489 FirstTime = usr.ut_time;
490 if (Flags & AC_D) {
491 ltm = localtime(&usr.ut_time);
492 if (day >= 0 && day != ltm->tm_yday) {
493 day = ltm->tm_yday;
495 * print yesterday's total
497 secs = usr.ut_time;
498 secs -= ltm->tm_sec;
499 secs -= 60 * ltm->tm_min;
500 secs -= 3600 * ltm->tm_hour;
501 show_today(Users, head, secs);
502 } else
503 day = ltm->tm_yday;
505 switch(*usr.ut_line) {
506 case '|':
507 secs = usr.ut_time;
508 break;
509 case '{':
510 secs -= usr.ut_time;
512 * adjust time for those logged in
514 for (lp = head; lp != NULL; lp = lp->next)
515 lp->usr.ut_time -= secs;
516 break;
517 case '~': /* reboot or shutdown */
518 head = log_out(head, &usr);
519 FirstTime = usr.ut_time; /* shouldn't be needed */
520 break;
521 default:
523 * if they came in on tty[p-y]*, then it is only
524 * a login session if the ut_host field is non-empty,
525 * or this tty is a login tty [eg. a console]
527 if (*usr.ut_name) {
528 if ((strncmp(usr.ut_line, "tty", 3) != 0 &&
529 strncmp(usr.ut_line, "dty", 3) != 0) ||
530 strchr("pqrstuvwxyzPQRST", usr.ut_line[3]) == 0 ||
531 *usr.ut_host != '\0' ||
532 is_login_tty(usr.ut_line))
533 head = log_in(head, &usr);
534 #ifdef DEBUG
535 else if (Debug)
536 printf("%-.*s %-.*s: %-.*s ignored\n",
537 19, ctime(&usr.ut_time),
538 sizeof (usr.ut_line), usr.ut_line,
539 sizeof (usr.ut_name), usr.ut_name);
540 #endif
541 } else
542 head = log_out(head, &usr);
543 break;
546 (void)fclose(fp);
547 usr.ut_time = time((time_t *)0);
548 (void)strcpy(usr.ut_line, "~");
550 if (Flags & AC_D) {
551 ltm = localtime(&usr.ut_time);
552 if (day >= 0 && day != ltm->tm_yday) {
554 * print yesterday's total
556 secs = usr.ut_time;
557 secs -= ltm->tm_sec;
558 secs -= 60 * ltm->tm_min;
559 secs -= 3600 * ltm->tm_hour;
560 show_today(Users, head, secs);
564 * anyone still logged in gets time up to now
566 head = log_out(head, &usr);
568 if (Flags & AC_D)
569 show_today(Users, head, time((time_t *)0));
570 else {
571 if (Flags & AC_P)
572 show_users(Users);
573 show("total", Total);
575 return 0;
578 static void
579 usage(void)
582 (void)fprintf(stderr,
583 "usage: %s [-d | -p] [-t tty] [-w wtmp] [users ...]\n",
584 getprogname());
585 exit(1);