Implement <utmpx.h>.

The utmpx interface is the standardized interface of the user accounting
database. The standard only defines a subset of the functions that were
present in System V-like systems.

I'd like to highlight some of the traits my implementation has:

- The standard allows the on-disk format to be different than the
  in-memory representation (struct utmpx). Most operating systems don't
  do this, but we do. This allows us to keep our ABI more stable, while
  giving us the opportunity to modify the on-disk format. It also allows
  us to use a common file format across different architectures (i.e.
  byte ordering).

- Our implementation of pututxline() also updates wtmp and lastlog (now
  called utx.log and utx.lastlogin). This means the databases are more
  likely to be in sync.

- Care must be taken that our implementation discard any fields that are
  not applicable. For example, our DEAD_PROCESS records do not hold a
  TTY name. Just a time stamp, a record identifier and a process
  identifier. It also guarantees that strings (ut_host, ut_line and
  ut_user) are null terminated. ut_id is obviously not null terminated,
  because it's not a string.

- The API and its behaviour should be conformant to POSIX, but there may
  be things that slightly deviate from the standard. This implementation
  uses separate file descriptors when writing to the log files. It also
  doesn't use getutxid() to search for a field to overwrite. It uses an
  allocation strategy similar to getutxid(), but prevents DEAD_PROCESS
  records from accumulating.

Make sure libulog doesn't overwrite the manpages shipped with our C
library. Also keep the symbol list in Symbol.map sorted.

I'll bump __FreeBSD_version later this evening. I first want to convert
everything to <utmpx.h> and get rid of <utmp.h>.
This commit is contained in:
Ed Schouten 2010-01-13 17:29:55 +00:00
parent 5364a38dba
commit a627ac61ab
10 changed files with 1280 additions and 15 deletions

View File

@ -23,8 +23,8 @@ INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h _ctype.h ctype.h \
stdbool.h stddef.h stdio.h stdlib.h string.h stringlist.h \
strings.h sysexits.h tar.h termios.h tgmath.h \
time.h timeconv.h timers.h ttyent.h \
ulimit.h unistd.h utime.h utmp.h uuid.h varargs.h vis.h wchar.h \
wctype.h wordexp.h
ulimit.h unistd.h utime.h utmp.h utmpx.h uuid.h varargs.h vis.h \
wchar.h wctype.h wordexp.h
MHDRS= float.h floatingpoint.h stdarg.h

88
include/utmpx.h Normal file
View File

@ -0,0 +1,88 @@
/*-
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _UTMPX_H_
#define _UTMPX_H_
#include <sys/cdefs.h>
#include <sys/_timeval.h>
#include <sys/_types.h>
#ifndef _PID_T_DECLARED
typedef __pid_t pid_t;
#define _PID_T_DECLARED
#endif
struct utmpx {
short ut_type; /* Type of entry. */
struct timeval ut_tv; /* Time entry was made. */
char ut_id[8]; /* Record identifier. */
pid_t ut_pid; /* Process ID. */
char ut_user[32]; /* User login name. */
char ut_line[16]; /* Device name. */
#if __BSD_VISIBLE
char ut_host[128]; /* Remote hostname. */
#else
char __ut_host[128];
#endif
char __ut_spare[64];
};
#define EMPTY 0 /* No valid user accounting information. */
#define BOOT_TIME 1 /* Time of system boot. */
#define OLD_TIME 2 /* Time when system clock changed. */
#define NEW_TIME 3 /* Time after system clock changed. */
#define USER_PROCESS 4 /* A process. */
#define INIT_PROCESS 5 /* A process spawned by the init process. */
#define LOGIN_PROCESS 6 /* The session leader of a logged-in user. */
#define DEAD_PROCESS 7 /* A session leader who has exited. */
#if __BSD_VISIBLE
#define SHUTDOWN_TIME 8 /* Time of system shutdown. */
#endif
#if __BSD_VISIBLE
#define UTXDB_ACTIVE 0 /* Active login sessions. */
#define UTXDB_LASTLOGIN 1 /* Last login sessions. */
#define UTXDB_LOG 2 /* Log indexed by time. */
#endif
__BEGIN_DECLS
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);
#if __BSD_VISIBLE
struct utmpx *getutxuser(const char *);
int setutxdb(int, const char *);
#endif
__END_DECLS
#endif /* !_UTMPX_H_ */

View File

