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