xref: /freebsd/usr.sbin/ac/ac.c (revision cf4d4e152c0c5fd6d2d619fdb9e2bf72a5ecedbc)
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>
30cf4d4e15SEd Schouten #include <utmpx.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;
37cf4d4e15SEd Schouten 	struct utmpx 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;
45cf4d4e15SEd Schouten 	char	name[sizeof(((struct utmpx *)0)->ut_user)];
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;
54cf4d4e15SEd Schouten 	char	name[sizeof(((struct utmpx *)0)->ut_host) + 2];
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 **);
84cf4d4e15SEd Schouten int			ac(const char *);
85d89167b4SAlfred Perlstein struct tty_list		*add_tty(char *);
86b5a06c25SGarance A Drosehn #ifdef DEBUG
87cf4d4e15SEd Schouten const char		*debug_pfx(const struct utmpx *, const struct utmpx *);
88b5a06c25SGarance A Drosehn #endif
89d89167b4SAlfred Perlstein int			do_tty(char *);
90cf4d4e15SEd Schouten struct utmp_list	*log_in(struct utmp_list *, struct utmpx *);
91cf4d4e15SEd Schouten struct utmp_list	*log_out(struct utmp_list *, struct utmpx *);
92d89167b4SAlfred Perlstein int			on_console(struct utmp_list *);
93d89167b4SAlfred Perlstein void			show(const char *, time_t);
9454b60d7eSAlfred Perlstein void			show_today(struct user_list *, struct utmp_list *,
9554b60d7eSAlfred Perlstein 			    time_t);
96d89167b4SAlfred Perlstein void			show_users(struct user_list *);
97d89167b4SAlfred Perlstein struct user_list	*update_user(struct user_list *, char *, time_t);
98d89167b4SAlfred Perlstein void			usage(void);
99ff9c3a32SDavid Greenman 
100ff9c3a32SDavid Greenman struct tty_list *
1017f761c52SGarance A Drosehn add_tty(char *name)
102ff9c3a32SDavid Greenman {
103ff9c3a32SDavid Greenman 	struct tty_list *tp;
1042adaffb0SGarance A Drosehn 	char *rcp;
105ff9c3a32SDavid Greenman 
106ff9c3a32SDavid Greenman 	Flags |= AC_T;
107ff9c3a32SDavid Greenman 
108ff9c3a32SDavid Greenman 	if ((tp = NEW(struct tty_list)) == NULL)
109c3737d34SPhilippe Charnier 		errx(1, "malloc failed");
110ff9c3a32SDavid Greenman 	tp->len = 0;				/* full match */
111ff9c3a32SDavid Greenman 	tp->ret = 1;				/* do if match */
112ff9c3a32SDavid Greenman 	if (*name == '!') {			/* don't do if match */
113ff9c3a32SDavid Greenman 		tp->ret = 0;
114ff9c3a32SDavid Greenman 		name++;
115ff9c3a32SDavid Greenman 	}
116fd96447aSKris Kennaway 	strlcpy(tp->name, name, sizeof (tp->name));
117ff9c3a32SDavid Greenman 	if ((rcp = strchr(tp->name, '*')) != NULL) {	/* wild card */
118ff9c3a32SDavid Greenman 		*rcp = '\0';
119ff9c3a32SDavid Greenman 		tp->len = strlen(tp->name);	/* match len bytes only */
120ff9c3a32SDavid Greenman 	}
121ff9c3a32SDavid Greenman 	tp->next = Ttys;
122ff9c3a32SDavid Greenman 	Ttys = tp;
123ff9c3a32SDavid Greenman 	return Ttys;
124ff9c3a32SDavid Greenman }
125ff9c3a32SDavid Greenman 
126ff9c3a32SDavid Greenman /*
127ff9c3a32SDavid Greenman  * should we process the named tty?
128ff9c3a32SDavid Greenman  */
129ff9c3a32SDavid Greenman int
1307f761c52SGarance A Drosehn do_tty(char *name)
131ff9c3a32SDavid Greenman {
132ff9c3a32SDavid Greenman 	struct tty_list *tp;
133ff9c3a32SDavid Greenman 	int def_ret = 0;
134ff9c3a32SDavid Greenman 
135ff9c3a32SDavid Greenman 	for (tp = Ttys; tp != NULL; tp = tp->next) {
136ff9c3a32SDavid Greenman 		if (tp->ret == 0)		/* specific don't */
137ff9c3a32SDavid Greenman 			def_ret = 1;		/* default do */
138ff9c3a32SDavid Greenman 		if (tp->len != 0) {
139ff9c3a32SDavid Greenman 			if (strncmp(name, tp->name, tp->len) == 0)
140ff9c3a32SDavid Greenman 				return tp->ret;
141ff9c3a32SDavid Greenman 		} else {
142ff9c3a32SDavid Greenman 			if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
143ff9c3a32SDavid Greenman 				return tp->ret;
144ff9c3a32SDavid Greenman 		}
145ff9c3a32SDavid Greenman 	}
146ff9c3a32SDavid Greenman 	return def_ret;
147ff9c3a32SDavid Greenman }
148ff9c3a32SDavid Greenman 
149ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
150ff9c3a32SDavid Greenman /*
151ff9c3a32SDavid Greenman  * is someone logged in on Console?
152ff9c3a32SDavid Greenman  */
153ff9c3a32SDavid Greenman int
1547f761c52SGarance A Drosehn on_console(struct utmp_list *head)
155ff9c3a32SDavid Greenman {
156ff9c3a32SDavid Greenman 	struct utmp_list *up;
157ff9c3a32SDavid Greenman 
158ff9c3a32SDavid Greenman 	for (up = head; up; up = up->next) {
159cf4d4e15SEd Schouten 		if (strcmp(up->usr.ut_line, Console) == 0)
160ff9c3a32SDavid Greenman 			return 1;
161ff9c3a32SDavid Greenman 	}
162ff9c3a32SDavid Greenman 	return 0;
163ff9c3a32SDavid Greenman }
164ff9c3a32SDavid Greenman #endif
165ff9c3a32SDavid Greenman 
166ff9c3a32SDavid Greenman /*
167ff9c3a32SDavid Greenman  * update user's login time
168ff9c3a32SDavid Greenman  */
169ff9c3a32SDavid Greenman struct user_list *
1707f761c52SGarance A Drosehn update_user(struct user_list *head, char *name, time_t secs)
171ff9c3a32SDavid Greenman {
172ff9c3a32SDavid Greenman 	struct user_list *up;
173ff9c3a32SDavid Greenman 
174ff9c3a32SDavid Greenman 	for (up = head; up != NULL; up = up->next) {
175cf4d4e15SEd Schouten 		if (strcmp(up->name, name) == 0) {
176ff9c3a32SDavid Greenman 			up->secs += secs;
177ff9c3a32SDavid Greenman 			Total += secs;
178ff9c3a32SDavid Greenman 			return head;
179ff9c3a32SDavid Greenman 		}
180ff9c3a32SDavid Greenman 	}
181ff9c3a32SDavid Greenman 	/*
182ff9c3a32SDavid Greenman 	 * not found so add new user unless specified users only
183ff9c3a32SDavid Greenman 	 */
184ff9c3a32SDavid Greenman 	if (Flags & AC_U)
185ff9c3a32SDavid Greenman 		return head;
186ff9c3a32SDavid Greenman 
187ff9c3a32SDavid Greenman 	if ((up = NEW(struct user_list)) == NULL)
188c3737d34SPhilippe Charnier 		errx(1, "malloc failed");
189ff9c3a32SDavid Greenman 	up->next = head;
190fd96447aSKris Kennaway 	strlcpy(up->name, name, sizeof (up->name));
191ff9c3a32SDavid Greenman 	up->secs = secs;
192ff9c3a32SDavid Greenman 	Total += secs;
193ff9c3a32SDavid Greenman 	return up;
194ff9c3a32SDavid Greenman }
195ff9c3a32SDavid Greenman 
196b5a06c25SGarance A Drosehn #ifdef DEBUG
197b5a06c25SGarance A Drosehn /*
198b5a06c25SGarance A Drosehn  * Create a string which is the standard prefix for a debug line.  It
199b5a06c25SGarance A Drosehn  * includes a timestamp (perhaps with year), device-name, and user-name.
200b5a06c25SGarance A Drosehn  */
201b5a06c25SGarance A Drosehn const char *
202cf4d4e15SEd Schouten debug_pfx(const struct utmpx *event_up, const struct utmpx *userinf_up)
203b5a06c25SGarance A Drosehn {
204cf4d4e15SEd Schouten 	static char str_result[40 + sizeof(userinf_up->ut_line) +
205cf4d4e15SEd Schouten 	    sizeof(userinf_up->ut_user)];
206b5a06c25SGarance A Drosehn 	static char thisyear[5];
207b5a06c25SGarance A Drosehn 	size_t maxcopy;
208b5a06c25SGarance A Drosehn 	time_t ut_timecopy;
209b5a06c25SGarance A Drosehn 
210b5a06c25SGarance A Drosehn 	if (thisyear[0] == '\0') {
211b5a06c25SGarance A Drosehn 		/* Figure out what "this year" is. */
212b5a06c25SGarance A Drosehn 		time(&ut_timecopy);
213b5a06c25SGarance A Drosehn 		strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result));
214b5a06c25SGarance A Drosehn 		strlcpy(thisyear, &str_result[20], sizeof(thisyear));
215b5a06c25SGarance A Drosehn 	}
216b5a06c25SGarance A Drosehn 
217cf4d4e15SEd Schouten 	if (event_up->ut_tv.tv_sec == 0)
218b5a06c25SGarance A Drosehn 		strlcpy(str_result, "*ZeroTime* --:--:-- ", sizeof(str_result));
219b5a06c25SGarance A Drosehn 	else {
220cf4d4e15SEd Schouten 		ut_timecopy = event_up->ut_tv.tv_sec;
221b5a06c25SGarance A Drosehn 		strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result));
222b5a06c25SGarance A Drosehn 		/*
223b5a06c25SGarance A Drosehn 		 * Include the year, if it is not the same year as "now".
224b5a06c25SGarance A Drosehn 		 */
225b5a06c25SGarance A Drosehn 		if (strncmp(&str_result[20], thisyear, 4) == 0)
226b5a06c25SGarance A Drosehn 			str_result[20] = '\0';
227b5a06c25SGarance A Drosehn 		else {
228b5a06c25SGarance A Drosehn 			str_result[24] = ' ';		/* Replace a '\n' */
229b5a06c25SGarance A Drosehn 			str_result[25] = '\0';
230b5a06c25SGarance A Drosehn 		}
231b5a06c25SGarance A Drosehn 	}
232b5a06c25SGarance A Drosehn 
233b5a06c25SGarance A Drosehn 	if (userinf_up->ut_line[0] == '\0')
234b5a06c25SGarance A Drosehn 		strlcat(str_result, "NoDev", sizeof(str_result));
235b5a06c25SGarance A Drosehn 	else {
236cf4d4e15SEd Schouten 		maxcopy = strlen(str_result) + sizeof(userinf_up->ut_line);
237b5a06c25SGarance A Drosehn 		if (maxcopy > sizeof(str_result))
238b5a06c25SGarance A Drosehn 			maxcopy = sizeof(str_result);
239b5a06c25SGarance A Drosehn 		strlcat(str_result, userinf_up->ut_line, maxcopy);
240b5a06c25SGarance A Drosehn 	}
241b5a06c25SGarance A Drosehn 	strlcat(str_result, ": ", sizeof(str_result));
242b5a06c25SGarance A Drosehn 
243cf4d4e15SEd Schouten 	if (userinf_up->ut_user[0] == '\0')
244b5a06c25SGarance A Drosehn 		strlcat(str_result, "LogOff", sizeof(str_result));
245b5a06c25SGarance A Drosehn 	else {
246cf4d4e15SEd Schouten 		maxcopy = strlen(str_result) + sizeof(userinf_up->ut_user);
247b5a06c25SGarance A Drosehn 		if (maxcopy > sizeof(str_result))
248b5a06c25SGarance A Drosehn 			maxcopy = sizeof(str_result);
249cf4d4e15SEd Schouten 		strlcat(str_result, userinf_up->ut_user, maxcopy);
250b5a06c25SGarance A Drosehn 	}
251b5a06c25SGarance A Drosehn 
252b5a06c25SGarance A Drosehn 	return (str_result);
253b5a06c25SGarance A Drosehn }
254b5a06c25SGarance A Drosehn #endif
255b5a06c25SGarance A Drosehn 
256ff9c3a32SDavid Greenman int
2577f761c52SGarance A Drosehn main(int argc, char *argv[])
258ff9c3a32SDavid Greenman {
259cf4d4e15SEd Schouten 	const char *wtmpf = NULL;
260ff9c3a32SDavid Greenman 	int c;
261ff9c3a32SDavid Greenman 
2625877948cSAndrey A. Chernov 	(void) setlocale(LC_TIME, "");
2635877948cSAndrey A. Chernov 
2646c3f552aSWarner Losh 	while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) {
265ff9c3a32SDavid Greenman 		switch (c) {
266ff9c3a32SDavid Greenman #ifdef DEBUG
267ff9c3a32SDavid Greenman 		case 'D':
268ff9c3a32SDavid Greenman 			Debug++;
269ff9c3a32SDavid Greenman 			break;
270ff9c3a32SDavid Greenman #endif
271ff9c3a32SDavid Greenman 		case 'c':
272ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
273ff9c3a32SDavid Greenman 			Console = optarg;
274ff9c3a32SDavid Greenman #else
275ff9c3a32SDavid Greenman 			usage();		/* XXX */
276ff9c3a32SDavid Greenman #endif
277ff9c3a32SDavid Greenman 			break;
278ff9c3a32SDavid Greenman 		case 'd':
279ff9c3a32SDavid Greenman 			Flags |= AC_D;
280ff9c3a32SDavid Greenman 			break;
281ff9c3a32SDavid Greenman 		case 'p':
282ff9c3a32SDavid Greenman 			Flags |= AC_P;
283ff9c3a32SDavid Greenman 			break;
284ff9c3a32SDavid Greenman 		case 't':			/* only do specified ttys */
285ff9c3a32SDavid Greenman 			add_tty(optarg);
286ff9c3a32SDavid Greenman 			break;
287ff9c3a32SDavid Greenman 		case 'w':
288cf4d4e15SEd Schouten 			Flags |= AC_W;
289cf4d4e15SEd Schouten 			wtmpf = optarg;
290ff9c3a32SDavid Greenman 			break;
291ff9c3a32SDavid Greenman 		case '?':
292ff9c3a32SDavid Greenman 		default:
293ff9c3a32SDavid Greenman 			usage();
294ff9c3a32SDavid Greenman 			break;
295ff9c3a32SDavid Greenman 		}
296ff9c3a32SDavid Greenman 	}
297ff9c3a32SDavid Greenman 	if (optind < argc) {
298ff9c3a32SDavid Greenman 		/*
299ff9c3a32SDavid Greenman 		 * initialize user list
300ff9c3a32SDavid Greenman 		 */
301ff9c3a32SDavid Greenman 		for (; optind < argc; optind++) {
302b51547cfSPhilippe Charnier 			Users = update_user(Users, argv[optind], (time_t)0);
303ff9c3a32SDavid Greenman 		}
304ff9c3a32SDavid Greenman 		Flags |= AC_U;			/* freeze user list */
305ff9c3a32SDavid Greenman 	}
306ff9c3a32SDavid Greenman 	if (Flags & AC_D)
307ff9c3a32SDavid Greenman 		Flags &= ~AC_P;
308cf4d4e15SEd Schouten 	ac(wtmpf);
309ff9c3a32SDavid Greenman 
310ff9c3a32SDavid Greenman 	return 0;
311ff9c3a32SDavid Greenman }
312ff9c3a32SDavid Greenman 
313ff9c3a32SDavid Greenman /*
314ff9c3a32SDavid Greenman  * print login time in decimal hours
315ff9c3a32SDavid Greenman  */
316ff9c3a32SDavid Greenman void
3177f761c52SGarance A Drosehn show(const char *name, time_t secs)
318ff9c3a32SDavid Greenman {
319cf4d4e15SEd Schouten 	(void)printf("\t%-*s %8.2f\n",
320cf4d4e15SEd Schouten 	    (int)sizeof(((struct utmpx *)0)->ut_user), name,
321ff9c3a32SDavid Greenman 	    ((double)secs / 3600));
322ff9c3a32SDavid Greenman }
323ff9c3a32SDavid Greenman 
324ff9c3a32SDavid Greenman void
3257f761c52SGarance A Drosehn show_users(struct user_list *list)
326ff9c3a32SDavid Greenman {
327ff9c3a32SDavid Greenman 	struct user_list *lp;
328ff9c3a32SDavid Greenman 
329ff9c3a32SDavid Greenman 	for (lp = list; lp; lp = lp->next)
330ff9c3a32SDavid Greenman 		show(lp->name, lp->secs);
331ff9c3a32SDavid Greenman }
332ff9c3a32SDavid Greenman 
333ff9c3a32SDavid Greenman /*
334ff9c3a32SDavid Greenman  * print total login time for 24hr period in decimal hours
335ff9c3a32SDavid Greenman  */
336ff9c3a32SDavid Greenman void
3377f761c52SGarance A Drosehn show_today(struct user_list *users, struct utmp_list *logins, time_t secs)
338ff9c3a32SDavid Greenman {
339ff9c3a32SDavid Greenman 	struct user_list *up;
340ff9c3a32SDavid Greenman 	struct utmp_list *lp;
341ff9c3a32SDavid Greenman 	char date[64];
342ff9c3a32SDavid Greenman 	time_t yesterday = secs - 1;
343497c7558SAndrey A. Chernov 	static int d_first = -1;
344ff9c3a32SDavid Greenman 
345497c7558SAndrey A. Chernov 	if (d_first < 0)
346497c7558SAndrey A. Chernov 		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
347497c7558SAndrey A. Chernov 	(void)strftime(date, sizeof (date),
348497c7558SAndrey A. Chernov 		       d_first ? "%e %b  total" : "%b %e  total",
349ff9c3a32SDavid Greenman 		       localtime(&yesterday));
350ff9c3a32SDavid Greenman 
351ff9c3a32SDavid Greenman 	/* restore the missing second */
352ff9c3a32SDavid Greenman 	yesterday++;
353ff9c3a32SDavid Greenman 
354ff9c3a32SDavid Greenman 	for (lp = logins; lp != NULL; lp = lp->next) {
355cf4d4e15SEd Schouten 		secs = yesterday - lp->usr.ut_tv.tv_sec;
356cf4d4e15SEd Schouten 		Users = update_user(Users, lp->usr.ut_user, secs);
357cf4d4e15SEd Schouten 		lp->usr.ut_tv.tv_sec = yesterday; /* as if they just logged in */
358ff9c3a32SDavid Greenman 	}
359ff9c3a32SDavid Greenman 	secs = 0;
360ff9c3a32SDavid Greenman 	for (up = users; up != NULL; up = up->next) {
361ff9c3a32SDavid Greenman 		secs += up->secs;
362ff9c3a32SDavid Greenman 		up->secs = 0;			/* for next day */
363ff9c3a32SDavid Greenman 	}
364ff9c3a32SDavid Greenman 	if (secs)
365ff9c3a32SDavid Greenman 		(void)printf("%s %11.2f\n", date, ((double)secs / 3600));
366ff9c3a32SDavid Greenman }
367ff9c3a32SDavid Greenman 
368ff9c3a32SDavid Greenman /*
369ff9c3a32SDavid Greenman  * log a user out and update their times.
370ff9c3a32SDavid Greenman  * if ut_line is "~", we log all users out as the system has
371ff9c3a32SDavid Greenman  * been shut down.
372ff9c3a32SDavid Greenman  */
373ff9c3a32SDavid Greenman struct utmp_list *
374cf4d4e15SEd Schouten log_out(struct utmp_list *head, struct utmpx *up)
375ff9c3a32SDavid Greenman {
376ff9c3a32SDavid Greenman 	struct utmp_list *lp, *lp2, *tlp;
377ff9c3a32SDavid Greenman 	time_t secs;
378ff9c3a32SDavid Greenman 
379ff9c3a32SDavid Greenman 	for (lp = head, lp2 = NULL; lp != NULL; )
380cf4d4e15SEd Schouten 		if (up->ut_type == BOOT_TIME || up->ut_type == SHUTDOWN_TIME ||
381cf4d4e15SEd Schouten 		    (up->ut_type == DEAD_PROCESS &&
382cf4d4e15SEd Schouten 		    memcmp(lp->usr.ut_id, up->ut_id, sizeof up->ut_id) == 0)) {
383cf4d4e15SEd Schouten 			secs = up->ut_tv.tv_sec - lp->usr.ut_tv.tv_sec;
384cf4d4e15SEd Schouten 			Users = update_user(Users, lp->usr.ut_user, secs);
385ff9c3a32SDavid Greenman #ifdef DEBUG
386ff9c3a32SDavid Greenman 			if (Debug)
387b5a06c25SGarance A Drosehn 				printf("%s logged out (%2d:%02d:%02d)\n",
388b5a06c25SGarance A Drosehn 				    debug_pfx(up, &lp->usr), (int)(secs / 3600),
389b5a06c25SGarance A Drosehn 				    (int)((secs % 3600) / 60),
390b5a06c25SGarance A Drosehn 				    (int)(secs % 60));
391ff9c3a32SDavid Greenman #endif
392ff9c3a32SDavid Greenman 			/*
393ff9c3a32SDavid Greenman 			 * now lose it
394ff9c3a32SDavid Greenman 			 */
395ff9c3a32SDavid Greenman 			tlp = lp;
396ff9c3a32SDavid Greenman 			lp = lp->next;
397ff9c3a32SDavid Greenman 			if (tlp == head)
398ff9c3a32SDavid Greenman 				head = lp;
399ff9c3a32SDavid Greenman 			else if (lp2 != NULL)
400ff9c3a32SDavid Greenman 				lp2->next = lp;
401ff9c3a32SDavid Greenman 			free(tlp);
402ff9c3a32SDavid Greenman 		} else {
403ff9c3a32SDavid Greenman 			lp2 = lp;
404ff9c3a32SDavid Greenman 			lp = lp->next;
405ff9c3a32SDavid Greenman 		}
406ff9c3a32SDavid Greenman 	return head;
407ff9c3a32SDavid Greenman }
408ff9c3a32SDavid Greenman 
409ff9c3a32SDavid Greenman 
410ff9c3a32SDavid Greenman /*
411ff9c3a32SDavid Greenman  * if do_tty says ok, login a user
412ff9c3a32SDavid Greenman  */
413ff9c3a32SDavid Greenman struct utmp_list *
414cf4d4e15SEd Schouten log_in(struct utmp_list *head, struct utmpx *up)
415ff9c3a32SDavid Greenman {
416ff9c3a32SDavid Greenman 	struct utmp_list *lp;
417ff9c3a32SDavid Greenman 
418ff9c3a32SDavid Greenman 	/*
419ff9c3a32SDavid Greenman 	 * this could be a login. if we're not dealing with
420ff9c3a32SDavid Greenman 	 * the console name, say it is.
421ff9c3a32SDavid Greenman 	 *
422ff9c3a32SDavid Greenman 	 * If we are, and if ut_host==":0.0" we know that it
423ff9c3a32SDavid Greenman 	 * isn't a real login. _But_ if we have not yet recorded
424ff9c3a32SDavid Greenman 	 * someone being logged in on Console - due to the wtmp
425ff9c3a32SDavid Greenman 	 * file starting after they logged in, we'll pretend they
426ff9c3a32SDavid Greenman 	 * logged in, at the start of the wtmp file.
427ff9c3a32SDavid Greenman 	 */
428ff9c3a32SDavid Greenman 
429ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
430ff9c3a32SDavid Greenman 	if (up->ut_host[0] == ':') {
431ff9c3a32SDavid Greenman 		/*
432ff9c3a32SDavid Greenman 		 * SunOS 4.0.2 does not treat ":0.0" as special but we
433ff9c3a32SDavid Greenman 		 * do.
434ff9c3a32SDavid Greenman 		 */
435ff9c3a32SDavid Greenman 		if (on_console(head))
436ff9c3a32SDavid Greenman 			return head;
437ff9c3a32SDavid Greenman 		/*
438ff9c3a32SDavid Greenman 		 * ok, no recorded login, so they were here when wtmp
439ff9c3a32SDavid Greenman 		 * started!  Adjust ut_time!
440ff9c3a32SDavid Greenman 		 */
441ff9c3a32SDavid Greenman 		up->ut_time = FirstTime;
442ff9c3a32SDavid Greenman 		/*
443ff9c3a32SDavid Greenman 		 * this allows us to pick the right logout
444ff9c3a32SDavid Greenman 		 */
445fd96447aSKris Kennaway 		strlcpy(up->ut_line, Console, sizeof (up->ut_line));
446ff9c3a32SDavid Greenman 	}
447ff9c3a32SDavid Greenman #endif
448ff9c3a32SDavid Greenman 	/*
449ff9c3a32SDavid Greenman 	 * If we are doing specified ttys only, we ignore
450ff9c3a32SDavid Greenman 	 * anything else.
451ff9c3a32SDavid Greenman 	 */
452ff9c3a32SDavid Greenman 	if (Flags & AC_T)
453ff9c3a32SDavid Greenman 		if (!do_tty(up->ut_line))
454ff9c3a32SDavid Greenman 			return head;
455ff9c3a32SDavid Greenman 
456ff9c3a32SDavid Greenman 	/*
457ff9c3a32SDavid Greenman 	 * go ahead and log them in
458ff9c3a32SDavid Greenman 	 */
459ff9c3a32SDavid Greenman 	if ((lp = NEW(struct utmp_list)) == NULL)
460c3737d34SPhilippe Charnier 		errx(1, "malloc failed");
461ff9c3a32SDavid Greenman 	lp->next = head;
462ff9c3a32SDavid Greenman 	head = lp;
463cf4d4e15SEd Schouten 	memmove(&lp->usr, up, sizeof *up);
464ff9c3a32SDavid Greenman #ifdef DEBUG
465ff9c3a32SDavid Greenman 	if (Debug) {
466b5a06c25SGarance A Drosehn 		printf("%s logged in", debug_pfx(&lp->usr, up));
467ff9c3a32SDavid Greenman 		if (*up->ut_host)
468b5a06c25SGarance A Drosehn 			printf(" (%-.*s)", (int)sizeof(up->ut_host),
469b5a06c25SGarance A Drosehn 			    up->ut_host);
470ff9c3a32SDavid Greenman 		putchar('\n');
471ff9c3a32SDavid Greenman 	}
472ff9c3a32SDavid Greenman #endif
473ff9c3a32SDavid Greenman 	return head;
474ff9c3a32SDavid Greenman }
475ff9c3a32SDavid Greenman 
476ff9c3a32SDavid Greenman int
477cf4d4e15SEd Schouten ac(const char *file)
478ff9c3a32SDavid Greenman {
479ff9c3a32SDavid Greenman 	struct utmp_list *lp, *head = NULL;
480cf4d4e15SEd Schouten 	struct utmpx *usr, usht;
481ff9c3a32SDavid Greenman 	struct tm *ltm;
482e3b218cdSGarance A Drosehn 	time_t prev_secs, secs, ut_timecopy;
483e3b218cdSGarance A Drosehn 	int day, rfound, tchanged, tskipped;
484ff9c3a32SDavid Greenman 
485e3b218cdSGarance A Drosehn 	day = -1;
486e3b218cdSGarance A Drosehn 	prev_secs = 1;			/* Minimum acceptable date == 1970 */
487e3b218cdSGarance A Drosehn 	rfound = tchanged = tskipped = 0;
488e3b218cdSGarance A Drosehn 	secs = 0;
489cf4d4e15SEd Schouten 	if (setutxdb(UTXDB_LOG, file) != 0)
490cf4d4e15SEd Schouten 		err(1, "%s", file);
491cf4d4e15SEd Schouten 	while ((usr = getutxent()) != NULL) {
492e3b218cdSGarance A Drosehn 		rfound++;
493cf4d4e15SEd Schouten 		ut_timecopy = usr->ut_tv.tv_sec;
494e3b218cdSGarance A Drosehn 		/*
495e3b218cdSGarance A Drosehn 		 * With sparc64 using 64-bit time_t's, there is some system
496e3b218cdSGarance A Drosehn 		 * routine which sets ut_time==0 (the high-order word of a
497e3b218cdSGarance A Drosehn 		 * 64-bit time) instead of a 32-bit time value.  For those
498e3b218cdSGarance A Drosehn 		 * wtmp files, it is "more-accurate" to substitute the most-
499e3b218cdSGarance A Drosehn 		 * recent time found, instead of throwing away the entire
500e3b218cdSGarance A Drosehn 		 * record.  While it is still just a guess, it is a better
501e3b218cdSGarance A Drosehn 		 * guess than throwing away a log-off record and therefore
502e3b218cdSGarance A Drosehn 		 * counting a session as if it continued to the end of the
503e3b218cdSGarance A Drosehn 		 * month, or the next system-reboot.
504e3b218cdSGarance A Drosehn 		 */
505e3b218cdSGarance A Drosehn 		if (ut_timecopy == 0 && prev_secs > 1) {
506e3b218cdSGarance A Drosehn #ifdef DEBUG
507e3b218cdSGarance A Drosehn 			if (Debug)
508e3b218cdSGarance A Drosehn 				printf("%s - date changed to: %s",
509cf4d4e15SEd Schouten 				    debug_pfx(usr, usr), ctime(&prev_secs));
510e3b218cdSGarance A Drosehn #endif
511e3b218cdSGarance A Drosehn 			tchanged++;
512cf4d4e15SEd Schouten 			usr->ut_tv.tv_sec = ut_timecopy = prev_secs;
513e3b218cdSGarance A Drosehn 		}
514e3b218cdSGarance A Drosehn 		/*
515e3b218cdSGarance A Drosehn 		 * Skip records where the time goes backwards.
516e3b218cdSGarance A Drosehn 		 */
517e3b218cdSGarance A Drosehn 		if (ut_timecopy < prev_secs) {
518e3b218cdSGarance A Drosehn #ifdef DEBUG
519e3b218cdSGarance A Drosehn 			if (Debug)
520e3b218cdSGarance A Drosehn 				printf("%s - bad date, record skipped\n",
521cf4d4e15SEd Schouten 				    debug_pfx(usr, usr));
522e3b218cdSGarance A Drosehn #endif
523e3b218cdSGarance A Drosehn 			tskipped++;
524e3b218cdSGarance A Drosehn 			continue;	/* Skip this invalid record. */
525e3b218cdSGarance A Drosehn 		}
526e3b218cdSGarance A Drosehn 		prev_secs = ut_timecopy;
527e3b218cdSGarance A Drosehn 
528ff9c3a32SDavid Greenman 		if (!FirstTime)
529e3b218cdSGarance A Drosehn 			FirstTime = ut_timecopy;
530ff9c3a32SDavid Greenman 		if (Flags & AC_D) {
531e3b218cdSGarance A Drosehn 			ltm = localtime(&ut_timecopy);
532ff9c3a32SDavid Greenman 			if (day >= 0 && day != ltm->tm_yday) {
533ff9c3a32SDavid Greenman 				day = ltm->tm_yday;
534ff9c3a32SDavid Greenman 				/*
535ff9c3a32SDavid Greenman 				 * print yesterday's total
536ff9c3a32SDavid Greenman 				 */
537e3b218cdSGarance A Drosehn 				secs = ut_timecopy;
538ff9c3a32SDavid Greenman 				secs -= ltm->tm_sec;
539ff9c3a32SDavid Greenman 				secs -= 60 * ltm->tm_min;
540ff9c3a32SDavid Greenman 				secs -= 3600 * ltm->tm_hour;
541ff9c3a32SDavid Greenman 				show_today(Users, head, secs);
542ff9c3a32SDavid Greenman 			} else
543ff9c3a32SDavid Greenman 				day = ltm->tm_yday;
544ff9c3a32SDavid Greenman 		}
545cf4d4e15SEd Schouten 		switch(usr->ut_type) {
546cf4d4e15SEd Schouten 		case OLD_TIME:
547e3b218cdSGarance A Drosehn 			secs = ut_timecopy;
548ff9c3a32SDavid Greenman 			break;
549cf4d4e15SEd Schouten 		case NEW_TIME:
550e3b218cdSGarance A Drosehn 			secs -= ut_timecopy;
551ff9c3a32SDavid Greenman 			/*
552ff9c3a32SDavid Greenman 			 * adjust time for those logged in
553ff9c3a32SDavid Greenman 			 */
554ff9c3a32SDavid Greenman 			for (lp = head; lp != NULL; lp = lp->next)
555cf4d4e15SEd Schouten 				lp->usr.ut_tv.tv_sec -= secs;
556ff9c3a32SDavid Greenman 			break;
557cf4d4e15SEd Schouten 		case BOOT_TIME:
558cf4d4e15SEd Schouten 		case SHUTDOWN_TIME:
559cf4d4e15SEd Schouten 			head = log_out(head, usr);
560e3b218cdSGarance A Drosehn 			FirstTime = ut_timecopy; /* shouldn't be needed */
561ff9c3a32SDavid Greenman 			break;
562cf4d4e15SEd Schouten 		case USER_PROCESS:
563ff9c3a32SDavid Greenman 			/*
5641ce46cefSPoul-Henning Kamp 			 * if they came in on tty[p-sP-S]*, then it is only
565ff9c3a32SDavid Greenman 			 * a login session if the ut_host field is non-empty
566ff9c3a32SDavid Greenman 			 */
567cf4d4e15SEd Schouten 			if (strncmp(usr->ut_line, "tty", 3) != 0 ||
568cf4d4e15SEd Schouten 			    strchr("pqrsPQRS", usr->ut_line[3]) == NULL ||
569cf4d4e15SEd Schouten 			    *usr->ut_host != '\0')
570cf4d4e15SEd Schouten 				head = log_in(head, usr);
571b5a06c25SGarance A Drosehn #ifdef DEBUG
572b5a06c25SGarance A Drosehn 			else if (Debug > 1)
573b5a06c25SGarance A Drosehn 				/* Things such as 'screen' sessions. */
574b5a06c25SGarance A Drosehn 				printf("%s - record ignored\n",
575cf4d4e15SEd Schouten 				    debug_pfx(usr, usr));
576b5a06c25SGarance A Drosehn #endif
577cf4d4e15SEd Schouten 			break;
578cf4d4e15SEd Schouten 		case DEAD_PROCESS:
579cf4d4e15SEd Schouten 			head = log_out(head, usr);
580ff9c3a32SDavid Greenman 			break;
581ff9c3a32SDavid Greenman 		}
582ff9c3a32SDavid Greenman 	}
583cf4d4e15SEd Schouten 	endutxent();
5842a30154fSSteve Price 	if (!(Flags & AC_W))
585cf4d4e15SEd Schouten 		usht.ut_tv.tv_sec = time(NULL);
586cf4d4e15SEd Schouten 	usht.ut_type = SHUTDOWN_TIME;
587ff9c3a32SDavid Greenman 
588ff9c3a32SDavid Greenman 	if (Flags & AC_D) {
589cf4d4e15SEd Schouten 		ut_timecopy = usht.ut_tv.tv_sec;
590e3b218cdSGarance A Drosehn 		ltm = localtime(&ut_timecopy);
591ff9c3a32SDavid Greenman 		if (day >= 0 && day != ltm->tm_yday) {
592ff9c3a32SDavid Greenman 			/*
593ff9c3a32SDavid Greenman 			 * print yesterday's total
594ff9c3a32SDavid Greenman 			 */
595e3b218cdSGarance A Drosehn 			secs = ut_timecopy;
596ff9c3a32SDavid Greenman 			secs -= ltm->tm_sec;
597ff9c3a32SDavid Greenman 			secs -= 60 * ltm->tm_min;
598ff9c3a32SDavid Greenman 			secs -= 3600 * ltm->tm_hour;
599ff9c3a32SDavid Greenman 			show_today(Users, head, secs);
600ff9c3a32SDavid Greenman 		}
601ff9c3a32SDavid Greenman 	}
602ff9c3a32SDavid Greenman 	/*
603ff9c3a32SDavid Greenman 	 * anyone still logged in gets time up to now
604ff9c3a32SDavid Greenman 	 */
605cf4d4e15SEd Schouten 	head = log_out(head, &usht);
606ff9c3a32SDavid Greenman 
607ff9c3a32SDavid Greenman 	if (Flags & AC_D)
608ff9c3a32SDavid Greenman 		show_today(Users, head, time((time_t *)0));
609ff9c3a32SDavid Greenman 	else {
610ff9c3a32SDavid Greenman 		if (Flags & AC_P)
611ff9c3a32SDavid Greenman 			show_users(Users);
612ff9c3a32SDavid Greenman 		show("total", Total);
613ff9c3a32SDavid Greenman 	}
614e3b218cdSGarance A Drosehn 
615e3b218cdSGarance A Drosehn 	if (tskipped > 0)
616e3b218cdSGarance A Drosehn 		printf("(Skipped %d of %d records due to invalid time values)\n",
617e3b218cdSGarance A Drosehn 		    tskipped, rfound);
618e3b218cdSGarance A Drosehn 	if (tchanged > 0)
619e3b218cdSGarance A Drosehn 		printf("(Changed %d of %d records to have a more likely time value)\n",
620e3b218cdSGarance A Drosehn 		    tchanged, rfound);
621e3b218cdSGarance A Drosehn 
622ff9c3a32SDavid Greenman 	return 0;
623ff9c3a32SDavid Greenman }
624ff9c3a32SDavid Greenman 
625ff9c3a32SDavid Greenman void
6267f761c52SGarance A Drosehn usage(void)
627ff9c3a32SDavid Greenman {
628ff9c3a32SDavid Greenman 	(void)fprintf(stderr,
629ff9c3a32SDavid Greenman #ifdef CONSOLE_TTY
630ff9c3a32SDavid Greenman 	    "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
631ff9c3a32SDavid Greenman #else
632ff9c3a32SDavid Greenman 	    "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");
633ff9c3a32SDavid Greenman #endif
634ff9c3a32SDavid Greenman 	exit(1);
635ff9c3a32SDavid Greenman }
636