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