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