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