640 number_to_scaled_string is duplicated in several commands
[unleashed.git] / usr / src / cmd / svr4pkg / pkgremove / main.c
blob8209b52158b959e82ed4f116afb8617acb7d156d
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2017 Peter Tribble.
27 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
31 /* All Rights Reserved */
34 #include <stdio.h>
35 #include <limits.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <pkgstrct.h>
45 #include <pkginfo.h>
46 #include <pkglocs.h>
47 #include <locale.h>
48 #include <libintl.h>
49 #include <assert.h>
50 #include <cfext.h>
51 #include <instzones_api.h>
52 #include <pkglib.h>
53 #include <install.h>
54 #include <libinst.h>
55 #include <libadm.h>
56 #include <messages.h>
58 struct cfent **eptlist;
59 extern int eptnum;
61 extern char *pkgdir;
62 extern char **environ;
64 /* quit.c */
65 extern sighdlrFunc_t *quitGetTrapHandler(void);
66 extern void quitSetSilentExit(boolean_t a_silentExit);
67 extern void quitSetZoneName(char *a_zoneName);
71 /* check.c */
72 extern void rcksetPreremoveCheck(boolean_t);
73 extern void rcksetZoneName(char *);
74 extern int rckpriv(void);
75 extern int rckdepend(void);
76 extern int rckrunlevel(void);
78 /* delmap.c */
79 extern int delmap(int flag, char *pkginst, PKGserver *server, VFP_T **tfp);
81 #define DEFPATH "/sbin:/usr/sbin:/usr/bin"
83 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
84 #define TEXT_DOMAIN "SYS_TEST"
85 #endif
87 /* This is the text for the "-O parent-zone-name=" option */
89 #define PARENTZONENAME "parent-zone-name="
90 #define PARENTZONENAME_LEN ((sizeof (PARENTZONENAME))-1)
92 /* This is the text for the "-O parent-zone-type=" option */
94 #define PARENTZONETYPE "parent-zone-type="
95 #define PARENTZONETYPE_LEN ((sizeof (PARENTZONETYPE))-1)
97 struct admin adm; /* holds info about installation admin */
98 int dreboot; /* non-zero if reboot required after installation */
99 int ireboot; /* non-zero if immediate reboot required */
100 int failflag; /* non-zero if fatal error has occurred */
101 int warnflag; /* non-zero if non-fatal error has occurred */
102 int pkgverbose; /* non-zero if verbose mode is selected */
103 int started;
104 int nocnflct = 0; /* pkgdbmerg needs this defined */
105 int nosetuid = 0; /* pkgdbmerg needs this defined */
107 char *pkginst; /* current package (source) instance to process */
109 int dbchg;
110 char *msgtext;
111 char pkgloc[PATH_MAX];
114 * The following variable is the name of the device to which stdin
115 * is connected during execution of a procedure script. /dev/null is
116 * correct for all ABI compliant packages. For non-ABI-compliant
117 * packages, the '-o' command line switch changes this to /dev/tty
118 * to allow user interaction during these scripts. -- JST
120 static char *script_in = PROC_STDIN; /* assume ABI compliance */
122 static char *client_mntdir; /* mount point for client's basedir */
123 static char pkgbin[PATH_MAX],
124 rlockfile[PATH_MAX],
125 *admnfile, /* file to use for installation admin */
126 *tmpdir; /* location to place temporary files */
128 static void ckreturn(int retcode, char *msg);
129 static void rmclass(char *aclass, int rm_remote, char *a_zoneName);
130 static void usage(void);
133 * Set by -O debug: debug output is enabled?
135 static boolean_t debugFlag = B_FALSE;
138 * Set by -O preremovecheck: do remove dependency checking only
140 static boolean_t preremoveCheck = B_FALSE;
142 /* Set by -O parent-zone-name= */
144 static char *parentZoneName = (char *)NULL;
146 /* Set by -O parent-zone-type= */
148 static char *parentZoneType = (char *)NULL;
150 static int nointeract; /* != 0 no interaction with user should occur */
155 main(int argc, char *argv[])
157 FILE *fp;
158 char *abi_comp_ptr;
159 char *abi_sym_ptr;
160 char *p;
161 char *prog_full_name = NULL;
162 char *pt;
163 char *value;
164 char *vfstab_file = NULL;
165 char *zoneName = (char *)NULL;
166 char cmdbin[PATH_MAX];
167 char param[MAX_PKG_PARAM_LENGTH];
168 char path[PATH_MAX];
169 char script[PATH_MAX];
170 int c;
171 int err;
172 int fd;
173 int i;
174 int map_client = 1;
175 int n;
176 int nodelete = 0; /* do not delete file or run scripts */
177 int pkgrmremote = 0; /* dont remove remote objects */
178 struct sigaction nact;
179 struct sigaction oact;
180 PKGserver pkgserver = NULL;
181 VFP_T *tmpfp;
183 /* reset contents of all default paths */
185 (void) memset(cmdbin, '\0', sizeof (cmdbin));
187 /* initialize locale environment */
189 (void) setlocale(LC_ALL, "");
190 (void) textdomain(TEXT_DOMAIN);
192 /* initialize program name */
194 prog_full_name = argv[0];
195 (void) set_prog_name(argv[0]);
197 /* tell spmi zones interface how to access package output functions */
199 z_set_output_functions(echo, echoDebug, progerr);
201 /* exit if not root */
203 if (getuid()) {
204 progerr(ERR_NOT_ROOT, get_prog_name());
205 exit(1);
206 /* NOTREACHED */
209 /* Read PKG_INSTALL_ROOT from the environment, if it's there. */
211 if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
212 progerr(ERR_ROOT_SET);
213 exit(1);
216 pkgserversetmode(DEFAULTMODE);
218 /* parse command line options */
220 while ((c = getopt(argc, argv, "?Aa:b:FMN:nO:oR:V:vy")) != EOF) {
221 switch (c) {
223 * Same as pkgrm: Allow admin to remove package objects from
224 * a shared area from a reference client.
226 case 'A':
227 pkgrmremote++;
228 break;
231 * Same as pkgrm: Use the installation
232 * administration file, admin, in place of the
233 * default admin file. pkgrm first looks in the
234 * current working directory for the administration
235 * file. If the specified administration file is not
236 * in the current working directory, pkgrm looks in
237 * the /var/sadm/install/admin directory for the
238 * administration file.
240 case 'a':
241 admnfile = flex_device(optarg, 0);
242 break;
245 * Same as pkgrm: location where package executables
246 * can be found - default is /usr/sadm/install/bin.
248 case 'b':
249 if (!path_valid(optarg)) {
250 progerr(ERR_PATH, optarg);
251 exit(1);
253 if (isdir(optarg) != 0) {
254 char *p = strerror(errno);
255 progerr(ERR_CANNOT_USE_DIR, optarg, p);
256 exit(1);
258 (void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
259 break;
262 * Same as pkgrm: suppresses the removal of any
263 * files and any class action scripts, and suppresses
264 * the running of any class action scripts. The
265 * package files remain but the package looks like it
266 * is not installed. This is mainly for use by the
267 * upgrade process.
269 case 'F':
270 nodelete++;
271 break;
274 * Same as pkgrm: Instruct pkgrm not to use the
275 * $root_path/etc/vfstab file for determining the
276 * client's mount points. This option assumes the
277 * mount points are correct on the server and it
278 * behaves consistently with Solaris 2.5 and earlier
279 * releases.
281 case 'M':
282 map_client = 0;
283 break;
286 * Different from pkgrm: specify program name to use
287 * for messages.
289 case 'N':
290 (void) set_prog_name(optarg);
291 break;
294 * Same as pkgrm: package removal occurs in
295 * non-interactive mode. Suppress output of the list of
296 * removed files. The default mode is interactive.
298 case 'n':
299 nointeract++;
300 (void) echoSetFlag(B_FALSE);
301 break;
304 * Almost same as pkgrm: the -O option allows the behavior
305 * of the package tools to be modified. Recognized options:
306 * -> debug
307 * ---> enable debugging output
308 * -> preremovecheck
309 * ---> perform a "pre removal" check of the specified
310 * ---> package - suppress all regular output and cause a
311 * ---> series of one or more "name=value" pair format lines
312 * ---> to be output that describes the "removability" of
313 * ---> the specified package
314 * -> enable-hollow-package-support
315 * --> Enable hollow package support. When specified, for any
316 * --> package that has SUNW_PKG_HOLLOW=true:
317 * --> Do not calculate and verify package size against target
318 * --> Do not run any package procedure or class action scripts
319 * --> Do not create or remove any target directories
320 * --> Do not perform any script locking
321 * --> Do not install or uninstall any components of any package
322 * --> Do not output any status or database update messages
324 case 'O':
325 for (p = strtok(optarg, ","); p != (char *)NULL;
326 p = strtok(NULL, ",")) {
328 /* process debug option */
330 if (strcmp(p, "debug") == 0) {
331 /* set debug flag/enable debug output */
332 debugFlag = B_TRUE;
333 (void) echoDebugSetFlag(debugFlag);
335 /* debug info on arguments to pkgadd */
336 for (n = 0; n < argc && argv[n]; n++) {
337 echoDebug(DBG_ARG, n, argv[n]);
340 continue;
343 /* process enable-hollow-package-support opt */
345 if (strcmp(p,
346 "enable-hollow-package-support") == 0) {
347 set_depend_pkginfo_DB(B_TRUE);
348 continue;
351 /* process preremovecheck option */
353 if (strcmp(p, "preremovecheck") == 0) {
354 preremoveCheck = B_TRUE;
355 nointeract++; /* -n */
356 nodelete++; /* -F */
357 quitSetSilentExit(B_TRUE);
358 continue;
361 /* process addzonename option */
363 if (strcmp(p, "addzonename") == 0) {
364 zoneName = z_get_zonename();
365 quitSetZoneName(zoneName);
366 continue;
369 /* process parent-zone-name option */
371 if (strncmp(p, PARENTZONENAME,
372 PARENTZONENAME_LEN) == 0) {
373 parentZoneName = p+PARENTZONENAME_LEN;
374 continue;
377 /* process parent-zone-type option */
379 if (strncmp(p, PARENTZONETYPE,
380 PARENTZONETYPE_LEN) == 0) {
381 parentZoneType = p+PARENTZONETYPE_LEN;
382 continue;
385 if (strncmp(p, PKGSERV_MODE,
386 PKGSERV_MODE_LEN) == 0) {
387 pkgserversetmode(pkgparsemode(p +
388 PKGSERV_MODE_LEN));
389 continue;
391 /* option not recognized - issue warning */
393 progerr(ERR_INVALID_O_OPTION, p);
394 continue;
396 break;
399 * Different from pkgrm: This is an old non-ABI package
402 case 'o':
403 script_in = PROC_XSTDIN;
404 break;
407 * Same as pkgrm: defines the full path name of a
408 * directory to use as the root_path. All files,
409 * including package system information files, are
410 * relocated to a directory tree starting in the
411 * specified root_path.
413 case 'R':
414 if (!set_inst_root(optarg)) {
415 progerr(ERR_ROOT_CMD);
416 exit(1);
418 break;
421 * Same as pkgrm: allow admin to establish the client
422 * filesystem using a vfstab-like file of stable format.
424 case 'V':
425 vfstab_file = flex_device(optarg, 2);
426 map_client = 1;
427 break;
430 * Same as pkgrm: trace all of the scripts that
431 * get executed by pkgrm, located in the
432 * pkginst/install directory. This option is used for
433 * debugging the procedural and non-procedural
434 * scripts.
436 case 'v':
437 pkgverbose++;
438 break;
441 * Different from pkgrm: process this package using
442 * old non-ABI symlinks
444 case 'y':
445 set_nonABI_symlinks();
446 break;
448 default:
449 usage();
450 /*NOTREACHED*/
452 * Although usage() calls a noreturn function,
453 * needed to add return (1); so that main() would
454 * pass compilation checks. The statement below
455 * should never be executed.
457 return (1);
462 * ********************************************************************
463 * validate command line options
464 * ********************************************************************
467 (void) echoDebugSetFlag(debugFlag);
468 (void) log_set_verbose(debugFlag);
470 if (z_running_in_global_zone()) {
471 echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
472 } else {
473 echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
474 z_get_zonename());
477 /* establish cmdbin path */
479 if (cmdbin[0] == '\0') {
480 (void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
483 /* Read the mount table */
485 if (get_mntinfo(map_client, vfstab_file)) {
486 quit(99);
490 * This function defines the standard /var/... directories used later
491 * to construct the paths to the various databases.
494 set_PKGpaths(get_inst_root());
497 * If this is being removed from a client whose /var filesystem is
498 * mounted in some odd way, remap the administrative paths to the
499 * real filesystem. This could be avoided by simply mounting up the
500 * client now; but we aren't yet to the point in the process where
501 * modification of the filesystem is permitted.
503 if (is_an_inst_root()) {
504 int fsys_value;
506 fsys_value = fsys(get_PKGLOC());
507 if (use_srvr_map_n(fsys_value))
508 set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
510 fsys_value = fsys(get_PKGADM());
511 if (use_srvr_map_n(fsys_value))
512 set_PKGADM(server_map(get_PKGADM(), fsys_value));
513 } else {
514 pkgrmremote = 0; /* Makes no sense on local host. */
518 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
521 /* hold SIGINT/SIGHUP interrupts */
523 (void) sighold(SIGHUP);
524 (void) sighold(SIGINT);
526 /* connect quit.c:trap() to SIGINT */
528 nact.sa_handler = quitGetTrapHandler();
529 nact.sa_flags = SA_RESTART;
530 (void) sigemptyset(&nact.sa_mask);
532 (void) sigaction(SIGINT, &nact, &oact);
534 /* connect quit.c:trap() to SIGHUP */
536 nact.sa_handler = quitGetTrapHandler();
537 nact.sa_flags = SA_RESTART;
538 (void) sigemptyset(&nact.sa_mask);
540 (void) sigaction(SIGHUP, &nact, &oact);
542 /* release hold on signals */
544 (void) sigrelse(SIGHUP);
545 (void) sigrelse(SIGINT);
547 pkginst = argv[optind++];
548 if (optind != argc) {
549 usage();
552 /* validate package software database (contents) file */
554 if (vcfile() == 0) {
555 quit(99);
559 * Acquire the package lock - currently at "remove initialization"
562 if (!lockinst(get_prog_name(), pkginst, "remove-initial")) {
563 quit(99);
566 /* establish temporary directory to use */
568 tmpdir = getenv("TMPDIR");
569 if (tmpdir == NULL) {
570 tmpdir = P_tmpdir;
573 echoDebug(DBG_PKGREMOVE_TMPDIR, tmpdir);
576 * Initialize installation admin parameters by reading
577 * the adminfile.
580 echoDebug(DBG_PKGREMOVE_ADMINFILE, admnfile ? admnfile : "");
581 setadminFile(admnfile);
584 * about to perform first operation that could be modified by the
585 * preremove check option - if preremove check is selected (that is,
586 * only gathering dependencies), then output a debug message to
587 * indicate that the check is beginning. Also turn echo() output
588 * off and set various other flags.
591 if (preremoveCheck == B_TRUE) {
592 (void) echoSetFlag(B_FALSE);
593 echoDebug(DBG_PKGREMOVE_PRERMCHK, pkginst ? pkginst : "",
594 zoneName ? zoneName : "global");
595 rcksetPreremoveCheck(B_TRUE);
596 rcksetZoneName(zoneName);
599 (void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s", get_PKGLOC(),
600 pkginst);
601 (void) snprintf(pkgbin, sizeof (pkgbin), "%s/install", pkgloc);
602 (void) snprintf(rlockfile, sizeof (rlockfile), "%s/!R-Lock!", pkgloc);
604 if (chdir(pkgbin)) {
605 progerr(ERR_CHDIR, pkgbin);
606 quit(99);
609 echo(MSG_PREREMOVE_REMINST, pkginst);
612 * if a lock file is present, then a previous attempt to remove this
613 * package may have been unsuccessful.
616 if (access(rlockfile, F_OK) == 0) {
617 echo(ERR_UNSUCC);
618 echoDebug(DBG_PKGINSTALL_HAS_LOCKFILE, pkginst, rlockfile,
619 zoneName ? zoneName : "global");
623 * Process all parameters from the pkginfo file
624 * and place them in the execution environment
627 /* Add DB retreival of the pkginfo parameters here */
628 (void) snprintf(path, sizeof (path), "%s/pkginfo", pkgloc);
629 if ((fp = fopen(path, "r")) == NULL) {
630 progerr(ERR_PKGINFO, path);
631 quit(99);
634 /* Mount up the client if necessary. */
635 if (map_client && !mount_client()) {
636 logerr(MSG_MANMOUNT);
639 /* Get mount point of client */
640 client_mntdir = getenv("CLIENT_MNTDIR");
642 getuserlocale();
645 * current environment has been read; clear environment out
646 * so putparam() can be used to populate the new environment
647 * to be passed to any executables/scripts.
650 environ = NULL;
652 if (nonABI_symlinks()) {
653 putparam("PKG_NONABI_SYMLINKS", "TRUE");
657 * read the pkginfo file and fix any PKGSAV path - the correct
658 * install_root will be prepended to the existing path.
661 param[0] = '\0';
662 while (value = fpkgparam(fp, param)) {
663 int validx = 0;
664 char *newvalue;
666 /* strip out any setting of PATH */
668 if (strcmp(param, "PATH") == 0) {
669 free(value);
670 param[0] = '\0';
671 continue;
674 /* if not PKGSAV then write out unchanged */
676 if (strcmp(param, "PKGSAV") != 0) {
677 putparam(param, value);
678 free(value);
679 param[0] = '\0';
680 continue;
684 * PKGSAV parameter found - interpret the directory:
685 * If in host:path format or marked with the leading "//",
686 * then there is no client-relative translation - take it
687 * literally later rather than use fixpath().
690 if (strstr(value, ":/")) {
691 /* no modification needed */
692 validx = 0;
693 } else if (strstr(value, "//") == value) {
694 validx = 1;
695 } else if (is_an_inst_root()) {
696 /* This PKGSAV needs to be made client-relative. */
697 newvalue = fixpath(value);
698 free(value);
699 value = newvalue;
701 putparam(param, value+validx);
702 free(value);
703 param[0] = '\0';
706 (void) fclose(fp);
708 /* write parent condition information to environment */
710 putConditionInfo(parentZoneName, parentZoneType);
712 putuserlocale();
715 * Now do all the various setups based on ABI compliance
718 /* Read the environment provided by the pkginfo file */
719 abi_comp_ptr = getenv("NONABI_SCRIPTS");
721 /* if not ABI compliant set global flag */
722 abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
723 if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
724 set_nonABI_symlinks();
728 * If pkginfo says it's not compliant then set non_abi_scripts.
730 if (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0) {
731 script_in = PROC_XSTDIN;
735 * Since this is a removal, we can tell whether it's absolute or
736 * not from the resident pkginfo file read above.
738 if ((err = set_basedirs((getenv("BASEDIR") != NULL), adm.basedir,
739 pkginst, nointeract)) != 0) {
740 quit(err);
744 * See if were are removing a package that only wants to update
745 * the database or only remove files associated with CAS's. We
746 * only check the PKG_HOLLOW_VARIABLE variable if told to do so by
747 * the caller.
750 if (is_depend_pkginfo_DB()) {
751 pt = getenv(PKG_HOLLOW_VARIABLE);
753 if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
754 echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
757 * this is a hollow package and hollow package support
758 * is enabled -- override admin settings to suppress
759 * checks that do not make sense since no scripts will
760 * be executed and no files will be removed.
763 setadminSetting("conflict", "nocheck");
764 setadminSetting("setuid", "nocheck");
765 setadminSetting("action", "nocheck");
766 setadminSetting("partial", "nocheck");
767 setadminSetting("space", "nocheck");
768 setadminSetting("authentication", "nocheck");
769 } else {
770 echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
771 set_depend_pkginfo_DB(B_FALSE);
775 put_path_params();
777 /* If client mount point, add it to pkgremove environment */
779 if (client_mntdir != NULL) {
780 putparam("CLIENT_MNTDIR", client_mntdir);
783 /* Establish the class list and the class attributes. */
785 if ((value = getenv("CLASSES")) != NULL) {
786 cl_sets(qstrdup(value));
787 } else {
788 progerr(ERR_CLASSES, path);
789 quit(99);
792 /* establish path and tmpdir */
794 if (cmdbin[0] == '\0') {
795 (void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
798 (void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
799 putparam("PATH", path);
801 putparam("TMPDIR", tmpdir);
804 * Check ulimit requirement (provided in pkginfo). The purpose of
805 * this limit is to terminate pathological file growth resulting from
806 * file edits in scripts. It does not apply to files in the pkgmap
807 * and it does not apply to any database files manipulated by the
808 * installation service.
810 if (value = getenv("ULIMIT")) {
811 if (assign_ulimit(value) == -1) {
812 progerr(ERR_BADULIMIT, value);
813 warnflag++;
815 putparam("PKG_ULIMIT", "TRUE");
819 * If only gathering dependencies, check and output status of all
820 * remaining dependencies and exit.
823 if (preremoveCheck == B_TRUE) {
825 * make sure current runlevel is appropriate
828 (void) fprintf(stdout, "rckrunlevel=%d\n", rckrunlevel());
831 * determine if any packaging scripts provided with
832 * this package will execute as a priviledged user
835 (void) fprintf(stdout, "rckpriv=%d\n", rckpriv());
838 * verify package dependencies
841 (void) fprintf(stdout, "rckdepend=%d\n", rckdepend());
844 * ****** preremove check done - exit ******
847 echoDebug(DBG_PKGREMOVE_PRERMCHK_OK);
848 quit(0);
849 /*NOTREACHED*/
853 * Not gathering dependencies only, proceed to check dependencies
854 * and continue with the package removal operation.
858 * make sure current runlevel is appropriate
861 n = rckrunlevel();
863 if (n != 0) {
864 quit(n);
865 /* NOTREACHED */
869 * determine if any packaging scripts provided with
870 * this package will execute as a priviledged user
873 n = rckpriv();
875 if (n != 0) {
876 quit(n);
877 /* NOTREACHED */
881 * verify package dependencies
883 n = rckdepend();
885 if (n != 0) {
886 quit(n);
887 /* NOTREACHED */
891 * *********************************************************************
892 * the actual removal of the package begins here
893 * *********************************************************************
897 * create lockfile to indicate start of removal
899 started++;
900 if ((fd = open(rlockfile, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
901 progerr(ERR_LOCKFILE, rlockfile);
902 quit(99);
903 } else {
904 (void) close(fd);
907 if (zoneName == (char *)NULL) {
908 echo(MSG_PKGREMOVE_PROCPKG_GZ);
909 echoDebug(DBG_PKGREMOVE_PROCPKG_GZ, pkginst, rlockfile);
910 } else {
911 echo(MSG_PKGREMOVE_PROCPKG_LZ, zoneName);
912 echoDebug(DBG_PKGREMOVE_PROCPKG_LZ, pkginst, rlockfile,
913 zoneName);
915 if (delmap(0, pkginst, &pkgserver, &tmpfp) != 0) {
916 progerr(ERR_DB_QUERY, pkginst);
917 quit(99);
921 * Run a preremove script if one is provided by the package.
922 * Don't execute preremove script if only updating the DB.
923 * Don't execute preremove script if files are not being deleted.
926 /* update the lock - at the preremove script */
927 lockupd("preremove");
929 /* execute preremove script if one is provided */
930 (void) snprintf(script, sizeof (script), "%s/preremove", pkgbin);
931 if (access(script, F_OK) != 0) {
932 /* no script present */
933 echoDebug(DBG_PKGREMOVE_POC_NONE, pkginst,
934 zoneName ? zoneName : "global");
935 } else if (nodelete) {
936 /* not deleting files: skip preremove script */
937 echoDebug(DBG_PKGREMOVE_POC_NODEL, pkginst, script,
938 zoneName ? zoneName : "global");
939 } else if (is_depend_pkginfo_DB()) {
940 /* updating db only: skip preremove script */
941 echoDebug(DBG_PKGREMOVE_POC_DBUPD, pkginst, script,
942 zoneName ? zoneName : "global");
943 } else {
944 /* script present and ok to run: run the script */
945 set_ulimit("preremove", ERR_PREREMOVE);
946 if (zoneName == (char *)NULL) {
947 echo(MSG_PKGREMOVE_EXEPOC_GZ);
948 echoDebug(DBG_PKGREMOVE_EXEPOC_GZ, pkginst, script);
949 } else {
950 echo(MSG_PKGREMOVE_EXEPOC_LZ, zoneName);
951 echoDebug(DBG_PKGREMOVE_EXEPOC_LZ, pkginst, script,
952 zoneName);
954 putparam("PKG_PROC_SCRIPT", "preremove");
955 if (pkgverbose) {
956 ckreturn(pkgexecl(script_in, PROC_STDOUT,
957 PROC_USER, PROC_GRP, SHELL, "-x",
958 script, NULL), ERR_PREREMOVE);
959 } else {
960 ckreturn(pkgexecl(script_in, PROC_STDOUT,
961 PROC_USER, PROC_GRP, SHELL, script,
962 NULL), ERR_PREREMOVE);
964 clr_ulimit();
967 /* update the lock - doing removal */
969 lockupd("remove");
972 * Ensure that the contents file is updated even if the db has
973 * been upgraded, in the case that there are relevant entries
974 * in a special_contents file. The return value is ignored
975 * since we do not want special_contents operation to prevent
976 * pkgremove from succeeding. We do report errors to stderr.
980 * Remove all components belonging to this package.
981 * Don't remove components if only updating the DB.
982 * Don't remove components if files are not being deleted.
985 if (nodelete) {
986 echoDebug(DBG_PKGREMOVE_REM_NODEL, pkginst,
987 zoneName ? zoneName : "global");
988 } else if (is_depend_pkginfo_DB()) {
989 echoDebug(DBG_PKGREMOVE_REM_DBUPD, pkginst,
990 zoneName ? zoneName : "global");
991 } else {
992 echoDebug(DBG_PKGREMOVE_REM, pkginst,
993 zoneName ? zoneName : "global");
995 * remove package one class at a time
998 /* reverse order of classes */
999 for (i = cl_getn() - 1; i >= 0; i--) {
1000 rmclass(cl_nam(i), pkgrmremote, zoneName);
1003 rmclass(NULL, pkgrmremote, zoneName);
1006 z_destroyMountTable();
1009 * Execute postremove script, if any
1010 * Don't execute postremove script if only updating the DB.
1011 * Don't execute postremove script if files are not being deleted.
1014 /* update the lock - at the postremove script */
1015 lockupd("postremove");
1017 /* execute postremove script if one is provided */
1018 (void) snprintf(script, sizeof (script), "%s/postremove", pkgbin);
1019 if (access(script, F_OK) != 0) {
1020 /* no script present */
1021 echoDebug(DBG_PKGREMOVE_PIC_NONE, pkginst,
1022 zoneName ? zoneName : "global");
1023 } else if (nodelete) {
1024 /* not deleting files: skip postremove script */
1025 echoDebug(DBG_PKGREMOVE_PIC_NODEL, pkginst, script,
1026 zoneName ? zoneName : "global");
1027 } else if (is_depend_pkginfo_DB()) {
1028 /* updating db only: skip postremove script */
1029 echoDebug(DBG_PKGREMOVE_PIC_DBUPD, pkginst, script,
1030 zoneName ? zoneName : "global");
1031 } else {
1032 /* script present and ok to run: run the script */
1033 set_ulimit("postremove", ERR_POSTREMOVE);
1034 if (zoneName == (char *)NULL) {
1035 echo(MSG_PKGREMOVE_EXEPIC_GZ);
1036 echoDebug(DBG_PKGREMOVE_EXEPIC_GZ, pkginst, script);
1037 } else {
1038 echo(MSG_PKGREMOVE_EXEPIC_LZ, zoneName);
1039 echoDebug(DBG_PKGREMOVE_EXEPIC_LZ, pkginst, script,
1040 zoneName);
1042 putparam("PKG_PROC_SCRIPT", "postremove");
1043 putparam("TMPDIR", tmpdir);
1044 if (pkgverbose) {
1045 ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
1046 PROC_GRP, SHELL, "-x", script, NULL),
1047 ERR_POSTREMOVE);
1048 } else {
1049 ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
1050 PROC_GRP, SHELL, script, NULL),
1051 ERR_POSTREMOVE);
1053 clr_ulimit();
1056 if (zoneName == (char *)NULL) {
1057 echo(MSG_PKGREMOVE_UPDINF_GZ);
1058 } else {
1059 echo(MSG_PKGREMOVE_UPDINF_LZ, zoneName);
1062 if (delmap(1, pkginst, &pkgserver, &tmpfp) != 0) {
1063 progerr(ERR_DB_QUERY, pkginst);
1064 quit(99);
1067 if (!warnflag && !failflag) {
1068 (void) chdir("/");
1069 if (rrmdir(pkgloc))
1070 warnflag++;
1073 if ((z_running_in_global_zone() == B_TRUE) &&
1074 (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE)) {
1075 boolean_t b;
1077 b = pkgRemovePackageFromGzonlyList(get_inst_root(), pkginst);
1078 if (b == B_FALSE) {
1079 progerr(ERR_PKGREMOVE_GZONLY_REMOVE, pkginst);
1080 ckreturn(1, NULL);
1084 /* release the generic package lock */
1086 (void) unlockinst();
1088 pkgcloseserver(pkgserver);
1090 quit(0);
1091 /* LINTED: no return */
1095 issymlink(char *path)
1097 struct stat statbuf;
1100 * Obtain status of path; if symbolic link get link's status
1103 if (lstat(path, &statbuf) != 0) {
1104 return (1); /* not symlink */
1108 * Status obtained - if symbolic link, return 0
1111 if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
1112 return (0); /* is a symlink */
1116 * Not a symbolic link - return 1
1119 return (1); /* not symlink */
1122 static void
1123 rmclass(char *aclass, int rm_remote, char *a_zoneName)
1125 struct cfent *ept;
1126 FILE *fp;
1127 char tmpfile[PATH_MAX];
1128 char script[PATH_MAX];
1129 int i;
1130 char *tmp_path;
1131 char *save_path = NULL;
1132 struct stat st;
1134 if (aclass == NULL) {
1135 for (i = 0; i < eptnum; i++) {
1136 if (eptlist[i] != NULL) {
1137 rmclass(eptlist[i]->pkg_class,
1138 rm_remote, a_zoneName);
1141 return;
1144 /* locate class action script to execute */
1145 (void) snprintf(script, sizeof (script), "%s/r.%s", pkgbin, aclass);
1146 if (access(script, F_OK) != 0) {
1147 (void) snprintf(script, sizeof (script), "%s/r.%s",
1148 PKGSCR, aclass);
1149 if (access(script, F_OK) != 0)
1150 script[0] = '\0';
1152 if (script[0] != '\0') {
1153 int td;
1155 (void) snprintf(tmpfile, sizeof (tmpfile), "%s/RMLISTXXXXXX",
1156 tmpdir);
1157 td = mkstemp(tmpfile);
1158 if (td == -1) {
1159 progerr(ERR_TMPFILE);
1160 quit(99);
1162 if ((fp = fdopen(td, "w")) == NULL) {
1163 progerr(ERR_WTMPFILE, tmpfile);
1164 quit(99);
1168 if (a_zoneName == (char *)NULL) {
1169 echo(MSG_PKGREMOVE_REMPATHCLASS_GZ, aclass);
1170 } else {
1171 echo(MSG_PKGREMOVE_REMPATHCLASS_LZ, aclass, a_zoneName);
1174 /* process paths in reverse order */
1175 i = eptnum;
1176 while (--i >= 0) {
1177 ept = eptlist[i];
1179 if ((ept == NULL) || strcmp(aclass, ept->pkg_class)) {
1180 continue;
1183 /* save the path, and prepend the ir */
1184 if (is_an_inst_root()) {
1185 save_path = ept->path;
1186 tmp_path = fixpath(ept->path);
1187 ept->path = tmp_path;
1190 if (!ept->ftype || (ept->ftype == '^' && !script[0])) {
1192 * A path owned by more than one package is marked with
1193 * a NULL ftype (seems odd, but that's how it's
1194 * done). Such files are sacro sanct. Shared editable
1195 * files are a special case, and are marked with an
1196 * ftype of '^'. These files should only be ignored if
1197 * no class action script is present. It is the CAS's
1198 * responsibility to not remove the editable object.
1200 echo(MSG_SHARED, ept->path);
1201 } else if (ept->pinfo->status == SERVED_FILE && !rm_remote) {
1203 * If the path is provided to the client from a
1204 * server, don't remove anything unless explicitly
1205 * requested through the "-f" option.
1207 echo(MSG_SERVER, ept->path);
1208 } else if (script[0]) {
1210 * If there's a class action script, just put the
1211 * path name into the list.
1213 (void) fprintf(fp, "%s\n", ept->path);
1214 } else if (strchr("dx", ept->ftype) != NULL ||
1215 (lstat(ept->path, &st) == 0 && S_ISDIR(st.st_mode))) {
1216 /* Directories are rmdir()'d. */
1218 if (rmdir(ept->path)) {
1219 if (errno == EBUSY) {
1220 echo(MSG_DIRBUSY, ept->path);
1221 } else if (errno == EEXIST) {
1222 echo(MSG_NOTEMPTY, ept->path);
1223 } else if (errno != ENOENT) {
1224 progerr(ERR_RMDIR, ept->path);
1225 warnflag++;
1227 } else {
1228 if (ept->pinfo->status == SERVED_FILE) {
1229 echo(MSG_RMSRVR, ept->path);
1230 } else {
1231 echo("%s", ept->path);
1235 } else {
1237 * Before removing this object one more
1238 * check should be done to assure that a
1239 * shared object is not removed.
1240 * This can happen if the original object
1241 * was incorrectly updated with the
1242 * incorrect class identifier.
1243 * This handles pathologcal cases that
1244 * weren't handled above.
1246 if (ept->npkgs > 1) {
1247 echo(MSG_SHARED, ept->path);
1248 continue;
1251 /* Regular files are unlink()'d. */
1253 if (unlink(ept->path)) {
1254 if (errno != ENOENT) {
1255 progerr(ERR_RMPATH, ept->path);
1256 warnflag++;
1258 } else {
1259 if (ept->pinfo->status == SERVED_FILE) {
1260 echo(MSG_RMSRVR, ept->path);
1261 } else {
1262 echo("%s", ept->path);
1267 /* restore the original path */
1269 if (is_an_inst_root()) {
1270 ept->path = save_path;
1274 * free memory allocated for this entry memory used for
1275 * pathnames will be freed later by a call to pathdup()
1278 if (eptlist[i]) {
1279 free(eptlist[i]);
1281 eptlist[i] = NULL;
1283 if (script[0]) {
1284 (void) fclose(fp);
1285 set_ulimit(script, ERR_CASFAIL);
1286 if (pkgverbose)
1287 ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
1288 CAS_GRP, SHELL, "-x", script, NULL),
1289 ERR_CASFAIL);
1290 else
1291 ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
1292 CAS_GRP, SHELL, script, NULL),
1293 ERR_CASFAIL);
1294 clr_ulimit();
1295 if (isfile(NULL, tmpfile) == 0) {
1296 if (unlink(tmpfile) == -1)
1297 progerr(ERR_RMPATH, tmpfile);
1302 static void
1303 ckreturn(int retcode, char *msg)
1305 switch (retcode) {
1306 case 2:
1307 case 12:
1308 case 22:
1309 warnflag++;
1310 /*FALLTHRU*/
1311 if (msg)
1312 progerr(msg);
1313 case 10:
1314 case 20:
1315 if (retcode >= 10)
1316 dreboot++;
1317 if (retcode >= 20)
1318 ireboot++;
1319 /*FALLTHRU*/
1320 case 0:
1321 break; /* okay */
1323 case -1:
1324 retcode = 99;
1325 /*FALLTHRU*/
1326 case 99:
1327 case 1:
1328 case 11:
1329 case 21:
1330 case 4:
1331 case 14:
1332 case 24:
1333 case 5:
1334 case 15:
1335 case 25:
1336 if (msg)
1337 progerr(msg);
1338 /*FALLTHRU*/
1339 case 3:
1340 case 13:
1341 case 23:
1342 quit(retcode);
1343 /* NOT REACHED */
1344 default:
1345 if (msg)
1346 progerr(msg);
1347 quit(1);
1351 static void
1352 usage(void)
1354 (void) fprintf(stderr, ERR_USAGE_PKGREMOVE);
1356 exit(1);