#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pkgstrct.h>
#include <pkginfo.h>
#include <pkglocs.h>
#include <locale.h>
#include <libintl.h>
#include <assert.h>
#include <cfext.h>
#include <instzones_api.h>
#include <pkglib.h>
#include <install.h>
#include <libinst.h>
#include <libadm.h>
#include <messages.h>
struct cfent **eptlist;
extern int eptnum;
extern char *pkgdir;
extern char **environ;
extern sighdlrFunc_t *quitGetTrapHandler(void);
extern void quitSetSilentExit(boolean_t a_silentExit);
extern void quitSetZoneName(char *a_zoneName);
extern void rcksetPreremoveCheck(boolean_t);
extern void rcksetZoneName(char *);
extern int rckpriv(void);
extern int rckdepend(void);
extern int rckrunlevel(void);
extern int delmap(int flag, char *pkginst, PKGserver *server, VFP_T **tfp);
#define DEFPATH "/sbin:/usr/sbin:/usr/bin"
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
#define PARENTZONENAME "parent-zone-name="
#define PARENTZONENAME_LEN ((sizeof (PARENTZONENAME))-1)
#define PARENTZONETYPE "parent-zone-type="
#define PARENTZONETYPE_LEN ((sizeof (PARENTZONETYPE))-1)
struct admin adm;
int dreboot;
int ireboot;
int failflag;
int warnflag;
int pkgverbose;
int started;
int nocnflct = 0;
int nosetuid = 0;
char *pkginst;
int dbchg;
char *msgtext;
char pkgloc[PATH_MAX];
static char *script_in = PROC_STDIN;
static char *client_mntdir;
static char pkgbin[PATH_MAX],
rlockfile[PATH_MAX],
*admnfile,
*tmpdir;
static void ckreturn(int retcode, char *msg);
static void rmclass(char *aclass, int rm_remote, char *a_zoneName);
static void usage(void);
static boolean_t debugFlag = B_FALSE;
static boolean_t preremoveCheck = B_FALSE;
static char *parentZoneName = (char *)NULL;
static char *parentZoneType = (char *)NULL;
static int nointeract;
int
main(int argc, char *argv[])
{
FILE *fp;
char *abi_comp_ptr;
char *abi_sym_ptr;
char *p;
char *prog_full_name = NULL;
char *pt;
char *value;
char *vfstab_file = NULL;
char *zoneName = (char *)NULL;
char cmdbin[PATH_MAX];
char param[MAX_PKG_PARAM_LENGTH];
char path[PATH_MAX];
char script[PATH_MAX];
int c;
int err;
int fd;
int i;
int map_client = 1;
int n;
int nodelete = 0;
int pkgrmremote = 0;
struct sigaction nact;
struct sigaction oact;
PKGserver pkgserver = NULL;
VFP_T *tmpfp;
(void) memset(cmdbin, '\0', sizeof (cmdbin));
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
prog_full_name = argv[0];
(void) set_prog_name(argv[0]);
z_set_output_functions(echo, echoDebug, progerr);
if (getuid()) {
progerr(ERR_NOT_ROOT, get_prog_name());
exit(1);
}
if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
progerr(ERR_ROOT_SET);
exit(1);
}
pkgserversetmode(DEFAULTMODE);
while ((c = getopt(argc, argv, "?Aa:b:FMN:nO:oR:V:vy")) != EOF) {
switch (c) {
case 'A':
pkgrmremote++;
break;
case 'a':
admnfile = flex_device(optarg, 0);
break;
case 'b':
if (!path_valid(optarg)) {
progerr(ERR_PATH, optarg);
exit(1);
}
if (isdir(optarg) != 0) {
char *p = strerror(errno);
progerr(ERR_CANNOT_USE_DIR, optarg, p);
exit(1);
}
(void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
break;
case 'F':
nodelete++;
break;
case 'M':
map_client = 0;
break;
case 'N':
(void) set_prog_name(optarg);
break;
case 'n':
nointeract++;
(void) echoSetFlag(B_FALSE);
break;
case 'O':
for (p = strtok(optarg, ","); p != (char *)NULL;
p = strtok(NULL, ",")) {
if (strcmp(p, "debug") == 0) {
debugFlag = B_TRUE;
(void) echoDebugSetFlag(debugFlag);
for (n = 0; n < argc && argv[n]; n++) {
echoDebug(DBG_ARG, n, argv[n]);
}
continue;
}
if (strcmp(p,
"enable-hollow-package-support") == 0) {
set_depend_pkginfo_DB(B_TRUE);
continue;
}
if (strcmp(p, "preremovecheck") == 0) {
preremoveCheck = B_TRUE;
nointeract++;
nodelete++;
quitSetSilentExit(B_TRUE);
continue;
}
if (strcmp(p, "addzonename") == 0) {
zoneName = z_get_zonename();
quitSetZoneName(zoneName);
continue;
}
if (strncmp(p, PARENTZONENAME,
PARENTZONENAME_LEN) == 0) {
parentZoneName = p+PARENTZONENAME_LEN;
continue;
}
if (strncmp(p, PARENTZONETYPE,
PARENTZONETYPE_LEN) == 0) {
parentZoneType = p+PARENTZONETYPE_LEN;
continue;
}
if (strncmp(p, PKGSERV_MODE,
PKGSERV_MODE_LEN) == 0) {
pkgserversetmode(pkgparsemode(p +
PKGSERV_MODE_LEN));
continue;
}
progerr(ERR_INVALID_O_OPTION, p);
continue;
}
break;
case 'o':
script_in = PROC_XSTDIN;
break;
case 'R':
if (!set_inst_root(optarg)) {
progerr(ERR_ROOT_CMD);
exit(1);
}
break;
case 'V':
vfstab_file = flex_device(optarg, 2);
map_client = 1;
break;
case 'v':
pkgverbose++;
break;
case 'y':
set_nonABI_symlinks();
break;
default:
usage();
return (1);
}
}
(void) echoDebugSetFlag(debugFlag);
(void) log_set_verbose(debugFlag);
if (z_running_in_global_zone()) {
echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
} else {
echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
z_get_zonename());
}
if (cmdbin[0] == '\0') {
(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
}
if (get_mntinfo(map_client, vfstab_file)) {
quit(99);
}
set_PKGpaths(get_inst_root());
if (is_an_inst_root()) {
int fsys_value;
fsys_value = fsys(get_PKGLOC());
if (use_srvr_map_n(fsys_value))
set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
fsys_value = fsys(get_PKGADM());
if (use_srvr_map_n(fsys_value))
set_PKGADM(server_map(get_PKGADM(), fsys_value));
} else {
pkgrmremote = 0;
}
(void) sighold(SIGHUP);
(void) sighold(SIGINT);
nact.sa_handler = quitGetTrapHandler();
nact.sa_flags = SA_RESTART;
(void) sigemptyset(&nact.sa_mask);
(void) sigaction(SIGINT, &nact, &oact);
nact.sa_handler = quitGetTrapHandler();
nact.sa_flags = SA_RESTART;
(void) sigemptyset(&nact.sa_mask);
(void) sigaction(SIGHUP, &nact, &oact);
(void) sigrelse(SIGHUP);
(void) sigrelse(SIGINT);
pkginst = argv[optind++];
if (optind != argc) {
usage();
}
if (vcfile() == 0) {
quit(99);
}
if (!lockinst(get_prog_name(), pkginst, "remove-initial")) {
quit(99);
}
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL) {
tmpdir = P_tmpdir;
}
echoDebug(DBG_PKGREMOVE_TMPDIR, tmpdir);
echoDebug(DBG_PKGREMOVE_ADMINFILE, admnfile ? admnfile : "");
setadminFile(admnfile);
if (preremoveCheck == B_TRUE) {
(void) echoSetFlag(B_FALSE);
echoDebug(DBG_PKGREMOVE_PRERMCHK, pkginst ? pkginst : "",
zoneName ? zoneName : "global");
rcksetPreremoveCheck(B_TRUE);
rcksetZoneName(zoneName);
}
(void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s", get_PKGLOC(),
pkginst);
(void) snprintf(pkgbin, sizeof (pkgbin), "%s/install", pkgloc);
(void) snprintf(rlockfile, sizeof (rlockfile), "%s/!R-Lock!", pkgloc);
if (chdir(pkgbin)) {
progerr(ERR_CHDIR, pkgbin);
quit(99);
}
echo(MSG_PREREMOVE_REMINST, pkginst);
if (access(rlockfile, F_OK) == 0) {
echo(ERR_UNSUCC);
echoDebug(DBG_PKGINSTALL_HAS_LOCKFILE, pkginst, rlockfile,
zoneName ? zoneName : "global");
}
(void) snprintf(path, sizeof (path), "%s/pkginfo", pkgloc);
if ((fp = fopen(path, "r")) == NULL) {
progerr(ERR_PKGINFO, path);
quit(99);
}
if (map_client && !mount_client()) {
logerr(MSG_MANMOUNT);
}
client_mntdir = getenv("CLIENT_MNTDIR");
getuserlocale();
environ = NULL;
if (nonABI_symlinks()) {
putparam("PKG_NONABI_SYMLINKS", "TRUE");
}
param[0] = '\0';
while (value = fpkgparam(fp, param)) {
int validx = 0;
char *newvalue;
if (strcmp(param, "PATH") == 0) {
free(value);
param[0] = '\0';
continue;
}
if (strcmp(param, "PKGSAV") != 0) {
putparam(param, value);
free(value);
param[0] = '\0';
continue;
}
if (strstr(value, ":/")) {
validx = 0;
} else if (strstr(value, "//") == value) {
validx = 1;
} else if (is_an_inst_root()) {
newvalue = fixpath(value);
free(value);
value = newvalue;
}
putparam(param, value+validx);
free(value);
param[0] = '\0';
}
(void) fclose(fp);
putConditionInfo(parentZoneName, parentZoneType);
putuserlocale();
abi_comp_ptr = getenv("NONABI_SCRIPTS");
abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
set_nonABI_symlinks();
}
if (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0) {
script_in = PROC_XSTDIN;
}
if ((err = set_basedirs((getenv("BASEDIR") != NULL), adm.basedir,
pkginst, nointeract)) != 0) {
quit(err);
}
if (is_depend_pkginfo_DB()) {
pt = getenv(PKG_HOLLOW_VARIABLE);
if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
setadminSetting("conflict", "nocheck");
setadminSetting("setuid", "nocheck");
setadminSetting("action", "nocheck");
setadminSetting("partial", "nocheck");
setadminSetting("space", "nocheck");
setadminSetting("authentication", "nocheck");
} else {
echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
set_depend_pkginfo_DB(B_FALSE);
}
}
put_path_params();
if (client_mntdir != NULL) {
putparam("CLIENT_MNTDIR", client_mntdir);
}
if ((value = getenv("CLASSES")) != NULL) {
cl_sets(qstrdup(value));
} else {
progerr(ERR_CLASSES, path);
quit(99);
}
if (cmdbin[0] == '\0') {
(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
}
(void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
putparam("PATH", path);
putparam("TMPDIR", tmpdir);
if (value = getenv("ULIMIT")) {
if (assign_ulimit(value) == -1) {
progerr(ERR_BADULIMIT, value);
warnflag++;
}
putparam("PKG_ULIMIT", "TRUE");
}
if (preremoveCheck == B_TRUE) {
(void) fprintf(stdout, "rckrunlevel=%d\n", rckrunlevel());
(void) fprintf(stdout, "rckpriv=%d\n", rckpriv());
(void) fprintf(stdout, "rckdepend=%d\n", rckdepend());
echoDebug(DBG_PKGREMOVE_PRERMCHK_OK);
quit(0);
}
n = rckrunlevel();
if (n != 0) {
quit(n);
}
n = rckpriv();
if (n != 0) {
quit(n);
}
n = rckdepend();
if (n != 0) {
quit(n);
}
started++;
if ((fd = open(rlockfile, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
progerr(ERR_LOCKFILE, rlockfile);
quit(99);
} else {
(void) close(fd);
}
if (zoneName == (char *)NULL) {
echo(MSG_PKGREMOVE_PROCPKG_GZ);
echoDebug(DBG_PKGREMOVE_PROCPKG_GZ, pkginst, rlockfile);
} else {
echo(MSG_PKGREMOVE_PROCPKG_LZ, zoneName);
echoDebug(DBG_PKGREMOVE_PROCPKG_LZ, pkginst, rlockfile,
zoneName);
}
if (delmap(0, pkginst, &pkgserver, &tmpfp) != 0) {
progerr(ERR_DB_QUERY, pkginst);
quit(99);
}
lockupd("preremove");
(void) snprintf(script, sizeof (script), "%s/preremove", pkgbin);
if (access(script, F_OK) != 0) {
echoDebug(DBG_PKGREMOVE_POC_NONE, pkginst,
zoneName ? zoneName : "global");
} else if (nodelete) {
echoDebug(DBG_PKGREMOVE_POC_NODEL, pkginst, script,
zoneName ? zoneName : "global");
} else if (is_depend_pkginfo_DB()) {
echoDebug(DBG_PKGREMOVE_POC_DBUPD, pkginst, script,
zoneName ? zoneName : "global");
} else {
set_ulimit("preremove", ERR_PREREMOVE);
if (zoneName == (char *)NULL) {
echo(MSG_PKGREMOVE_EXEPOC_GZ);
echoDebug(DBG_PKGREMOVE_EXEPOC_GZ, pkginst, script);
} else {
echo(MSG_PKGREMOVE_EXEPOC_LZ, zoneName);
echoDebug(DBG_PKGREMOVE_EXEPOC_LZ, pkginst, script,
zoneName);
}
putparam("PKG_PROC_SCRIPT", "preremove");
if (pkgverbose) {
ckreturn(pkgexecl(script_in, PROC_STDOUT,
PROC_USER, PROC_GRP, SHELL, "-x",
script, NULL), ERR_PREREMOVE);
} else {
ckreturn(pkgexecl(script_in, PROC_STDOUT,
PROC_USER, PROC_GRP, SHELL, script,
NULL), ERR_PREREMOVE);
}
clr_ulimit();
}
lockupd("remove");
if (nodelete) {
echoDebug(DBG_PKGREMOVE_REM_NODEL, pkginst,
zoneName ? zoneName : "global");
} else if (is_depend_pkginfo_DB()) {
echoDebug(DBG_PKGREMOVE_REM_DBUPD, pkginst,
zoneName ? zoneName : "global");
} else {
echoDebug(DBG_PKGREMOVE_REM, pkginst,
zoneName ? zoneName : "global");
for (i = cl_getn() - 1; i >= 0; i--) {
rmclass(cl_nam(i), pkgrmremote, zoneName);
}
rmclass(NULL, pkgrmremote, zoneName);
}
z_destroyMountTable();
lockupd("postremove");
(void) snprintf(script, sizeof (script), "%s/postremove", pkgbin);
if (access(script, F_OK) != 0) {
echoDebug(DBG_PKGREMOVE_PIC_NONE, pkginst,
zoneName ? zoneName : "global");
} else if (nodelete) {
echoDebug(DBG_PKGREMOVE_PIC_NODEL, pkginst, script,
zoneName ? zoneName : "global");
} else if (is_depend_pkginfo_DB()) {
echoDebug(DBG_PKGREMOVE_PIC_DBUPD, pkginst, script,
zoneName ? zoneName : "global");
} else {
set_ulimit("postremove", ERR_POSTREMOVE);
if (zoneName == (char *)NULL) {
echo(MSG_PKGREMOVE_EXEPIC_GZ);
echoDebug(DBG_PKGREMOVE_EXEPIC_GZ, pkginst, script);
} else {
echo(MSG_PKGREMOVE_EXEPIC_LZ, zoneName);
echoDebug(DBG_PKGREMOVE_EXEPIC_LZ, pkginst, script,
zoneName);
}
putparam("PKG_PROC_SCRIPT", "postremove");
putparam("TMPDIR", tmpdir);
if (pkgverbose) {
ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
PROC_GRP, SHELL, "-x", script, NULL),
ERR_POSTREMOVE);
} else {
ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
PROC_GRP, SHELL, script, NULL),
ERR_POSTREMOVE);
}
clr_ulimit();
}
if (zoneName == (char *)NULL) {
echo(MSG_PKGREMOVE_UPDINF_GZ);
} else {
echo(MSG_PKGREMOVE_UPDINF_LZ, zoneName);
}
if (delmap(1, pkginst, &pkgserver, &tmpfp) != 0) {
progerr(ERR_DB_QUERY, pkginst);
quit(99);
}
if (!warnflag && !failflag) {
(void) chdir("/");
if (rrmdir(pkgloc))
warnflag++;
}
if ((z_running_in_global_zone() == B_TRUE) &&
(pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE)) {
boolean_t b;
b = pkgRemovePackageFromGzonlyList(get_inst_root(), pkginst);
if (b == B_FALSE) {
progerr(ERR_PKGREMOVE_GZONLY_REMOVE, pkginst);
ckreturn(1, NULL);
}
}
(void) unlockinst();
pkgcloseserver(pkgserver);
quit(0);
}
int
issymlink(char *path)
{
struct stat statbuf;
if (lstat(path, &statbuf) != 0) {
return (1);
}
if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
return (0);
}
return (1);
}
static void
rmclass(char *aclass, int rm_remote, char *a_zoneName)
{
struct cfent *ept;
FILE *fp = NULL;
char tmpfile[PATH_MAX];
char script[PATH_MAX];
int i;
char *tmp_path;
char *save_path = NULL;
struct stat st;
if (aclass == NULL) {
for (i = 0; i < eptnum; i++) {
if (eptlist[i] != NULL) {
rmclass(eptlist[i]->pkg_class,
rm_remote, a_zoneName);
}
}
return;
}
(void) snprintf(script, sizeof (script), "%s/r.%s", pkgbin, aclass);
if (access(script, F_OK) != 0) {
(void) snprintf(script, sizeof (script), "%s/r.%s",
PKGSCR, aclass);
if (access(script, F_OK) != 0)
script[0] = '\0';
}
if (script[0] != '\0') {
int td;
(void) snprintf(tmpfile, sizeof (tmpfile), "%s/RMLISTXXXXXX",
tmpdir);
td = mkstemp(tmpfile);
if (td == -1) {
progerr(ERR_TMPFILE);
quit(99);
}
if ((fp = fdopen(td, "w")) == NULL) {
progerr(ERR_WTMPFILE, tmpfile);
quit(99);
}
}
if (a_zoneName == (char *)NULL) {
echo(MSG_PKGREMOVE_REMPATHCLASS_GZ, aclass);
} else {
echo(MSG_PKGREMOVE_REMPATHCLASS_LZ, aclass, a_zoneName);
}
i = eptnum;
while (--i >= 0) {
ept = eptlist[i];
if ((ept == NULL) || strcmp(aclass, ept->pkg_class)) {
continue;
}
if (is_an_inst_root()) {
save_path = ept->path;
tmp_path = fixpath(ept->path);
ept->path = tmp_path;
}
if (!ept->ftype || (ept->ftype == '^' && !script[0])) {
echo(MSG_SHARED, ept->path);
} else if (ept->pinfo->status == SERVED_FILE && !rm_remote) {
echo(MSG_SERVER, ept->path);
} else if (script[0]) {
(void) fprintf(fp, "%s\n", ept->path);
} else if (strchr("dx", ept->ftype) != NULL ||
(lstat(ept->path, &st) == 0 && S_ISDIR(st.st_mode))) {
if (rmdir(ept->path)) {
if (errno == EBUSY) {
echo(MSG_DIRBUSY, ept->path);
} else if (errno == EEXIST) {
echo(MSG_NOTEMPTY, ept->path);
} else if (errno != ENOENT) {
progerr(ERR_RMDIR, ept->path);
warnflag++;
}
} else {
if (ept->pinfo->status == SERVED_FILE) {
echo(MSG_RMSRVR, ept->path);
} else {
echo("%s", ept->path);
}
}
} else {
if (ept->npkgs > 1) {
echo(MSG_SHARED, ept->path);
continue;
}
if (unlink(ept->path)) {
if (errno != ENOENT) {
progerr(ERR_RMPATH, ept->path);
warnflag++;
}
} else {
if (ept->pinfo->status == SERVED_FILE) {
echo(MSG_RMSRVR, ept->path);
} else {
echo("%s", ept->path);
}
}
}
if (is_an_inst_root()) {
ept->path = save_path;
}
if (eptlist[i]) {
free(eptlist[i]);
}
eptlist[i] = NULL;
}
if (script[0]) {
(void) fclose(fp);
set_ulimit(script, ERR_CASFAIL);
if (pkgverbose)
ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
CAS_GRP, SHELL, "-x", script, NULL),
ERR_CASFAIL);
else
ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
CAS_GRP, SHELL, script, NULL),
ERR_CASFAIL);
clr_ulimit();
if (isfile(NULL, tmpfile) == 0) {
if (unlink(tmpfile) == -1)
progerr(ERR_RMPATH, tmpfile);
}
}
}
static void
ckreturn(int retcode, char *msg)
{
switch (retcode) {
case 2:
case 12:
case 22:
warnflag++;
if (msg)
progerr(msg);
case 10:
case 20:
if (retcode >= 10)
dreboot++;
if (retcode >= 20)
ireboot++;
case 0:
break;
case -1:
retcode = 99;
case 99:
case 1:
case 11:
case 21:
case 4:
case 14:
case 24:
case 5:
case 15:
case 25:
if (msg)
progerr(msg);
case 3:
case 13:
case 23:
quit(retcode);
default:
if (msg)
progerr(msg);
quit(1);
}
}
static void
usage(void)
{
(void) fprintf(stderr, ERR_USAGE_PKGREMOVE);
exit(1);
}