1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include "namespace.h"
33#include <sys/endian.h>
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <errno.h>
37#include <stdio.h>
38#include <string.h>
39#include <utmpx.h>
40#include "utxdb.h"
41#include "un-namespace.h"
42
43#ifdef __NO_TLS
44static FILE *uf = NULL;
45static int udb;
46#else
47static _Thread_local FILE *uf = NULL;
48static _Thread_local int udb;
49#endif
50
51int
52setutxdb(int db, const char *file)
53{
54	struct stat sb;
55
56	switch (db) {
57	case UTXDB_ACTIVE:
58		if (file == NULL)
59			file = _PATH_UTX_ACTIVE;
60		break;
61	case UTXDB_LASTLOGIN:
62		if (file == NULL)
63			file = _PATH_UTX_LASTLOGIN;
64		break;
65	case UTXDB_LOG:
66		if (file == NULL)
67			file = _PATH_UTX_LOG;
68		break;
69	default:
70		errno = EINVAL;
71		return (-1);
72	}
73
74	if (uf != NULL)
75		fclose(uf);
76	uf = fopen(file, "re");
77	if (uf == NULL)
78		return (-1);
79
80	if (db != UTXDB_LOG) {
81		/* Safety check: never use broken files. */
82		if (_fstat(fileno(uf), &sb) != -1 &&
83		    sb.st_size % sizeof(struct futx) != 0) {
84			fclose(uf);
85			uf = NULL;
86			errno = EFTYPE;
87			return (-1);
88		}
89		/* Prevent reading of partial records. */
90		(void)setvbuf(uf, NULL, _IOFBF,
91		    rounddown(BUFSIZ, sizeof(struct futx)));
92	}
93
94	udb = db;
95	return (0);
96}
97
98void
99setutxent(void)
100{
101
102	setutxdb(UTXDB_ACTIVE, NULL);
103}
104
105void
106endutxent(void)
107{
108
109	if (uf != NULL) {
110		fclose(uf);
111		uf = NULL;
112	}
113}
114
115static int
116getfutxent(struct futx *fu)
117{
118
119	if (uf == NULL)
120		setutxent();
121	if (uf == NULL)
122		return (-1);
123
124	if (udb == UTXDB_LOG) {
125		uint16_t len;
126
127retry:
128		if (fread(&len, sizeof(len), 1, uf) != 1)
129			return (-1);
130		len = be16toh(len);
131		if (len == 0) {
132			/*
133			 * XXX: Though zero-size records are valid in theory,
134			 * they can never occur in practice. Zero-size records
135			 * indicate file corruption. Seek one byte forward, to
136			 * see if we can find a record there.
137			 */
138			ungetc('\0', uf);
139			goto retry;
140		}
141		if (len > sizeof *fu) {
142			/* Forward compatibility. */
143			if (fread(fu, sizeof(*fu), 1, uf) != 1)
144				return (-1);
145			fseek(uf, len - sizeof(*fu), SEEK_CUR);
146		} else {
147			/* Partial record. */
148			memset(fu, 0, sizeof(*fu));
149			if (fread(fu, len, 1, uf) != 1)
150				return (-1);
151		}
152	} else {
153		if (fread(fu, sizeof(*fu), 1, uf) != 1)
154			return (-1);
155	}
156	return (0);
157}
158
159struct utmpx *
160getutxent(void)
161{
162	struct futx fu;
163
164	if (getfutxent(&fu) != 0)
165		return (NULL);
166	return (futx_to_utx(&fu));
167}
168
169struct utmpx *
170getutxid(const struct utmpx *id)
171{
172	struct futx fu;
173
174	for (;;) {
175		if (getfutxent(&fu) != 0)
176			return (NULL);
177
178		switch (fu.fu_type) {
179		case USER_PROCESS:
180		case INIT_PROCESS:
181		case LOGIN_PROCESS:
182		case DEAD_PROCESS:
183			switch (id->ut_type) {
184			case USER_PROCESS:
185			case INIT_PROCESS:
186			case LOGIN_PROCESS:
187			case DEAD_PROCESS:
188				if (memcmp(fu.fu_id, id->ut_id,
189				    MIN(sizeof(fu.fu_id), sizeof(id->ut_id))) ==
190				    0)
191					goto found;
192			}
193			break;
194		default:
195			if (fu.fu_type == id->ut_type)
196				goto found;
197			break;
198		}
199	}
200
201found:
202	return (futx_to_utx(&fu));
203}
204
205struct utmpx *
206getutxline(const struct utmpx *line)
207{
208	struct futx fu;
209
210	for (;;) {
211		if (getfutxent(&fu) != 0)
212			return (NULL);
213
214		switch (fu.fu_type) {
215		case USER_PROCESS:
216		case LOGIN_PROCESS:
217			if (strncmp(fu.fu_line, line->ut_line,
218			    MIN(sizeof(fu.fu_line), sizeof(line->ut_line))) ==
219			    0)
220				goto found;
221			break;
222		}
223	}
224
225found:
226	return (futx_to_utx(&fu));
227}
228
229struct utmpx *
230getutxuser(const char *user)
231{
232	struct futx fu;
233
234	for (;;) {
235		if (getfutxent(&fu) != 0)
236			return (NULL);
237
238		switch (fu.fu_type) {
239		case USER_PROCESS:
240			if (strncmp(fu.fu_user, user, sizeof(fu.fu_user)) == 0)
241				goto found;
242			break;
243		}
244	}
245
246found:
247	return (futx_to_utx(&fu));
248}
249