#include "lint.h"
#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utmpx.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <utime.h>
#include <sys/wait.h>
#define IDLEN 4
#define SC_WILDC 0xff
#define MAXVAL 255
#ifdef ut_time
#undef ut_time
#endif
static void utmp_frec2api(const struct futmp *, struct utmp *);
static void utmp_api2frec(const struct utmp *, struct futmp *);
struct utmp *_compat_getutent(void);
struct utmp *_compat_getutid(const struct utmp *);
struct utmp *_compat_getutline(const struct utmp *);
struct utmp *_compat_pututline(const struct utmp *);
void _compat_setutent(void);
void _compat_endutent(void);
void _compat_updwtmp(const char *, struct utmp *);
struct utmp *_compat_makeut(struct utmp *);
struct utmp *_compat_modut(struct utmp *);
static void unlockut(void);
static int idcmp(const char *, const char *);
static int allocid(char *, unsigned char *);
static int lockut(void);
static int fd = -1;
extern char _compat_utmpfile[];
#ifdef ERRDEBUG
static long loc_utmp;
#endif
static struct futmp fubuf;
static struct utmp ubuf;
static void
utmp_frec2api(const struct futmp *src, struct utmp *dst)
{
if (src == NULL)
return;
(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
dst->ut_pid = src->ut_pid;
dst->ut_type = src->ut_type;
dst->ut_exit.e_termination = src->ut_exit.e_termination;
dst->ut_exit.e_exit = src->ut_exit.e_exit;
dst->ut_time = (time_t)src->ut_time;
}
static void
utmp_api2frec(const struct utmp *src, struct futmp *dst)
{
if (src == NULL)
return;
(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
dst->ut_pid = src->ut_pid;
dst->ut_type = src->ut_type;
dst->ut_exit.e_termination = src->ut_exit.e_termination;
dst->ut_exit.e_exit = src->ut_exit.e_exit;
dst->ut_time = (time32_t)src->ut_time;
}
static struct futmp *
getutent_frec(void)
{
if (fd < 0) {
if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0) {
if ((fd = open(_compat_utmpfile, O_RDONLY)) < 0)
return (NULL);
}
}
if (read(fd, &fubuf, sizeof (fubuf)) != sizeof (fubuf)) {
bzero(&fubuf, sizeof (fubuf));
return (NULL);
}
(void) lseek(fd, 0L, 1);
return (&fubuf);
}
struct utmp *
_compat_getutent(void)
{
struct futmp *futp;
futp = getutent_frec();
utmp_frec2api(&fubuf, &ubuf);
if (futp == NULL)
return (NULL);
return (&ubuf);
}
struct utmp *
_compat_getutid(const struct utmp *entry)
{
short type;
utmp_api2frec(&ubuf, &fubuf);
do {
if (fubuf.ut_type != EMPTY) {
switch (entry->ut_type) {
case EMPTY:
return (NULL);
case RUN_LVL:
case BOOT_TIME:
case DOWN_TIME:
case OLD_TIME:
case NEW_TIME:
if (entry->ut_type == fubuf.ut_type) {
utmp_frec2api(&fubuf, &ubuf);
return (&ubuf);
}
break;
case INIT_PROCESS:
case LOGIN_PROCESS:
case USER_PROCESS:
case DEAD_PROCESS:
if (((type = fubuf.ut_type) == INIT_PROCESS ||
type == LOGIN_PROCESS ||
type == USER_PROCESS ||
type == DEAD_PROCESS) &&
fubuf.ut_id[0] == entry->ut_id[0] &&
fubuf.ut_id[1] == entry->ut_id[1] &&
fubuf.ut_id[2] == entry->ut_id[2] &&
fubuf.ut_id[3] == entry->ut_id[3]) {
utmp_frec2api(&fubuf, &ubuf);
return (&ubuf);
}
break;
default:
return (NULL);
}
}
} while (getutent_frec() != NULL);
utmp_frec2api(&fubuf, &ubuf);
return (NULL);
}
struct utmp *
_compat_getutline(const struct utmp *entry)
{
utmp_api2frec(&ubuf, &fubuf);
do {
if (fubuf.ut_type != EMPTY &&
(fubuf.ut_type == LOGIN_PROCESS ||
fubuf.ut_type == USER_PROCESS) &&
strncmp(&entry->ut_line[0], &fubuf.ut_line[0],
sizeof (fubuf.ut_line)) == 0) {
utmp_frec2api(&fubuf, &ubuf);
return (&ubuf);
}
} while (getutent_frec() != NULL);
utmp_frec2api(&fubuf, &ubuf);
return (NULL);
}
struct utmp *
_compat_pututline(const struct utmp *entry)
{
int fc;
struct utmp *answer;
struct utmp tmpbuf;
struct futmp ftmpbuf;
tmpbuf = *entry;
utmp_api2frec(entry, &ftmpbuf);
(void) getutent_frec();
if (fd < 0) {
#ifdef ERRDEBUG
gdebug("pututline: Unable to create utmp file.\n");
#endif
return (NULL);
}
if ((fc = fcntl(fd, F_GETFL, NULL)) == -1 || (fc & O_RDWR) != O_RDWR)
return (NULL);
if (_compat_getutid(&tmpbuf) == NULL) {
#ifdef ERRDEBUG
gdebug("1st getutid() failed. fd: %d", fd);
#endif
_compat_setutent();
if (_compat_getutid(&tmpbuf) == NULL) {
#ifdef ERRDEBUG
loc_utmp = lseek(fd, 0L, 1);
gdebug("2nd getutid() failed. fd: %d loc_utmp: %ld\n",
fd, loc_utmp);
#endif
(void) fcntl(fd, F_SETFL, fc | O_APPEND);
} else
(void) lseek(fd, -(long)sizeof (struct futmp), 1);
} else
(void) lseek(fd, -(long)sizeof (struct futmp), 1);
if (write(fd, &ftmpbuf, sizeof (ftmpbuf)) != sizeof (ftmpbuf)) {
#ifdef ERRDEBUG
gdebug("pututline failed: write-%d\n", errno);
#endif
answer = NULL;
} else {
fubuf = ftmpbuf;
utmp_frec2api(&fubuf, &ubuf);
answer = &ubuf;
#ifdef ERRDEBUG
gdebug("id: %c%c loc: %ld\n", fubuf.ut_id[0],
fubuf.ut_id[1], fubuf.ut_id[2], fubuf.ut_id[3],
loc_utmp);
#endif
}
(void) fcntl(fd, F_SETFL, fc);
return (answer);
}
void
_compat_setutent(void)
{
if (fd != -1)
(void) lseek(fd, 0L, 0);
bzero(&ubuf, sizeof (ubuf));
bzero(&fubuf, sizeof (fubuf));
}
void
_compat_endutent(void)
{
if (fd != -1)
(void) close(fd);
fd = -1;
bzero(&ubuf, sizeof (ubuf));
bzero(&fubuf, sizeof (fubuf));
}
void
_compat_updwtmp(const char *file, struct utmp *ut)
{
struct futmp fut;
int fd;
fd = open(file, O_WRONLY | O_APPEND);
if (fd < 0) {
if ((fd = open(file, O_WRONLY|O_CREAT, 0644)) < 0)
return;
}
(void) lseek(fd, 0, 2);
utmp_api2frec(ut, &fut);
(void) write(fd, &fut, sizeof (fut));
(void) close(fd);
}
struct utmp *
_compat_makeut(struct utmp *utmp)
{
int i;
struct utmp *utp;
int wild;
unsigned char saveid[IDLEN];
wild = 0;
for (i = 0; i < IDLEN; i++)
if ((unsigned char)utmp->ut_id[i] == SC_WILDC) {
wild = 1;
break;
}
if (wild) {
if (lockut())
return (0);
_compat_setutent();
for (i = 0; i < MAXVAL; ++i)
if (isalnum(i))
break;
(void) memset(saveid, i, IDLEN);
while ((utp = _compat_getutent()) != 0) {
if (idcmp(utmp->ut_id, utp->ut_id))
continue;
if (utp->ut_type == DEAD_PROCESS)
break;
(void) memcpy(saveid, utp->ut_id, IDLEN);
}
if (utp) {
(void) memcpy(utmp->ut_id, utp->ut_id, IDLEN);
utp = _compat_pututline(utmp);
if (utp)
_compat_updwtmp(WTMP_FILE, utp);
_compat_endutent();
unlockut();
return (utp);
} else {
if (allocid(utmp->ut_id, saveid)) {
_compat_endutent();
unlockut();
return (NULL);
} else {
utp = _compat_pututline(utmp);
if (utp)
_compat_updwtmp(WTMP_FILE, utp);
_compat_endutent();
unlockut();
return (utp);
}
}
} else {
utp = _compat_pututline(utmp);
if (utp)
_compat_updwtmp(WTMP_FILE, utp);
_compat_endutent();
return (utp);
}
}
struct utmp *
_compat_modut(struct utmp *utp)
{
int i;
struct utmp utmp;
struct utmp *ucp = &utmp;
struct utmp *up;
struct futmp *fup;
for (i = 0; i < IDLEN; ++i)
if ((unsigned char)utp->ut_id[i] == SC_WILDC)
return (0);
utmp = *utp;
_compat_setutent();
while ((fup = getutent_frec()) != NULL) {
if (idcmp(ucp->ut_id, fup->ut_id))
continue;
break;
}
up = _compat_pututline(ucp);
if (up)
_compat_updwtmp(WTMP_FILE, up);
_compat_endutent();
return (up);
}
static int
idcmp(const char *s1, const char *s2)
{
int i;
for (i = 0; i < IDLEN; ++i)
if ((unsigned char)*s1 != SC_WILDC && (*s1++ != *s2++))
return (-1);
return (0);
}
static int
allocid(char *srcid, unsigned char *saveid)
{
int i;
int changed;
char copyid[IDLEN];
(void) memcpy(copyid, srcid, IDLEN);
changed = 0;
for (i = 0; i < IDLEN; ++i) {
if ((unsigned char) copyid[i] != SC_WILDC)
continue;
copyid[i] = saveid[i];
if (!changed && (saveid[i] < MAXVAL)) {
while (++saveid[i] < MAXVAL) {
if (isalnum(saveid[i])) {
copyid[i] = saveid[i];
changed = 1;
break;
}
}
if (!changed) {
saveid[i] = 0;
while (!isalnum(saveid[i]))
saveid[i]++;
copyid[i] = ++saveid[i];
}
}
}
if (changed) {
(void) memcpy(srcid, copyid, IDLEN);
return (0);
} else
return (-1);
}
static int
lockut(void)
{
if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0)
return (-1);
if (lockf(fd, F_LOCK, 0) < 0) {
(void) close(fd);
fd = -1;
return (-1);
}
return (0);
}
static void
unlockut(void)
{
(void) lockf(fd, F_ULOCK, 0);
(void) close(fd);
fd = -1;
}
#ifdef ERRDEBUG
#include <stdarg.h>
#include <stdio.h>
static void
gdebug(const char *fmt, ...)
{
FILE *fp;
int errnum;
va_list ap;
if ((fp = fopen("/etc/dbg.getut", "a+F")) == NULL)
return;
va_start(ap, fmt);
(void) vfprintf(fp, fmt, ap);
va_end(ap);
(void) fclose(fp);
}
#endif