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 *
quitGetTrapHandler(void)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
quitSetZoneName(char * a_zoneName)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
quitSetDstreamTmpdir(char * a_dstreamTempDir)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
quitSetUpdatingExisting(boolean_t a_updatingExistingPackage)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
quitSetInstallStarted(boolean_t a_installStarted)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
quitSetPkgask(boolean_t a_pkgaskFlag)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
quitSetSilentExit(boolean_t a_silentExit)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
quit(int retcode)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] != NULL)
293 (void) rrmdir(tmpdir);
294
295 } else {
296 /* fix bug #1082589 that deletes root file */
297 if (tmpdir[0] != NULL) {
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
quitmsg(int retcode)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
mailmsg(int retcode)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
trap(int signo)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