Subject: sample utmpx implementation
To: None <tech-userlevel@netbsd.org>
From: Christos Zoulas <christos@zoulas.com>
List: tech-userlevel
Date: 11/01/2001 14:06:01
Hello,

This code is just WIP of a utmpx implementation... It is posted just for
feedback; I am not planning to commit anything until it is done. This
code has not been tested and I don't know if it works.

- The utmpx file does not have a version 
      I am thinking of implementing utmpx file versioning by using the
      first utmpx entry in the file: Define a new ut_type `VERSION' and
      put the version number and magic in the ut_name field.

- The lastlog stuff is missing
      I am thinking of implementing lastlog using db, so that it does not
      become huge like it is now.

- utmp_update is supposed to be a program that is setuid or setgid to
  something that can write the utmpx file. This is similar I believe
  to what some svr4 systems do.

- I have also started working on a utmp <-> utmpx device driver, but
  I am not sure if it is worth the trouble.

- I also don't know what to do about utmpd. pututent() is supposed to
  register utmpx entries with utmpd, and utmpd is supposed to detect
  that processes have died and change the ut_type field appropriately.
  Is that something we want?

christos

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	utmpx.h
#	utmpx.c
#	utmp_update.c
#
echo x - utmpx.h
sed 's/^X//' >utmpx.h << 'END-of-utmpx.h'
X/*	$NetBSD$	 */
X
X/*-
X * Copyright (c) 2001 The NetBSD Foundation, Inc.
X * All rights reserved.
X *
X * This code is derived from software contributed to The NetBSD Foundation
X * by Christos Zoulas.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *        This product includes software developed by the NetBSD
X *        Foundation, Inc. and its contributors.
X * 4. Neither the name of The NetBSD Foundation nor the names of its
X *    contributors may be used to endorse or promote products derived
X *    from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
X * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
X * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
X * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
X * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
X * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
X * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
X * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X#ifndef	_UTMPX_H_
X#define	_UTMPX_H_
X
X#define	_PATH_UTMPX		"/var/run/utmpx"
X#define	_PATH_WTMPX		"/var/log/wtmpx"
X#define	_PATH_LASTLOG		"/var/log/lastlogx"
X#define	_PATH_UTMP_UPDATE	"/usr/libexec/utmp_update"
X
X#define _UTX_NAMESIZE	32
X#define _UTX_LINESIZE	32
X#define	_UTX_IDSIZE	4
X#define _UTX_HOSTSIZE	256
X
X#ifndef _XOPEN_SOURCE
X#define UTX_NAMESIZE	_UTX_NAMESIZE
X#define UTX_LINESIZE	_UTX_LINESIZE
X#define	UTX_IDSIZE	_UTX_IDSIZE
X#define UTX_HOSTSIZE	_UTX_HOSTSIZE
X#endif
X
X#define EMPTY		0
X#define RUN_LVL		1
X#define BOOT_TIME	2
X#define OLD_TIME	3
X#define NEW_TIME	4
X#define INIT_PROCESS	5
X#define LOGIN_PROCESS	6
X#define USER_PROCESS	7
X#define DEAD_PROCESS	8
X
X#ifndef _XOPEN_SOURCE
X#define ACCOUNTING	9
X#endif
X
X/*
X * The following structure describes the fields of the utmpx entries
X * stored in _PATH_UTMPX or _PATH_WTMPX. This is not the format the
X * entries are stored in the files, and application should only access
X * entries using routines described in getutxent(3).
X */
Xstruct utmpx {
X	char ut_name[_UTX_NAMESIZE];	/* login name */
X	char ut_id[_UTX_IDSIZE];	/* inittab id */
X	char ut_line[_UTX_LINESIZE];	/* tty name */
X	char ut_host[_UTX_HOSTSIZE];	/* host name */
X	uint16_t ut_session;		/* session id used for windowing */
X	uint16_t ut_type;		/* type of this entry */
X	pid_t ut_pid;			/* process id creating the entry */
X	struct {
X		uint16_t e_termination;	/* process termination signal */
X		uint16_t e_exit;	/* process exit status */
X	} ut_exit;
X	struct sockaddr_storage ut_ss;	/* address where entry was made from */
X	struct timespec ut_time;	/* time entry was created */
X	uint32_t ut_pad[10];		/* reserved for future use */
X};
X
X#ifndef _XOPEN_SOURCE
Xstruct lastlogx {
X	struct timespec ll_time;	/* time entry was created */
X	char ll_line[_UTX_LINESIZE];	/* tty name */
X	char ll_host[_UTX_HOSTSIZE];	/* host name */
X	struct sockaddr_storage ll_ss;	/* address where entry was made from */
X};
X#endif	/* !_XOPEN_SOURCE */
X
X__BEGIN_DECLS
X
Xvoid setutxent(void);
Xvoid endutxent(void);
Xstruct utmpx *getutxent(void);
Xstruct utmpx *getutxid(const struct utmpx *);
Xstruct utmpx *getutxline(const struct utmpx *);
Xstruct utmpx *pututxline(const struct utmpx *);
X
X#ifndef _XOPEN_SOURCE
X
Xint lastlogxname(const char *);
Xstruct lastlogx *getlastlogxuid(uid_t);
Xvoid updlastlogx(const char *, struct lastlogx *);
X
Xint utmpxname(const char *);
X
X#endif /* !_XOPEN_SOURCE */
X
X__END_DECLS
X
X#endif /* !_UTMPX_H_ */
END-of-utmpx.h
echo x - utmpx.c
sed 's/^X//' >utmpx.c << 'END-of-utmpx.c'
X/*	$NetBSD$	 */
X
X/*-
X * Copyright (c) 2001 The NetBSD Foundation, Inc.
X * All rights reserved.
X *
X * This code is derived from software contributed to The NetBSD Foundation
X * by Christos Zoulas.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *        This product includes software developed by the NetBSD
X *        Foundation, Inc. and its contributors.
X * 4. Neither the name of The NetBSD Foundation nor the names of its
X *    contributors may be used to endorse or promote products derived
X *    from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
X * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
X * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
X * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
X * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
X * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
X * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
X * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X#include <sys/cdefs.h>
X
X#if defined(LIBC_SCCS) && !defined(lint)
X__RCSID("$NetBSD$");
X#endif /* LIBC_SCCS and not lint */
X
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/wait.h>
X#include <sys/socket.h>
X#include <sys/time.h>
X
X#include <stdio.h>
X#include <string.h>
X#include <vis.h>
X#include <utmpx.h>
X#include <unistd.h>
X
Xstatic FILE *fp;
Xstatic struct utmpx ut;
Xstatic char utfile[MAXPATHLEN] = _PATH_UTMPX;
X
Xstatic struct utmpx *utmp_update(const struct utmpx *);
X
Xvoid
Xsetutxent()
X{
X	(void)memset(&ut, 0, sizeof(ut));
X	if (fp == NULL)
X		return;
X	(void)fseeko(fp, (off_t)0, SEEK_SET);
X}
X
X
Xvoid
Xendutxent()
X{
X	(void)memset(&ut, 0, sizeof(ut));
X	if (fp != NULL)
X		(void)fclose(fp);
X}
X
X
Xstruct utmpx *
Xgetutxent()
X{
X	if (fp == NULL) {
X		if ((fp = fopen(utfile, "r+")) == NULL)
X			if ((fp = fopen(utfile, "r")) == NULL)
X				goto fail;
X	}
X
X	if (fread(&ut, sizeof(ut), 1, fp) != sizeof(ut))
X		goto fail;
X
X	return &ut;
Xfail:
X	(void)memset(&ut, 0, sizeof(ut));
X	return NULL;
X}
X
X
Xstruct utmpx *
Xgetutxid(const struct utmpx *utx)
X{
X	if (utx->ut_type == EMPTY)
X		return NULL;
X
X	do {
X		if (ut.ut_type == EMPTY)
X			continue;
X		switch (utx->ut_type) {
X		case EMPTY:
X			return NULL;
X		case RUN_LVL:
X		case BOOT_TIME:
X		case OLD_TIME:
X		case NEW_TIME:
X			if (ut.ut_type == utx->ut_type)
X				return &ut;
X			break;
X		case INIT_PROCESS:
X		case LOGIN_PROCESS:
X		case USER_PROCESS:
X		case DEAD_PROCESS:
X			switch (ut.ut_type) {
X			case INIT_PROCESS:
X			case LOGIN_PROCESS:
X			case USER_PROCESS:
X			case DEAD_PROCESS:
X				if (memcmp(ut.ut_id, utx->ut_id,
X				    sizeof(ut.ut_id)) == 0)
X					return &ut;
X				break;
X			default:
X				break;
X			}
X			break;
X		default:
X			return NULL;
X		}
X	}
X	while (getutxent() != NULL);
X	return NULL;
X}
X
X
Xstruct utmpx *
Xgetutxline(const struct utmpx *utx)
X{
X	do {
X		switch (ut.ut_type) {
X		case EMPTY:
X			break;
X		case LOGIN_PROCESS:
X		case USER_PROCESS:
X			if (strncmp(ut.ut_line, utx->ut_line,
X			    sizeof(ut.ut_line)) == 0)
X				return &ut;
X			break;
X		default:
X			break;
X		}
X	}
X	while (getutxent() != NULL);
X	return NULL;
X}
X
X
Xstruct utmpx *
Xpututxline(const struct utmpx *utx)
X{
X	struct utmpx temp, *u = NULL;
X	int gotlock = 0;
X
X	if (strcmp(_PATH_UTMPX, utfile) == 0 && geteuid() != 0)
X		return utmp_update(utx);
X
X	if (utx == NULL)
X		return NULL;
X
X	(void)memcpy(&temp, utx, sizeof(temp));
X
X	if (fp == NULL) {
X		(void)getutxent();
X		if (fp == NULL)
X			return NULL;
X	}
X
X	if (getutxid(&temp) == NULL) {
X		setutxent();
X		if (getutxid(&temp) == NULL) {
X			if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1)
X				return NULL;
X			gotlock++;
X			if (fseeko(fp, (off_t)0, SEEK_END) == -1)
X				goto fail;
X		}
X	}
X
X	if (!gotlock) {
X		/* we are not appending */
X		if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1)
X			return NULL;
X	}
X
X	if (fwrite(&temp, sizeof (temp), 1, fp) != 1)
X		goto fail;
X
X	if (fflush(fp) == -1)
X		goto fail;
X
X	u = memcpy(&ut, &temp, sizeof(ut));
Xfail:
X	if (gotlock) {
X		if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1)
X			return NULL;
X	}
X	return u;
X}
X
X
Xstatic struct utmpx *
Xutmp_update(const struct utmpx *utx)
X{
X	char buf[sizeof(*utx) * 4 + 1];
X	pid_t pid;
X	int status;
X
X	(void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx),
X	    VIS_WHITE);
X	switch (pid = fork()) {
X	case 0:
X		(void)execl(_PATH_UTMP_UPDATE,
X		    strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf);
X		exit(1);
X		/*NOTREACHED*/
X	case -1:
X		return NULL;
X	default:
X		if (waitpid(pid, &status, 0) == -1)
X			return NULL;
X		if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
X			return memcpy(&ut, utx, sizeof(ut));
X		return NULL;
X	}
X
X}
X
X
X/*
X * The following are extensions and not part of the X/Open spec
X */
Xint
Xutmpxname(const char *fname)
X{
X	size_t len = strlen(fname);
X
X	if (len >= sizeof(utfile))
X		return 0;
X
X	/* must end in x! */
X	if (fname[len - 1] != 'x')
X		return 0;
X
X	(void)strcpy(utfile, fname);
X	endutxent();
X	return 1;
X}
END-of-utmpx.c
echo x - utmp_update.c
sed 's/^X//' >utmp_update.c << 'END-of-utmp_update.c'
X/*	$NetBSD$	 */
X
X/*-
X * Copyright (c) 2001 The NetBSD Foundation, Inc.
X * All rights reserved.
X *
X * This code is derived from software contributed to The NetBSD Foundation
X * by Christos Zoulas.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *        This product includes software developed by the NetBSD
X *        Foundation, Inc. and its contributors.
X * 4. Neither the name of The NetBSD Foundation nor the names of its
X *    contributors may be used to endorse or promote products derived
X *    from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
X * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
X * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
X * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
X * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
X * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
X * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
X * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
X * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X#include <sys/stat.h>
X
X#include <stdio.h>
X#include <vis.h>
X#include <err.h>
X#include <pwd.h>
X#include <utmpx.h>
X#include <stdlib.h>
X
Xint main(int, char *[]);
X
Xint
Xmain(int argc, char *argv[])
X{
X	struct utmpx *utx;
X	size_t len;
X	struct passwd *pwd;
X	struct stat st;
X
X	if (argc != 1) {
X		(void)fprintf(stderr, "Usage: %s <vis-utmpx-entry>\n",
X			getprogname());
X		exit(1);
X	}
X
X	len = strlen(argv[1]);
X
X	if (len > sizeof(*utx) * 4 + 1 || len < sizeof(*utx))
X		errx(1, "Bad argument");
X
X	if ((utx = malloc(len)) == NULL)
X		err(1, "");
X
X	if (strunvis((char *)utx, argv[1]) != sizeof(*utx))
X		errx(1, "Decoding error");
X
X	switch (utx->ut_type) {
X	case USER_PROCESS:
X	case DEAD_PROCESS:
X		break;
X	default:
X		errx(1, "Invalid utmpx type %d\n", (int)utx->ut_type);
X	}
X
X	if ((pwd = getpwuid(getuid())) == NULL)
X		errx(1, "User %lu does not exist in password database",
X		     (unsigned long)getuid());
X
X	if (strcmp(pwd->pw_name, utx->ut_name) != 0)
X		errx(1, "Current user `%s' does not match `%s' in utmpx entry",
X		     pwd->pw_name, utx->ut_name);
X
X	if (stat(utx->ut_line, &st) == -1)
X		err(1, "Cannot stat `%s'", utx->ut_line);
X
X	if (!S_ISCHR(st.st_mode))
X		errx(1, "%s: Not a character device", utx->ut_line);
X
X	/*
X	 * to check if the tty is writable here is problematic. First we
X	 * don't even know if it is a tty, secondly we are setuid so it
X	 * is not trivial to use access
X	 */
X
X	pututxline(utx);
X
X	return 0;
X}
END-of-utmp_update.c
exit