xref: /illumos-gate/usr/src/cmd/svr4pkg/pkgremove/main.c (revision 5bb0bdfe588c5df0f63ff8ac292cd608a5f4492a)
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 (c) 2018 Peter Tribble.
24  */
25 
26 /*
27  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
28  */
29 
30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
31 /* All Rights Reserved */
32 
33 
34 #include <stdio.h>
35 #include <limits.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <pkgstrct.h>
45 #include <pkginfo.h>
46 #include <pkglocs.h>
47 #include <locale.h>
48 #include <libintl.h>
49 #include <assert.h>
50 #include <cfext.h>
51 #include <instzones_api.h>
52 #include <pkglib.h>
53 #include <install.h>
54 #include <libinst.h>
55 #include <libadm.h>
56 #include <messages.h>
57 
58 struct cfent **eptlist;
59 extern int	eptnum;
60 
61 extern char	*pkgdir;
62 extern char	**environ;
63 
64 /* quit.c */
65 extern sighdlrFunc_t	*quitGetTrapHandler(void);
66 extern void		quitSetSilentExit(boolean_t a_silentExit);
67 extern void		quitSetZoneName(char *a_zoneName);
68 
69 
70 
71 /* check.c */
72 extern void	rcksetPreremoveCheck(boolean_t);
73 extern void	rcksetZoneName(char *);
74 extern int	rckpriv(void);
75 extern int	rckdepend(void);
76 extern int	rckrunlevel(void);
77 
78 /* delmap.c */
79 extern int delmap(int flag, char *pkginst, PKGserver *server, VFP_T **tfp);
80 
81 #define	DEFPATH		"/sbin:/usr/sbin:/usr/bin"
82 
83 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
84 #define	TEXT_DOMAIN "SYS_TEST"
85 #endif
86 
87 /* This is the text for the "-O parent-zone-name=" option */
88 
89 #define	PARENTZONENAME	"parent-zone-name="
90 #define	PARENTZONENAME_LEN	((sizeof (PARENTZONENAME))-1)
91 
92 /* This is the text for the "-O parent-zone-type=" option */
93 
94 #define	PARENTZONETYPE	"parent-zone-type="
95 #define	PARENTZONETYPE_LEN	((sizeof (PARENTZONETYPE))-1)
96 
97 struct	admin adm; 	/* holds info about installation admin */
98 int	dreboot; 	/* non-zero if reboot required after installation */
99 int	ireboot;	/* non-zero if immediate reboot required */
100 int	failflag;	/* non-zero if fatal error has occurred */
101 int	warnflag;	/* non-zero if non-fatal error has occurred */
102 int	pkgverbose;	/* non-zero if verbose mode is selected */
103 int	started;
104 int	nocnflct = 0; 	/* pkgdbmerg needs this defined */
105 int	nosetuid = 0; 	/* pkgdbmerg needs this defined */
106 
107 char	*pkginst; 	/* current package (source) instance to process */
108 
109 int	dbchg;
110 char	*msgtext;
111 char	pkgloc[PATH_MAX];
112 
113 /*
114  * The following variable is the name of the device to which stdin
115  * is connected during execution of a procedure script. /dev/null is
116  * correct for all ABI compliant packages. For non-ABI-compliant
117  * packages, the '-o' command line switch changes this to /dev/tty
118  * to allow user interaction during these scripts. -- JST
119  */
120 static char 	*script_in = PROC_STDIN;	/* assume ABI compliance */
121 
122 static char	*client_mntdir; 	/* mount point for client's basedir */
123 static char	pkgbin[PATH_MAX],
124 		rlockfile[PATH_MAX],
125 		*admnfile, 		/* file to use for installation admin */
126 		*tmpdir; 		/* location to place temporary files */
127 
128 static void		ckreturn(int retcode, char *msg);
129 static void		rmclass(char *aclass, int rm_remote, char *a_zoneName);
130 static void		usage(void);
131 
132 /*
133  * Set by -O debug: debug output is enabled?
134  */
135 static boolean_t	debugFlag = B_FALSE;
136 
137 /*
138  * Set by -O preremovecheck: do remove dependency checking only
139  */
140 static boolean_t	preremoveCheck = B_FALSE;
141 
142 /* Set by -O parent-zone-name= */
143 
144 static char		*parentZoneName = (char *)NULL;
145 
146 /* Set by -O parent-zone-type= */
147 
148 static char		*parentZoneType = (char *)NULL;
149 
150 static int	nointeract;	/* != 0 no interaction with user should occur */
151 
152 
153 
154 int
155 main(int argc, char *argv[])
156 {
157 	FILE		*fp;
158 	char		*abi_comp_ptr;
159 	char		*abi_sym_ptr;
160 	char		*p;
161 	char		*prog_full_name = NULL;
162 	char		*pt;
163 	char		*value;
164 	char		*vfstab_file = NULL;
165 	char		*zoneName = (char *)NULL;
166 	char		cmdbin[PATH_MAX];
167 	char		param[MAX_PKG_PARAM_LENGTH];
168 	char		path[PATH_MAX];
169 	char		script[PATH_MAX];
170 	int		c;
171 	int		err;
172 	int		fd;
173 	int		i;
174 	int		map_client = 1;
175 	int		n;
176 	int		nodelete = 0; 	/* do not delete file or run scripts */
177 	int		pkgrmremote = 0;	/* dont remove remote objects */
178 	struct sigaction	nact;
179 	struct sigaction	oact;
180 	PKGserver	pkgserver = NULL;
181 	VFP_T		*tmpfp;
182 
183 	/* reset contents of all default paths */
184 
185 	(void) memset(cmdbin, '\0', sizeof (cmdbin));
186 
187 	/* initialize locale environment */
188 
189 	(void) setlocale(LC_ALL, "");
190 	(void) textdomain(TEXT_DOMAIN);
191 
192 	/* initialize program name */
193 
194 	prog_full_name = argv[0];
195 	(void) set_prog_name(argv[0]);
196 
197 	/* tell spmi zones interface how to access package output functions */
198 
199 	z_set_output_functions(echo, echoDebug, progerr);
200 
201 	/* exit if not root */
202 
203 	if (getuid()) {
204 		progerr(ERR_NOT_ROOT, get_prog_name());
205 		exit(1);
206 		/* NOTREACHED */
207 	}
208 
209 	/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
210 
211 	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
212 		progerr(ERR_ROOT_SET);
213 		exit(1);
214 	}
215 
216 	pkgserversetmode(DEFAULTMODE);
217 
218 	/* parse command line options */
219 
220 	while ((c = getopt(argc, argv, "?Aa:b:FMN:nO:oR:V:vy")) != EOF) {
221 		switch (c) {
222 		/*
223 		 * Same as pkgrm: Allow admin to remove package objects from
224 		 * a shared area from a reference client.
225 		 */
226 		case 'A':
227 			pkgrmremote++;
228 			break;
229 
230 		/*
231 		 * Same as pkgrm: Use the installation
232 		 * administration file, admin, in place of the
233 		 * default admin file. pkgrm first looks in the
234 		 * current working directory for the administration
235 		 * file.  If the specified administration file is not
236 		 * in the current working directory, pkgrm looks in
237 		 * the /var/sadm/install/admin directory for the
238 		 * administration file.
239 		 */
240 		case 'a':
241 			admnfile = flex_device(optarg, 0);
242 			break;
243 
244 		/*
245 		 * Same as pkgrm: location where package executables
246 		 * can be found - default is /usr/sadm/install/bin.
247 		 */
248 		case 'b':
249 			if (!path_valid(optarg)) {
250 				progerr(ERR_PATH, optarg);
251 				exit(1);
252 			}
253 			if (isdir(optarg) != 0) {
254 				char *p = strerror(errno);
255 				progerr(ERR_CANNOT_USE_DIR, optarg, p);
256 				exit(1);
257 			}
258 			(void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
259 			break;
260 
261 		/*
262 		 * Same as pkgrm: suppresses the removal of any
263 		 * files and any class action scripts, and suppresses
264 		 * the running of any class action scripts.  The
265 		 * package files remain but the package looks like it
266 		 * is not installed. This is mainly for use by the
267 		 * upgrade process.
268 		 */
269 		case 'F':
270 			nodelete++;
271 			break;
272 
273 		/*
274 		 * Same as pkgrm: Instruct pkgrm not to use the
275 		 * $root_path/etc/vfstab file for determining the
276 		 * client's mount points. This option assumes the
277 		 * mount points are correct on the server and it
278 		 * behaves consistently with Solaris 2.5 and earlier
279 		 * releases.
280 		 */
281 		case 'M':
282 			map_client = 0;
283 			break;
284 
285 		/*
286 		 * Different from pkgrm: specify program name to use
287 		 * for messages.
288 		 */
289 		case 'N':
290 			(void) set_prog_name(optarg);
291 			break;
292 
293 		/*
294 		 * Same as pkgrm: package removal occurs in
295 		 * non-interactive mode.  Suppress output of the list of
296 		 * removed files. The default mode is interactive.
297 		 */
298 		case 'n':
299 			nointeract++;
300 			(void) echoSetFlag(B_FALSE);
301 			break;
302 
303 		/*
304 		 * Almost same as pkgrm: the -O option allows the behavior
305 		 * of the package tools to be modified. Recognized options:
306 		 * -> debug
307 		 * ---> enable debugging output
308 		 * -> preremovecheck
309 		 * ---> perform a "pre removal" check of the specified
310 		 * ---> package - suppress all regular output and cause a
311 		 * ---> series of one or more "name=value" pair format lines
312 		 * ---> to be output that describes the "removability" of
313 		 * ---> the specified package
314 		 * -> enable-hollow-package-support
315 		 * --> Enable hollow package support. When specified, for any
316 		 * --> package that has SUNW_PKG_HOLLOW=true:
317 		 * --> Do not calculate and verify package size against target
318 		 * --> Do not run any package procedure or class action scripts
319 		 * --> Do not create or remove any target directories
320 		 * --> Do not perform any script locking
321 		 * --> Do not install or uninstall any components of any package
322 		 * --> Do not output any status or database update messages
323 		 */
324 		case 'O':
325 			for (p = strtok(optarg, ","); p != (char *)NULL;
326 			    p = strtok(NULL, ",")) {
327 
328 				/* process debug option */
329 
330 				if (strcmp(p, "debug") == 0) {
331 					/* set debug flag/enable debug output */
332 					debugFlag = B_TRUE;
333 					(void) echoDebugSetFlag(debugFlag);
334 
335 					/* debug info on arguments to pkgadd */
336 					for (n = 0; n < argc && argv[n]; n++) {
337 						echoDebug(DBG_ARG, n, argv[n]);
338 					}
339 
340 					continue;
341 				}
342 
343 				/* process enable-hollow-package-support opt */
344 
345 				if (strcmp(p,
346 				    "enable-hollow-package-support") == 0) {
347 					set_depend_pkginfo_DB(B_TRUE);
348 					continue;
349 				}
350 
351 				/* process preremovecheck option */
352 
353 				if (strcmp(p, "preremovecheck") == 0) {
354 					preremoveCheck = B_TRUE;
355 					nointeract++;	/* -n */
356 					nodelete++;	/* -F */
357 					quitSetSilentExit(B_TRUE);
358 					continue;
359 				}
360 
361 				/* process addzonename option */
362 
363 				if (strcmp(p, "addzonename") == 0) {
364 					zoneName = z_get_zonename();
365 					quitSetZoneName(zoneName);
366 					continue;
367 				}
368 
369 				/* process parent-zone-name option */
370 
371 				if (strncmp(p, PARENTZONENAME,
372 				    PARENTZONENAME_LEN) == 0) {
373 					parentZoneName = p+PARENTZONENAME_LEN;
374 					continue;
375 				}
376 
377 				/* process parent-zone-type option */
378 
379 				if (strncmp(p, PARENTZONETYPE,
380 				    PARENTZONETYPE_LEN) == 0) {
381 					parentZoneType = p+PARENTZONETYPE_LEN;
382 					continue;
383 				}
384 
385 				if (strncmp(p, PKGSERV_MODE,
386 				    PKGSERV_MODE_LEN) == 0) {
387 					pkgserversetmode(pkgparsemode(p +
388 					    PKGSERV_MODE_LEN));
389 					continue;
390 				}
391 				/* option not recognized - issue warning */
392 
393 				progerr(ERR_INVALID_O_OPTION, p);
394 				continue;
395 			}
396 			break;
397 
398 		/*
399 		 * Different from pkgrm: This is an old non-ABI package
400 		 */
401 
402 		case 'o':
403 			script_in = PROC_XSTDIN;
404 			break;
405 
406 		/*
407 		 * Same as pkgrm: defines the full path name of a
408 		 * directory to use as the root_path.  All files,
409 		 * including package system information files, are
410 		 * relocated to a directory tree starting in the
411 		 * specified root_path.
412 		 */
413 		case 'R':
414 			if (!set_inst_root(optarg)) {
415 				progerr(ERR_ROOT_CMD);
416 				exit(1);
417 			}
418 			break;
419 
420 		/*
421 		 * Same as pkgrm: allow admin to establish the client
422 		 * filesystem using a vfstab-like file of stable format.
423 		 */
424 		case 'V':
425 			vfstab_file = flex_device(optarg, 2);
426 			map_client = 1;
427 			break;
428 
429 		/*
430 		 * Same as pkgrm: trace all of the scripts that
431 		 * get executed by pkgrm, located in the
432 		 * pkginst/install directory. This option is used for
433 		 * debugging the procedural and non-procedural
434 		 * scripts.
435 		 */
436 		case 'v':
437 			pkgverbose++;
438 			break;
439 
440 		/*
441 		 * Different from pkgrm: process this package using
442 		 * old non-ABI symlinks
443 		 */
444 		case 'y':
445 			set_nonABI_symlinks();
446 			break;
447 
448 		default:
449 			usage();
450 			/*NOTREACHED*/
451 			/*
452 			 * Although usage() calls a noreturn function,
453 			 * needed to add return (1);  so that main() would
454 			 * pass compilation checks. The statement below
455 			 * should never be executed.
456 			 */
457 			return (1);
458 		}
459 	}
460 
461 	/*
462 	 * ********************************************************************
463 	 * validate command line options
464 	 * ********************************************************************
465 	 */
466 
467 	(void) echoDebugSetFlag(debugFlag);
468 	(void) log_set_verbose(debugFlag);
469 
470 	if (z_running_in_global_zone()) {
471 		echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
472 	} else {
473 		echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
474 		    z_get_zonename());
475 	}
476 
477 	/* establish cmdbin path */
478 
479 	if (cmdbin[0] == '\0') {
480 		(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
481 	}
482 
483 	/* Read the mount table */
484 
485 	if (get_mntinfo(map_client, vfstab_file)) {
486 		quit(99);
487 	}
488 
489 	/*
490 	 * This function defines the standard /var/... directories used later
491 	 * to construct the paths to the various databases.
492 	 */
493 
494 	set_PKGpaths(get_inst_root());
495 
496 	/*
497 	 * If this is being removed from a client whose /var filesystem is
498 	 * mounted in some odd way, remap the administrative paths to the
499 	 * real filesystem. This could be avoided by simply mounting up the
500 	 * client now; but we aren't yet to the point in the process where
501 	 * modification of the filesystem is permitted.
502 	 */
503 	if (is_an_inst_root()) {
504 		int fsys_value;
505 
506 		fsys_value = fsys(get_PKGLOC());
507 		if (use_srvr_map_n(fsys_value))
508 			set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
509 
510 		fsys_value = fsys(get_PKGADM());
511 		if (use_srvr_map_n(fsys_value))
512 			set_PKGADM(server_map(get_PKGADM(), fsys_value));
513 	} else {
514 		pkgrmremote = 0;	/* Makes no sense on local host. */
515 	}
516 
517 	/*
518 	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
519 	 */
520 
521 	/* hold SIGINT/SIGHUP interrupts */
522 
523 	(void) sighold(SIGHUP);
524 	(void) sighold(SIGINT);
525 
526 	/* connect quit.c:trap() to SIGINT */
527 
528 	nact.sa_handler = quitGetTrapHandler();
529 	nact.sa_flags = SA_RESTART;
530 	(void) sigemptyset(&nact.sa_mask);
531 
532 	(void) sigaction(SIGINT, &nact, &oact);
533 
534 	/* connect quit.c:trap() to SIGHUP */
535 
536 	nact.sa_handler = quitGetTrapHandler();
537 	nact.sa_flags = SA_RESTART;
538 	(void) sigemptyset(&nact.sa_mask);
539 
540 	(void) sigaction(SIGHUP, &nact, &oact);
541 
542 	/* release hold on signals */
543 
544 	(void) sigrelse(SIGHUP);
545 	(void) sigrelse(SIGINT);
546 
547 	pkginst = argv[optind++];
548 	if (optind != argc) {
549 		usage();
550 	}
551 
552 	/* validate package software database (contents) file */
553 
554 	if (vcfile() == 0) {
555 		quit(99);
556 	}
557 
558 	/*
559 	 * Acquire the package lock - currently at "remove initialization"
560 	 */
561 
562 	if (!lockinst(get_prog_name(), pkginst, "remove-initial")) {
563 		quit(99);
564 	}
565 
566 	/* establish temporary directory to use */
567 
568 	tmpdir = getenv("TMPDIR");
569 	if (tmpdir == NULL) {
570 		tmpdir = P_tmpdir;
571 	}
572 
573 	echoDebug(DBG_PKGREMOVE_TMPDIR, tmpdir);
574 
575 	/*
576 	 * Initialize installation admin parameters by reading
577 	 * the adminfile.
578 	 */
579 
580 	echoDebug(DBG_PKGREMOVE_ADMINFILE, admnfile ? admnfile : "");
581 	setadminFile(admnfile);
582 
583 	/*
584 	 * about to perform first operation that could be modified by the
585 	 * preremove check option - if preremove check is selected (that is,
586 	 * only gathering dependencies), then output a debug message to
587 	 * indicate that the check is beginning. Also turn echo() output
588 	 * off and set various other flags.
589 	 */
590 
591 	if (preremoveCheck == B_TRUE) {
592 		(void) echoSetFlag(B_FALSE);
593 		echoDebug(DBG_PKGREMOVE_PRERMCHK, pkginst ? pkginst : "",
594 		    zoneName ? zoneName : "global");
595 		rcksetPreremoveCheck(B_TRUE);
596 		rcksetZoneName(zoneName);
597 	}
598 
599 	(void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s", get_PKGLOC(),
600 	    pkginst);
601 	(void) snprintf(pkgbin, sizeof (pkgbin), "%s/install", pkgloc);
602 	(void) snprintf(rlockfile, sizeof (rlockfile), "%s/!R-Lock!", pkgloc);
603 
604 	if (chdir(pkgbin)) {
605 		progerr(ERR_CHDIR, pkgbin);
606 		quit(99);
607 	}
608 
609 	echo(MSG_PREREMOVE_REMINST, pkginst);
610 
611 	/*
612 	 * if a lock file is present, then a previous attempt to remove this
613 	 * package may have been unsuccessful.
614 	 */
615 
616 	if (access(rlockfile, F_OK) == 0) {
617 		echo(ERR_UNSUCC);
618 		echoDebug(DBG_PKGINSTALL_HAS_LOCKFILE, pkginst, rlockfile,
619 		    zoneName ? zoneName : "global");
620 	}
621 
622 	/*
623 	 * Process all parameters from the pkginfo file
624 	 * and place them in the execution environment
625 	 */
626 
627 	/* Add DB retreival of the pkginfo parameters here */
628 	(void) snprintf(path, sizeof (path), "%s/pkginfo", pkgloc);
629 	if ((fp = fopen(path, "r")) == NULL) {
630 		progerr(ERR_PKGINFO, path);
631 		quit(99);
632 	}
633 
634 	/* Mount up the client if necessary. */
635 	if (map_client && !mount_client()) {
636 		logerr(MSG_MANMOUNT);
637 	}
638 
639 	/* Get mount point of client */
640 	client_mntdir = getenv("CLIENT_MNTDIR");
641 
642 	getuserlocale();
643 
644 	/*
645 	 * current environment has been read; clear environment out
646 	 * so putparam() can be used to populate the new environment
647 	 * to be passed to any executables/scripts.
648 	 */
649 
650 	environ = NULL;
651 
652 	if (nonABI_symlinks()) {
653 		putparam("PKG_NONABI_SYMLINKS", "TRUE");
654 	}
655 
656 	/*
657 	 * read the pkginfo file and fix any PKGSAV path - the correct
658 	 * install_root will be prepended to the existing path.
659 	 */
660 
661 	param[0] = '\0';
662 	while (value = fpkgparam(fp, param)) {
663 		int validx = 0;
664 		char *newvalue;
665 
666 		/* strip out any setting of PATH */
667 
668 		if (strcmp(param, "PATH") == 0) {
669 			free(value);
670 			param[0] = '\0';
671 			continue;
672 		}
673 
674 		/* if not PKGSAV then write out unchanged */
675 
676 		if (strcmp(param, "PKGSAV") != 0) {
677 			putparam(param, value);
678 			free(value);
679 			param[0] = '\0';
680 			continue;
681 		}
682 
683 		/*
684 		 * PKGSAV parameter found - interpret the directory:
685 		 * If in host:path format or marked with the leading "//",
686 		 * then there is no client-relative translation - take it
687 		 * literally later rather than use fixpath().
688 		 */
689 
690 		if (strstr(value, ":/")) {
691 			/* no modification needed */
692 			validx = 0;
693 		} else if (strstr(value, "//") == value) {
694 			validx = 1;
695 		} else if (is_an_inst_root()) {
696 			/* This PKGSAV needs to be made client-relative. */
697 			newvalue = fixpath(value);
698 			free(value);
699 			value = newvalue;
700 		}
701 		putparam(param, value+validx);
702 		free(value);
703 		param[0] = '\0';
704 	}
705 
706 	(void) fclose(fp);
707 
708 	/* write parent condition information to environment */
709 
710 	putConditionInfo(parentZoneName, parentZoneType);
711 
712 	putuserlocale();
713 
714 	/*
715 	 * Now do all the various setups based on ABI compliance
716 	 */
717 
718 	/* Read the environment provided by the pkginfo file */
719 	abi_comp_ptr = getenv("NONABI_SCRIPTS");
720 
721 	/* if not ABI compliant set global flag */
722 	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
723 	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
724 		set_nonABI_symlinks();
725 	}
726 
727 	/*
728 	 * If pkginfo says it's not compliant then set non_abi_scripts.
729 	 */
730 	if (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0) {
731 		script_in = PROC_XSTDIN;
732 	}
733 
734 	/*
735 	 * Since this is a removal, we can tell whether it's absolute or
736 	 * not from the resident pkginfo file read above.
737 	 */
738 	if ((err = set_basedirs((getenv("BASEDIR") != NULL), adm.basedir,
739 	    pkginst, nointeract)) != 0) {
740 		quit(err);
741 	}
742 
743 	/*
744 	 * See if were are removing a package that only wants to update
745 	 * the database or only remove files associated with CAS's. We
746 	 * only check the PKG_HOLLOW_VARIABLE variable if told to do so by
747 	 * the caller.
748 	 */
749 
750 	if (is_depend_pkginfo_DB()) {
751 		pt = getenv(PKG_HOLLOW_VARIABLE);
752 
753 		if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
754 			echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
755 
756 			/*
757 			 * this is a hollow package and hollow package support
758 			 * is enabled -- override admin settings to suppress
759 			 * checks that do not make sense since no scripts will
760 			 * be executed and no files will be removed.
761 			 */
762 
763 			setadminSetting("conflict", "nocheck");
764 			setadminSetting("setuid", "nocheck");
765 			setadminSetting("action", "nocheck");
766 			setadminSetting("partial", "nocheck");
767 			setadminSetting("space", "nocheck");
768 			setadminSetting("authentication", "nocheck");
769 		} else {
770 			echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
771 			set_depend_pkginfo_DB(B_FALSE);
772 		}
773 	}
774 
775 	put_path_params();
776 
777 	/* If client mount point, add it to pkgremove environment */
778 
779 	if (client_mntdir != NULL) {
780 		putparam("CLIENT_MNTDIR", client_mntdir);
781 	}
782 
783 	/* Establish the class list and the class attributes. */
784 
785 	if ((value = getenv("CLASSES")) != NULL) {
786 		cl_sets(qstrdup(value));
787 	} else {
788 		progerr(ERR_CLASSES, path);
789 		quit(99);
790 	}
791 
792 	/* establish path and tmpdir */
793 
794 	if (cmdbin[0] == '\0') {
795 		(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
796 	}
797 
798 	(void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
799 	putparam("PATH", path);
800 
801 	putparam("TMPDIR", tmpdir);
802 
803 	/*
804 	 * Check ulimit requirement (provided in pkginfo). The purpose of
805 	 * this limit is to terminate pathological file growth resulting from
806 	 * file edits in scripts. It does not apply to files in the pkgmap
807 	 * and it does not apply to any database files manipulated by the
808 	 * installation service.
809 	 */
810 	if (value = getenv("ULIMIT")) {
811 		if (assign_ulimit(value) == -1) {
812 			progerr(ERR_BADULIMIT, value);
813 			warnflag++;
814 		}
815 		putparam("PKG_ULIMIT", "TRUE");
816 	}
817 
818 	/*
819 	 * If only gathering dependencies, check and output status of all
820 	 * remaining dependencies and exit.
821 	 */
822 
823 	if (preremoveCheck == B_TRUE) {
824 		/*
825 		 * make sure current runlevel is appropriate
826 		 */
827 
828 		(void) fprintf(stdout, "rckrunlevel=%d\n", rckrunlevel());
829 
830 		/*
831 		 * determine if any packaging scripts provided with
832 		 * this package will execute as a priviledged user
833 		 */
834 
835 		(void) fprintf(stdout, "rckpriv=%d\n", rckpriv());
836 
837 		/*
838 		 * verify package dependencies
839 		 */
840 
841 		(void) fprintf(stdout, "rckdepend=%d\n", rckdepend());
842 
843 		/*
844 		 * ****** preremove check done - exit ******
845 		 */
846 
847 		echoDebug(DBG_PKGREMOVE_PRERMCHK_OK);
848 		quit(0);
849 		/*NOTREACHED*/
850 	}
851 
852 	/*
853 	 * Not gathering dependencies only, proceed to check dependencies
854 	 * and continue with the package removal operation.
855 	 */
856 
857 	/*
858 	 * make sure current runlevel is appropriate
859 	 */
860 
861 	n = rckrunlevel();
862 
863 	if (n != 0) {
864 		quit(n);
865 		/* NOTREACHED */
866 	}
867 
868 	/*
869 	 * determine if any packaging scripts provided with
870 	 * this package will execute as a priviledged user
871 	 */
872 
873 	n = rckpriv();
874 
875 	if (n != 0) {
876 		quit(n);
877 		/* NOTREACHED */
878 	}
879 
880 	/*
881 	 * verify package dependencies
882 	 */
883 	n = rckdepend();
884 
885 	if (n != 0) {
886 		quit(n);
887 		/* NOTREACHED */
888 	}
889 
890 	/*
891 	 * *********************************************************************
892 	 * the actual removal of the package begins here
893 	 * *********************************************************************
894 	 */
895 
896 	/*
897 	 * create lockfile to indicate start of removal
898 	 */
899 	started++;
900 	if ((fd = open(rlockfile, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
901 		progerr(ERR_LOCKFILE, rlockfile);
902 		quit(99);
903 	} else {
904 		(void) close(fd);
905 	}
906 
907 	if (zoneName == (char *)NULL) {
908 		echo(MSG_PKGREMOVE_PROCPKG_GZ);
909 		echoDebug(DBG_PKGREMOVE_PROCPKG_GZ, pkginst, rlockfile);
910 	} else {
911 		echo(MSG_PKGREMOVE_PROCPKG_LZ, zoneName);
912 		echoDebug(DBG_PKGREMOVE_PROCPKG_LZ, pkginst, rlockfile,
913 		    zoneName);
914 	}
915 	if (delmap(0, pkginst, &pkgserver, &tmpfp) != 0) {
916 		progerr(ERR_DB_QUERY, pkginst);
917 		quit(99);
918 	}
919 
920 	/*
921 	 * Run a preremove script if one is provided by the package.
922 	 * Don't execute preremove script if only updating the DB.
923 	 * Don't execute preremove script if files are not being deleted.
924 	 */
925 
926 	/* update the lock - at the preremove script */
927 	lockupd("preremove");
928 
929 	/* execute preremove script if one is provided */
930 	(void) snprintf(script, sizeof (script), "%s/preremove", pkgbin);
931 	if (access(script, F_OK) != 0) {
932 		/* no script present */
933 		echoDebug(DBG_PKGREMOVE_POC_NONE, pkginst,
934 		    zoneName ? zoneName : "global");
935 	} else if (nodelete) {
936 		/* not deleting files: skip preremove script */
937 		echoDebug(DBG_PKGREMOVE_POC_NODEL, pkginst, script,
938 		    zoneName ? zoneName : "global");
939 	} else if (is_depend_pkginfo_DB()) {
940 		/* updating db only: skip preremove script */
941 		echoDebug(DBG_PKGREMOVE_POC_DBUPD, pkginst, script,
942 		    zoneName ? zoneName : "global");
943 	} else {
944 		/* script present and ok to run: run the script */
945 		set_ulimit("preremove", ERR_PREREMOVE);
946 		if (zoneName == (char *)NULL) {
947 			echo(MSG_PKGREMOVE_EXEPOC_GZ);
948 			echoDebug(DBG_PKGREMOVE_EXEPOC_GZ, pkginst, script);
949 		} else {
950 			echo(MSG_PKGREMOVE_EXEPOC_LZ, zoneName);
951 			echoDebug(DBG_PKGREMOVE_EXEPOC_LZ, pkginst, script,
952 			    zoneName);
953 		}
954 		putparam("PKG_PROC_SCRIPT", "preremove");
955 		if (pkgverbose) {
956 			ckreturn(pkgexecl(script_in, PROC_STDOUT,
957 			    PROC_USER, PROC_GRP, SHELL, "-x",
958 			    script, NULL), ERR_PREREMOVE);
959 		} else {
960 			ckreturn(pkgexecl(script_in, PROC_STDOUT,
961 			    PROC_USER, PROC_GRP, SHELL, script,
962 			    NULL), ERR_PREREMOVE);
963 		}
964 		clr_ulimit();
965 	}
966 
967 	/* update the lock - doing removal */
968 
969 	lockupd("remove");
970 
971 	/*
972 	 * Remove all components belonging to this package.
973 	 * Don't remove components if only updating the DB.
974 	 * Don't remove components if files are not being deleted.
975 	 */
976 
977 	if (nodelete) {
978 		echoDebug(DBG_PKGREMOVE_REM_NODEL, pkginst,
979 		    zoneName ? zoneName : "global");
980 	} else if (is_depend_pkginfo_DB()) {
981 		echoDebug(DBG_PKGREMOVE_REM_DBUPD, pkginst,
982 		    zoneName ? zoneName : "global");
983 	} else {
984 		echoDebug(DBG_PKGREMOVE_REM, pkginst,
985 		    zoneName ? zoneName : "global");
986 		/*
987 		 * remove package one class at a time
988 		 */
989 
990 		/* reverse order of classes */
991 		for (i = cl_getn() - 1; i >= 0; i--) {
992 			rmclass(cl_nam(i), pkgrmremote, zoneName);
993 		}
994 
995 		rmclass(NULL, pkgrmremote, zoneName);
996 	}
997 
998 	z_destroyMountTable();
999 
1000 	/*
1001 	 * Execute postremove script, if any
1002 	 * Don't execute postremove script if only updating the DB.
1003 	 * Don't execute postremove script if files are not being deleted.
1004 	 */
1005 
1006 	/* update the lock - at the postremove script */
1007 	lockupd("postremove");
1008 
1009 	/* execute postremove script if one is provided */
1010 	(void) snprintf(script, sizeof (script), "%s/postremove", pkgbin);
1011 	if (access(script, F_OK) != 0) {
1012 		/* no script present */
1013 		echoDebug(DBG_PKGREMOVE_PIC_NONE, pkginst,
1014 		    zoneName ? zoneName : "global");
1015 	} else if (nodelete) {
1016 		/* not deleting files: skip postremove script */
1017 		echoDebug(DBG_PKGREMOVE_PIC_NODEL, pkginst, script,
1018 		    zoneName ? zoneName : "global");
1019 	} else if (is_depend_pkginfo_DB()) {
1020 		/* updating db only: skip postremove script */
1021 		echoDebug(DBG_PKGREMOVE_PIC_DBUPD, pkginst, script,
1022 		    zoneName ? zoneName : "global");
1023 	} else {
1024 		/* script present and ok to run: run the script */
1025 		set_ulimit("postremove", ERR_POSTREMOVE);
1026 		if (zoneName == (char *)NULL) {
1027 			echo(MSG_PKGREMOVE_EXEPIC_GZ);
1028 			echoDebug(DBG_PKGREMOVE_EXEPIC_GZ, pkginst, script);
1029 		} else {
1030 			echo(MSG_PKGREMOVE_EXEPIC_LZ, zoneName);
1031 			echoDebug(DBG_PKGREMOVE_EXEPIC_LZ, pkginst, script,
1032 			    zoneName);
1033 		}
1034 		putparam("PKG_PROC_SCRIPT", "postremove");
1035 		putparam("TMPDIR", tmpdir);
1036 		if (pkgverbose) {
1037 			ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
1038 			    PROC_GRP, SHELL, "-x", script, NULL),
1039 			    ERR_POSTREMOVE);
1040 		} else {
1041 			ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
1042 			    PROC_GRP, SHELL, script, NULL),
1043 			    ERR_POSTREMOVE);
1044 		}
1045 		clr_ulimit();
1046 	}
1047 
1048 	if (zoneName == (char *)NULL) {
1049 		echo(MSG_PKGREMOVE_UPDINF_GZ);
1050 	} else {
1051 		echo(MSG_PKGREMOVE_UPDINF_LZ, zoneName);
1052 	}
1053 
1054 	if (delmap(1, pkginst, &pkgserver, &tmpfp) != 0) {
1055 		progerr(ERR_DB_QUERY, pkginst);
1056 		quit(99);
1057 	}
1058 
1059 	if (!warnflag && !failflag) {
1060 		(void) chdir("/");
1061 		if (rrmdir(pkgloc))
1062 			warnflag++;
1063 	}
1064 
1065 	if ((z_running_in_global_zone() == B_TRUE) &&
1066 	    (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE)) {
1067 		boolean_t	b;
1068 
1069 		b = pkgRemovePackageFromGzonlyList(get_inst_root(), pkginst);
1070 		if (b == B_FALSE) {
1071 			progerr(ERR_PKGREMOVE_GZONLY_REMOVE, pkginst);
1072 			ckreturn(1, NULL);
1073 		}
1074 	}
1075 
1076 	/* release the generic package lock */
1077 
1078 	(void) unlockinst();
1079 
1080 	pkgcloseserver(pkgserver);
1081 
1082 	quit(0);
1083 	/* LINTED: no return */
1084 }
1085 
1086 int
1087 issymlink(char *path)
1088 {
1089 	struct stat statbuf;
1090 
1091 	/*
1092 	 * Obtain status of path; if symbolic link get link's status
1093 	 */
1094 
1095 	if (lstat(path, &statbuf) != 0) {
1096 		return (1);	/* not symlink */
1097 	}
1098 
1099 	/*
1100 	 * Status obtained - if symbolic link, return 0
1101 	 */
1102 
1103 	if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
1104 		return (0);	/* is a symlink */
1105 	}
1106 
1107 	/*
1108 	 * Not a symbolic link - return 1
1109 	 */
1110 
1111 	return (1);		/* not symlink */
1112 }
1113 
1114 static void
1115 rmclass(char *aclass, int rm_remote, char *a_zoneName)
1116 {
1117 	struct cfent	*ept;
1118 	FILE	*fp;
1119 	char	tmpfile[PATH_MAX];
1120 	char	script[PATH_MAX];
1121 	int	i;
1122 	char	*tmp_path;
1123 	char	*save_path = NULL;
1124 	struct stat st;
1125 
1126 	if (aclass == NULL) {
1127 		for (i = 0; i < eptnum; i++) {
1128 			if (eptlist[i] != NULL) {
1129 				rmclass(eptlist[i]->pkg_class,
1130 				    rm_remote, a_zoneName);
1131 			}
1132 		}
1133 		return;
1134 	}
1135 
1136 	/* locate class action script to execute */
1137 	(void) snprintf(script, sizeof (script), "%s/r.%s", pkgbin, aclass);
1138 	if (access(script, F_OK) != 0) {
1139 		(void) snprintf(script, sizeof (script), "%s/r.%s",
1140 		    PKGSCR, aclass);
1141 		if (access(script, F_OK) != 0)
1142 			script[0] = '\0';
1143 	}
1144 	if (script[0] != '\0') {
1145 		int td;
1146 
1147 		(void) snprintf(tmpfile, sizeof (tmpfile), "%s/RMLISTXXXXXX",
1148 		    tmpdir);
1149 		td = mkstemp(tmpfile);
1150 		if (td == -1) {
1151 			progerr(ERR_TMPFILE);
1152 			quit(99);
1153 		}
1154 		if ((fp = fdopen(td, "w")) == NULL) {
1155 			progerr(ERR_WTMPFILE, tmpfile);
1156 			quit(99);
1157 		}
1158 	}
1159 
1160 	if (a_zoneName == (char *)NULL) {
1161 		echo(MSG_PKGREMOVE_REMPATHCLASS_GZ, aclass);
1162 	} else {
1163 		echo(MSG_PKGREMOVE_REMPATHCLASS_LZ, aclass, a_zoneName);
1164 	}
1165 
1166 	/* process paths in reverse order */
1167 	i = eptnum;
1168 	while (--i >= 0) {
1169 		ept = eptlist[i];
1170 
1171 		if ((ept == NULL) || strcmp(aclass, ept->pkg_class)) {
1172 			continue;
1173 		}
1174 
1175 		/* save the path, and prepend the ir */
1176 		if (is_an_inst_root()) {
1177 			save_path = ept->path;
1178 			tmp_path = fixpath(ept->path);
1179 			ept->path = tmp_path;
1180 		}
1181 
1182 		if (!ept->ftype || (ept->ftype == '^' && !script[0])) {
1183 			/*
1184 			 * A path owned by more than one package is marked with
1185 			 * a NULL ftype (seems odd, but that's how it's
1186 			 * done). Such files are sacro sanct. Shared editable
1187 			 * files are a special case, and are marked with an
1188 			 * ftype of '^'. These files should only be ignored if
1189 			 * no class action script is present. It is the CAS's
1190 			 * responsibility to not remove the editable object.
1191 			 */
1192 			echo(MSG_SHARED, ept->path);
1193 		} else if (ept->pinfo->status == SERVED_FILE && !rm_remote) {
1194 			/*
1195 			 * If the path is provided to the client from a
1196 			 * server, don't remove anything unless explicitly
1197 			 * requested through the "-f" option.
1198 			 */
1199 			echo(MSG_SERVER, ept->path);
1200 		} else if (script[0]) {
1201 			/*
1202 			 * If there's a class action script, just put the
1203 			 * path name into the list.
1204 			 */
1205 			(void) fprintf(fp, "%s\n", ept->path);
1206 		} else if (strchr("dx", ept->ftype) != NULL ||
1207 		    (lstat(ept->path, &st) == 0 && S_ISDIR(st.st_mode))) {
1208 			/* Directories are rmdir()'d. */
1209 
1210 			if (rmdir(ept->path)) {
1211 				if (errno == EBUSY) {
1212 					echo(MSG_DIRBUSY, ept->path);
1213 				} else if (errno == EEXIST) {
1214 					echo(MSG_NOTEMPTY, ept->path);
1215 				} else if (errno != ENOENT) {
1216 					progerr(ERR_RMDIR, ept->path);
1217 					warnflag++;
1218 				}
1219 			} else {
1220 				if (ept->pinfo->status == SERVED_FILE) {
1221 					echo(MSG_RMSRVR, ept->path);
1222 				} else {
1223 					echo("%s", ept->path);
1224 				}
1225 			}
1226 
1227 		} else {
1228 			/*
1229 			 * Before removing this object one more
1230 			 * check should be done to assure that a
1231 			 * shared object is not removed.
1232 			 * This can happen if the original object
1233 			 * was incorrectly updated with the
1234 			 * incorrect class identifier.
1235 			 * This handles pathologcal cases that
1236 			 * weren't handled above.
1237 			 */
1238 			if (ept->npkgs > 1) {
1239 				echo(MSG_SHARED, ept->path);
1240 				continue;
1241 			}
1242 
1243 			/* Regular files are unlink()'d. */
1244 
1245 			if (unlink(ept->path)) {
1246 				if (errno != ENOENT) {
1247 					progerr(ERR_RMPATH, ept->path);
1248 					warnflag++;
1249 				}
1250 			} else {
1251 				if (ept->pinfo->status == SERVED_FILE) {
1252 					echo(MSG_RMSRVR, ept->path);
1253 				} else {
1254 					echo("%s", ept->path);
1255 				}
1256 			}
1257 		}
1258 
1259 		/* restore the original path */
1260 
1261 		if (is_an_inst_root()) {
1262 			ept->path = save_path;
1263 		}
1264 
1265 		/*
1266 		 * free memory allocated for this entry memory used for
1267 		 * pathnames will be freed later by a call to pathdup()
1268 		 */
1269 
1270 		if (eptlist[i]) {
1271 			free(eptlist[i]);
1272 		}
1273 		eptlist[i] = NULL;
1274 	}
1275 	if (script[0]) {
1276 		(void) fclose(fp);
1277 		set_ulimit(script, ERR_CASFAIL);
1278 		if (pkgverbose)
1279 			ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
1280 			    CAS_GRP, SHELL, "-x", script, NULL),
1281 			    ERR_CASFAIL);
1282 		else
1283 			ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
1284 			    CAS_GRP, SHELL, script, NULL),
1285 			    ERR_CASFAIL);
1286 		clr_ulimit();
1287 		if (isfile(NULL, tmpfile) == 0) {
1288 			if (unlink(tmpfile) == -1)
1289 				progerr(ERR_RMPATH, tmpfile);
1290 		}
1291 	}
1292 }
1293 
1294 static void
1295 ckreturn(int retcode, char *msg)
1296 {
1297 	switch (retcode) {
1298 	case 2:
1299 	case 12:
1300 	case 22:
1301 		warnflag++;
1302 		if (msg)
1303 			progerr(msg);
1304 		/* FALLTHROUGH */
1305 	case 10:
1306 	case 20:
1307 		if (retcode >= 10)
1308 			dreboot++;
1309 		if (retcode >= 20)
1310 			ireboot++;
1311 		/* FALLTHROUGH */
1312 	case 0:
1313 		break; /* okay */
1314 
1315 	case -1:
1316 		retcode = 99;
1317 		/* FALLTHROUGH */
1318 	case 99:
1319 	case 1:
1320 	case 11:
1321 	case 21:
1322 	case 4:
1323 	case 14:
1324 	case 24:
1325 	case 5:
1326 	case 15:
1327 	case 25:
1328 		if (msg)
1329 			progerr(msg);
1330 		/* FALLTHROUGH */
1331 	case 3:
1332 	case 13:
1333 	case 23:
1334 		quit(retcode);
1335 		/* NOT REACHED */
1336 	default:
1337 		if (msg)
1338 			progerr(msg);
1339 		quit(1);
1340 	}
1341 }
1342 
1343 static void
1344 usage(void)
1345 {
1346 	(void) fprintf(stderr, ERR_USAGE_PKGREMOVE);
1347 
1348 	exit(1);
1349 }
1350