1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1996 John M. Vinopal
5 * Copyright (c) 2018 Philip Paeps
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed for the NetBSD Project
19 *	by John M. Vinopal.
20 * 4. The name of the author may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37#ifndef lint
38__RCSID("$FreeBSD$");
39__RCSID("$NetBSD: lastlogin.c,v 1.4 1998/02/03 04:45:35 perry Exp $");
40#endif
41
42#include <err.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <time.h>
47#include <unistd.h>
48#include <utmpx.h>
49
50#include <libxo/xo.h>
51
52	int	main(int, char **);
53static	void	output(struct utmpx *);
54static	void	usage(void);
55static int	utcmp_user(const void *, const void *);
56
57static int	order = 1;
58static const char *file = NULL;
59static int	(*utcmp)(const void *, const void *) = utcmp_user;
60
61static int
62utcmp_user(const void *u1, const void *u2)
63{
64
65	return (order * strcmp(((const struct utmpx *)u1)->ut_user,
66	    ((const struct utmpx *)u2)->ut_user));
67}
68
69static int
70utcmp_time(const void *u1, const void *u2)
71{
72	time_t t1, t2;
73
74	t1 = ((const struct utmpx *)u1)->ut_tv.tv_sec;
75	t2 = ((const struct utmpx *)u2)->ut_tv.tv_sec;
76	return (t1 < t2 ? order : t1 > t2 ? -order : 0);
77}
78
79int
80main(int argc, char *argv[])
81{
82	int	ch, i, ulistsize;
83	struct utmpx *u, *ulist;
84
85	argc = xo_parse_args(argc, argv);
86	if (argc < 0)
87		exit(1);
88
89	while ((ch = getopt(argc, argv, "f:rt")) != -1) {
90		switch (ch) {
91		case 'f':
92			file = optarg;
93			break;
94		case 'r':
95			order = -1;
96			break;
97		case 't':
98			utcmp = utcmp_time;
99			break;
100		default:
101			usage();
102		}
103	}
104	argc -= optind;
105	argv += optind;
106
107	xo_open_container("lastlogin-information");
108	xo_open_list("lastlogin");
109
110	if (argc > 0) {
111		/* Process usernames given on the command line. */
112		for (i = 0; i < argc; i++) {
113			if (setutxdb(UTXDB_LASTLOGIN, file) != 0)
114				xo_err(1, "failed to open lastlog database");
115			if ((u = getutxuser(argv[i])) == NULL) {
116				xo_warnx("user '%s' not found", argv[i]);
117				continue;
118			}
119			output(u);
120			endutxent();
121		}
122	} else {
123		/* Read all lastlog entries, looking for active ones. */
124		if (setutxdb(UTXDB_LASTLOGIN, file) != 0)
125			xo_err(1, "failed to open lastlog database");
126		ulist = NULL;
127		ulistsize = 0;
128		while ((u = getutxent()) != NULL) {
129			if (u->ut_type != USER_PROCESS)
130				continue;
131			if ((ulistsize % 16) == 0) {
132				ulist = realloc(ulist,
133				    (ulistsize + 16) * sizeof(struct utmpx));
134				if (ulist == NULL)
135					xo_err(1, "malloc");
136			}
137			ulist[ulistsize++] = *u;
138		}
139		endutxent();
140
141		qsort(ulist, ulistsize, sizeof(struct utmpx), utcmp);
142		for (i = 0; i < ulistsize; i++)
143			output(&ulist[i]);
144	}
145
146	xo_close_list("lastlogin");
147	xo_close_container("lastlogin-information");
148	xo_finish();
149
150	exit(0);
151}
152
153/* Duplicate the output of last(1) */
154static void
155output(struct utmpx *u)
156{
157	time_t t = u->ut_tv.tv_sec;
158
159	xo_open_instance("lastlogin");
160	xo_emit("{:user/%-10s/%s} {:tty/%-8s/%s} {:from/%-22.22s/%s}",
161		u->ut_user, u->ut_line, u->ut_host);
162	xo_attr("seconds", "%lu", (unsigned long)t);
163	xo_emit(" {:login-time/%.24s/%.24s}\n", ctime(&t));
164	xo_close_instance("lastlogin");
165}
166
167static void
168usage(void)
169{
170	xo_error("usage: lastlogin [-f file] [-rt] [user ...]\n");
171	xo_finish();
172	exit(1);
173}
174