#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
#include <utmpx.h>
#include <utmpx-darwin.h>
#include <asl.h>
#include <asl_private.h>
#include <pwd.h>
#include <stddef.h>
#include <mach/mach.h>
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#include <servers/bootstrap.h>
#include <pthread.h>
#include <asl_ipc.h>
#ifdef UTMP_COMPAT
#include <ttyent.h>
#endif
extern const char _utmpx_vers[];
extern char *asl_list_to_string(asl_search_result_t *, uint32_t *);
extern asl_search_result_t *asl_list_from_string(const char *);
static void msg2lastlogx(const aslmsg, struct lastlogx *);
static void msg2utmpx(const aslmsg, struct utmpx *);
static void utmpx2msg(const struct utmpx *, aslmsg);
static mach_port_t asl_server_port = MACH_PORT_NULL;
static int pw_size = 0;
#define ASL_SERVICE_NAME "com.apple.system.logger"
#define FACILITY "Facility"
#define WTMP_COUNT 32
#define ASL_QUERY_TIMEOUT 4000
#define STR(x) __STRING(x)
static char *
_pwnam_r(const char *user, struct passwd *pw)
{
struct passwd *p;
char *buf;
if (pw_size <= 0) {
pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (pw_size <= 0)
return NULL;
}
if ((buf = malloc(pw_size)) == NULL)
return NULL;
getpwnam_r(user, pw, buf, pw_size, &p);
if (!p) {
free(buf);
return NULL;
}
return buf;
}
static char *
_pwuid_r(uid_t uid, struct passwd *pw)
{
struct passwd *p;
char *buf;
if (pw_size <= 0) {
pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (pw_size <= 0)
return NULL;
}
if ((buf = malloc(pw_size)) == NULL)
return NULL;
getpwuid_r(uid, pw, buf, pw_size, &p);
if (!p) {
free(buf);
return NULL;
}
return buf;
}
struct lastlogx *
getlastlogx(uid_t uid, struct lastlogx *lx)
{
char *buf;
struct passwd pw;
struct lastlogx *l;
if ((buf = _pwuid_r(uid, &pw)) == NULL)
return NULL;
l = getlastlogxbyname(pw.pw_name, lx);
free(buf);
return l;
}
struct lastlogx *
getlastlogxbyname(const char *user, struct lastlogx *lx)
{
aslmsg q;
asl_msg_t *m[1];
asl_search_result_t s, *l;
char *qstr, *res;
uint32_t len, reslen, status;
uint64_t cmax;
security_token_t sec;
caddr_t vmstr;
struct lastlogx *result = NULL;
mach_port_t port;
if (!user || !*user)
return NULL;
if (bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &port) != KERN_SUCCESS)
return NULL;
if ((q = asl_new(ASL_TYPE_QUERY)) == NULL)
goto out;
asl_set_query(q, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
asl_set_query(q, "ut_user", user, ASL_QUERY_OP_EQUAL);
m[0] = q;
s.count = 1;
s.msg = m;
len = 0;
qstr = asl_list_to_string(&s, &len);
asl_free(q);
if (qstr == NULL)
goto out;
if (vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE) != KERN_SUCCESS) {
free(qstr);
goto out;
}
strcpy(vmstr, qstr);
free(qstr);
res = NULL;
reslen = 0;
cmax = 0;
sec.val[0] = -1;
sec.val[1] = -1;
status = 0;
_asl_server_query_timeout(port, vmstr, len, -1, 1, 1, ASL_QUERY_TIMEOUT, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec);
if (res == NULL)
goto out;
l = asl_list_from_string(res);
vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
q = aslresponse_next(l);
if (q == NULL) {
aslresponse_free(l);
goto out;
}
if (lx == NULL) {
if ((lx = (struct lastlogx *)malloc(sizeof(*lx))) == NULL) {
aslresponse_free(l);
goto out;
}
}
msg2lastlogx(q, lx);
aslresponse_free(l);
result = lx;
out:
mach_port_deallocate(mach_task_self(), port);
return result;
}
#define IGET(e,p) if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
u->p##_##e = strtol(cp, NULL, 10);
#define LGET(e,p) IGET(e,p)
#define SGET(e,p) if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
strncpy(u->p##_##e, cp, sizeof(u->p##_##e))
static void
msg2lastlogx(const aslmsg m, struct lastlogx *u)
{
const char *cp;
bzero(u, sizeof(*u));
SGET(line, ll);
LGET(tv.tv_sec, ll);
IGET(tv.tv_usec, ll);
SGET(host, ll);
}
static void
msg2utmpx(const aslmsg m, struct utmpx *u)
{
const char *cp;
bzero(u, sizeof(*u));
SGET(user, ut);
SGET(id, ut);
SGET(line, ut);
IGET(pid, ut);
IGET(type, ut);
LGET(tv.tv_sec, ut);
IGET(tv.tv_usec, ut);
SGET(host, ut);
}
static void
utmpx2msg(const struct utmpx *u, aslmsg m)
{
char buf[_UTX_HOSTSIZE + 1];
char *cp;
#define ISET(e) { snprintf(buf, sizeof(buf), "%d", u->e); \
asl_set(m, #e, buf); }
#define LSET(e) { snprintf(buf, sizeof(buf), "%ld", u->e); \
asl_set(m, #e, buf); }
#define SSET(e) if (*(u->e)) { \
strncpy(buf, u->e, sizeof(u->e)); \
buf[sizeof(u->e)] = 0; \
asl_set(m, #e, buf); \
}
SSET(ut_user);
cp = u->ut_id + sizeof(u->ut_id);
while(--cp >= u->ut_id && isprint(*cp)) {}
if(cp < u->ut_id) {
SSET(ut_id);
} else {
snprintf(buf, sizeof(buf), "0x%02x 0x%02x 0x%02x 0x%02x",
(unsigned)u->ut_id[0], (unsigned)u->ut_id[1],
(unsigned)u->ut_id[2], (unsigned)u->ut_id[3]);
asl_set(m, "ut_id", buf);
}
SSET(ut_line);
if (u->ut_pid > 0)
ISET(ut_pid);
ISET(ut_type);
LSET(ut_tv.tv_sec);
ISET(ut_tv.tv_usec);
SSET(ut_host);
}
static const char *utmpx_types[] = {
"EMPTY",
"RUN_LVL",
"BOOT_TIME",
"OLD_TIME",
"NEW_TIME",
"INIT_PROCESS",
"LOGIN_PROCESS",
"USER_PROCESS",
"DEAD_PROCESS",
"ACCOUNTING",
"SIGNATURE",
"SHUTDOWN_TIME",
};
__private_extern__ void
_utmpx_asl(const struct utmpx *u)
{
aslclient asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE);
aslmsg m;
char msg[64];
if (u->ut_type == EMPTY)
return;
if ((m = asl_new(ASL_TYPE_MSG)) == NULL) {
asl_close(asl);
return;
}
if (u->ut_type == USER_PROCESS)
asl_set(m, FACILITY, LASTLOG_FACILITY);
else
asl_set(m, FACILITY, UTMPX_FACILITY);
asl_set(m, ASL_KEY_LEVEL, STR(ASL_LEVEL_NOTICE));
utmpx2msg(u, m);
switch (u->ut_type) {
case BOOT_TIME:
case OLD_TIME:
case NEW_TIME:
case SHUTDOWN_TIME:
sprintf(msg, "%s: %ld %d", utmpx_types[u->ut_type], u->ut_tv.tv_sec, u->ut_tv.tv_usec);
break;
case INIT_PROCESS:
case LOGIN_PROCESS:
sprintf(msg, "%s: %d", utmpx_types[u->ut_type], (int)u->ut_pid);
break;
case USER_PROCESS:
case DEAD_PROCESS:
sprintf(msg, "%s: %d %.*s", utmpx_types[u->ut_type], (int)u->ut_pid, (int)sizeof(u->ut_line), u->ut_line);
break;
default:
if (u->ut_type >= 0 && u->ut_type < (sizeof(utmpx_types) / sizeof(*utmpx_types)))
sprintf(msg, "%s", utmpx_types[u->ut_type]);
else
sprintf(msg, "ut_type=%d", (int)u->ut_type);
break;
}
asl_set(m, ASL_KEY_MSG, msg);
asl_send(asl, m);
asl_free(m);
if (asl)
asl_close(asl);
}
#define UT_USER (1 << 0)
#define UT_ID (1 << 1)
#define UT_LINE (1 << 2)
#define UT_PID (1 << 3)
#define UT_TV (1 << 4)
__private_extern__ const struct utmpx *
_utmpx_working_copy(const struct utmpx *utx, struct utmpx *temp, int onlyid)
{
int which;
static char idzero[_UTX_IDSIZE];
if ((utx->ut_type & (UTMPX_AUTOFILL_MASK | UTMPX_DEAD_IF_CORRESPONDING_MASK)) == 0)
return utx;
memcpy(temp, utx, sizeof(*temp));
temp->ut_type &= ~(UTMPX_AUTOFILL_MASK | UTMPX_DEAD_IF_CORRESPONDING_MASK);
if ((utx->ut_type & UTMPX_AUTOFILL_MASK) == 0)
return temp;
which = UT_TV;
switch(temp->ut_type) {
case EMPTY:
return temp;
case USER_PROCESS:
which |= (UT_USER | UT_LINE | UT_PID);
if (memcmp(temp->ut_id, idzero, sizeof(temp->ut_id)) == 0)
which |= UT_ID;
break;
case INIT_PROCESS:
which |= UT_PID;
break;
case LOGIN_PROCESS:
which |= (UT_USER | UT_PID);
break;
case DEAD_PROCESS:
which |= UT_PID;
if (memcmp(temp->ut_id, idzero, sizeof(temp->ut_id)) == 0)
which |= (UT_ID | UT_LINE);
break;
}
if (onlyid)
which = (which & UT_ID) ? (UT_LINE | UT_ID) : 0;
if ((which & UT_LINE) && !*temp->ut_line) {
char buf[256];
char *cp;
#if __DARWIN_UNIX03
int err;
err = ttyname_r(0, buf, sizeof(buf));
if (err)
err = ttyname_r(1, buf, sizeof(buf));
if (err)
err = ttyname_r(2, buf, sizeof(buf));
if (err)
return NULL;
#else
cp = ttyname_r(0, buf, sizeof(buf));
if (!cp)
cp = ttyname_r(1, buf, sizeof(buf));
if (!cp)
cp = ttyname_r(2, buf, sizeof(buf));
if (!cp)
return NULL;
#endif
cp = strrchr(buf, '/');
if (cp)
cp++;
else
cp = buf;
strncpy(temp->ut_line, cp, sizeof(temp->ut_line));
}
if ((which & UT_ID)) {
char *cp;
int i = sizeof(temp->ut_line);
for(cp = temp->ut_line; i > 0 && *cp; i--)
cp++;
i = cp - temp->ut_line;
if(i >= sizeof(temp->ut_id))
memcpy(temp->ut_id, cp - sizeof(temp->ut_id), sizeof(temp->ut_id));
else
memcpy(temp->ut_id, temp->ut_line, i);
}
if ((which & UT_PID) && !temp->ut_pid)
temp->ut_pid = getpid();
if ((which & UT_USER) && !*temp->ut_user) {
char *buf;
struct passwd pw;
if ((buf = _pwuid_r(getuid(), &pw)) == NULL)
return NULL;
strncpy(temp->ut_user, pw.pw_name, sizeof(temp->ut_user));
free(buf);
}
if ((which & UT_TV) && !temp->ut_tv.tv_sec && !temp->ut_tv.tv_usec)
gettimeofday(&temp->ut_tv, NULL);
return temp;
}
static void end_asl(void);
static void end_file(void);
static struct utmpx *get_asl(void);
static struct utmpx *get_file(void);
static void set_asl(int);
static void set_file(int);
enum {WTMP_ASL, WTMP_FILE};
static struct {
int which;
void (*end)(void);
struct utmpx *(*get)(void);
void (*set)(int);
} wtmp_func = {
WTMP_ASL,
end_asl,
get_asl,
set_asl
};
static struct {
uint64_t start;
int dir;
asl_search_result_t *res;
char *str;
uint32_t len;
char inited;
char done;
} wtmp_asl = {-1, 1};
static struct {
int fd;
int dir;
char file[MAXPATHLEN];
off_t off;
size_t count;
#ifdef __LP64__
struct utmpx32 *buf;
struct utmpx32 *next;
#else
struct utmpx *buf;
struct utmpx *next;
#endif
int left;
} wtmp_file = {-1, -1};
void
endutxent_wtmp(void)
{
wtmp_func.end();
}
struct utmpx *
getutxent_wtmp(void)
{
return wtmp_func.get();
}
void
setutxent_wtmp(int dir)
{
wtmp_func.set(dir);
}
int
wtmpxname(const char *fname)
{
size_t len;
if (fname == NULL) {
if (wtmp_func.which == WTMP_ASL) {
end_asl();
return 1;
}
end_file();
wtmp_func.which = WTMP_ASL;
wtmp_func.end = end_asl;
wtmp_func.get = get_asl;
wtmp_func.set = set_asl;
return 1;
}
len = strlen(fname);
if (len >= sizeof(wtmp_file.file))
return 0;
if (fname[len - 1] != 'x')
return 0;
(void)strlcpy(wtmp_file.file, fname, sizeof(wtmp_file.file));
if (wtmp_func.which == WTMP_ASL)
end_asl();
else if (wtmp_file.fd >= 0) {
close(wtmp_file.fd);
wtmp_file.fd = -1;
}
wtmp_func.which = WTMP_FILE;
wtmp_func.end = end_file;
wtmp_func.get = get_file;
wtmp_func.set = set_file;
return 1;
}
static void
end_asl(void)
{
if (wtmp_asl.res) {
aslresponse_free(wtmp_asl.res);
wtmp_asl.res = NULL;
}
wtmp_asl.inited = 0;
wtmp_asl.done = 0;
if (asl_server_port != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), asl_server_port);
asl_server_port = MACH_PORT_NULL;
}
}
static void
end_file(void)
{
if (wtmp_file.fd >= 0) {
close(wtmp_file.fd);
wtmp_file.fd = -1;
}
if (wtmp_file.buf) {
free(wtmp_file.buf);
wtmp_file.buf = NULL;
}
}
static struct utmpx *
get_asl(void)
{
aslmsg q;
char *res;
uint32_t reslen, status;
security_token_t sec;
caddr_t vmstr;
static struct utmpx utx;
get_asl_repeat:
if (wtmp_asl.res) {
if ((q = aslresponse_next(wtmp_asl.res)) != NULL) {
msg2utmpx(q, &utx);
return &utx;
}
aslresponse_free(wtmp_asl.res);
wtmp_asl.res = NULL;
} else if (!wtmp_asl.inited) {
set_asl(-1);
if (!wtmp_asl.inited)
return NULL;
}
if (wtmp_asl.done)
return NULL;
if (asl_server_port == MACH_PORT_NULL) {
if (bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port) != KERN_SUCCESS) {
get_asl_done:
wtmp_asl.done = 1;
return NULL;
}
}
if (vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, wtmp_asl.len, TRUE) != KERN_SUCCESS)
goto get_asl_done;
strcpy(vmstr, wtmp_asl.str);
res = NULL;
reslen = 0;
sec.val[0] = -1;
sec.val[1] = -1;
status = 0;
_asl_server_query_timeout(asl_server_port, vmstr, wtmp_asl.len, wtmp_asl.start, WTMP_COUNT, wtmp_asl.dir, ASL_QUERY_TIMEOUT, (caddr_t *)&res, &reslen, &wtmp_asl.start, (int *)&status, &sec);
if (res == NULL)
goto get_asl_done;
wtmp_asl.res = asl_list_from_string(res);
vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
if(!wtmp_asl.res)
goto get_asl_done;
goto get_asl_repeat;
}
static struct utmpx *
get_file(void)
{
int n, r;
char *cp;
#ifdef __LP64__
static struct utmpx ux;
#endif
get_file_repeat:
if (wtmp_file.left > 0) {
#ifdef __LP64__
struct utmpx32 *u = wtmp_file.next;
#else
struct utmpx *u = wtmp_file.next;
#endif
wtmp_file.next += wtmp_file.dir;
wtmp_file.left--;
#ifdef __LP64__
_utmpx32_64(u, &ux);
return &ux;
#else
return u;
#endif
} else if (wtmp_file.fd < 0) {
set_file(-1);
if (wtmp_file.fd < 0)
return NULL;
goto get_file_repeat;
}
if (wtmp_file.count <= 0)
return NULL;
#ifdef __LP64__
n = WTMP_COUNT * sizeof(struct utmpx32);
#else
n = WTMP_COUNT * sizeof(struct utmpx);
#endif
if (wtmp_file.dir > 0)
wtmp_file.next = wtmp_file.buf;
else {
wtmp_file.next = wtmp_file.buf + WTMP_COUNT - 1;
wtmp_file.off -= n;
if (lseek(wtmp_file.fd, wtmp_file.off, SEEK_SET) < 0) {
get_file_done:
wtmp_file.count = 0;
return NULL;
}
}
cp = (char *)wtmp_file.buf;
do {
if((r = read(wtmp_file.fd, cp, n)) <= 0) {
if (r < 0 && (errno == EINTR || errno == EAGAIN))
continue;
goto get_file_done;
}
cp += r;
} while((n -= r) > 0);
wtmp_file.left = WTMP_COUNT;
wtmp_file.count -= WTMP_COUNT;
goto get_file_repeat;
}
static void
_set_dir(int forward)
{
if (forward < 0)
return;
if (forward) {
wtmp_asl.dir = 0;
wtmp_asl.start = 0;
wtmp_file.dir = 1;
} else {
wtmp_asl.dir = 1;
wtmp_asl.start = -1;
wtmp_file.dir = -1;
}
}
static void
set_asl(int forward)
{
_set_dir(forward);
if (!wtmp_asl.str) {
aslmsg q0, q1;
asl_msg_t *m[2];
asl_search_result_t s;
if ((q0 = asl_new(ASL_TYPE_QUERY)) == NULL)
return;
if ((q1 = asl_new(ASL_TYPE_QUERY)) == NULL) {
asl_free(q0);
return;
}
asl_set_query(q0, FACILITY, UTMPX_FACILITY, ASL_QUERY_OP_EQUAL);
asl_set_query(q1, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
m[0] = q0;
m[1] = q1;
s.count = 2;
s.msg = m;
wtmp_asl.len = 0;
wtmp_asl.str = asl_list_to_string(&s, &wtmp_asl.len);
asl_free(q1);
asl_free(q0);
if(!wtmp_asl.str)
return;
}
if (wtmp_asl.res) {
aslresponse_free(wtmp_asl.res);
wtmp_asl.res = NULL;
}
wtmp_asl.inited = 1;
wtmp_asl.done = 0;
}
static void
set_file(int forward)
{
struct stat s;
size_t c;
int n, r;
char *cp;
_set_dir(forward);
#ifdef __LP64__
if (wtmp_file.buf == NULL &&
(wtmp_file.buf = (struct utmpx32 *)malloc(WTMP_COUNT * sizeof(struct utmpx32))) == NULL)
#else
if (wtmp_file.buf == NULL &&
(wtmp_file.buf = (struct utmpx *)malloc(WTMP_COUNT * sizeof(struct utmpx))) == NULL)
#endif
return;
if (wtmp_file.fd >= 0)
close(wtmp_file.fd);
if ((wtmp_file.fd = open(wtmp_file.file, O_RDONLY, 0)) < 0)
return;
if (fstat(wtmp_file.fd, &s) < 0)
goto set_file_error;
#ifdef __LP64__
if ((wtmp_file.count = s.st_size / sizeof(struct utmpx32)) <= 1)
#else
if ((wtmp_file.count = s.st_size / sizeof(struct utmpx)) <= 1)
#endif
goto set_file_error;
#ifdef __LP64__
if (read(wtmp_file.fd, wtmp_file.buf, sizeof(struct utmpx32)) != sizeof(struct utmpx32))
#else
if (read(wtmp_file.fd, wtmp_file.buf, sizeof(struct utmpx)) != sizeof(struct utmpx))
#endif
goto set_file_error;
if (strcmp(wtmp_file.buf->ut_user, _utmpx_vers) != 0 ||
wtmp_file.buf->ut_type != SIGNATURE)
goto set_file_error;
wtmp_file.count--;
c = WTMP_COUNT * ((wtmp_file.count - 1) / WTMP_COUNT);
wtmp_file.left = wtmp_file.count - c;
wtmp_file.count -= wtmp_file.left;
if (wtmp_file.dir < 0) {
#ifdef __LP64__
wtmp_file.off = (c + 1) * sizeof(struct utmpx32);
#else
wtmp_file.off = (c + 1) * sizeof(struct utmpx);
#endif
if (lseek(wtmp_file.fd, wtmp_file.off, SEEK_SET) < 0)
goto set_file_error;
}
#ifdef __LP64__
n = wtmp_file.left * sizeof(struct utmpx32);
#else
n = wtmp_file.left * sizeof(struct utmpx);
#endif
cp = (char *)wtmp_file.buf;
do {
if((r = read(wtmp_file.fd, cp, n)) <= 0) {
if (r < 0 && (errno == EINTR || errno == EAGAIN))
continue;
goto set_file_error;
}
cp += r;
} while((n -= r) > 0);
if(wtmp_file.dir > 0)
wtmp_file.next = wtmp_file.buf;
else
wtmp_file.next = wtmp_file.buf + wtmp_file.left - 1;
return;
set_file_error:
wtmp_file.left = 0;
close(wtmp_file.fd);
wtmp_file.fd = -1;
return;
}
#ifdef __LP64__
__private_extern__ void
_utmpx32_64(const struct utmpx32 *u32, struct utmpx *u)
{
bzero(u, sizeof(*u));
memcpy(u, u32, offsetof(struct utmpx, ut_type) + sizeof(u->ut_type));
u->ut_tv.tv_sec = u32->ut_tv.tv_sec;
u->ut_tv.tv_usec = u32->ut_tv.tv_usec;
memcpy((char *)u + offsetof(struct utmpx, ut_host),
(char *)u32 + offsetof(struct utmpx32, ut_host),
sizeof(struct utmpx) - offsetof(struct utmpx, ut_host));
}
__private_extern__ void
_utmpx64_32(const struct utmpx *u, struct utmpx32 *u32)
{
bzero(u32, sizeof(*u32));
memcpy(u32, u, offsetof(struct utmpx32, ut_type) + sizeof(u32->ut_type));
u32->ut_tv.tv_sec = u->ut_tv.tv_sec;
u32->ut_tv.tv_usec = u->ut_tv.tv_usec;
memcpy((char *)u32 + offsetof(struct utmpx32, ut_host),
(char *)u + offsetof(struct utmpx, ut_host),
sizeof(struct utmpx32) - offsetof(struct utmpx32, ut_host));
}
#endif
#ifdef UTMP_COMPAT
#ifdef __LP64__
__private_extern__ void
_getutmp32(const struct utmpx *ux, struct utmp32 *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;
}
#endif
__private_extern__ int
#ifdef __LP64__
_utmp_compat(const struct utmpx *ux, struct utmp32 *u)
#else
_utmp_compat(const struct utmpx *ux, struct utmp *u)
#endif
{
#ifdef __LP64__
_getutmp32(ux, u);
#else
getutmp(ux, u);
#endif
switch (ux->ut_type) {
case BOOT_TIME:
case SHUTDOWN_TIME:
bzero(u->ut_line, sizeof(u->ut_line));
u->ut_line[0] = '~';
bzero(u->ut_name, sizeof(u->ut_name));
strcpy(u->ut_name, (ux->ut_type == BOOT_TIME ? "reboot" : "shutdown"));
return UTMP_COMPAT_WTMP;
case OLD_TIME:
case NEW_TIME:
bzero(u->ut_line, sizeof(u->ut_line));
u->ut_line[0] = (ux->ut_type == OLD_TIME ? '|' : '{');
bzero(u->ut_name, sizeof(u->ut_name));
strcpy(u->ut_name, "date");
return UTMP_COMPAT_WTMP;
case USER_PROCESS:
return UTMP_COMPAT_UTMP0 | UTMP_COMPAT_WTMP | UTMP_COMPAT_LASTLOG;
case DEAD_PROCESS:
bzero(u->ut_name, sizeof(u->ut_name));
bzero(u->ut_host, sizeof(u->ut_host));
return UTMP_COMPAT_UTMP1 | UTMP_COMPAT_WTMP;
}
return 0; ;
}
__private_extern__ void
#ifdef __LP64__
_write_lastlog(const struct utmp32 *u, const struct utmpx *ux)
#else
_write_lastlog(const struct utmp *u, const struct utmpx *ux)
#endif
{
int fd;
#ifdef __LP64__
struct lastlog32 l;
#else
struct lastlog l;
#endif
struct flock lock;
struct passwd pw;
char name[sizeof(ux->ut_user) + 1];
char *buf;
off_t off;
int retry = 10;
if (ux) {
if(!*ux->ut_user)
return;
strncpy(name, ux->ut_user, sizeof(ux->ut_user));
name[sizeof(ux->ut_user)] = 0;
} else {
if (!*u->ut_name)
return;
strncpy(name, u->ut_name, sizeof(u->ut_name));
name[sizeof(u->ut_name)] = 0;
}
if ((buf = _pwnam_r(name, &pw)) == NULL)
return;
#ifdef __LP64__
off = (off_t)pw.pw_uid * sizeof(struct lastlog32);
#else
off = (off_t)pw.pw_uid * sizeof(struct lastlog);
#endif
free(buf);
if ((fd = open(_PATH_LASTLOG, O_WRONLY, 0)) < 0)
return;
(void)lseek(fd, off, SEEK_SET);
bzero(&lock, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = off;
#ifdef __LP64__
lock.l_len = sizeof(struct lastlog32);
#else
lock.l_len = sizeof(struct lastlog);
#endif
while(retry-- > 0) {
if (fcntl(fd, F_SETLK, &lock) == 0)
break;
usleep(10000);
}
l.ll_time = u->ut_time;
strncpy(l.ll_line, u->ut_line, sizeof(l.ll_line));
strncpy(l.ll_host, u->ut_host, sizeof(l.ll_host));
(void) write(fd, &l, sizeof(l));
lock.l_type = F_UNLCK;
(void) fcntl(fd, F_SETLK, &lock);
(void) close(fd);
}
__private_extern__ void
#ifdef __LP64__
_write_utmp(const struct utmp32 *u, int mustexist)
#else
_write_utmp(const struct utmp *u, int mustexist)
#endif
{
int fd, slot;
struct ttyent *ttyp;
#ifdef __LP64__
struct utmp32 tmp;
#else
struct utmp tmp;
#endif
int found = 0;
static struct {
char line[sizeof(u->ut_line)];
int slot;
} cache;
if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0)
return;
if (!strncmp(cache.line, u->ut_line, sizeof(u->ut_line))) {
slot = cache.slot;
found++;
}
if (!found) {
setttyent();
slot = 1;
for(;;) {
if ((ttyp = getttyent()) == NULL)
break;
if (!strncmp(ttyp->ty_name, u->ut_line, sizeof(u->ut_line))) {
strncpy(cache.line, u->ut_line, sizeof(u->ut_line));
cache.slot = slot;
found++;
break;
}
slot++;
}
endttyent();
}
if (!found) {
#ifdef __LP64__
(void)lseek(fd, (off_t)slot * sizeof(struct utmp32), SEEK_SET);
#else
(void)lseek(fd, (off_t)slot * sizeof(struct utmp), SEEK_SET);
#endif
for(;;) {
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
break;
if (!strncmp(tmp.ut_line, u->ut_line, sizeof(u->ut_line))) {
strncpy(cache.line, u->ut_line, sizeof(u->ut_line));
cache.slot = slot;
found++;
break;
}
slot++;
}
}
if (!found && mustexist) {
(void)close(fd);
return;
}
#ifdef __LP64__
(void)lseek(fd, (off_t)slot * sizeof(struct utmp32), SEEK_SET);
(void)write(fd, u, sizeof(struct utmp32));
#else
(void)lseek(fd, (off_t)slot * sizeof(struct utmp), SEEK_SET);
(void)write(fd, u, sizeof(struct utmp));
#endif
(void)close(fd);
}
__private_extern__ void
_write_utmp_compat(const struct utmpx *ux)
{
#ifdef __LP64__
struct utmp32 u;
#else
struct utmp u;
#endif
int which;
which = _utmp_compat(ux, &u);
if (which & UTMP_COMPAT_UTMP0)
_write_utmp(&u, 0);
else if (which & UTMP_COMPAT_UTMP1)
_write_utmp(&u, 1);
if (which & UTMP_COMPAT_WTMP)
_write_wtmp(&u);
if (which & UTMP_COMPAT_LASTLOG)
_write_lastlog(&u, ux);
}
__private_extern__ void
#ifdef __LP64__
_write_wtmp(const struct utmp32 *u)
#else
_write_wtmp(const struct utmp *u)
#endif
{
int fd;
struct stat buf;
if ((fd = open(_PATH_WTMP, O_WRONLY | O_APPEND, 0)) < 0)
return;
if (fstat(fd, &buf) == 0) {
if (write(fd, u, sizeof(*u)) != sizeof(*u))
(void) ftruncate(fd, buf.st_size);
}
(void) close(fd);
}
#endif