1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * The Solaris package installer in-memory database server. 29 * 30 * We'll keep the contents file as before; but we cache it 31 * and we don't write it as often. Instead, we log all 32 * modifications to the log file. 33 * Using the contents file and the logfile, the pkgserv can 34 * rebuild the up-to-date contents file. 35 * The logfile is constructed so that rebuilding the 36 * contents file with the logfile is idempotent. 37 * 38 * The libpkg will start the daemon. 39 * 40 * The pkgserv will daemonize itself; the parent process 41 * waits until the child process has initialized and will 42 * start the door server. 43 * If any error occurs during start-up, the error messages 44 * are printed to stderr and the daemon will exit. 45 * After start-up, any further errors are logged to syslog. 46 * The parent pkgserv will exit with: 47 * 0 - We've started 48 * 1 - We couldn't start (locked) 49 * 2 - Other problems (error on stderr) 50 * 99 - Nothing reported; the caller must report. 51 * 52 * The daemon will timeout, by default. It will write the 53 * contents file after a first timeout; and after a further 54 * timeout, the daemon will exit. 55 * 56 * The daemon will only timeout if the current "client" has exited; 57 * to this end, we always look at the pid of the last caller. 58 * If the last client is no longer around, we record the new client. 59 * In the typical case of running installf/removef from a post/preinstall 60 * script, we continue to follow the pkginstall/pkgremove client's pid. 61 * 62 * In the particular case of install, we make sure the daemon 63 * sticks around. (Install == install, (live)upgrade, zone install) 64 */ 65 66 #ifdef lint 67 #undef _FILE_OFFSET_BITS 68 #endif 69 70 #include <door.h> 71 #include <errno.h> 72 #include <fcntl.h> 73 #include <limits.h> 74 #include <pthread.h> 75 #include <signal.h> 76 #include <stddef.h> 77 #include <stdio.h> 78 #include <stdlib.h> 79 #include <strings.h> 80 #include <synch.h> 81 #include <sys/avl.h> 82 #include <sys/stat.h> 83 #include <sys/statvfs.h> 84 #include <sys/mman.h> 85 #include <sys/time.h> 86 #include <sys/wait.h> 87 #include <syslog.h> 88 #include <limits.h> 89 #include <thread.h> 90 #include <ucred.h> 91 #include <umem.h> 92 #include <unistd.h> 93 #include <libintl.h> 94 #include <locale.h> 95 96 #include <pkglib.h> 97 98 #define SADM_DIR "/var/sadm/install" 99 100 #define LOCK ".pkg.lock" 101 #define CLIENTLOCK ".pkg.lock.client" 102 #define CONTENTS "contents" 103 #define TCONTENTS "t.contents" 104 #define BADCONTENTS "contents.badXXXXXX" 105 106 #define LLNANOSEC ((int64_t)NANOSEC) 107 108 #define DUMPTIMEOUT 60 109 #define EXITTIMEOUT 300 110 111 /* 112 * Contents file storage format. At install time, the amount of memory 113 * might be limited, so we make sure that we use as little memory 114 * as possible. The package tools modify the entries; so we install the 115 * single lines. We also remember the length of the path; this is needed 116 * for avlcmp and we return it to the tools. This saves time. 117 * 118 * All strings are allocated using umem_alloc. 119 */ 120 typedef struct pkgentry { 121 char *line; /* The contents line for the file */ 122 avl_node_t avl; /* The avl header */ 123 int pkgoff; /* Where the packages live; start with SP */ 124 int pathlen; /* The length of the pathname */ 125 int len; /* Length of the line (incl NUL) */ 126 } pkgentry_t; 127 128 static char IS_ST0[256]; 129 static char IS_ST0Q[256]; 130 131 static void pkg_door_srv(void *, char *, size_t, door_desc_t *, uint_t); 132 static char *file_find(pkgfilter_t *, int *); 133 static void parse_contents(void); 134 static int parse_log(void); 135 static void pkgdump(void); 136 static int logflush(void); 137 static int avlcmp(const void *, const void *); 138 static void freeentry(pkgentry_t *); 139 static void swapentry(pkgentry_t *, pkgentry_t *); 140 static int establish_lock(char *); 141 static int no_memory_abort(void); 142 static int pkgfilter(pkgfilter_t *, door_desc_t *); 143 static int pkgaddlines(pkgfilter_t *); 144 static void finish(void); 145 static void signal_handler(int); 146 static void my_cond_reltimedwait(hrtime_t, int); 147 static hrtime_t time_since_(hrtime_t); 148 149 /* 150 * Server actions 151 * - set mode (contents file, log file) 152 * - roll log 153 * - remove package 154 * - merge package entries 155 */ 156 157 static FILE *log; 158 static char *door = PKGDOOR; 159 160 static avl_tree_t listp, *list = &listp; 161 162 /* Keep the "the last command modified the contents file ... */ 163 static char *ccmnt[2]; 164 static int cind = 0; 165 166 static mutex_t mtx = DEFAULTMUTEX; 167 static cond_t cv = DEFAULTCV; 168 169 static int flushbeforemark = 1; 170 static int logerrcnt = 0; 171 static int loglines = 0; 172 static int suppressed = 0; 173 static int logcount; 174 static int ndumps; 175 static int ncalls; 176 static int changes; 177 static hrtime_t lastchange; 178 static hrtime_t lastcall; 179 static volatile int want_to_quit; 180 static boolean_t read_only = B_FALSE; 181 static boolean_t permanent = B_FALSE; 182 static boolean_t one_shot = B_FALSE; 183 static int write_locked; 184 static pid_t client_pid; 185 static int verbose = 1; 186 static hrtime_t dumptimeout = DUMPTIMEOUT; 187 static boolean_t sync_needed = B_FALSE; 188 189 static uid_t myuid; 190 191 static char marker[] = "###Marker\n"; 192 193 static umem_cache_t *ecache; 194 195 static char pkgdir[PATH_MAX]; 196 197 static void 198 server_main(int argc, char **argv) 199 { 200 int did; 201 int c; 202 struct statvfs vfsbuf; 203 int imexit = 0; 204 pid_t parent; 205 char *root = NULL; 206 char *sadmdir = NULL; 207 hrtime_t delta; 208 int dir = 0; 209 int dfd; 210 211 (void) set_prog_name("pkgserv"); 212 213 openlog("pkgserv", LOG_PID | LOG_ODELAY, LOG_DAEMON); 214 215 while ((c = getopt(argc, argv, "d:eoN:pP:R:r:")) != EOF) { 216 switch (c) { 217 case 'e': 218 imexit = 1; 219 break; 220 case 'd': 221 sadmdir = optarg; 222 if (*sadmdir != '/' || strlen(sadmdir) >= PATH_MAX || 223 access(sadmdir, X_OK) != 0) 224 exit(99); 225 break; 226 case 'N': 227 (void) set_prog_name(optarg); 228 break; 229 case 'o': 230 one_shot = B_TRUE; 231 verbose = 0; 232 break; 233 case 'p': 234 /* 235 * We are updating possibly many zones; so we're not 236 * dumping based on a short timeout and we will not 237 * exit. 238 */ 239 permanent = B_TRUE; 240 dumptimeout = 3600; 241 break; 242 case 'P': 243 client_pid = atoi(optarg); 244 break; 245 case 'R': 246 root = optarg; 247 if (*root != '/' || strlen(root) >= PATH_MAX || 248 access(root, X_OK) != 0) 249 exit(99); 250 break; 251 case 'r': 252 read_only = B_TRUE; 253 one_shot = B_TRUE; 254 verbose = 0; 255 door = optarg; 256 break; 257 default: 258 exit(99); 259 } 260 } 261 262 if (one_shot && permanent) { 263 progerr(gettext("Incorrect Usage")); 264 exit(99); 265 } 266 267 umem_nofail_callback(no_memory_abort); 268 269 if (root != NULL && strcmp(root, "/") != 0) { 270 if (snprintf(pkgdir, PATH_MAX, "%s%s", root, 271 sadmdir == NULL ? SADM_DIR : sadmdir) >= PATH_MAX) { 272 exit(99); 273 } 274 } else { 275 if (sadmdir == NULL) 276 (void) strcpy(pkgdir, SADM_DIR); 277 else 278 (void) strcpy(pkgdir, sadmdir); 279 } 280 281 if (chdir(pkgdir) != 0) { 282 progerr(gettext("can't chdir to %s"), pkgdir); 283 exit(2); 284 } 285 286 closefrom(3); 287 288 if (!read_only && establish_lock(LOCK) < 0) { 289 progerr(gettext( 290 "couldn't lock in %s (server running?): %s"), 291 pkgdir, strerror(errno)); 292 exit(1); 293 } 294 295 did = door_create(pkg_door_srv, 0, DOOR_REFUSE_DESC); 296 if (did == -1) { 297 progerr("door_create: %s", strerror(errno)); 298 exit(2); 299 } 300 301 (void) fdetach(door); 302 303 if ((dfd = creat(door, 0644)) < 0 || close(dfd) < 0) { 304 progerr("door_create: %s", strerror(errno)); 305 exit(2); 306 } 307 308 (void) mutex_lock(&mtx); 309 310 myuid = geteuid(); 311 312 (void) sigset(SIGHUP, signal_handler); 313 (void) sigset(SIGTERM, signal_handler); 314 (void) sigset(SIGINT, signal_handler); 315 (void) sigset(SIGQUIT, signal_handler); 316 317 (void) signal(SIGPIPE, SIG_IGN); 318 319 (void) atexit(finish); 320 321 if (fattach(did, door) != 0) { 322 progerr(gettext("attach door: %s"), strerror(errno)); 323 exit(2); 324 } 325 (void) close(did); 326 327 ecache = umem_cache_create("entry", sizeof (pkgentry_t), 328 sizeof (char *), NULL, NULL, NULL, NULL, NULL, 0); 329 330 avl_create(list, avlcmp, sizeof (pkgentry_t), 331 offsetof(pkgentry_t, avl)); 332 333 IS_ST0['\0'] = 1; 334 IS_ST0[' '] = 1; 335 IS_ST0['\t'] = 1; 336 337 IS_ST0Q['\0'] = 1; 338 IS_ST0Q[' '] = 1; 339 IS_ST0Q['\t'] = 1; 340 IS_ST0Q['='] = 1; 341 342 parse_contents(); 343 if (parse_log() > 0) 344 pkgdump(); 345 346 if (imexit) 347 exit(0); 348 349 if (statvfs(".", &vfsbuf) != 0) { 350 progerr(gettext("statvfs: %s"), strerror(errno)); 351 exit(2); 352 } 353 354 if (strcmp(vfsbuf.f_basetype, "zfs") == 0) 355 flushbeforemark = 0; 356 357 /* We've started, tell the parent */ 358 parent = getppid(); 359 if (parent != 1) 360 (void) kill(parent, SIGUSR1); 361 362 if (!one_shot) { 363 int fd; 364 (void) setsid(); 365 fd = open("/dev/null", O_RDWR, 0); 366 if (fd >= 0) { 367 (void) dup2(fd, STDIN_FILENO); 368 (void) dup2(fd, STDOUT_FILENO); 369 (void) dup2(fd, STDERR_FILENO); 370 if (fd > 2) 371 (void) close(fd); 372 } 373 } 374 375 lastcall = lastchange = gethrtime(); 376 377 /* 378 * Start the main thread, here is where we unlock the mutex. 379 */ 380 for (;;) { 381 if (want_to_quit) { 382 pkgdump(); 383 exit(0); 384 } 385 /* Wait forever when root or when there's a running filter */ 386 if (write_locked || 387 (!one_shot && permanent && dir == changes)) { 388 (void) cond_wait(&cv, &mtx); 389 continue; 390 } 391 delta = time_since_(lastchange); 392 /* Wait until DUMPTIMEOUT after last change before we pkgdump */ 393 if (delta < dumptimeout * LLNANOSEC) { 394 my_cond_reltimedwait(delta, dumptimeout); 395 continue; 396 } 397 /* Client still around? Just wait then. */ 398 if (client_pid > 1 && kill(client_pid, 0) == 0) { 399 lastchange = lastcall = gethrtime(); 400 continue; 401 } 402 /* Wait for another EXITTIMEOUT seconds before we exit */ 403 if ((one_shot || !permanent) && dir == changes) { 404 delta = time_since_(lastcall); 405 if (delta < EXITTIMEOUT * LLNANOSEC) { 406 my_cond_reltimedwait(delta, EXITTIMEOUT); 407 continue; 408 } 409 exit(0); 410 } 411 pkgdump(); 412 dir = changes; 413 } 414 415 /*NOTREACHED*/ 416 } 417 418 /*ARGSUSED*/ 419 static void 420 nothing(int sig) 421 { 422 } 423 424 int 425 main(int argc, char **argv) 426 { 427 int sig; 428 sigset_t sset; 429 int stat; 430 431 /* 432 * We're starting the daemon; this process exits when the door 433 * server is established or when it fails to establish. 434 * We wait until the child process sends a SIGUSR1 or when it 435 * exits. 436 * We keep around who started us and as long as it lives, we don't 437 * exit. 438 */ 439 440 (void) setlocale(LC_ALL, ""); 441 (void) textdomain(TEXT_DOMAIN); 442 443 client_pid = getppid(); 444 445 (void) sigemptyset(&sset); 446 (void) sigaddset(&sset, SIGUSR1); 447 (void) sigaddset(&sset, SIGCLD); 448 449 /* We need to catch the SIGCLD before we can sigwait for it. */ 450 (void) sigset(SIGCLD, nothing); 451 /* We need to make sure that SIGUSR1 is not ignored. */ 452 (void) sigset(SIGUSR1, SIG_DFL); 453 (void) sigprocmask(SIG_BLOCK, &sset, NULL); 454 455 /* We install the contents file readable. */ 456 (void) umask(022); 457 458 switch (fork()) { 459 case -1: 460 exit(99); 461 /*NOTREACHED*/ 462 case 0: 463 server_main(argc, argv); 464 /*NOTREACHED*/ 465 default: 466 /* In the parent */ 467 break; 468 } 469 470 for (;;) { 471 sig = sigwait(&sset); 472 473 switch (sig) { 474 case SIGCLD: 475 if (wait(&stat) > 0) { 476 if (WIFEXITED(stat)) 477 _exit(WEXITSTATUS(stat)); 478 else if (WIFSIGNALED(stat)) 479 _exit(99); 480 } 481 break; 482 case SIGUSR1: 483 _exit(0); 484 } 485 } 486 } 487 488 /*ARGSUSED*/ 489 static void 490 pkg_door_srv(void *cookie, char *argp, size_t asz, door_desc_t *dp, 491 uint_t ndesc) 492 { 493 char *p = NULL; 494 pkgcmd_t *pcmd = (pkgcmd_t *)argp; 495 ucred_t *uc = NULL; 496 uid_t caller; 497 pid_t pcaller; 498 door_desc_t ddp; 499 int dnum = 0; 500 int one = 1; 501 int len = -1; 502 503 if (asz < sizeof (pkgcmd_t)) { 504 (void) door_return(NULL, 0, NULL, 0); 505 return; 506 } 507 508 if (door_ucred(&uc) != 0) { 509 (void) door_return(NULL, 0, NULL, 0); 510 return; 511 } 512 513 caller = ucred_geteuid(uc); 514 pcaller = ucred_getpid(uc); 515 ucred_free(uc); 516 517 if (caller != myuid) { 518 (void) door_return(NULL, 0, NULL, 0); 519 return; 520 } 521 522 (void) mutex_lock(&mtx); 523 ncalls++; 524 525 if (pcaller != client_pid && pcaller != -1 && 526 (client_pid == 1 || kill(client_pid, 0) != 0)) { 527 client_pid = pcaller; 528 } 529 530 if (PKG_WRITE_COMMAND(pcmd->cmd)) 531 while (write_locked > 0) 532 (void) cond_wait(&cv, &mtx); 533 534 switch (pcmd->cmd) { 535 case PKG_FINDFILE: 536 p = file_find((pkgfilter_t *)argp, &len); 537 break; 538 case PKG_DUMP: 539 if (read_only) 540 goto err; 541 if (logcount > 0) 542 pkgdump(); 543 break; 544 case PKG_EXIT: 545 if (logcount > 0) 546 pkgdump(); 547 exit(0); 548 /*NOTREACHED*/ 549 case PKG_PKGSYNC: 550 if (read_only || logflush() != 0) 551 goto err; 552 break; 553 case PKG_FILTER: 554 if (pkgfilter((pkgfilter_t *)argp, &ddp) == 0) 555 dnum = 1; 556 break; 557 case PKG_ADDLINES: 558 if (read_only) 559 goto err; 560 changes++; 561 562 if (pkgaddlines((pkgfilter_t *)argp) != 0) 563 goto err; 564 /* If we've updated the database, tell the dump thread */ 565 lastchange = gethrtime(); 566 (void) cond_broadcast(&cv); 567 break; 568 case PKG_NOP: 569 /* Do nothing but register the current client's pid. */ 570 break; 571 default: 572 goto err; 573 } 574 575 lastcall = gethrtime(); 576 (void) mutex_unlock(&mtx); 577 (void) door_return(p, len != -1 ? len : p == NULL ? 0 : strlen(p) + 1, 578 dnum == 0 ? NULL : &ddp, dnum); 579 return; 580 581 err: 582 (void) mutex_unlock(&mtx); 583 (void) door_return((void *)&one, 4, NULL, NULL); 584 } 585 586 /* 587 * This function returns the length of the string including exactly 588 * nf fields. 589 */ 590 static ptrdiff_t 591 fieldoff(char *info, int nf) 592 { 593 char *q = info; 594 595 while (nf > 0) { 596 if (IS_ST0[(unsigned char)*q++]) { 597 if (q[-1] == 0) 598 break; 599 nf--; 600 } 601 } 602 return (q - info - 1); 603 } 604 605 /* 606 * The buf points into list of \n delimited lines. We copy it, 607 * removing the newline and adding a \0. 608 */ 609 static char * 610 mystrcpy(char *buf, int len) 611 { 612 char *res = umem_alloc(len, UMEM_NOFAIL); 613 614 (void) memcpy(res, buf, len - 1); 615 res[len - 1] = '\0'; 616 return (res); 617 } 618 619 /* 620 * Entry: a single line without the NEWLINE 621 * Return: the package entry with the path determined. 622 */ 623 static pkgentry_t * 624 parse_line(char *buf, int blen, boolean_t full) 625 { 626 char *t; 627 pkgentry_t *p; 628 int nfields; 629 630 p = umem_cache_alloc(ecache, UMEM_NOFAIL); 631 buf = p->line = mystrcpy(buf, blen + 1); 632 p->len = blen + 1; 633 634 t = buf; 635 636 while (!IS_ST0Q[(unsigned char)*t++]) 637 ; 638 639 p->pathlen = t - buf - 1; 640 if (p->pathlen == 0 || p->pathlen >= PATH_MAX) { 641 progerr("bad entry read in contents file"); 642 logerr("pathname: Unknown"); 643 logerr("problem: unable to read pathname field"); 644 if (one_shot) 645 exit(2); 646 } 647 if (t[-1] == '=') 648 while (!IS_ST0[(unsigned char)*t++]) 649 ; 650 651 /* Partial as found in the "-" entries for log */ 652 if (t[-1] == '\0') { 653 if (full) 654 goto badline; 655 656 p->pkgoff = -1; 657 return (p); 658 } 659 660 switch (*t) { 661 case '?': 662 nfields = 0; 663 break; 664 case 's': 665 case 'l': 666 /* Fields: class */ 667 nfields = 1; 668 break; 669 case 'p': 670 case 'x': 671 case 'd': 672 /* class mode owner group */ 673 nfields = 4; 674 break; 675 case 'f': 676 case 'e': 677 case 'v': 678 /* class mode owner group size csum time */ 679 nfields = 7; 680 break; 681 case 'c': 682 case 'b': 683 /* class major minor mode owner group */ 684 nfields = 6; 685 break; 686 default: 687 progerr("bad entry read in contents file"); 688 logerr("pathname: %.*s", p->pathlen, p->line); 689 logerr("problem: unknown ftype"); 690 freeentry(p); 691 if (one_shot) 692 exit(2); 693 return (NULL); 694 } 695 696 p->pkgoff = t + fieldoff(t, nfields + 1) - buf; 697 698 if (p->line[p->pkgoff] != '\0' || p->pkgoff == p->len - 1) 699 return (p); 700 701 badline: 702 progerr(gettext("bad entry read in contents file")); 703 logerr(gettext("pathname: Unknown")); 704 logerr(gettext("problem: unknown ftype")); 705 freeentry(p); 706 if (one_shot) 707 exit(2); 708 return (NULL); 709 } 710 711 static void 712 handle_comments(char *buf, int len) 713 { 714 if (cind >= 2) 715 return; 716 717 if (buf[0] != '#') 718 return; 719 720 if (ccmnt[cind] != NULL) 721 umem_free(ccmnt[cind], strlen(ccmnt[cind]) + 1); 722 ccmnt[cind] = mystrcpy(buf, len); 723 cind++; 724 } 725 726 static void 727 parse_contents(void) 728 { 729 int cnt; 730 pkgentry_t *ent, *e2; 731 avl_index_t where; 732 int num = 0; 733 struct stat stb; 734 ptrdiff_t off; 735 char *p, *q, *map; 736 pkgentry_t *lastentry = NULL; 737 int d; 738 int cntserrs = 0; 739 740 cnt = open(CONTENTS, O_RDONLY); 741 742 cind = 0; 743 744 if (cnt == -1) { 745 if (errno == ENOENT) 746 return; 747 exit(99); 748 } 749 750 if (fstat(cnt, &stb) != 0) { 751 (void) close(cnt); 752 exit(99); 753 } 754 if (stb.st_size == 0) { 755 (void) close(cnt); 756 return; 757 } 758 759 map = mmap(0, stb.st_size, PROT_READ, MAP_PRIVATE, cnt, 0); 760 (void) close(cnt); 761 if (map == (char *)-1) 762 return; 763 764 (void) madvise(map, stb.st_size, MADV_WILLNEED); 765 766 for (off = 0; off < stb.st_size; off += q - p) { 767 p = map + off; 768 q = memchr(p, '\n', stb.st_size - off); 769 if (q == NULL) 770 break; 771 772 q++; 773 num++; 774 if (p[0] == '#' || p[0] == '\n') { 775 handle_comments(p, q - p); 776 continue; 777 } 778 ent = parse_line(p, q - p - 1, B_TRUE); 779 780 if (ent == NULL) { 781 cntserrs++; 782 continue; 783 } 784 785 /* 786 * We save time by assuming the database is sorted; by 787 * using avl_insert_here(), building the tree is nearly free. 788 * lastentry always contains the last entry in the AVL tree. 789 */ 790 if (lastentry == NULL) { 791 avl_add(list, ent); 792 lastentry = ent; 793 } else if ((d = avlcmp(ent, lastentry)) == 1) { 794 avl_insert_here(list, ent, lastentry, AVL_AFTER); 795 lastentry = ent; 796 } else if (d == 0 || 797 (e2 = avl_find(list, ent, &where)) != NULL) { 798 /* 799 * This can only happen if the contents file is bad; 800 * this can, e.g., happen with the old SQL contents DB, 801 * it didn't sort properly. Assume the first one 802 * is the correct one, but who knows? 803 */ 804 if (d == 0) 805 e2 = lastentry; 806 if (strcmp(ent->line, e2->line) != 0) { 807 progerr(gettext("two entries for %.*s"), 808 ent->pathlen, ent->line); 809 cntserrs++; 810 } 811 freeentry(ent); 812 } else { 813 /* Out of order: not an error for us, really. */ 814 progerr(gettext("bad read of contents file")); 815 logerr(gettext("pathname: Unknown")); 816 logerr(gettext( 817 "problem: unable to read pathname field")); 818 if (one_shot) 819 exit(2); 820 avl_insert(list, ent, where); 821 } 822 } 823 824 cind = 0; 825 826 (void) munmap(map, stb.st_size); 827 828 /* By default, we ignore bad lines, keep them in a copy. */ 829 if (cntserrs > 0 && stb.st_nlink == 1) { 830 char bcf[sizeof (BADCONTENTS)]; 831 832 (void) strcpy(bcf, BADCONTENTS); 833 if (mktemp(bcf) != NULL) { 834 (void) link(CONTENTS, bcf); 835 syslog(LOG_WARNING, "A bad contents file was saved: %s", 836 bcf); 837 } 838 } 839 } 840 841 static int 842 parse_log(void) 843 { 844 pkgentry_t *ent, *look; 845 avl_index_t where; 846 int num = 0; 847 int logfd; 848 struct stat stb; 849 int mlen = strlen(marker); 850 off_t realend; 851 ptrdiff_t off; 852 char *p, *q, *map; 853 854 logfd = open(PKGLOG, O_RDONLY); 855 856 if (logfd < 0) { 857 if (errno == ENOENT) 858 return (0); 859 progerr(gettext("cannot read "PKGLOG": %s"), strerror(errno)); 860 exit(2); 861 } 862 863 if (fstat(logfd, &stb) != 0) { 864 progerr(gettext("cannot stat "PKGLOG": %s"), strerror(errno)); 865 exit(2); 866 } 867 868 if (stb.st_size == 0) { 869 (void) close(logfd); 870 /* Force pkgdump && remove of the logfile. */ 871 return (1); 872 } 873 874 map = mmap(0, stb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, 875 logfd, 0); 876 (void) close(logfd); 877 if (map == (char *)-1) { 878 progerr(gettext("Cannot mmap the "PKGLOG": %s"), 879 strerror(errno)); 880 exit(2); 881 } 882 883 cind = 0; 884 885 realend = stb.st_size; 886 887 if (memcmp(map + realend - mlen, marker, mlen) != 0) { 888 progerr(gettext(PKGLOG" is not complete")); 889 890 map[stb.st_size - 1] = '\0'; /* for strstr() */ 891 realend = 0; 892 for (p = map; q = strstr(p, marker); ) { 893 if (q == map || q[-1] == '\n') 894 realend = q - map + mlen; 895 p = q + mlen; 896 } 897 progerr(gettext("Ignoring %ld bytes from log"), 898 (long)(stb.st_size - realend)); 899 } 900 901 for (off = 0; off < realend; off += q - p) { 902 p = map + off; 903 q = memchr(p, '\n', realend - off); 904 if (q == NULL) 905 break; 906 907 q++; 908 num++; 909 if (p[0] == '#' || p[0] == '\n') { 910 if (memcmp(marker, p, mlen) == 0) 911 cind = 0; 912 else 913 handle_comments(p, q - p); 914 continue; 915 } 916 917 ent = parse_line(p + 1, q - (p + 1) - 1, p[0] != '-'); 918 if (ent == NULL) 919 continue; 920 look = avl_find(list, ent, &where); 921 /* 922 * The log can be replayed; so any value of "look" is 923 * not unexpected. 924 */ 925 switch (p[0]) { 926 case '+': 927 case '=': 928 if (look != NULL) 929 swapentry(look, ent); 930 else 931 avl_insert(list, ent, where); 932 break; 933 case '-': 934 if (look != NULL) { 935 avl_remove(list, look); 936 freeentry(look); 937 } 938 freeentry(ent); 939 break; 940 default: 941 freeentry(ent); 942 progerr(gettext("log %d: bad line"), num); 943 break; 944 } 945 } 946 (void) munmap(map, stb.st_size); 947 948 /* Force pkgdump && remove of the logfile if there are no valid mods. */ 949 return (num == 0 ? 1 : num); 950 } 951 952 static char * 953 file_find(pkgfilter_t *cmd, int *len) 954 { 955 pkgentry_t p; 956 pkgentry_t *look; 957 958 p.line = cmd->buf; 959 p.pathlen = cmd->len; 960 961 look = avl_find(list, &p, NULL); 962 963 if (look == NULL) 964 return (NULL); 965 966 *len = look->len; 967 return (look->line); 968 } 969 970 static void 971 pkgdump(void) 972 { 973 FILE *cnts; 974 int err = 0; 975 pkgentry_t *p; 976 977 if (read_only) 978 return; 979 980 /* We cannot dump when the current transaction is not complete. */ 981 if (sync_needed) 982 return; 983 984 cnts = fopen(TCONTENTS, "w"); 985 986 if (cnts == NULL) 987 exit(99); 988 989 for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) { 990 if (fprintf(cnts, "%s\n", p->line) < 0) 991 err++; 992 } 993 994 if (ccmnt[0] != NULL) 995 (void) fprintf(cnts, "%s\n", ccmnt[0]); 996 if (ccmnt[1] != NULL) 997 (void) fprintf(cnts, "%s\n", ccmnt[1]); 998 999 if (err != 0 || fflush(cnts) == EOF || fsync(fileno(cnts)) != 0 || 1000 fclose(cnts) == EOF || rename(TCONTENTS, CONTENTS) != 0) { 1001 err++; 1002 } 1003 1004 if (err != 0) { 1005 progerr("cannot rewrite the contents file"); 1006 exit(2); 1007 } 1008 1009 (void) fclose(log); 1010 (void) unlink(PKGLOG); 1011 log = NULL; 1012 ndumps++; 1013 logcount = 0; 1014 } 1015 1016 static void 1017 freeentry(pkgentry_t *p) 1018 { 1019 umem_free(p->line, p->len); 1020 umem_cache_free(ecache, p); 1021 } 1022 1023 static void 1024 swapentry(pkgentry_t *cur, pkgentry_t *new) 1025 { 1026 if (cur->len == new->len && 1027 strcmp(cur->line + cur->pathlen, 1028 new->line + new->pathlen) == 0) { 1029 suppressed++; 1030 freeentry(new); 1031 return; 1032 } 1033 1034 /* Free old line */ 1035 umem_free(cur->line, cur->len); 1036 1037 /* Copy new value: pathlen is the same and avl is kept */ 1038 cur->line = new->line; 1039 cur->len = new->len; 1040 cur->pkgoff = new->pkgoff; 1041 1042 umem_cache_free(ecache, new); 1043 } 1044 1045 static int 1046 logentry(char type, pkgentry_t *p) 1047 { 1048 int len; 1049 1050 if (type == '-') 1051 len = fprintf(log, "-%.*s\n", p->pathlen, p->line); 1052 else 1053 len = fprintf(log, "%c%s\n", type, p->line); 1054 1055 loglines++; 1056 if (len < 0) { 1057 logerrcnt++; 1058 return (-1); 1059 } 1060 logcount += len; 1061 return (0); 1062 } 1063 1064 static int 1065 logflush(void) 1066 { 1067 int len; 1068 static int lastflush; 1069 1070 if (log == NULL) 1071 return (0); 1072 1073 if (lastflush == logcount) 1074 return (0); 1075 1076 if (cind == 2) { 1077 (void) fprintf(log, "%s\n", ccmnt[0]); 1078 (void) fprintf(log, "%s\n", ccmnt[1]); 1079 cind = 0; 1080 } 1081 1082 /* 1083 * When using zfs, if the mark is there, then so is the rest before 1084 * it. But with ufs, we need to flush twice. 1085 */ 1086 if (flushbeforemark) { 1087 if (fflush(log) == EOF) 1088 logerrcnt++; 1089 } 1090 /* Anything before the last marker found in the log will be valid */ 1091 len = fprintf(log, "%s", marker); 1092 if (len < 0) 1093 logerrcnt++; 1094 else 1095 logcount += len; 1096 1097 if (fflush(log) == EOF) 1098 logerrcnt++; 1099 1100 sync_needed = B_FALSE; 1101 1102 if (logerrcnt > 0 || logcount > MAXLOGFILESIZE) 1103 pkgdump(); 1104 1105 if (logerrcnt > 0) 1106 return (-1); 1107 1108 lastflush = logcount; 1109 1110 return (0); 1111 } 1112 1113 static int 1114 avlcmp(const void *ca, const void *cb) 1115 { 1116 const pkgentry_t *a = ca; 1117 const pkgentry_t *b = cb; 1118 int i = memcmp(a->line, b->line, 1119 a->pathlen > b->pathlen ? b->pathlen : a->pathlen); 1120 1121 if (i < 0) 1122 return (-1); 1123 else if (i > 0) 1124 return (1); 1125 else if (a->pathlen == b->pathlen) 1126 return (0); 1127 else if (a->pathlen > b->pathlen) 1128 return (1); 1129 else 1130 return (-1); 1131 } 1132 1133 /* 1134 * Returns: 1135 * 0 - if we can get the lock 1136 * -1 - we can't lock 1137 */ 1138 1139 static int 1140 establish_lock(char *lock) 1141 { 1142 int fd = open(lock, O_RDWR|O_CREAT, 0644); 1143 int i; 1144 1145 if (fd < 0) 1146 return (-1); 1147 1148 for (i = 0; i < 5; i++) { 1149 if (lockf(fd, F_TLOCK, 0) == 0) 1150 return (0); 1151 (void) sleep(1); 1152 } 1153 1154 (void) close(fd); 1155 return (-1); 1156 } 1157 1158 static int 1159 no_memory_abort(void) 1160 { 1161 return (UMEM_CALLBACK_EXIT(99)); 1162 } 1163 1164 /* 1165 * Dump a part of the contents file in a pipe; grep for the "filter". 1166 * It doesn't matter if we return too much. 1167 */ 1168 1169 static void * 1170 thr_pkgfilter(void *v) 1171 { 1172 pkgfilter_t *pf = v; 1173 pkgentry_t *p; 1174 int nums[2]; 1175 FILE *cnts; 1176 1177 cnts = fdopen(pf->cmd, "w"); 1178 if (cnts == NULL) 1179 goto free; 1180 1181 /* 1182 * Remove wild card: don't care about extra matches; make sure 1183 * we remove both the "*" and the "." in front of it. 1184 */ 1185 if (pf->len > 0) { 1186 char *p; 1187 1188 for (p = pf->buf; *p; p++) { 1189 if (*p == '*') { 1190 *p = 0; 1191 if (p > pf->buf && p[-1] == '.') 1192 p[-1] = 0; 1193 break; 1194 } 1195 } 1196 } 1197 1198 /* Disable modifications while the filter is running */ 1199 (void) mutex_lock(&mtx); 1200 write_locked++; 1201 (void) mutex_unlock(&mtx); 1202 /* 1203 * The protocol for the contents file for the clients: 1204 * <int:len><int:pathlen><line + 0> 1205 */ 1206 1207 for (p = avl_first(list); p != NULL; p = AVL_NEXT(list, p)) { 1208 if (pf->len > 0 && strstr(p->line, pf->buf) == NULL) 1209 continue; 1210 1211 nums[0] = p->len; 1212 nums[1] = p->pathlen; 1213 if (fwrite(nums, sizeof (int), 2, cnts) != 2) 1214 break; 1215 if (fwrite(p->line, 1, p->len, cnts) != p->len) 1216 break; 1217 } 1218 1219 (void) mutex_lock(&mtx); 1220 lastcall = gethrtime(); 1221 write_locked--; 1222 (void) cond_broadcast(&cv); 1223 (void) mutex_unlock(&mtx); 1224 (void) fclose(cnts); 1225 1226 free: 1227 umem_free(pf, sizeof (pkgfilter_t) + pf->len); 1228 return (NULL); 1229 } 1230 1231 static hrtime_t 1232 time_since_(hrtime_t last) 1233 { 1234 return (gethrtime() - last); 1235 } 1236 1237 static void 1238 my_cond_reltimedwait(hrtime_t delta, int sec) 1239 { 1240 hrtime_t wait = sec * LLNANOSEC - delta; 1241 timestruc_t waitfor; 1242 1243 waitfor.tv_nsec = wait % LLNANOSEC; 1244 waitfor.tv_sec = wait / LLNANOSEC; 1245 (void) cond_reltimedwait(&cv, &mtx, &waitfor); 1246 } 1247 1248 static int 1249 pkgfilter(pkgfilter_t *pf, door_desc_t *dp) 1250 { 1251 1252 int p[2]; 1253 thread_t tid; 1254 pkgfilter_t *cpf; 1255 1256 if (pipe(p) != 0) 1257 return (-1); 1258 1259 cpf = umem_alloc(sizeof (pkgfilter_t) + pf->len, UMEM_NOFAIL); 1260 1261 (void) memcpy(cpf, pf, sizeof (pkgfilter_t) + pf->len); 1262 1263 /* Copy the file descriptor in the command field */ 1264 cpf->cmd = p[1]; 1265 1266 if (thr_create(NULL, NULL, thr_pkgfilter, cpf, THR_DETACHED, 1267 &tid) != 0) { 1268 (void) close(p[0]); 1269 (void) close(p[1]); 1270 umem_free(cpf, sizeof (pkgfilter_t) + pf->len); 1271 return (-1); 1272 } 1273 (void) memset(dp, 0, sizeof (*dp)); 1274 dp->d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE; 1275 dp->d_data.d_desc.d_descriptor = p[0]; 1276 1277 return (0); 1278 } 1279 1280 static int 1281 pkgaddlines(pkgfilter_t *pf) 1282 { 1283 char *map = pf->buf; 1284 int len = pf->len; 1285 int off; 1286 pkgentry_t *ent, *look; 1287 avl_index_t where; 1288 char *q, *p; 1289 char c; 1290 int r = 0; 1291 1292 if (log == NULL) { 1293 log = fopen(PKGLOG, "w"); 1294 if (log == NULL) 1295 return (-1); 1296 } 1297 1298 for (off = 0; off < len; off += q - p) { 1299 p = map + off; 1300 q = memchr(p, '\n', len - off); 1301 1302 if (q == NULL) 1303 break; 1304 1305 q++; 1306 1307 if (p[0] == '#' || p[0] == '\n') { 1308 handle_comments(p, q - p); 1309 continue; 1310 } 1311 1312 if (*p == '-') 1313 ent = parse_line(p + 1, q - (p + 1) - 1, B_FALSE); 1314 else 1315 ent = parse_line(p, q - p - 1, B_TRUE); 1316 1317 if (ent == NULL) { 1318 r++; 1319 continue; 1320 } 1321 1322 look = avl_find(list, ent, &where); 1323 if (look != NULL) { 1324 c = *p == '-' ? '-' : '='; 1325 if (c == '=') { 1326 swapentry(look, ent); 1327 ent = look; 1328 } else { 1329 avl_remove(list, look); 1330 freeentry(look); 1331 } 1332 } else if (*p == '-') { 1333 /* Remove something which isn't there: no-op */ 1334 freeentry(ent); 1335 continue; 1336 } else { 1337 avl_insert(list, ent, where); 1338 c = '+'; 1339 } 1340 1341 sync_needed = B_TRUE; 1342 r += logentry(c, ent); 1343 if (c == '-') 1344 freeentry(ent); 1345 } 1346 1347 return (r); 1348 } 1349 1350 static void 1351 finish(void) 1352 { 1353 if (verbose) { 1354 syslog(LOG_DEBUG, 1355 "finished: calls %d, pkgdumps %d, loglines %d " 1356 "(suppressed %d)\n", 1357 ncalls, ndumps, loglines, suppressed); 1358 } 1359 (void) fdetach(door); 1360 if (read_only) 1361 (void) unlink(door); 1362 } 1363 1364 /* 1365 * Tell the wait thread to wake up and quit. 1366 */ 1367 /* ARGSUSED */ 1368 static void 1369 signal_handler(int sig) 1370 { 1371 if (read_only) 1372 exit(0); 1373 want_to_quit = 1; 1374 (void) cond_broadcast(&cv); 1375 } 1376