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