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 #include <pkglib.h> 28 29 #include <alloca.h> 30 #include <assert.h> 31 #include <door.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <pthread.h> 35 #include <spawn.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <strings.h> 39 #include <sys/mman.h> 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 #include <sys/wait.h> 43 #include <unistd.h> 44 #include <libintl.h> 45 #include <sys/mnttab.h> 46 #include <sys/mkdev.h> 47 48 #define PKGADD_MAX (512 * 1024) 49 50 #define SADM_DIR "/var/sadm/install" 51 52 #define PKGSERV_PATH "/usr/sadm/install/bin/pkgserv" 53 54 #define ERR_PATH_TOO_BIG "alternate root path is too long" 55 #define ERR_OPEN_DOOR "cannot open pkgserv door" 56 #define ERR_START_SERVER "cannot start pkgserv daemon: %s" 57 #define ERR_START_FILTER "cannot enumerate database entries" 58 #define ERR_FIND_SADM "cannot find sadm directory" 59 60 struct pkg_server { 61 FILE *fp; 62 char *curbuf; 63 int buflen; 64 int door; 65 boolean_t onetime; 66 }; 67 68 static PKGserver current_server; 69 70 static start_mode_t defmode = INVALID; 71 static boolean_t registered = B_FALSE; 72 static pid_t master_pid = -1; 73 74 static void 75 pkgfilename(char path[PATH_MAX], const char *root, const char *sadmdir, 76 const char *file) 77 { 78 if (snprintf(path, PATH_MAX, "%s%s/%s", root == NULL ? "" : root, 79 sadmdir == NULL ? SADM_DIR : sadmdir, file) >= PATH_MAX) { 80 progerr(gettext(ERR_PATH_TOO_BIG)); 81 exit(99); 82 } 83 } 84 85 static void 86 free_xmnt(struct extmnttab *xmnt) 87 { 88 free(xmnt->mnt_special); 89 free(xmnt->mnt_mountp); 90 free(xmnt->mnt_fstype); 91 } 92 93 static void 94 copy_xmnt(const struct extmnttab *xmnt, struct extmnttab *saved) 95 { 96 97 free_xmnt(saved); 98 99 /* 100 * Copy everything and then strdup the strings we later use and NULL 101 * the ones we don't. 102 */ 103 *saved = *xmnt; 104 105 if (saved->mnt_special != NULL) 106 saved->mnt_special = strdup(saved->mnt_special); 107 if (saved->mnt_mountp != NULL) 108 saved->mnt_mountp = strdup(saved->mnt_mountp); 109 if (saved->mnt_fstype != NULL) 110 saved->mnt_fstype = strdup(saved->mnt_fstype); 111 112 saved->mnt_mntopts = NULL; 113 saved->mnt_time = NULL; 114 } 115 116 static int 117 testdoor(char *path) 118 { 119 int dir; 120 int fd; 121 struct door_info di; 122 int res; 123 124 dir = open(path, O_RDONLY); 125 126 if (dir == -1) 127 return (-1); 128 129 fd = openat(dir, PKGDOOR, O_RDWR); 130 (void) close(dir); 131 if (fd == -1) 132 return (-1); 133 134 res = door_info(fd, &di); 135 (void) close(fd); 136 return (res); 137 } 138 139 /* 140 * We need to make sure that we can locate the pkgserv and the door; 141 * lofs mounts makes this more difficult: "nosub" mounts don't propagate 142 * the door and doors created in lofs mounts are not propagated back to 143 * the original filesystem. 144 * Here we peel off the lofs mount points until we're 145 * at /var/sadm/install or 146 * we find a working door or 147 * there's nothing more to peel off. 148 * The fullpath parameter is used to return the result (stored in *sadmdir), 149 * root is used but returned in the computed sadmdir and so the caller should 150 * not use "root" any longer or set it to NULL. 151 */ 152 static void 153 pkgfindrealsadmdir(char fullpath[PATH_MAX], const char *root, 154 const char **sadmdir) 155 { 156 struct stat buf; 157 struct extmnttab xmnt; 158 FILE *mnttab = NULL; 159 char temp[PATH_MAX]; 160 struct extmnttab saved = {NULL, NULL, NULL, NULL, NULL, 0, 0}; 161 162 if (snprintf(temp, PATH_MAX, "%s%s", 163 root == NULL ? "" : root, 164 *sadmdir == NULL ? SADM_DIR : *sadmdir) >= PATH_MAX) { 165 progerr(gettext(ERR_PATH_TOO_BIG)); 166 exit(99); 167 } 168 169 if (stat(temp, &buf) != 0) { 170 progerr(gettext(ERR_FIND_SADM)); 171 exit(99); 172 } 173 174 /* 175 * To find the underlying mount point, you will need to 176 * search the mnttab and find our mountpoint and the underlying 177 * filesystem. 178 * To find the mount point: use the longest prefix but limit 179 * us to the filesystems with the same major/minor numbers. 180 * To find the underlying mount point: find a non-lofs file 181 * system or a <mnt> <mnt> entry (fake mountpoint for zones). 182 */ 183 for (;;) { 184 size_t max = 0; 185 186 if (realpath(temp, fullpath) == NULL) { 187 progerr(gettext(ERR_FIND_SADM)); 188 exit(99); 189 } 190 191 if (strcmp(fullpath, SADM_DIR) == 0) 192 break; 193 194 if (testdoor(fullpath) == 0) 195 break; 196 197 if (mnttab == NULL) 198 mnttab = fopen(MNTTAB, "r"); 199 else 200 resetmnttab(mnttab); 201 202 while (getextmntent(mnttab, &xmnt, 0) == 0) { 203 size_t len; 204 205 if (major(buf.st_dev) != xmnt.mnt_major || 206 minor(buf.st_dev) != xmnt.mnt_minor) 207 continue; 208 209 len = strlen(xmnt.mnt_mountp); 210 if (len < max) 211 continue; 212 213 if (strncmp(xmnt.mnt_mountp, fullpath, len) == 0 && 214 (len == 1 || fullpath[len] == '/' || 215 fullpath[len] == '\0')) { 216 max = len; 217 copy_xmnt(&xmnt, &saved); 218 } 219 } 220 if (strcmp(saved.mnt_fstype, "lofs") != 0 || 221 strcmp(saved.mnt_mountp, saved.mnt_special) == 0) { 222 break; 223 } 224 /* Create a new path in the underlying filesystem. */ 225 if (snprintf(temp, PATH_MAX, "%s%s", saved.mnt_special, 226 &fullpath[max]) >= PATH_MAX) { 227 progerr(gettext(ERR_PATH_TOO_BIG)); 228 exit(99); 229 } 230 } 231 232 if (mnttab != NULL) { 233 free_xmnt(&saved); 234 (void) fclose(mnttab); 235 } 236 *sadmdir = fullpath; 237 } 238 239 static void 240 pkgexit_close(void) 241 { 242 if (current_server != NULL) 243 pkgcloseserver(current_server); 244 } 245 246 static PKGserver 247 pkgopenserver_i(const char *root, const char *sadmdir, boolean_t readonly, 248 start_mode_t mode) 249 { 250 PKGserver server; 251 struct door_info di; 252 pid_t pid; 253 int stat; 254 int first = B_TRUE; 255 char *cmd[16]; 256 int args; 257 char pkgdoor[PATH_MAX]; 258 char realsadmdir[PATH_MAX]; 259 extern char **environ; 260 char *prog; 261 char pidbuf[12]; 262 263 if (current_server != NULL) 264 return (current_server); 265 266 if (!registered) { 267 registered = B_TRUE; 268 (void) atexit(pkgexit_close); 269 } 270 if (readonly) { 271 int fd; 272 273 (void) strcpy(pkgdoor, "/tmp/pkgdoor.XXXXXX"); 274 if ((fd = mkstemp(pkgdoor)) < 0) { 275 progerr(gettext(ERR_OPEN_DOOR)); 276 return (NULL); 277 } 278 (void) close(fd); 279 } else { 280 pkgfindrealsadmdir(realsadmdir, root, &sadmdir); 281 root = NULL; 282 pkgfilename(pkgdoor, root, sadmdir, PKGDOOR); 283 } 284 285 server = malloc(sizeof (*server)); 286 287 if (server == NULL) 288 goto return_null; 289 290 server->fp = NULL; 291 server->onetime = readonly; 292 293 openserver: 294 server->door = open(pkgdoor, O_RDWR); 295 296 if (server->door >= 0) { 297 if (door_info(server->door, &di) == 0 && di.di_target >= 0) { 298 pkgcmd_t n; 299 n.cmd = PKG_NOP; 300 server->buflen = 1024; 301 server->curbuf = malloc(1024); 302 if (server->curbuf == NULL || 303 pkgcmd(server, &n, sizeof (n), NULL, NULL, NULL)) { 304 pkgcloseserver(server); 305 return (NULL); 306 } 307 return (current_server = server); 308 } 309 310 (void) close(server->door); 311 } 312 313 if (!first || mode == NEVER) 314 goto return_null; 315 316 first = B_FALSE; 317 318 args = 0; 319 cmd[args++] = strrchr(PKGSERV_PATH, '/') + 1; 320 if (root != NULL && strcmp(root, "/") != 0) { 321 cmd[args++] = "-R"; 322 cmd[args++] = (char *)root; 323 } 324 if (sadmdir != NULL && strcmp(sadmdir, SADM_DIR) != 0) { 325 cmd[args++] = "-d"; 326 cmd[args++] = (char *)sadmdir; 327 } 328 if (readonly) { 329 cmd[args++] = "-r"; 330 cmd[args++] = pkgdoor; 331 } 332 prog = get_prog_name(); 333 if (prog != NULL) { 334 cmd[args++] = "-N"; 335 cmd[args++] = prog; 336 } 337 338 switch (mode) { 339 case FLUSH_LOG: 340 cmd[args++] = "-e"; 341 break; 342 case RUN_ONCE: 343 cmd[args++] = "-o"; 344 break; 345 case PERMANENT: 346 cmd[args++] = "-p"; 347 break; 348 default: 349 break; 350 } 351 352 if (master_pid != -1) { 353 cmd[args++] = "-P"; 354 (void) snprintf(pidbuf, sizeof (pidbuf), "%d", master_pid); 355 cmd[args++] = pidbuf; 356 } 357 cmd[args++] = NULL; 358 assert(args <= sizeof (cmd)/sizeof (char *)); 359 360 if (posix_spawn(&pid, PKGSERV_PATH, NULL, NULL, cmd, environ) == 0) { 361 server->onetime |= (mode == RUN_ONCE); 362 while (wait4(pid, &stat, 0, NULL) != -1) { 363 if (WIFEXITED(stat)) { 364 int s = WEXITSTATUS(stat); 365 if (s == 0 || s == 1) 366 if (mode == FLUSH_LOG) 367 goto return_null; 368 else 369 goto openserver; 370 if (s == 2) 371 goto return_null; 372 break; 373 } else if (WIFSIGNALED(stat)) { 374 break; 375 } 376 } 377 } 378 379 progerr(gettext(ERR_START_SERVER), strerror(errno)); 380 381 return_null: 382 if (readonly) 383 (void) unlink(pkgdoor); 384 free(server); 385 return (NULL); 386 } 387 388 PKGserver 389 pkgopenserver(const char *root, const char *sadmdir, boolean_t ro) 390 { 391 return (pkgopenserver_i(root, sadmdir, ro, pkgservergetmode())); 392 } 393 394 start_mode_t 395 pkgparsemode(const char *mode) 396 { 397 if (strcasecmp(mode, MODE_PERMANENT) == 0) { 398 return (PERMANENT); 399 } else if (strncasecmp(mode, MODE_TIMEOUT, 400 sizeof (MODE_TIMEOUT) - 1) == 0) { 401 const char *pidstr = mode + sizeof (MODE_TIMEOUT) - 1; 402 if (pidstr[0] != '\0') { 403 master_pid = atoi(pidstr); 404 if (master_pid <= 1 || kill(master_pid, 0) != 0) 405 master_pid = -1; 406 } 407 408 return (TIMEOUT); 409 } else if (strcasecmp(mode, MODE_RUN_ONCE) == 0) { 410 return (RUN_ONCE); 411 } else { 412 progerr(gettext("invalid pkgserver mode: %s"), mode); 413 exit(99); 414 /*NOTREACHED*/ 415 } 416 } 417 418 char * 419 pkgmodeargument(start_mode_t mode) 420 { 421 static char timebuf[sizeof (PKGSERV_MODE) + sizeof (MODE_TIMEOUT) + 10]; 422 423 switch (mode) { 424 case PERMANENT: 425 return (PKGSERV_MODE MODE_PERMANENT); 426 case TIMEOUT: 427 (void) snprintf(timebuf, sizeof (timebuf), 428 PKGSERV_MODE MODE_TIMEOUT "%d", 429 (master_pid > 1 && kill(master_pid, 0) == 0) ? master_pid : 430 getpid()); 431 return (timebuf); 432 case RUN_ONCE: 433 return (PKGSERV_MODE MODE_RUN_ONCE); 434 } 435 progerr(gettext("Bad pkgserv mode: %d"), (int)mode); 436 exit(99); 437 /*NOTREACHED*/ 438 } 439 440 void 441 pkgserversetmode(start_mode_t mode) 442 { 443 if (mode == DEFAULTMODE || mode == INVALID) { 444 char *var = getenv(SUNW_PKG_SERVERMODE); 445 446 if (var != NULL) 447 defmode = pkgparsemode(var); 448 else 449 defmode = DEFAULTMODE; 450 } else { 451 defmode = mode; 452 } 453 } 454 455 start_mode_t 456 pkgservergetmode(void) 457 { 458 if (defmode == INVALID) 459 pkgserversetmode(DEFAULTMODE); 460 return (defmode); 461 } 462 463 void 464 pkgcloseserver(PKGserver server) 465 { 466 467 if (server->fp != NULL) 468 (void) fclose(server->fp); 469 free(server->curbuf); 470 if (server->onetime) { 471 pkgcmd_t cmd; 472 cmd.cmd = PKG_EXIT; 473 (void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL); 474 } 475 (void) close(server->door); 476 if (server == current_server) 477 current_server = NULL; 478 free(server); 479 } 480 481 int 482 pkgcmd(PKGserver srv, void *cmd, size_t len, char **result, size_t *rlen, 483 int *fd) 484 { 485 door_arg_t da; 486 487 da.data_ptr = cmd; 488 da.data_size = len; 489 da.desc_ptr = NULL; 490 da.desc_num = 0; 491 da.rbuf = result == NULL ? NULL : *result; 492 da.rsize = rlen == NULL ? 0 : *rlen; 493 494 if (door_call(srv->door, &da) != 0) { 495 if (((pkgcmd_t *)cmd)->cmd == PKG_EXIT && errno == EINTR) 496 return (0); 497 return (-1); 498 } 499 500 if (da.desc_ptr != NULL) { 501 int i = 0; 502 if (fd != NULL) 503 *fd = da.desc_ptr[i++].d_data.d_desc.d_descriptor; 504 for (; i < da.desc_num; i++) 505 (void) close(da.desc_ptr[i].d_data.d_desc.d_descriptor); 506 } 507 /* Error return */ 508 if (da.data_size == sizeof (int)) { 509 int x = *(int *)da.data_ptr; 510 if (x != 0) { 511 if (result == NULL || da.rbuf != *result) 512 (void) munmap(da.rbuf, da.rsize); 513 return (x); 514 } 515 } 516 517 /* Other result */ 518 if (result != NULL) { 519 /* Make sure that the result is at the start of the buffer. */ 520 if (da.data_ptr != NULL && da.rbuf != da.data_ptr) 521 (void) memmove(da.rbuf, da.data_ptr, da.data_size); 522 *result = da.rbuf; 523 *rlen = da.data_size; 524 } else if (da.rbuf != NULL) { 525 (void) munmap(da.rbuf, da.rsize); 526 } 527 return (0); 528 } 529 530 /* 531 * Pkgsync: 532 * If the server is running, make sure that the contents 533 * file is written. 534 * If the server is not running, check for the log file; 535 * if there's a non-empty log file, we need to start the server 536 * as it will incorporate the log file into the contents file. 537 * And then check if the door is present. If it doesn't, we don't 538 * need to call it. 539 */ 540 541 boolean_t 542 pkgsync_needed(const char *root, const char *sadmdir, boolean_t want_quit) 543 { 544 struct stat pbuf; 545 char pkgfile[PATH_MAX]; 546 boolean_t sync_needed, running; 547 int fd; 548 struct door_info di; 549 550 pkgfilename(pkgfile, root, sadmdir, PKGLOG); 551 552 sync_needed = stat(pkgfile, &pbuf) == 0 && pbuf.st_size > 0; 553 554 if (!sync_needed && !want_quit) 555 return (B_FALSE); 556 557 pkgfilename(pkgfile, root, sadmdir, PKGDOOR); 558 559 /* sync_needed == B_TRUE || want_quit == B_TRUE */ 560 running = B_FALSE; 561 562 fd = open(pkgfile, O_RDWR); 563 564 if (fd >= 0) { 565 if (door_info(fd, &di) == 0) { 566 /* It's mounted, so the server is likely there */ 567 running = B_TRUE; 568 } 569 (void) close(fd); 570 } 571 return (running || sync_needed); 572 } 573 574 int 575 pkgsync(const char *root, const char *sadmdir, boolean_t force_quit) 576 { 577 void *server; 578 pkgcmd_t cmd; 579 580 /* No need to write contents file; don't start if not running */ 581 if (!pkgsync_needed(root, sadmdir, force_quit)) 582 return (0); 583 584 server = pkgopenserver_i(root, sadmdir, B_FALSE, FLUSH_LOG); 585 /* 586 * We're assuming that it started the server and exited immediately. 587 * If that didn't work, there's nothing we can do. 588 */ 589 if (server == NULL) 590 return (0); 591 592 cmd.cmd = force_quit ? PKG_EXIT : PKG_DUMP; 593 594 (void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL); 595 (void) pkgcloseserver(server); 596 return (0); 597 } 598 599 int 600 pkgservercommitfile(VFP_T *a_vfp, PKGserver server) 601 { 602 size_t len = vfpGetModifiedLen(a_vfp); 603 ssize_t rem = len; 604 size_t off; 605 pkgfilter_t *pcmd; 606 char *map = a_vfp->_vfpStart; 607 608 if (len < PKGADD_MAX) 609 pcmd = alloca(sizeof (*pcmd) + len); 610 else 611 pcmd = alloca(sizeof (*pcmd) + PKGADD_MAX); 612 613 614 off = 0; 615 pcmd->cmd = PKG_ADDLINES; 616 while (rem > 0) { 617 char *p = map + off; 618 len = rem; 619 620 if (len >= PKGADD_MAX) { 621 len = PKGADD_MAX - 1; 622 while (p[len] != '\n' && len > 0) 623 len--; 624 if (p[len] != '\n') 625 return (-1); 626 len++; 627 } 628 (void) memcpy(&pcmd->buf[0], p, len); 629 pcmd->len = len; 630 631 if (pkgcmd(server, pcmd, sizeof (*pcmd) + len - 1, 632 NULL, NULL, NULL) != 0) { 633 return (-1); 634 } 635 rem -= len; 636 off += len; 637 } 638 pcmd->len = 0; 639 pcmd->cmd = PKG_PKGSYNC; 640 if (pkgcmd(server, pcmd, sizeof (*pcmd), NULL, NULL, NULL) != 0) 641 return (-1); 642 643 /* Mark it unmodified. */ 644 vfpTruncate(a_vfp); 645 (void) vfpClearModified(a_vfp); 646 647 return (0); 648 } 649 650 int 651 pkgopenfilter(PKGserver server, const char *filt) 652 { 653 int fd; 654 pkgfilter_t *pfcmd; 655 int clen = filt == NULL ? 0 : strlen(filt); 656 int len = sizeof (*pfcmd) + clen; 657 658 pfcmd = alloca(len); 659 660 if (server->fp != NULL) { 661 (void) fclose(server->fp); 662 server->fp = NULL; 663 } 664 665 pfcmd->cmd = PKG_FILTER; 666 pfcmd->len = clen; 667 if (filt != NULL) 668 (void) strcpy(pfcmd->buf, filt); 669 670 fd = -1; 671 672 if (pkgcmd(server, pfcmd, len, NULL, NULL, &fd) != 0 || fd == -1) { 673 progerr(gettext(ERR_START_FILTER)); 674 return (-1); 675 } 676 (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 677 678 server->fp = fdopen(fd, "r"); 679 if (server->fp == NULL) { 680 (void) close(fd); 681 progerr(gettext(ERR_START_FILTER)); 682 return (-1); 683 } 684 return (0); 685 } 686 687 void 688 pkgclosefilter(PKGserver server) 689 { 690 if (server->fp != NULL) { 691 (void) fclose(server->fp); 692 server->fp = NULL; 693 } 694 } 695 696 /* 697 * Report the next entry from the contents file. 698 */ 699 char * 700 pkggetentry(PKGserver server, int *len, int *pathlen) 701 { 702 int num[2]; 703 704 if (server->fp == NULL) 705 return (NULL); 706 707 if (feof(server->fp) || ferror(server->fp)) 708 return (NULL); 709 710 if (fread(num, sizeof (int), 2, server->fp) != 2) 711 return (NULL); 712 713 if (num[0] > server->buflen) { 714 free(server->curbuf); 715 server->buflen = num[0]; 716 server->curbuf = malloc(server->buflen); 717 if (server->curbuf == NULL) 718 return (NULL); 719 } 720 if (fread(server->curbuf, 1, num[0], server->fp) != num[0]) 721 return (NULL); 722 723 *len = num[0]; 724 *pathlen = num[1]; 725 726 return (server->curbuf); 727 } 728 729 char * 730 pkggetentry_named(PKGserver server, const char *path, int *len, int *pathlen) 731 { 732 int plen = strlen(path); 733 pkgfilter_t *pcmd = alloca(sizeof (*pcmd) + plen); 734 char *result; 735 unsigned int rlen; 736 737 pcmd->cmd = PKG_FINDFILE; 738 *pathlen = pcmd->len = plen; 739 (void) memcpy(pcmd->buf, path, pcmd->len + 1); 740 741 result = server->curbuf; 742 rlen = server->buflen; 743 744 if (pkgcmd(server, pcmd, sizeof (*pcmd) + pcmd->len, 745 &result, &rlen, NULL) != 0) { 746 return (NULL); 747 } 748 if (rlen == 0) 749 return (NULL); 750 751 /* Result too big */ 752 if (result != server->curbuf) { 753 free(server->curbuf); 754 server->buflen = rlen; 755 server->curbuf = malloc(server->buflen); 756 if (server->curbuf == NULL) 757 return (NULL); 758 (void) memcpy(server->curbuf, result, rlen); 759 (void) munmap(result, rlen); 760 } 761 *len = rlen; 762 763 return (server->curbuf); 764 } 765