4a1f09d742
The TTY line name should always be set for DEAD_PROCESS entries right now. When we parse a clean utmp entry, we don't want to interpret it as a DEAD_PROCESS entry if the TTY has never been used yet.
318 lines
7.4 KiB
C
318 lines
7.4 KiB
C
/*-
|
|
* Copyright (c) 2009 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 <sys/param.h>
|
|
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <timeconv.h>
|
|
|
|
#include "ulog_internal.h"
|
|
|
|
static FILE *ufile;
|
|
static int ufileindex = -1;
|
|
|
|
void
|
|
ulog_endutxent(void)
|
|
{
|
|
if (ufile != NULL)
|
|
fclose(ufile);
|
|
ufile = NULL;
|
|
}
|
|
|
|
/*
|
|
* Conversion from on-disk formats to generic ulog_utmpx structure.
|
|
*/
|
|
|
|
static void
|
|
ulog_futmp_to_utmpx(const struct futmp *ut, struct ulog_utmpx *utx)
|
|
{
|
|
|
|
memset(utx, 0, sizeof *utx);
|
|
#define COPY_STRING(field) do { \
|
|
strncpy(utx->ut_ ## field, ut->ut_ ## field, \
|
|
MIN(sizeof utx->ut_ ## field - 1, sizeof ut->ut_ ## field));\
|
|
} while (0)
|
|
#define MATCH(field, value) (strncmp(ut->ut_ ## field, (value), \
|
|
sizeof(ut->ut_ ## field)) == 0)
|
|
if (MATCH(user, "reboot") && MATCH(line, "~"))
|
|
utx->ut_type = BOOT_TIME;
|
|
else if (MATCH(user, "date") && MATCH(line, "|"))
|
|
utx->ut_type = OLD_TIME;
|
|
else if (MATCH(user, "date") && MATCH(line, "{"))
|
|
utx->ut_type = NEW_TIME;
|
|
else if (MATCH(user, "shutdown") && MATCH(line, "~"))
|
|
utx->ut_type = SHUTDOWN_TIME;
|
|
else if (MATCH(user, "") && MATCH(host, "") && !MATCH(line, "")) {
|
|
utx->ut_type = DEAD_PROCESS;
|
|
/* XXX: ut_id and ut_pid missing. ut_line not needed. */
|
|
COPY_STRING(line);
|
|
} else if (!MATCH(user, "") && !MATCH(line, "") && ut->ut_time != 0) {
|
|
utx->ut_type = USER_PROCESS;
|
|
/* XXX: ut_id and ut_pid missing. */
|
|
COPY_STRING(user);
|
|
COPY_STRING(line);
|
|
COPY_STRING(host);
|
|
} else {
|
|
/* Only set ut_type for EMPTY. */
|
|
utx->ut_type = EMPTY;
|
|
return;
|
|
}
|
|
#undef COPY_STRING
|
|
#undef MATCH
|
|
utx->ut_tv.tv_sec = _time32_to_time(ut->ut_time);
|
|
utx->ut_tv.tv_usec = 0;
|
|
}
|
|
|
|
static void
|
|
ulog_flastlog_to_utmpx(const struct flastlog *ll, struct ulog_utmpx *utx)
|
|
{
|
|
|
|
memset(utx, 0, sizeof *utx);
|
|
#define COPY_STRING(field) do { \
|
|
strncpy(utx->ut_ ## field, ll->ll_ ## field, \
|
|
MIN(sizeof utx->ut_ ## field - 1, sizeof ll->ll_ ## field));\
|
|
} while (0)
|
|
#define MATCH(field, value) (strncmp(ll->ll_ ## field, (value), \
|
|
sizeof(ll->ll_ ## field)) == 0)
|
|
if (!MATCH(line, "") && ll->ll_time != 0) {
|
|
utx->ut_type = USER_PROCESS;
|
|
/* XXX: ut_id and ut_pid missing. */
|
|
COPY_STRING(line);
|
|
COPY_STRING(host);
|
|
} else {
|
|
/* Only set ut_type for EMPTY. */
|
|
utx->ut_type = EMPTY;
|
|
return;
|
|
}
|
|
#undef COPY_STRING
|
|
#undef MATCH
|
|
utx->ut_tv.tv_sec = _time32_to_time(ll->ll_time);
|
|
utx->ut_tv.tv_usec = 0;
|
|
}
|
|
|
|
/*
|
|
* File I/O.
|
|
*/
|
|
|
|
static inline off_t
|
|
ulog_tell(void)
|
|
{
|
|
|
|
if (ufileindex == UTXI_USER)
|
|
return (ftello(ufile) / sizeof(struct flastlog));
|
|
else
|
|
return (ftello(ufile) / sizeof(struct futmp));
|
|
}
|
|
|
|
static struct ulog_utmpx *
|
|
ulog_read(off_t off, int whence, int resolve_user)
|
|
{
|
|
static struct ulog_utmpx utx;
|
|
|
|
if (ufile == NULL)
|
|
ulog_setutxent();
|
|
if (ufile == NULL)
|
|
return (NULL);
|
|
|
|
/* Only allow seeking to move forward. */
|
|
if (whence == SEEK_SET && ulog_tell() > off)
|
|
return (NULL);
|
|
|
|
if (ufileindex == UTXI_USER) {
|
|
struct flastlog ll;
|
|
struct passwd *pw = NULL;
|
|
uid_t uid;
|
|
|
|
if (fseeko(ufile, off * sizeof ll, whence) != 0)
|
|
return (NULL);
|
|
uid = ulog_tell();
|
|
if (fread(&ll, sizeof ll, 1, ufile) != 1)
|
|
return (NULL);
|
|
ulog_flastlog_to_utmpx(&ll, &utx);
|
|
if (utx.ut_type == USER_PROCESS && resolve_user)
|
|
pw = getpwuid(uid);
|
|
if (pw != NULL)
|
|
strlcpy(utx.ut_user, pw->pw_name, sizeof utx.ut_user);
|
|
else
|
|
sprintf(utx.ut_user, "%u", (unsigned int)uid);
|
|
} else {
|
|
struct futmp ut;
|
|
|
|
if (fseeko(ufile, off * sizeof(struct futmp), whence) != 0)
|
|
return (NULL);
|
|
if (fread(&ut, sizeof ut, 1, ufile) != 1)
|
|
return (NULL);
|
|
ulog_futmp_to_utmpx(&ut, &utx);
|
|
}
|
|
return (&utx);
|
|
}
|
|
|
|
/*
|
|
* getutxent().
|
|
*
|
|
* Read the next entry from the file.
|
|
*/
|
|
|
|
struct ulog_utmpx *
|
|
ulog_getutxent(void)
|
|
{
|
|
|
|
return ulog_read(0, SEEK_CUR, 1);
|
|
}
|
|
|
|
/*
|
|
* ulog_getutxline().
|
|
*
|
|
* Read entries from the file, until reaching an entry which matches the
|
|
* provided TTY device name. We can optimize the case for utmp files,
|
|
* because they are indexed by TTY device name.
|
|
*/
|
|
|
|
struct ulog_utmpx *
|
|
ulog_getutxline(const struct ulog_utmpx *line)
|
|
{
|
|
struct ulog_utmpx *utx;
|
|
|
|
if (ufile == NULL)
|
|
ulog_setutxent();
|
|
if (ufile == NULL)
|
|
return (NULL);
|
|
|
|
if (ufileindex == UTXI_TTY) {
|
|
unsigned int slot;
|
|
|
|
slot = ulog_ttyslot(line->ut_line);
|
|
if (slot == 0)
|
|
return (NULL);
|
|
utx = ulog_read(slot, SEEK_SET, 1);
|
|
if (utx->ut_type == USER_PROCESS &&
|
|
strcmp(utx->ut_line, line->ut_line) == 0)
|
|
return (utx);
|
|
return (NULL);
|
|
} else {
|
|
for (;;) {
|
|
utx = ulog_read(0, SEEK_CUR, 1);
|
|
if (utx == NULL)
|
|
return (NULL);
|
|
if (utx->ut_type == USER_PROCESS &&
|
|
strcmp(utx->ut_line, line->ut_line) == 0)
|
|
return (utx);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ulog_getutxuser().
|
|
*
|
|
* Read entries from the file, until reaching an entry which matches the
|
|
* provided username. We can optimize the case for lastlog files,
|
|
* because they are indexed by user ID.
|
|
*/
|
|
|
|
struct ulog_utmpx *
|
|
ulog_getutxuser(const char *user)
|
|
{
|
|
struct ulog_utmpx *utx;
|
|
|
|
if (ufileindex == UTXI_USER) {
|
|
struct passwd *pw;
|
|
|
|
pw = getpwnam(user);
|
|
if (pw == NULL)
|
|
return (NULL);
|
|
utx = ulog_read(pw->pw_uid, SEEK_SET, 0);
|
|
if (utx != NULL)
|
|
strlcpy(utx->ut_user, user, sizeof utx->ut_user);
|
|
return (utx);
|
|
} else {
|
|
for (;;) {
|
|
utx = ulog_read(0, SEEK_CUR, 1);
|
|
if (utx == NULL)
|
|
return (NULL);
|
|
if (utx->ut_type == USER_PROCESS &&
|
|
strcmp(utx->ut_user, user) == 0)
|
|
return (utx);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ulog_setutxfile().
|
|
*
|
|
* Switch to a different record file. When no filename is provided, the
|
|
* system default is opened.
|
|
*/
|
|
|
|
int
|
|
ulog_setutxfile(int uidx, const char *file)
|
|
{
|
|
|
|
/* Supply default files. */
|
|
switch (uidx) {
|
|
case UTXI_TTY:
|
|
if (file == NULL)
|
|
file = _PATH_UTMP;
|
|
break;
|
|
case UTXI_TIME:
|
|
if (file == NULL)
|
|
file = _PATH_WTMP;
|
|
break;
|
|
case UTXI_USER:
|
|
if (file == NULL)
|
|
file = _PATH_LASTLOG;
|
|
break;
|
|
default:
|
|
return (-1);
|
|
}
|
|
|
|
if (ufile != NULL)
|
|
fclose(ufile);
|
|
ufile = fopen(file, "r");
|
|
ufileindex = uidx;
|
|
if (ufile == NULL)
|
|
return (-1);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ulog_endutxfile().
|
|
*
|
|
* Close any opened files.
|
|
*/
|
|
|
|
void
|
|
ulog_setutxent(void)
|
|
{
|
|
|
|
ulog_setutxfile(UTXI_TTY, NULL);
|
|
}
|