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