Ed's short guide on utmp(x)

Some time ago I spent some time working with utmp(x) and I noticed many applications/programmers make wrong assumptions about what it is, how it works and how to write applications that use it. This is why I decided to write an article about it, which is hopefully of any use to people who want to use this interface.

First of all, a small introduction. My name is Ed Schouten and I am a developer at The FreeBSD Project. I am a user since 2005 and a developer since May 2008. In the last couple of years I've been working on FreeBSD's TTY layer, the terminal emulator for the console, our utmpx implementation and many other things.

What is utmp(x)?

Many operating systems provide facilities to somehow log user login sessions. In many cases there are three types of queries which are of interest:

In order to answer all these queries, most UNIX-like operating systems store login records in three databases, namely:

The utmp(x) and wtmp(x) hold various types of records, namely user logins, user logouts, but also changes to the system clock and system reboots. These events need to be stored as well, since they must be taken into account when calculating the length of a user login session.

Some history

In order to understand why things work the way they do, it's good to explain some historical information about utmp and utmpx.

In the beginning...

In a very ancient history many UNIX-like operating systems had a header file called <utmp.h>. This header file basically had the following contents. The actual structure differs between implementations.

struct utmp {
	char	ut_line[];
	char	ut_name[];
	char	ut_host[];
	time_t	ut_time;
};

This structure defines the layout of a single entry in the utmp and wtmp databases, which was often stored at /etc/utmp or /var/run/utmp and /var/log/wtmp.

Applications that wanted to update records in the utmp database had to know at which location in the file they had to write the new record. This offset was often based on the name of the TTY. The ttyslot() function was often provided to obtain this offset. It typically returned the line number at which the TTY was listed in /etc/ttys.

Later many operating systems also provided a structure in the same header file which looked as follows:

struct lastlog {
	char	ll_line[];
	char	ll_host[];
	time_t	ll_time;
};

This structure was used by the lastlog database. The lastlog database was often indexed by user ID, which means there is no need to store the username as well. It can be derived from the offset at which the entry is stored.

System V: utmpx

The System V developers implemented a replacement for utmp and wtmp called utmpx and wtmpx. utmpx users a different header file called <utmpx.h>. This header file basically declares the following things:

struct utmpx {
	char		ut_user[];
	char		ut_id[];
	char		ut_line[];
	pid_t		ut_pid;
	short		ut_type;
	struct timeval	ut_tv;
};

#define EMPTY		0x...
#define BOOT_TIME	0x...
#define OLD_TIME	0x...
#define NEW_TIME	0x...
#define USER_PROCESS	0x...
#define INIT_PROCESS	0x...
#define LOGIN_PROCESS	0x...
#define DEAD_PROCESS	0x...

void	endutxent(void);
struct utmpx *getutxent(void);
struct utmpx *getutxid(const struct utmpx *);
struct utmpx *getutxline(const struct utmpx *);
struct utmpx *pututxline(const struct utmpx *);
void	setutxent(void);

This interface has many advantages over the old utmp interface, namely:

So what happened to wtmpx? Well, the implementation often offered a function like updwtmpx() which could be used to append records to the wtmpx log file.

Standardization

Finally the utmpx interface got standardized, which means it is now part of POSIX. POSIX only standardizes the previously given structure and utility functions, which means there is no standardized way to update the wtmpx and lastlogx database, when provided.

Operating system support

Right now there are lots of UNIX-like operating systems that support utmp and utmpx. Below is a list of which operating system implements what.

How to write portable code

As a FreeBSD developer, I have to say utmpx support on FreeBSD was long overdue. FreeBSD 9.0 was released in January 2012, which means we'll be stuck with non-utmpx for quite some time to come.

In my opinion application maintainers should just do the following:

Finally I want to say, when in doubt about what is the best thing to do, be sure to ask the respective operating system maintainers (which includes me). Good luck!

Last modified: Sun Jan 8 23:38:11 2012 +0100