wsreg_pkgrm.c revision 5c51f1241dbbdf2656d0e10011981411ed0c9673
/*
* 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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* Background information:
*
* In the past, pkgrm did not check whether a package was needed by
* products in the product registry. The only check that pkgrm does
* is whether any packages depend on the package to be removed. This
* meant that it was trivial to use pkgrm correctly and damage products
* (installed by webstart wizards) - without even receiving a warning.
*
* This enhancement to pkgrm will determine if the package to remove is
* needed by any registered products. If not, a '0' is returned and the
* pkgrm can proceed. If there is a conflict, nonzero is returned and
* a list of all products which will be effected. Note that removing
* one package may damage several products. This is because some
* packages are used by several products, and some components are shared
* by several products.
*
* The list returned is a string, which the caller must free by calling
* free().
*
* The purpose of the list is to inform the user, exactly as is done with
* the 'depends' information. The user must be presented with the list
* as a warning and be able to either abort the operation or proceed -
* well advised of the consequences.
*
* How this works
*
* Installed products are associated with 'components' in a product
* registry database. Components in the product registry are often
* associated with packages. Packages are the mechanism in which
* software is actually installed, on Solaris. For example, when a
* webstart wizard install occurs, one or more packages are added.
* These are associated with 'components' (install metadata containers)
* in the product registry. The product registry interface acts as
* though these packages *really are* installed.
*
* In order to ensure that this remains the case, the product registry
* is examined for instances of a package before that package is removed.
*
* See libwsreg(3LIB) for general information about the product
* registry library used to determine if removing a package is OK.
*
* See prodreg(1M) for information about a tool which can be used
* to inspect the product registry. Any component which has an
* attribute 'pkgs' will list those packages which cannot be removed
* safely. For example: 'pkgs= SUNWfoo SUNWbar' would imply that
* neither SUNWfoo or SUNWbar can be removed.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include "wsreg_pkgrm.h"
struct dstrp {
char **ppc;
int len;
int max;
};
Wsreg_component *, int *, const char *);
static char *get_locale();
/*
* wsreg_pkgrm_check
*
* This routine determines if removing a particular package will
* 'damage' a product.
*
* pcRoot IN: The alternate root directory. If this parameter
* is NULL - then the root "/" is assumed.
*
* pcPKG IN: The name of the package to remove (a normal NULL-
* terminated string.)
* This parameter must not be NULL.
*
* pppcID OUT: The location of a char ** pointer is passed in.
* This parameter must not be NULL. The result
* will be a NULL terminated array of ID strings.
* The caller must free both the array of strings
* and each individual string. Example:
*
* char ** ppcID;
* int i;
*
* if (wsreg_pkgrm_check(NULL, "SUNWblah", &ppcID, ..)
* > 0) {
*
* for (i = 0; ppcID[i]; i++) {
* do_something(ppcID[i]);
* free(ppcID[i]);
* }
* free(ppcID);
* }
*
* pppcName OUT: As pppcID, except this contains the human readable
* localized name of the component. The index of the
* name array coincides with that of the ID array, so
* there will be the same number of items in both and
* the component whose name is *pppcName[0] has the
* id *pppcID[0].
*
* Returns: 0 if there is no problem. pkgrm my proceed.
* positive - there is a conflict. pppcID & pppcName return strings.
* negative - there was a problem running this function.
* Error conditions include: (errno will be set)
* ENOENT The pcRoot directory was not valid.
* ENOMEM The string to return could not be allocated.
* EACCES The registry database could not be read.
*
* Side effects: The pppcID and pppcName parameters may be changed and set
* to the value of arrays of strings which the caller must free.
*/
int
{
int i, r;
char *locale = get_locale();
locale = "en";
return (-1);
}
errno = 0;
r = 0; /* A return value 0 indicates nothing was found. */
pcRoot = "/";
wsreg_can_access_registry(O_RDONLY) == 0) {
return (-1);
}
ppws = wsreg_get_all();
locale);
int depth;
depth = 0;
r = 1;
r = -1;
break;
}
}
}
if (r > 0) {
}
return (r);
}
/*
* in_list
*
* pcList A white space delimited list of words (non-white characters)
* pcItem A word (not NULL, an empty string or containing white space)
*
* Returns 0 if pcItem is not in pcList. nonzero if pcItem is in pcList
* Side effects: None
*/
static int
{
int i = 0, j = 0, k = 0;
return (0);
while (pcList[i] != '\0') {
if (i == j) {
i++;
j++;
} else {
if ((i - j) == k &&
return (1);
} else {
j = i;
}
}
} else {
i++;
}
/* last element in the list case */
if (pcList[i] == '\0' && j < i &&
return (1);
}
return (0);
}
#define APPEND_INCR 20
/*
* append_dstrp
*
* This routine manages a dynamic array of strings in a very minimal way.
* It assumes it has been passed a cleared struct dstrp = { NULL, 0, 0 }
* It will add the appended string to the end of the array. When needed,
* the array of strings is grown to the next APPEND_INCR in size.
*
* Note this routine is different than append_dstr since that accumulates
* char, this accumulates char *.
*
* pd The dynamic string. Must be initialized to {NULL,0,0}. Must not
* be NULL.
*
* str The string to add. May be of 0 length. If NULL, a string of 0
* length will be added (NOT a NULL).
*
* Returns: 0 if OK, -1 if malloc failed.
* Side effects: The value of pd->ppc[pd->len] changes, taking strdup(str)
* The final entry in the array will be NULL. There will be pd->len
* entries. To free this, free each string in the array and the array
* itself. The caller must free the allocated memory.
*/
static int
{
/* Initialize if necessary */
return (-1);
/*
* Grow the array.
* Always leave room for a single NULL end item: That is
* why we grow when +2 equals the max, not +1.
*/
return (-1);
} else {
APPEND_INCR * sizeof (char *));
}
}
} else {
return (-1);
}
return (0);
}
#define DEPTH_MAX 100
/*
* get_all_dependents_r
*
* This routine accumulates the id and name of all components which
* depend (directly or indirectly) on a component which has a pkg which
* may be removed. By calling this routine recursively, the entire list
* of existing dependencies can be accumulated.
*
* id The dynamic accumulation of all ids of dependent components.
* nm The dynamic accumulation of all names of dep. components.
* pws The component to check for dependencies, record their
* ids and names, then call check these components for redun-
* dancy also.
* pdepth The depth of the recursion. This must be set to 0 upon the
* first call to this function. Only DEPTH_MAX calls will be
* attempted.
* locale The locale to use for querying for display names.
*
* Return value: None.
* Side effects. strings will be added to id and nm. The depth counter
* will increase.
*/
static void
{
int i;
/* Get the list of dependent components. */
return;
locale = "en";
return;
/*
* Prevent infinite loops in the case where there is a cycle
* in the dependency graph. Such a cycle should never happen,
* but a clueless user of the libwsreg API could construct such
* a failure case. This is defensive programming.
*/
return;
(*pdepth)++;
for (i = 0; ppws[i]; i++) {
/*
* Errors in append_dstrp happen only due to malloc
* failing on small allocations. If we fail here
* this is the least of the user's problems. We
* can just stop accumulating new info at this point.
*/
return;
}
}
/*
* init_locale
*
* Set locale and textdomain for localization. Note that the return value
* of setlocale is the locale string. It is in the form
*
* "/" LC_CTYPE "/" LC_COLLATE "/" LC_CTIME "/" LC_NUMERIC "/"
* LC_MONETARY "/ LC_MESSAGES
*
* This routine parses this result line to determine the value of
* the LC_MESSAGES field. If it is "C", the default language "en"
* is selected. If not, the string is disected to get only the
* ISO 639 two letter tag: "en_US.ISO8859-1" becomes "en".
*
* Returns: Returns a newly allocated language tag string.
* Returns NULL if setlocale() returns a null pointer.
* Side effects:
* (1) setlocale changes behavior of the application.
*/
static char *
{
int i = 0, c, n;
char lang[32];
return (NULL);
}
if (pc[0] == '/') {
/* Skip to the 6th field, which is 'LC_MESSAGES.' */
c = 0;
if (pc[i] == '/') c++;
}
/* Strip off any dialect tag and character encoding. */
n = 0;
}
}
if (i > 2) {
} else {
}
} else {
}
return (tag);
}