/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <locale.h>
#include <libintl.h>
#include <errno.h>
#include "pkglib.h"
#include "install.h"
#include "libadm.h"
#include "libinst.h"
#include "pkginstall.h"
#include "messages.h"
extern char instdir[], pkgbin[], pkgloc[], savlog[], *pkginst, **environ;
extern char saveSpoolInstallDir[];
extern char pkgsav[]; /* pkginstall/main.c */
static char *infoloc;
/*
* flag definitions for each entry in table
*/
typedef unsigned int TBL_FLAG_T;
/* no flag set */
#define FLAG_NONE ((TBL_FLAG_T)0x0000)
/* exclude this attribute if found */
#define FLAG_EXCLUDE ((TBL_FLAG_T)0x0001)
/* this attribute must not change if found */
#define FLAG_IDENTICAL ((TBL_FLAG_T)0x0002)
/*
* macro to generate an entry in the table:
* TBL_ENTRY("PKGINFO_ATTRIBUTE=", FLAG_XXX)
* where:
* "PKGINFO_ATTRIBUTE=" is the attribute to look for
* FLAG_XXX is the action to perform when the attribute is found
*/
#define TBL_ENTRY(_Y_, _F_) { (_Y_), ((sizeof ((_Y_)))-1), (_F_) }
/*
* table containing attributes that require special handling
*/
struct _namelist {
char *_nlName; /* attribute name */
int _nlLen; /* attribute length */
TBL_FLAG_T _nlFlag; /* attribute disposition flag */
};
typedef struct _namelist NAMELIST_T;
/*
* These are attributes to be acted on in some way when a pkginfo file is
* merged. This table MUST be in alphabetical order because it is searched
* using a binary search algorithm.
*/
static NAMELIST_T attrTbl[] = {
TBL_ENTRY("BASEDIR=", FLAG_EXCLUDE),
TBL_ENTRY("CLASSES=", FLAG_EXCLUDE),
TBL_ENTRY("CLIENT_BASEDIR=", FLAG_EXCLUDE),
TBL_ENTRY("INST_DATADIR=", FLAG_EXCLUDE),
TBL_ENTRY("PKG_CAS_PASSRELATIVE=", FLAG_EXCLUDE),
TBL_ENTRY("PKG_DST_QKVERIFY=", FLAG_EXCLUDE),
TBL_ENTRY("PKG_INIT_INSTALL=", FLAG_EXCLUDE),
TBL_ENTRY("PKG_INSTALL_ROOT=", FLAG_EXCLUDE),
TBL_ENTRY("PKG_SRC_NOVERIFY=", FLAG_EXCLUDE),
TBL_ENTRY("SUNW_PKGCOND_GLOBAL_DATA=", FLAG_EXCLUDE),
TBL_ENTRY("SUNW_PKG_ALLZONES=", FLAG_IDENTICAL),
TBL_ENTRY("SUNW_PKG_DIR=", FLAG_EXCLUDE),
TBL_ENTRY("SUNW_PKG_HOLLOW=", FLAG_IDENTICAL),
TBL_ENTRY("SUNW_PKG_INSTALL_ZONENAME=", FLAG_EXCLUDE),
TBL_ENTRY("SUNW_PKG_THISZONE=", FLAG_IDENTICAL),
};
#define ATTRTBL_SIZE (sizeof (attrTbl) / sizeof (NAMELIST_T))
/*
* While pkgsav has to be set up with reference to the server for package
* scripts, it has to be client-relative in the pkginfo file. This function
* is used to set the client-relative value for use in the pkginfo file.
*/
void
set_infoloc(char *path)
{
if (path && *path) {
if (is_an_inst_root()) {
/* Strip the server portion of the path. */
infoloc = orig_path(path);
} else {
infoloc = strdup(path);
}
}
}
void
merginfo(struct cl_attr **pclass, int install_from_pspool)
{
DIR *pdirfp;
FILE *fp;
FILE *pkginfoFP;
char path[PATH_MAX];
char cmd[PATH_MAX];
char pkginfoPath[PATH_MAX];
char temp[PATH_MAX];
int i;
int nc;
int out;
/* remove savelog from previous attempts */
(void) unlink(savlog);
/*
* create path to appropriate pkginfo file for the package that is
* already installed - is_spool_create() will be set (!= 0) if the
* -t option is presented to pkginstall - the -t option is used to
* disable save spool area creation; do not spool any partial package
* contents, that is, suppress the creation and population of the
* package save spool area (var/sadm/pkg/PKG/save/pspool/PKG). This
* option is set only when a non-global zone is being created.
*/
if (is_spool_create() == 0) {
/*
* normal package install (not a non-global zone install);
* use the standard installed pkginfo file for this package:
* --> /var/sadm/pkg/PKGINST/pkginfo
* as the source pkginfo file to scan.
*/
i = snprintf(pkginfoPath, sizeof (pkginfoPath),
"%s/var/sadm/pkg/%s/%s",
((get_inst_root()) &&
(strcmp(get_inst_root(), "/") != 0)) ?
get_inst_root() : "", pkginst,
PKGINFO);
if (i > sizeof (pkginfoPath)) {
progerr(ERR_CREATE_PATH_2,
((get_inst_root()) &&
(strcmp(get_inst_root(), "/") != 0)) ?
get_inst_root() : "/",
pkginst);
quit(1);
}
} else {
/*
* non-global zone installation - use the "saved" pspool
* pkginfo file in the global zone for this package:
* --> /var/sadm/install/PKG/save/pspool/PKG/pkginfo
* as the source pkginfo file to scan.
*/
i = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/%s",
saveSpoolInstallDir, PKGINFO);
if (i > sizeof (pkginfoPath)) {
progerr(ERR_CREATE_PATH_2,
saveSpoolInstallDir, PKGINFO);
quit(1);
}
}
i = snprintf(path, PATH_MAX, "%s/%s", pkgloc, PKGINFO);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_2, pkgloc, PKGINFO);
quit(1);
}
/* entry debugging info */
echoDebug(DBG_MERGINFO_ENTRY,
instdir ? instdir : "??",
((get_inst_root()) &&
(strcmp(get_inst_root(), "/") != 0)) ?
get_inst_root() : "??",
saveSpoolInstallDir ? saveSpoolInstallDir : "??",
pkgloc ? pkgloc : "??", is_spool_create(),
get_info_basedir() ? get_info_basedir() : "??",
pkginfoPath, path);
/*
* open the pkginfo file:
* if the source pkginfo file to check is the same as the merged one
* (e.g. /var/sadm/pkg/PKGINST/pkginfo) then do not open the source
* pkginfo file to "verify"
*/
if (strcmp(pkginfoPath, path) == 0) {
pkginfoFP = (FILE *)NULL;
echoDebug(DBG_MERGINFO_SAME, path);
} else {
echoDebug(DBG_MERGINFO_DIFFERENT, pkginfoPath, path);
pkginfoFP = fopen(pkginfoPath, "r");
if (pkginfoFP == (FILE *)NULL) {
echoDebug(ERR_NO_PKG_INFOFILE, pkginst, pkginfoPath,
strerror(errno));
}
}
/*
* output packaging environment to create a pkginfo file in pkgloc[]
*/
if ((fp = fopen(path, "w")) == NULL) {
progerr(ERR_CANNOT_OPEN_FOR_WRITING, path, strerror(errno));
quit(99);
}
/*
* output CLASSES attribute
*/
out = 0;
(void) fputs("CLASSES=", fp);
if (pclass) {
(void) fputs(pclass[0]->name, fp);
out++;
for (i = 1; pclass[i]; i++) {
(void) putc(' ', fp);
(void) fputs(pclass[i]->name, fp);
out++;
}
}
nc = cl_getn();
for (i = 0; i < nc; i++) {
int found = 0;
if (pclass) {
int j;
for (j = 0; pclass[j]; ++j) {
if (cl_nam(i) != NULL &&
strcmp(cl_nam(i),
pclass[j]->name) == 0) {
found++;
break;
}
}
}
if (!found) {
if (out > 0) {
(void) putc(' ', fp);
}
(void) fputs(cl_nam(i), fp);
out++;
}
}
(void) putc('\n', fp);
/*
* NOTE : BASEDIR below is relative to the machine that
* *runs* the package. If there's an install root, this
* is actually the CLIENT_BASEDIR wrt the machine
* doing the pkgadd'ing here. -- JST
*/
if (is_a_basedir()) {
static char *txs1 = "BASEDIR=";
(void) fputs(txs1, fp);
(void) fputs(get_info_basedir(), fp);
(void) putc('\n', fp);
} else {
(void) fputs("BASEDIR=/", fp);
(void) putc('\n', fp);
}
/*
* output all other environment attributes except those which
* are relevant only to install.
*/
for (i = 0; environ[i] != (char *)NULL; i++) {
char *ep = environ[i];
int attrPos = -1;
int incr = (ATTRTBL_SIZE >> 1)+1; /* searches possible */
int pos = ATTRTBL_SIZE >> 1; /* start in middle */
NAMELIST_T *pp = (NAMELIST_T *)NULL;
/*
* find this attribute in the table - accept the attribute if it
* is outside of the bounds of the table; otherwise, do a binary
* search looking for this attribute.
*/
if (strncmp(ep, attrTbl[0]._nlName, attrTbl[0]._nlLen) < 0) {
/* entry < first entry in attribute table */
echoDebug(DBG_MERGINFO_LESS_THAN, ep,
attrTbl[0]._nlName);
} else if (strncmp(ep, attrTbl[ATTRTBL_SIZE-1]._nlName,
attrTbl[ATTRTBL_SIZE-1]._nlLen) > 0) {
/* entry > last entry in attribute table */
echoDebug(DBG_MERGINFO_GREATER_THAN, ep,
attrTbl[ATTRTBL_SIZE-1]._nlName);
} else {
/* first entry < entry < last entry in table: search */
echoDebug(DBG_MERGINFO_SEARCHING, ep,
attrTbl[0]._nlName,
attrTbl[ATTRTBL_SIZE-1]._nlName);
while (incr > 0) { /* while possible to divide */
int r;
pp = &attrTbl[pos];
/* compare current attr with this table entry */
r = strncmp(pp->_nlName, ep, pp->_nlLen);
/* break out of loop if match */
if (r == 0) {
/* save location/break if match found */
attrPos = pos;
break;
}
/* no match search to next/prev half */
incr = incr >> 1;
pos += (r < 0) ? incr : -incr;
continue;
}
}
/* handle excluded attribute found */
if ((attrPos >= 0) && (pp->_nlFlag == FLAG_EXCLUDE)) {
/* attribute is excluded */
echoDebug(DBG_MERGINFO_EXCLUDING, ep);
continue;
}
/* handle fixed attribute found */
if ((pkginfoFP != (FILE *)NULL) && (attrPos >= 0) &&
(pp->_nlFlag == FLAG_IDENTICAL)) {
/* attribute must not change */
char *src = ep+pp->_nlLen;
char *trg;
char theAttr[PATH_MAX+1];
/* isolate attribute name only without '=' at end */
(void) strncpy(theAttr, pp->_nlName, pp->_nlLen-1);
theAttr[pp->_nlLen-1] = '\0';
/* lookup attribute in installed package pkginfo file */
rewind(pkginfoFP);
trg = fpkgparam(pkginfoFP, theAttr);
echoDebug(DBG_MERGINFO_ATTRCOMP, theAttr,
trg ? trg : "");
/* if target not found attribute is being added */
if (trg == (char *)NULL) {
progerr(ERR_PKGINFO_ATTR_ADDED, pkginst, ep);
quit(1);
}
/* error if two values are not the same */
if (strcmp(src, trg) != 0) {
progerr(ERR_PKGINFO_ATTR_CHANGED, pkginst,
theAttr, src, trg);
quit(1);
}
}
/* attribute not excluded/has not changed - process */
if ((strncmp(ep, "PKGSAV=", 7) == 0)) {
(void) fputs("PKGSAV=", fp);
(void) fputs(infoloc, fp);
(void) putc('/', fp);
(void) fputs(pkginst, fp);
(void) fputs("/save\n", fp);
continue;
}
if ((strncmp(ep, "UPDATE=", 7) == 0) &&
install_from_pspool != 0 &&
!isUpdate()) {
continue;
}
echoDebug(DBG_MERGINFO_FINAL, ep);
(void) fputs(ep, fp);
(void) putc('\n', fp);
}
(void) fclose(fp);
(void) fclose(pkginfoFP);
/*
* copy all packaging scripts to appropriate directory
*/
i = snprintf(path, PATH_MAX, "%s/install", instdir);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_2, instdir, "/install");
quit(1);
}
if ((pdirfp = opendir(path)) != NULL) {
struct dirent *dp;
while ((dp = readdir(pdirfp)) != NULL) {
if (dp->d_name[0] == '.')
continue;
i = snprintf(path, PATH_MAX, "%s/install/%s",
instdir, dp->d_name);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_3, instdir, "/install/",
dp->d_name);
quit(1);
}
i = snprintf(temp, PATH_MAX, "%s/%s", pkgbin,
dp->d_name);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_2, pkgbin, dp->d_name);
quit(1);
}
if (cppath(MODE_SRC|DIR_DISPLAY, path, temp, 0644)) {
progerr(ERR_CANNOT_COPY, dp->d_name, pkgbin);
quit(99);
}
}
(void) closedir(pdirfp);
}
/*
* copy all packaging scripts to the partial spool directory
*/
if (!is_spool_create()) {
/* packages are being spooled to ../save/pspool/.. */
i = snprintf(path, PATH_MAX, "%s/install", instdir);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_2, instdir, "/install");
quit(1);
}
if ((pdirfp = opendir(path)) != NULL) {
struct dirent *dp;
while ((dp = readdir(pdirfp)) != NULL) {
if (dp->d_name[0] == '.')
continue;
/*
* Don't copy i.none since if it exists it
* contains Class Archive Format procedure
* for installing archives. Only Directory
* Format packages can exist
* in a global spooled area.
*/
if (strcmp(dp->d_name, "i.none") == 0)
continue;
i = snprintf(path, PATH_MAX, "%s/install/%s",
instdir, dp->d_name);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_3, instdir,
"/install/", dp->d_name);
quit(1);
}
i = snprintf(temp, PATH_MAX, "%s/install/%s",
saveSpoolInstallDir,
dp->d_name);
if (i > PATH_MAX) {
progerr(ERR_CREATE_PATH_3,
saveSpoolInstallDir,
"/install/", dp->d_name);
quit(1);
}
if (cppath(MODE_SRC, path, temp, 0644)) {
progerr(ERR_CANNOT_COPY, path, temp);
(void) closedir(pdirfp);
quit(99);
}
}
(void) closedir(pdirfp);
}
/*
* Now copy the original pkginfo and pkgmap files from the
* installing package to the spooled directory.
*/
i = snprintf(path, sizeof (path), "%s/%s", instdir, PKGINFO);
if (i > sizeof (path)) {
progerr(ERR_CREATE_PATH_2, instdir, PKGINFO);
quit(1);
}
i = snprintf(temp, sizeof (temp), "%s/%s",
saveSpoolInstallDir, PKGINFO);
if (i > sizeof (temp)) {
progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
PKGINFO);
quit(1);
}
if (cppath(MODE_SRC, path, temp, 0644)) {
progerr(ERR_CANNOT_COPY, path, temp);
quit(99);
}
i = snprintf(path, sizeof (path), "%s/pkgmap", instdir);
if (i > sizeof (path)) {
progerr(ERR_CREATE_PATH_2, instdir, "pkgmap");
quit(1);
}
i = snprintf(temp, sizeof (temp), "%s/pkgmap",
saveSpoolInstallDir);
if (i > sizeof (path)) {
progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
"pkgmap");
quit(1);
}
if (cppath(MODE_SRC, path, temp, 0644)) {
progerr(ERR_CANNOT_COPY, path, temp);
quit(99);
}
}
/*
* If we are installing from a spool directory
* copy the save directory from it, it may have
* been patched. Duplicate it only if this
* installation isn't an update and is not to
* an alternate root.
*/
if (strstr(instdir, "pspool") != NULL) {
struct stat status;
i = snprintf(path, sizeof (path), "%s/save", instdir);
if (i > sizeof (path)) {
progerr(ERR_CREATE_PATH_2, instdir, "save");
quit(1);
}
if ((stat(path, &status) == 0) && (status.st_mode & S_IFDIR)) {
i = snprintf(cmd, sizeof (cmd), "cp -pr %s/* %s",
path, pkgsav);
if (i > sizeof (cmd)) {
progerr(ERR_SNPRINTF, "cp -pr %s/* %s");
quit(1);
}
if (system(cmd)) {
progerr(ERR_PKGBINCP, path, pkgsav);
quit(99);
}
}
}
}