]> git.saurik.com Git - apple/system_cmds.git/blob - ac.tproj/ac.c
system_cmds-643.1.1.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 #include <sys/cdefs.h>
41 __unused static char rcsid[] = "$Id: ac.c,v 1.2 2006/02/07 05:51:22 lindak 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 <utmpx.h>
54 #include <unistd.h>
55
56 #define UT_NAMESIZE 8 /* from utmp.h; only for formatting */
57
58 /*
59 * this is for our list of currently logged in sessions
60 */
61 struct utmp_list {
62 struct utmp_list *next;
63 struct utmpx usr;
64 };
65
66 /*
67 * this is for our list of users that are accumulating time.
68 */
69 struct user_list {
70 struct user_list *next;
71 char name[_UTX_USERSIZE+1];
72 time_t secs;
73 };
74
75 /*
76 * this is for chosing whether to ignore a login
77 */
78 struct tty_list {
79 struct tty_list *next;
80 char name[_UTX_USERSIZE+3];
81 int len;
82 int ret;
83 };
84
85 /*
86 * globals - yes yuk
87 */
88 #ifdef CONSOLE_TTY
89 static char *Console = CONSOLE_TTY;
90 #endif
91 static time_t Total = 0;
92 static time_t FirstTime = 0;
93 static int Flags = 0;
94 static struct user_list *Users = NULL;
95 static struct tty_list *Ttys = NULL;
96
97 #define NEW(type) (type *)malloc(sizeof (type))
98
99 #define AC_W 1 /* not _PATH_WTMP */
100 #define AC_D 2 /* daily totals (ignore -p) */
101 #define AC_P 4 /* per-user totals */
102 #define AC_U 8 /* specified users only */
103 #define AC_T 16 /* specified ttys only */
104
105 #ifdef DEBUG
106 static int Debug = 0;
107 #endif
108
109 int main __P((int, char **));
110 int ac __P((void));
111 struct tty_list *add_tty __P((char *));
112 int do_tty __P((char *));
113 struct utmp_list *log_in __P((struct utmp_list *, struct utmpx *));
114 struct utmp_list *log_out __P((struct utmp_list *, struct utmpx *));
115 int on_console __P((struct utmp_list *));
116 void show __P((char *, time_t));
117 void show_today __P((struct user_list *, struct utmp_list *,
118 time_t));
119 void show_users __P((struct user_list *));
120 struct user_list *update_user __P((struct user_list *, char *, time_t));
121 void usage __P((void));
122
123 struct tty_list *
124 add_tty(name)
125 char *name;
126 {
127 struct tty_list *tp;
128 register char *rcp;
129
130 Flags |= AC_T;
131
132 if ((tp = NEW(struct tty_list)) == NULL)
133 err(1, "malloc");
134 tp->len = 0; /* full match */
135 tp->ret = 1; /* do if match */
136 if (*name == '!') { /* don't do if match */
137 tp->ret = 0;
138 name++;
139 }
140 (void)strncpy(tp->name, name, sizeof (tp->name) - 1);
141 tp->name[sizeof (tp->name) - 1] = '\0';
142 if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */
143 *rcp = '\0';
144 tp->len = (int)strlen(tp->name); /* match len bytes only */
145 }
146 tp->next = Ttys;
147 Ttys = tp;
148 return Ttys;
149 }
150
151 /*
152 * should we process the named tty?
153 */
154 int
155 do_tty(name)
156 char *name;
157 {
158 struct tty_list *tp;
159 int def_ret = 0;
160
161 for (tp = Ttys; tp != NULL; tp = tp->next) {
162 if (tp->ret == 0) /* specific don't */
163 def_ret = 1; /* default do */
164 if (tp->len != 0) {
165 if (strncmp(name, tp->name, tp->len) == 0)
166 return tp->ret;
167 } else {
168 if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
169 return tp->ret;
170 }
171 }
172 return def_ret;
173 }
174
175 #ifdef CONSOLE_TTY
176 /*
177 * is someone logged in on Console?
178 */
179 int
180 on_console(head)
181 struct utmp_list *head;
182 {
183 struct utmp_list *up;
184
185 for (up = head; up; up = up->next) {
186 if (strncmp(up->usr.ut_line, Console,
187 sizeof (up->usr.ut_line)) == 0)
188 return 1;
189 }
190 return 0;
191 }
192 #endif
193
194 /*
195 * update user's login time
196 */
197 struct user_list *
198 update_user(head, name, secs)
199 struct user_list *head;
200 char *name;
201 time_t secs;
202 {
203 struct user_list *up;
204
205 for (up = head; up != NULL; up = up->next) {
206 if (strncmp(up->name, name, sizeof (up->name)) == 0) {
207 up->secs += secs;
208 Total += secs;
209 return head;
210 }
211 }
212 /*
213 * not found so add new user unless specified users only
214 */
215 if (Flags & AC_U)
216 return head;
217
218 if ((up = NEW(struct user_list)) == NULL)
219 err(1, "malloc");
220 up->next = head;
221 (void)strncpy(up->name, name, sizeof (up->name) - 1);
222 up->name[sizeof (up->name) - 1] = '\0'; /* paranoid! */
223 up->secs = secs;
224 Total += secs;
225 return up;
226 }
227
228 int
229 main(argc, argv)
230 int argc;
231 char **argv;
232 {
233 FILE *fp;
234 int c;
235
236 fp = NULL;
237 while ((c = getopt(argc, argv, "Dc:dpt:w:")) != EOF) {
238 switch (c) {
239 #ifdef DEBUG
240 case 'D':
241 Debug++;
242 break;
243 #endif
244 case 'c':
245 #ifdef CONSOLE_TTY
246 Console = optarg;
247 #else
248 usage(); /* XXX */
249 #endif
250 break;
251 case 'd':
252 Flags |= AC_D;
253 break;
254 case 'p':
255 Flags |= AC_P;
256 break;
257 case 't': /* only do specified ttys */
258 add_tty(optarg);
259 break;
260 case 'w':
261 wtmpxname(optarg);
262 break;
263 case '?':
264 default:
265 usage();
266 break;
267 }
268 }
269 if (optind < argc) {
270 /*
271 * initialize user list
272 */
273 for (; optind < argc; optind++) {
274 Users = update_user(Users, argv[optind], 0L);
275 }
276 Flags |= AC_U; /* freeze user list */
277 }
278 if (Flags & AC_D)
279 Flags &= ~AC_P;
280 ac();
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_tv.tv_sec;
329 Users = update_user(Users, lp->usr.ut_user, secs);
330 lp->usr.ut_tv.tv_sec = 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 utmpx *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_type == BOOT_TIME || up->ut_type == SHUTDOWN_TIME || strncmp(lp->usr.ut_line, up->ut_line,
356 sizeof (up->ut_line)) == 0) {
357 secs = up->ut_tv.tv_sec - lp->usr.ut_tv.tv_sec;
358 Users = update_user(Users, lp->usr.ut_user, secs);
359 #ifdef DEBUG
360 if (Debug)
361 printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
362 19, ctime(&up->ut_tv.tv_sec),
363 (int)sizeof (lp->usr.ut_line), lp->usr.ut_line,
364 (int)sizeof (lp->usr.ut_user), lp->usr.ut_user,
365 secs / 3600, (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 utmpx *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_tv.tv_sec!
417 */
418 up->ut_tv.tv_sec = FirstTime;
419 /*
420 * this allows us to pick the right logout
421 */
422 (void)strncpy(up->ut_line, Console, sizeof (up->ut_line) - 1);
423 up->ut_line[sizeof (up->ut_line) - 1] = '\0'; /* paranoid! */
424 }
425 #endif
426 /*
427 * If we are doing specified ttys only, we ignore
428 * anything else.
429 */
430 if (Flags & AC_T)
431 if (!do_tty(up->ut_line))
432 return head;
433
434 /*
435 * go ahead and log them in
436 */
437 if ((lp = NEW(struct utmp_list)) == NULL)
438 err(1, "malloc");
439 lp->next = head;
440 head = lp;
441 memmove((char *)&lp->usr, (char *)up, sizeof (struct utmpx));
442 #ifdef DEBUG
443 if (Debug) {
444 printf("%-.*s %-.*s: %-.*s logged in", 19,
445 ctime(&lp->usr.ut_tv.tv_sec), (int)sizeof (up->ut_line),
446 up->ut_line, (int)sizeof (up->ut_user), up->ut_user);
447 if (*up->ut_host)
448 printf(" (%-.*s)", (int)sizeof (up->ut_host), up->ut_host);
449 putchar('\n');
450 }
451 #endif
452 return head;
453 }
454
455 int
456 ac()
457 {
458 struct utmp_list *lp, *head = NULL;
459 struct utmpx *u, end;
460 struct tm *ltm;
461 time_t secs = 0;
462 int day = -1;
463
464 setutxent_wtmp(1); /* read in forward direction */
465 while ((u = getutxent_wtmp()) != NULL) {
466 if (!FirstTime)
467 FirstTime = u->ut_tv.tv_sec;
468 if (Flags & AC_D) {
469 ltm = localtime(&u->ut_tv.tv_sec);
470 if (day >= 0 && day != ltm->tm_yday) {
471 day = ltm->tm_yday;
472 /*
473 * print yesterday's total
474 */
475 secs = u->ut_tv.tv_sec;
476 secs -= ltm->tm_sec;
477 secs -= 60 * ltm->tm_min;
478 secs -= 3600 * ltm->tm_hour;
479 show_today(Users, head, secs);
480 } else
481 day = ltm->tm_yday;
482 }
483 switch(u->ut_type) {
484 case OLD_TIME:
485 secs = u->ut_tv.tv_sec;
486 break;
487 case NEW_TIME:
488 secs -= u->ut_tv.tv_sec;
489 /*
490 * adjust time for those logged in
491 */
492 for (lp = head; lp != NULL; lp = lp->next)
493 lp->usr.ut_tv.tv_sec -= secs;
494 break;
495 case BOOT_TIME: /* reboot or shutdown */
496 case SHUTDOWN_TIME:
497 head = log_out(head, u);
498 FirstTime = u->ut_tv.tv_sec; /* shouldn't be needed */
499 break;
500 case USER_PROCESS:
501 /*
502 * if they came in on tty[p-y]*, then it is only
503 * a login session if the ut_host field is non-empty
504 */
505 if (strncmp(u->ut_line, "tty", 3) != 0 ||
506 strchr("pqrstuvwxy", u->ut_line[3]) == 0 ||
507 *u->ut_host != '\0')
508 head = log_in(head, u);
509 break;
510 case DEAD_PROCESS:
511 head = log_out(head, u);
512 break;
513 }
514 }
515 endutxent_wtmp();
516 bzero(&end, sizeof(end));
517 end.ut_tv.tv_sec = time((time_t *)0);
518 end.ut_type = SHUTDOWN_TIME;
519
520 if (Flags & AC_D) {
521 ltm = localtime(&end.ut_tv.tv_sec);
522 if (day >= 0 && day != ltm->tm_yday) {
523 /*
524 * print yesterday's total
525 */
526 secs = end.ut_tv.tv_sec;
527 secs -= ltm->tm_sec;
528 secs -= 60 * ltm->tm_min;
529 secs -= 3600 * ltm->tm_hour;
530 show_today(Users, head, secs);
531 }
532 }
533 /*
534 * anyone still logged in gets time up to now
535 */
536 head = log_out(head, &end);
537
538 if (Flags & AC_D)
539 show_today(Users, head, time((time_t *)0));
540 else {
541 if (Flags & AC_P)
542 show_users(Users);
543 show("total", Total);
544 }
545 return 0;
546 }
547
548 void
549 usage()
550 {
551 (void)fprintf(stderr,
552 #ifdef CONSOLE_TTY
553 "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
554 #else
555 "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");
556 #endif
557 exit(1);
558 }