]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ac/ac.c
This commit was generated by cvs2svn to compensate for changes in r106424,
[FreeBSD/FreeBSD.git] / usr.sbin / ac / ac.c
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 #include <sys/cdefs.h>
17 __FBSDID("$FreeBSD$");
18
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <err.h>
22 #include <errno.h>
23 #include <langinfo.h>
24 #include <locale.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <timeconv.h>
29 #include <unistd.h>
30 #include <utmp.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+3];
55         size_t  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(int, char **);
84 int                     ac(FILE *);
85 struct tty_list         *add_tty(char *);
86 int                     do_tty(char *);
87 FILE                    *file(const char *);
88 struct utmp_list        *log_in(struct utmp_list *, struct utmp *);
89 struct utmp_list        *log_out(struct utmp_list *, struct utmp *);
90 int                     on_console(struct utmp_list *);
91 void                    show(const char *, time_t);
92 void                    show_today(struct user_list *, struct utmp_list *,
93                             time_t);
94 void                    show_users(struct user_list *);
95 struct user_list        *update_user(struct user_list *, char *, time_t);
96 void                    usage(void);
97
98 /*
99  * open wtmp or die
100  */
101 FILE *
102 file(name)
103         const 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                 errx(1, "malloc failed");
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         strlcpy(tp->name, name, sizeof (tp->name));
133         if ((rcp = strchr(tp->name, '*')) != NULL) {    /* wild card */
134                 *rcp = '\0';
135                 tp->len = strlen(tp->name);     /* match len bytes only */
136         }
137         tp->next = Ttys;
138         Ttys = tp;
139         return Ttys;
140 }
141
142 /*
143  * should we process the named tty?
144  */
145 int
146 do_tty(name)
147         char *name;
148 {
149         struct tty_list *tp;
150         int def_ret = 0;
151
152         for (tp = Ttys; tp != NULL; tp = tp->next) {
153                 if (tp->ret == 0)               /* specific don't */
154                         def_ret = 1;            /* default do */
155                 if (tp->len != 0) {
156                         if (strncmp(name, tp->name, tp->len) == 0)
157                                 return tp->ret;
158                 } else {
159                         if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
160                                 return tp->ret;
161                 }
162         }
163         return def_ret;
164 }
165
166 #ifdef CONSOLE_TTY
167 /*
168  * is someone logged in on Console?
169  */
170 int
171 on_console(head)
172         struct utmp_list *head;
173 {
174         struct utmp_list *up;
175
176         for (up = head; up; up = up->next) {
177                 if (strncmp(up->usr.ut_line, Console,
178                     sizeof (up->usr.ut_line)) == 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 (strncmp(up->name, name, UT_NAMESIZE) == 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                 errx(1, "malloc failed");
211         up->next = head;
212         strlcpy(up->name, name, sizeof (up->name));
213         up->secs = secs;
214         Total += secs;
215         return up;
216 }
217
218 int
219 main(argc, argv)
220         int     argc;
221         char    **argv;
222 {
223         FILE *fp;
224         int c;
225
226         (void) setlocale(LC_TIME, "");
227
228         fp = NULL;
229         while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) {
230                 switch (c) {
231 #ifdef DEBUG
232                 case 'D':
233                         Debug++;
234                         break;
235 #endif
236                 case 'c':
237 #ifdef CONSOLE_TTY
238                         Console = optarg;
239 #else
240                         usage();                /* XXX */
241 #endif
242                         break;
243                 case 'd':
244                         Flags |= AC_D;
245                         break;
246                 case 'p':
247                         Flags |= AC_P;
248                         break;
249                 case 't':                       /* only do specified ttys */
250                         add_tty(optarg);
251                         break;
252                 case 'w':
253                         fp = file(optarg);
254                         break;
255                 case '?':
256                 default:
257                         usage();
258                         break;
259                 }
260         }
261         if (optind < argc) {
262                 /*
263                  * initialize user list
264                  */
265                 for (; optind < argc; optind++) {
266                         Users = update_user(Users, argv[optind], (time_t)0);
267                 }
268                 Flags |= AC_U;                  /* freeze user list */
269         }
270         if (Flags & AC_D)
271                 Flags &= ~AC_P;
272         if (fp == NULL) {
273                 /*
274                  * if _PATH_WTMP does not exist, exit quietly
275                  */
276                 if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT)
277                         return 0;
278
279                 fp = file(_PATH_WTMP);
280         }
281         ac(fp);
282
283         return 0;
284 }
285
286 /*
287  * print login time in decimal hours
288  */
289 void
290 show(name, secs)
291         const char *name;
292         time_t secs;
293 {
294         (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
295             ((double)secs / 3600));
296 }
297
298 void
299 show_users(list)
300         struct user_list *list;
301 {
302         struct user_list *lp;
303
304         for (lp = list; lp; lp = lp->next)
305                 show(lp->name, lp->secs);
306 }
307
308 /*
309  * print total login time for 24hr period in decimal hours
310  */
311 void
312 show_today(users, logins, secs)
313         struct user_list *users;
314         struct utmp_list *logins;
315         time_t secs;
316 {
317         struct user_list *up;
318         struct utmp_list *lp;
319         char date[64];
320         time_t yesterday = secs - 1;
321         static int d_first = -1;
322
323         if (d_first < 0)
324                 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
325         (void)strftime(date, sizeof (date),
326                        d_first ? "%e %b  total" : "%b %e  total",
327                        localtime(&yesterday));
328
329         /* restore the missing second */
330         yesterday++;
331
332         for (lp = logins; lp != NULL; lp = lp->next) {
333                 secs = yesterday - lp->usr.ut_time;
334                 Users = update_user(Users, lp->usr.ut_name, secs);
335                 lp->usr.ut_time = yesterday;    /* as if they just logged in */
336         }
337         secs = 0;
338         for (up = users; up != NULL; up = up->next) {
339                 secs += up->secs;
340                 up->secs = 0;                   /* for next day */
341         }
342         if (secs)
343                 (void)printf("%s %11.2f\n", date, ((double)secs / 3600));
344 }
345
346 /*
347  * log a user out and update their times.
348  * if ut_line is "~", we log all users out as the system has
349  * been shut down.
350  */
351 struct utmp_list *
352 log_out(head, up)
353         struct utmp_list *head;
354         struct utmp *up;
355 {
356         struct utmp_list *lp, *lp2, *tlp;
357         time_t secs;
358
359         for (lp = head, lp2 = NULL; lp != NULL; )
360                 if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line,
361                     sizeof (up->ut_line)) == 0) {
362                         secs = up->ut_time - lp->usr.ut_time;
363                         Users = update_user(Users, lp->usr.ut_name, secs);
364 #ifdef DEBUG
365                         if (Debug)
366                                 printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
367                                     19, ctime(&up->ut_time),
368                                     sizeof (lp->usr.ut_line), lp->usr.ut_line,
369                                     sizeof (lp->usr.ut_name), lp->usr.ut_name,
370                                     secs / 3600, (secs % 3600) / 60, secs % 60);
371 #endif
372                         /*
373                          * now lose it
374                          */
375                         tlp = lp;
376                         lp = lp->next;
377                         if (tlp == head)
378                                 head = lp;
379                         else if (lp2 != NULL)
380                                 lp2->next = lp;
381                         free(tlp);
382                 } else {
383                         lp2 = lp;
384                         lp = lp->next;
385                 }
386         return head;
387 }
388
389
390 /*
391  * if do_tty says ok, login a user
392  */
393 struct utmp_list *
394 log_in(head, up)
395         struct utmp_list *head;
396         struct utmp *up;
397 {
398         struct utmp_list *lp;
399
400         /*
401          * this could be a login. if we're not dealing with
402          * the console name, say it is.
403          *
404          * If we are, and if ut_host==":0.0" we know that it
405          * isn't a real login. _But_ if we have not yet recorded
406          * someone being logged in on Console - due to the wtmp
407          * file starting after they logged in, we'll pretend they
408          * logged in, at the start of the wtmp file.
409          */
410
411 #ifdef CONSOLE_TTY
412         if (up->ut_host[0] == ':') {
413                 /*
414                  * SunOS 4.0.2 does not treat ":0.0" as special but we
415                  * do.
416                  */
417                 if (on_console(head))
418                         return head;
419                 /*
420                  * ok, no recorded login, so they were here when wtmp
421                  * started!  Adjust ut_time!
422                  */
423                 up->ut_time = FirstTime;
424                 /*
425                  * this allows us to pick the right logout
426                  */
427                 strlcpy(up->ut_line, Console, sizeof (up->ut_line));
428         }
429 #endif
430         /*
431          * If we are doing specified ttys only, we ignore
432          * anything else.
433          */
434         if (Flags & AC_T)
435                 if (!do_tty(up->ut_line))
436                         return head;
437
438         /*
439          * go ahead and log them in
440          */
441         if ((lp = NEW(struct utmp_list)) == NULL)
442                 errx(1, "malloc failed");
443         lp->next = head;
444         head = lp;
445         memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
446 #ifdef DEBUG
447         if (Debug) {
448                 printf("%-.*s %-.*s: %-.*s logged in", 19,
449                     ctime(&lp->usr.ut_time), sizeof (up->ut_line),
450                        up->ut_line, sizeof (up->ut_name), up->ut_name);
451                 if (*up->ut_host)
452                         printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host);
453                 putchar('\n');
454         }
455 #endif
456         return head;
457 }
458
459 int
460 ac(fp)
461         FILE    *fp;
462 {
463         struct utmp_list *lp, *head = NULL;
464         struct utmp usr;
465         struct tm *ltm;
466         time_t secs;
467         int day = -1;
468
469         while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
470                 if (!FirstTime)
471                         FirstTime = usr.ut_time;
472                 if (Flags & AC_D) {
473                         time_t t = _int_to_time(usr.ut_time);
474                         ltm = localtime(&t);
475                         if (day >= 0 && day != ltm->tm_yday) {
476                                 day = ltm->tm_yday;
477                                 /*
478                                  * print yesterday's total
479                                  */
480                                 secs = usr.ut_time;
481                                 secs -= ltm->tm_sec;
482                                 secs -= 60 * ltm->tm_min;
483                                 secs -= 3600 * ltm->tm_hour;
484                                 show_today(Users, head, secs);
485                         } else
486                                 day = ltm->tm_yday;
487                 }
488                 switch(*usr.ut_line) {
489                 case '|':
490                         secs = usr.ut_time;
491                         break;
492                 case '{':
493                         secs -= usr.ut_time;
494                         /*
495                          * adjust time for those logged in
496                          */
497                         for (lp = head; lp != NULL; lp = lp->next)
498                                 lp->usr.ut_time -= secs;
499                         break;
500                 case '~':                       /* reboot or shutdown */
501                         head = log_out(head, &usr);
502                         FirstTime = usr.ut_time; /* shouldn't be needed */
503                         break;
504                 default:
505                         /*
506                          * if they came in on tty[p-sP-S]*, then it is only
507                          * a login session if the ut_host field is non-empty
508                          */
509                         if (*usr.ut_name) {
510                                 if (strncmp(usr.ut_line, "tty", 3) == 0 ||
511                                     strchr("pqrsPQRS", usr.ut_line[3]) != 0 ||
512                                     *usr.ut_host != '\0')
513                                         head = log_in(head, &usr);
514                         } else
515                                 head = log_out(head, &usr);
516                         break;
517                 }
518         }
519         (void)fclose(fp);
520         if (!(Flags & AC_W))
521                 usr.ut_time = time((time_t *)0);
522         (void)strcpy(usr.ut_line, "~");
523
524         if (Flags & AC_D) {
525                 time_t t = _int_to_time(usr.ut_time);
526                 ltm = localtime(&t);
527                 if (day >= 0 && day != ltm->tm_yday) {
528                         /*
529                          * print yesterday's total
530                          */
531                         secs = usr.ut_time;
532                         secs -= ltm->tm_sec;
533                         secs -= 60 * ltm->tm_min;
534                         secs -= 3600 * ltm->tm_hour;
535                         show_today(Users, head, secs);
536                 }
537         }
538         /*
539          * anyone still logged in gets time up to now
540          */
541         head = log_out(head, &usr);
542
543         if (Flags & AC_D)
544                 show_today(Users, head, time((time_t *)0));
545         else {
546                 if (Flags & AC_P)
547                         show_users(Users);
548                 show("total", Total);
549         }
550         return 0;
551 }
552
553 void
554 usage()
555 {
556         (void)fprintf(stderr,
557 #ifdef CONSOLE_TTY
558             "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
559 #else
560             "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");
561 #endif
562         exit(1);
563 }