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
41static FILE *uf = NULL;
42static int udb;
43
44int
45setutxdb(int db, const char *file)
46{
47	struct stat sb;
48
49	switch (db) {
50	case UTXDB_ACTIVE:
51		if (file == NULL)
52			file = _PATH_UTX_ACTIVE;
53		break;
54	case UTXDB_LASTLOGIN:
55		if (file == NULL)
56			file = _PATH_UTX_LASTLOGIN;
57		break;
58	case UTXDB_LOG:
59		if (file == NULL)
60			file = _PATH_UTX_LOG;
61		break;
62	default:
63		errno = EINVAL;
64		return (-1);
65	}
66
67	if (uf != NULL)
68		fclose(uf);
69	uf = fopen(file, "r");
70	if (uf == NULL)
71		return (-1);
72
73	if (db != UTXDB_LOG) {
74		/* Safety check: never use broken files. */
75		if (_fstat(fileno(uf), &sb) != -1 &&
76		    sb.st_size % sizeof(struct futx) != 0) {
77			fclose(uf);
78			uf = NULL;
79			errno = EFTYPE;
80			return (-1);
81		}
82		/* Prevent reading of partial records. */
83		(void)setvbuf(uf, NULL, _IOFBF,
84		    rounddown(BUFSIZ, sizeof(struct futx)));
85	}
86
87	udb = db;
88	return (0);
89}
90
91void
92setutxent(void)
93{
94
95	setutxdb(UTXDB_ACTIVE, NULL);
96}
97
98void
99endutxent(void)
100{
101
102	if (uf != NULL) {
103		fclose(uf);
104		uf = NULL;
105	}
106}
107
108static int
109getfutxent(struct futx *fu)
110{
111
112	if (uf == NULL)
113		setutxent();
114	if (uf == NULL)
115		return (-1);
116
117	if (udb == UTXDB_LOG) {
118		uint16_t len;
119
120		if (fread(&len, sizeof(len), 1, uf) != 1)
121			return (-1);
122		len = be16toh(len);
123		if (len > sizeof *fu) {
124			/* Forward compatibility. */
125			if (fread(fu, sizeof(*fu), 1, uf) != 1)
126				return (-1);
127			fseek(uf, len - sizeof(*fu), SEEK_CUR);
128		} else {
129			/* Partial record. */
130			memset(fu, 0, sizeof(*fu));
131			if (fread(fu, len, 1, uf) != 1)
132				return (-1);
133		}
134	} else {
135		if (fread(fu, sizeof(*fu), 1, uf) != 1)
136			return (-1);
137	}
138	return (0);
139}
140
141struct utmpx *
142getutxent(void)
143{
144	struct futx fu;
145
146	if (getfutxent(&fu) != 0)
147		return (NULL);
148	return (futx_to_utx(&fu));
149}
150
151struct utmpx *
152getutxid(const struct utmpx *id)
153{
154	struct futx fu;
155
156	for (;;) {
157		if (getfutxent(&fu) != 0)
158			return (NULL);
159
160		switch (fu.fu_type) {
161		case USER_PROCESS:
162		case INIT_PROCESS:
163		case LOGIN_PROCESS:
164		case DEAD_PROCESS:
165			switch (id->ut_type) {
166			case USER_PROCESS:
167			case INIT_PROCESS:
168			case LOGIN_PROCESS:
169			case DEAD_PROCESS:
170				if (memcmp(fu.fu_id, id->ut_id,
171				    MIN(sizeof(fu.fu_id), sizeof(id->ut_id))) ==
172				    0)
173					goto found;
174			}
175			break;
176		default:
177			if (fu.fu_type == id->ut_type)
178				goto found;
179			break;
180		}
181	}
182
183found:
184	return (futx_to_utx(&fu));
185}
186
187struct utmpx *
188getutxline(const struct utmpx *line)
189{
190	struct futx fu;
191
192	for (;;) {
193		if (getfutxent(&fu) != 0)
194			return (NULL);
195
196		switch (fu.fu_type) {
197		case USER_PROCESS:
198		case LOGIN_PROCESS:
199			if (strncmp(fu.fu_line, line->ut_line,
200			    MIN(sizeof(fu.fu_line), sizeof(line->ut_line))) ==
201			    0)
202				goto found;
203			break;
204		}
205	}
206
207found:
208	return (futx_to_utx(&fu));
209}
210
211struct utmpx *
212getutxuser(const char *user)
213{
214	struct futx fu;
215
216	for (;;) {
217		if (getfutxent(&fu) != 0)
218			return (NULL);
219
220		switch (fu.fu_type) {
221		case USER_PROCESS:
222			if (strncmp(fu.fu_user, user, sizeof(fu.fu_user)) == 0)
223				goto found;
224			break;
225		}
226	}
227
228found:
229	return (futx_to_utx(&fu));
230}
231