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