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