2 * Copyright (c) 1994 Christopher G. Demetriou.
3 * @(#)Copyright (c) 1994, Simon J. Gerraty.
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
11 * 2/ that any changes to this code are clearly commented
12 * as such so that the author does not get blamed for bugs
15 * $FreeBSD: src/usr.sbin/ac/ac.c,v 1.14.2.2 2002/03/12 19:55:04 phantom Exp $
18 #include <sys/types.h>
33 * this is for our list of currently logged in sessions
36 struct utmp_list
*next
;
41 * this is for our list of users that are accumulating time.
44 struct user_list
*next
;
45 char name
[UT_NAMESIZE
+1];
50 * this is for chosing whether to ignore a login
53 struct tty_list
*next
;
54 char name
[UT_LINESIZE
+3];
63 static char *Console
= CONSOLE_TTY
;
65 static time_t Total
= 0;
66 static time_t FirstTime
= 0;
68 static struct user_list
*Users
= NULL
;
69 static struct tty_list
*Ttys
= NULL
;
71 #define NEW(type) (type *)malloc(sizeof(type))
73 #define AC_W 1 /* not _PATH_WTMP */
74 #define AC_D 2 /* daily totals (ignore -p) */
75 #define AC_P 4 /* per-user totals */
76 #define AC_U 8 /* specified users only */
77 #define AC_T 16 /* specified ttys only */
83 static int ac(FILE *);
84 static struct tty_list
*add_tty(char *);
85 static int do_tty(char *);
86 static FILE *file(const char *);
87 static struct utmp_list
*log_in(struct utmp_list
*, struct utmp
*);
88 static struct utmp_list
*log_out(struct utmp_list
*, struct utmp
*);
90 static int on_console(struct utmp_list
*);
92 static void show(const char *, time_t);
93 static void show_today(struct user_list
*, struct utmp_list
*,
95 static void show_users(struct user_list
*);
96 static struct user_list
*update_user(struct user_list
*, char *, time_t);
97 static void usage(void) __dead2
;
103 file(const char *name
)
107 if ((fp
= fopen(name
, "r")) == NULL
)
109 /* in case we want to discriminate */
110 if (strcmp(_PATH_WTMP
, name
))
115 static struct tty_list
*
123 if ((tp
= NEW(struct tty_list
)) == NULL
)
124 errx(1, "malloc failed");
125 tp
->len
= 0; /* full match */
126 tp
->ret
= 1; /* do if match */
127 if (*name
== '!') { /* don't do if match */
131 strncpy(tp
->name
, name
, sizeof(tp
->name
) - 1);
132 tp
->name
[sizeof(tp
->name
) - 1] = '\0';
133 if ((rcp
= strchr(tp
->name
, '*')) != NULL
) { /* wild card */
135 tp
->len
= strlen(tp
->name
); /* match len bytes only */
143 * should we process the named tty?
151 for (tp
= Ttys
; tp
!= NULL
; tp
= tp
->next
) {
152 if (tp
->ret
== 0) /* specific don't */
153 def_ret
= 1; /* default do */
155 if (strncmp(name
, tp
->name
, tp
->len
) == 0)
158 if (strncmp(name
, tp
->name
, sizeof(tp
->name
)) == 0)
167 * is someone logged in on Console?
170 on_console(struct utmp_list
*head
)
172 struct utmp_list
*up
;
174 for (up
= head
; up
; up
= up
->next
) {
175 if (strncmp(up
->usr
.ut_line
, Console
,
176 sizeof(up
->usr
.ut_line
)) == 0)
184 * update user's login time
186 static struct user_list
*
187 update_user(struct user_list
*head
, char *name
, time_t secs
)
189 struct user_list
*up
;
191 for (up
= head
; up
!= NULL
; up
= up
->next
) {
192 if (strncmp(up
->name
, name
, UT_NAMESIZE
) == 0) {
199 * not found so add new user unless specified users only
204 if ((up
= NEW(struct user_list
)) == NULL
)
205 errx(1, "malloc failed");
207 strncpy(up
->name
, name
, sizeof(up
->name
) - 1);
208 up
->name
[sizeof(up
->name
) - 1] = '\0'; /* paranoid! */
215 main(int argc
, char **argv
)
220 setlocale(LC_TIME
, "");
223 while ((c
= getopt(argc
, argv
, "Dc:dpt:w:")) != -1) {
243 case 't': /* only do specified ttys */
257 * initialize user list
259 for (; optind
< argc
; optind
++) {
260 Users
= update_user(Users
, argv
[optind
], 0L);
262 Flags
|= AC_U
; /* freeze user list */
268 * if _PATH_WTMP does not exist, exit quietly
270 if (access(_PATH_WTMP
, 0) != 0 && errno
== ENOENT
)
273 fp
= file(_PATH_WTMP
);
281 * print login time in decimal hours
284 show(const char *name
, time_t secs
)
286 printf("\t%-*s %8.2f\n", UT_NAMESIZE
, name
,
287 ((double)secs
/ 3600));
291 show_users(struct user_list
*list
)
293 struct user_list
*lp
;
295 for (lp
= list
; lp
; lp
= lp
->next
)
296 show(lp
->name
, lp
->secs
);
300 * print total login time for 24hr period in decimal hours
303 show_today(struct user_list
*users
, struct utmp_list
*logins
, time_t secs
)
305 struct user_list
*up
;
306 struct utmp_list
*lp
;
308 time_t yesterday
= secs
- 1;
309 static int d_first
= -1;
312 d_first
= (*nl_langinfo(D_MD_ORDER
) == 'd');
313 strftime(date
, sizeof(date
),
314 d_first
? "%e %b total" : "%b %e total",
315 localtime(&yesterday
));
317 /* restore the missing second */
320 for (lp
= logins
; lp
!= NULL
; lp
= lp
->next
) {
321 secs
= yesterday
- lp
->usr
.ut_time
;
322 Users
= update_user(Users
, lp
->usr
.ut_name
, secs
);
323 lp
->usr
.ut_time
= yesterday
; /* as if they just logged in */
326 for (up
= users
; up
!= NULL
; up
= up
->next
) {
328 up
->secs
= 0; /* for next day */
331 printf("%s %11.2f\n", date
, ((double)secs
/ 3600));
335 * log a user out and update their times.
336 * if ut_line is "~", we log all users out as the system has
339 static struct utmp_list
*
340 log_out(struct utmp_list
*head
, struct utmp
*up
)
342 struct utmp_list
*lp
, *lp2
, *tlp
;
345 for (lp
= head
, lp2
= NULL
; lp
!= NULL
; )
346 if (*up
->ut_line
== '~' || strncmp(lp
->usr
.ut_line
, up
->ut_line
,
347 sizeof(up
->ut_line
)) == 0) {
348 secs
= up
->ut_time
- lp
->usr
.ut_time
;
349 Users
= update_user(Users
, lp
->usr
.ut_name
, secs
);
352 printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
353 19, ctime(&up
->ut_time
),
354 sizeof(lp
->usr
.ut_line
), lp
->usr
.ut_line
,
355 sizeof(lp
->usr
.ut_name
), lp
->usr
.ut_name
,
356 secs
/ 3600, (secs
% 3600) / 60, secs
% 60);
365 else if (lp2
!= NULL
)
377 * if do_tty says ok, login a user
379 static struct utmp_list
*
380 log_in(struct utmp_list
*head
, struct utmp
*up
)
382 struct utmp_list
*lp
;
385 * this could be a login. if we're not dealing with
386 * the console name, say it is.
388 * If we are, and if ut_host==":0.0" we know that it
389 * isn't a real login. _But_ if we have not yet recorded
390 * someone being logged in on Console - due to the wtmp
391 * file starting after they logged in, we'll pretend they
392 * logged in, at the start of the wtmp file.
396 if (up
->ut_host
[0] == ':') {
398 * SunOS 4.0.2 does not treat ":0.0" as special but we
401 if (on_console(head
))
404 * ok, no recorded login, so they were here when wtmp
405 * started! Adjust ut_time!
407 up
->ut_time
= FirstTime
;
409 * this allows us to pick the right logout
411 strncpy(up
->ut_line
, Console
, sizeof(up
->ut_line
) - 1);
412 up
->ut_line
[sizeof(up
->ut_line
) - 1] = '\0'; /* paranoid! */
416 * If we are doing specified ttys only, we ignore
420 if (!do_tty(up
->ut_line
))
424 * go ahead and log them in
426 if ((lp
= NEW(struct utmp_list
)) == NULL
)
427 errx(1, "malloc failed");
430 memmove((char *)&lp
->usr
, (char *)up
, sizeof(struct utmp
));
433 printf("%-.*s %-.*s: %-.*s logged in", 19,
434 ctime(&lp
->usr
.ut_time
), sizeof(up
->ut_line
),
435 up
->ut_line
, sizeof(up
->ut_name
), up
->ut_name
);
437 printf(" (%-.*s)", sizeof(up
->ut_host
), up
->ut_host
);
447 struct utmp_list
*lp
, *head
= NULL
;
455 while (fread((char *)&usr
, sizeof(usr
), 1, fp
) == 1) {
457 FirstTime
= usr
.ut_time
;
459 ltm
= localtime(&usr
.ut_time
);
460 if (day
>= 0 && day
!= ltm
->tm_yday
) {
463 * print yesterday's total
467 secs
-= 60 * ltm
->tm_min
;
468 secs
-= 3600 * ltm
->tm_hour
;
469 show_today(Users
, head
, secs
);
473 switch(*usr
.ut_line
) {
480 * adjust time for those logged in
482 for (lp
= head
; lp
!= NULL
; lp
= lp
->next
)
483 lp
->usr
.ut_time
-= secs
;
485 case '~': /* reboot or shutdown */
486 head
= log_out(head
, &usr
);
487 FirstTime
= usr
.ut_time
; /* shouldn't be needed */
491 * if they came in on tty[p-sP-S]*, then it is only
492 * a login session if the ut_host field is non-empty
495 if (strncmp(usr
.ut_line
, "tty", 3) != 0 ||
496 strchr("pqrsPQRS", usr
.ut_line
[3]) == 0 ||
497 *usr
.ut_host
!= '\0')
498 head
= log_in(head
, &usr
);
500 head
= log_out(head
, &usr
);
506 usr
.ut_time
= time(NULL
);
507 strcpy(usr
.ut_line
, "~");
510 ltm
= localtime(&usr
.ut_time
);
511 if (day
>= 0 && day
!= ltm
->tm_yday
) {
513 * print yesterday's total
517 secs
-= 60 * ltm
->tm_min
;
518 secs
-= 3600 * ltm
->tm_hour
;
519 show_today(Users
, head
, secs
);
523 * anyone still logged in gets time up to now
525 head
= log_out(head
, &usr
);
528 show_today(Users
, head
, time(NULL
));
532 show("total", Total
);
542 "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
544 "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");