1/*	$NetBSD: utmpx.c,v 1.25 2008/04/28 20:22:59 martin 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include <sys/cdefs.h>
32
33#if defined(LIBC_SCCS) && !defined(lint)
34__RCSID("$NetBSD: utmpx.c,v 1.25 2008/04/28 20:22:59 martin Exp $");
35#endif /* LIBC_SCCS and not lint */
36
37#include "namespace.h"
38#include <sys/types.h>
39#include <sys/param.h>
40#include <sys/socket.h>
41#include <sys/stat.h>
42#include <sys/time.h>
43#include <sys/wait.h>
44
45#include <fcntl.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50#ifdef UNIFDEF_LEGACY_UTMP_APIS
51#include <utmp.h>
52#endif /* UNIFDEF_LEGACY_UTMP_APIS */
53#include <utmpx.h>
54#include <utmpx-darwin.h>
55#include <errno.h>
56#include <vis.h>
57#include <notify.h>
58
59static struct _utmpx *__utx__ = NULL;
60
61static void
62__default_utx_init(void)
63{
64	__utx__ = calloc(1, sizeof(struct _utmpx));
65	const char magic[] = __UTX_MAGIC__;
66	memcpy(&__utx__->magic, magic, UTMPX_MAGIC);
67	pthread_mutex_init(&__utx__->utmpx_mutex, NULL);
68	__utx__->utfile = _PATH_UTMPX;
69	__utx__->utfile_system = 1;
70}
71
72struct _utmpx *
73__default_utx(void)
74{
75	static pthread_once_t once = PTHREAD_ONCE_INIT;
76	pthread_once(&once, &__default_utx_init);
77	return __utx__;
78}
79
80static struct utmpx *__getutxid(struct _utmpx *, const struct utmpx *);
81
82__private_extern__ const char _utmpx_vers[] = "utmpx-1.00";
83
84__private_extern__ void
85__setutxent(struct _utmpx *U)
86{
87
88	(void)memset(&U->ut, 0, sizeof(U->ut));
89	if (U->fp == NULL)
90		return;
91#ifdef __LP64__
92	(void)fseeko(U->fp, (off_t)sizeof(struct utmpx32), SEEK_SET);
93#else /* __LP64__ */
94	(void)fseeko(U->fp, (off_t)sizeof(U->ut), SEEK_SET);
95#endif /* __LP64__ */
96}
97
98void
99_setutxent(struct _utmpx *U)
100{
101
102	TEST_UTMPX_T("_setutxent", U);
103	UTMPX_LOCK(U);
104	__setutxent(U);
105	UTMPX_UNLOCK(U);
106}
107
108
109void
110setutxent(void)
111{
112	_setutxent(__default_utx());
113}
114
115
116__private_extern__ void
117__endutxent(struct _utmpx *U)
118{
119	(void)memset(&U->ut, 0, sizeof(U->ut));
120	if (U->fp != NULL) {
121		int saveerrno = errno;
122		(void)fclose(U->fp);
123		errno = saveerrno;
124		U->fp = NULL;
125		U->readonly = 0;
126	}
127}
128
129
130void
131_endutxent(struct _utmpx *U)
132{
133	TEST_UTMPX_T("_endutxent", U);
134	UTMPX_LOCK(U);
135	__endutxent(U);
136	UTMPX_UNLOCK(U);
137}
138
139
140void
141endutxent(void)
142{
143	_endutxent(__default_utx());
144}
145
146
147__private_extern__ struct utmpx *
148__getutxent(struct _utmpx *U)
149{
150	int saveerrno;
151#ifdef __LP64__
152	struct utmpx32 ut32;
153#endif /* __LP64__ */
154
155	if (U->fp == NULL) {
156		struct stat st;
157
158		if ((U->fp = fopen(U->utfile, "r+")) == NULL)
159			if ((U->fp = fopen(U->utfile, "w+")) == NULL) {
160				if ((U->fp = fopen(U->utfile, "r")) == NULL)
161					goto fail;
162				else
163					U->readonly = 1;
164			}
165
166		fcntl(fileno(U->fp), F_SETFD, 1); /* set close-on-exec flag */
167
168		/* get file size in order to check if new file */
169		if (fstat(fileno(U->fp), &st) == -1)
170			goto failclose;
171
172		if (st.st_size == 0) {
173			/* new file, add signature record */
174#ifdef __LP64__
175			(void)memset(&ut32, 0, sizeof(ut32));
176			ut32.ut_type = SIGNATURE;
177			(void)memcpy(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers));
178			if (fwrite(&ut32, sizeof(ut32), 1, U->fp) != 1)
179#else /* __LP64__ */
180			(void)memset(&U->ut, 0, sizeof(U->ut));
181			U->ut.ut_type = SIGNATURE;
182			(void)memcpy(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers));
183			if (fwrite(&U->ut, sizeof(U->ut), 1, U->fp) != 1)
184#endif /* __LP64__ */
185				goto failclose;
186		} else {
187			/* old file, read signature record */
188#ifdef __LP64__
189			if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1)
190#else /* __LP64__ */
191			if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1)
192#endif /* __LP64__ */
193				goto failclose;
194#ifdef __LP64__
195			if (memcmp(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 ||
196			    ut32.ut_type != SIGNATURE)
197#else /* __LP64__ */
198			if (memcmp(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 ||
199			    U->ut.ut_type != SIGNATURE)
200#endif /* __LP64__ */
201			{
202				errno = EINVAL;
203				goto failclose;
204			}
205		}
206	}
207
208#ifdef __LP64__
209	if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1)
210#else /* __LP64__ */
211	if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1)
212#endif /* __LP64__ */
213		goto fail;
214
215#ifdef __LP64__
216	_utmpx32_64(&ut32, &U->ut);
217#endif /* __LP64__ */
218	return &U->ut;
219failclose:
220	saveerrno = errno;
221	(void)fclose(U->fp);
222	errno = saveerrno;
223	U->fp = NULL;
224fail:
225	(void)memset(&U->ut, 0, sizeof(U->ut));
226	return NULL;
227}
228
229
230struct utmpx *
231_getutxent(struct _utmpx *U)
232{
233	struct utmpx *ret;
234
235	TEST_UTMPX_T("_getutxent", U);
236	UTMPX_LOCK(U);
237	ret = __getutxent(U);
238	UTMPX_UNLOCK(U);
239	return ret;
240}
241
242
243struct utmpx *
244getutxent(void)
245{
246	return _getutxent(__default_utx());
247}
248
249
250struct utmpx *
251_getutxid(struct _utmpx *U, const struct utmpx *utx)
252{
253	struct utmpx temp;
254	const struct utmpx *ux;
255	struct utmpx *ret;
256
257	if (utx->ut_type == EMPTY)
258		return NULL;
259
260	TEST_UTMPX_T("_getutxid", U);
261	UTMPX_LOCK(U);
262	/* make a copy as needed, and auto-fill if requested */
263	ux = _utmpx_working_copy(utx, &temp, 1);
264	if (!ux) {
265		UTMPX_UNLOCK(U);
266		return NULL;
267	}
268
269	ret = __getutxid(U, ux);
270	UTMPX_UNLOCK(U);
271	return ret;
272}
273
274
275struct utmpx *
276getutxid(const struct utmpx *utx)
277{
278	return _getutxid(__default_utx(), utx);
279}
280
281
282static struct utmpx *
283__getutxid(struct _utmpx *U, const struct utmpx *utx)
284{
285
286	do {
287		if (U->ut.ut_type == EMPTY)
288			continue;
289		switch (utx->ut_type) {
290		case EMPTY:
291			return NULL;
292		case RUN_LVL:
293		case BOOT_TIME:
294		case OLD_TIME:
295		case NEW_TIME:
296			if (U->ut.ut_type == utx->ut_type)
297				return &U->ut;
298			break;
299		case INIT_PROCESS:
300		case LOGIN_PROCESS:
301		case USER_PROCESS:
302		case DEAD_PROCESS:
303			switch (U->ut.ut_type) {
304			case INIT_PROCESS:
305			case LOGIN_PROCESS:
306			case USER_PROCESS:
307			case DEAD_PROCESS:
308				if (memcmp(U->ut.ut_id, utx->ut_id,
309				    sizeof(U->ut.ut_id)) == 0)
310					return &U->ut;
311				break;
312			default:
313				break;
314			}
315			break;
316		default:
317			return NULL;
318		}
319	} while (__getutxent(U) != NULL);
320	return NULL;
321}
322
323
324static struct utmpx *
325__getutxline(struct _utmpx *U, const struct utmpx *utx)
326{
327	do {
328		switch (U->ut.ut_type) {
329		case EMPTY:
330			break;
331		case LOGIN_PROCESS:
332		case USER_PROCESS:
333			if (strncmp(U->ut.ut_line, utx->ut_line,
334			    sizeof(U->ut.ut_line)) == 0)
335				return &U->ut;
336			break;
337		default:
338			break;
339		}
340	} while (__getutxent(U) != NULL);
341	return NULL;
342}
343
344
345struct utmpx *
346_getutxline(struct _utmpx *U, const struct utmpx *utx)
347{
348	struct utmpx *ret;
349
350	TEST_UTMPX_T("_getutxline", U);
351	UTMPX_LOCK(U);
352	ret = __getutxline(U, utx);
353	UTMPX_UNLOCK(U);
354	return ret;
355}
356
357
358struct utmpx *
359getutxline(const struct utmpx *utx)
360{
361	return _getutxline(__default_utx(), utx);
362}
363
364
365struct utmpx *
366_pututxline(struct _utmpx *U, const struct utmpx *utx)
367{
368	struct utmpx *ux;
369
370	if (utx == NULL) {
371		errno = EINVAL;
372		return NULL;
373	}
374
375	TEST_UTMPX_T("_pututxline", U);
376	UTMPX_LOCK(U);
377	if ((ux = __pututxline(__default_utx(), utx)) != NULL && __default_utx()->utfile_system) {
378		_utmpx_asl(ux);	/* the equivalent of wtmpx and lastlogx */
379#ifdef UTMP_COMPAT
380		_write_utmp_compat(ux);
381#endif /* UTMP_COMPAT */
382	}
383	UTMPX_UNLOCK(U);
384	return ux;
385}
386
387
388struct utmpx *
389pututxline(const struct utmpx *utx)
390{
391	return _pututxline(__default_utx(), utx);
392}
393
394__private_extern__ struct utmpx *
395__pututxline(struct _utmpx *U, const struct utmpx *utx)
396{
397	struct utmpx temp, *u = NULL, *x;
398	const struct utmpx *ux;
399#ifdef __LP64__
400	struct utmpx32 ut32;
401#endif /* __LP64__ */
402	struct flock fl;
403#define gotlock		(fl.l_start >= 0)
404
405	fl.l_start = -1; /* also means we haven't locked */
406	if (U->utfile_system)
407		if ((U->fp != NULL && U->readonly) || (U->fp == NULL && geteuid() != 0)) {
408			errno = EPERM;
409			return NULL;
410		}
411
412	if (U->fp == NULL) {
413		(void)__getutxent(U);
414		if (U->fp == NULL || U->readonly) {
415			errno = EPERM;
416			return NULL;
417		}
418	}
419
420	/* make a copy as needed, and auto-fill if requested */
421	ux = _utmpx_working_copy(utx, &temp, 0);
422	if (!ux)
423		return NULL;
424
425	if ((x = __getutxid(U, ux)) == NULL) {
426		__setutxent(U);
427		if ((x = __getutxid(U, ux)) == NULL) {
428			/*
429			 * utx->ut_type has any original mask bits, while
430			 * ux->ut_type has those mask bits removed.  If we
431			 * are trying to record a dead process, and
432			 * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, then since
433			 * there is no matching entry, we return NULL.
434			 */
435			if (ux->ut_type == DEAD_PROCESS &&
436			    (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK)) {
437				errno = EINVAL;
438				return NULL;
439			}
440			/*
441			 * Replace lockf() with fcntl() and a fixed start
442			 * value.  We should already be at EOF.
443			 */
444			if ((fl.l_start = lseek(fileno(U->fp), 0, SEEK_CUR)) < 0)
445				return NULL;
446			fl.l_len = 0;
447			fl.l_whence = SEEK_SET;
448			fl.l_type = F_WRLCK;
449			if (fcntl(fileno(U->fp), F_SETLKW, &fl) == -1)
450				return NULL;
451			if (fseeko(U->fp, (off_t)0, SEEK_END) == -1)
452				goto fail;
453		}
454	}
455
456	if (!gotlock) {
457		/*
458		 * utx->ut_type has any original mask bits, while
459		 * ux->ut_type has those mask bits removed.  If we
460		 * are trying to record a dead process, if
461		 * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, but the found
462		 * entry is not a (matching) USER_PROCESS, then return NULL.
463		 */
464		if (ux->ut_type == DEAD_PROCESS &&
465		    (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK) &&
466		    x->ut_type != USER_PROCESS) {
467			errno = EINVAL;
468			return NULL;
469		}
470		/* we are not appending */
471#ifdef __LP64__
472		if (fseeko(U->fp, -(off_t)sizeof(ut32), SEEK_CUR) == -1)
473#else /* __LP64__ */
474		if (fseeko(U->fp, -(off_t)sizeof(U->ut), SEEK_CUR) == -1)
475#endif /* __LP64__ */
476			return NULL;
477	}
478
479#ifdef __LP64__
480	_utmpx64_32(ux, &ut32);
481	if (fwrite(&ut32, sizeof (ut32), 1, U->fp) != 1)
482#else /* __LP64__ */
483	if (fwrite(ux, sizeof (*ux), 1, U->fp) != 1)
484#endif /* __LP64__ */
485		goto fail;
486
487	if (fflush(U->fp) == -1)
488		goto fail;
489
490	u = memcpy(&U->ut, ux, sizeof(U->ut));
491	notify_post(UTMPX_CHANGE_NOTIFICATION);
492fail:
493	if (gotlock) {
494		int save = errno;
495		fl.l_type = F_UNLCK;
496		if (fcntl(fileno(U->fp), F_SETLK, &fl) == -1)
497			return NULL;
498		errno = save;
499	}
500	return u;
501}
502
503
504/*
505 * The following are extensions and not part of the X/Open spec.
506 */
507__private_extern__ int
508__utmpxname(struct _utmpx *U, const char *fname)
509{
510	size_t len;
511
512	if (fname == NULL) {
513		if(!U->utfile_system)
514			free(U->utfile);
515		U->utfile = _PATH_UTMPX;
516		U->utfile_system = 1;
517		__endutxent(U);
518		return 1;
519	}
520
521	len = strlen(fname);
522
523	if (len >= MAXPATHLEN)
524		return 0;
525
526	/* must end in x! */
527	if (fname[len - 1] != 'x')
528		return 0;
529
530	if (U->utfile_system)
531		U->utfile = NULL;
532	U->utfile_system = 0;
533	if ((U->utfile = reallocf(U->utfile, len + 1)) == NULL)
534		return 0;
535
536	(void)strcpy(U->utfile, fname);
537	__endutxent(U);
538	return 1;
539}
540
541int
542_utmpxname(struct _utmpx *U, const char *fname)
543{
544	int ret;
545
546	TEST_UTMPX_T("_utmpxname", U);
547	UTMPX_LOCK(U);
548	ret = __utmpxname(U, fname);
549	UTMPX_UNLOCK(U);
550	return ret;
551}
552
553int
554utmpxname(const char *fname)
555{
556	return _utmpxname(__default_utx(), fname);
557}
558
559#ifdef UNIFDEF_LEGACY_UTMP_APIS
560void
561getutmp(const struct utmpx *ux, struct utmp *u)
562{
563
564	bzero(u, sizeof(*u));
565	(void)memcpy(u->ut_name, ux->ut_user, sizeof(u->ut_name));
566	(void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
567	(void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
568	u->ut_time = ux->ut_tv.tv_sec;
569}
570
571void
572getutmpx(const struct utmp *u, struct utmpx *ux)
573{
574
575	bzero(ux, sizeof(*ux));
576	(void)memcpy(ux->ut_user, u->ut_name, sizeof(u->ut_name));
577	ux->ut_user[sizeof(u->ut_name)] = 0;
578	(void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line));
579	ux->ut_line[sizeof(u->ut_line)] = 0;
580	(void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host));
581	ux->ut_host[sizeof(u->ut_host)] = 0;
582	ux->ut_tv.tv_sec = u->ut_time;
583	ux->ut_tv.tv_usec = 0;
584	ux->ut_pid = getpid();
585	ux->ut_type = USER_PROCESS;
586}
587#endif /* UNIFDEF_LEGACY_UTMP_APIS */
588