1 /* $NetBSD: ac.c,v 1.22 2006/05/23 01:16:33 christos Exp $ */
4 * Copyright (c) 1994 Christopher G. Demetriou
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
45 * 2/ that any changes to this code are clearly commented
46 * as such so that the author does not get blamed for bugs
50 #include <sys/cdefs.h>
52 __RCSID("$NetBSD: ac.c,v 1.22 2006/05/23 01:16:33 christos Exp $");
55 #include <sys/types.h>
69 * this is for our list of currently logged in sessions
72 struct utmp_list
*next
;
77 * this is for our list of users that are accumulating time.
80 struct user_list
*next
;
81 char name
[UT_NAMESIZE
+1];
86 * this is for chosing whether to ignore a login
89 struct tty_list
*next
;
90 char name
[UT_LINESIZE
+3];
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 */
118 static int Debug
= 0;
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
*);
128 static int on_console(struct utmp_list
*);
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
*,
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);
143 file(const char *name
)
147 if (strcmp(name
, "-") == 0)
149 else if ((fp
= fopen(name
, "r")) == NULL
)
151 /* in case we want to discriminate */
152 if (strcmp(_PATH_WTMP
, name
))
157 static struct tty_list
*
165 if ((tp
= NEW(struct tty_list
)) == NULL
)
167 tp
->len
= 0; /* full match */
168 tp
->ret
= 1; /* do if match */
169 if (*name
== '!') { /* don't do if match */
173 (void)strlcpy(tp
->name
, name
, sizeof (tp
->name
));
174 if ((rcp
= strchr(tp
->name
, '*')) != NULL
) { /* wild card */
176 tp
->len
= strlen(tp
->name
); /* match len bytes only */
184 * should we process the named tty?
192 for (tp
= Ttys
; tp
!= NULL
; tp
= tp
->next
) {
193 if (tp
->ret
== 0) /* specific don't */
194 def_ret
= 1; /* default do */
196 if (strncmp(name
, tp
->name
, tp
->len
) == 0)
199 if (strncmp(name
, tp
->name
, sizeof (tp
->name
)) == 0)
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.
218 find_login_ttys(void)
221 char (*nCon
)[UT_LINESIZE
];
223 if ((Con
= malloc((Maxcon
= 10) * sizeof(Con
[0]))) == NULL
)
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
)
237 (void)strncpy(Con
[Ncon
++], tty
->ty_name
, UT_LINESIZE
);
240 qsort(Con
, Ncon
, sizeof(Con
[0]), compare
);
245 * is someone logged in on Console/login tty?
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
))
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) {
276 * not found so add new user unless specified users only
281 if ((up
= NEW(struct user_list
)) == NULL
)
284 (void)strlcpy(up
->name
, name
, sizeof (up
->name
));
291 main(int argc
, char **argv
)
297 while ((c
= getopt(argc
, argv
, "Ddpt:w:")) != -1) {
310 case 't': /* only do specified ttys */
326 * initialize user list
328 for (; optind
< argc
; optind
++) {
329 Users
= update_user(Users
, argv
[optind
], 0L);
331 Flags
|= AC_U
; /* freeze user list */
337 * if _PATH_WTMP does not exist, exit quietly
339 if (access(_PATH_WTMP
, 0) != 0 && errno
== ENOENT
)
342 fp
= file(_PATH_WTMP
);
350 * print login time in decimal hours
353 show(const char *name
, time_t secs
)
355 (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE
, name
,
356 ((double)secs
/ 3600));
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
372 show_today(struct user_list
*users
, struct utmp_list
*logins
, time_t secs
)
374 struct user_list
*up
;
375 struct utmp_list
*lp
;
377 time_t yesterday
= secs
- 1;
379 (void)strftime(date
, sizeof (date
), "%b %e total",
380 localtime(&yesterday
));
382 /* restore the missing second */
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 */
391 for (up
= users
; up
!= NULL
; up
= up
->next
) {
393 up
->secs
= 0; /* for next day */
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
404 static struct utmp_list
*
405 log_out(struct utmp_list
*head
, struct utmp
*up
)
407 struct utmp_list
*lp
, *lp2
, *tlp
;
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
);
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);
430 } else if (lp2
!= NULL
)
442 * if do_tty says ok, login a user
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
454 if (!do_tty(up
->ut_line
))
458 * go ahead and log them in
460 if ((lp
= NEW(struct utmp_list
)) == NULL
)
464 memmove((char *)&lp
->usr
, (char *)up
, sizeof (struct utmp
));
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
);
471 printf(" (%-.*s)", sizeof (up
->ut_host
), up
->ut_host
);
481 struct utmp_list
*lp
, *head
= NULL
;
487 while (fread((char *)&usr
, sizeof(usr
), 1, fp
) == 1) {
489 FirstTime
= usr
.ut_time
;
491 ltm
= localtime(&usr
.ut_time
);
492 if (day
>= 0 && day
!= ltm
->tm_yday
) {
495 * print yesterday's total
499 secs
-= 60 * ltm
->tm_min
;
500 secs
-= 3600 * ltm
->tm_hour
;
501 show_today(Users
, head
, secs
);
505 switch(*usr
.ut_line
) {
512 * adjust time for those logged in
514 for (lp
= head
; lp
!= NULL
; lp
= lp
->next
)
515 lp
->usr
.ut_time
-= secs
;
517 case '~': /* reboot or shutdown */
518 head
= log_out(head
, &usr
);
519 FirstTime
= usr
.ut_time
; /* shouldn't be needed */
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]
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
);
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
);
542 head
= log_out(head
, &usr
);
547 usr
.ut_time
= time((time_t *)0);
548 (void)strcpy(usr
.ut_line
, "~");
551 ltm
= localtime(&usr
.ut_time
);
552 if (day
>= 0 && day
!= ltm
->tm_yday
) {
554 * print yesterday's total
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
);
569 show_today(Users
, head
, time((time_t *)0));
573 show("total", Total
);
582 (void)fprintf(stderr
,
583 "usage: %s [-d | -p] [-t tty] [-w wtmp] [users ...]\n",