/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#ifdef UTMP_COMPAT
#include <utmp.h>
+#endif /* UTMP_COMPAT */
#include <utmpx.h>
#include <utmpx-darwin.h>
+#include <utmpx_thread.h>
#include <asl.h>
-#include <asl_private.h>
#include <pwd.h>
#include <stddef.h>
#include <mach/mach_types.h>
#include <servers/bootstrap.h>
#include <pthread.h>
-#include <asl_ipc.h>
#ifdef UTMP_COMPAT
#include <ttyent.h>
#endif /* UTMP_COMPAT */
+__private_extern__ const char __utx_magic__[UTMPX_MAGIC] = __UTX_MAGIC__;
+
extern const char _utmpx_vers[]; /* in utmpx.c */
-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 void msg2lastlogx(asl_object_t, struct lastlogx *);
+static void msg2utmpx(asl_object_t, struct utmpx *);
+static void utmpx2msg(const struct utmpx *, asl_object_t);
-static mach_port_t asl_server_port = MACH_PORT_NULL;
-static int pw_size = 0;
+static size_t pw_size = 0;
-#define ASL_SERVICE_NAME "com.apple.system.logger"
#define FACILITY "Facility"
#define WTMP_COUNT 32
-/* ASL timeout in milliseconds */
-#define ASL_QUERY_TIMEOUT 4000
+/* ASL timeout in microseconds */
+#define ASL_QUERY_TIMEOUT 4000000
/* indirection causes argument to be substituted before stringification */
#define STR(x) __STRING(x)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#ifdef UTMP_COMPAT
static char *
_pwnam_r(const char *user, struct passwd *pw)
{
}
return buf;
}
+#endif
static char *
_pwuid_r(uid_t uid, struct passwd *pw)
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;
+ asl_object_t m, query, res;
+ size_t cmax;
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 (!user || !*user) return NULL;
/*
* We search for the last LASTLOG_FACILITY entry that has the
* ut_user entry matching the user's name.
*/
- 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;
-
- /* the server frees this memory */
- if (vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE) != KERN_SUCCESS) {
- free(qstr);
- goto out;
+ m = asl_new(ASL_TYPE_QUERY);
+ if (m == NULL)
+ {
+ return NULL;
+ }
+
+ asl_set_query(m, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
+ asl_set_query(m, "ut_user", user, ASL_QUERY_OP_EQUAL);
+
+ query = asl_new(ASL_TYPE_LIST);
+ if (query == NULL)
+ {
+ asl_release(m);
+ return NULL;
}
- strcpy(vmstr, qstr);
- free(qstr);
+ asl_append(query, m);
+ asl_release(m);
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;
+
+ res = asl_match(NULL, query, &cmax, -1, 1, ASL_QUERY_TIMEOUT, ASL_MATCH_DIRECTION_REVERSE);
+ asl_release(query);
+
+ if (res == NULL) return NULL;
+
+ m = asl_next(res);
+
+ if (m == NULL)
+ {
+ asl_release(res);
+ return NULL;
}
- if (lx == NULL) {
- if ((lx = (struct lastlogx *)malloc(sizeof(*lx))) == NULL) {
- aslresponse_free(l);
- goto out;
+ if (lx == NULL)
+ {
+ if ((lx = (struct lastlogx *)malloc(sizeof(*lx))) == NULL)
+ {
+ asl_release(res);
+ return NULL;
}
}
- msg2lastlogx(q, lx);
- aslresponse_free(l);
+
+ msg2lastlogx(m, lx);
+ asl_release(res);
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))
+#define IGET(e,p) do { if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
+ u->p##_##e = (int)strtol(cp, NULL, 10); } while (0)
+#define LGET(e,p) do { if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
+ u->p##_##e = strtol(cp, NULL, 10); } while (0)
+#define SGET(e,p) do { if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
+ strncpy(u->p##_##e, cp, sizeof(u->p##_##e)); } while (0)
-/* fill in a struct lastlogx from a aslmsg */
+/* fill in a struct lastlogx from an ASL message */
static void
-msg2lastlogx(const aslmsg m, struct lastlogx *u)
+msg2lastlogx(asl_object_t m, struct lastlogx *u)
{
const char *cp;
SGET(host, ll);
}
-/* fill in a struct utmpx from a aslmsg */
+/* fill in a struct utmpx from an ASL message */
static void
-msg2utmpx(const aslmsg m, struct utmpx *u)
+msg2utmpx(asl_object_t m, struct utmpx *u)
{
const char *cp;
SGET(host, ut);
}
-/* fill in a aslmsg from a struct utmpx */
+/* fill in an ASL message from a struct utmpx */
static void
-utmpx2msg(const struct utmpx *u, aslmsg m)
+utmpx2msg(const struct utmpx *u, asl_object_t m)
{
char buf[_UTX_HOSTSIZE + 1]; /* the largest string in struct utmpx */
- char *cp;
+ const 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); \
}
SSET(ut_user);
- cp = u->ut_id + sizeof(u->ut_id);
+ cp = (char *)u->ut_id + sizeof(u->ut_id);
while(--cp >= u->ut_id && isprint(*cp)) {}
if(cp < u->ut_id) {
SSET(ut_id);
"SHUTDOWN_TIME", /* 11 */
};
-/* send a struct utmpx record using asl */
+/* send a struct utmpx record using ASL */
__private_extern__ void
_utmpx_asl(const struct utmpx *u)
{
- aslclient asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE); /* could be NULL, but still works */
- aslmsg m;
+ asl_object_t asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE);
+ asl_object_t m;
char msg[64];
if (u->ut_type == EMPTY)
return;
if ((m = asl_new(ASL_TYPE_MSG)) == NULL) {
- asl_close(asl);
+ asl_release(asl);
return;
}
/*
}
asl_set(m, ASL_KEY_MSG, msg);
asl_send(asl, m);
- asl_free(m);
- if (asl)
- asl_close(asl);
+ asl_release(m);
+ asl_release(asl);
}
#define UT_USER (1 << 0)
/* UT_ID is set only if we already know we need to add it */
if ((which & UT_ID)) {
char *cp;
- int i = sizeof(temp->ut_line);
+ ssize_t i = sizeof(temp->ut_line);
for(cp = temp->ut_line; i > 0 && *cp; i--)
cp++;
get_asl,
set_asl
};
+
static struct {
uint64_t start;
int dir;
- asl_search_result_t *res;
+ asl_object_t res;
char *str;
uint32_t len;
char inited;
static struct {
int fd;
int dir;
- char file[MAXPATHLEN];
+ char *file;
off_t off;
size_t count;
#ifdef __LP64__
}
len = strlen(fname);
- if (len >= sizeof(wtmp_file.file))
+ if (len >= MAXPATHLEN)
return 0;
/* must end in x! */
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;
}
+
+ if (wtmp_file.file)
+ free(wtmp_file.file);
+
+ wtmp_file.file = strdup(fname);
+ if (wtmp_file.file == NULL)
+ return 0;
+
wtmp_func.which = WTMP_FILE;
wtmp_func.end = end_file;
wtmp_func.get = get_file;
static void
end_asl(void)
{
- if (wtmp_asl.res) {
- aslresponse_free(wtmp_asl.res);
+ if (wtmp_asl.res != NULL)
+ {
+ asl_release(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
static struct utmpx *
get_asl(void)
{
- aslmsg q;
- char *res;
- uint32_t reslen, status;
- security_token_t sec;
- caddr_t vmstr;
+ asl_object_t m;
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.inited == 0) set_asl(-1);
+ if (wtmp_asl.done != 0) return NULL;
- if (wtmp_asl.done)
+ m = asl_next(wtmp_asl.res);
+ if (m == NULL)
+ {
+ asl_release(wtmp_asl.res);
+ wtmp_asl.res = NULL;
+ wtmp_asl.done = 1;
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;
- }
}
- /* the server frees this memory */
- if (vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, wtmp_asl.len, TRUE) != KERN_SUCCESS)
- goto get_asl_done;
-
- /* the search string is defined in set_asl */
- 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;
+ msg2utmpx(m, &utx);
+ return &utx;
}
+
static struct utmpx *
get_file(void)
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;
+ if (forward < 0) return;
+
+ if (forward == 0)
+ {
+ /* go backward */
+ wtmp_asl.dir = -1;
wtmp_asl.start = -1;
wtmp_file.dir = -1;
}
+ else
+ {
+ /* go forward */
+ wtmp_asl.dir = 1;
+ wtmp_asl.start = 0;
+ wtmp_file.dir = 1;
+ }
}
static void
set_asl(int forward)
{
+ asl_object_t q0, q1, query;
+ size_t cmax;
+
_set_dir(forward);
- if (!wtmp_asl.str) {
- aslmsg q0, q1;
- asl_msg_t *m[2];
- asl_search_result_t s;
-
- /*
- * Create a search string that matches either UTMPX_FACILITY
- * or LASTLOG_FACILITY.
- */
- 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.inited = 0;
+ wtmp_asl.done = 0;
+
+ if (wtmp_asl.res != NULL)
+ {
+ asl_release(wtmp_asl.res);
wtmp_asl.res = NULL;
}
+
+ /*
+ * Create a search query that matches either UTMPX_FACILITY
+ * or LASTLOG_FACILITY.
+ */
+ q0 = asl_new(ASL_TYPE_QUERY);
+ if (q0 == NULL) return;
+
+ asl_set_query(q0, FACILITY, UTMPX_FACILITY, ASL_QUERY_OP_EQUAL);
+
+ q1 = asl_new(ASL_TYPE_QUERY);
+ if (q1 == NULL)
+ {
+ asl_release(q0);
+ return;
+ }
+
+ asl_set_query(q1, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
+
+ query = asl_new(ASL_TYPE_LIST);
+ if (query == NULL)
+ {
+ asl_release(q0);
+ asl_release(q1);
+ return;
+ }
+
+ asl_append(query, q0);
+ asl_append(query, q1);
+
+ asl_release(q0);
+ asl_release(q1);
+
+ cmax = 0;
+
+ wtmp_asl.res = asl_match(NULL, query, &cmax, wtmp_asl.start, 0, ASL_QUERY_TIMEOUT, wtmp_asl.dir);
+ asl_release(query);
+
+ if (wtmp_asl.res == NULL) return;
+
wtmp_asl.inited = 1;
wtmp_asl.done = 0;
}
(void) close(fd);
}
#endif /* UTMP_COMPAT */
+
+/*
+ * thread aware SPI
+ */
+utmpx_t
+_openutx(const char *name)
+{
+ struct _utmpx *U;
+
+ if ((U = calloc(1, sizeof(struct _utmpx))) == NULL)
+ return NULL;
+ memcpy(U->magic, __utx_magic__, UTMPX_MAGIC);
+ U->utmpx_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
+ if (__utmpxname(U, name) == 0) {
+ if (!U->utfile_system)
+ free(U->utfile);
+ free(U);
+ errno = EINVAL;
+ return NULL;
+ }
+ return (utmpx_t)U;
+}
+
+int
+_closeutx(utmpx_t u)
+{
+ struct _utmpx *U = (struct _utmpx *)u;
+
+ if (!U || memcmp(U->magic, __utx_magic__, UTMPX_MAGIC) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ UTMPX_LOCK(U);
+ __endutxent(U);
+ if (!U->utfile_system)
+ free(U->utfile);
+ UTMPX_UNLOCK(U);
+ free(U);
+ return 0;
+}
+
+#pragma clang diagnostic pop