GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ac/ac.c Lines: 0 162 0.0 %
Date: 2017-11-07 Branches: 0 113 0.0 %

Line Branch Exec Source
1
/*-
2
 * Copyright (c) 1994 Christopher G. Demetriou
3
 * Copyright (c) 1994 Simon J. Gerraty
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
 * SUCH DAMAGE.
26
 */
27
28
#include <sys/types.h>
29
#include <sys/file.h>
30
#include <sys/time.h>
31
#include <err.h>
32
#include <errno.h>
33
#include <pwd.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <utmp.h>
38
#include <unistd.h>
39
40
/*
41
 * this is for our list of currently logged in sessions
42
 */
43
struct utmp_list {
44
	struct utmp_list *next;
45
	struct utmp usr;
46
};
47
48
/*
49
 * this is for our list of users that are accumulating time.
50
 */
51
struct user_list {
52
	struct user_list *next;
53
	char	name[UT_NAMESIZE+1];
54
	time_t	secs;
55
};
56
57
/*
58
 * this is for chosing whether to ignore a login
59
 */
60
struct tty_list {
61
	struct tty_list *next;
62
	char	name[UT_LINESIZE+3];
63
	size_t	len;
64
	int	ret;
65
};
66
67
/*
68
 * globals - yes yuk
69
 */
70
static time_t	Total = 0;
71
static time_t	FirstTime = 0;
72
static int	Flags = 0;
73
static struct user_list *Users = NULL;
74
static struct tty_list *Ttys = NULL;
75
76
#define	AC_W	1				/* not _PATH_WTMP */
77
#define	AC_D	2				/* daily totals (ignore -p) */
78
#define	AC_P	4				/* per-user totals */
79
#define	AC_U	8				/* specified users only */
80
#define	AC_T	16				/* specified ttys only */
81
82
#ifdef DEBUG
83
static int Debug = 0;
84
#endif
85
86
int			main(int, char **);
87
int			ac(FILE *);
88
void			add_tty(char *);
89
int			do_tty(char *);
90
FILE			*file(char *);
91
struct utmp_list	*log_in(struct utmp_list *, struct utmp *);
92
struct utmp_list	*log_out(struct utmp_list *, struct utmp *);
93
int			on_console(struct utmp_list *);
94
void			show(char *, time_t);
95
void			show_today(struct user_list *, struct utmp_list *,
96
			    time_t);
97
void			show_users(struct user_list *);
98
struct user_list	*update_user(struct user_list *, char *, time_t);
99
void			usage(void);
100
101
/*
102
 * open wtmp or die
103
 */
104
FILE *
105
file(char *name)
106
{
107
	FILE *fp;
108
109
	if (strcmp(name, "-") == 0)
110
		fp = stdin;
111
	else if ((fp = fopen(name, "r")) == NULL)
112
		err(1, "%s", name);
113
	/* in case we want to discriminate */
114
	if (strcmp(_PATH_WTMP, name))
115
		Flags |= AC_W;
116
	return fp;
117
}
118
119
void
120
add_tty(char *name)
121
{
122
	struct tty_list *tp;
123
	char *rcp;
124
125
	Flags |= AC_T;
126
127
	if ((tp = malloc(sizeof(struct tty_list))) == NULL)
128
		err(1, "malloc");
129
	tp->len = 0;				/* full match */
130
	tp->ret = 1;				/* do if match */
131
	if (*name == '!') {			/* don't do if match */
132
		tp->ret = 0;
133
		name++;
134
	}
135
	strlcpy(tp->name, name, sizeof (tp->name));
136
	if ((rcp = strchr(tp->name, '*')) != NULL) {	/* wild card */
137
		*rcp = '\0';
138
		tp->len = strlen(tp->name);	/* match len bytes only */
139
	}
140
	tp->next = Ttys;
141
	Ttys = tp;
142
}
143
144
/*
145
 * should we process the named tty?
146
 */
147
int
148
do_tty(char *name)
149
{
150
	struct tty_list *tp;
151
	int def_ret = 0;
152
153
	for (tp = Ttys; tp != NULL; tp = tp->next) {
154
		if (tp->ret == 0)		/* specific don't */
155
			def_ret = 1;		/* default do */
156
		if (tp->len != 0) {
157
			if (strncmp(name, tp->name, tp->len) == 0)
158
				return tp->ret;
159
		} else {
160
			if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
161
				return tp->ret;
162
		}
163
	}
164
	return def_ret;
165
}
166
167
/*
168
 * update user's login time
169
 */
