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 28 #include <stdio.h> 29 #include <limits.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include <sys/stat.h> 33 #include <unistd.h> 34 #include <errno.h> 35 #include <utmpx.h> 36 #include <dirent.h> 37 #include <sys/types.h> 38 #include <locale.h> 39 #include <libintl.h> 40 #include <pkgstrct.h> 41 #include <pkglocs.h> 42 #include <assert.h> 43 #include <pkglib.h> 44 #include <install.h> 45 #include <libinst.h> 46 #include <libadm.h> 47 #include <messages.h> 48 #include <instzones_api.h> 49 50 extern int npkgs; /* the number of packages yet to be installed */ 51 52 /* 53 * ckquit is a global that controls 'ckyorn' (defined in libadm) 54 * If ckquit is non-zero, then "quit" is allowed as an answer when 55 * ckyorn is called. If is it zero, then "quit" is not an allowed answer. 56 */ 57 extern int ckquit; 58 59 extern struct admin adm; 60 61 /* 62 * each one of these represents a single kind of dependency check 63 */ 64 65 static depckError_t er_depsonme = {0, (depckErrorRecord_t *)NULL}; 66 static depckError_t er_prenci = {0, (depckErrorRecord_t *)NULL}; 67 static depckError_t er_prereq = {0, (depckErrorRecord_t *)NULL}; 68 static depckError_t er_rckdepend = {0, (depckErrorRecord_t *)NULL}; 69 static depckError_t er_rckpriv = {0, (depckErrorRecord_t *)NULL}; 70 static depckError_t er_rckrunlevel = {0, (depckErrorRecord_t *)NULL}; 71 static depckError_t er_runlevel = {0, (depckErrorRecord_t *)NULL}; 72 73 /* 74 * each one of these represents a localized message for a single kind 75 * of dependency check 76 */ 77 78 static char *IMSG_PKGRMCHK_CKRUNLVL = (char *)NULL; 79 static char *IMSG_PKGRMCHK_DEPEND = (char *)NULL; 80 static char *IMSG_PKGRMCHK_DEPSONME = (char *)NULL; 81 static char *IMSG_PKGRMCHK_PRENCI = (char *)NULL; 82 static char *IMSG_PKGRMCHK_PREREQ = (char *)NULL; 83 static char *IMSG_PKGRMCHK_PRIV = (char *)NULL; 84 static char *IMSG_PKGRMCHK_RUNLEVEL = (char *)NULL; 85 86 /* 87 * each one of these represents a function to handle a single kind of 88 * dependency check 89 */ 90 91 static int rckdepend(char *a_msg, char *a_pkg); 92 static int rckdepsonme(char *a_msg, char *a_pkg); 93 static int rckprenci(char *a_msg, char *a_pkg); 94 static int rckprereq(char *a_msg, char *a_pkg); 95 static int rckpriv(char *a_msg, char *a_pkg); 96 static int rckrunlevel(char *a_msg, char *a_pkg); 97 98 static depckl_t DEPCKL[] = { 99 /* 100 * Message hierarchy: 101 * -- runlevel=%s 102 * --- rckrunlevel=%d 103 * --- rckpriv=%d **** 104 * -- incompat=%s 105 * -- prerequisite-incomplete=%s 106 * -- dependonme=%s 107 * -- dependsonme=%s:%s 108 * -- prerequisite-installed=%s 109 * ---rckdepend=%d **** 110 */ 111 112 /* name, ignore_values, err_msg, depcklFunc, recrd */ 113 /* 114 * package and zone information is collected in the "record" object for 115 * each occurance - then a message is constructed for each zone that 116 * reported the condition - the message includes that portion of the 117 * check past the "=" - then the specified "depcklFunc" is called to 118 * process each message. 119 * Message format: 120 * %s %s <%s> %s <%s> 121 * Message arguments: 122 * value, "package", package-name, "zone/zones", zone-name 123 */ 124 125 { "dependsonme=", NULL, &IMSG_PKGRMCHK_DEPSONME, 126 &rckdepsonme, &er_depsonme 127 }, 128 { "dependonme=", NULL, &IMSG_PKGRMCHK_DEPSONME, 129 &rckdepsonme, &er_depsonme 130 }, 131 { "prerequisite-incomplete=", NULL, &IMSG_PKGRMCHK_PRENCI, 132 &rckprenci, &er_prenci 133 }, 134 { "prerequisite-installed=", NULL, &IMSG_PKGRMCHK_PREREQ, 135 &rckprereq, &er_prereq 136 }, 137 { "runlevel=", NULL, &IMSG_PKGRMCHK_RUNLEVEL, 138 NULL, &er_runlevel 139 }, 140 141 /* 142 * these checks are ignored if they return one of the listed values 143 * if they do NOT return one of the listed values, then the package 144 * and zone information is collected in the "record" object for each 145 * occurance - then a single unified message is constructed for all 146 * zones that report the same condition; then the specified "depcklFunc" 147 * is called to process the resulting combined message. 148 * Message format: 149 * %s <%s> %s <%s> 150 * Message arguments: 151 * "package", package-name, "zone/zones", zone-name(s) 152 */ 153 154 { "rckdepend=", "0", &IMSG_PKGRMCHK_DEPEND, 155 &rckdepend, &er_rckdepend 156 }, 157 { "rckpriv=", "0", &IMSG_PKGRMCHK_PRIV, 158 &rckpriv, &er_rckpriv 159 }, 160 { "rckrunlevel=", "0", &IMSG_PKGRMCHK_CKRUNLVL, 161 &rckrunlevel, &er_rckrunlevel 162 }, 163 164 /* 165 * same as above BUT no check to ignore is done; message always reported 166 */ 167 168 { NULL, NULL, NULL, 169 NULL, NULL 170 } 171 }; 172 173 /* 174 * Name: preremove_verify 175 * Description: verify results of preremoval dependency checking 176 * Arguments: a_pkglist - pointer to array of strings representing the names 177 * of all the packages that have been checked 178 * a_zlst - list of zones that dependencies were checked on 179 * a_zoneTempDir - pointer to string representing the path where 180 * the files containing the preremoval dependency 181 * check data are located 182 * Returns: int 183 * == 0 - continue processing 184 * != 0 - do not continue processing 185 */ 186 187 int 188 preremove_verify(char **a_pkglist, zoneList_t a_zlst, char *a_zoneTempDir) 189 { 190 char *pkginst; 191 int i; 192 int savenpkgs = npkgs; 193 194 /* 195 * entry assertions 196 */ 197 198 assert(a_pkglist != (char **)NULL); 199 assert(a_zlst != (zoneList_t)NULL); 200 assert(a_zoneTempDir != (char *)NULL); 201 202 /* 203 * entry debugging info 204 */ 205 206 echoDebug(DBG_PRERVFY_ENTRY); 207 208 /* 209 * localize messages 210 */ 211 212 IMSG_PKGRMCHK_DEPSONME = MSG_PKGRMCHK_DEPSONME; 213 IMSG_PKGRMCHK_PRENCI = MSG_PKGRMCHK_PRENCI; 214 IMSG_PKGRMCHK_PREREQ = MSG_PKGRMCHK_PREREQ; 215 IMSG_PKGRMCHK_RUNLEVEL = MSG_PKGRMCHK_RUNLEVEL; 216 IMSG_PKGRMCHK_DEPEND = MSG_PKGRMCHK_DEPEND; 217 IMSG_PKGRMCHK_PRIV = MSG_PKGRMCHK_PRIV; 218 IMSG_PKGRMCHK_CKRUNLVL = MSG_PKGRMCHK_CKRUNLVL; 219 220 /* 221 * outer loop - process each package first 222 */ 223 224 for (i = 0; (pkginst = a_pkglist[i]) != NULL; i++) { 225 226 char *zoneName; 227 int zoneIndex; 228 229 /* 230 * inner loop - for each package process each zone second 231 */ 232 233 if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) { 234 continue; 235 } 236 237 for (zoneIndex = 0; 238 (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != 239 (char *)NULL; zoneIndex++) { 240 241 FILE *fp; 242 char line[PATH_MAX+1]; 243 char preremovecheckPath[PATH_MAX+1]; 244 int len; 245 246 /* skip the zone if it is NOT bootable */ 247 248 if (z_zlist_is_zone_runnable(a_zlst, 249 zoneIndex) == B_FALSE) { 250 continue; 251 } 252 253 /* create path to this packages preremove check data */ 254 255 len = snprintf(preremovecheckPath, 256 sizeof (preremovecheckPath), 257 "%s/%s.%s.preremovecheck.txt", 258 a_zoneTempDir, pkginst, 259 z_zlist_get_scratch(a_zlst, zoneIndex)); 260 261 if (len > sizeof (preremovecheckPath)) { 262 progerr(ERR_CREATE_PATH_3, a_zoneTempDir, 263 pkginst, zoneName); 264 continue; 265 } 266 267 /* error if preremove check data path is not a file */ 268 269 if (isfile((char *)NULL, preremovecheckPath) != 0) { 270 echoDebug(DBG_PRERVFY_NOFILE, pkginst, zoneName, 271 preremovecheckPath, strerror(errno)); 272 progerr(ERR_PRERVFY_NOFILE, pkginst, zoneName); 273 continue; 274 } 275 276 /* open the preremove check data file */ 277 278 fp = fopen(preremovecheckPath, "r"); 279 if (fp == (FILE *)NULL) { 280 progerr(ERR_PRERVFY_OPEN_FILE, 281 preremovecheckPath, pkginst, zoneName, 282 strerror(errno)); 283 continue; 284 } 285 286 /* read and process each preremove check data line */ 287 288 while (fgets(line, sizeof (line), fp) != (char *)NULL) { 289 int len; 290 int j; 291 292 /* remove all new-lines from end of line */ 293 294 len = strlen(line); 295 while ((len > 0) && (line[len-1] == '\n')) { 296 line[--len] = '\0'; 297 } 298 299 /* ignore comment lines */ 300 301 if (line[0] == '#') { 302 continue; 303 } 304 305 /* ignore empty lines */ 306 307 if (line[0] == '\0') { 308 continue; 309 } 310 311 /* scan dependency list for this item */ 312 313 for (j = 0; 314 DEPCKL[j].name != (char *)NULL; j++) { 315 len = strlen(DEPCKL[j].name); 316 317 if (strncmp(line, DEPCKL[j].name, 318 len) == 0) { 319 break; 320 } 321 } 322 323 echoDebug(DBG_PRERVFY_SCAN, line, pkginst, 324 zoneName); 325 326 /* ignore line if not found */ 327 328 if (DEPCKL[j].name == (char *)NULL) { 329 progerr(ERR_PRERVFY_UNKNOWN_LINE, line, 330 pkginst, zoneName); 331 continue; 332 } 333 334 if ((DEPCKL[j].ignore_values != (char *)NULL) && 335 (*(DEPCKL[j].ignore_values) != '\0') && 336 (strchr(DEPCKL[j].ignore_values, 337 line[len]) != (char *)NULL)) { 338 continue; 339 } 340 /* found match - record this dependency issue */ 341 342 depchkRecordError(DEPCKL[j].record, pkginst, 343 zoneName, &line[len]); 344 } 345 346 /* close preremove check data file */ 347 348 (void) fclose(fp); 349 } 350 } 351 352 /* 353 * all dependency issues have been recorded; report results 354 */ 355 356 i = depchkReportErrors(DEPCKL); 357 358 /* restore "npkgs" */ 359 360 npkgs = savenpkgs; 361 362 /* return continue/dont dontinue results */ 363 364 return (i); 365 } 366 367 /* 368 * Name: getyorn 369 * Description: Deliver dependency check reason; ask question; return response 370 * Arguments: a_msg - pointer to string representing the message to output 371 * such as 'The package <..> contains <...>' 372 * a_pkg - pointer to string representing the package for which 373 * the question is being asked 374 * a_nocheck - should the message be output? 375 * == 0 - do not output the message 376 * != 0 - output the message 377 * a_quit - should the question NOT be asked? 378 * == 0 - ask the question 379 * != 0 - do not ask the question - return "no" 380 * a_helpMsg - pointer to string representing help message to be 381 * made available if the question is asked 382 * == NULL - no help message is available 383 * a_adminMsg - pointer to string representing the dependency check 384 * failure 'reason' - such as "Privilege checking failed." 385 * == NULL - no failure reason is available 386 * Returns: int - results of question/response actions 387 * 0 - success 388 * 1 - end of file 389 * 2 - undefined error 390 * 3 - answer was not "y"/was "q" 391 * 4 - quit action taken 392 * 5 - interactive mode required 393 */ 394 395 static int 396 getyorn(char *a_msg, char *a_pkg, int a_nocheck, int a_quit, 397 char *a_helpMsg, char *a_adminMsg) 398 { 399 char ans[MAX_INPUT]; 400 char ask_cont[MSG_MAX]; 401 int n; 402 int saveCkquit; 403 404 /* 405 * entry assertions 406 */ 407 408 assert(a_pkg != (char *)NULL); 409 assert(*a_pkg != '\0'); 410 411 /* 412 * entry debugging info 413 */ 414 415 echoDebug(DBG_PRERVFY_GETYORN_ARGS, a_pkg, a_nocheck, a_quit, a_msg, 416 a_adminMsg ? a_adminMsg : ""); 417 418 /* return success (0) if "nocheck" is non-zero */ 419 420 if (a_nocheck != 0) { 421 echoDebug(DBG_PRERVFY_GETYORN_NOCHECK, a_pkg); 422 return (0); 423 } 424 425 /* output reason for this particular failure */ 426 427 if ((a_msg != (char *)NULL) && (*a_msg != '\0')) { 428 ptext(stderr, "%s", a_msg); 429 } 430 431 /* return "4 (administration)" if "quit" is non-zero */ 432 433 if (a_quit != 0) { 434 /* output failure "admin reason" if available */ 435 if ((a_adminMsg != (char *)NULL) && (*a_adminMsg != '\0')) { 436 ptext(stderr, a_adminMsg); 437 } 438 echoDebug(DBG_PRERVFY_GETYORN_QUIT, a_pkg); 439 return (4); 440 } 441 442 /* return "5 (administration interaction required)" if -n */ 443 444 if (echoGetFlag() == B_FALSE) { 445 ptext(stderr, MSG_PRERVFY_GETYORN_SUSP, a_pkg); 446 echoDebug(DBG_PRERVFY_GETYORN_QUIT_USER, a_pkg); 447 return (5); 448 } 449 450 /* prepare question to ask "continue with removal of pkg <xxx>?" */ 451 452 (void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_PKGRMCHK_CONT), 453 a_pkg); 454 455 /* ask question */ 456 457 saveCkquit = ckquit; 458 ckquit = 0; 459 460 n = ckyorn(ans, NULL, NULL, a_helpMsg, ask_cont); 461 462 ckquit = saveCkquit; 463 464 if (n != 0) { 465 ptext(stderr, MSG_PRERVFY_GETYORN_TERM, a_pkg); 466 echoDebug(DBG_PRERVFY_GETYORN_CKYORN, a_pkg, n); 467 return (n); 468 } 469 470 /* return "3 (interruption) if not "y" or "Y" */ 471 472 if (strchr("yY", *ans) == NULL) { 473 ptext(stderr, MSG_PRERVFY_GETYORN_TERM_USER, a_pkg); 474 echoDebug(DBG_PRERVFY_GETYORN_NOT_Y, a_pkg, ans); 475 return (3); 476 } 477 478 /* return "0 - success" */ 479 480 echoDebug(DBG_PRERVFY_GETYORN_SUCCESS, a_pkg); 481 482 return (0); 483 } 484 485 /* 486 * Trigger: dependsonme=<<package>> 487 * Sequence: - one or more: dependsonme=<<package>> 488 * - one: rckdepend=<<n>> 489 * Actions: Output message if "rdepend!=nocheck" 490 * Return 0 491 * Terminate when 'rckdepend' processed 492 */ 493 494 static int 495 rckdepsonme(char *a_msg, char *a_pkg) 496 { 497 echoDebug(DBG_PRERVFY_RCKDEPSONME, a_pkg, a_msg); 498 499 if (!(ADM(rdepend, "nocheck"))) { 500 ptext(stderr, "%s", a_msg); 501 } 502 503 return (0); 504 } 505 506 /* 507 * Trigger: prerequisite-incomplete=<<package>> 508 * Sequence: - one or more: prerequisite-incomplete=<<package>> 509 * - one: rckdepend=<<n>> 510 * Actions: Output message if "rdepend!=nocheck" 511 * Return 0 512 * Terminate when 'rckdepend' processed 513 */ 514 515 static int 516 rckprenci(char *a_msg, char *a_pkg) 517 { 518 echoDebug(DBG_PRERVFY_RCKPRENCI, a_pkg, a_msg); 519 520 if (!(ADM(rdepend, "nocheck"))) { 521 ptext(stderr, "%s", a_msg); 522 } 523 524 return (0); 525 } 526 527 /* 528 * Trigger: prerequisite-installed=<<package>> 529 * Sequence: - one or more: prerequisite-installed=<<package>> 530 * - one: rckdepend=<<n>> 531 * Actions: Output message if "rdepend!=nocheck" 532 * Return 0 533 * Terminate when 'rckdepend' processed 534 */ 535 536 static int 537 rckprereq(char *a_msg, char *a_pkg) 538 { 539 echoDebug(DBG_PRERVFY_RCKPREREQ, a_pkg, a_msg); 540 541 if (!(ADM(rdepend, "nocheck"))) { 542 ptext(stderr, "%s", a_msg); 543 } 544 545 return (0); 546 } 547 548 /* 549 * Return value: int 550 * 0 - success 551 * 1 - end of file 552 * 2 - undefined error 553 * 3 - answer was not "y"/was "q" 554 * 4 - quit action taken 555 * 5 - interactive mode required 556 * 99 - fatal error 557 */ 558 559 static int 560 rckrunlevel(char *a_msg, char *a_pkg) 561 { 562 echoDebug(DBG_PRERVFY_RCKRUNLEVEL, a_pkg, a_msg); 563 /* 564 * For now, we are ignoring runlevel removal issues within 565 * non-global zones. This is questionable, but the RSTATES 566 * feature is rarely used and known uses within Solaris are 567 * effectively no-ops as of this time 568 */ 569 return (0); 570 } 571 572 /* 573 * Trigger: rckdepend=<<n>> 574 * Sequence: - one or more of: 575 * -- incompat=<<package>> 576 * -- prerequisite-incomplete=<<package>> 577 * -- prerequisite-installed=<<package>> 578 * -- dependson=<<package>> 579 * -- dependsonme=<<package>> 580 * - one: ckpdepend=<<n>> 581 * Actions: process according to settings 582 * Return value: int 583 * 0 - success 584 * 1 - end of file 585 * 2 - undefined error 586 * 3 - answer was not "y"/was "q" 587 * 4 - quit action taken 588 * 5 - interactive mode required 589 */ 590 591 static int 592 rckdepend(char *a_msg, char *a_pkg) 593 { 594 echoDebug(DBG_PRERVFY_RCKDEPEND, a_pkg, a_msg); 595 596 return (getyorn(a_msg, a_pkg, ADM(rdepend, "nocheck"), 597 ADM(rdepend, "quit"), HLP_PKGRMCHK_DEPEND, 598 ERR_PKGRMCHK_DEPFAILED)); 599 } 600 601 /* 602 * Trigger: rckpriv=<<n>> 603 * Sequence: - one: rckpriv=<<n>> 604 * Actions: process according to settings 605 * Return value: int 606 * 0 - success 607 * 1 - end of file 608 * 2 - undefined error 609 * 3 - answer was not "y"/was "q" 610 * 4 - quit action taken 611 * 5 - interactive mode required 612 */ 613 614 static int 615 rckpriv(char *a_msg, char *a_pkg) 616 { 617 echoDebug(DBG_PRERVFY_RCKPRIV, a_pkg, a_msg); 618 619 return (getyorn(a_msg, a_pkg, ADM(action, "nocheck"), 620 ADM(action, "quit"), HLP_PKGRMCHK_PRIV, 621 ERR_PKGRMCHK_PRIVFAILED)); 622 } 623