#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: utmpx.c,v 1.25 2008/04/28 20:22:59 martin Exp $");
#endif
#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>
#ifdef UNIFDEF_LEGACY_UTMP_APIS
#include <utmp.h>
#endif
#include <utmpx.h>
#include <utmpx-darwin.h>
#include <errno.h>
#include <vis.h>
#include <notify.h>
__private_extern__
struct _utmpx __utx__ = {
__UTX_MAGIC__,
{},
PTHREAD_MUTEX_INITIALIZER,
_PATH_UTMPX,
NULL,
1,
0,
};
static struct utmpx *__getutxid(struct _utmpx *, const struct utmpx *);
__private_extern__ const char _utmpx_vers[] = "utmpx-1.00";
__private_extern__ void
__setutxent(struct _utmpx *U)
{
(void)memset(&U->ut, 0, sizeof(U->ut));
if (U->fp == NULL)
return;
#ifdef __LP64__
(void)fseeko(U->fp, (off_t)sizeof(struct utmpx32), SEEK_SET);
#else
(void)fseeko(U->fp, (off_t)sizeof(U->ut), SEEK_SET);
#endif
}
void
_setutxent(struct _utmpx *U)
{
TEST_UTMPX_T("_setutxent", U);
UTMPX_LOCK(U);
__setutxent(U);
UTMPX_UNLOCK(U);
}
void
setutxent()
{
_setutxent(&__utx__);
}
__private_extern__ void
__endutxent(struct _utmpx *U)
{
(void)memset(&U->ut, 0, sizeof(U->ut));
if (U->fp != NULL) {
int saveerrno = errno;
(void)fclose(U->fp);
errno = saveerrno;
U->fp = NULL;
U->readonly = 0;
}
}
void
_endutxent(struct _utmpx *U)
{
TEST_UTMPX_T("_endutxent", U);
UTMPX_LOCK(U);
__endutxent(U);
UTMPX_UNLOCK(U);
}
void
endutxent()
{
_endutxent(&__utx__);
}
__private_extern__ struct utmpx *
__getutxent(struct _utmpx *U)
{
int saveerrno;
#ifdef __LP64__
struct utmpx32 ut32;
#endif
if (U->fp == NULL) {
struct stat st;
if ((U->fp = fopen(U->utfile, "r+")) == NULL)
if ((U->fp = fopen(U->utfile, "w+")) == NULL) {
if ((U->fp = fopen(U->utfile, "r")) == NULL)
goto fail;
else
U->readonly = 1;
}
fcntl(fileno(U->fp), F_SETFD, 1);
if (fstat(fileno(U->fp), &st) == -1)
goto failclose;
if (st.st_size == 0) {
#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, U->fp) != 1)
#else
(void)memset(&U->ut, 0, sizeof(U->ut));
U->ut.ut_type = SIGNATURE;
(void)memcpy(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers));
if (fwrite(&U->ut, sizeof(U->ut), 1, U->fp) != 1)
#endif
goto failclose;
} else {
#ifdef __LP64__
if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1)
#else
if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1)
#endif
goto failclose;
#ifdef __LP64__
if (memcmp(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 ||
ut32.ut_type != SIGNATURE)
#else
if (memcmp(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 ||
U->ut.ut_type != SIGNATURE)
#endif
{
errno = EINVAL;
goto failclose;
}
}
}
#ifdef __LP64__
if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1)
#else
if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1)
#endif
goto fail;
#ifdef __LP64__
_utmpx32_64(&ut32, &U->ut);
#endif
return &U->ut;
failclose:
saveerrno = errno;
(void)fclose(U->fp);
errno = saveerrno;
U->fp = NULL;
fail:
(void)memset(&U->ut, 0, sizeof(U->ut));
return NULL;
}
struct utmpx *
_getutxent(struct _utmpx *U)
{
struct utmpx *ret;
TEST_UTMPX_T("_getutxent", U);
UTMPX_LOCK(U);
ret = __getutxent(U);
UTMPX_UNLOCK(U);
return ret;
}
struct utmpx *
getutxent()
{
return _getutxent(&__utx__);
}
struct utmpx *
_getutxid(struct _utmpx *U, const struct utmpx *utx)
{
struct utmpx temp;
const struct utmpx *ux;
struct utmpx *ret;
if (utx->ut_type == EMPTY)
return NULL;
TEST_UTMPX_T("_getutxid", U);
UTMPX_LOCK(U);
ux = _utmpx_working_copy(utx, &temp, 1);
if (!ux) {
UTMPX_UNLOCK(U);
return NULL;
}
ret = __getutxid(U, ux);
UTMPX_UNLOCK(U);
return ret;
}
struct utmpx *
getutxid(const struct utmpx *utx)
{
return _getutxid(&__utx__, utx);
}
static struct utmpx *
__getutxid(struct _utmpx *U, const struct utmpx *utx)
{
do {
if (U->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 (U->ut.ut_type == utx->ut_type)
return &U->ut;
break;
case INIT_PROCESS:
case LOGIN_PROCESS:
case USER_PROCESS:
case DEAD_PROCESS:
switch (U->ut.ut_type) {
case INIT_PROCESS:
case LOGIN_PROCESS:
case USER_PROCESS:
case DEAD_PROCESS:
if (memcmp(U->ut.ut_id, utx->ut_id,
sizeof(U->ut.ut_id)) == 0)
return &U->ut;
break;
default:
break;
}
break;
default:
return NULL;
}
} while (__getutxent(U) != NULL);
return NULL;
}
static struct utmpx *
__getutxline(struct _utmpx *U, const struct utmpx *utx)
{
do {
switch (U->ut.ut_type) {
case EMPTY:
break;
case LOGIN_PROCESS:
case USER_PROCESS:
if (strncmp(U->ut.ut_line, utx->ut_line,
sizeof(U->ut.ut_line)) == 0)
return &U->ut;
break;
default:
break;
}
} while (__getutxent(U) != NULL);
return NULL;
}
struct utmpx *
_getutxline(struct _utmpx *U, const struct utmpx *utx)
{
struct utmpx *ret;
TEST_UTMPX_T("_getutxline", U);
UTMPX_LOCK(U);
ret = __getutxline(U, utx);
UTMPX_UNLOCK(U);
return ret;
}
struct utmpx *
getutxline(const struct utmpx *utx)
{
return _getutxline(&__utx__, utx);
}
struct utmpx *
_pututxline(struct _utmpx *U, const struct utmpx *utx)
{
struct utmpx *ux;
if (utx == NULL) {
errno = EINVAL;
return NULL;
}
TEST_UTMPX_T("_pututxline", U);
UTMPX_LOCK(U);
if ((ux = __pututxline(&__utx__, utx)) != NULL && __utx__.utfile_system) {
_utmpx_asl(ux);
#ifdef UTMP_COMPAT
_write_utmp_compat(ux);
#endif
}
UTMPX_UNLOCK(U);
return ux;
}
struct utmpx *
pututxline(const struct utmpx *utx)
{
return _pututxline(&__utx__, utx);
}
__private_extern__ struct utmpx *
__pututxline(struct _utmpx *U, const struct utmpx *utx)
{
struct utmpx temp, *u = NULL, *x;
const struct utmpx *ux;
#ifdef __LP64__
struct utmpx32 ut32;
#endif
struct flock fl;
#define gotlock (fl.l_start >= 0)
fl.l_start = -1;
if (U->utfile_system)
if ((U->fp != NULL && U->readonly) || (U->fp == NULL && geteuid() != 0)) {
errno = EPERM;
return NULL;
}
if (U->fp == NULL) {
(void)__getutxent(U);
if (U->fp == NULL || U->readonly) {
errno = EPERM;
return NULL;
}
}
ux = _utmpx_working_copy(utx, &temp, 0);
if (!ux)
return NULL;
if ((x = __getutxid(U, ux)) == NULL) {
__setutxent(U);
if ((x = __getutxid(U, ux)) == NULL) {
if (ux->ut_type == DEAD_PROCESS &&
(utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK)) {
errno = EINVAL;
return NULL;
}
if ((fl.l_start = lseek(fileno(U->fp), 0, SEEK_CUR)) < 0)
return NULL;
fl.l_len = 0;
fl.l_whence = SEEK_SET;
fl.l_type = F_WRLCK;
if (fcntl(fileno(U->fp), F_SETLKW, &fl) == -1)
return NULL;
if (fseeko(U->fp, (off_t)0, SEEK_END) == -1)
goto fail;
}
}
if (!gotlock) {
if (ux->ut_type == DEAD_PROCESS &&
(utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK) &&
x->ut_type != USER_PROCESS) {
errno = EINVAL;
return NULL;
}
#ifdef __LP64__
if (fseeko(U->fp, -(off_t)sizeof(ut32), SEEK_CUR) == -1)
#else
if (fseeko(U->fp, -(off_t)sizeof(U->ut), SEEK_CUR) == -1)
#endif
return NULL;
}
#ifdef __LP64__
_utmpx64_32(ux, &ut32);
if (fwrite(&ut32, sizeof (ut32), 1, U->fp) != 1)
#else
if (fwrite(ux, sizeof (*ux), 1, U->fp) != 1)
#endif
goto fail;
if (fflush(U->fp) == -1)
goto fail;
u = memcpy(&U->ut, ux, sizeof(U->ut));
notify_post(UTMPX_CHANGE_NOTIFICATION);
fail:
if (gotlock) {
int save = errno;
fl.l_type = F_UNLCK;
if (fcntl(fileno(U->fp), F_SETLK, &fl) == -1)
return NULL;
errno = save;
}
return u;
}
__private_extern__ int
__utmpxname(struct _utmpx *U, const char *fname)
{
size_t len;
if (fname == NULL) {
if(!U->utfile_system)
free(U->utfile);
U->utfile = _PATH_UTMPX;
U->utfile_system = 1;
__endutxent(U);
return 1;
}
len = strlen(fname);
if (len >= MAXPATHLEN)
return 0;
if (fname[len - 1] != 'x')
return 0;
if (U->utfile_system)
U->utfile = NULL;
U->utfile_system = 0;
if ((U->utfile = reallocf(U->utfile, len + 1)) == NULL)
return 0;
(void)strcpy(U->utfile, fname);
__endutxent(U);
return 1;
}
int
_utmpxname(struct _utmpx *U, const char *fname)
{
int ret;
TEST_UTMPX_T("_utmpxname", U);
UTMPX_LOCK(U);
ret = __utmpxname(U, fname);
UTMPX_UNLOCK(U);
return ret;
}
int
utmpxname(const char *fname)
{
return _utmpxname(&__utx__, fname);
}
#ifdef UNIFDEF_LEGACY_UTMP_APIS
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;
}
#endif