xref: /freebsd/usr.sbin/ac/ac.c (revision e3b218cd7bf8e16229118fe40f6b958f2c18709d)
1ff9c3a32SDavid Greenman /*
2ff9c3a32SDavid Greenman  *      Copyright (c) 1994 Christopher G. Demetriou.
3ff9c3a32SDavid Greenman  *      @(#)Copyright (c) 1994, Simon J. Gerraty.
4ff9c3a32SDavid Greenman  *
5ff9c3a32SDavid Greenman  *      This is free software.  It comes with NO WARRANTY.
6ff9c3a32SDavid Greenman  *      Permission to use, modify and distribute this source code
7ff9c3a32SDavid Greenman  *      is granted subject to the following conditions.
8ff9c3a32SDavid Greenman  *      1/ that the above copyright notice and this notice
9ff9c3a32SDavid Greenman  *      are preserved in all copies and that due credit be given
10ff9c3a32SDavid Greenman  *      to the author.
11ff9c3a32SDavid Greenman  *      2/ that any changes to this code are clearly commented
12ff9c3a32SDavid Greenman  *      as such so that the author does not get blamed for bugs
13ff9c3a32SDavid Greenman  *      other than his own.
14ff9c3a32SDavid Greenman  */
15ff9c3a32SDavid Greenman 
16b51547cfSPhilippe Charnier #include <sys/cdefs.h>
17b51547cfSPhilippe Charnier __FBSDID("$FreeBSD$");
18ff9c3a32SDavid Greenman 
19ff9c3a32SDavid Greenman #include <sys/types.h>
20ff9c3a32SDavid Greenman #include <sys/time.h>
21ff9c3a32SDavid Greenman #include <err.h>
22ff9c3a32SDavid Greenman #include <errno.h>
23497c7558SAndrey A. Chernov #include <langinfo.h>
243efa2f58SPhilippe Charnier #include <locale.h>
25ff9c3a32SDavid Greenman #include <stdio.h>
26ff9c3a32SDavid Greenman #include <stdlib.h>
27ff9c3a32SDavid Greenman #include <string.h>
28db774d60SBruce Evans #include <timeconv.h>
29ff9c3a32SDavid Greenman #include <unistd.h>
303efa2f58SPhilippe Charnier #include <utmp.h>
31ff9c3a32SDavid Greenman 
32ff9c3a32SDavid Greenman /*
33ff9c3a32SDavid Greenman  * this is for our list of currently logged in sessions
34ff9c3a32SDavid Greenman  */
35ff9c3a32SDavid Greenman struct utmp_list {
36ff9c3a32SDavid Greenman 	struct utmp_list *next;
37ff9c3a32SDavid Greenman 	struct utmp usr;
38ff9c3a32SDavid Greenman };
39ff9c3a32SDavid Greenman 
40ff9c3a32SDavid Greenman /*
41ff9c3a32SDavid Greenman  * this is for our list of users that are accumulating time.
42ff9c3a32SDavid Greenman  */
43ff9c3a32SDavid Greenman struct user_list {
44ff9c3a32SDavid Greenman 	struct user_list *next;
45ff9c3a32SDavid Greenman 	char	name[UT_NAMESIZE+1];
46ff9c3a32SDavid Greenman 	time_t	secs;
47ff9c3a32SDavid Greenman };
48ff9c3a32SDavid Greenman 
49ff9c3a32SDavid Greenman /*
50ff9c3a32SDavid Greenman  * this is for chosing whether to ignore a login
51ff9c3a32SDavid Greenman  */
52ff9c3a32SDavid Greenman struct tty_list {
53ff9c3a32SDavid Greenman 	struct tty_list *next;
54ff9c3a32SDavid Greenman 	char	name[UT_LINESIZE+3];
55b51547cfSPhilippe Charnier 	size_t	len;
56ff9c3a32SDavid Greenman 	int	ret;
57ff9c3a32SDavid Greenman };
58ff9c3a32SDavid Greenman 
59ff9c3a32SDavid Greenman /*
60ff9c3a32SDavid Greenman  * globals - yes yuk
61ff9c3a32SDavid Greenman  */
62ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
63ff9c3a32SDavid Greenman static char 	*Console = CONSOLE_TTY;
64ff9c3a32SDavid Greenman #endif
65ff9c3a32SDavid Greenman static time_t	Total = 0;
66ff9c3a32SDavid Greenman static time_t	FirstTime = 0;
67ff9c3a32SDavid Greenman static int	Flags = 0;
68ff9c3a32SDavid Greenman static struct user_list *Users = NULL;
69ff9c3a32SDavid Greenman static struct tty_list *Ttys = NULL;
70ff9c3a32SDavid Greenman 
71ff9c3a32SDavid Greenman #define NEW(type) (type *)malloc(sizeof (type))
72ff9c3a32SDavid Greenman 
73ff9c3a32SDavid Greenman #define	AC_W	1				/* not _PATH_WTMP */
74ff9c3a32SDavid Greenman #define	AC_D	2				/* daily totals (ignore -p) */
75ff9c3a32SDavid Greenman #define	AC_P	4				/* per-user totals */
76ff9c3a32SDavid Greenman #define	AC_U	8				/* specified users only */
77ff9c3a32SDavid Greenman #define	AC_T	16				/* specified ttys only */
78ff9c3a32SDavid Greenman 
79ff9c3a32SDavid Greenman #ifdef DEBUG
80ff9c3a32SDavid Greenman static int Debug = 0;
81ff9c3a32SDavid Greenman #endif
82ff9c3a32SDavid Greenman 
83d89167b4SAlfred Perlstein int			main(int, char **);
84d89167b4SAlfred Perlstein int			ac(FILE *);
85d89167b4SAlfred Perlstein struct tty_list		*add_tty(char *);
86b5a06c25SGarance A Drosehn #ifdef DEBUG
87b5a06c25SGarance A Drosehn const char		*debug_pfx(const struct utmp *, const struct utmp *);
88b5a06c25SGarance A Drosehn #endif
89d89167b4SAlfred Perlstein int			do_tty(char *);
90d89167b4SAlfred Perlstein FILE			*file(const char *);
91d89167b4SAlfred Perlstein struct utmp_list	*log_in(struct utmp_list *, struct utmp *);
92d89167b4SAlfred Perlstein struct utmp_list	*log_out(struct utmp_list *, struct utmp *);
93d89167b4SAlfred Perlstein int			on_console(struct utmp_list *);
94d89167b4SAlfred Perlstein void			show(const char *, time_t);
9554b60d7eSAlfred Perlstein void			show_today(struct user_list *, struct utmp_list *,
9654b60d7eSAlfred Perlstein 			    time_t);
97d89167b4SAlfred Perlstein void			show_users(struct user_list *);
98d89167b4SAlfred Perlstein struct user_list	*update_user(struct user_list *, char *, time_t);
99d89167b4SAlfred Perlstein void			usage(void);
100ff9c3a32SDavid Greenman 
101ff9c3a32SDavid Greenman /*
102ff9c3a32SDavid Greenman  * open wtmp or die
103ff9c3a32SDavid Greenman  */
104ff9c3a32SDavid Greenman FILE *
1057f761c52SGarance A Drosehn file(const char *name)
106ff9c3a32SDavid Greenman {
107ff9c3a32SDavid Greenman 	FILE *fp;
108ff9c3a32SDavid Greenman 
109ff9c3a32SDavid Greenman 	if ((fp = fopen(name, "r")) == NULL)
110ff9c3a32SDavid Greenman 		err(1, "%s", name);
111ff9c3a32SDavid Greenman 	/* in case we want to discriminate */
112ff9c3a32SDavid Greenman 	if (strcmp(_PATH_WTMP, name))
113ff9c3a32SDavid Greenman 		Flags |= AC_W;
114ff9c3a32SDavid Greenman 	return fp;
115ff9c3a32SDavid Greenman }
116ff9c3a32SDavid Greenman 
117ff9c3a32SDavid Greenman struct tty_list *
1187f761c52SGarance A Drosehn add_tty(char *name)
119ff9c3a32SDavid Greenman {
120ff9c3a32SDavid Greenman 	struct tty_list *tp;
1212adaffb0SGarance A Drosehn 	char *rcp;
122ff9c3a32SDavid Greenman 
123ff9c3a32SDavid Greenman 	Flags |= AC_T;
124ff9c3a32SDavid Greenman 
125ff9c3a32SDavid Greenman 	if ((tp = NEW(struct tty_list)) == NULL)
126c3737d34SPhilippe Charnier 		errx(1, "malloc failed");
127ff9c3a32SDavid Greenman 	tp->len = 0;				/* full match */
128ff9c3a32SDavid Greenman 	tp->ret = 1;				/* do if match */
129ff9c3a32SDavid Greenman 	if (*name == '!') {			/* don't do if match */
130ff9c3a32SDavid Greenman 		tp->ret = 0;
131ff9c3a32SDavid Greenman 		name++;
132ff9c3a32SDavid Greenman 	}
133fd96447aSKris Kennaway 	strlcpy(tp->name, name, sizeof (tp->name));
134ff9c3a32SDavid Greenman 	if ((rcp = strchr(tp->name, '*')) != NULL) {	/* wild card */
135ff9c3a32SDavid Greenman 		*rcp = '\0';
136ff9c3a32SDavid Greenman 		tp->len = strlen(tp->name);	/* match len bytes only */
137ff9c3a32SDavid Greenman 	}
138ff9c3a32SDavid Greenman 	tp->next = Ttys;
139ff9c3a32SDavid Greenman 	Ttys = tp;
140ff9c3a32SDavid Greenman 	return Ttys;
141ff9c3a32SDavid Greenman }
142ff9c3a32SDavid Greenman 
143ff9c3a32SDavid Greenman /*
144ff9c3a32SDavid Greenman  * should we process the named tty?
145ff9c3a32SDavid Greenman  */
146ff9c3a32SDavid Greenman int
1477f761c52SGarance A Drosehn do_tty(char *name)
148ff9c3a32SDavid Greenman {
149ff9c3a32SDavid Greenman 	struct tty_list *tp;
150ff9c3a32SDavid Greenman 	int def_ret = 0;
151ff9c3a32SDavid Greenman 
152ff9c3a32SDavid Greenman 	for (tp = Ttys; tp != NULL; tp = tp->next) {
153ff9c3a32SDavid Greenman 		if (tp->ret == 0)		/* specific don't */
154ff9c3a32SDavid Greenman 			def_ret = 1;		/* default do */
155ff9c3a32SDavid Greenman 		if (tp->len != 0) {
156ff9c3a32SDavid Greenman 			if (strncmp(name, tp->name, tp->len) == 0)
157ff9c3a32SDavid Greenman 				return tp->ret;
158ff9c3a32SDavid Greenman 		} else {
159ff9c3a32SDavid Greenman 			if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
160ff9c3a32SDavid Greenman 				return tp->ret;
161ff9c3a32SDavid Greenman 		}
162ff9c3a32SDavid Greenman 	}
163ff9c3a32SDavid Greenman 	return def_ret;
164ff9c3a32SDavid Greenman }
165ff9c3a32SDavid Greenman 
166ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
167ff9c3a32SDavid Greenman /*
168ff9c3a32SDavid Greenman  * is someone logged in on Console?
169ff9c3a32SDavid Greenman  */
170ff9c3a32SDavid Greenman int
1717f761c52SGarance A Drosehn on_console(struct utmp_list *head)
172ff9c3a32SDavid Greenman {
173ff9c3a32SDavid Greenman 	struct utmp_list *up;
174ff9c3a32SDavid Greenman 
175ff9c3a32SDavid Greenman 	for (up = head; up; up = up->next) {
176ff9c3a32SDavid Greenman 		if (strncmp(up->usr.ut_line, Console,
177ff9c3a32SDavid Greenman 		    sizeof (up->usr.ut_line)) == 0)
178ff9c3a32SDavid Greenman 			return 1;
179ff9c3a32SDavid Greenman 	}
180ff9c3a32SDavid Greenman 	return 0;
181ff9c3a32SDavid Greenman }
182ff9c3a32SDavid Greenman #endif
183ff9c3a32SDavid Greenman 
184ff9c3a32SDavid Greenman /*
185ff9c3a32SDavid Greenman  * update user's login time
186ff9c3a32SDavid Greenman  */
187ff9c3a32SDavid Greenman struct user_list *
1887f761c52SGarance A Drosehn update_user(struct user_list *head, char *name, time_t secs)
189ff9c3a32SDavid Greenman {
190ff9c3a32SDavid Greenman 	struct user_list *up;
191ff9c3a32SDavid Greenman 
192ff9c3a32SDavid Greenman 	for (up = head; up != NULL; up = up->next) {
1933f6dabb2SAndrey A. Chernov 		if (strncmp(up->name, name, UT_NAMESIZE) == 0) {
194ff9c3a32SDavid Greenman 			up->secs += secs;
195ff9c3a32SDavid Greenman 			Total += secs;
196ff9c3a32SDavid Greenman 			return head;
197ff9c3a32SDavid Greenman 		}
198ff9c3a32SDavid Greenman 	}
199ff9c3a32SDavid Greenman 	/*
200ff9c3a32SDavid Greenman 	 * not found so add new user unless specified users only
201ff9c3a32SDavid Greenman 	 */
202ff9c3a32SDavid Greenman 	if (Flags & AC_U)
203ff9c3a32SDavid Greenman 		return head;
204ff9c3a32SDavid Greenman 
205ff9c3a32SDavid Greenman 	if ((up = NEW(struct user_list)) == NULL)
206c3737d34SPhilippe Charnier 		errx(1, "malloc failed");
207ff9c3a32SDavid Greenman 	up->next = head;
208fd96447aSKris Kennaway 	strlcpy(up->name, name, sizeof (up->name));
209ff9c3a32SDavid Greenman 	up->secs = secs;
210ff9c3a32SDavid Greenman 	Total += secs;
211ff9c3a32SDavid Greenman 	return up;
212ff9c3a32SDavid Greenman }
213ff9c3a32SDavid Greenman 
214b5a06c25SGarance A Drosehn #ifdef DEBUG
215b5a06c25SGarance A Drosehn /*
216b5a06c25SGarance A Drosehn  * Create a string which is the standard prefix for a debug line.  It
217b5a06c25SGarance A Drosehn  * includes a timestamp (perhaps with year), device-name, and user-name.
218b5a06c25SGarance A Drosehn  */
219b5a06c25SGarance A Drosehn const char *
220b5a06c25SGarance A Drosehn debug_pfx(const struct utmp *event_up, const struct utmp *userinf_up)
221b5a06c25SGarance A Drosehn {
222b5a06c25SGarance A Drosehn 	static char str_result[40+UT_LINESIZE+UT_NAMESIZE];
223b5a06c25SGarance A Drosehn 	static char thisyear[5];
224b5a06c25SGarance A Drosehn 	size_t maxcopy;
225b5a06c25SGarance A Drosehn 	time_t ut_timecopy;
226b5a06c25SGarance A Drosehn 
227b5a06c25SGarance A Drosehn 	if (thisyear[0] == '\0') {
228b5a06c25SGarance A Drosehn 		/* Figure out what "this year" is. */
229b5a06c25SGarance A Drosehn 		time(&ut_timecopy);
230b5a06c25SGarance A Drosehn 		strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result));
231b5a06c25SGarance A Drosehn 		strlcpy(thisyear, &str_result[20], sizeof(thisyear));
232b5a06c25SGarance A Drosehn 	}
233b5a06c25SGarance A Drosehn 
234b5a06c25SGarance A Drosehn 	if (event_up->ut_time == 0)
235b5a06c25SGarance A Drosehn 		strlcpy(str_result, "*ZeroTime* --:--:-- ", sizeof(str_result));
236b5a06c25SGarance A Drosehn 	else {
237b5a06c25SGarance A Drosehn 		/*
238b5a06c25SGarance A Drosehn 		* The type of utmp.ut_time is not necessary type time_t, as
239b5a06c25SGarance A Drosehn 		* it is explicitly defined as type int32_t.  Copy the value
240b5a06c25SGarance A Drosehn 		* for platforms where sizeof(time_t) != sizeof(int32_t).
241b5a06c25SGarance A Drosehn 		*/
242b5a06c25SGarance A Drosehn 		ut_timecopy = _time32_to_time(event_up->ut_time);
243b5a06c25SGarance A Drosehn 		strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result));
244b5a06c25SGarance A Drosehn 		/*
245b5a06c25SGarance A Drosehn 		 * Include the year, if it is not the same year as "now".
246b5a06c25SGarance A Drosehn 		 */
247b5a06c25SGarance A Drosehn 		if (strncmp(&str_result[20], thisyear, 4) == 0)
248b5a06c25SGarance A Drosehn 			str_result[20] = '\0';
249b5a06c25SGarance A Drosehn 		else {
250b5a06c25SGarance A Drosehn 			str_result[24] = ' ';		/* Replace a '\n' */
251b5a06c25SGarance A Drosehn 			str_result[25] = '\0';
252b5a06c25SGarance A Drosehn 		}
253b5a06c25SGarance A Drosehn 	}
254b5a06c25SGarance A Drosehn 
255b5a06c25SGarance A Drosehn 	if (userinf_up->ut_line[0] == '\0')
256b5a06c25SGarance A Drosehn 		strlcat(str_result, "NoDev", sizeof(str_result));
257b5a06c25SGarance A Drosehn 	else {
258b5a06c25SGarance A Drosehn 		/* ut_line is not necessarily null-terminated. */
259b5a06c25SGarance A Drosehn 		maxcopy = strlen(str_result) + UT_LINESIZE + 1;
260b5a06c25SGarance A Drosehn 		if (maxcopy > sizeof(str_result))
261b5a06c25SGarance A Drosehn 			maxcopy = sizeof(str_result);
262b5a06c25SGarance A Drosehn 		strlcat(str_result, userinf_up->ut_line, maxcopy);
263b5a06c25SGarance A Drosehn 	}
264b5a06c25SGarance A Drosehn 	strlcat(str_result, ": ", sizeof(str_result));
265b5a06c25SGarance A Drosehn 
266b5a06c25SGarance A Drosehn 	if (userinf_up->ut_name[0] == '\0')
267b5a06c25SGarance A Drosehn 		strlcat(str_result, "LogOff", sizeof(str_result));
268b5a06c25SGarance A Drosehn 	else {
269b5a06c25SGarance A Drosehn 		/* ut_name is not necessarily null-terminated. */
270b5a06c25SGarance A Drosehn 		maxcopy = strlen(str_result) + UT_NAMESIZE + 1;
271b5a06c25SGarance A Drosehn 		if (maxcopy > sizeof(str_result))
272b5a06c25SGarance A Drosehn 			maxcopy = sizeof(str_result);
273b5a06c25SGarance A Drosehn 		strlcat(str_result, userinf_up->ut_name, maxcopy);
274b5a06c25SGarance A Drosehn 	}
275b5a06c25SGarance A Drosehn 
276b5a06c25SGarance A Drosehn 	return (str_result);
277b5a06c25SGarance A Drosehn }
278b5a06c25SGarance A Drosehn #endif
279b5a06c25SGarance A Drosehn 
280ff9c3a32SDavid Greenman int
2817f761c52SGarance A Drosehn main(int argc, char *argv[])
282ff9c3a32SDavid Greenman {
283ff9c3a32SDavid Greenman 	FILE *fp;
284ff9c3a32SDavid Greenman 	int c;
285ff9c3a32SDavid Greenman 
2865877948cSAndrey A. Chernov 	(void) setlocale(LC_TIME, "");
2875877948cSAndrey A. Chernov 
288ff9c3a32SDavid Greenman 	fp = NULL;
2896c3f552aSWarner Losh 	while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) {
290ff9c3a32SDavid Greenman 		switch (c) {
291ff9c3a32SDavid Greenman #ifdef DEBUG
292ff9c3a32SDavid Greenman 		case 'D':
293ff9c3a32SDavid Greenman 			Debug++;
294ff9c3a32SDavid Greenman 			break;
295ff9c3a32SDavid Greenman #endif
296ff9c3a32SDavid Greenman 		case 'c':
297ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
298ff9c3a32SDavid Greenman 			Console = optarg;
299ff9c3a32SDavid Greenman #else
300ff9c3a32SDavid Greenman 			usage();		/* XXX */
301ff9c3a32SDavid Greenman #endif
302ff9c3a32SDavid Greenman 			break;
303ff9c3a32SDavid Greenman 		case 'd':
304ff9c3a32SDavid Greenman 			Flags |= AC_D;
305ff9c3a32SDavid Greenman 			break;
306ff9c3a32SDavid Greenman 		case 'p':
307ff9c3a32SDavid Greenman 			Flags |= AC_P;
308ff9c3a32SDavid Greenman 			break;
309ff9c3a32SDavid Greenman 		case 't':			/* only do specified ttys */
310ff9c3a32SDavid Greenman 			add_tty(optarg);
311ff9c3a32SDavid Greenman 			break;
312ff9c3a32SDavid Greenman 		case 'w':
313ff9c3a32SDavid Greenman 			fp = file(optarg);
314ff9c3a32SDavid Greenman 			break;
315ff9c3a32SDavid Greenman 		case '?':
316ff9c3a32SDavid Greenman 		default:
317ff9c3a32SDavid Greenman 			usage();
318ff9c3a32SDavid Greenman 			break;
319ff9c3a32SDavid Greenman 		}
320ff9c3a32SDavid Greenman 	}
321ff9c3a32SDavid Greenman 	if (optind < argc) {
322ff9c3a32SDavid Greenman 		/*
323ff9c3a32SDavid Greenman 		 * initialize user list
324ff9c3a32SDavid Greenman 		 */
325ff9c3a32SDavid Greenman 		for (; optind < argc; optind++) {
326b51547cfSPhilippe Charnier 			Users = update_user(Users, argv[optind], (time_t)0);
327ff9c3a32SDavid Greenman 		}
328ff9c3a32SDavid Greenman 		Flags |= AC_U;			/* freeze user list */
329ff9c3a32SDavid Greenman 	}
330ff9c3a32SDavid Greenman 	if (Flags & AC_D)
331ff9c3a32SDavid Greenman 		Flags &= ~AC_P;
332ff9c3a32SDavid Greenman 	if (fp == NULL) {
333ff9c3a32SDavid Greenman 		/*
334ff9c3a32SDavid Greenman 		 * if _PATH_WTMP does not exist, exit quietly
335ff9c3a32SDavid Greenman 		 */
336ff9c3a32SDavid Greenman 		if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT)
337ff9c3a32SDavid Greenman 			return 0;
338ff9c3a32SDavid Greenman 
339ff9c3a32SDavid Greenman 		fp = file(_PATH_WTMP);
340ff9c3a32SDavid Greenman 	}
341ff9c3a32SDavid Greenman 	ac(fp);
342ff9c3a32SDavid Greenman 
343ff9c3a32SDavid Greenman 	return 0;
344ff9c3a32SDavid Greenman }
345ff9c3a32SDavid Greenman 
346ff9c3a32SDavid Greenman /*
347ff9c3a32SDavid Greenman  * print login time in decimal hours
348ff9c3a32SDavid Greenman  */
349ff9c3a32SDavid Greenman void
3507f761c52SGarance A Drosehn show(const char *name, time_t secs)
351ff9c3a32SDavid Greenman {
352ff9c3a32SDavid Greenman 	(void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
353ff9c3a32SDavid Greenman 	    ((double)secs / 3600));
354ff9c3a32SDavid Greenman }
355ff9c3a32SDavid Greenman 
356ff9c3a32SDavid Greenman void
3577f761c52SGarance A Drosehn show_users(struct user_list *list)
358ff9c3a32SDavid Greenman {
359ff9c3a32SDavid Greenman 	struct user_list *lp;
360ff9c3a32SDavid Greenman 
361ff9c3a32SDavid Greenman 	for (lp = list; lp; lp = lp->next)
362ff9c3a32SDavid Greenman 		show(lp->name, lp->secs);
363ff9c3a32SDavid Greenman }
364ff9c3a32SDavid Greenman 
365ff9c3a32SDavid Greenman /*
366ff9c3a32SDavid Greenman  * print total login time for 24hr period in decimal hours
367ff9c3a32SDavid Greenman  */
368ff9c3a32SDavid Greenman void
3697f761c52SGarance A Drosehn show_today(struct user_list *users, struct utmp_list *logins, time_t secs)
370ff9c3a32SDavid Greenman {
371ff9c3a32SDavid Greenman 	struct user_list *up;
372ff9c3a32SDavid Greenman 	struct utmp_list *lp;
373ff9c3a32SDavid Greenman 	char date[64];
374ff9c3a32SDavid Greenman 	time_t yesterday = secs - 1;
375497c7558SAndrey A. Chernov 	static int d_first = -1;
376ff9c3a32SDavid Greenman 
377497c7558SAndrey A. Chernov 	if (d_first < 0)
378497c7558SAndrey A. Chernov 		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
379497c7558SAndrey A. Chernov 	(void)strftime(date, sizeof (date),
380497c7558SAndrey A. Chernov 		       d_first ? "%e %b  total" : "%b %e  total",
381ff9c3a32SDavid Greenman 		       localtime(&yesterday));
382ff9c3a32SDavid Greenman 
383ff9c3a32SDavid Greenman 	/* restore the missing second */
384ff9c3a32SDavid Greenman 	yesterday++;
385ff9c3a32SDavid Greenman 
386ff9c3a32SDavid Greenman 	for (lp = logins; lp != NULL; lp = lp->next) {
387ff9c3a32SDavid Greenman 		secs = yesterday - lp->usr.ut_time;
388ff9c3a32SDavid Greenman 		Users = update_user(Users, lp->usr.ut_name, secs);
389ff9c3a32SDavid Greenman 		lp->usr.ut_time = yesterday;	/* as if they just logged in */
390ff9c3a32SDavid Greenman 	}
391ff9c3a32SDavid Greenman 	secs = 0;
392ff9c3a32SDavid Greenman 	for (up = users; up != NULL; up = up->next) {
393ff9c3a32SDavid Greenman 		secs += up->secs;
394ff9c3a32SDavid Greenman 		up->secs = 0;			/* for next day */
395ff9c3a32SDavid Greenman 	}
396ff9c3a32SDavid Greenman 	if (secs)
397ff9c3a32SDavid Greenman 		(void)printf("%s %11.2f\n", date, ((double)secs / 3600));
398ff9c3a32SDavid Greenman }
399ff9c3a32SDavid Greenman 
400ff9c3a32SDavid Greenman /*
401ff9c3a32SDavid Greenman  * log a user out and update their times.
402ff9c3a32SDavid Greenman  * if ut_line is "~", we log all users out as the system has
403ff9c3a32SDavid Greenman  * been shut down.
404ff9c3a32SDavid Greenman  */
405ff9c3a32SDavid Greenman struct utmp_list *
4067f761c52SGarance A Drosehn log_out(struct utmp_list *head, struct utmp *up)
407ff9c3a32SDavid Greenman {
408ff9c3a32SDavid Greenman 	struct utmp_list *lp, *lp2, *tlp;
409ff9c3a32SDavid Greenman 	time_t secs;
410ff9c3a32SDavid Greenman 
411ff9c3a32SDavid Greenman 	for (lp = head, lp2 = NULL; lp != NULL; )
412ff9c3a32SDavid Greenman 		if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line,
413ff9c3a32SDavid Greenman 		    sizeof (up->ut_line)) == 0) {
414ff9c3a32SDavid Greenman 			secs = up->ut_time - lp->usr.ut_time;
415ff9c3a32SDavid Greenman 			Users = update_user(Users, lp->usr.ut_name, secs);
416ff9c3a32SDavid Greenman #ifdef DEBUG
417ff9c3a32SDavid Greenman 			if (Debug)
418b5a06c25SGarance A Drosehn 				printf("%s logged out (%2d:%02d:%02d)\n",
419b5a06c25SGarance A Drosehn 				    debug_pfx(up, &lp->usr), (int)(secs / 3600),
420b5a06c25SGarance A Drosehn 				    (int)((secs % 3600) / 60),
421b5a06c25SGarance A Drosehn 				    (int)(secs % 60));
422ff9c3a32SDavid Greenman #endif
423ff9c3a32SDavid Greenman 			/*
424ff9c3a32SDavid Greenman 			 * now lose it
425ff9c3a32SDavid Greenman 			 */
426ff9c3a32SDavid Greenman 			tlp = lp;
427ff9c3a32SDavid Greenman 			lp = lp->next;
428ff9c3a32SDavid Greenman 			if (tlp == head)
429ff9c3a32SDavid Greenman 				head = lp;
430ff9c3a32SDavid Greenman 			else if (lp2 != NULL)
431ff9c3a32SDavid Greenman 				lp2->next = lp;
432ff9c3a32SDavid Greenman 			free(tlp);
433ff9c3a32SDavid Greenman 		} else {
434ff9c3a32SDavid Greenman 			lp2 = lp;
435ff9c3a32SDavid Greenman 			lp = lp->next;
436ff9c3a32SDavid Greenman 		}
437ff9c3a32SDavid Greenman 	return head;
438ff9c3a32SDavid Greenman }
439ff9c3a32SDavid Greenman 
440ff9c3a32SDavid Greenman 
441ff9c3a32SDavid Greenman /*
442ff9c3a32SDavid Greenman  * if do_tty says ok, login a user
443ff9c3a32SDavid Greenman  */
444ff9c3a32SDavid Greenman struct utmp_list *
4457f761c52SGarance A Drosehn log_in(struct utmp_list *head, struct utmp *up)
446ff9c3a32SDavid Greenman {
447ff9c3a32SDavid Greenman 	struct utmp_list *lp;
448ff9c3a32SDavid Greenman 
449ff9c3a32SDavid Greenman 	/*
450ff9c3a32SDavid Greenman 	 * this could be a login. if we're not dealing with
451ff9c3a32SDavid Greenman 	 * the console name, say it is.
452ff9c3a32SDavid Greenman 	 *
453ff9c3a32SDavid Greenman 	 * If we are, and if ut_host==":0.0" we know that it
454ff9c3a32SDavid Greenman 	 * isn't a real login. _But_ if we have not yet recorded
455ff9c3a32SDavid Greenman 	 * someone being logged in on Console - due to the wtmp
456ff9c3a32SDavid Greenman 	 * file starting after they logged in, we'll pretend they
457ff9c3a32SDavid Greenman 	 * logged in, at the start of the wtmp file.
458ff9c3a32SDavid Greenman 	 */
459ff9c3a32SDavid Greenman 
460ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
461ff9c3a32SDavid Greenman 	if (up->ut_host[0] == ':') {
462ff9c3a32SDavid Greenman 		/*
463ff9c3a32SDavid Greenman 		 * SunOS 4.0.2 does not treat ":0.0" as special but we
464ff9c3a32SDavid Greenman 		 * do.
465ff9c3a32SDavid Greenman 		 */
466ff9c3a32SDavid Greenman 		if (on_console(head))
467ff9c3a32SDavid Greenman 			return head;
468ff9c3a32SDavid Greenman 		/*
469ff9c3a32SDavid Greenman 		 * ok, no recorded login, so they were here when wtmp
470ff9c3a32SDavid Greenman 		 * started!  Adjust ut_time!
471ff9c3a32SDavid Greenman 		 */
472ff9c3a32SDavid Greenman 		up->ut_time = FirstTime;
473ff9c3a32SDavid Greenman 		/*
474ff9c3a32SDavid Greenman 		 * this allows us to pick the right logout
475ff9c3a32SDavid Greenman 		 */
476fd96447aSKris Kennaway 		strlcpy(up->ut_line, Console, sizeof (up->ut_line));
477ff9c3a32SDavid Greenman 	}
478ff9c3a32SDavid Greenman #endif
479ff9c3a32SDavid Greenman 	/*
480ff9c3a32SDavid Greenman 	 * If we are doing specified ttys only, we ignore
481ff9c3a32SDavid Greenman 	 * anything else.
482ff9c3a32SDavid Greenman 	 */
483ff9c3a32SDavid Greenman 	if (Flags & AC_T)
484ff9c3a32SDavid Greenman 		if (!do_tty(up->ut_line))
485ff9c3a32SDavid Greenman 			return head;
486ff9c3a32SDavid Greenman 
487ff9c3a32SDavid Greenman 	/*
488ff9c3a32SDavid Greenman 	 * go ahead and log them in
489ff9c3a32SDavid Greenman 	 */
490ff9c3a32SDavid Greenman 	if ((lp = NEW(struct utmp_list)) == NULL)
491c3737d34SPhilippe Charnier 		errx(1, "malloc failed");
492ff9c3a32SDavid Greenman 	lp->next = head;
493ff9c3a32SDavid Greenman 	head = lp;
494ff9c3a32SDavid Greenman 	memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
495ff9c3a32SDavid Greenman #ifdef DEBUG
496ff9c3a32SDavid Greenman 	if (Debug) {
497b5a06c25SGarance A Drosehn 		printf("%s logged in", debug_pfx(&lp->usr, up));
498ff9c3a32SDavid Greenman 		if (*up->ut_host)
499b5a06c25SGarance A Drosehn 			printf(" (%-.*s)", (int)sizeof(up->ut_host),
500b5a06c25SGarance A Drosehn 			    up->ut_host);
501ff9c3a32SDavid Greenman 		putchar('\n');
502ff9c3a32SDavid Greenman 	}
503ff9c3a32SDavid Greenman #endif
504ff9c3a32SDavid Greenman 	return head;
505ff9c3a32SDavid Greenman }
506ff9c3a32SDavid Greenman 
507ff9c3a32SDavid Greenman int
5087f761c52SGarance A Drosehn ac(FILE	*fp)
509ff9c3a32SDavid Greenman {
510ff9c3a32SDavid Greenman 	struct utmp_list *lp, *head = NULL;
511ff9c3a32SDavid Greenman 	struct utmp usr;
512ff9c3a32SDavid Greenman 	struct tm *ltm;
513e3b218cdSGarance A Drosehn 	time_t prev_secs, secs, ut_timecopy;
514e3b218cdSGarance A Drosehn 	int day, rfound, tchanged, tskipped;
515ff9c3a32SDavid Greenman 
516e3b218cdSGarance A Drosehn 	day = -1;
517e3b218cdSGarance A Drosehn 	prev_secs = 1;			/* Minimum acceptable date == 1970 */
518e3b218cdSGarance A Drosehn 	rfound = tchanged = tskipped = 0;
519e3b218cdSGarance A Drosehn 	secs = 0;
520ff9c3a32SDavid Greenman 	while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
521e3b218cdSGarance A Drosehn 		rfound++;
522e3b218cdSGarance A Drosehn 		/*
523e3b218cdSGarance A Drosehn 		 * The type of utmp.ut_time is not necessary type time_t, as
524e3b218cdSGarance A Drosehn 		 * it is explicitly defined as type int32_t.  Copy the value
525e3b218cdSGarance A Drosehn 		 * for platforms where sizeof(time_t) != size(int32_t).
526e3b218cdSGarance A Drosehn 		 */
527e3b218cdSGarance A Drosehn 		ut_timecopy = _time32_to_time(usr.ut_time);
528e3b218cdSGarance A Drosehn 		/*
529e3b218cdSGarance A Drosehn 		 * With sparc64 using 64-bit time_t's, there is some system
530e3b218cdSGarance A Drosehn 		 * routine which sets ut_time==0 (the high-order word of a
531e3b218cdSGarance A Drosehn 		 * 64-bit time) instead of a 32-bit time value.  For those
532e3b218cdSGarance A Drosehn 		 * wtmp files, it is "more-accurate" to substitute the most-
533e3b218cdSGarance A Drosehn 		 * recent time found, instead of throwing away the entire
534e3b218cdSGarance A Drosehn 		 * record.  While it is still just a guess, it is a better
535e3b218cdSGarance A Drosehn 		 * guess than throwing away a log-off record and therefore
536e3b218cdSGarance A Drosehn 		 * counting a session as if it continued to the end of the
537e3b218cdSGarance A Drosehn 		 * month, or the next system-reboot.
538e3b218cdSGarance A Drosehn 		 */
539e3b218cdSGarance A Drosehn 		if (ut_timecopy == 0 && prev_secs > 1) {
540e3b218cdSGarance A Drosehn #ifdef DEBUG
541e3b218cdSGarance A Drosehn 			if (Debug)
542e3b218cdSGarance A Drosehn 				printf("%s - date changed to: %s",
543e3b218cdSGarance A Drosehn 				    debug_pfx(&usr, &usr), ctime(&prev_secs));
544e3b218cdSGarance A Drosehn #endif
545e3b218cdSGarance A Drosehn 			tchanged++;
546e3b218cdSGarance A Drosehn 			usr.ut_time = ut_timecopy = prev_secs;
547e3b218cdSGarance A Drosehn 		}
548e3b218cdSGarance A Drosehn 		/*
549e3b218cdSGarance A Drosehn 		 * Skip records where the time goes backwards.
550e3b218cdSGarance A Drosehn 		 */
551e3b218cdSGarance A Drosehn 		if (ut_timecopy < prev_secs) {
552e3b218cdSGarance A Drosehn #ifdef DEBUG
553e3b218cdSGarance A Drosehn 			if (Debug)
554e3b218cdSGarance A Drosehn 				printf("%s - bad date, record skipped\n",
555e3b218cdSGarance A Drosehn 				    debug_pfx(&usr, &usr));
556e3b218cdSGarance A Drosehn #endif
557e3b218cdSGarance A Drosehn 			tskipped++;
558e3b218cdSGarance A Drosehn 			continue;	/* Skip this invalid record. */
559e3b218cdSGarance A Drosehn 		}
560e3b218cdSGarance A Drosehn 		prev_secs = ut_timecopy;
561e3b218cdSGarance A Drosehn 
562ff9c3a32SDavid Greenman 		if (!FirstTime)
563e3b218cdSGarance A Drosehn 			FirstTime = ut_timecopy;
564ff9c3a32SDavid Greenman 		if (Flags & AC_D) {
565e3b218cdSGarance A Drosehn 			ltm = localtime(&ut_timecopy);
566ff9c3a32SDavid Greenman 			if (day >= 0 && day != ltm->tm_yday) {
567ff9c3a32SDavid Greenman 				day = ltm->tm_yday;
568ff9c3a32SDavid Greenman 				/*
569ff9c3a32SDavid Greenman 				 * print yesterday's total
570ff9c3a32SDavid Greenman 				 */
571e3b218cdSGarance A Drosehn 				secs = ut_timecopy;
572ff9c3a32SDavid Greenman 				secs -= ltm->tm_sec;
573ff9c3a32SDavid Greenman 				secs -= 60 * ltm->tm_min;
574ff9c3a32SDavid Greenman 				secs -= 3600 * ltm->tm_hour;
575ff9c3a32SDavid Greenman 				show_today(Users, head, secs);
576ff9c3a32SDavid Greenman 			} else
577ff9c3a32SDavid Greenman 				day = ltm->tm_yday;
578ff9c3a32SDavid Greenman 		}
579ff9c3a32SDavid Greenman 		switch(*usr.ut_line) {
580ff9c3a32SDavid Greenman 		case '|':
581e3b218cdSGarance A Drosehn 			secs = ut_timecopy;
582ff9c3a32SDavid Greenman 			break;
583ff9c3a32SDavid Greenman 		case '{':
584e3b218cdSGarance A Drosehn 			secs -= ut_timecopy;
585ff9c3a32SDavid Greenman 			/*
586ff9c3a32SDavid Greenman 			 * adjust time for those logged in
587ff9c3a32SDavid Greenman 			 */
588ff9c3a32SDavid Greenman 			for (lp = head; lp != NULL; lp = lp->next)
589ff9c3a32SDavid Greenman 				lp->usr.ut_time -= secs;
590ff9c3a32SDavid Greenman 			break;
591ff9c3a32SDavid Greenman 		case '~':			/* reboot or shutdown */
592ff9c3a32SDavid Greenman 			head = log_out(head, &usr);
593e3b218cdSGarance A Drosehn 			FirstTime = ut_timecopy; /* shouldn't be needed */
594ff9c3a32SDavid Greenman 			break;
595ff9c3a32SDavid Greenman 		default:
596ff9c3a32SDavid Greenman 			/*
5971ce46cefSPoul-Henning Kamp 			 * if they came in on tty[p-sP-S]*, then it is only
598ff9c3a32SDavid Greenman 			 * a login session if the ut_host field is non-empty
599ff9c3a32SDavid Greenman 			 */
600ff9c3a32SDavid Greenman 			if (*usr.ut_name) {
601974a54a2SJonathan Chen 				if (strncmp(usr.ut_line, "tty", 3) == 0 ||
602974a54a2SJonathan Chen 				    strchr("pqrsPQRS", usr.ut_line[3]) != 0 ||
603ff9c3a32SDavid Greenman 				    *usr.ut_host != '\0')
604ff9c3a32SDavid Greenman 					head = log_in(head, &usr);
605b5a06c25SGarance A Drosehn #ifdef DEBUG
606b5a06c25SGarance A Drosehn 				else if (Debug > 1)
607b5a06c25SGarance A Drosehn 					/* Things such as 'screen' sessions. */
608b5a06c25SGarance A Drosehn 					printf("%s - record ignored\n",
609b5a06c25SGarance A Drosehn 					    debug_pfx(&usr, &usr));
610b5a06c25SGarance A Drosehn #endif
611ff9c3a32SDavid Greenman 			} else
612ff9c3a32SDavid Greenman 				head = log_out(head, &usr);
613ff9c3a32SDavid Greenman 			break;
614ff9c3a32SDavid Greenman 		}
615ff9c3a32SDavid Greenman 	}
616ff9c3a32SDavid Greenman 	(void)fclose(fp);
6172a30154fSSteve Price 	if (!(Flags & AC_W))
618ff9c3a32SDavid Greenman 		usr.ut_time = time((time_t *)0);
619ff9c3a32SDavid Greenman 	(void)strcpy(usr.ut_line, "~");
620ff9c3a32SDavid Greenman 
621ff9c3a32SDavid Greenman 	if (Flags & AC_D) {
622e3b218cdSGarance A Drosehn 		ut_timecopy = _time32_to_time(usr.ut_time);
623e3b218cdSGarance A Drosehn 		ltm = localtime(&ut_timecopy);
624ff9c3a32SDavid Greenman 		if (day >= 0 && day != ltm->tm_yday) {
625ff9c3a32SDavid Greenman 			/*
626ff9c3a32SDavid Greenman 			 * print yesterday's total
627ff9c3a32SDavid Greenman 			 */
628e3b218cdSGarance A Drosehn 			secs = ut_timecopy;
629ff9c3a32SDavid Greenman 			secs -= ltm->tm_sec;
630ff9c3a32SDavid Greenman 			secs -= 60 * ltm->tm_min;
631ff9c3a32SDavid Greenman 			secs -= 3600 * ltm->tm_hour;
632ff9c3a32SDavid Greenman 			show_today(Users, head, secs);
633ff9c3a32SDavid Greenman 		}
634ff9c3a32SDavid Greenman 	}
635ff9c3a32SDavid Greenman 	/*
636ff9c3a32SDavid Greenman 	 * anyone still logged in gets time up to now
637ff9c3a32SDavid Greenman 	 */
638ff9c3a32SDavid Greenman 	head = log_out(head, &usr);
639ff9c3a32SDavid Greenman 
640ff9c3a32SDavid Greenman 	if (Flags & AC_D)
641ff9c3a32SDavid Greenman 		show_today(Users, head, time((time_t *)0));
642ff9c3a32SDavid Greenman 	else {
643ff9c3a32SDavid Greenman 		if (Flags & AC_P)
644ff9c3a32SDavid Greenman 			show_users(Users);
645ff9c3a32SDavid Greenman 		show("total", Total);
646ff9c3a32SDavid Greenman 	}
647e3b218cdSGarance A Drosehn 
648e3b218cdSGarance A Drosehn 	if (tskipped > 0)
649e3b218cdSGarance A Drosehn 		printf("(Skipped %d of %d records due to invalid time values)\n",
650e3b218cdSGarance A Drosehn 		    tskipped, rfound);
651e3b218cdSGarance A Drosehn 	if (tchanged > 0)
652e3b218cdSGarance A Drosehn 		printf("(Changed %d of %d records to have a more likely time value)\n",
653e3b218cdSGarance A Drosehn 		    tchanged, rfound);
654e3b218cdSGarance A Drosehn 
655ff9c3a32SDavid Greenman 	return 0;
656ff9c3a32SDavid Greenman }
657ff9c3a32SDavid Greenman 
658ff9c3a32SDavid Greenman void
6597f761c52SGarance A Drosehn usage(void)
660ff9c3a32SDavid Greenman {
661ff9c3a32SDavid Greenman 	(void)fprintf(stderr,
662ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
663ff9c3a32SDavid Greenman 	    "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
664ff9c3a32SDavid Greenman #else
665ff9c3a32SDavid Greenman 	    "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");
666ff9c3a32SDavid Greenman #endif
667ff9c3a32SDavid Greenman 	exit(1);
668ff9c3a32SDavid Greenman }
669