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