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 2005 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 <string.h> 33 #include <signal.h> 34 #include <sys/utsname.h> 35 #include <limits.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <pkgdev.h> 39 #include <pkglocs.h> 40 #include <locale.h> 41 #include <libintl.h> 42 #include <errno.h> 43 #include <pkglib.h> 44 #include "install.h" 45 #include "dryrun.h" 46 #include "libadm.h" 47 #include "libinst.h" 48 #include "pkginstall.h" 49 #include "messages.h" 50 51 /* main.c */ 52 extern char *pkgdrtarg; 53 extern struct cfextra **extlist; 54 55 extern struct admin adm; 56 extern struct pkgdev pkgdev; /* holds info about the installation device */ 57 58 extern int dparts; 59 extern int dreboot; /* != 0 if reboot required after installation */ 60 extern int failflag; /* != 0 if fatal error has occurred (1) */ 61 extern int ireboot; /* != 0 if immediate reboot required */ 62 extern int warnflag; /* != 0 if non-fatal error has occurred (2) */ 63 64 extern char tmpdir[]; 65 extern char pkgloc[]; 66 extern char pkgloc_sav[]; 67 extern char *msgtext; 68 extern char *pkginst; 69 extern char *pkgname; 70 71 /* 72 * exported functions 73 */ 74 75 void quit(int retcode); 76 void quitSetZoneName(char *a_zoneName); 77 sighdlrFunc_t *quitGetTrapHandler(void); 78 79 /* 80 * forward declarations 81 */ 82 83 static void trap(int signo); 84 static void mailmsg(int retcode); 85 static void quitmsg(int retcode); 86 87 static boolean_t silentExit = B_FALSE; 88 static boolean_t pkgaskFlag = B_FALSE; 89 static boolean_t installStarted = B_FALSE; 90 static boolean_t updatingExistingPackage = B_FALSE; 91 92 static char *dstreamTempDir = (char *)NULL; 93 static char *zoneName = (char *)NULL; 94 static int includeZonename = 0; 95 static int trapEntered = 0; 96 97 /* 98 * ***************************************************************************** 99 * global external (public) functions 100 * ***************************************************************************** 101 */ 102 103 /* 104 * Name: quitGetTrapHandler 105 * Description: return address of this modules "signal trap" handler 106 * Arguments: void 107 * Returns: sighdlrFunc_t 108 * The address of the trap handler that can be passed to 109 * the signal() type system calls 110 */ 111 112 sighdlrFunc_t * 113 quitGetTrapHandler(void) 114 { 115 return (&trap); 116 } 117 118 /* 119 * Name: quitSetZoneName 120 * Description: set the zone name the program is running in 121 * Arguments: a_zoneName - pointer to string representing the name of the zone 122 * that the program is running in 123 * Returns: void 124 */ 125 126 void 127 quitSetZoneName(char *a_zoneName) 128 { 129 zoneName = a_zoneName; 130 if ((zoneName == (char *)NULL || *zoneName == '\0')) { 131 includeZonename = 0; 132 } else { 133 includeZonename = 1; 134 } 135 } 136 137 /* 138 * Name: quitSetDstreamTmpdir 139 * Description: set the name of a temporary directory that contains package 140 * streams to be removed when quit() is called 141 * Arguments: a_dstreamTempDir - pointer to string representing the path 142 * to the temporary directory to remove when quit() 143 * is called 144 * Returns: void 145 */ 146 147 void 148 quitSetDstreamTmpdir(char *a_dstreamTempDir) 149 { 150 dstreamTempDir = a_dstreamTempDir; 151 } 152 153 /* 154 * Name: quitSetUpdatingExisting 155 * Description: set the "updating existing" flag - used in conjunction 156 * with the "install started" flag to determine the type 157 * of cleanup to be done when quit() is called 158 * Arguments: a_updatingExistingPackage - indicates whether or not existing 159 * packages are being updated (B_TRUE) or new packages 160 * are being installed (B_FALSE) 161 * Returns: void 162 */ 163 164 void 165 quitSetUpdatingExisting(boolean_t a_updatingExistingPackage) 166 { 167 updatingExistingPackage = a_updatingExistingPackage; 168 } 169 170 /* 171 * Name: quitSetInstallStarted 172 * Description: set the "install started" flag - used in conjunction 173 * with the "updating existing" flag to determine the type 174 * of cleanup to be done when quit() is called, and the 175 * type of message to be output for the "reason" why quit() 176 * was called 177 * Arguments: a_installStarted - indicates whether or not installation 178 * has started 179 * Returns: void 180 */ 181 182 void 183 quitSetInstallStarted(boolean_t a_installStarted) 184 { 185 installStarted = a_installStarted; 186 } 187 188 /* 189 * Name: quitSetPkgask 190 * Description: set the "pkgask is being run" flag - used to determine 191 * the type of message to be output for the "reason" why 192 * quit() was called 193 * Arguments: a_pkgaskflag - indicates whether or not pkgask is being run 194 * Returns: void 195 */ 196 197 void 198 quitSetPkgask(boolean_t a_pkgaskFlag) 199 { 200 pkgaskFlag = a_pkgaskFlag; 201 } 202 203 /* 204 * Name: quitSetSilentExit 205 * Description: set the "silent exit" flag - if silent exit is TRUE, then 206 * no messages are output by quit() when it is called 207 * Arguments: a_silentExit - indicates whether or not silent exit is set 208 * Returns: void 209 */ 210 211 void 212 quitSetSilentExit(boolean_t a_silentExit) 213 { 214 silentExit = a_silentExit; 215 } 216 217 /* 218 * Name: quit 219 * Description: cleanup and exit 220 * Arguments: a_retcode - the code to use to determine final exit status; 221 * if this is NOT "99" and if a "ckreturnFunc" is 222 * set, then that function is called with a_retcode 223 * to set the final exit status. 224 * Valid values are: 225 * 0 - success 226 * 1 - package operation failed (fatal error) 227 * 2 - non-fatal error (warning) 228 * 3 - user selected quit (operation interrupted) 229 * 4 - admin settings prevented operation 230 * 5 - interaction required and -n (non-interactive) specified 231 * "10" is added to indicate "immediate reboot required" 232 * "20" is be added to indicate "reboot after install required" 233 * 99 - do not interpret the code - just exit "99" 234 * Returns: <<this function does not return - calls exit()>> 235 */ 236 237 void 238 quit(int retcode) 239 { 240 /* disable interrupts */ 241 242 (void) signal(SIGINT, SIG_IGN); 243 (void) signal(SIGHUP, SIG_IGN); 244 245 /* process return code if not quit(99) */ 246 247 if (retcode != 99) { 248 if ((retcode % 10) == 0) { 249 if (failflag) { 250 retcode += 1; 251 } else if (warnflag) { 252 retcode += 2; 253 } 254 } 255 256 if (ireboot) { 257 retcode = (retcode % 10) + 20; 258 } 259 if (dreboot) { 260 retcode = (retcode % 10) + 10; 261 } 262 } 263 264 /* if set remove dstream temporary directory */ 265 266 if (dstreamTempDir != (char *)NULL) { 267 echoDebug(DBG_REMOVING_DSTREAM_TMPDIR, dstreamTempDir); 268 (void) rrmdir(dstreamTempDir); 269 dstreamTempDir = (char *)NULL; 270 } 271 272 /* If we're in dryrun mode, write out the dryrun file(s). */ 273 if (in_dryrun_mode()) { 274 char exit_msg[200]; 275 set_dr_info(EXITCODE, retcode); 276 if (failflag || warnflag) { 277 set_dr_exitmsg(msgtext); 278 } else { 279 /* LINTED variable format specified */ 280 (void) snprintf(exit_msg, sizeof (exit_msg), 281 qreason(1, retcode, installStarted, 282 includeZonename), 283 (pkginst ? pkginst : "unknown"), 284 zoneName); 285 set_dr_exitmsg(exit_msg); 286 } 287 288 write_dryrun_file(extlist); 289 ptext(stderr, MSG_DRYRUN_DONE); 290 ptext(stderr, MSG_NOCHANGE); 291 292 if (tmpdir[0] != '\0') 293 (void) rrmdir(tmpdir); 294 295 } else { 296 /* fix bug #1082589 that deletes root file */ 297 if (tmpdir[0] != '\0') { 298 (void) rrmdir(tmpdir); 299 } 300 301 /* send mail to appropriate user list */ 302 mailmsg(retcode); 303 304 /* display message about this installation */ 305 quitmsg(retcode); 306 } 307 308 /* 309 * In the event that this quit() was called prior to completion of 310 * the task, do an unlockinst() just in case. 311 */ 312 unlockinst(); 313 314 /* Unmount anything that's our responsibility. */ 315 (void) unmount_client(); 316 317 /* 318 * No need to umount device since calling process 319 * was responsible for original mount 320 */ 321 322 if (!updatingExistingPackage) { 323 if (!installStarted && pkgloc[0]) { 324 /* 325 * install not yet started; if package install 326 * location is defined, remove the package. 327 */ 328 echoDebug(DBG_QUIT_REMOVING_PKGDIR, pkgloc); 329 330 (void) chdir("/"); 331 if (pkgloc[0]) { 332 (void) rrmdir(pkgloc); 333 } 334 } 335 } else { 336 if (!installStarted) { 337 /* 338 * If we haven't started, but have already done 339 * the <PKGINST>/install directory rename, then 340 * remove the new <PKGINST>/install directory 341 * and rename <PKGINST>/install.save back to 342 * <PKGINST>/install. 343 */ 344 if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) { 345 if (pkgloc[0] && !access(pkgloc, F_OK)) 346 (void) rrmdir(pkgloc); 347 if (rename(pkgloc_sav, pkgloc) == -1) { 348 progerr(ERR_PACKAGEBINREN, 349 pkgloc_sav, pkgloc); 350 } 351 } 352 } else { 353 if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) { 354 echoDebug(DBG_QUIT_REMOVING_PKGSAV, pkgloc_sav); 355 (void) rrmdir(pkgloc_sav); 356 } 357 } 358 } 359 360 /* 361 * pkginst can be null if an administration setting doesn't all 362 * the package to be installed. Make sure pkginst exeists before 363 * updating the DB 364 */ 365 366 if (dparts > 0) 367 ds_skiptoend(pkgdev.cdevice); 368 (void) ds_close(1); 369 370 /* Free the filesystem table. */ 371 fs_tab_free(); 372 373 /* Free the package information lists. */ 374 pinfo_free(); 375 376 /* Free all stragglers. */ 377 bl_free(BL_ALL); 378 (void) pathdup(NULL); 379 380 /* Free regfiles. */ 381 regfiles_free(); 382 383 /* final exit debugging message */ 384 385 echoDebug(DBG_EXIT_WITH_CODE, retcode); 386 387 exit(retcode); 388 /*NOTREACHED*/ 389 } 390 391 /* 392 * ***************************************************************************** 393 * static internal (private) functions 394 * ***************************************************************************** 395 */ 396 397 static void 398 quitmsg(int retcode) 399 { 400 if (silentExit == B_TRUE) { 401 return; 402 } 403 404 (void) putc('\n', stderr); 405 if (pkgaskFlag) { 406 ptext(stderr, qreason(0, retcode, installStarted, 407 includeZonename), zoneName); 408 } else if (pkginst) { 409 ptext(stderr, qreason(1, retcode, installStarted, 410 includeZonename), pkginst, zoneName); 411 } 412 413 if (retcode && !installStarted) { 414 ptext(stderr, MSG_NOCHANGE); 415 } 416 } 417 418 static void 419 mailmsg(int retcode) 420 { 421 struct utsname utsbuf; 422 FILE *pp; 423 char *cmd; 424 size_t len; 425 426 if (silentExit == B_TRUE) { 427 return; 428 } 429 430 if (!installStarted || pkgaskFlag || (adm.mail == NULL)) { 431 return; 432 } 433 434 len = strlen(adm.mail) + sizeof (MAILCMD) + 2; 435 cmd = calloc(len, sizeof (char)); 436 if (cmd == NULL) { 437 logerr(WRN_NOMAIL); 438 return; 439 } 440 441 (void) snprintf(cmd, len, "%s %s", MAILCMD, adm.mail); 442 if ((pp = popen(cmd, "w")) == NULL) { 443 logerr(WRN_NOMAIL); 444 return; 445 } 446 447 if (msgtext) 448 ptext(pp, msgtext); 449 450 (void) strcpy(utsbuf.nodename, MSG_NODENAME); 451 (void) uname(&utsbuf); 452 453 ptext(pp, qreason(2, retcode, installStarted, includeZonename), 454 pkgname, utsbuf.nodename, pkginst, zoneName); 455 456 if (pclose(pp)) { 457 logerr(WRN_FLMAIL); 458 } 459 } 460 461 /* 462 * Name: trap 463 * Description: signal handler connected via quitGetTrapHandler() 464 * Arguments: signo - [RO, *RO] - (int) 465 * Integer representing the signal that caused the trap 466 * to this function to occur 467 * Returns: << NONE >> 468 * NOTE: This function exits the program after doing mandatory cleanup. 469 * NOTE: Even though quit() should NOT return, there is a call to _exit() 470 * put after each call to quit() just in case quit() ever returned 471 * by mistake. 472 */ 473 474 static void 475 trap(int signo) 476 { 477 /* prevent reentrance */ 478 479 if (trapEntered++ != 0) { 480 return; 481 } 482 483 if ((signo == SIGINT) || (signo == SIGHUP)) { 484 quit(3); 485 _exit(3); 486 } 487 quit(1); 488 _exit(1); 489 } 490