@ -18,12 +18,12 @@ SRCS+= __getosreldate.c __xuname.c \
gethostname.c getloadavg.c getlogin.c getmntinfo.c getnetgrent.c \
getosreldate.c getpagesize.c getpagesizes.c \
getpeereid.c getprogname.c getpwent.c getttyent.c \
getusershell.c getvfsbyname.c glob.c \
getusershell.c getutxent.c getvfsbyname.c glob.c \
initgroups.c isatty.c isinf.c isnan.c jrand48.c lcong48.c \
lockf.c lrand48.c mrand48.c nftw.c nice.c \
nlist.c nrand48.c opendir.c \
pause.c pmadvise.c popen.c posix_spawn.c \
psignal.c pw_scan.c pwcache.c \
psignal.c pututxline.c pw_scan.c pwcache.c \
raise.c readdir.c readpassphrase.c rewinddir.c \
scandir.c seed48.c seekdir.c sem.c sem_new.c semctl.c \
setdomainname.c sethostname.c setjmperr.c setmode.c \
@ -32,7 +32,7 @@ SRCS+= __getosreldate.c __xuname.c \
sysconf.c sysctl.c sysctlbyname.c sysctlnametomib.c \
syslog.c telldir.c termios.c time.c times.c timezone.c tls.c \
ttyname.c ttyslot.c ualarm.c ulimit.c uname.c unvis.c \
usleep.c utime.c valloc.c vis.c wait.c wait3.c waitpid.c \
usleep.c utime.c utxdb.c valloc.c vis.c wait.c wait3.c waitpid.c \
wordexp.c
SYM_MAPS+=${.CURDIR}/gen/Symbol.map
@ -54,7 +54,7 @@ MAN+= alarm.3 arc4random.3 \
getgrent.3 getgrouplist.3 gethostname.3 getloadavg.3 \
getmntinfo.3 getnetgrent.3 getosreldate.3 getpagesize.3 \
getpagesizes.3 getpass.3 getpeereid.3 getprogname.3 getpwent.3 \
getttyent.3 getusershell.3 getvfsbyname.3 \
getttyent.3 getusershell.3 getutxent.3 getvfsbyname.3 \
glob.3 initgroups.3 isgreater.3 ldexp.3 lockf.3 makecontext.3 \
modf.3 \
nice.3 nlist.3 pause.3 popen.3 \
@ -126,6 +126,10 @@ MLINKS+=getttyent.3 endttyent.3 getttyent.3 getttynam.3 \
getttyent.3 isdialuptty.3 getttyent.3 isnettty.3 \
getttyent.3 setttyent.3
MLINKS+=getusershell.3 endusershell.3 getusershell.3 setusershell.3
MLINKS+=getutxent.3 endutxent.3 getutxent.3 getutxid.3 \
getutxent.3 getutxline.3 getutxent.3 getutxuser.3 \
getutxent.3 pututxline.3 getutxent.3 setutxdb.3 \
getutxent.3 setutxent.3
MLINKS+=glob.3 globfree.3
MLINKS+=isgreater.3 isgreaterequal.3 isgreater.3 isless.3 \
isgreater.3 islessequal.3 isgreater.3 islessgreater.3 \

View File

