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