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 2009 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 46 #define PKGADD_MAX (512 * 1024) 47 48 #define SADM_DIR "/var/sadm/install" 49 50 #define PKGSERV_PATH "/usr/sadm/install/bin/pkgserv" 51 52 #define ERR_PATH_TOO_BIG "alternate root path is too long" 53 #define ERR_OPEN_DOOR "cannot open pkgserv door" 54 #define ERR_START_SERVER "cannot start pkgserv daemon: %s" 55 #define ERR_START_FILTER "cannot enumerate database entries" 56 57 struct pkg_server { 58 FILE *fp; 59 char *curbuf; 60 int buflen; 61 int door; 62 boolean_t onetime; 63 }; 64 65 static PKGserver current_server; 66 67 static start_mode_t defmode = INVALID; 68 static boolean_t registered = B_FALSE; 69 static pid_t master_pid = -1; 70 71 static void 72 pkgfilename(char path[PATH_MAX], const char *root, const char *sadmdir, 73 const char *file) 74 { 75 if (snprintf(path, PATH_MAX, "%s%s/%s", root == NULL ? "" : root, 76 sadmdir == NULL ? SADM_DIR : sadmdir, file) >= PATH_MAX) { 77 progerr(gettext(ERR_PATH_TOO_BIG)); 78 exit(99); 79 } 80 } 81 82 static void 83 pkgexit_close(void) 84 { 85 if (current_server != NULL) 86 pkgcloseserver(current_server); 87 } 88 89 static PKGserver 90 pkgopenserver_i(const char *root, const char *sadmdir, boolean_t readonly, 91 start_mode_t mode) 92 { 93 PKGserver server; 94 struct door_info di; 95 pid_t pid; 96 int stat; 97 int first = B_TRUE; 98 char *cmd[16]; 99 int args; 100 char pkgdoor[PATH_MAX]; 101 extern char **environ; 102 char *prog; 103 char pidbuf[12]; 104 105 if (current_server != NULL) 106 return (current_server); 107 108 if (!registered) { 109 registered = B_TRUE; 110 (void) atexit(pkgexit_close); 111 } 112 if (readonly) { 113 int fd; 114 115 (void) strcpy(pkgdoor, "/tmp/pkgdoor.XXXXXX"); 116 if ((fd = mkstemp(pkgdoor)) < 0) { 117 progerr(gettext(ERR_OPEN_DOOR)); 118 return (NULL); 119 } 120 (void) close(fd); 121 } else { 122 pkgfilename(pkgdoor, root, sadmdir, PKGDOOR); 123 } 124 125 server = malloc(sizeof (*server)); 126 127 if (server == NULL) 128 goto return_null; 129 130 server->fp = NULL; 131 server->onetime = readonly; 132 133 openserver: 134 server->door = open(pkgdoor, O_RDWR); 135 136 if (server->door >= 0) { 137 if (door_info(server->door, &di) == 0 && di.di_target >= 0) { 138 pkgcmd_t n; 139 n.cmd = PKG_NOP; 140 server->buflen = 1024; 141 server->curbuf = malloc(1024); 142 if (server->curbuf == NULL || 143 pkgcmd(server, &n, sizeof (n), NULL, NULL, NULL)) { 144 pkgcloseserver(server); 145 return (NULL); 146 } 147 return (current_server = server); 148 } 149 150 (void) close(server->door); 151 } 152 153 if (!first || mode == NEVER) 154 goto return_null; 155 156 first = B_FALSE; 157 158 args = 0; 159 cmd[args++] = strrchr(PKGSERV_PATH, '/') + 1; 160 if (root != NULL && strcmp(root, "/") != 0) { 161 cmd[args++] = "-R"; 162 cmd[args++] = (char *)root; 163 } 164 if (sadmdir != NULL && strcmp(sadmdir, SADM_DIR) != 0) { 165 cmd[args++] = "-d"; 166 cmd[args++] = (char *)sadmdir; 167 } 168 if (readonly) { 169 cmd[args++] = "-r"; 170 cmd[args++] = pkgdoor; 171 } 172 prog = get_prog_name(); 173 if (prog != NULL) { 174 cmd[args++] = "-N"; 175 cmd[args++] = prog; 176 } 177 178 switch (mode) { 179 case FLUSH_LOG: 180 cmd[args++] = "-e"; 181 break; 182 case RUN_ONCE: 183 cmd[args++] = "-o"; 184 break; 185 case PERMANENT: 186 cmd[args++] = "-p"; 187 break; 188 default: 189 break; 190 } 191 192 if (master_pid != -1) { 193 cmd[args++] = "-P"; 194 (void) snprintf(pidbuf, sizeof (pidbuf), "%d", master_pid); 195 cmd[args++] = pidbuf; 196 } 197 cmd[args++] = NULL; 198 assert(args <= sizeof (cmd)/sizeof (char *)); 199 200 if (posix_spawn(&pid, PKGSERV_PATH, NULL, NULL, cmd, environ) == 0) { 201 server->onetime |= mode == RUN_ONCE; 202 while (wait4(pid, &stat, 0, NULL) != -1) { 203 if (WIFEXITED(stat)) { 204 int s = WEXITSTATUS(stat); 205 if (s == 0 || s == 1) 206 if (mode == FLUSH_LOG) 207 goto return_null; 208 else 209 goto openserver; 210 if (s == 2) 211 goto return_null; 212 break; 213 } else if (WIFSIGNALED(stat)) { 214 break; 215 } 216 } 217 } 218 219 progerr(gettext(ERR_START_SERVER), strerror(errno)); 220 221 return_null: 222 if (readonly) 223 (void) unlink(pkgdoor); 224 free(server); 225 return (NULL); 226 } 227 228 PKGserver 229 pkgopenserver(const char *root, const char *sadmdir, boolean_t ro) 230 { 231 return (pkgopenserver_i(root, sadmdir, ro, pkgservergetmode())); 232 } 233 234 start_mode_t 235 pkgparsemode(const char *mode) 236 { 237 if (strcasecmp(mode, MODE_PERMANENT) == 0) { 238 return (PERMANENT); 239 } else if (strncasecmp(mode, MODE_TIMEOUT, 240 sizeof (MODE_TIMEOUT) - 1) == 0) { 241 const char *pidstr = mode + sizeof (MODE_TIMEOUT) - 1; 242 if (pidstr[0] != '\0') { 243 master_pid = atoi(pidstr); 244 if (master_pid <= 1 || kill(master_pid, 0) != 0) 245 master_pid = -1; 246 } 247 248 return (TIMEOUT); 249 } else if (strcasecmp(mode, MODE_RUN_ONCE) == 0) { 250 return (RUN_ONCE); 251 } else { 252 progerr(gettext("invalid pkgserver mode: %s"), mode); 253 exit(99); 254 /*NOTREACHED*/ 255 } 256 } 257 258 char * 259 pkgmodeargument(start_mode_t mode) 260 { 261 static char timebuf[sizeof (PKGSERV_MODE) + sizeof (MODE_TIMEOUT) + 10]; 262 263 switch (mode) { 264 case PERMANENT: 265 return (PKGSERV_MODE MODE_PERMANENT); 266 case TIMEOUT: 267 (void) snprintf(timebuf, sizeof (timebuf), 268 PKGSERV_MODE MODE_TIMEOUT "%d", 269 (master_pid > 1 && kill(master_pid, 0) == 0) ? master_pid : 270 getpid()); 271 return (timebuf); 272 case RUN_ONCE: 273 return (PKGSERV_MODE MODE_RUN_ONCE); 274 } 275 progerr(gettext("Bad pkgserv mode: %d"), (int)mode); 276 exit(99); 277 } 278 279 void 280 pkgserversetmode(start_mode_t mode) 281 { 282 if (mode == DEFAULTMODE || mode == INVALID) { 283 char *var = getenv(SUNW_PKG_SERVERMODE); 284 285 if (var != NULL) 286 defmode = pkgparsemode(var); 287 else 288 defmode = DEFAULTMODE; 289 } else { 290 defmode = mode; 291 } 292 } 293 294 start_mode_t 295 pkgservergetmode(void) 296 { 297 if (defmode == INVALID) 298 pkgserversetmode(DEFAULTMODE); 299 return (defmode); 300 } 301 302 void 303 pkgcloseserver(PKGserver server) 304 { 305 306 if (server->fp != NULL) 307 (void) fclose(server->fp); 308 free(server->curbuf); 309 if (server->onetime) { 310 pkgcmd_t cmd; 311 cmd.cmd = PKG_EXIT; 312 (void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL); 313 } 314 (void) close(server->door); 315 if (server == current_server) 316 current_server = NULL; 317 free(server); 318 } 319 320 int 321 pkgcmd(PKGserver srv, void *cmd, size_t len, char **result, size_t *rlen, 322 int *fd) 323 { 324 door_arg_t da; 325 326 da.data_ptr = cmd; 327 da.data_size = len; 328 da.desc_ptr = NULL; 329 da.desc_num = 0; 330 da.rbuf = result == NULL ? NULL : *result; 331 da.rsize = rlen == NULL ? 0 : *rlen; 332 333 if (door_call(srv->door, &da) != 0) { 334 if (((pkgcmd_t *)cmd)->cmd == PKG_EXIT && errno == EINTR) 335 return (0); 336 return (-1); 337 } 338 339 if (da.desc_ptr != NULL) { 340 int i = 0; 341 if (fd != NULL) 342 *fd = da.desc_ptr[i++].d_data.d_desc.d_descriptor; 343 for (; i < da.desc_num; i++) 344 (void) close(da.desc_ptr[i].d_data.d_desc.d_descriptor); 345 } 346 /* Error return */ 347 if (da.data_size == sizeof (int)) { 348 int x = *(int *)da.data_ptr; 349 if (x != 0) { 350 if (result == NULL || da.rbuf != *result) 351 (void) munmap(da.rbuf, da.rsize); 352 return (x); 353 } 354 } 355 356 /* Other result */ 357 if (result != NULL) { 358 /* Make sure that the result is at the start of the buffer. */ 359 if (da.data_ptr != NULL && da.rbuf != da.data_ptr) 360 (void) memmove(da.rbuf, da.data_ptr, da.data_size); 361 *result = da.rbuf; 362 *rlen = da.data_size; 363 } else if (da.rbuf != NULL) { 364 (void) munmap(da.rbuf, da.rsize); 365 } 366 return (0); 367 } 368 369 /* 370 * Pkgsync: 371 * If the server is running, make sure that the contents 372 * file is written. 373 * If the server is not running, check for the log file; 374 * if there's a non-empty log file, we need to start the server 375 * as it will incorporate the log file into the contents file. 376 * And then check if the door is present. If it doesn't, we don't 377 * need to call it. 378 */ 379 380 boolean_t 381 pkgsync_needed(const char *root, const char *sadmdir, boolean_t want_quit) 382 { 383 struct stat pbuf; 384 char pkgfile[PATH_MAX]; 385 boolean_t sync_needed, running; 386 int fd; 387 struct door_info di; 388 389 pkgfilename(pkgfile, root, sadmdir, PKGLOG); 390 391 sync_needed = stat(pkgfile, &pbuf) == 0 && pbuf.st_size > 0; 392 393 if (!sync_needed && !want_quit) 394 return (B_FALSE); 395 396 pkgfilename(pkgfile, root, sadmdir, PKGDOOR); 397 398 /* sync_needed == B_TRUE || want_quit == B_TRUE */ 399 running = B_FALSE; 400 401 fd = open(pkgfile, O_RDWR); 402 403 if (fd >= 0) { 404 if (door_info(fd, &di) == 0) { 405 /* It's mounted, so the server is likely there */ 406 running = B_TRUE; 407 } 408 (void) close(fd); 409 } 410 return (running || sync_needed); 411 } 412 413 int 414 pkgsync(const char *root, const char *sadmdir, boolean_t force_quit) 415 { 416 void *server; 417 pkgcmd_t cmd; 418 419 /* No need to write contents file; don't start if not running */ 420 if (!pkgsync_needed(root, sadmdir, force_quit)) 421 return (0); 422 423 server = pkgopenserver_i(root, sadmdir, B_FALSE, FLUSH_LOG); 424 /* 425 * We're assuming that it started the server and exited immediately. 426 * If that didn't work, there's nothing we can do. 427 */ 428 if (server == NULL) 429 return (0); 430 431 cmd.cmd = force_quit ? PKG_EXIT : PKG_DUMP; 432 433 (void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL); 434 (void) pkgcloseserver(server); 435 return (0); 436 } 437 438 int 439 pkgservercommitfile(VFP_T *a_vfp, PKGserver server) 440 { 441 size_t len = vfpGetModifiedLen(a_vfp); 442 ssize_t rem = len; 443 size_t off; 444 pkgfilter_t *pcmd; 445 char *map = a_vfp->_vfpStart; 446 447 if (len < PKGADD_MAX) 448 pcmd = alloca(sizeof (*pcmd) + len); 449 else 450 pcmd = alloca(sizeof (*pcmd) + PKGADD_MAX); 451 452 453 off = 0; 454 pcmd->cmd = PKG_ADDLINES; 455 while (rem > 0) { 456 char *p = map + off; 457 len = rem; 458 459 if (len >= PKGADD_MAX) { 460 len = PKGADD_MAX - 1; 461 while (p[len] != '\n' && len > 0) 462 len--; 463 if (p[len] != '\n') 464 return (-1); 465 len++; 466 } 467 (void) memcpy(&pcmd->buf[0], p, len); 468 pcmd->len = len; 469 470 if (pkgcmd(server, pcmd, sizeof (*pcmd) + len - 1, 471 NULL, NULL, NULL) != 0) { 472 return (-1); 473 } 474 rem -= len; 475 off += len; 476 } 477 pcmd->len = 0; 478 pcmd->cmd = PKG_PKGSYNC; 479 if (pkgcmd(server, pcmd, sizeof (*pcmd), NULL, NULL, NULL) != 0) 480 return (-1); 481 482 /* Mark it unmodified. */ 483 vfpTruncate(a_vfp); 484 (void) vfpClearModified(a_vfp); 485 486 return (0); 487 } 488 489 int 490 pkgopenfilter(PKGserver server, const char *filt) 491 { 492 int fd; 493 pkgfilter_t *pfcmd; 494 int clen = filt == NULL ? 0 : strlen(filt); 495 int len = sizeof (*pfcmd) + clen; 496 497 pfcmd = alloca(len); 498 499 if (server->fp != NULL) { 500 (void) fclose(server->fp); 501 server->fp = NULL; 502 } 503 504 pfcmd->cmd = PKG_FILTER; 505 pfcmd->len = clen; 506 if (filt != NULL) 507 (void) strcpy(pfcmd->buf, filt); 508 509 fd = -1; 510 511 if (pkgcmd(server, pfcmd, len, NULL, NULL, &fd) != 0 || fd == -1) { 512 progerr(gettext(ERR_START_FILTER)); 513 return (-1); 514 } 515 (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 516 517 server->fp = fdopen(fd, "r"); 518 if (server->fp == NULL) { 519 (void) close(fd); 520 progerr(gettext(ERR_START_FILTER)); 521 return (-1); 522 } 523 return (0); 524 } 525 526 void 527 pkgclosefilter(PKGserver server) 528 { 529 if (server->fp != NULL) { 530 (void) fclose(server->fp); 531 server->fp = NULL; 532 } 533 } 534 535 /* 536 * Report the next entry from the contents file. 537 */ 538 char * 539 pkggetentry(PKGserver server, int *len, int *pathlen) 540 { 541 int num[2]; 542 543 if (server->fp == NULL) 544 return (NULL); 545 546 if (feof(server->fp) || ferror(server->fp)) 547 return (NULL); 548 549 if (fread(num, sizeof (int), 2, server->fp) != 2) 550 return (NULL); 551 552 if (num[0] > server->buflen) { 553 free(server->curbuf); 554 server->buflen = num[0]; 555 server->curbuf = malloc(server->buflen); 556 if (server->curbuf == NULL) 557 return (NULL); 558 } 559 if (fread(server->curbuf, 1, num[0], server->fp) != num[0]) 560 return (NULL); 561 562 *len = num[0]; 563 *pathlen = num[1]; 564 565 return (server->curbuf); 566 } 567 568 char * 569 pkggetentry_named(PKGserver server, const char *path, int *len, int *pathlen) 570 { 571 int plen = strlen(path); 572 pkgfilter_t *pcmd = alloca(sizeof (*pcmd) + plen); 573 char *result; 574 unsigned int rlen; 575 576 pcmd->cmd = PKG_FINDFILE; 577 *pathlen = pcmd->len = plen; 578 (void) memcpy(pcmd->buf, path, pcmd->len + 1); 579 580 result = server->curbuf; 581 rlen = server->buflen; 582 583 if (pkgcmd(server, pcmd, sizeof (*pcmd) + pcmd->len, 584 &result, &rlen, NULL) != 0) { 585 return (NULL); 586 } 587 if (rlen == 0) 588 return (NULL); 589 590 /* Result too big */ 591 if (result != server->curbuf) { 592 free(server->curbuf); 593 server->buflen = rlen; 594 server->curbuf = malloc(server->buflen); 595 if (server->curbuf == NULL) 596 return (NULL); 597 (void) memcpy(server->curbuf, result, rlen); 598 (void) munmap(result, rlen); 599 } 600 *len = rlen; 601 602 return (server->curbuf); 603 } 604