@ -359,17 +359,25 @@ FBSD_1.1 {
FBSD_1.2 {
basename_r;
endutxent;
getpagesizes;
getutxent;
getutxid;
getutxline;
getutxuser;
pututxline;
sem_close;
sem_destroy;
sem_getvalue;
sem_init;
sem_open;
sem_post;
sem_timedwait;
sem_trywait;
sem_post;
sem_wait;
sem_unlink;
sem_wait;
setutxdb;
setutxent;
};
FBSDprivate_1.0 {

440
lib/libc/gen/getutxent.3 Normal file
View File

@ -0,0 +1,440 @@
.\" Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
.\" All rights reserved.
.\"
.\" 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.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
.\"
.\" $FreeBSD$
.\"
.Dd January 8, 2010
.Os
.Dt GETUTXENT 3
.Sh NAME
.Nm endutxent ,
.Nm getutxent ,
.Nm getutxid ,
.Nm getutxline ,
.Nm getutxuser ,
.Nm pututxline ,
.Nm setutxdb ,
.Nm setutxent
.Nd user accounting database functions
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In utmpx.h
.Ft void
.Fn endutxent "void"
.Ft struct utmpx *
.Fn getutxent "void"
.Ft struct utmpx *
.Fn getutxid "const struct utmpx *id"
.Ft struct utmpx *
.Fn getutxline "const struct utmpx *line"
.Ft struct utmpx *
.Fn getutxuser "const char *user"
.Ft struct utmpx *
.Fn pututxline "const struct utmpx *utmpx"
.Ft int
.Fn setutxdb "int type" "const char *file"
.Ft void
.Fn setutxent "void"
.Sh DESCRIPTION
These functions operate on the user accounting database which stores
records of various system activities, such as user login and logouts,
but also system startups and shutdowns and modifications to the system's
clock.
The system stores these records in three databases, each having a
different purpose:
.Bl -tag -width indent
.It Pa /var/run/utx.active
Log of currently active user login sessions.
This file is similar to the traditional
.Pa utmp
file.
This file only contains process related entries, such as user login and
logout records.
.It Pa /var/log/utx.lastlogin
Log of last user login entries per user.
This file is similar to the traditional
.Pa lastlog
file.
This file only contains user login records for users who have at least
logged in once.
.It Pa /var/log/utx.log
Log of all entries, sorted by date of addition.
This file is similar to the traditional
.Pa wtmp
file.
This file may contain any type of record described below.
.El
.Pp
Each entry in these databases is defined by the structure
.Vt utmpx
found in the include file
.In utmpx.h :
.Bd -literal -offset indent
struct utmpx {
short ut_type; /* Type of entry. */
struct timeval ut_tv; /* Time entry was made. */
char ut_id[]; /* Record identifier. */
pid_t ut_pid; /* Process ID. */
char ut_user[]; /* User login name. */
char ut_line[]; /* Device name. */
char ut_host[]; /* Remote hostname. */
};
.Ed
.Pp
The
.Fa ut_type
field indicates the type of the log entry, which can have one of the
following values:
.Bl -tag -width LOGIN_PROCESS
.It Dv EMPTY
No valid user accounting information.
.It Dv BOOT_TIME
Identifies time of system boot.
.It Dv SHUTDOWN_TIME
Identifies time of system shutdown.
.It Dv OLD_TIME
Identifies time when system clock changed.
.It Dv NEW_TIME
Identifies time after system clock changed.
.It Dv USER_PROCESS
Identifies a process.
.It Dv INIT_PROCESS
Identifies a process spawned by the init process.
.It Dv LOGIN_PROCESS
Identifies the session leader of a logged-in user.
.It Dv DEAD_PROCESS
Identifies a session leader who has exited.
.El
.Pp
Entries of type
.Dv INIT_PROCESS
and
.Dv LOGIN_PROCESS
are not processed by this implementation.
.Pp
Other fields inside the structure are:
.Bl -tag -width ut_user
.It Fa ut_tv
The time the event occured.
This field is used for all types of entries.
.It Fa ut_id
An identifier that is used to refer to the entry.
This identifier can be used to remove or replace a login entry by
writing a new entry to the database containing the same value for
.Fa ut_id .
This field is only applicable to entries of type
.Dv USER_PROCESS ,
.Dv INIT_PROCESS ,
.Dv LOGIN_PROCESS
and
.Dv DEAD_PROCESS .
.It Fa ut_pid
The process identifier of the session leader of the login session.
This field is only applicable to entries of type
.Dv USER_PROCESS ,
.Dv INIT_PROCESS ,
.Dv LOGIN_PROCESS
and
.Dv DEAD_PROCESS .
.It Fa ut_user
The user login name corresponding with the login session.
This field is only applicable to entries of type
.Dv USER_PROCESS
and
.Dv INIT_PROCESS .
For
.Dv INIT_PROCESS
entries this entry typically contains the name of the login process.
.It Fa ut_line
The name of the TTY character device, without the leading
.Pa /dev/
prefix, corresponding with the device used to facilitate the user login
session.
If no TTY character device is used, this field is left blank.
This field is only applicable to entries of type
.Dv USER_PROCESS .
.It Fa ut_host
The network hostname of the remote system, connecting to perform a user
login.
If the user login session is not performed across a network, this field
is left blank.
This field is only applicable to entries of type
.Dv USER_PROCESS .
.El
.Pp
This implementation guarantees all inapplicable fields are discarded.
The
.Fa ut_user ,
.Fa ut_line
and
.Fa ut_host
fields of the structure returned by the library functions are also
guaranteed to be null-terminated in this implementation.
.Pp
The
.Fn getutxent
function can be used to read the next entry from the user accounting
database.
.Pp
The
.Fn getutxid
function searches for the next entry in the database of which the
behaviour is based on the
.Fa ut_type
field of
.Fa id .
If
.Fa ut_type
has a value of
.Dv BOOT_TIME ,
.Dv SHUTDOWN_TIME ,
.Dv OLD_TIME
or
.Dv NEW_TIME ,
it will return the next entry whose
.Fa ut_type
has an equal value.
If
.Fa ut_type
has a value of
.Dv USER_PROCESS ,
.Dv INIT_PROCESS ,
.Dv LOGIN_PROCESS
or
.Dv DEAD_PROCESS ,
it will return the next entry whose
.Fa ut_type
has one of the previously mentioned values and whose
.Fa ut_id
is equal.
.Pp
The
.Fn getutxline
function searches for the next entry in the database whose
.Fa ut_type
has a value of
.Dv USER_PROCESS
or
.Dv LOGIN_PROCESS
and whose
.Fa ut_line
is equal to the the same field in
.Fa line .
.Pp
The
.Fn getutxuser
function searches for the next entry in the database whose
.Fa ut_type
has a value of
.Dv USER_PROCESS
and whose
.Fa ut_user
is equal to
.Fa user .
.Pp
The previously mentioned functions will automatically try to open the
user accounting database if not already done so.
The
.Fn setutxdb
and
.Fn setutxent
functions allow the database to be opened manually, causing the offset
within the user accounting database to be rewound.
The
.Fn endutxent
function closes the database.
.Pp
The
.Fn setutxent
database always opens the active sessions database.
The
.Fn setutxdb
function opens the database identified by
.Fa type ,
whose value is either
.Dv UTXDB_ACTIVE ,
.Dv UTXDB_LASTLOGIN
or
.Dv UTXDB_LOG .
It will open a custom file with filename
.Fa file
instead of the system-default if
.Fa file
is not null.
Care must be taken that when using a custom filename,
.Fa type
still has to match with the actual format, since each database may use
its own file format.
.Pp
The
.Fn pututxline
function writes record
.Fa utmpx
to the system-default user accounting databases.
The value of
.Fa ut_type
determines which databases are modified.
.Pp
Entries of type
.Dv BOOT_TIME ,
.Dv SHUTDOWN_TIME ,
.Dv OLD_TIME
and
.Dv NEW_TIME
will only be written to
.Pa /var/log/utx.log .
.Pp
Entries of type
.Dv USER_PROCESS
will also be written to
.Pa /var/run/utx.active .
It will only be written to
.Pa /var/log/utx.lastlogin
if
.Fa ut_tv
for that user has a greater value than the existing entry or when no
entry for the user has been found.
.Pp
Entries of type
.Dv DEAD_PROCESS
will only be written to
.Pa /var/log/utx.log
and
.Pa /var/run/utx.active
if a corresponding
.Dv USER_PROCESS ,
.Dv INIT_PROCESS
or
.Dv LOGIN_PROCESS
entry whose
.Fa ut_id
is equal has been found in the latter.
.Pp
In addition, entries of type
.Dv BOOT_TIME
and
.Dv SHUTDOWN_TIME
will cause all entries in
.Pa /var/run/utx.active
to be discarded.
.Pp
All entries whose type has not been mentioned previously, are discarded
by this implementation of
.Fn pututxline .
.Sh RETURN VALUES
The
.Fn getutxent ,
.Fn getutxid ,
.Fn getutxline ,
and
.Fn getutxuser
functions return a pointer to an
.Vt utmpx
structure that matches the mentioned constraints on success or
.Dv NULL
when reaching the end-of-file or when an error occurs.
.Pp
The
.Fn pututxline
function returns a pointer to an
.Vt utmpx
structure containing a copy of the structure written to disk upon
success.
It returns
.Dv NULL
when the provided
.Vt utmpx
is invalid.
This may be because
.Fa ut_type
is invalid or
.Fa ut_type
has a value of
.Dv DEAD_PROCESS
and an entry with an identifier with a value equal to the field
.Fa ut_id
was not found.
.Pp
The
.Fn setutxdb
function returns 0 if the user accounting database was opened
successfully.
Otherwise, -1 is returned and the global variable
.Va errno
is set to indicate the error.
.Sh ERRORS
In addition to the error conditions described in
.Xr fopen 3 ,
the
.Fn setutxdb
function can generate the following errors:
.Bl -tag -width Er
.It Bq Er EINVAL
The
.Fa type
argument contains a value not supported by this implementation.
.It Bq Er EFTYPE
The file format is invalid.
.El
.Sh SEE ALSO
.Xr last 1 ,
.Xr write 1 ,
.Xr getpid 2 ,
.Xr gettimeofday 2 ,
.Xr tty 4 ,
.Xr ac 8 ,
.Xr newsyslog 8
.Sh STANDARDS
The
.Fn endutxent ,
.Fn getutxent ,
.Fn getutxid ,
.Fn getutxline ,
.Fn pututxline
and
.Fn setutxent
functions are expected to conform to
.St -p1003.1-2008 .
.Pp
The
.Fn getutxuser
and
.Fn setutxdb
functions,
the
.Fa ut_host
field of the
.Vt utmpx
structure and
.Dv SHUTDOWN_TIME
are extensions.
.Sh HISTORY
These functions appeared in
.Fx 9.0 .
They replaced the
.In utmp.h
interface.
.Sh AUTHORS
.An Ed Schouten Aq ed@FreeBSD.org

232
lib/libc/gen/getutxent.c Normal file
View File

@ -0,0 +1,232 @@
/*-
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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>
__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <sys/endian.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <utmpx.h>
#include "utxdb.h"
#include "un-namespace.h"
static FILE *uf = NULL;
static int udb;
static struct utmpx utx;
int
setutxdb(int db, const char *file)
{
struct stat sb;
switch (db) {
case UTXDB_ACTIVE:
if (file == NULL)
file = _PATH_UTX_ACTIVE;
break;
case UTXDB_LASTLOGIN:
if (file == NULL)
file = _PATH_UTX_LASTLOGIN;
break;
case UTXDB_LOG:
if (file == NULL)
file = _PATH_UTX_LOG;
break;
default:
errno = EINVAL;
return (-1);
}
if (uf != NULL)
fclose(uf);
uf = fopen(file, "r");
if (uf == NULL)
return (-1);
/* Safety check: never use broken files. */
if (db != UTXDB_LOG && _fstat(fileno(uf), &sb) != -1 &&
sb.st_size % sizeof(struct futx) != 0) {
fclose(uf);
uf = NULL;
errno = EFTYPE;
return (-1);
}
udb = db;
return (0);
}
void
setutxent(void)
{
setutxdb(UTXDB_ACTIVE, NULL);
}
void
endutxent(void)
{
if (uf != NULL) {
fclose(uf);
uf = NULL;
}
}
static struct futx *
getfutxent(void)
{
static struct futx fu;
if (uf == NULL)
setutxent();
if (uf == NULL)
return (NULL);
if (udb == UTXDB_LOG) {
uint16_t len;
if (fread(&len, sizeof len, 1, uf) != 1)
return (NULL);
len = be16toh(len);
if (len > sizeof fu) {
/* Forward compatibility. */
if (fread(&fu, sizeof fu, 1, uf) != 1)
return (NULL);
fseek(uf, len - sizeof fu, SEEK_CUR);
} else {
/* Partial record. */
memset(&fu, 0, sizeof fu);
if (fread(&fu, len, 1, uf) != 1)
return (NULL);
}
} else {
if (fread(&fu, sizeof fu, 1, uf) != 1)
return (NULL);
}
return (&fu);
}
struct utmpx *
getutxent(void)
{
struct futx *fu;
fu = getfutxent();
if (fu == NULL)
return (NULL);
futx_to_utx(fu, &utx);
return (&utx);
}
struct utmpx *
getutxid(const struct utmpx *id)
{
struct futx *fu;
for (;;) {
fu = getfutxent();
if (fu == NULL)
return (NULL);
switch (fu->fu_type) {
case BOOT_TIME:
case OLD_TIME:
case NEW_TIME:
case SHUTDOWN_TIME:
if (fu->fu_type == id->ut_type)
goto found;
case USER_PROCESS:
case INIT_PROCESS:
case LOGIN_PROCESS:
case DEAD_PROCESS:
switch (id->ut_type) {
case USER_PROCESS:
case INIT_PROCESS:
case LOGIN_PROCESS:
case DEAD_PROCESS:
if (memcmp(fu->fu_id, id->ut_id,
MIN(sizeof fu->fu_id, sizeof id->ut_id)) == 0)
goto found;
}
}
}
found:
futx_to_utx(fu, &utx);
return (&utx);
}
struct utmpx *
getutxline(const struct utmpx *line)
{
struct futx *fu;
for (;;) {
fu = getfutxent();
if (fu == NULL)
return (NULL);
switch (fu->fu_type) {
case USER_PROCESS:
case LOGIN_PROCESS:
if (strncmp(fu->fu_line, line->ut_line,
MIN(sizeof fu->fu_line, sizeof line->ut_line)) == 0)
goto found;
}
}
found:
futx_to_utx(fu, &utx);
return (&utx);
}
struct utmpx *
getutxuser(const char *user)
{
struct futx *fu;
for (;;) {
fu = getfutxent();
if (fu == NULL)
return (NULL);
switch (fu->fu_type) {
case USER_PROCESS:
if (strncmp(fu->fu_user, user, sizeof fu->fu_user) == 0)
goto found;
}
}
found:
futx_to_utx(fu, &utx);
return (&utx);
}

277
lib/libc/gen/pututxline.c Normal file
View File

@ -0,0 +1,277 @@
/*-
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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>
__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <sys/endian.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <utmpx.h>
#include "utxdb.h"
#include "un-namespace.h"
static FILE *
futx_open(const char *file)
{
int fd;
FILE *fp;
struct stat sb;
fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK, 0644);
if (fd < 0)
return (NULL);
/* Safety check: never use broken files. */
if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
_close(fd);
return (NULL);
}
fp = fdopen(fd, "r+");
if (fp == NULL) {
_close(fd);
return (NULL);
}
return (fp);
}
static void
utx_active_add(const struct futx *fu)
{
FILE *fp;
struct futx fe;
off_t partial = -1;
/*
* Register user login sessions. Overwrite entries of sessions
* that have already been terminated.
*/
fp = futx_open(_PATH_UTX_ACTIVE);
if (fp == NULL)
return;
while (fread(&fe, sizeof fe, 1, fp) == 1) {
switch (fe.fu_type) {
case USER_PROCESS:
case INIT_PROCESS:
case LOGIN_PROCESS:
case DEAD_PROCESS:
/* Overwrite when ut_id matches. */
if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) == 0) {
fseeko(fp, -sizeof fe, SEEK_CUR);
goto exact;
}
if (fe.fu_type != DEAD_PROCESS)
break;
/* FALLTHROUGH */
default:
/* Allow us to overwrite unused records. */
if (partial == -1)
partial = fseeko(fp, 0, SEEK_CUR) - sizeof fe;
break;
}
}
/*
* No exact match found. Use the partial match. If no partial
* match was found, just append a new record.
*/
if (partial != -1)
fseeko(fp, partial, SEEK_SET);
exact:
fwrite(fu, sizeof *fu, 1, fp);
fclose(fp);
}
static int
utx_active_remove(struct futx *fu)
{
FILE *fp;
struct futx fe;
/*
* Remove user login sessions, having the same ut_id.
*/
fp = futx_open(_PATH_UTX_ACTIVE);
if (fp == NULL)
return (0);
while (fread(&fe, sizeof fe, 1, fp) == 1) {
switch (fe.fu_type) {
case USER_PROCESS:
case INIT_PROCESS:
case LOGIN_PROCESS:
if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) != 0)
continue;
/*
* Prevent login sessions from having a negative
* timespan.
*/
if (be64toh(fu->fu_tv) < be64toh(fe.fu_tv))
fu->fu_tv = fe.fu_tv;
/* Terminate session. */
fseeko(fp, -sizeof fe, SEEK_CUR);
fwrite(fu, sizeof *fu, 1, fp);
fclose(fp);
return (0);
}
}
fclose(fp);
return (1);
}
static void
utx_active_purge(void)
{
truncate(_PATH_UTX_ACTIVE, 0);
}
static void
utx_lastlogin_add(const struct futx *fu)
{
FILE *fp;
struct futx fe;
/*
* Write an entry to lastlogin. Overwrite the entry if the
* current user already has an entry. If not, append a new
* entry.
*/
fp = futx_open(_PATH_UTX_LASTLOGIN);
if (fp == NULL)
return;
while (fread(&fe, sizeof fe, 1, fp) == 1) {
if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
continue;
/* Prevent lowering the time value. */
if (be64toh(fu->fu_tv) <= be64toh(fe.fu_tv))
goto done;
/* Found a previous lastlogin entry for this user. */
fseeko(fp, -sizeof fe, SEEK_CUR);
break;
}
fwrite(fu, sizeof *fu, 1, fp);
done:
fclose(fp);
}
static void
utx_lastlogin_upgrade(void)
{
int fd;
struct stat sb;
fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644);
if (fd < 0)
return;
/*
* Truncate broken lastlogin files. In the future we should
* check for older versions of the file format here and try to
* upgrade it.
*/
if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
ftruncate(fd, 0);
_close(fd);
}
static void
utx_log_add(const struct futx *fu)
{
int fd;
uint16_t l;
struct iovec vec[2];
/*
* Append an entry to the log file. We only need to append
* records to this file, so to conserve space, trim any trailing
* zero-bytes. Prepend a length field, indicating the length of
* the record, excluding the length field itself.
*/
for (l = sizeof *fu; l > 0 && ((const char *)fu)[l - 1] == '\0'; l--);
vec[0].iov_base = &l;
vec[0].iov_len = sizeof l;
vec[1].iov_base = __DECONST(void *, fu);
vec[1].iov_len = l;
l = htobe16(l);
fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644);
if (fd < 0)
return;
_writev(fd, vec, 2);
_close(fd);
}
struct utmpx *
pututxline(const struct utmpx *utmpx)
{
struct futx fu;
static struct utmpx ut;
utx_to_futx(utmpx, &fu);
switch (fu.fu_type) {
case BOOT_TIME:
case SHUTDOWN_TIME:
utx_active_purge();
utx_lastlogin_upgrade();
break;
case OLD_TIME:
case NEW_TIME:
break;
case USER_PROCESS:
utx_active_add(&fu);
utx_lastlogin_add(&fu);
break;
#if 0 /* XXX: Are these records of any use to us? */
case INIT_PROCESS:
case LOGIN_PROCESS:
utx_active_add(&fu);
break;
#endif
case DEAD_PROCESS:
if (utx_active_remove(&fu) != 0)
return (NULL);
break;
default:
return (NULL);
}
utx_log_add(&fu);
futx_to_utx(&fu, &ut);
return (&ut);
}

