xref: /netbsd/lib/libc/gen/utmpx.c (revision c4a72b64)
1 /*	$NetBSD: utmpx.c,v 1.16 2002/11/26 16:52:07 christos Exp $	 */
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 #include <sys/cdefs.h>
39 
40 #if defined(LIBC_SCCS) && !defined(lint)
41 __RCSID("$NetBSD: utmpx.c,v 1.16 2002/11/26 16:52:07 christos Exp $");
42 #endif /* LIBC_SCCS and not lint */
43 
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/wait.h>
47 #include <sys/socket.h>
48 #include <sys/time.h>
49 #include <sys/stat.h>
50 
51 #include <assert.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <vis.h>
56 #include <utmp.h>
57 #include <utmpx.h>
58 #include <unistd.h>
59 #include <fcntl.h>
60 #include <errno.h>
61 #include <db.h>
62 
63 static FILE *fp;
64 static struct utmpx ut;
65 static char utfile[MAXPATHLEN] = _PATH_UTMPX;
66 static char llfile[MAXPATHLEN] = _PATH_LASTLOGX;
67 
68 static struct utmpx *utmp_update(const struct utmpx *);
69 
70 static const char vers[] = "utmpx-1.00";
71 
72 void
73 setutxent()
74 {
75 
76 	(void)memset(&ut, 0, sizeof(ut));
77 	if (fp == NULL)
78 		return;
79 	(void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET);
80 }
81 
82 
83 void
84 endutxent()
85 {
86 
87 	(void)memset(&ut, 0, sizeof(ut));
88 	if (fp != NULL) {
89 		(void)fclose(fp);
90 		fp = NULL;
91 	}
92 }
93 
94 
95 struct utmpx *
96 getutxent()
97 {
98 
99 	if (fp == NULL) {
100 		struct stat st;
101 
102 		if ((fp = fopen(utfile, "r+")) == NULL)
103 			if ((fp = fopen(utfile, "w+")) == NULL)
104 				if ((fp = fopen(utfile, "r")) == NULL)
105 					goto fail;
106 
107 		/* get file size in order to check if new file */
108 		if (fstat(fileno(fp), &st) == -1)
109 			goto failclose;
110 
111 		if (st.st_size == 0) {
112 			/* new file, add signature record */
113 			(void)memset(&ut, 0, sizeof(ut));
114 			ut.ut_type = SIGNATURE;
115 			(void)memcpy(ut.ut_user, vers, sizeof(vers));
116 			if (fwrite(&ut, sizeof(ut), 1, fp) != 1)
117 				goto failclose;
118 		} else {
119 			/* old file, read signature record */
120 			if (fread(&ut, sizeof(ut), 1, fp) != 1)
121 				goto failclose;
122 			if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 ||
123 			    ut.ut_type != SIGNATURE)
124 				goto failclose;
125 		}
126 	}
127 
128 	if (fread(&ut, sizeof(ut), 1, fp) != 1)
129 		goto fail;
130 
131 	return &ut;
132 failclose:
133 	(void)fclose(fp);
134 fail:
135 	(void)memset(&ut, 0, sizeof(ut));
136 	return NULL;
137 }
138 
139 
140 struct utmpx *
141 getutxid(const struct utmpx *utx)
142 {
143 
144 	_DIAGASSERT(utx != NULL);
145 
146 	if (utx->ut_type == EMPTY)
147 		return NULL;
148 
149 	do {
150 		if (ut.ut_type == EMPTY)
151 			continue;
152 		switch (utx->ut_type) {
153 		case EMPTY:
154 			return NULL;
155 		case RUN_LVL:
156 		case BOOT_TIME:
157 		case OLD_TIME:
158 		case NEW_TIME:
159 			if (ut.ut_type == utx->ut_type)
160 				return &ut;
161 			break;
162 		case INIT_PROCESS:
163 		case LOGIN_PROCESS:
164 		case USER_PROCESS:
165 		case DEAD_PROCESS:
166 			switch (ut.ut_type) {
167 			case INIT_PROCESS:
168 			case LOGIN_PROCESS:
169 			case USER_PROCESS:
170 			case DEAD_PROCESS:
171 				if (memcmp(ut.ut_id, utx->ut_id,
172 				    sizeof(ut.ut_id)) == 0)
173 					return &ut;
174 				break;
175 			default:
176 				break;
177 			}
178 			break;
179 		default:
180 			return NULL;
181 		}
182 	} while (getutxent() != NULL);
183 	return NULL;
184 }
185 
186 
187 struct utmpx *
188 getutxline(const struct utmpx *utx)
189 {
190 
191 	_DIAGASSERT(utx != NULL);
192 
193 	do {
194 		switch (ut.ut_type) {
195 		case EMPTY:
196 			break;
197 		case LOGIN_PROCESS:
198 		case USER_PROCESS:
199 			if (strncmp(ut.ut_line, utx->ut_line,
200 			    sizeof(ut.ut_line)) == 0)
201 				return &ut;
202 			break;
203 		default:
204 			break;
205 		}
206 	} while (getutxent() != NULL);
207 	return NULL;
208 }
209 
210 
211 struct utmpx *
212 pututxline(const struct utmpx *utx)
213 {
214 	struct utmpx temp, *u = NULL;
215 	int gotlock = 0;
216 
217 	_DIAGASSERT(utx != NULL);
218 
219 	if (strcmp(_PATH_UTMPX, utfile) == 0 && geteuid() != 0)
220 		return utmp_update(utx);
221 
222 	if (utx == NULL)
223 		return NULL;
224 
225 	(void)memcpy(&temp, utx, sizeof(temp));
226 
227 	if (fp == NULL) {
228 		(void)getutxent();
229 		if (fp == NULL)
230 			return NULL;
231 	}
232 
233 	if (getutxid(&temp) == NULL) {
234 		setutxent();
235 		if (getutxid(&temp) == NULL) {
236 			if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1)
237 				return NULL;
238 			gotlock++;
239 			if (fseeko(fp, (off_t)0, SEEK_END) == -1)
240 				goto fail;
241 		}
242 	}
243 
244 	if (!gotlock) {
245 		/* we are not appending */
246 		if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1)
247 			return NULL;
248 	}
249 
250 	if (fwrite(&temp, sizeof (temp), 1, fp) != 1)
251 		goto fail;
252 
253 	if (fflush(fp) == -1)
254 		goto fail;
255 
256 	u = memcpy(&ut, &temp, sizeof(ut));
257 fail:
258 	if (gotlock) {
259 		if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1)
260 			return NULL;
261 	}
262 	return u;
263 }
264 
265 
266 static struct utmpx *
267 utmp_update(const struct utmpx *utx)
268 {
269 	char buf[sizeof(*utx) * 4 + 1];
270 	pid_t pid;
271 	int status;
272 
273 	_DIAGASSERT(utx != NULL);
274 
275 	(void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx),
276 	    VIS_WHITE);
277 	switch (pid = fork()) {
278 	case 0:
279 		(void)execl(_PATH_UTMP_UPDATE,
280 		    strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL);
281 		exit(1);
282 		/*NOTREACHED*/
283 	case -1:
284 		return NULL;
285 	default:
286 		if (waitpid(pid, &status, 0) == -1)
287 			return NULL;
288 		if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
289 			return memcpy(&ut, utx, sizeof(ut));
290 		return NULL;
291 	}
292 
293 }
294 
295 /*
296  * The following are extensions and not part of the X/Open spec.
297  */
298 int
299 updwtmpx(const char *file, const struct utmpx *utx)
300 {
301 	int fd;
302 
303 	_DIAGASSERT(file != NULL);
304 	_DIAGASSERT(utx != NULL);
305 
306 	fd = open(file, O_WRONLY|O_APPEND|O_EXLOCK);
307 
308 	if (fd == -1) {
309 		if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1)
310 			return -1;
311 		(void)memset(&ut, 0, sizeof(ut));
312 		ut.ut_type = SIGNATURE;
313 		(void)memcpy(ut.ut_user, vers, sizeof(vers));
314 		if (write(fd, &ut, sizeof(ut)) == -1)
315 			return -1;
316 	}
317 	if (write(fd, utx, sizeof(*utx)) == -1)
318 		return -1;
319 	if (close(fd) == -1)
320 		return -1;
321 	return 0;
322 }
323 
324 
325 int
326 utmpxname(const char *fname)
327 {
328 	size_t len;
329 
330 	_DIAGASSERT(fname != NULL);
331 
332 	len = strlen(fname);
333 
334 	if (len >= sizeof(utfile))
335 		return 0;
336 
337 	/* must end in x! */
338 	if (fname[len - 1] != 'x')
339 		return 0;
340 
341 	(void)strlcpy(utfile, fname, sizeof(utfile));
342 	endutxent();
343 	return 1;
344 }
345 
346 
347 void
348 getutmp(const struct utmpx *ux, struct utmp *u)
349 {
350 
351 	_DIAGASSERT(ux != NULL);
352 	_DIAGASSERT(u != NULL);
353 
354 	(void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name));
355 	(void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
356 	(void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
357 	u->ut_time = ux->ut_tv.tv_sec;
358 }
359 
360 void
361 getutmpx(const struct utmp *u, struct utmpx *ux)
362 {
363 
364 	_DIAGASSERT(ux != NULL);
365 	_DIAGASSERT(u != NULL);
366 
367 	(void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name));
368 	(void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line));
369 	(void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host));
370 	ux->ut_tv.tv_sec = u->ut_time;
371 	ux->ut_tv.tv_usec = 0;
372 	(void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss));
373 	ux->ut_pid = 0;
374 	ux->ut_type = USER_PROCESS;
375 	ux->ut_session = 0;
376 	ux->ut_exit.e_termination = 0;
377 	ux->ut_exit.e_exit = 0;
378 }
379 
380 int
381 lastlogxname(const char *fname)
382 {
383 	size_t len;
384 
385 	_DIAGASSERT(fname != NULL);
386 
387 	len = strlen(fname);
388 
389 	if (len >= sizeof(llfile))
390 		return 0;
391 
392 	/* must end in x! */
393 	if (fname[len - 1] != 'x')
394 		return 0;
395 
396 	(void)strlcpy(llfile, fname, sizeof(llfile));
397 	return 1;
398 }
399 
400 struct lastlogx *
401 getlastlogx(uid_t uid, struct lastlogx *ll)
402 {
403 	DBT key, data;
404 	DB *db;
405 
406 	_DIAGASSERT(ll != NULL);
407 
408 	db = dbopen(llfile, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
409 
410 	if (db == NULL)
411 		return NULL;
412 
413 	key.data = &uid;
414 	key.size = sizeof(uid);
415 
416 	if ((db->get)(db, &key, &data, 0) != 0)
417 		goto error;
418 
419 	if (data.size != sizeof(*ll)) {
420 		errno = EFTYPE;
421 		goto error;
422 	}
423 
424 	if (ll == NULL)
425 		if ((ll = malloc(sizeof(*ll))) == NULL)
426 			goto done;
427 
428 	(void)memcpy(ll, data.data, sizeof(*ll));
429 	goto done;
430 error:
431 	ll = NULL;
432 done:
433 	(db->close)(db);
434 	return ll;
435 }
436 
437 int
438 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll)
439 {
440 	DBT key, data;
441 	int error = 0;
442 	DB *db;
443 
444 	_DIAGASSERT(fname != NULL);
445 	_DIAGASSERT(ll != NULL);
446 
447 	db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0, DB_HASH, NULL);
448 
449 	if (db == NULL)
450 		return -1;
451 
452 	key.data = &uid;
453 	key.size = sizeof(uid);
454 	data.data = ll;
455 	data.size = sizeof(*ll);
456 	if ((db->put)(db, &key, &data, 0) != 0)
457 		error = -1;
458 
459 	(db->close)(db);
460 	return error;
461 }
462