xref: /netbsd-src/lib/libc/gen/utmpx.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: utmpx.c,v 1.22 2004/11/11 22:14:20 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.22 2004/11/11 22:14:20 christos Exp $");
42 #endif /* LIBC_SCCS and not lint */
43 
44 #include "namespace.h"
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/socket.h>
48 #include <sys/stat.h>
49 #include <sys/time.h>
50 #include <sys/wait.h>
51 
52 #include <assert.h>
53 #include <db.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <utmp.h>
61 /* don't define earlier, has side effects in fcntl.h */
62 #define __LIBC12_SOURCE__
63 #include <utmpx.h>
64 #include <vis.h>
65 
66 __warn_references(getlastlogx,
67     "warning: reference to compatibility getlastlogx(); include <utmpx.h> for correct reference")
68 __warn_references(lastlogxname,
69     "warning: reference to deprecated lastlogxname()")
70 
71 static FILE *fp;
72 static int readonly = 0;
73 static struct utmpx ut;
74 static char utfile[MAXPATHLEN] = _PATH_UTMPX;
75 static char llfile[MAXPATHLEN] = _PATH_LASTLOGX;
76 
77 static struct utmpx *utmp_update(const struct utmpx *);
78 
79 static const char vers[] = "utmpx-1.00";
80 
81 void
82 setutxent()
83 {
84 
85 	(void)memset(&ut, 0, sizeof(ut));
86 	if (fp == NULL)
87 		return;
88 	(void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET);
89 }
90 
91 
92 void
93 endutxent()
94 {
95 
96 	(void)memset(&ut, 0, sizeof(ut));
97 	if (fp != NULL) {
98 		(void)fclose(fp);
99 		fp = NULL;
100 		readonly = 0;
101 	}
102 }
103 
104 
105 struct utmpx *
106 getutxent()
107 {
108 
109 	if (fp == NULL) {
110 		struct stat st;
111 
112 		if ((fp = fopen(utfile, "r+")) == NULL)
113 			if ((fp = fopen(utfile, "w+")) == NULL) {
114 				if ((fp = fopen(utfile, "r")) == NULL)
115 					goto fail;
116 				else
117 					readonly = 1;
118 			}
119 
120 
121 		/* get file size in order to check if new file */
122 		if (fstat(fileno(fp), &st) == -1)
123 			goto failclose;
124 
125 		if (st.st_size == 0) {
126 			/* new file, add signature record */
127 			(void)memset(&ut, 0, sizeof(ut));
128 			ut.ut_type = SIGNATURE;
129 			(void)memcpy(ut.ut_user, vers, sizeof(vers));
130 			if (fwrite(&ut, sizeof(ut), 1, fp) != 1)
131 				goto failclose;
132 		} else {
133 			/* old file, read signature record */
134 			if (fread(&ut, sizeof(ut), 1, fp) != 1)
135 				goto failclose;
136 			if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 ||
137 			    ut.ut_type != SIGNATURE)
138 				goto failclose;
139 		}
140 	}
141 
142 	if (fread(&ut, sizeof(ut), 1, fp) != 1)
143 		goto fail;
144 
145 	return &ut;
146 failclose:
147 	(void)fclose(fp);
148 fail:
149 	(void)memset(&ut, 0, sizeof(ut));
150 	return NULL;
151 }
152 
153 
154 struct utmpx *
155 getutxid(const struct utmpx *utx)
156 {
157 
158 	_DIAGASSERT(utx != NULL);
159 
160 	if (utx->ut_type == EMPTY)
161 		return NULL;
162 
163 	do {
164 		if (ut.ut_type == EMPTY)
165 			continue;
166 		switch (utx->ut_type) {
167 		case EMPTY:
168 			return NULL;
169 		case RUN_LVL:
170 		case BOOT_TIME:
171 		case OLD_TIME:
172 		case NEW_TIME:
173 			if (ut.ut_type == utx->ut_type)
174 				return &ut;
175 			break;
176 		case INIT_PROCESS:
177 		case LOGIN_PROCESS:
178 		case USER_PROCESS:
179 		case DEAD_PROCESS:
180 			switch (ut.ut_type) {
181 			case INIT_PROCESS:
182 			case LOGIN_PROCESS:
183 			case USER_PROCESS:
184 			case DEAD_PROCESS:
185 				if (memcmp(ut.ut_id, utx->ut_id,
186 				    sizeof(ut.ut_id)) == 0)
187 					return &ut;
188 				break;
189 			default:
190 				break;
191 			}
192 			break;
193 		default:
194 			return NULL;
195 		}
196 	} while (getutxent() != NULL);
197 	return NULL;
198 }
199 
200 
201 struct utmpx *
202 getutxline(const struct utmpx *utx)
203 {
204 
205 	_DIAGASSERT(utx != NULL);
206 
207 	do {
208 		switch (ut.ut_type) {
209 		case EMPTY:
210 			break;
211 		case LOGIN_PROCESS:
212 		case USER_PROCESS:
213 			if (strncmp(ut.ut_line, utx->ut_line,
214 			    sizeof(ut.ut_line)) == 0)
215 				return &ut;
216 			break;
217 		default:
218 			break;
219 		}
220 	} while (getutxent() != NULL);
221 	return NULL;
222 }
223 
224 
225 struct utmpx *
226 pututxline(const struct utmpx *utx)
227 {
228 	struct utmpx temp, *u = NULL;
229 	int gotlock = 0;
230 
231 	_DIAGASSERT(utx != NULL);
232 
233 	if (utx == NULL)
234 		return NULL;
235 
236 	if (strcmp(_PATH_UTMPX, utfile) == 0)
237 		if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0))
238 			return utmp_update(utx);
239 
240 
241 	(void)memcpy(&temp, utx, sizeof(temp));
242 
243 	if (fp == NULL) {
244 		(void)getutxent();
245 		if (fp == NULL || readonly)
246 			return NULL;
247 	}
248 
249 	if (getutxid(&temp) == NULL) {
250 		setutxent();
251 		if (getutxid(&temp) == NULL) {
252 			if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1)
253 				return NULL;
254 			gotlock++;
255 			if (fseeko(fp, (off_t)0, SEEK_END) == -1)
256 				goto fail;
257 		}
258 	}
259 
260 	if (!gotlock) {
261 		/* we are not appending */
262 		if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1)
263 			return NULL;
264 	}
265 
266 	if (fwrite(&temp, sizeof (temp), 1, fp) != 1)
267 		goto fail;
268 
269 	if (fflush(fp) == -1)
270 		goto fail;
271 
272 	u = memcpy(&ut, &temp, sizeof(ut));
273 fail:
274 	if (gotlock) {
275 		if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1)
276 			return NULL;
277 	}
278 	return u;
279 }
280 
281 
282 static struct utmpx *
283 utmp_update(const struct utmpx *utx)
284 {
285 	char buf[sizeof(*utx) * 4 + 1];
286 	pid_t pid;
287 	int status;
288 
289 	_DIAGASSERT(utx != NULL);
290 
291 	(void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx),
292 	    VIS_WHITE);
293 	switch (pid = fork()) {
294 	case 0:
295 		(void)execl(_PATH_UTMP_UPDATE,
296 		    strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL);
297 		exit(1);
298 		/*NOTREACHED*/
299 	case -1:
300 		return NULL;
301 	default:
302 		if (waitpid(pid, &status, 0) == -1)
303 			return NULL;
304 		if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
305 			return memcpy(&ut, utx, sizeof(ut));
306 		return NULL;
307 	}
308 
309 }
310 
311 /*
312  * The following are extensions and not part of the X/Open spec.
313  */
314 int
315 updwtmpx(const char *file, const struct utmpx *utx)
316 {
317 	int fd;
318 	int saved_errno;
319 
320 	_DIAGASSERT(file != NULL);
321 	_DIAGASSERT(utx != NULL);
322 
323 	fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK);
324 
325 	if (fd == -1) {
326 		if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1)
327 			return -1;
328 		(void)memset(&ut, 0, sizeof(ut));
329 		ut.ut_type = SIGNATURE;
330 		(void)memcpy(ut.ut_user, vers, sizeof(vers));
331 		if (write(fd, &ut, sizeof(ut)) == -1)
332 			goto failed;
333 	}
334 	if (write(fd, utx, sizeof(*utx)) == -1)
335 		goto failed;
336 	if (close(fd) == -1)
337 		return -1;
338 	return 0;
339 
340   failed:
341 	saved_errno = errno;
342 	(void) close(fd);
343 	errno = saved_errno;
344 	return -1;
345 }
346 
347 
348 int
349 utmpxname(const char *fname)
350 {
351 	size_t len;
352 
353 	_DIAGASSERT(fname != NULL);
354 
355 	len = strlen(fname);
356 
357 	if (len >= sizeof(utfile))
358 		return 0;
359 
360 	/* must end in x! */
361 	if (fname[len - 1] != 'x')
362 		return 0;
363 
364 	(void)strlcpy(utfile, fname, sizeof(utfile));
365 	endutxent();
366 	return 1;
367 }
368 
369 
370 void
371 getutmp(const struct utmpx *ux, struct utmp *u)
372 {
373 
374 	_DIAGASSERT(ux != NULL);
375 	_DIAGASSERT(u != NULL);
376 
377 	(void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name));
378 	(void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
379 	(void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
380 	u->ut_time = ux->ut_tv.tv_sec;
381 }
382 
383 void
384 getutmpx(const struct utmp *u, struct utmpx *ux)
385 {
386 
387 	_DIAGASSERT(ux != NULL);
388 	_DIAGASSERT(u != NULL);
389 
390 	(void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name));
391 	(void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line));
392 	(void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host));
393 	ux->ut_tv.tv_sec = u->ut_time;
394 	ux->ut_tv.tv_usec = 0;
395 	(void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss));
396 	ux->ut_pid = 0;
397 	ux->ut_type = USER_PROCESS;
398 	ux->ut_session = 0;
399 	ux->ut_exit.e_termination = 0;
400 	ux->ut_exit.e_exit = 0;
401 }
402 
403 int
404 lastlogxname(const char *fname)
405 {
406 	size_t len;
407 
408 	_DIAGASSERT(fname != NULL);
409 
410 	len = strlen(fname);
411 
412 	if (len >= sizeof(llfile))
413 		return 0;
414 
415 	/* must end in x! */
416 	if (fname[len - 1] != 'x')
417 		return 0;
418 
419 	(void)strlcpy(llfile, fname, sizeof(llfile));
420 	return 1;
421 }
422 
423 struct lastlogx *
424 getlastlogx(uid_t uid, struct lastlogx *ll)
425 {
426 
427 	return __getlastlogx13(_PATH_LASTLOGX, uid, ll);
428 }
429 
430 struct lastlogx *
431 __getlastlogx13(const char *fname, uid_t uid, struct lastlogx *ll)
432 {
433 	DBT key, data;
434 	DB *db;
435 
436 	_DIAGASSERT(fname != NULL);
437 	_DIAGASSERT(ll != NULL);
438 
439 	db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
440 
441 	if (db == NULL)
442 		return NULL;
443 
444 	key.data = &uid;
445 	key.size = sizeof(uid);
446 
447 	if ((db->get)(db, &key, &data, 0) != 0)
448 		goto error;
449 
450 	if (data.size != sizeof(*ll)) {
451 		errno = EFTYPE;
452 		goto error;
453 	}
454 
455 	if (ll == NULL)
456 		if ((ll = malloc(sizeof(*ll))) == NULL)
457 			goto done;
458 
459 	(void)memcpy(ll, data.data, sizeof(*ll));
460 	goto done;
461 error:
462 	ll = NULL;
463 done:
464 	(db->close)(db);
465 	return ll;
466 }
467 
468 int
469 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll)
470 {
471 	DBT key, data;
472 	int error = 0;
473 	DB *db;
474 
475 	_DIAGASSERT(fname != NULL);
476 	_DIAGASSERT(ll != NULL);
477 
478 	db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL);
479 
480 	if (db == NULL)
481 		return -1;
482 
483 	key.data = &uid;
484 	key.size = sizeof(uid);
485 	data.data = ll;
486 	data.size = sizeof(*ll);
487 	if ((db->put)(db, &key, &data, 0) != 0)
488 		error = -1;
489 
490 	(db->close)(db);
491 	return error;
492 }
493