1202188Sed/*-
2202188Sed * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
3202188Sed * All rights reserved.
4202188Sed *
5202188Sed * Redistribution and use in source and binary forms, with or without
6202188Sed * modification, are permitted provided that the following conditions
7202188Sed * are met:
8202188Sed * 1. Redistributions of source code must retain the above copyright
9202188Sed *    notice, this list of conditions and the following disclaimer.
10202188Sed * 2. Redistributions in binary form must reproduce the above copyright
11202188Sed *    notice, this list of conditions and the following disclaimer in the
12202188Sed *    documentation and/or other materials provided with the distribution.
13202188Sed *
14202188Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15202188Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16202188Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17202188Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18202188Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19202188Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20202188Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21202188Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22202188Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23202188Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24202188Sed * SUCH DAMAGE.
25202188Sed */
26202188Sed
27202188Sed#include <sys/cdefs.h>
28202188Sed__FBSDID("$FreeBSD$");
29202188Sed
30202188Sed#include "namespace.h"
31202188Sed#include <sys/endian.h>
32202188Sed#include <sys/param.h>
33202188Sed#include <sys/stat.h>
34202188Sed#include <errno.h>
35202188Sed#include <stdio.h>
36202188Sed#include <string.h>
37202188Sed#include <utmpx.h>
38202188Sed#include "utxdb.h"
39202188Sed#include "un-namespace.h"
40202188Sed
41233345Sed#ifdef __NO_TLS
42202188Sedstatic FILE *uf = NULL;
43202188Sedstatic int udb;
44233345Sed#else
45233345Sedstatic _Thread_local FILE *uf = NULL;
46233345Sedstatic _Thread_local int udb;
47233345Sed#endif
48202188Sed
49202188Sedint
50202188Sedsetutxdb(int db, const char *file)
51202188Sed{
52202188Sed	struct stat sb;
53202188Sed
54202188Sed	switch (db) {
55202188Sed	case UTXDB_ACTIVE:
56202188Sed		if (file == NULL)
57202188Sed			file = _PATH_UTX_ACTIVE;
58202188Sed		break;
59202188Sed	case UTXDB_LASTLOGIN:
60202188Sed		if (file == NULL)
61202188Sed			file = _PATH_UTX_LASTLOGIN;
62202188Sed		break;
63202188Sed	case UTXDB_LOG:
64202188Sed		if (file == NULL)
65202188Sed			file = _PATH_UTX_LOG;
66202188Sed		break;
67202188Sed	default:
68202188Sed		errno = EINVAL;
69202188Sed		return (-1);
70202188Sed	}
71202188Sed
72202188Sed	if (uf != NULL)
73202188Sed		fclose(uf);
74244092Sjilles	uf = fopen(file, "re");
75202188Sed	if (uf == NULL)
76202188Sed		return (-1);
77202188Sed
78231514Sed	if (db != UTXDB_LOG) {
79231514Sed		/* Safety check: never use broken files. */
80231514Sed		if (_fstat(fileno(uf), &sb) != -1 &&
81231514Sed		    sb.st_size % sizeof(struct futx) != 0) {
82231514Sed			fclose(uf);
83231514Sed			uf = NULL;
84231514Sed			errno = EFTYPE;
85231514Sed			return (-1);
86231514Sed		}
87231514Sed		/* Prevent reading of partial records. */
88231514Sed		(void)setvbuf(uf, NULL, _IOFBF,
89231514Sed		    rounddown(BUFSIZ, sizeof(struct futx)));
90202188Sed	}
91202188Sed
92202188Sed	udb = db;
93202188Sed	return (0);
94202188Sed}
95202188Sed
96202188Sedvoid
97202188Sedsetutxent(void)
98202188Sed{
99202188Sed
100202188Sed	setutxdb(UTXDB_ACTIVE, NULL);
101202188Sed}
102202188Sed
103202188Sedvoid
104202188Sedendutxent(void)
105202188Sed{
106202188Sed
107202188Sed	if (uf != NULL) {
108202188Sed		fclose(uf);
109202188Sed		uf = NULL;
110202188Sed	}
111202188Sed}
112202188Sed
113202530Sedstatic int
114202530Sedgetfutxent(struct futx *fu)
115202188Sed{
116202188Sed
117202188Sed	if (uf == NULL)
118202188Sed		setutxent();
119202188Sed	if (uf == NULL)
120202530Sed		return (-1);
121202188Sed
122202188Sed	if (udb == UTXDB_LOG) {
123202188Sed		uint16_t len;
124202188Sed
125257320Sglebiusretry:
126218846Sed		if (fread(&len, sizeof(len), 1, uf) != 1)
127202530Sed			return (-1);
128202188Sed		len = be16toh(len);
129257320Sglebius		if (len == 0) {
130257320Sglebius			/*
131257320Sglebius			 * XXX: Though zero-size records are valid in theory,
132257320Sglebius			 * they can never occur in practice. Zero-size records
133257320Sglebius			 * indicate file corruption. Seek one byte forward, to
134257320Sglebius			 * see if we can find a record there.
135257320Sglebius			 */
136257320Sglebius			ungetc('\0', uf);
137257320Sglebius			goto retry;
138257320Sglebius		}
139202530Sed		if (len > sizeof *fu) {
140202188Sed			/* Forward compatibility. */
141218846Sed			if (fread(fu, sizeof(*fu), 1, uf) != 1)
142202530Sed				return (-1);
143218846Sed			fseek(uf, len - sizeof(*fu), SEEK_CUR);
144202188Sed		} else {
145202188Sed			/* Partial record. */
146218846Sed			memset(fu, 0, sizeof(*fu));
147202530Sed			if (fread(fu, len, 1, uf) != 1)
148202530Sed				return (-1);
149202188Sed		}
150202188Sed	} else {
151218846Sed		if (fread(fu, sizeof(*fu), 1, uf) != 1)
152202530Sed			return (-1);
153202188Sed	}
154202530Sed	return (0);
155202188Sed}
156202188Sed
157202188Sedstruct utmpx *
158202188Sedgetutxent(void)
159202188Sed{
160202530Sed	struct futx fu;
161202188Sed
162202530Sed	if (getfutxent(&fu) != 0)
163202188Sed		return (NULL);
164202530Sed	return (futx_to_utx(&fu));
165202188Sed}
166202188Sed
167202188Sedstruct utmpx *
168202188Sedgetutxid(const struct utmpx *id)
169202188Sed{
170202530Sed	struct futx fu;
171202188Sed
172202188Sed	for (;;) {
173202530Sed		if (getfutxent(&fu) != 0)
174202188Sed			return (NULL);
175202188Sed
176202530Sed		switch (fu.fu_type) {
177202188Sed		case USER_PROCESS:
178202188Sed		case INIT_PROCESS:
179202188Sed		case LOGIN_PROCESS:
180202188Sed		case DEAD_PROCESS:
181202188Sed			switch (id->ut_type) {
182202188Sed			case USER_PROCESS:
183202188Sed			case INIT_PROCESS:
184202188Sed			case LOGIN_PROCESS:
185202188Sed			case DEAD_PROCESS:
186202530Sed				if (memcmp(fu.fu_id, id->ut_id,
187218846Sed				    MIN(sizeof(fu.fu_id), sizeof(id->ut_id))) ==
188218846Sed				    0)
189202188Sed					goto found;
190202188Sed			}
191202527Sed			break;
192202530Sed		default:
193202530Sed			if (fu.fu_type == id->ut_type)
194202530Sed				goto found;
195202530Sed			break;
196202188Sed		}
197202188Sed	}
198202188Sed
199202188Sedfound:
200202530Sed	return (futx_to_utx(&fu));
201202188Sed}
202202188Sed
203202188Sedstruct utmpx *
204202188Sedgetutxline(const struct utmpx *line)
205202188Sed{
206202530Sed	struct futx fu;
207202188Sed
208202188Sed	for (;;) {
209202530Sed		if (getfutxent(&fu) != 0)
210202188Sed			return (NULL);
211202188Sed
212202530Sed		switch (fu.fu_type) {
213202188Sed		case USER_PROCESS:
214202188Sed		case LOGIN_PROCESS:
215202530Sed			if (strncmp(fu.fu_line, line->ut_line,
216218846Sed			    MIN(sizeof(fu.fu_line), sizeof(line->ut_line))) ==
217218846Sed			    0)
218202188Sed				goto found;
219202530Sed			break;
220202188Sed		}
221202188Sed	}
222202188Sed
223202188Sedfound:
224202530Sed	return (futx_to_utx(&fu));
225202188Sed}
226202188Sed
227202188Sedstruct utmpx *
228202188Sedgetutxuser(const char *user)
229202188Sed{
230202530Sed	struct futx fu;
231202188Sed
232202188Sed	for (;;) {
233202530Sed		if (getfutxent(&fu) != 0)
234202188Sed			return (NULL);
235202188Sed
236202530Sed		switch (fu.fu_type) {
237202188Sed		case USER_PROCESS:
238218846Sed			if (strncmp(fu.fu_user, user, sizeof(fu.fu_user)) == 0)
239202188Sed				goto found;
240202530Sed			break;
241202188Sed		}
242202188Sed	}
243202188Sed
244202188Sedfound:
245202530Sed	return (futx_to_utx(&fu));
246202188Sed}
247