I've implemented utmpx for FreeBSD, and placed it under BSD license. This should be a standards-compliant (SUSv2) implementation, that is approximately equivilent to the utmp in glibc or Solaris. The major difference from Linux is that it uses /var/run/utmpx, and getutent becomes getutxent. I haven't tested this thing very heavily yet, so please look it over closely. If someone would like to take on writing some code to test it thoroughly, I'd be very appreciative. I have it working with sysvinit (major reduction in the size and ugliness of that patch) and shellutils. I'll be better able to tell how well it's working after I get Matthew Garrett's libshadow ported over. Enjoy, ---Nathan
/*
* Copyright (c) 2002
* Nathan Hawkins. 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.
*
*/
#ifndef _UTMPX_H_
#define _UTMPX_H_
#include <sys/types.h>
#include <sys/time.h>
#define _PATH_UTMPX "/var/run/utmpx"
#define UTMPX_FILE _PATH_UTMPX
/* features in utmpx */
#define _HAVE_UT_PID 1
#define _HAVE_UT_ID 1
#define _HAVE_UT_TV 1
#define _HAVE_UT_TYPE 1
#define _HAVE_UT_HOST 1
/* size of character arrays in struct utmpx */
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
struct exit_status {
short int e_termination; /* Process termination status. */
short int e_exit; /* Process exit status. */
};
struct utmpx
{
pid_t ut_pid;
char ut_id[4];
/* reserve space for IPv6 addresses */
u_int32_t ut_addr6[4];
long int ut_session; /* used by X? */
struct timeval ut_tv;
struct exit_status ut_exit;
short int ut_type;
char ut_line[UT_LINESIZE];
char ut_user[UT_NAMESIZE];
char ut_host[UT_HOSTSIZE];
char _unused[20];
};
/* for compatibility */
#define ut_name ut_user
/* values for ut_type */
#define EMPTY 0 /* Empty struct */
#define RUN_LVL 1 /* SysV runlevel */
#define BOOT_TIME 2 /* System boot */
#define NEW_TIME 3 /* Mark new time at time change */
#define OLD_TIME 4 /* Mark old time at time change */
#define INIT_PROCESS 5 /* Process started by init */
#define LOGIN_PROCESS 6 /* User login */
#define USER_PROCESS 7 /* Normal process. */
#define DEAD_PROCESS 8 /* Terminated process. */
#define ACCOUNTING 9
__BEGIN_DECLS
void endutxent(void);
void setutxent(void);
struct utmpx *getutxent(void);
struct utmpx *getutxid(const struct utmpx *);
struct utmpx *getutxline(const struct utmpx *);
struct utmpx *pututxline(const struct utmpx *);
void utmpxname(const char *);
__END_DECLS
#endif /* !_UTMPX_H_ */
/*
* Copyright (c) 2002
* Nathan Hawkins. 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/file.h>
#include <stdio.h>
#include <fcntl.h>
#include "utmpx.h"
static int utx_fd=-1;
static off_t utx_offset=-1;
static struct utmpx utx_last;
static char default_utmpxfile[]=_PATH_UTMPX;
static char *utmpxfile=&default_utmpxfile[0];
static int utx_ro=0;
static int
openutxfile (void)
{
if(utx_fd == -1) {
utx_fd = open(utmpxfile, O_RDWR);
if(utx_fd == -1) {
utx_fd = open(utmpxfile, O_RDONLY);
utx_ro = 1; /* set read-only flag */
if(utx_fd == -1)
return (1);
} else
utx_ro=0; /* clear read-only flag */
}
utx_offset = 0;
return (0);
}
/*
* setutxent rewinds the utmpx file. If the file isn't open, it does nothing.
*/
void
setutxent (void)
{
if (utx_fd == -1)
return;
utx_offset = lseek(utx_fd, 0, SEEK_SET);
}
/*
* endutxent closes the utmpx file, and restores the filename to the default.
*/
void
endutxent (void)
{
if (utx_fd != -1) {
close (utx_fd);
utx_fd = -1;
}
utmpxfile = default_utmpxfile;
}
/*
* utmpxname is a nonstandard function used to select an alternate utmpx file.
* The string passed here isn't used until one of the other functions opens the
* file. endutxent resets the filename to default.
*/
void
utmpxname (const char *utmpx)
{
if (utmpx != NULL)
utmpxfile = (char *)utmpx;
}
static struct utmpx *
getutxent_internal (struct utmpx *ut)
{
/* either read a full struct utmpx from the file or return NULL */
if (read(utx_fd, ut, sizeof (struct utmpx)) !=
sizeof (struct utmpx))
return (NULL);
utx_offset += sizeof (struct utmpx);
return (ut);
}
struct utmpx *
getutxent_r (struct utmpx *buffer)
{
struct utmpx *ut;
int flag;
/* open the file if it isn't already */
if (utx_fd == -1 && openutxfile())
return (NULL);
/* lock the file for reading */
flag=flock(utx_fd,LOCK_SH);
if (flag == -1)
return (NULL); /* failed to get the lock */
ut=getutxent_internal(buffer);
flag=flock(utx_fd,LOCK_UN);
if (flag == -1)
return (NULL); /* failed to unlock for some reason */
return (ut);
}
/*
* According to SUSv2:
*
* The getutxid() function searches forward from the current point in the
* database. If the ut_type value of the utmpx structure pointed to by id is
* BOOT_TIME, OLD_TIME or NEW_TIME, then it stops when it finds an entry with a
* matching ut_type value. If the ut_type value is INIT_PROCESS, LOGIN_PROCESS,
* USER_PROCESS, or DEAD_PROCESS, then it stops when it finds an entry whose
* type is one of these four and whose ut_id member matches the ut_id member of
* the utmpx structure pointed to by id. If the end of the database is reached
* without a match, getutxid() fails.
*/
struct utmpx *
getutxid_r (const struct utmpx *ut, struct utmpx *buffer)
{
int finished;
struct utmpx *last;
int flag;
/* open the file if it isn't already */
if (utx_fd == -1 && openutxfile())
return (NULL);
/* lock the file for reading */
flag=flock(utx_fd,LOCK_SH);
if (flag == -1)
return (NULL); /* failed to get the lock */
for (;;) {
if ((last=getutxent_internal(buffer)) == NULL)
break;
switch (ut->ut_type) {
case BOOT_TIME:
case OLD_TIME:
case NEW_TIME:
if (ut->ut_type == last->ut_type)
finished = 1;
break;
case INIT_PROCESS:
case LOGIN_PROCESS:
case USER_PROCESS:
case DEAD_PROCESS:
if (ut->ut_type == last->ut_type &&
strncmp(ut->ut_id,last->ut_id,4) == 0)
finished = 1;
break;
/* evidently this function ignores other types */
default:
break;
}
if (finished)
break;
}
flag=flock(utx_fd,LOCK_UN);
if (flag == -1)
return (NULL); /* failed to unlock for some reason */
return (last);
}
/*
* According to SUSv2:
*
* The getutxline() function searches forward from the current point in the
* database until it finds an entry of the type LOGIN_PROCESS or USER_PROCESS
* which also has a ut_line value matching that in the utmpx structure pointed
* to by line. If the end of the database is reached without a match,
* getutxline() fails.
*/
struct utmpx *
getutxline_r (const struct utmpx *ut, struct utmpx *buffer)
{
int finished;
struct utmpx *last;
int flag;
/* open the file if it isn't already */
if (utx_fd == -1 && openutxfile())
return (NULL);
/* lock the file for reading */
flag=flock(utx_fd,LOCK_SH);
if (flag == -1)
return (NULL); /* failed to get the lock */
for (;;) {
if ((last=getutxent_internal(buffer)) == NULL)
break;
switch (last->ut_type) {
case LOGIN_PROCESS:
case USER_PROCESS:
if (strncmp(ut->ut_line,last->ut_line,UT_LINESIZE) == 0)
finished = 1;
break;
/* evidently this function ignores other types */
default:
break;
}
if (finished)
break;
}
flag=flock(utx_fd,LOCK_UN);
if (flag == -1)
return (NULL); /* failed to unlock for some reason */
return (last);
}
/*
* According to SUSv2:
*
* If the process has appropriate privileges, the pututxline() function writes
* out the structure into the user accounting database. It uses getutxid() to
* search for a record that satisfies the request. If this search succeeds,
* then the entry is replaced. Otherwise, a new entry is made at the end of the
* user accounting database.
*
* The implicit read done by pututxline() (if it finds that it is not already
* at the correct place in the user accounting database) will not modify the
* static structure returned by getutxent(), getutxid() or getutxline(), if the
* application has just modified this structure and passed the pointer back to
* pututxline().
*/
struct utmpx *
pututxline (const struct utmpx *ut)
{
struct utmpx findbuf;
struct utmpx *found;
int flag;
if (utx_ro)
return (NULL); /* don't have write access to utmpx */
if ((found=getutxid_r(ut,&findbuf))!=NULL) {
/* try to rewind the file by exactly one record */
utx_offset = lseek(utx_fd, utx_offset - sizeof (struct utmpx),
SEEK_CUR);
} else {
/* seek to the end of the file */
utx_offset = lseek(utx_fd, 0, SEEK_END);
}
/* lock the file for writing */
flag=flock(utx_fd,LOCK_EX);
if (flag == -1)
return (NULL); /* failed to get the lock */
if (write(utx_fd, ut, sizeof (struct utmpx)) != sizeof (struct utmpx)) {
/*
* In case of failed write to the end of the file, truncate the
* record. This keeps the file format from getting corrupted.
*/
if (found != NULL) {
ftruncate(utx_fd, utx_offset);
return (NULL);
} else {
return (NULL);
}
}
utx_offset += sizeof (struct utmpx);
flag=flock(utx_fd,LOCK_UN);
if (flag == -1)
return (NULL); /* failed to unlock for some reason */
return ((struct utmpx *)ut);
}
struct utmpx *
getutxent(void)
{
getutxent_r(&utx_last);
}
struct utmpx *
getutxid (const struct utmpx *ut)
{
getutxid_r(ut,&utx_last);
}
struct utmpx *
getutxline (const struct utmpx *ut)
{
getutxid_r(ut,&utx_last);
}
Attachment:
pgpBXq4S0v2ZE.pgp
Description: PGP signature