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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #include <stdio.h> 32 #include <fcntl.h> 33 #include <ctype.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <signal.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <pkginfo.h> 40 #include <pkgstrct.h> 41 #include <pkglocs.h> 42 #include <locale.h> 43 #include <libintl.h> 44 #include <instzones_api.h> 45 #include <pkglib.h> 46 #include <install.h> 47 #include <libadm.h> 48 #include <libinst.h> 49 #include "installf.h" 50 51 #define BASEDIR "/BASEDIR/" 52 53 #define INSTALF (*prog == 'i') 54 #define REMOVEF (*prog == 'r') 55 56 #define MSG_MANMOUNT "Assuming mounts were provided." 57 58 #define ERR_PKGNAME_TOO_LONG \ 59 "The package name specified on the command line\n" \ 60 "exceeds the maximum package name length: a package name may contain a\n" \ 61 "maximum of <%d> characters; however, the package name specified on\n" \ 62 "the command line contains <%d> characters, which exceeds the maximum\n" \ 63 "package name length by <%d> characters. Please specify a package name\n" \ 64 "that contains no more than <%d> characters." 65 66 #define ERR_DB_GET "unable to retrieve entries from the database." 67 #define ERR_DB_PUT "unable to update the package database." 68 #define ERR_ROOT_SET "Could not set install root from the environment." 69 #define ERR_ROOT_CMD "Command line install root contends with environment." 70 #define ERR_CLASSLONG "classname argument too long" 71 #define ERR_CLASSCHAR "bad character in classname" 72 #define ERR_INVAL "package instance <%s> is invalid" 73 #define ERR_NOTINST "package instance <%s> is not installed" 74 #define ERR_MERG "unable to merge contents file" 75 #define ERR_SORT "unable to sort contents file" 76 #define ERR_I_FAIL "installf did not complete successfully" 77 #define ERR_R_FAIL "removef did not complete successfully" 78 #define ERR_NOTROOT "You must be \"root\" for %s to execute properly." 79 #define ERR_USAGE0 "usage:\n" \ 80 "\t%s [[-M|-A] -R host_path] [-V ...] pkginst path " \ 81 "[path ...]\n" \ 82 "\t%s [[-M|-A] -R host_path] [-V ...] pkginst path\n" 83 84 #define ERR_USAGE1 "usage:\n" \ 85 "\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \ 86 "<path>\n" \ 87 "\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \ 88 "<path> <specs>\n" \ 89 "\t where <specs> may be defined as:\n" \ 90 "\t\tf <mode> <owner> <group>\n" \ 91 "\t\tv <mode> <owner> <group>\n" \ 92 "\t\te <mode> <owner> <group>\n" \ 93 "\t\td <mode> <owner> <group>\n" \ 94 "\t\tx <mode> <owner> <group>\n" \ 95 "\t\tp <mode> <owner> <group>\n" \ 96 "\t\tc <major> <minor> <mode> <owner> <group>\n" \ 97 "\t\tb <major> <minor> <mode> <owner> <group>\n" \ 98 "\t\ts <path>=<srcpath>\n" \ 99 "\t\tl <path>=<srcpath>\n" \ 100 "\t%s [[-M] -R host_path] [-V ...] [-c class] -f pkginst\n" 101 102 #define CMD_SORT "sort +0 -1" 103 104 #define LINK 1 105 106 extern char dbst; /* libinst/pkgdbmerg.c */ 107 108 struct cfextra **extlist; 109 struct pinfo **eptlist; 110 111 char *classname = NULL; 112 char *pkginst; 113 char *uniTmp; 114 char *abi_sym_ptr; 115 char *ulim; 116 char *script; 117 118 int eptnum; 119 int sortflag; 120 int nosetuid; 121 int nocnflct; 122 int warnflag = 0; 123 124 /* libadm/pkgparam.c */ 125 extern void set_PKGADM(char *newpath); 126 extern void set_PKGLOC(char *newpath); 127 128 extern void set_limit(void); 129 130 int 131 main(int argc, char **argv) 132 { 133 FILE *pp; 134 VFP_T *cfTmpVfp; 135 VFP_T *cfVfp; 136 char *cmd; 137 char *tp; 138 char *prog; 139 char *pt; 140 char *vfstab_file = NULL; 141 char line[1024]; 142 char outbuf[PATH_MAX]; 143 int c; 144 int dbchg; 145 int err; 146 int fflag = 0; 147 int map_client = 1; 148 int n; 149 int pkgrmremote = 0; /* don't remove remote files */ 150 struct cfent *ept; 151 152 /* hookup signals */ 153 154 (void) signal(SIGHUP, exit); 155 (void) signal(SIGINT, exit); 156 (void) signal(SIGQUIT, exit); 157 158 /* initialize locale mechanism */ 159 160 (void) setlocale(LC_ALL, ""); 161 162 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 163 #define TEXT_DOMAIN "SYS_TEST" 164 #endif /* !defined(TEXT_DOMAIN) */ 165 166 (void) textdomain(TEXT_DOMAIN); 167 168 /* determine program name */ 169 170 prog = set_prog_name(argv[0]); 171 172 /* tell instzones interface how to access package output functions */ 173 174 z_set_output_functions(echo, echoDebug, progerr); 175 176 /* only allow root to run this program */ 177 178 if (getuid() != 0) { 179 progerr(gettext(ERR_NOTROOT), prog); 180 exit(1); 181 } 182 183 ulim = getenv("PKG_ULIMIT"); 184 script = getenv("PKG_PROC_SCRIPT"); 185 186 if (ulim && script) { 187 set_limit(); 188 clr_ulimit(); 189 } 190 191 /* bug id 4244631, not ABI compliant */ 192 abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS"); 193 if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) 194 set_nonABI_symlinks(); 195 196 /* bugId 4012147 */ 197 if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL) 198 map_client = 0; 199 if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) { 200 progerr(gettext(ERR_ROOT_SET)); 201 exit(1); 202 } 203 204 while ((c = getopt(argc, argv, "c:V:fAMR:?")) != EOF) { 205 switch (c) { 206 case 'f': 207 fflag++; 208 break; 209 210 case 'c': 211 classname = optarg; 212 /* validate that classname is acceptable */ 213 if (strlen(classname) > (size_t)CLSSIZ) { 214 progerr(gettext(ERR_CLASSLONG)); 215 exit(1); 216 } 217 for (pt = classname; *pt; pt++) { 218 if (!isalpha(*pt) && !isdigit(*pt)) { 219 progerr(gettext(ERR_CLASSCHAR)); 220 exit(1); 221 } 222 } 223 break; 224 225 /* 226 * Don't map the client filesystem onto the server's. Assume 227 * the mounts have been made for us. 228 */ 229 case 'M': 230 map_client = 0; 231 break; 232 233 /* 234 * Allow admin to establish the client filesystem using a 235 * vfstab-like file of stable format. 236 */ 237 case 'V': 238 vfstab_file = flex_device(optarg, 2); 239 map_client = 1; 240 break; 241 242 case 'A': 243 pkgrmremote++; 244 break; 245 246 case 'R': /* added for newroot option */ 247 if (!set_inst_root(optarg)) { 248 progerr(gettext(ERR_ROOT_CMD)); 249 exit(1); 250 } 251 break; 252 253 default: 254 usage(); 255 /*NOTREACHED*/ 256 /* 257 * Although usage() calls a noreturn function, 258 * needed to add return (1); so that main() would 259 * pass compilation checks. The statement below 260 * should never be executed. 261 */ 262 return (1); 263 } 264 } 265 266 if (pkgrmremote && (!is_an_inst_root() || fflag || INSTALF)) { 267 usage(); 268 /*NOTREACHED*/ 269 } 270 271 /* 272 * Get the mount table info and store internally. 273 */ 274 if (get_mntinfo(map_client, vfstab_file)) 275 exit(1); 276 277 /* 278 * This function defines the standard /var/... directories used later 279 * to construct the paths to the various databases. 280 */ 281 (void) set_PKGpaths(get_inst_root()); 282 283 /* 284 * If this is being installed on a client whose /var filesystem is 285 * mounted in some odd way, remap the administrative paths to the 286 * real filesystem. This could be avoided by simply mounting up the 287 * client now; but we aren't yet to the point in the process where 288 * modification of the filesystem is permitted. 289 */ 290 if (is_an_inst_root()) { 291 int fsys_value; 292 293 fsys_value = fsys(get_PKGLOC()); 294 if (use_srvr_map_n(fsys_value)) 295 set_PKGLOC(server_map(get_PKGLOC(), fsys_value)); 296 297 fsys_value = fsys(get_PKGADM()); 298 if (use_srvr_map_n(fsys_value)) 299 set_PKGADM(server_map(get_PKGADM(), fsys_value)); 300 } 301 302 sortflag = 0; 303 304 /* 305 * get the package name and verify length is not too long 306 */ 307 308 pkginst = argv[optind++]; 309 if (pkginst == NULL) { 310 usage(); 311 /*NOTREACHED*/ 312 313 } 314 315 n = strlen(pkginst); 316 if (n > PKGSIZ) { 317 progerr(gettext(ERR_PKGNAME_TOO_LONG), PKGSIZ, n, n-PKGSIZ, 318 PKGSIZ); 319 usage(); 320 /*NOTREACHED*/ 321 } 322 323 /* 324 * The following is used to setup the environment. Note that the 325 * variable 'BASEDIR' is only meaningful for this utility if there 326 * is an install root, recorded in PKG_INSTALL_ROOT. Otherwise, this 327 * utility can create a file or directory anywhere unfettered by 328 * the basedir associated with the package instance. 329 */ 330 if ((err = set_basedirs(0, NULL, pkginst, 1)) != 0) 331 exit(err); 332 333 if (INSTALF) 334 mkbasedir(0, get_basedir()); 335 336 if (fflag) { 337 /* installf and removef must only have pkginst */ 338 if (optind != argc) { 339 usage(); 340 /*NOTREACHED*/ 341 } 342 } else { 343 /* 344 * installf and removef must have at minimum 345 * pkginst & pathname specified on command line 346 */ 347 if (optind >= argc) { 348 usage(); 349 /*NOTREACHED*/ 350 } 351 } 352 if (REMOVEF) { 353 if (classname) { 354 usage(); 355 } 356 } 357 if (pkgnmchk(pkginst, "all", 0)) { 358 progerr(gettext(ERR_INVAL), pkginst); 359 exit(1); 360 } 361 if (fpkginst(pkginst, NULL, NULL) == NULL) { 362 progerr(gettext(ERR_NOTINST), pkginst); 363 exit(1); 364 } 365 366 #ifdef ALLOW_EXCEPTION_PKG_LIST 367 /* 368 * ********************************************************************* 369 * this feature is removed starting with Solaris 10 - there is no built 370 * in list of packages that should be run "the old way" 371 * ********************************************************************* 372 */ 373 /* Until 2.9, set it from the execption list */ 374 if (pkginst && exception_pkg(pkginst, LINK)) 375 set_nonABI_symlinks(); 376 #endif 377 /* 378 * This maps the client filesystems into the server's space. 379 */ 380 if (map_client && !mount_client()) 381 logerr(gettext(MSG_MANMOUNT)); 382 383 /* open the package database (contents) file */ 384 385 if (!ocfile(&cfVfp, &cfTmpVfp, 0L)) { 386 quit(1); 387 } 388 389 if (fflag) { 390 dbchg = dofinal(cfVfp, cfTmpVfp, REMOVEF, classname, prog); 391 } else { 392 if (INSTALF) { 393 dbst = INST_RDY; 394 if (installf(argc-optind, &argv[optind])) 395 quit(1); 396 } else { 397 dbst = RM_RDY; 398 removef(argc-optind, &argv[optind]); 399 } 400 401 dbchg = pkgdbmerg(cfVfp, cfTmpVfp, extlist, 0); 402 if (dbchg < 0) { 403 progerr(gettext(ERR_MERG)); 404 quit(99); 405 } 406 } 407 408 if (dbchg) { 409 if ((n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1)) 410 == RESULT_WRN) { 411 warnflag++; 412 } else if (n == RESULT_ERR) { 413 quit(99); 414 } 415 } 416 417 relslock(); 418 419 if (REMOVEF && !fflag) { 420 for (n = 0; extlist[n]; n++) { 421 ept = &(extlist[n]->cf_ent); 422 423 /* Skip duplicated paths */ 424 if ((n > 0) && (strncmp(ept->path, 425 extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) { 426 continue; 427 } 428 429 if (!extlist[n]->mstat.shared) { 430 /* 431 * Only output paths that can be deleted. 432 * so need to skip if the object is owned 433 * by a remote server and removal is not 434 * being forced. 435 */ 436 if (ept->pinfo && 437 (ept->pinfo->status == SERVED_FILE) && 438 !pkgrmremote) 439 continue; 440 441 c = 0; 442 if (is_a_cl_basedir() && !is_an_inst_root()) { 443 c = strlen(get_client_basedir()); 444 (void) snprintf(outbuf, sizeof (outbuf), 445 "%s/%s\n", get_basedir(), 446 &(ept->path[c])); 447 } else if (is_an_inst_root()) { 448 (void) snprintf(outbuf, sizeof (outbuf), 449 "%s/%s\n", get_inst_root(), 450 &(ept->path[c])); 451 } else { 452 (void) snprintf(outbuf, sizeof (outbuf), 453 "%s\n", &(ept->path[c])); 454 } 455 canonize(outbuf); 456 (void) printf("%s", outbuf); 457 } 458 } 459 } else if (INSTALF && !fflag) { 460 for (n = 0; extlist[n]; n++) { 461 ept = &(extlist[n]->cf_ent); 462 463 if (strchr("dxcbp", ept->ftype)) { 464 tp = fixpath(ept->path); 465 (void) averify(1, &ept->ftype, 466 tp, &ept->ainfo); 467 } 468 } 469 } 470 471 /* Sort the contents files if needed */ 472 if (sortflag) { 473 int n; 474 475 warnflag += (ocfile(&cfVfp, &cfTmpVfp, 0L)) ? 0 : 1; 476 if (!warnflag) { 477 size_t len; 478 479 len = strlen(CMD_SORT) + strlen(get_PKGADM()) + 480 strlen("/contents") + 5; 481 cmd = (char *)malloc(len); 482 (void) snprintf(cmd, len, "%s %s/contents", 483 CMD_SORT, get_PKGADM()); 484 pp = popen(cmd, "r"); 485 if (pp == NULL) { 486 (void) vfpClose(&cfVfp); 487 (void) vfpClose(&cfTmpVfp); 488 free(cmd); 489 progerr(gettext(ERR_SORT)); 490 quit(1); 491 } 492 while (fgets(line, 1024, pp) != NULL) { 493 if (line[0] != DUP_ENTRY) { 494 vfpPuts(cfTmpVfp, line); 495 } 496 } 497 free(cmd); 498 (void) pclose(pp); 499 n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1); 500 if (n == RESULT_WRN) { 501 warnflag++; 502 } else if (n == RESULT_ERR) { 503 quit(99); 504 } 505 506 relslock(); /* Unlock the database. */ 507 } 508 } 509 510 z_destroyMountTable(); 511 512 quit(warnflag ? 1 : 0); 513 /* LINTED: no return */ 514 } 515 516 void 517 quit(int n) 518 { 519 char *prog = get_prog_name(); 520 521 unmount_client(); 522 523 if (ulim && script) { 524 if (REMOVEF) { 525 set_ulimit(script, gettext(ERR_R_FAIL)); 526 } else { 527 set_ulimit(script, gettext(ERR_I_FAIL)); 528 } 529 } 530 531 exit(n); 532 } 533 534 void 535 usage(void) 536 { 537 char *prog = get_prog_name(); 538 539 if (REMOVEF) { 540 (void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog); 541 } else { 542 (void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog); 543 } 544 exit(1); 545 } 546