--- /dev/null
+/* $NetBSD: utmpx.c,v 1.21 2003/09/06 16:42:10 wiz Exp $ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: utmpx.c,v 1.21 2003/09/06 16:42:10 wiz Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include "namespace.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <utmpx.h>
+#include <utmpx-darwin.h>
+#include <errno.h>
+#include <vis.h>
+#include <notify.h>
+
+static FILE *fp;
+static int readonly = 0;
+static struct utmpx ut;
+static char utfile[MAXPATHLEN] = _PATH_UTMPX;
+__private_extern__ int utfile_system = 1; /* are we using _PATH_UTMPX? */
+
+static struct utmpx *_getutxid(const struct utmpx *);
+
+__private_extern__ const char _utmpx_vers[] = "utmpx-1.00";
+
+void
+setutxent()
+{
+
+ (void)memset(&ut, 0, sizeof(ut));
+ if (fp == NULL)
+ return;
+#ifdef __LP64__
+ (void)fseeko(fp, (off_t)sizeof(struct utmpx32), SEEK_SET);
+#else /* __LP64__ */
+ (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET);
+#endif /* __LP64__ */
+}
+
+
+void
+endutxent()
+{
+
+ (void)memset(&ut, 0, sizeof(ut));
+ if (fp != NULL) {
+ (void)fclose(fp);
+ fp = NULL;
+ readonly = 0;
+ }
+}
+
+
+struct utmpx *
+getutxent()
+{
+#ifdef __LP64__
+ struct utmpx32 ut32;
+#endif /* __LP64__ */
+
+ if (fp == NULL) {
+ struct stat st;
+
+ if ((fp = fopen(utfile, "r+")) == NULL)
+ if ((fp = fopen(utfile, "w+")) == NULL) {
+ if ((fp = fopen(utfile, "r")) == NULL)
+ goto fail;
+ else
+ readonly = 1;
+ }
+
+
+ /* get file size in order to check if new file */
+ if (fstat(fileno(fp), &st) == -1)
+ goto failclose;
+
+ if (st.st_size == 0) {
+ /* new file, add signature record */
+#ifdef __LP64__
+ (void)memset(&ut32, 0, sizeof(ut32));
+ ut32.ut_type = SIGNATURE;
+ (void)memcpy(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers));
+ if (fwrite(&ut32, sizeof(ut32), 1, fp) != 1)
+#else /* __LP64__ */
+ (void)memset(&ut, 0, sizeof(ut));
+ ut.ut_type = SIGNATURE;
+ (void)memcpy(ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers));
+ if (fwrite(&ut, sizeof(ut), 1, fp) != 1)
+#endif /* __LP64__ */
+ goto failclose;
+ } else {
+ /* old file, read signature record */
+#ifdef __LP64__
+ if (fread(&ut32, sizeof(ut32), 1, fp) != 1)
+#else /* __LP64__ */
+ if (fread(&ut, sizeof(ut), 1, fp) != 1)
+#endif /* __LP64__ */
+ goto failclose;
+#ifdef __LP64__
+ if (memcmp(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 ||
+ ut32.ut_type != SIGNATURE)
+#else /* __LP64__ */
+ if (memcmp(ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 ||
+ ut.ut_type != SIGNATURE)
+#endif /* __LP64__ */
+ goto failclose;
+ }
+ }
+
+#ifdef __LP64__
+ if (fread(&ut32, sizeof(ut32), 1, fp) != 1)
+#else /* __LP64__ */
+ if (fread(&ut, sizeof(ut), 1, fp) != 1)
+#endif /* __LP64__ */
+ goto fail;
+
+#ifdef __LP64__
+ _utmpx32_64(&ut32, &ut);
+#endif /* __LP64__ */
+ return &ut;
+failclose:
+ (void)fclose(fp);
+ fp = NULL;
+fail:
+ (void)memset(&ut, 0, sizeof(ut));
+ return NULL;
+}
+
+struct utmpx *
+getutxid(const struct utmpx *utx)
+{
+ struct utmpx temp;
+ const struct utmpx *ux;
+
+ _DIAGASSERT(utx != NULL);
+
+ if (utx->ut_type == EMPTY)
+ return NULL;
+
+ /* make a copy as needed, and auto-fill if requested */
+ ux = _utmpx_working_copy(utx, &temp, 1);
+ if (!ux)
+ return NULL;
+
+ return _getutxid(ux);
+}
+
+
+static struct utmpx *
+_getutxid(const struct utmpx *utx)
+{
+
+ do {
+ if (ut.ut_type == EMPTY)
+ continue;
+ switch (utx->ut_type) {
+ case EMPTY:
+ return NULL;
+ case RUN_LVL:
+ case BOOT_TIME:
+ case OLD_TIME:
+ case NEW_TIME:
+ if (ut.ut_type == utx->ut_type)
+ return &ut;
+ break;
+ case INIT_PROCESS:
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ case DEAD_PROCESS:
+ switch (ut.ut_type) {
+ case INIT_PROCESS:
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ case DEAD_PROCESS:
+ if (memcmp(ut.ut_id, utx->ut_id,
+ sizeof(ut.ut_id)) == 0)
+ return &ut;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ } while (getutxent() != NULL);
+ return NULL;
+}
+
+
+struct utmpx *
+getutxline(const struct utmpx *utx)
+{
+
+ _DIAGASSERT(utx != NULL);
+
+ do {
+ switch (ut.ut_type) {
+ case EMPTY:
+ break;
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ if (strncmp(ut.ut_line, utx->ut_line,
+ sizeof(ut.ut_line)) == 0)
+ return &ut;
+ break;
+ default:
+ break;
+ }
+ } while (getutxent() != NULL);
+ return NULL;
+}
+
+
+struct utmpx *
+pututxline(const struct utmpx *utx)
+{
+ struct utmpx *ux;
+
+ _DIAGASSERT(utx != NULL);
+
+ if (utx == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((ux = _pututxline(utx)) != NULL && utfile_system) {
+ _utmpx_asl(ux); /* the equivalent of wtmpx and lastlogx */
+#ifdef UTMP_COMPAT
+ _write_utmp_compat(ux);
+#endif /* UTMP_COMPAT */
+ }
+ return ux;
+}
+
+__private_extern__ struct utmpx *
+_pututxline(const struct utmpx *utx)
+{
+ struct utmpx temp, *u = NULL, *x;
+ const struct utmpx *ux;
+#ifdef __LP64__
+ struct utmpx32 ut32;
+#endif /* __LP64__ */
+ int gotlock = 0;
+
+ if (utfile_system)
+ if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ if (fp == NULL) {
+ (void)getutxent();
+ if (fp == NULL || readonly) {
+ errno = EPERM;
+ return NULL;
+ }
+ }
+
+ /* make a copy as needed, and auto-fill if requested */
+ ux = _utmpx_working_copy(utx, &temp, 0);
+ if (!ux)
+ return NULL;
+
+ if ((x = _getutxid(ux)) == NULL) {
+ setutxent();
+ if ((x = _getutxid(ux)) == NULL) {
+ /*
+ * utx->ut_type has any original mask bits, while
+ * ux->ut_type has those mask bits removed. If we
+ * are trying to record a dead process, and
+ * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, then since
+ * there is no matching entry, we return NULL.
+ */
+ if (ux->ut_type == DEAD_PROCESS &&
+ (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1)
+ return NULL;
+ gotlock++;
+ if (fseeko(fp, (off_t)0, SEEK_END) == -1)
+ goto fail;
+ }
+ }
+
+ if (!gotlock) {
+ /*
+ * utx->ut_type has any original mask bits, while
+ * ux->ut_type has those mask bits removed. If we
+ * are trying to record a dead process, if
+ * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, but the found
+ * entry is not a (matching) USER_PROCESS, then return NULL.
+ */
+ if (ux->ut_type == DEAD_PROCESS &&
+ (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK) &&
+ x->ut_type != USER_PROCESS) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* we are not appending */
+#ifdef __LP64__
+ if (fseeko(fp, -(off_t)sizeof(ut32), SEEK_CUR) == -1)
+#else /* __LP64__ */
+ if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1)
+#endif /* __LP64__ */
+ return NULL;
+ }
+
+#ifdef __LP64__
+ _utmpx64_32(ux, &ut32);
+ if (fwrite(&ut32, sizeof (ut32), 1, fp) != 1)
+#else /* __LP64__ */
+ if (fwrite(ux, sizeof (*ux), 1, fp) != 1)
+#endif /* __LP64__ */
+ goto fail;
+
+ if (fflush(fp) == -1)
+ goto fail;
+
+ u = memcpy(&ut, ux, sizeof(ut));
+ notify_post(UTMPX_CHANGE_NOTIFICATION);
+fail:
+ if (gotlock) {
+ int save = errno;
+ if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1)
+ return NULL;
+ errno = save;
+ }
+ return u;
+}
+
+
+/*
+ * The following are extensions and not part of the X/Open spec.
+ */
+int
+utmpxname(const char *fname)
+{
+ size_t len;
+
+ if (fname == NULL) {
+ strcpy(utfile, _PATH_UTMPX);
+ utfile_system = 1;
+ endutxent();
+ return 1;
+ }
+
+ len = strlen(fname);
+
+ if (len >= sizeof(utfile))
+ return 0;
+
+ /* must end in x! */
+ if (fname[len - 1] != 'x')
+ return 0;
+
+ (void)strlcpy(utfile, fname, sizeof(utfile));
+ endutxent();
+ utfile_system = 0;
+ return 1;
+}
+
+
+void
+getutmp(const struct utmpx *ux, struct utmp *u)
+{
+
+ bzero(u, sizeof(*u));
+ (void)memcpy(u->ut_name, ux->ut_user, sizeof(u->ut_name));
+ (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
+ (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
+ u->ut_time = ux->ut_tv.tv_sec;
+}
+
+void
+getutmpx(const struct utmp *u, struct utmpx *ux)
+{
+
+ bzero(ux, sizeof(*ux));
+ (void)memcpy(ux->ut_user, u->ut_name, sizeof(u->ut_name));
+ ux->ut_user[sizeof(u->ut_name)] = 0;
+ (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line));
+ ux->ut_line[sizeof(u->ut_line)] = 0;
+ (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host));
+ ux->ut_host[sizeof(u->ut_host)] = 0;
+ ux->ut_tv.tv_sec = u->ut_time;
+ ux->ut_tv.tv_usec = 0;
+ ux->ut_pid = getpid();
+ ux->ut_type = USER_PROCESS;
+}