170
struct user_list *
171
update_user(struct user_list *head, char *name, time_t secs)
172
{
173
	struct user_list *up;
174
175
	for (up = head; up != NULL; up = up->next) {
176
		if (strncmp(up->name, name, sizeof (up->name) - 1) == 0) {
177
			up->secs += secs;
178
			Total += secs;
179
			return head;
180
		}
181
	}
182
	/*
183
	 * not found so add new user unless specified users only
184
	 */
185
	if (Flags & AC_U)
186
		return head;
187
188
	if ((up = malloc(sizeof(struct user_list))) == NULL)
189
		err(1, "malloc");
190
	up->next = head;
191
	strlcpy(up->name, name, sizeof (up->name));
192
	up->secs = secs;
193
	Total += secs;
194
	return up;
195
}
196
197
int
198
main(int argc, char *argv[])
199
{
200
	FILE *fp;
201
	int c;
202
203
	if (pledge("stdio rpath flock cpath wpath", NULL) == -1)
204
		err(1, "pledge");
205
206
	fp = NULL;
207
	while ((c = getopt(argc, argv, "Ddpt:w:")) != -1) {
208
		switch (c) {
209
#ifdef DEBUG
210
		case 'D':
211
			Debug = 1;
212
			break;
213
#endif
214
		case 'd':
215
			Flags |= AC_D;
216
			break;
217
		case 'p':
218
			Flags |= AC_P;
219
			break;
220
		case 't':			/* only do specified ttys */
221
			add_tty(optarg);
222
			break;
223
		case 'w':
224
			fp = file(optarg);
225
			break;
226
		case '?':
227
		default:
228
			usage();
229
			break;
230
		}
231
	}
232
	if (optind < argc) {
233
		/*
234
		 * initialize user list
235
		 */
236
		for (; optind < argc; optind++) {
237
			Users = update_user(Users, argv[optind], 0L);
238
		}
239
		Flags |= AC_U;			/* freeze user list */
240
	}
241
	if (Flags & AC_D)
242
		Flags &= ~AC_P;
243
	if (fp == NULL) {
244
		/*
245
		 * if _PATH_WTMP does not exist, exit quietly
246
		 */
247
		if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT)
248
			return 0;
249
250
		fp = file(_PATH_WTMP);
251
	}
252
	ac(fp);
253
254
	return 0;
255
}
256
257
/*
258
 * print login time in decimal hours
259
 */
260
void
261
show(char *name, time_t secs)
262
{
263
	(void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
264
	    ((double)secs / 3600));
265
}
266
267
void
268
show_users(struct user_list *list)
269
{
270
	struct user_list *lp;
271
272
	for (lp = list; lp; lp = lp->next)
273
		show(lp->name, lp->secs);
274
}
275
276
/*
277
 * print total login time for 24hr period in decimal hours
278
 */
279
void
280
show_today(struct user_list *users, struct utmp_list *logins, time_t secs)
281
{
282
	struct user_list *up;
283
	struct utmp_list *lp;
284
	char date[64];
285
	time_t yesterday = secs - 1;
286
287
	(void)strftime(date, sizeof (date), "%b %e  total",
288
	    localtime(&yesterday));
289
290
	/* restore the missing second */
291
	yesterday++;
292
293
	for (lp = logins; lp != NULL; lp = lp->next) {
294
		secs = yesterday - lp->usr.ut_time;
295
		Users = update_user(Users, lp->usr.ut_name, secs);
296
		lp->usr.ut_time = yesterday;	/* as if they just logged in */
297
	}
298
	secs = 0;
299
	for (up = users; up != NULL; up = up->next) {
300
		secs += up->secs;
301
		up->secs = 0;			/* for next day */
302
	}
303
	if (secs)
304
		(void)printf("%s %11.2f\n", date, ((double)secs / 3600));
305
}
306
307
/*
308
 * log a user out and update their times.
309
 * if ut_line is "~", we log all users out as the system has
310
 * been shut down.
311
 */
312
struct utmp_list *
313
log_out(struct utmp_list *head, struct utmp *up)
314
{
315
	struct utmp_list *lp, *lp2, *tlp;
316
	time_t secs;
317
318
	for (lp = head, lp2 = NULL; lp != NULL; )
319
		if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line,
320
		    sizeof (up->ut_line)) == 0) {
321
			secs = up->ut_time - lp->usr.ut_time;
322
			Users = update_user(Users, lp->usr.ut_name, secs);
323
#ifdef DEBUG
324
			if (Debug)
325
				printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
326
				    19, ctime(&up->ut_time),
327
				    sizeof (lp->usr.ut_line), lp->usr.ut_line,
328
				    sizeof (lp->usr.ut_name), lp->usr.ut_name,
329
				    secs / 3600, (secs % 3600) / 60, secs % 60);
330
#endif
331
			/*
332
			 * now lose it
333
			 */
334
			tlp = lp;
335
			lp = lp->next;
336
			if (tlp == head)
337
				head = lp;
338
			else if (lp2 != NULL)
339
				lp2->next = lp;
340
			free(tlp);
341
		} else {
342
			lp2 = lp;
343
			lp = lp->next;
344
		}
345
	return head;
346
}
347
348
349
/*
350
 * if do_tty says ok, login a user
351
 */
