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