xref: /freebsd/lib/libc/gen/getutxent.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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
42 static FILE *uf = NULL;
43 static int udb;
44 #else
45 static _Thread_local FILE *uf = NULL;
46 static _Thread_local int udb;
47 #endif
48 
49 int
50 setutxdb(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 
96 void
97 setutxent(void)
98 {
99 
100 	setutxdb(UTXDB_ACTIVE, NULL);
101 }
102 
103 void
104 endutxent(void)
105 {
106 
107 	if (uf != NULL) {
108 		fclose(uf);
109 		uf = NULL;
110 	}
111 }
112 
113 static int
114 getfutxent(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 
125 retry:
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 
157 struct utmpx *
158 getutxent(void)
159 {
160 	struct futx fu;
161 
162 	if (getfutxent(&fu) != 0)
163 		return (NULL);
164 	return (futx_to_utx(&fu));
165 }
166 
167 struct utmpx *
168 getutxid(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 
199 found:
200 	return (futx_to_utx(&fu));
201 }
202 
203 struct utmpx *
204 getutxline(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 
223 found:
224 	return (futx_to_utx(&fu));
225 }
226 
227 struct utmpx *
228 getutxuser(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 
244 found:
245 	return (futx_to_utx(&fu));
246 }
247