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