162
lib/libc/gen/utxdb.c Normal file
View File

@ -0,0 +1,162 @@
/*-
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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>
__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <sys/endian.h>
#include <sys/param.h>
#include <string.h>
#include <utmpx.h>
#include "utxdb.h"
#include "un-namespace.h"
#define UTOF_STRING(ut, fu, field) do { \
strncpy((fu)->fu_ ## field, (ut)->ut_ ## field, \
MIN(sizeof (fu)->fu_ ## field, sizeof (ut)->ut_ ## field)); \
} while (0)
#define UTOF_ID(ut, fu) do { \
memcpy((fu)->fu_id, (ut)->ut_id, \
MIN(sizeof (fu)->fu_id, sizeof (ut)->ut_id)); \
} while (0)
#define UTOF_PID(ut, fu) do { \
(fu)->fu_pid = htobe32((ut)->ut_pid); \
} while (0)
#define UTOF_TYPE(ut, fu) do { \
(fu)->fu_type = (ut)->ut_type; \
} while (0)
#define UTOF_TV(ut, fu) do { \
(fu)->fu_tv = htobe64((uint64_t)(ut)->ut_tv.tv_sec * 1000000 + \
(uint64_t)(ut)->ut_tv.tv_usec); \
} while (0)
void
utx_to_futx(const struct utmpx *ut, struct futx *fu)
{
memset(fu, 0, sizeof *fu);
switch (ut->ut_type) {
case BOOT_TIME:
case OLD_TIME:
case NEW_TIME:
/* Extension: shutdown time. */
case SHUTDOWN_TIME:
break;
case USER_PROCESS:
UTOF_ID(ut, fu);
UTOF_STRING(ut, fu, user);
UTOF_STRING(ut, fu, line);
/* Extension: host name. */
UTOF_STRING(ut, fu, host);
UTOF_PID(ut, fu);
break;
case INIT_PROCESS:
UTOF_ID(ut, fu);
UTOF_PID(ut, fu);
break;
case LOGIN_PROCESS:
UTOF_ID(ut, fu);
UTOF_STRING(ut, fu, user);
UTOF_PID(ut, fu);
break;
case DEAD_PROCESS:
UTOF_ID(ut, fu);
UTOF_PID(ut, fu);
break;
default:
fu->fu_type = EMPTY;
return;
}
UTOF_TYPE(ut, fu);
UTOF_TV(ut, fu);
}
#define FTOU_STRING(fu, ut, field) do { \
strncpy((ut)->ut_ ## field, (fu)->fu_ ## field, \
MIN(sizeof (ut)->ut_ ## field - 1, sizeof (fu)->fu_ ## field)); \
} while (0)
#define FTOU_ID(fu, ut) do { \
memcpy((ut)->ut_id, (fu)->fu_id, \
MIN(sizeof (ut)->ut_id, sizeof (fu)->fu_id)); \
} while (0)
#define FTOU_PID(fu, ut) do { \
(ut)->ut_pid = be32toh((fu)->fu_pid); \
} while (0)
#define FTOU_TYPE(fu, ut) do { \
(ut)->ut_type = (fu)->fu_type; \
} while (0)
#define FTOU_TV(fu, ut) do { \
uint64_t t; \
t = be64toh((fu)->fu_tv); \
(ut)->ut_tv.tv_sec = t / 1000000; \
(ut)->ut_tv.tv_usec = t % 1000000; \
} while (0)
void
futx_to_utx(const struct futx *fu, struct utmpx *ut)
{
memset(ut, 0, sizeof *ut);
switch (fu->fu_type) {
case BOOT_TIME:
case OLD_TIME:
case NEW_TIME:
/* Extension: shutdown time. */
case SHUTDOWN_TIME:
break;
case USER_PROCESS:
FTOU_ID(fu, ut);
FTOU_STRING(fu, ut, user);
FTOU_STRING(fu, ut, line);
/* Extension: host name. */
FTOU_STRING(fu, ut, host);
FTOU_PID(fu, ut);
break;
case INIT_PROCESS:
FTOU_ID(fu, ut);
FTOU_PID(fu, ut);
break;
case LOGIN_PROCESS:
FTOU_ID(fu, ut);
FTOU_STRING(fu, ut, user);
FTOU_PID(fu, ut);
break;
case DEAD_PROCESS:
FTOU_ID(fu, ut);
FTOU_PID(fu, ut);
break;
default:
ut->ut_type = EMPTY;
return;
}
FTOU_TYPE(fu, ut);
FTOU_TV(fu, ut);
}