352
struct utmp_list *
353
log_in(struct utmp_list *head, struct utmp *up)
354
{
355
	struct utmp_list *lp;
356
357
	/*
358
	 * this could be a login. if we're not dealing with
359
	 * the console name, say it is.
360
	 *
361
	 * If we are, and if ut_host==":0.0" we know that it
362
	 * isn't a real login. _But_ if we have not yet recorded
363
	 * someone being logged in on Console - due to the wtmp
364
	 * file starting after they logged in, we'll pretend they
365
	 * logged in, at the start of the wtmp file.
366
	 */
367
368
	/*
369
	 * If we are doing specified ttys only, we ignore
370
	 * anything else.
371
	 */
372
	if (Flags & AC_T)
373
		if (!do_tty(up->ut_line))
374
			return head;
375
376
	/*
377
	 * go ahead and log them in
378
	 */
379
	if ((lp = malloc(sizeof(struct utmp_list))) == NULL)
380
		err(1, "malloc");
381
	lp->next = head;
382
	head = lp;
383
	memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
384
#ifdef DEBUG
385
	if (Debug) {
386
		printf("%-.*s %-.*s: %-.*s logged in", 19,
387
		    ctime(&lp->usr.ut_time), sizeof (up->ut_line),
388
		    up->ut_line, sizeof (up->ut_name), up->ut_name);
389
		if (*up->ut_host)
390
			printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host);
391
		putchar('\n');
392
	}
393
#endif
394
	return head;
395
}
396
397
int
398
ac(FILE	*fp)
399
{
400
	struct utmp_list *lp, *head = NULL;
401
	struct utmp usr;
402
	struct tm *ltm;
403
	time_t secs = 0, prev = 0;
404
	int day = -1;
405
406
	while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
407
		if (!FirstTime)
408
			FirstTime = usr.ut_time;
409
		if (usr.ut_time < prev)
410
			continue;	/* broken record */
411
		prev = usr.ut_time;
412
		if (Flags & AC_D) {
413
			ltm = localtime(&usr.ut_time);
414
			if (day >= 0 && day != ltm->tm_yday) {
415
				day = ltm->tm_yday;
416
				/*
417
				 * print yesterday's total
418
				 */
419
				secs = usr.ut_time;
420
				secs -= ltm->tm_sec;
421
				secs -= 60 * ltm->tm_min;
422
				secs -= 3600 * ltm->tm_hour;
423
				show_today(Users, head, secs);
424
			} else
425
				day = ltm->tm_yday;
426
		}
427
		switch(*usr.ut_line) {
428
		case '|':
429
			secs = usr.ut_time;
430
			break;
431
		case '{':
432
			secs -= usr.ut_time;
433
			/*
434
			 * adjust time for those logged in
435
			 */
436
			for (lp = head; lp != NULL; lp = lp->next)
437
				lp->usr.ut_time -= secs;
438
			break;
439
		case '~':			/* reboot or shutdown */
440
			head = log_out(head, &usr);
441
			FirstTime = usr.ut_time; /* shouldn't be needed */
442
			break;
443
		default:
444
			/*
445
			 * if they came in on a pseudo-tty, then it is only
446
			 * a login session if the ut_host field is non-empty
447
			 */
448
			if (*usr.ut_name) {
449
				if (strncmp(usr.ut_line, "tty", 3) != 0 ||
450
				    strchr("pqrstuvwxyzPQRST", usr.ut_line[3]) != NULL ||
451
				    *usr.ut_host != '\0')
452
					head = log_in(head, &usr);
453
			} else
454
				head = log_out(head, &usr);
455
			break;
456
		}
457
	}
458
	(void)fclose(fp);
459
	if (!(Flags & AC_W))
460
		usr.ut_time = time(NULL);
461
	(void)strlcpy(usr.ut_line, "~", sizeof usr.ut_line);
462
463
	if (Flags & AC_D) {
464
		ltm = localtime(&usr.ut_time);
465
		if (day >= 0 && day != ltm->tm_yday) {
466
			/*
467
			 * print yesterday's total
468
			 */
469
			secs = usr.ut_time;
470
			secs -= ltm->tm_sec;
471
			secs -= 60 * ltm->tm_min;
472
			secs -= 3600 * ltm->tm_hour;
473
			show_today(Users, head, secs);
474
		}
475
	}
476
	/*
477
	 * anyone still logged in gets time up to now
478
	 */
479
	head = log_out(head, &usr);
480
481
	if (Flags & AC_D)
482
		show_today(Users, head, time(NULL));
483
	else {
484
		if (Flags & AC_P)
485
			show_users(Users);
486
		show("total", Total);
487
	}
488
	return 0;
489
}
490
491
void
492
usage(void)
493
{
494
	extern char *__progname;
495
	(void)fprintf(stderr, "usage: "
496
	    "%s [-dp] [-t tty] [-w wtmp] [user ...]\n", __progname);
497
	exit(1);
498
}