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