1/*
2 * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 2004 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License').  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24#include <sys/cdefs.h>
25#include <sys/types.h>
26#include <sys/param.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29
30#include <ctype.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37#ifdef UTMP_COMPAT
38#include <utmp.h>
39#endif /* UTMP_COMPAT */
40#include <utmpx.h>
41#include <utmpx-darwin.h>
42#include <utmpx_thread.h>
43#include <asl.h>
44#include <asl_private.h>
45#include <asl_store.h>
46#include <pwd.h>
47#include <stddef.h>
48
49#include <mach/mach.h>
50#include <mach/std_types.h>
51#include <mach/mig.h>
52#include <mach/mach_types.h>
53#include <servers/bootstrap.h>
54#include <pthread.h>
55
56#ifdef UTMP_COMPAT
57#include <ttyent.h>
58#endif /* UTMP_COMPAT */
59
60__private_extern__ const char __utx_magic__[UTMPX_MAGIC] = __UTX_MAGIC__;
61
62extern const char _utmpx_vers[];	/* in utmpx.c */
63
64static void msg2lastlogx(const aslmsg, struct lastlogx *);
65static void msg2utmpx(const aslmsg, struct utmpx *);
66static void utmpx2msg(const struct utmpx *, aslmsg);
67
68static size_t pw_size = 0;
69
70#define FACILITY		"Facility"
71#define WTMP_COUNT		32
72
73/* ASL timeout in microseconds */
74#define ASL_QUERY_TIMEOUT 4000000
75
76/* indirection causes argument to be substituted before stringification */
77#define STR(x)		__STRING(x)
78
79#ifdef UTMP_COMPAT
80static char *
81_pwnam_r(const char *user, struct passwd *pw)
82{
83	struct passwd *p;
84	char *buf;
85
86	if (pw_size <= 0) {
87		pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
88		if (pw_size <= 0)
89			return NULL;
90	}
91	if ((buf = malloc(pw_size)) == NULL)
92		return NULL;
93
94	getpwnam_r(user, pw, buf, pw_size, &p);
95	if (!p) {
96		free(buf);
97		return NULL;
98	}
99	return buf;
100}
101#endif
102
103static char *
104_pwuid_r(uid_t uid, struct passwd *pw)
105{
106	struct passwd *p;
107	char *buf;
108
109	if (pw_size <= 0) {
110		pw_size = sysconf(_SC_GETPW_R_SIZE_MAX);
111		if (pw_size <= 0)
112			return NULL;
113	}
114	if ((buf = malloc(pw_size)) == NULL)
115		return NULL;
116
117	getpwuid_r(uid, pw, buf, pw_size, &p);
118	if (!p) {
119		free(buf);
120		return NULL;
121	}
122	return buf;
123}
124
125struct lastlogx *
126getlastlogx(uid_t uid, struct lastlogx *lx)
127{
128	char *buf;
129	struct passwd pw;
130	struct lastlogx *l;
131
132	if ((buf = _pwuid_r(uid, &pw)) == NULL)
133		return NULL;
134
135	l = getlastlogxbyname(pw.pw_name, lx);
136	free(buf);
137	return l;
138}
139
140struct lastlogx *
141getlastlogxbyname(const char *user, struct lastlogx *lx)
142{
143	aslmsg m;
144	asl_msg_t *qm[1];
145	asl_search_result_t query, *res;
146	uint32_t status;
147	uint64_t cmax;
148	struct lastlogx *result = NULL;
149	asl_store_t *store;
150
151	if (!user || !*user) return NULL;
152
153	store = NULL;
154	status = asl_store_open_read(NULL, &store);
155	if (status != 0) return NULL;
156	if (store == NULL) return NULL;
157
158	/*
159	 * We search for the last LASTLOG_FACILITY entry that has the
160	 * ut_user entry matching the user's name.
161	 */
162	if ((m = asl_new(ASL_TYPE_QUERY)) == NULL)
163	{
164		asl_store_close(store);
165		return NULL;
166	}
167
168	asl_set_query(m, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
169	asl_set_query(m, "ut_user", user, ASL_QUERY_OP_EQUAL);
170	qm[0] = (asl_msg_t *)m;
171	query.count = 1;
172	query.msg = qm;
173
174	res = NULL;
175	cmax = 0;
176
177	asl_store_match_timeout(store, &query, &res, &cmax, -1, 1, -1, ASL_QUERY_TIMEOUT);
178	asl_store_close(store);
179	asl_free(m);
180
181	if (status != 0) return NULL;
182	if (res == NULL) return NULL;
183
184	m = aslresponse_next(res);
185	if (m == NULL)
186	{
187		aslresponse_free(res);
188		return NULL;
189	}
190
191	if (lx == NULL)
192	{
193		if ((lx = (struct lastlogx *)malloc(sizeof(*lx))) == NULL)
194		{
195			aslresponse_free(res);
196			return NULL;
197		}
198	}
199
200	msg2lastlogx(m, lx);
201	aslresponse_free(res);
202	result = lx;
203
204	return result;
205}
206
207#define IGET(e,p)	if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
208				u->p##_##e = strtol(cp, NULL, 10);
209#define LGET(e,p)	IGET(e,p)
210#define SGET(e,p)	if ((cp = asl_get(m, __STRING(ut_##e))) != NULL) \
211				strncpy(u->p##_##e, cp, sizeof(u->p##_##e))
212
213/* fill in a struct lastlogx from a aslmsg */
214static void
215msg2lastlogx(const aslmsg m, struct lastlogx *u)
216{
217	const char *cp;
218
219	bzero(u, sizeof(*u));
220	SGET(line, ll);
221	LGET(tv.tv_sec, ll);
222	IGET(tv.tv_usec, ll);
223	SGET(host, ll);
224}
225
226/* fill in a struct utmpx from a aslmsg */
227static void
228msg2utmpx(const aslmsg m, struct utmpx *u)
229{
230	const char *cp;
231
232	bzero(u, sizeof(*u));
233	SGET(user, ut);
234	SGET(id, ut);
235	SGET(line, ut);
236	IGET(pid, ut);
237	IGET(type, ut);
238	LGET(tv.tv_sec, ut);
239	IGET(tv.tv_usec, ut);
240	SGET(host, ut);
241}
242
243/* fill in a aslmsg from a struct utmpx */
244static void
245utmpx2msg(const struct utmpx *u, aslmsg m)
246{
247	char buf[_UTX_HOSTSIZE + 1];	/* the largest string in struct utmpx */
248	const char *cp;
249#define ISET(e)	{ snprintf(buf, sizeof(buf), "%d", u->e); \
250		asl_set(m, #e, buf); }
251#define LSET(e)	{ snprintf(buf, sizeof(buf), "%ld", u->e); \
252		asl_set(m, #e, buf); }
253#define SSET(e)	if (*(u->e)) { \
254			strncpy(buf, u->e, sizeof(u->e)); \
255			buf[sizeof(u->e)] = 0; \
256			asl_set(m, #e, buf); \
257		}
258
259	SSET(ut_user);
260	cp = (char *)u->ut_id + sizeof(u->ut_id);
261	while(--cp >= u->ut_id && isprint(*cp)) {}
262	if(cp < u->ut_id) {
263	    SSET(ut_id);
264	} else {
265	    snprintf(buf, sizeof(buf), "0x%02x 0x%02x 0x%02x 0x%02x",
266		(unsigned)u->ut_id[0], (unsigned)u->ut_id[1],
267		(unsigned)u->ut_id[2], (unsigned)u->ut_id[3]);
268	    asl_set(m, "ut_id", buf);
269	}
270	SSET(ut_line);
271	if (u->ut_pid > 0)
272		ISET(ut_pid);
273	ISET(ut_type);
274	LSET(ut_tv.tv_sec);
275	ISET(ut_tv.tv_usec);
276	SSET(ut_host);
277}
278
279static const char *utmpx_types[] = {
280	"EMPTY",		/* 0 */
281	"RUN_LVL",		/* 1 */
282	"BOOT_TIME",		/* 2 */
283	"OLD_TIME",		/* 3 */
284	"NEW_TIME",		/* 4 */
285	"INIT_PROCESS",		/* 5 */
286	"LOGIN_PROCESS",	/* 6 */
287	"USER_PROCESS",		/* 7 */
288	"DEAD_PROCESS",		/* 8 */
289	"ACCOUNTING",		/* 9 */
290	"SIGNATURE",		/* 10 */
291	"SHUTDOWN_TIME",	/* 11 */
292};
293
294/* send a struct utmpx record using asl */
295__private_extern__ void
296_utmpx_asl(const struct utmpx *u)
297{
298	aslclient asl = asl_open(NULL, NULL, ASL_OPT_NO_REMOTE); /* could be NULL, but still works */
299	aslmsg m;
300	char msg[64];
301
302	if (u->ut_type == EMPTY)
303		return;
304	if ((m = asl_new(ASL_TYPE_MSG)) == NULL) {
305		asl_close(asl);
306		return;
307	}
308	/*
309	 * If the ut_type is USER_PROCESS, we use the LASTLOG_FACILITY,
310	 * otherwise we use the UTMPX_FACILITY.  This makes it easy to
311	 * search for lastlog entries, but for wtmp, we have to search
312	 * for both facilities.
313	 */
314	if (u->ut_type == USER_PROCESS)
315		asl_set(m, FACILITY, LASTLOG_FACILITY);
316	else
317		asl_set(m, FACILITY, UTMPX_FACILITY);
318	asl_set(m, ASL_KEY_LEVEL, STR(ASL_LEVEL_NOTICE));
319	utmpx2msg(u, m);
320
321	/* Make a visible message for system.log */
322	switch (u->ut_type) {
323	case BOOT_TIME:
324	case OLD_TIME:
325	case NEW_TIME:
326	case SHUTDOWN_TIME:
327		sprintf(msg, "%s: %ld %d", utmpx_types[u->ut_type], u->ut_tv.tv_sec, u->ut_tv.tv_usec);
328		break;
329	case INIT_PROCESS:
330	case LOGIN_PROCESS:
331		sprintf(msg, "%s: %d", utmpx_types[u->ut_type], (int)u->ut_pid);
332		break;
333	case USER_PROCESS:
334	case DEAD_PROCESS:
335		sprintf(msg, "%s: %d %.*s", utmpx_types[u->ut_type], (int)u->ut_pid, (int)sizeof(u->ut_line), u->ut_line);
336		break;
337	default:
338		if (u->ut_type >= 0 && u->ut_type < (sizeof(utmpx_types) / sizeof(*utmpx_types)))
339			sprintf(msg, "%s", utmpx_types[u->ut_type]);
340		else
341			sprintf(msg, "ut_type=%d", (int)u->ut_type);
342		break;
343	}
344	asl_set(m, ASL_KEY_MSG, msg);
345	asl_send(asl, m);
346	asl_free(m);
347	if (asl)
348		asl_close(asl);
349}
350
351#define UT_USER	(1 << 0)
352#define UT_ID	(1 << 1)
353#define UT_LINE	(1 << 2)
354#define UT_PID	(1 << 3)
355#define UT_TV	(1 << 4)
356
357__private_extern__ const struct utmpx *
358_utmpx_working_copy(const struct utmpx *utx, struct utmpx *temp, int onlyid)
359{
360	int which;
361	static char idzero[_UTX_IDSIZE];
362
363	if ((utx->ut_type & (UTMPX_AUTOFILL_MASK | UTMPX_DEAD_IF_CORRESPONDING_MASK)) == 0)
364		return utx;
365	memcpy(temp, utx, sizeof(*temp));
366	temp->ut_type &= ~(UTMPX_AUTOFILL_MASK | UTMPX_DEAD_IF_CORRESPONDING_MASK);
367
368	if ((utx->ut_type & UTMPX_AUTOFILL_MASK) == 0)
369		return temp;
370
371	which = UT_TV; /* they all need time */
372	switch(temp->ut_type) {
373	case EMPTY:
374		return temp;
375	case USER_PROCESS:
376		which |= (UT_USER | UT_LINE | UT_PID);
377		/* Set UT_ID if ut_id isn't there */
378		if (memcmp(temp->ut_id, idzero, sizeof(temp->ut_id)) == 0)
379			which |= UT_ID;
380		break;
381	case INIT_PROCESS:
382		which |= UT_PID;
383		break;
384	case LOGIN_PROCESS:
385		which |= (UT_USER | UT_PID);
386		break;
387	case DEAD_PROCESS:
388		which |= UT_PID;
389		/* Set UT_ID if ut_id isn't there.  We will also need UT_LINE */
390		if (memcmp(temp->ut_id, idzero, sizeof(temp->ut_id)) == 0)
391			which |= (UT_ID | UT_LINE);
392		break;
393	}
394	/*
395	 * If onlyid is set: if ut_id isn't set but is needed, then set
396	 * which to (UT_LINE | UT_ID), otherwise zero
397	 */
398	if (onlyid)
399		which = (which & UT_ID) ? (UT_LINE | UT_ID) : 0;
400	if ((which & UT_LINE) && !*temp->ut_line) {
401		char buf[256];
402		char *cp;
403#if __DARWIN_UNIX03
404		int err;
405
406		err = ttyname_r(0, buf, sizeof(buf));
407		if (err)
408			err = ttyname_r(1, buf, sizeof(buf));
409		if (err)
410			err = ttyname_r(2, buf, sizeof(buf));
411		if (err)
412			return NULL;
413#else /* !__DARWIN_UNIX03 */
414		cp = ttyname_r(0, buf, sizeof(buf));
415		if (!cp)
416			cp = ttyname_r(1, buf, sizeof(buf));
417		if (!cp)
418			cp = ttyname_r(2, buf, sizeof(buf));
419		if (!cp)
420			return NULL;
421#endif /* __DARWIN_UNIX03 */
422		cp = strrchr(buf, '/');
423		if (cp)
424			cp++;
425		else
426			cp = buf;
427		strncpy(temp->ut_line, cp, sizeof(temp->ut_line));
428	}
429	/* UT_ID is set only if we already know we need to add it */
430	if ((which & UT_ID)) {
431		char *cp;
432		int i = sizeof(temp->ut_line);
433
434		for(cp = temp->ut_line; i > 0 && *cp; i--)
435			cp++;
436		i = cp - temp->ut_line;
437		if(i >= sizeof(temp->ut_id))
438			memcpy(temp->ut_id, cp - sizeof(temp->ut_id), sizeof(temp->ut_id));
439		else
440			memcpy(temp->ut_id, temp->ut_line, i);
441	}
442	if ((which & UT_PID) && !temp->ut_pid)
443		temp->ut_pid = getpid();
444	if ((which & UT_USER) && !*temp->ut_user) {
445		char *buf;
446		struct passwd pw;
447
448		if ((buf = _pwuid_r(getuid(), &pw)) == NULL)
449			return NULL;
450		strncpy(temp->ut_user, pw.pw_name, sizeof(temp->ut_user));
451		free(buf);
452	}
453	if ((which & UT_TV) && !temp->ut_tv.tv_sec && !temp->ut_tv.tv_usec)
454		gettimeofday(&temp->ut_tv, NULL);
455	return temp;
456}
457
458/*
459 * We can read from either asl or from a file, so we need to switch between
460 * the two.
461 */
462static void end_asl(void);
463static void end_file(void);
464static struct utmpx *get_asl(void);
465static struct utmpx *get_file(void);
466static void set_asl(int);
467static void set_file(int);
468
469enum {WTMP_ASL, WTMP_FILE};
470
471static struct {
472	int which;
473	void (*end)(void);
474	struct utmpx *(*get)(void);
475	void (*set)(int);
476} wtmp_func = {
477	WTMP_ASL,
478	end_asl,
479	get_asl,
480	set_asl
481};
482static struct {
483	uint64_t start;
484	int dir;
485	asl_search_result_t *res;
486	char *str;
487	uint32_t len;
488	char inited;
489	char done;
490} wtmp_asl = {-1, 1};
491static struct {
492	int fd;
493	int dir;
494	char *file;
495	off_t off;
496	size_t count;
497#ifdef __LP64__
498	struct utmpx32 *buf;
499	struct utmpx32 *next;
500#else /* __LP64__ */
501	struct utmpx *buf;
502	struct utmpx *next;
503#endif /* __LP64__ */
504	int left;
505} wtmp_file = {-1, -1};
506
507void
508endutxent_wtmp(void)
509{
510	wtmp_func.end();
511}
512
513struct utmpx *
514getutxent_wtmp(void)
515{
516	return wtmp_func.get();
517}
518
519void
520setutxent_wtmp(int dir)
521{
522	wtmp_func.set(dir);
523}
524
525/* use the given file, or if NULL, read from asl */
526int
527wtmpxname(const char *fname)
528{
529	size_t len;
530
531	if (fname == NULL) {
532		if (wtmp_func.which == WTMP_ASL) {
533			end_asl();
534			return 1;
535		}
536		end_file();
537		wtmp_func.which = WTMP_ASL;
538		wtmp_func.end = end_asl;
539		wtmp_func.get = get_asl;
540		wtmp_func.set = set_asl;
541		return 1;
542	}
543
544	len = strlen(fname);
545	if (len >= MAXPATHLEN)
546		return 0;
547
548	/* must end in x! */
549	if (fname[len - 1] != 'x')
550		return 0;
551
552	if (wtmp_func.which == WTMP_ASL)
553		end_asl();
554	else if (wtmp_file.fd >= 0) {
555		close(wtmp_file.fd);
556		wtmp_file.fd = -1;
557	}
558
559	if (wtmp_file.file)
560		free(wtmp_file.file);
561
562	wtmp_file.file = strdup(fname);
563	if (wtmp_file.file == NULL)
564		return 0;
565
566	wtmp_func.which = WTMP_FILE;
567	wtmp_func.end = end_file;
568	wtmp_func.get = get_file;
569	wtmp_func.set = set_file;
570	return 1;
571}
572
573static void
574end_asl(void)
575{
576	if (wtmp_asl.res != NULL)
577	{
578		aslresponse_free(wtmp_asl.res);
579		wtmp_asl.res = NULL;
580	}
581
582	wtmp_asl.inited = 0;
583	wtmp_asl.done = 0;
584}
585
586static void
587end_file(void)
588{
589	if (wtmp_file.fd >= 0) {
590		close(wtmp_file.fd);
591		wtmp_file.fd = -1;
592	}
593	if (wtmp_file.buf) {
594		free(wtmp_file.buf);
595		wtmp_file.buf = NULL;
596	}
597}
598
599static struct utmpx *
600get_asl(void)
601{
602	aslmsg m;
603	static struct utmpx utx;
604
605	if (wtmp_asl.inited == 0) set_asl(-1);
606	if (wtmp_asl.done != 0) return NULL;
607
608	m = aslresponse_next(wtmp_asl.res);
609	if (m == NULL)
610	{
611		aslresponse_free(wtmp_asl.res);
612		wtmp_asl.res = NULL;
613		wtmp_asl.done = 1;
614		return NULL;
615	}
616
617	msg2utmpx(m, &utx);
618	return &utx;
619}
620
621
622static struct utmpx *
623get_file(void)
624{
625	int n, r;
626	char *cp;
627#ifdef __LP64__
628	static struct utmpx ux;
629#endif /* __LP64__ */
630
631get_file_repeat:
632	if (wtmp_file.left > 0) {
633#ifdef __LP64__
634		struct utmpx32 *u = wtmp_file.next;
635#else /* __LP64__ */
636		struct utmpx *u = wtmp_file.next;
637#endif /* __LP64__ */
638		wtmp_file.next += wtmp_file.dir;
639		wtmp_file.left--;
640#ifdef __LP64__
641		_utmpx32_64(u, &ux);
642		return &ux;
643#else /* __LP64__ */
644		return u;
645#endif /* __LP64__ */
646	} else if (wtmp_file.fd < 0) {
647		set_file(-1); /* keep current read direction */
648		if (wtmp_file.fd < 0)
649			return NULL;
650		goto get_file_repeat;
651	}
652	if (wtmp_file.count <= 0)
653		return NULL;
654
655#ifdef __LP64__
656	n = WTMP_COUNT * sizeof(struct utmpx32);
657#else /* __LP64__ */
658	n = WTMP_COUNT * sizeof(struct utmpx);
659#endif /* __LP64__ */
660	if (wtmp_file.dir > 0)
661		wtmp_file.next = wtmp_file.buf;
662	else {
663		wtmp_file.next = wtmp_file.buf + WTMP_COUNT - 1;
664		wtmp_file.off -= n;
665		if (lseek(wtmp_file.fd, wtmp_file.off, SEEK_SET) < 0) {
666get_file_done:
667			wtmp_file.count = 0;
668			return NULL;
669		}
670	}
671
672	cp = (char *)wtmp_file.buf;
673	do {
674		if((r = read(wtmp_file.fd, cp, n)) <= 0) {
675			if (r < 0 && (errno == EINTR || errno == EAGAIN))
676				continue;
677			goto get_file_done;
678		}
679		cp += r;
680	} while((n -= r) > 0);
681
682	wtmp_file.left = WTMP_COUNT;
683	wtmp_file.count -= WTMP_COUNT;
684
685	goto get_file_repeat;
686}
687
688/*
689 * This sets the directions for both asl and reading from a file.  If forward
690 * is negative, skip.
691 */
692static void
693_set_dir(int forward)
694{
695	if (forward < 0) return;
696
697	if (forward == 0)
698	{
699		/* go backward */
700		wtmp_asl.dir = -1;
701		wtmp_asl.start = -1;
702		wtmp_file.dir = -1;
703	}
704	else
705	{
706		/* go forward */
707		wtmp_asl.dir = 1;
708		wtmp_asl.start = 0;
709		wtmp_file.dir = 1;
710	}
711}
712
713static void
714set_asl(int forward)
715{
716	aslmsg q0, q1;
717	asl_msg_t *m[2];
718	asl_search_result_t query;
719	uint64_t cmax;
720	asl_store_t *store;
721	uint32_t status;
722
723	_set_dir(forward);
724
725	wtmp_asl.inited = 0;
726	wtmp_asl.done = 0;
727
728	if (wtmp_asl.res != NULL)
729	{
730		aslresponse_free(wtmp_asl.res);
731		wtmp_asl.res = NULL;
732	}
733
734	store = NULL;
735	status = asl_store_open_read(NULL, &store);
736	if (status != 0) return;
737	if (store == NULL) return;
738
739	/*
740	 * Create a search query that matches either UTMPX_FACILITY
741	 * or LASTLOG_FACILITY.
742	 */
743	q0 = asl_new(ASL_TYPE_QUERY);
744	q1 = asl_new(ASL_TYPE_QUERY);
745
746	if ((q0 == NULL) || (q1 == NULL))
747	{
748		asl_store_close(store);
749		if (q0 != NULL) free(q0);
750		if (q1 != NULL) free(q1);
751		return;
752	}
753
754	asl_set_query(q0, FACILITY, UTMPX_FACILITY, ASL_QUERY_OP_EQUAL);
755	asl_set_query(q1, FACILITY, LASTLOG_FACILITY, ASL_QUERY_OP_EQUAL);
756
757	m[0] = (asl_msg_t *)q0;
758	m[1] = (asl_msg_t *)q1;
759	query.count = 2;
760	query.msg = m;
761
762	cmax = 0;
763
764	asl_store_match_timeout(store, &query, &(wtmp_asl.res), &cmax, wtmp_asl.start, 0, wtmp_asl.dir, ASL_QUERY_TIMEOUT);
765	asl_store_close(store);
766
767	asl_free(q1);
768	asl_free(q0);
769
770	if (wtmp_asl.res == NULL) return;
771
772	wtmp_asl.inited = 1;
773	wtmp_asl.done = 0;
774}
775
776static void
777set_file(int forward)
778{
779	struct stat s;
780	size_t c;
781	int n, r;
782	char *cp;
783
784	_set_dir(forward);
785#ifdef __LP64__
786	if (wtmp_file.buf == NULL &&
787	    (wtmp_file.buf = (struct utmpx32 *)malloc(WTMP_COUNT * sizeof(struct utmpx32))) == NULL)
788#else /* __LP64__ */
789	if (wtmp_file.buf == NULL &&
790	    (wtmp_file.buf = (struct utmpx *)malloc(WTMP_COUNT * sizeof(struct utmpx))) == NULL)
791#endif /* __LP64__ */
792		return;
793	if (wtmp_file.fd >= 0)
794		close(wtmp_file.fd);
795	if ((wtmp_file.fd = open(wtmp_file.file, O_RDONLY, 0)) < 0)
796		return;
797	if (fstat(wtmp_file.fd, &s) < 0)
798		goto set_file_error;
799	/*
800	 * We must have a file at least 2 sizeof(struct utmpx) in size,
801	 * with the first struct utmpx matching a signature record.
802	 */
803#ifdef __LP64__
804	if ((wtmp_file.count = s.st_size / sizeof(struct utmpx32)) <= 1)
805#else /* __LP64__ */
806	if ((wtmp_file.count = s.st_size / sizeof(struct utmpx)) <= 1)
807#endif /* __LP64__ */
808		goto set_file_error;
809#ifdef __LP64__
810	if (read(wtmp_file.fd, wtmp_file.buf, sizeof(struct utmpx32)) != sizeof(struct utmpx32))
811#else /* __LP64__ */
812	if (read(wtmp_file.fd, wtmp_file.buf, sizeof(struct utmpx)) != sizeof(struct utmpx))
813#endif /* __LP64__ */
814		goto set_file_error;
815	if (strcmp(wtmp_file.buf->ut_user, _utmpx_vers) != 0 ||
816	    wtmp_file.buf->ut_type != SIGNATURE)
817		goto set_file_error;
818	wtmp_file.count--;
819
820	/*
821	 * We will first read any records modulo WTMP_COUNT (or WTMP_COUNT),
822	 * either at the beginning or the end, so that all subsequent reads
823	 * must contain WTMP_COUNT records.
824	 */
825	c = WTMP_COUNT * ((wtmp_file.count - 1) / WTMP_COUNT);
826	wtmp_file.left = wtmp_file.count - c;
827	wtmp_file.count -= wtmp_file.left;
828
829	/* Seek to the end for reverse reading */
830	if (wtmp_file.dir < 0) {
831#ifdef __LP64__
832		wtmp_file.off = (c + 1) * sizeof(struct utmpx32);
833#else /* __LP64__ */
834		wtmp_file.off = (c + 1) * sizeof(struct utmpx);
835#endif /* __LP64__ */
836		if (lseek(wtmp_file.fd, wtmp_file.off, SEEK_SET) < 0)
837			goto set_file_error;
838	}
839#ifdef __LP64__
840	n = wtmp_file.left * sizeof(struct utmpx32);
841#else /* __LP64__ */
842	n = wtmp_file.left * sizeof(struct utmpx);
843#endif /* __LP64__ */
844	cp = (char *)wtmp_file.buf;
845	do {
846		if((r = read(wtmp_file.fd, cp, n)) <= 0) {
847			if (r < 0 && (errno == EINTR || errno == EAGAIN))
848				continue;
849			goto set_file_error;
850		}
851		cp += r;
852	} while((n -= r) > 0);
853
854	/* Point to either the beginning or end of the buffer */
855	if(wtmp_file.dir > 0)
856		wtmp_file.next = wtmp_file.buf;
857	else
858		wtmp_file.next = wtmp_file.buf + wtmp_file.left - 1;
859	return;
860
861set_file_error:
862	wtmp_file.left = 0;
863	close(wtmp_file.fd);
864	wtmp_file.fd = -1;
865	return;
866}
867
868#ifdef __LP64__
869/*
870 * these routines assume natural alignment so that struct utmpx32 has
871 * the same size and layout as the 32-bit struct utmpx
872 */
873__private_extern__ void
874_utmpx32_64(const struct utmpx32 *u32, struct utmpx *u)
875{
876	bzero(u, sizeof(*u));
877	memcpy(u, u32, offsetof(struct utmpx, ut_type) + sizeof(u->ut_type));
878	u->ut_tv.tv_sec = u32->ut_tv.tv_sec;
879	u->ut_tv.tv_usec = u32->ut_tv.tv_usec;
880	memcpy((char *)u + offsetof(struct utmpx, ut_host),
881	    (char *)u32 + offsetof(struct utmpx32, ut_host),
882	    sizeof(struct utmpx) - offsetof(struct utmpx, ut_host));
883}
884
885__private_extern__ void
886_utmpx64_32(const struct utmpx *u, struct utmpx32 *u32)
887{
888	bzero(u32, sizeof(*u32));
889	memcpy(u32, u, offsetof(struct utmpx32, ut_type) + sizeof(u32->ut_type));
890	u32->ut_tv.tv_sec = u->ut_tv.tv_sec;
891	u32->ut_tv.tv_usec = u->ut_tv.tv_usec;
892	memcpy((char *)u32 + offsetof(struct utmpx32, ut_host),
893	    (char *)u + offsetof(struct utmpx, ut_host),
894	    sizeof(struct utmpx32) - offsetof(struct utmpx32, ut_host));
895}
896#endif /* __LP64__ */
897
898#ifdef UTMP_COMPAT
899#ifdef __LP64__
900__private_extern__ void
901_getutmp32(const struct utmpx *ux, struct utmp32 *u)
902{
903
904	bzero(u, sizeof(*u));
905	(void)memcpy(u->ut_name, ux->ut_user, sizeof(u->ut_name));
906	(void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line));
907	(void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host));
908	u->ut_time = ux->ut_tv.tv_sec;
909}
910#endif /* __LP64__ */
911
912/*
913 * _utmp_compat converts a struct utmpx to a struct utmp, using the conventions
914 * described in utmp(5).  It then returns a value that specifies what
915 * combination of utmp, wtmp and lastlog to write.  UTMP_COMPAT_UTMP1 will
916 * write utmp only if a matching record with the same ut_line value is found;
917 * UTMP_COMPAT_UTMP0 replaces an existing record or writes a new one.
918 */
919__private_extern__ int
920#ifdef __LP64__
921_utmp_compat(const struct utmpx *ux, struct utmp32 *u)
922#else /* __LP64__ */
923_utmp_compat(const struct utmpx *ux, struct utmp *u)
924#endif /* __LP64__ */
925{
926#ifdef __LP64__
927	_getutmp32(ux, u);
928#else /* __LP64__ */
929	getutmp(ux, u);
930#endif /* __LP64__ */
931
932	switch (ux->ut_type) {
933	case BOOT_TIME:
934	case SHUTDOWN_TIME:
935		bzero(u->ut_line, sizeof(u->ut_line));
936		u->ut_line[0] = '~';
937		bzero(u->ut_name, sizeof(u->ut_name));
938		strcpy(u->ut_name, (ux->ut_type == BOOT_TIME ? "reboot" : "shutdown"));
939		return UTMP_COMPAT_WTMP;
940	case OLD_TIME:
941	case NEW_TIME:
942		bzero(u->ut_line, sizeof(u->ut_line));
943		u->ut_line[0] = (ux->ut_type == OLD_TIME ? '|' : '{');
944		bzero(u->ut_name, sizeof(u->ut_name));
945		strcpy(u->ut_name, "date");
946		return UTMP_COMPAT_WTMP;
947	case USER_PROCESS:
948		return UTMP_COMPAT_UTMP0 | UTMP_COMPAT_WTMP | UTMP_COMPAT_LASTLOG;
949	case DEAD_PROCESS:
950		bzero(u->ut_name, sizeof(u->ut_name));
951		bzero(u->ut_host, sizeof(u->ut_host));
952		return UTMP_COMPAT_UTMP1 | UTMP_COMPAT_WTMP;
953	}
954	return 0;	/* skip */;
955}
956
957/*
958 * Write _PATH_LASTLOG given a struct utmp record.  We use
959 * advisory record locking.
960 */
961__private_extern__ void
962#ifdef __LP64__
963_write_lastlog(const struct utmp32 *u, const struct utmpx *ux)
964#else /* __LP64__ */
965_write_lastlog(const struct utmp *u, const struct utmpx *ux)
966#endif /* __LP64__ */
967{
968	int fd;
969#ifdef __LP64__
970	struct lastlog32 l;
971#else /* __LP64__ */
972	struct lastlog l;
973#endif /* __LP64__ */
974	struct flock lock;
975	struct passwd pw;
976	// sizeof(ux->ut_user) > sizeof(u->ut_name)
977	char name[sizeof(ux->ut_user) + 1];
978	char *buf;
979	off_t off;
980	int retry = 10;
981
982	if (ux) {
983		if(!*ux->ut_user)
984			return;
985		strncpy(name, ux->ut_user, sizeof(ux->ut_user));
986		name[sizeof(ux->ut_user)] = 0;
987	} else {
988		if (!*u->ut_name)
989			return;
990		strncpy(name, u->ut_name, sizeof(u->ut_name));
991		name[sizeof(u->ut_name)] = 0;
992	}
993	if ((buf = _pwnam_r(name, &pw)) == NULL)
994		return;
995#ifdef __LP64__
996	off = (off_t)pw.pw_uid * sizeof(struct lastlog32);
997#else /* __LP64__ */
998	off = (off_t)pw.pw_uid * sizeof(struct lastlog);
999#endif /* __LP64__ */
1000	free(buf);
1001
1002	if ((fd = open(_PATH_LASTLOG, O_WRONLY, 0)) < 0)
1003		return;
1004	(void)lseek(fd, off, SEEK_SET);
1005	bzero(&lock, sizeof(lock));
1006	lock.l_type = F_WRLCK;
1007	lock.l_whence = SEEK_SET;
1008	lock.l_start = off;
1009#ifdef __LP64__
1010	lock.l_len = sizeof(struct lastlog32);
1011#else /* __LP64__ */
1012	lock.l_len = sizeof(struct lastlog);
1013#endif /* __LP64__ */
1014	/* try to lock, but give up after retry times, and write anyways */
1015	while(retry-- > 0) {
1016		if (fcntl(fd, F_SETLK, &lock) == 0)
1017			break;
1018		usleep(10000);
1019	}
1020	l.ll_time = u->ut_time;
1021	strncpy(l.ll_line, u->ut_line, sizeof(l.ll_line));
1022	strncpy(l.ll_host, u->ut_host, sizeof(l.ll_host));
1023	(void) write(fd, &l, sizeof(l));
1024	lock.l_type = F_UNLCK;
1025	(void) fcntl(fd, F_SETLK, &lock);
1026	(void) close(fd);
1027}
1028
1029/*
1030 * Write _PATH_UTMP, given a struct utmp, depending on the value of
1031 * "mustexist".
1032 */
1033__private_extern__ void
1034#ifdef __LP64__
1035_write_utmp(const struct utmp32 *u, int mustexist)
1036#else /* __LP64__ */
1037_write_utmp(const struct utmp *u, int mustexist)
1038#endif /* __LP64__ */
1039{
1040	int fd, slot;
1041	struct ttyent *ttyp;
1042#ifdef __LP64__
1043	struct utmp32 tmp;
1044#else /* __LP64__ */
1045	struct utmp tmp;
1046#endif /* __LP64__ */
1047	int found = 0;
1048	static struct {
1049		char line[sizeof(u->ut_line)];
1050		int slot;
1051	} cache;
1052
1053	if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0)
1054		return;
1055
1056	if (!strncmp(cache.line, u->ut_line, sizeof(u->ut_line))) {
1057		slot = cache.slot;
1058		found++;
1059	}
1060	/* do equivalent of ttyslot(), but using u->ut_line */
1061	if (!found) {
1062		setttyent();
1063		slot = 1;
1064		for(;;) {
1065			if ((ttyp = getttyent()) == NULL)
1066				break;
1067			if (!strncmp(ttyp->ty_name, u->ut_line, sizeof(u->ut_line))) {
1068				strncpy(cache.line, u->ut_line, sizeof(u->ut_line));
1069				cache.slot = slot;
1070				found++;
1071				break;
1072			}
1073			slot++;
1074		}
1075		endttyent();
1076	}
1077
1078	if (!found) {	/* no assigned slot */
1079#ifdef __LP64__
1080		(void)lseek(fd, (off_t)slot * sizeof(struct utmp32), SEEK_SET);
1081#else /* __LP64__ */
1082		(void)lseek(fd, (off_t)slot * sizeof(struct utmp), SEEK_SET);
1083#endif /* __LP64__ */
1084		for(;;) {
1085			if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
1086				break;
1087			if (!strncmp(tmp.ut_line, u->ut_line, sizeof(u->ut_line))) {
1088				strncpy(cache.line, u->ut_line, sizeof(u->ut_line));
1089				cache.slot = slot;
1090				found++;
1091				break;
1092			}
1093			slot++;
1094		}
1095	}
1096
1097	if (!found && mustexist) {
1098		(void)close(fd);
1099		return;
1100	}
1101#ifdef __LP64__
1102	(void)lseek(fd, (off_t)slot * sizeof(struct utmp32), SEEK_SET);
1103	(void)write(fd, u, sizeof(struct utmp32));
1104#else /* __LP64__ */
1105	(void)lseek(fd, (off_t)slot * sizeof(struct utmp), SEEK_SET);
1106	(void)write(fd, u, sizeof(struct utmp));
1107#endif /* __LP64__ */
1108	(void)close(fd);
1109}
1110
1111/*
1112 * Write all the necessary files (utmp, wtmp, lastlog), depending on the
1113 * given struct utmpx.
1114 */
1115__private_extern__ void
1116_write_utmp_compat(const struct utmpx *ux)
1117{
1118#ifdef __LP64__
1119	struct utmp32 u;
1120#else /* __LP64__ */
1121	struct utmp u;
1122#endif /* __LP64__ */
1123	int which;
1124
1125	which = _utmp_compat(ux, &u);
1126	if (which & UTMP_COMPAT_UTMP0)
1127		_write_utmp(&u, 0);
1128	else if (which & UTMP_COMPAT_UTMP1)
1129		_write_utmp(&u, 1);
1130	if (which & UTMP_COMPAT_WTMP)
1131		_write_wtmp(&u);
1132	if (which & UTMP_COMPAT_LASTLOG)
1133		_write_lastlog(&u, ux);
1134}
1135
1136/* Append a struct wtmp to _PATH_WTMP */
1137__private_extern__ void
1138#ifdef __LP64__
1139_write_wtmp(const struct utmp32 *u)
1140#else /* __LP64__ */
1141_write_wtmp(const struct utmp *u)
1142#endif /* __LP64__ */
1143{
1144	int fd;
1145	struct stat buf;
1146
1147	if ((fd = open(_PATH_WTMP, O_WRONLY | O_APPEND, 0)) < 0)
1148		return;
1149	if (fstat(fd, &buf) == 0) {
1150		if (write(fd, u, sizeof(*u)) != sizeof(*u))
1151			(void) ftruncate(fd, buf.st_size);
1152	}
1153	(void) close(fd);
1154}
1155#endif /* UTMP_COMPAT */
1156
1157/*
1158 * thread aware SPI
1159 */
1160utmpx_t
1161_openutx(const char *name)
1162{
1163	struct _utmpx *U;
1164
1165	if ((U = calloc(1, sizeof(struct _utmpx))) == NULL)
1166		return NULL;
1167	memcpy(U->magic, __utx_magic__, UTMPX_MAGIC);
1168	U->utmpx_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
1169	if (__utmpxname(U, name) == 0) {
1170		if (!U->utfile_system)
1171			free(U->utfile);
1172		free(U);
1173		errno = EINVAL;
1174		return NULL;
1175	}
1176	return (utmpx_t)U;
1177}
1178
1179int
1180_closeutx(utmpx_t u)
1181{
1182	struct _utmpx *U = (struct _utmpx *)u;
1183
1184	if (!U || memcmp(U->magic, __utx_magic__, UTMPX_MAGIC) != 0) {
1185		errno = EINVAL;
1186		return -1;
1187	}
1188	UTMPX_LOCK(U);
1189	__endutxent(U);
1190	if (!U->utfile_system)
1191		free(U->utfile);
1192	UTMPX_UNLOCK(U);
1193	free(U);
1194	return 0;
1195}
1196