61
lib/libc/gen/utxdb.h Normal file
View File

@ -0,0 +1,61 @@
/*-
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _UTXDB_H_
#define _UTXDB_H_
#include <stdint.h>
#define _PATH_UTX_ACTIVE "/var/run/utx.active"
#define _PATH_UTX_LASTLOGIN "/var/log/utx.lastlogin"
#define _PATH_UTX_LOG "/var/log/utx.log"
/*
* Entries in struct futx are ordered by how often they are used. In
* utx.log only entries will be written until the last non-zero byte,
* which means we want to put the hostname at the end. Most primitive
* records only store a ut_type and ut_tv, which means we want to store
* those at the front.
*/
struct utmpx;
struct futx {
uint8_t fu_type;
uint64_t fu_tv;
char fu_id[8];
uint32_t fu_pid;
char fu_user[32];
char fu_line[16];
char fu_host[128];
} __packed;
void utx_to_futx(const struct utmpx *, struct futx *);
void futx_to_utx(const struct futx *, struct utmpx *);
#endif /* !_UTXDB_H_ */

View File

@ -26,13 +26,6 @@ MLINKS+=ulog_getutxent.3 ulog_endutxent.3 \
utempter_remove_added_record.3 removeFromUtmp.3 \
utempter_remove_record.3 removeLineFromUtmp.3
# Add links to <utmpx.h>-style functions.
MLINKS+=ulog_endutxent.3 endutxent.3 \
ulog_getutxent.3 getutxent.3 \
ulog_getutxline.3 getutxline.3 \
ulog_pututxline.3 pututxline.3 \
ulog_setutxent.3 setutxent.3
VERSION_DEF= ${.CURDIR}/../libc/Versions.def
SYMBOL_MAPS= ${.CURDIR}/Symbol.map