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