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