libzonecfg.c revision 44ce818bb6485101baba3d69cefccb5a711476e9
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libsysevent.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <fnmatch.h>
#include <strings.h>
#include <unistd.h>
#include <assert.h>
#include <libgen.h>
#include <libintl.h>
#include <alloca.h>
#include <ctype.h>
#include <ftw.h>
#include <pool.h>
#include <libscf.h>
#include <libproc.h>
#include <sys/priocntl.h>
#include <libuutil.h>
#include <netdb.h>
#include <libxml/xmlmemory.h>
#include <libdevinfo.h>
#include <dirent.h>
#include <libbrand.h>
#include <libzonecfg.h>
#include "zonecfg_impl.h"
#define _PATH_TMPFILE "/zonecfg.XXXXXX"
#define ZONE_CB_RETRY_COUNT 10
#define ZONE_EVENT_PING_SUBCLASS "ping"
#define ZONE_EVENT_PING_PUBLISHER "solaris"
#define DTD_ENTITY_BOOLEAN "boolean"
#define DTD_ENTITY_DEVPATH "devpath"
#define DTD_ENTITY_DRIVER "driver"
#define DTD_ENTITY_DRVMIN "drv_min"
#define DTD_ENTITY_FALSE "false"
#define DTD_ENTITY_INT "int"
#define DTD_ENTITY_STRING "string"
#define DTD_ENTITY_TRUE "true"
#define DTD_ENTITY_UINT "uint"
#define DETACHED "SUNWdetached.xml"
#define ATTACH_FORCED "SUNWattached.xml"
#define CONTENTS_FILE "/var/sadm/install/contents"
#define SUNW_PKG_ALL_ZONES "SUNW_PKG_ALLZONES=true\n"
#define SUNW_PKG_THIS_ZONE "SUNW_PKG_THISZONE=true\n"
#define VERSION "VERSION="
#define PATCHLIST "PATCHLIST="
#define PATCHINFO "PATCH_INFO_"
#define PKGINFO_RD_LEN 128
#define TMP_POOL_NAME "SUNWtmp_%s"
#define RCAP_SERVICE "system/rcap:default"
#define POOLD_SERVICE "system/pools/dynamic:default"
/*
* rctl alias definitions
*
* This holds the alias, the full rctl name, the default priv value, action
* and lower limit. The functions that handle rctl aliases step through
* this table, matching on the alias, and using the full values for setting
* the rctl entry as well the limit for validation.
*/
static struct alias {
char *shortname;
char *realname;
char *priv;
char *action;
} aliases[] = {
};
/*
* Structure for applying rctls to a running zone. It allows important
* process values to be passed together easily.
*/
typedef struct pr_info_handle {
struct ps_prochandle *pr;
struct zone_dochandle {
char *zone_dh_rootdir;
char zone_dh_delete_name[ZONENAME_MAX];
};
struct znotify {
void * zn_private;
char zn_subscriber_id[MAX_SUBID_LEN];
int zn_failure_count;
};
struct zone_pkginfo {
int zpi_patch_cnt;
char *zpi_version;
char **zpi_patchinfo;
};
typedef struct {
char *patch_num;
char *patch_vers;
} patch_node_t;
typedef struct {
char *patch_num;
typedef struct {
int res;
char *zonecfg_root = "";
/*
* For functions which return int, which is most of the functions herein,
* the return values should be from the Z_foo set defined in <libzonecfg.h>.
* In some instances, we take pains mapping some libc errno values to Z_foo
* values from this set.
*/
/*
* Set the root (/) path for all zonecfg configuration files. This is a
* private interface used by Live Upgrade extensions to access zone
* configuration inside mounted alternate boot environments.
*/
void
zonecfg_set_root(const char *rootpath)
{
if (*zonecfg_root != '\0')
zonecfg_root = "";
}
const char *
zonecfg_get_root(void)
{
return (zonecfg_root);
}
zonecfg_in_alt_root(void)
{
return (*zonecfg_root != '\0');
}
/*
* Callers of the _file_path() functions are expected to have the second
* parameter be a (char foo[MAXPATHLEN]).
*/
static boolean_t
{
}
static boolean_t
{
}
/*ARGSUSED*/
static void
{
/*
* This function does nothing by design. Its purpose is to prevent
*/
}
zonecfg_init_handle(void)
{
return (NULL);
}
/* generic libxml initialization */
(void) xmlKeepBlanksDefault(0);
return (handle);
}
int
{
return (Z_BAD_HANDLE);
return (Z_OK);
}
void
{
}
static int
zonecfg_destroy_impl(char *filename)
{
return (Z_ACCES);
return (Z_NO_ZONE);
return (Z_MISC_FS);
}
return (Z_OK);
}
int
{
char path[MAXPATHLEN];
return (Z_MISC_FS);
/*
* If there is no file, and no index entry, reliably indicate that no
* such zone exists.
*/
return (Z_NO_ZONE);
/*
* Handle any other filesystem related errors (except if the XML
* file is missing, which we treat silently), unless we're forcing,
* in which case we plow on.
*/
return (Z_ACCES);
else if (!force)
return (Z_MISC_FS);
}
if (state > ZONE_STATE_INSTALLED)
return (Z_BAD_ZONE_STATE);
return (Z_BAD_ZONE_STATE);
/*
* Index deletion succeeds even if the entry doesn't exist. So this
* will fail only if we've had some more severe problem.
*/
if (!force)
return (err);
/*
* Treat failure to find the XML file silently, since, well, it's
* gone, and with the index file cleaned up, we're done.
*/
return (Z_OK);
return (err);
}
int
zonecfg_destroy_snapshot(const char *zonename)
{
char path[MAXPATHLEN];
return (Z_MISC_FS);
return (zonecfg_destroy_impl(path));
}
static int
{
return (Z_BAD_HANDLE);
return (Z_EMPTY_DOCUMENT);
return (Z_WRONG_DOC_TYPE);
return (Z_OK);
}
static int
{
int err;
return (err);
return (Z_OK);
}
static int
{
return (Z_BAD_PROPERTY);
return (Z_TOO_BIG);
return (Z_OK);
}
static int
{
return (Z_BAD_PROPERTY);
return (Z_NOMEM);
}
return (Z_OK);
}
static int
{
int err;
return (err);
}
static int
char **propval)
{
int err;
return (err);
}
static int
const char *propval)
{
int err;
return (err);
/*
* If we get a null propval remove the property (ignore return since it
* may not be set to begin with).
*/
} else {
== NULL)
return (Z_INVAL);
}
return (Z_OK);
}
static void
{
}
static void
{
continue;
}
}
}
static void
{
continue;
}
}
}
static int
{
int valid;
return (Z_NO_ZONE);
/* distinguish file not found vs. found but not parsed */
return (Z_INVALID_DOCUMENT);
return (Z_NO_ZONE);
}
return (Z_NOMEM);
if (valid == 0)
return (Z_INVALID_DOCUMENT);
/* delete any comments such as inherited Sun copyright / ident str */
return (Z_OK);
}
int
{
char path[MAXPATHLEN];
return (Z_MISC_FS);
}
int
{
char migpath[MAXPATHLEN];
int err;
sizeof (migpath))
return (Z_NOMEM);
return (Z_NO_ZONE);
sizeof (migpath))
return (Z_NOMEM);
return (err);
if (!preserve_sw)
return (err);
}
int
{
char path[MAXPATHLEN];
return (Z_MISC_FS);
}
int
{
char path[MAXPATHLEN];
int err;
return (Z_MISC_FS);
return (err);
}
int
{
int err;
return (Z_MISC_FS);
return (err);
return (Z_OK);
}
/*
* Initialize two handles from the manifest read on fd. The rem_handle
* is initialized from the input file, including the sw inventory. The
* local_handle is initialized with the same zone configuration but with
* no sw inventory.
*/
int
{
int valid;
/* load the manifest into the handle for the remote system */
return (Z_INVALID_DOCUMENT);
}
return (Z_NOMEM);
if (valid == 0)
return (Z_INVALID_DOCUMENT);
/* delete any comments such as inherited Sun copyright / ident str */
/*
* Now use the remote system handle to generate a local system handle
* with an identical zones configuration but no sw inventory.
*/
1)) == NULL) {
return (Z_INVALID_DOCUMENT);
}
/*
* We need to re-run xmlValidateDocument on local_handle to properly
* update the in-core representation of the configuration.
*/
return (Z_NOMEM);
if (valid == 0)
return (Z_INVALID_DOCUMENT);
return (Z_OK);
}
static boolean_t
{
if (handle->zone_dh_newzone)
return (B_FALSE);
return (B_TRUE);
return (B_FALSE);
}
static boolean_t
{
}
static boolean_t
{
return (handle->zone_dh_snapshot);
}
/*
* It would be great to be able to use libc's ctype(3c) macros, but we
* can't, as they are locale sensitive, and it would break our limited thread
* safety if this routine had to change the app locale on the fly.
*/
int
zonecfg_validate_zonename(const char *zone)
{
int i;
return (Z_BOGUS_ZONE_NAME);
return (Z_BOGUS_ZONE_NAME);
return (Z_BOGUS_ZONE_NAME);
return (Z_BOGUS_ZONE_NAME);
}
return (Z_OK);
}
/*
* Changing the zone name requires us to track both the old and new
* name of the zone until commit time.
*/
int
{
}
int
{
int err;
return (err);
return (Z_OK);
/*
* Switching zone names to one beginning with SUNW is not permitted.
*/
return (Z_BOGUS_ZONE_NAME);
return (err);
/*
* Setting the name back to the original name (effectively a revert of
* the name) is fine. But if we carry on, we'll falsely identify the
* name as "in use," so special case here.
*/
return (err);
}
/* Check to see if new name chosen is already in use */
return (Z_NAME_IN_USE);
/*
* If this isn't already "new" or in a renaming transition, then
* we're initiating a rename here; so stash the "delete name"
* (i.e. the name of the zone we'll be removing) for the rename.
*/
sizeof (old_delname));
/*
* Name change is allowed only when the zone we're altering
* is not ready or running.
*/
if (state > ZONE_STATE_INSTALLED)
return (Z_BAD_ZONE_STATE);
return (err);
}
sizeof (handle->zone_dh_delete_name));
} else if (is_renaming(handle)) {
if (state > ZONE_STATE_INSTALLED)
return (Z_BAD_ZONE_STATE);
return (err);
}
}
/*
* Restore the deletename to whatever it was at the
* top of the routine, since we've had a failure.
*/
sizeof (handle->zone_dh_delete_name));
return (err);
}
return (Z_OK);
}
int
{
return (Z_TOO_BIG);
}
int
{
/*
* The user deals in absolute paths in the running global zone, but the
* internal configuration files deal with boot environment relative
* paths. Strip out the alternate root when specified.
*/
return (Z_BAD_PROPERTY);
}
int
{
/* If the zone has no brand, it is native. */
else
}
return (ret);
}
int
{
}
int
{
char autobootstr[DTD_ENTITY_BOOL_LEN];
int ret;
sizeof (autobootstr))) != Z_OK)
return (ret);
else
return (ret);
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
/*
* in the <zonename>.xml file: the path to the zone. This is for performance,
* since we need to walk all zonepath's in order to be able to detect conflicts
* (see crosscheck_zonepaths() in the zoneadm command).
*
* An additional complexity is that when doing a rename, we'd like the entire
* index update operation (rename, and potential state changes) to be atomic.
* In general, the operation of this function should succeed or fail as
* a unit.
*/
int
{
int err;
int opcode;
char *zn;
return (err);
return (err);
if (is_renaming(handle)) {
opcode = PZE_MODIFY;
/*
* Be tolerant of the zone already existing in the index file,
* since we might be forcibly overwriting an existing
* configuration with a new one (for example 'create -F'
* in zonecfg).
*/
cookie = setzoneent();
opcode = PZE_MODIFY;
break;
}
}
} else {
opcode = PZE_MODIFY;
}
return (err);
return (Z_OK);
}
/*
* The goal of this routine is to cause the index file update and the
* document save to happen as an atomic operation. We do the document
* first, saving a backup copy using a hard link; if that succeeds, we go
* on to the index. If that fails, we roll the document back into place.
*
* Strategy:
*
* New zone 'foo' configuration:
* Create tmpfile (zonecfg.xxxxxx)
* Write XML to tmpfile
* Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
* Add entry to index file
* If it fails, delete foo.xml, leaving nothing behind.
*
* Save existing zone 'foo':
* Make backup of foo.xml -> .backup
* Create tmpfile (zonecfg.xxxxxx)
* Write XML to tmpfile
* Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
* Modify index file as needed
* If it fails, recover from .backup -> foo.xml
*
* Rename 'foo' to 'bar':
* Create tmpfile (zonecfg.xxxxxx)
* Write XML to tmpfile
* Rename tmpfile to xmlfile (zonecfg.xxxxxx -> bar.xml)
* Add entry for 'bar' to index file, Remove entry for 'foo' (refresh)
*/
static int
{
char tmpfile[MAXPATHLEN];
if (tmpfd == -1) {
return (Z_TEMP_FILE);
}
/*
* We do a final validation of the document. Since the library has
* malfunctioned if it fails to validate, we follow-up with an
* assert() that the doc is valid.
*/
goto err;
/*
* In the event we are doing a standard save, hard link a copy of the
* original file in .backup.<pid>.filename so we can restore it if
* something goes wrong.
*/
return (Z_ACCES);
return (Z_MISC_FS);
}
}
/*
* Move the new document over top of the old.
* i.e.: zonecfg.XXXXXX -> myzone.xml
*/
if (backup)
return (Z_ACCES);
return (Z_MISC_FS);
}
/*
* If this is a snapshot, we're done-- don't add an index entry.
*/
if (is_snapshot(handle))
return (Z_OK);
/* now update the index file to reflect whatever we just did */
if (backup) {
/*
* Try to restore from our backup.
*/
} else {
/*
* Either the zone is new, in which case we can delete
* new.xml, or we're doing a rename, so ditto.
*/
}
return (Z_UPDATING_INDEX);
}
if (backup)
return (Z_OK);
err:
return (Z_SAVING_FILE);
}
int
{
char delpath[MAXPATHLEN];
int err = Z_SAVING_FILE;
return (Z_BAD_HANDLE);
/*
* We don't support saving snapshots or a tree containing a sw
* inventory at this time.
*/
return (Z_INVAL);
return (err);
return (Z_MISC_FS);
"FILE. Use zonecfg(1M) instead.\n");
return (err);
if (is_renaming(handle)) {
}
return (Z_OK);
}
int
{
int valid;
return (Z_BAD_HANDLE);
/*
* We do a final validation of the document. Since the library has
* malfunctioned if it fails to validate, we follow-up with an
* assert() that the doc is valid.
*/
return (Z_SAVING_FILE);
return (Z_OK);
}
int
{
char zname[ZONENAME_MAX];
char path[MAXPATHLEN];
char migpath[MAXPATHLEN];
int err = Z_SAVING_FILE;
int valid;
return (Z_BAD_HANDLE);
/*
* We can only detach if we have taken a sw inventory.
*/
if (!handle->zone_dh_sw_inv)
return (Z_INVAL);
if (flags & ZONE_DRY_RUN) {
} else {
!= Z_OK)
return (err);
!= Z_OK)
return (err);
>= sizeof (migpath))
return (Z_NOMEM);
}
return (err);
"Use zonecfg(1M) and zoneadm(1M) attach.\n");
/*
* We do a final validation of the document. Since the library has
* malfunctioned if it fails to validate, we follow-up with an
* assert() that the doc is valid.
*/
return (Z_SAVING_FILE);
if (!(flags & ZONE_DRY_RUN))
return (Z_OK);
}
zonecfg_detached(const char *path)
{
char migpath[MAXPATHLEN];
sizeof (migpath))
return (B_FALSE);
return (B_TRUE);
return (B_FALSE);
}
void
{
char zname[ZONENAME_MAX];
char path[MAXPATHLEN];
char detached[MAXPATHLEN];
char attached[MAXPATHLEN];
return;
return;
return;
if (forced) {
} else {
}
}
/*
* Special case: if access(2) fails with ENOENT, then try again using
* ZONE_CONFIG_ROOT instead of config_file_path(zonename). This is how we
* work around the case of a config file which has not been created yet:
* the user will need access to the directory so use that as a heuristic.
*/
int
{
char path[MAXPATHLEN];
return (Z_INVAL);
return (Z_OK);
ZONE_CONFIG_ROOT) >= sizeof (path))
return (Z_INVAL);
return (Z_OK);
}
return (Z_ACCES);
return (Z_INVAL);
return (Z_MISC_FS);
}
int
zonecfg_create_snapshot(const char *zonename)
{
return (Z_NOMEM);
}
goto out;
goto out;
goto out;
goto out;
}
/*
* If the resolved path is not the same as the original path, then
* save the resolved path in the snapshot, thus preventing any
* potential problems down the line when zoneadmd goes to unmount
* file systems and depends on initial string matches with resolved
* paths.
*/
goto out;
}
ZONE_SNAPSHOT_ROOT) >= sizeof (path)) {
goto out;
}
goto out;
}
goto out;
}
"It is a snapshot of running zone state.\n");
out:
return (error);
}
int
{
int err;
if (err == Z_BAD_PROPERTY) {
/* Return default value */
return (Z_OK);
return (err);
}
*iptypep = ZS_EXCLUSIVE;
else
return (Z_INVAL);
return (Z_OK);
}
int
{
return (Z_INVAL);
return (Z_EMPTY_DOCUMENT);
}
return (Z_WRONG_DOC_TYPE);
}
switch (iptype) {
case ZS_SHARED:
/*
* Since "shared" is the default, we don't write it to the
* configuration file, so that it's easier to migrate those
* zones elsewhere, eg., to systems which are not IP-Instances
* aware.
* xmlUnsetProp only fails when the attribute doesn't exist,
* which we don't care.
*/
break;
case ZS_EXCLUSIVE:
return (Z_INVAL);
break;
}
return (Z_OK);
}
static int
{
return (Z_BAD_PROPERTY);
}
return (Z_OK);
}
static int
{
int err;
return (err);
return (err);
return (err);
return (err);
return (err);
}
}
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
static int
{
int err;
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
{
return (Z_NOMEM);
sizeof (new->zone_fsopt_opt));
else
return (Z_OK);
}
int
{
else
return (Z_OK);
} else
}
return (Z_NO_PROPERTY_ID);
}
void
{
}
}
void
{
return;
}
static boolean_t
{
int prop_result;
return (B_FALSE);
return ((prop_result == 0));
}
static int
struct zone_fstab *tabptr)
{
continue;
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_fstab *oldtabptr,
struct zone_fstab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
static int
{
continue;
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_fstab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_fstab *tabptr)
{
int err;
char options_str[MAX_MNTOPT_STR];
return (Z_INVAL);
return (err);
/*
* Walk the list of children looking for matches on any properties
* specified in the fstab parameter. If more than one resource
* matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
* Z_NO_RESOURCE_ID.
*/
firstmatch = NULL;
continue;
if (firstmatch == NULL)
firstmatch = cur;
else
return (Z_INSUFFICIENT_SPEC);
}
}
special) == 0) {
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
}
if (firstmatch == NULL)
return (Z_NO_RESOURCE_ID);
cur = firstmatch;
return (err);
return (err);
return (err);
return (err);
/* options are optional */
sizeof (options_str)) != Z_OK))
break;
break;
}
return (Z_OK);
}
int
{
int err;
char dirname[MAXPATHLEN];
return (Z_INVAL);
return (err);
/*
* General algorithm:
* Walk the list of children looking for matches on any properties
* specified in the fstab parameter. If more than one resource
* matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
* Z_NO_RESOURCE_ID.
*/
continue;
else
return (Z_INSUFFICIENT_SPEC);
}
}
}
return (Z_NO_RESOURCE_ID);
return (err);
return (Z_OK);
}
/*
* Compare two IP addresses in string form. Allow for the possibility that
* one might have "/<prefix-length>" at the end: allow a match on just the
* IP address (or host name) part.
*/
{
int result;
return (B_TRUE);
/*
* If neither has a slash or both do, they need to match to be
* considered the same, but they did not match above, so fail.
*/
return (B_FALSE);
/*
* Only one had a slash: pick that one, zero out the slash, compare
* the "address only" strings, restore the slash, and return the
* result of the comparison.
*/
*slashp = '\0';
*slashp = '/';
return ((result == 0));
}
int
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
*slashp = '\0';
return (Z_IPV6_ADDR_PREFIX_LEN);
} else {
/* "address" may be a host name */
return (Z_BOGUS_ADDRESS);
/* LINTED E_BAD_PTR_CAST_ALIGN */
sizeof (struct in_addr));
}
return (Z_OK);
}
{
int so;
int save_errno;
/* Odd - can't tell if the ifname exists */
return (B_FALSE);
}
save_errno = errno;
errno = save_errno;
return (B_FALSE);
}
return (B_TRUE);
}
int
{
int err;
return (Z_INVAL);
return (err);
firstmatch = NULL;
continue;
physical) == 0)) {
if (firstmatch == NULL)
firstmatch = cur;
else
return (Z_INSUFFICIENT_SPEC);
}
}
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
}
if (firstmatch == NULL)
return (Z_NO_RESOURCE_ID);
cur = firstmatch;
return (err);
return (err);
return (err);
return (Z_OK);
}
static int
{
int err;
return (err);
return (err);
/*
* Do not add this property when it is not set, for backwards
* compatibility and because it is optional.
*/
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
static int
{
continue;
if (addr_match && phys_match) {
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_nwiftab *oldtabptr,
struct zone_nwiftab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
char match[MAXPATHLEN];
return (Z_INVAL);
return (err);
firstmatch = NULL;
continue;
continue;
match) == 0) {
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
if (firstmatch == NULL)
return (Z_NO_RESOURCE_ID);
cur = firstmatch;
return (err);
return (Z_OK);
}
static int
{
int err;
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
static int
{
int match_match;
continue;
if (match_match) {
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_devtab *oldtabptr,
struct zone_devtab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
/* Lock to serialize all zonecfg_devwalks */
/*
* Global variables used to pass data from zonecfg_devwalk to the nftw
* call-back (zonecfg_devwalk_cb). g_devwalk_data is really the void*
* parameter and g_devwalk_cb is really the *cb parameter from zonecfg_devwalk.
*/
static void *g_devwalk_data;
void *);
static size_t g_devwalk_skip_prefix;
/*
* This is the nftw call-back function used by zonecfg_devwalk. It is
* responsible for calling the actual call-back that is passed in to
* zonecfg_devwalk as the *cb argument.
*/
/* ARGSUSED2 */
static int
{
/* skip all but character and block devices */
return (0);
}
return (0);
return (0);
}
/*
* Walk the dev tree for the zone specified by hdl and call the call-back (cb)
* function for each entry in the tree. The call-back will be passed the
* name, uid, gid, mode, acl string and the void *data input parameter
* for each dev entry.
*
* Data is passed to the zonecfg_devwalk_cb through the global variables
* g_devwalk_data, *g_devwalk_cb, and g_devwalk_skip_prefix. The
* zonecfg_devwalk_cb function will actually call *cb.
*/
int
void *data)
{
char path[MAXPATHLEN];
int ret;
return (ret);
return (Z_TOO_BIG);
/*
* We have to serialize all zonecfg_devwalks in the same process
* (which should be fine), since nftw() is so badly designed.
*/
(void) pthread_mutex_lock(&zonecfg_devwalk_lock);
g_devwalk_cb = cb;
(void) pthread_mutex_unlock(&zonecfg_devwalk_lock);
return (Z_OK);
}
/*
* Update the owner, group, mode and acl on the specified dev (inpath) for
* the zone (hdl). This function can be used to fix up the dev tree after
* attaching a migrated zone.
*/
int
{
int ret;
char path[MAXPATHLEN];
return (ret);
return (Z_TOO_BIG);
return (Z_TOO_BIG);
return (Z_INVAL);
/* make sure we're only touching device nodes */
return (Z_INVAL);
return (Z_SYSTEM);
return (Z_SYSTEM);
return (Z_OK);
return (Z_SYSTEM);
errno = 0;
return (Z_SYSTEM);
}
return (Z_OK);
}
/*
* This function finds everything mounted under a zone's rootpath.
* This returns the number of mounts under rootpath, or -1 on error.
* callback is called once per mount found with the first argument
* pointing to the mount point.
*
* If the callback function returns non-zero zonecfg_find_mounts
* aborts with an error.
*/
int
void *priv) {
struct mnttab m;
size_t l;
int zfsl;
int rv = 0;
char zfs_path[MAXPATHLEN];
>= sizeof (zfs_path))
return (-1);
return (-1);
rv = -1;
goto out;
}
(m.mnt_mountp[l] == '/') &&
rv++;
continue;
rv = -1;
goto out;
}
}
}
out:
return (rv);
}
int
{
int err;
return (Z_INVAL);
return (err);
firstmatch = NULL;
continue;
if (firstmatch == NULL)
firstmatch = cur;
else
return (Z_INSUFFICIENT_SPEC);
}
}
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
0) {
if (firstmatch == NULL)
firstmatch = cur;
else if (firstmatch != cur)
return (Z_INSUFFICIENT_SPEC);
} else {
/*
* If another property matched but this
* one doesn't then reset firstmatch.
*/
if (firstmatch == cur)
firstmatch = NULL;
}
}
}
}
if (firstmatch == NULL)
return (Z_NO_RESOURCE_ID);
cur = firstmatch;
return (err);
return (err);
return (err);
return (Z_OK);
}
static int
{
int err;
return (err);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
static int
{
continue;
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_attrtab *oldtabptr,
struct zone_attrtab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
int
{
return (Z_INVAL);
return (Z_INVAL);
return (Z_OK);
}
return (Z_OK);
}
return (Z_INVAL);
}
int
{
long long result;
char *endptr;
return (Z_INVAL);
return (Z_INVAL);
errno = 0;
return (Z_INVAL);
return (Z_OK);
}
int
{
return (Z_INVAL);
return (Z_INVAL);
return (Z_TOO_BIG);
return (Z_OK);
}
int
{
unsigned long long result;
long long neg_result;
char *endptr;
return (Z_INVAL);
return (Z_INVAL);
errno = 0;
return (Z_INVAL);
errno = 0;
/*
* Incredibly, strtoull("<negative number>", ...) will not fail but
* return whatever (negative) number cast as a u_longlong_t, so we
* need to look for this here.
*/
if (errno == 0 && neg_result < 0)
return (Z_INVAL);
return (Z_OK);
}
int
{
char savedname[MAXNAMELEN];
struct zone_rctlvaltab *valptr;
int err;
return (Z_INVAL);
return (err);
continue;
sizeof (struct zone_rctlvaltab));
return (Z_NOMEM);
sizeof (valptr->zone_rctlval_priv)) !=
Z_OK))
break;
sizeof (valptr->zone_rctlval_limit)) !=
Z_OK))
break;
sizeof (valptr->zone_rctlval_action)) !=
Z_OK))
break;
Z_OK)
break;
}
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
static int
{
struct zone_rctlvaltab *valptr;
int err;
return (err);
return (err);
return (err);
return (err);
}
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
static int
{
int name_result;
continue;
continue;
if (name_result == 0) {
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_rctltab *oldtabptr,
struct zone_rctltab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_rctltab *tabptr,
struct zone_rctlvaltab *valtabptr)
{
return (Z_INVAL);
if (!zonecfg_valid_rctlblk(rctlblk))
return (Z_INVAL);
else
return (Z_OK);
}
int
struct zone_rctltab *tabptr,
struct zone_rctlvaltab *valtabptr)
{
valtabptr->zone_rctlval_priv) == 0 &&
valtabptr->zone_rctlval_limit) == 0 &&
valtabptr->zone_rctlval_action) == 0) {
else
return (Z_OK);
} else
}
return (Z_NO_PROPERTY_ID);
}
char *
zonecfg_strerror(int errnum)
{
switch (errnum) {
case Z_OK:
case Z_EMPTY_DOCUMENT:
case Z_WRONG_DOC_TYPE:
case Z_BAD_PROPERTY:
case Z_TEMP_FILE:
return (dgettext(TEXT_DOMAIN,
"Problem creating temporary file"));
case Z_SAVING_FILE:
case Z_NO_ENTRY:
case Z_BOGUS_ZONE_NAME:
case Z_REQD_RESOURCE_MISSING:
case Z_REQD_PROPERTY_MISSING:
case Z_BAD_HANDLE:
case Z_NOMEM:
case Z_INVAL:
case Z_ACCES:
case Z_TOO_BIG:
case Z_MISC_FS:
return (dgettext(TEXT_DOMAIN,
"Miscellaneous file system error"));
case Z_NO_ZONE:
case Z_NO_RESOURCE_TYPE:
case Z_NO_RESOURCE_ID:
case Z_NO_PROPERTY_TYPE:
case Z_NO_PROPERTY_ID:
case Z_BAD_ZONE_STATE:
return (dgettext(TEXT_DOMAIN,
"Zone state is invalid for the requested operation"));
case Z_INVALID_DOCUMENT:
case Z_NAME_IN_USE:
case Z_NO_SUCH_ID:
case Z_UPDATING_INDEX:
case Z_LOCKING_FILE:
case Z_UNLOCKING_FILE:
case Z_INSUFFICIENT_SPEC:
case Z_RESOLVED_PATH:
case Z_IPV6_ADDR_PREFIX_LEN:
return (dgettext(TEXT_DOMAIN,
"IPv6 address missing required prefix length"));
case Z_BOGUS_ADDRESS:
return (dgettext(TEXT_DOMAIN,
"Neither an IPv4 nor an IPv6 address nor a host name"));
case Z_PRIV_PROHIBITED:
return (dgettext(TEXT_DOMAIN,
"Specified privilege is prohibited"));
case Z_PRIV_REQUIRED:
return (dgettext(TEXT_DOMAIN,
"Required privilege is missing"));
case Z_PRIV_UNKNOWN:
return (dgettext(TEXT_DOMAIN,
"Specified privilege is unknown"));
case Z_BRAND_ERROR:
return (dgettext(TEXT_DOMAIN,
"Brand-specific error"));
case Z_INCOMPATIBLE:
case Z_ALIAS_DISALLOW:
return (dgettext(TEXT_DOMAIN,
"An incompatible rctl already exists for this property"));
case Z_CLEAR_DISALLOW:
return (dgettext(TEXT_DOMAIN,
"Clearing this property is not allowed"));
case Z_POOL:
case Z_POOLS_NOT_ACTIVE:
"zone will not be bound to pool"));
case Z_POOL_ENABLE:
return (dgettext(TEXT_DOMAIN,
"Could not enable pools facility"));
case Z_NO_POOL:
return (dgettext(TEXT_DOMAIN,
"Pool not found; using default pool"));
case Z_POOL_CREATE:
return (dgettext(TEXT_DOMAIN,
"Could not create a temporary pool"));
case Z_POOL_BIND:
default:
}
}
/*
* Note that the zonecfg_setXent() and zonecfg_endXent() calls are all the
* same, as they just turn around and call zonecfg_setent() / zonecfg_endent().
*/
static int
{
int err;
return (Z_INVAL);
return (err);
}
return (Z_OK);
}
static int
{
return (Z_INVAL);
return (Z_OK);
}
/*
* Do the work required to manipulate a process through libproc.
* If grab_process() returns no errors (0), then release_process()
* must eventually be called.
*
* Return values:
* 0 Successful creation of agent thread
* 1 Error grabbing
* 2 Error creating agent
*/
static int
{
int ret;
return (1);
}
if (Pcreate_agent(p->pr) == 0) {
return (0);
} else {
return (2);
}
} else {
return (1);
}
}
/*
* Release the specified process. This destroys the agent
* and releases the process. If the process is NULL, nothing
* is done. This function should only be called if grab_process()
* has previously been called and returned success.
*
* This function is Pgrab-safe.
*/
static void
{
return;
}
static boolean_t
{
int pid_self;
return (B_FALSE);
return (B_FALSE);
/* Skip self */
continue;
continue;
continue;
/* attempt to grab process */
if (grab_process(p) != 0)
continue;
release_process(p->pr);
continue;
}
return (B_TRUE);
}
return (B_FALSE);
}
static boolean_t
{
return (B_FALSE);
return (B_TRUE);
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Apply the current rctl settings to the specified, running zone.
*/
int
{
int err;
struct zone_rctltab rctl;
return (err);
(void) zonecfg_endrctlent(handle);
return (Z_NOMEM);
}
if (!grab_zone_proc(zone_name, &p)) {
(void) zonecfg_endrctlent(handle);
return (Z_SYSTEM);
}
char *rname;
struct zone_rctlvaltab *valptr;
/* first delete all current privileged settings for this rctl */
0) {
goto done;
}
}
/* now set each new value for the rctl */
!= Z_OK) {
goto done;
}
goto done;
}
}
}
done:
release_process(p.pr);
(void) zonecfg_endrctlent(handle);
return (res);
}
static const xmlChar *
{
return (DTD_ELEM_DEVICE);
return (DTD_ELEM_FS);
return (DTD_ELEM_IPD);
return (DTD_ELEM_NET);
return (DTD_ELEM_ATTR);
return (DTD_ELEM_RCTL);
return (DTD_ELEM_DATASET);
return (NULL);
}
int
{
int num = 0;
return (num);
return (num);
num++;
(void) zonecfg_endent(handle);
return (num);
}
int
{
int err;
return (Z_NO_RESOURCE_TYPE);
return (err);
continue;
}
}
(void) zonecfg_endent(handle);
return (Z_OK);
}
static boolean_t
valid_uint(char *s, uint64_t *n)
{
char *endp;
/* strtoull accepts '-'?! so we want to flag that as an error */
return (B_FALSE);
errno = 0;
return (B_FALSE);
return (B_TRUE);
}
/*
* Convert a string representing a number (possibly a fraction) into an integer.
* The string can have a modifier (K, M, G or T). The modifiers are treated
* as powers of two (not 10).
*/
int
{
long double val;
char *unitp;
return (-1);
/* remove any leading white space from units string */
++unitp;
/* if no units explicitly set, error */
scale = 1;
} else {
int i;
scale = 1024;
/* update scale based on units */
break;
scale <<= 10;
}
return (-1);
}
return (0);
}
{
return (B_FALSE);
return (B_TRUE);
}
zonecfg_valid_importance(char *impstr)
{
return (B_FALSE);
return (B_TRUE);
}
{
int i;
break;
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
{
return (B_FALSE);
return (B_TRUE);
}
static int
{
return (res);
}
static int
{
/* set the maximum number of cpus for the pset */
}
/* set the minimum number of cpus for the pset */
}
return (Z_OK);
}
static int
struct zone_psettab *pset_tab)
{
/* create a temporary pool configuration */
return (res);
}
goto done;
}
/* set pool importance */
goto done;
}
goto done;
}
!= PO_SUCCESS) {
goto done;
}
}
goto done;
/* validation */
goto done;
}
/*
* This validation is the one we expect to fail if the user specified
* an invalid configuration (too many cpus) for this system.
*/
goto done;
}
/*
* Commit the dynamic configuration but not the pool configuration
* file.
*/
done:
(void) pool_conf_close(pconf);
return (res);
}
static int
struct zone_psettab *pset_tab)
{
int nfound = 0;
!= POC_INVAL) {
nfound++;
}
}
nfound++;
}
nfound++;
}
}
if (nfound == 3)
return (PO_SUCCESS);
return (PO_FAIL);
}
/*
* Determine if a tmp pool is configured and if so, if the configuration is
* still valid or if it has been changed since the tmp pool was created.
* If the tmp pool configuration is no longer valid, delete the tmp pool.
*
* Set *valid=B_TRUE if there is an existing, valid tmp pool configuration.
*/
static int
{
struct zone_psettab pset_current;
!= PO_SUCCESS) {
return (res);
}
/* no tmp pool configured */
goto done;
}
/*
* If an existing tmp pool for this zone is configured with the proper
* settings, then the tmp pool is valid.
*/
== PO_SUCCESS &&
pset_current.zone_ncpu_min) == 0 &&
pset_current.zone_ncpu_max) == 0 &&
pset_current.zone_importance) == 0) {
} else {
/*
* An out-of-date tmp pool configuration exists. Delete it
* so that we can create the correct tmp pool config.
*/
goto done;
}
goto done;
}
/* commit dynamic config */
}
done:
(void) pool_conf_close(pconf);
return (res);
}
/*
* Destroy any existing tmp pool.
*/
int
{
int status;
char tmp_name[MAX_TMP_POOL_NAME];
/* if pools not enabled then nothing to do */
return (Z_OK);
!= PO_SUCCESS) {
return (res);
}
/* nothing to destroy, we're done */
goto done;
}
goto done;
}
goto done;
}
/* commit dynamic config */
done:
(void) pool_conf_close(pconf);
return (res);
}
/*
* Attempt to bind to a tmp pool for this zone. If there is no tmp pool
* configured, we just return Z_OK.
*
* We either attempt to create the tmp pool for this zone or rebind to an
* existing tmp pool for this zone.
*
* Rebinding is used when a zone with a tmp pool reboots so that we don't have
* to recreate the tmp pool. To do this we need to be sure we work correctly
* for the following cases:
*
* - there is an existing, properly configured tmp pool.
* - zonecfg added tmp pool after zone was booted, must now create.
* - zonecfg updated tmp pool config after zone was booted, in this case
* we destroy the old tmp pool and create a new one.
*/
int
int err_size)
{
struct zone_psettab pset_tab;
int err;
int status;
char zone_name[ZONENAME_MAX];
char tmp_name[MAX_TMP_POOL_NAME];
/* if no temporary pool configured, we're done */
if (err == Z_NO_ENTRY)
return (Z_OK);
/*
* importance might not have a value but we need to validate it here,
* so set the default.
*/
sizeof (pset_tab.zone_importance));
/* if pools not enabled, enable them now */
return (Z_POOL_ENABLE);
}
/*
* reuse it.
*/
return (err);
}
if (!exists)
&pset_tab);
return (err);
/* Bind the zone to the pool. */
return (Z_OK);
}
/*
* Attempt to bind to a permanent pool for this zone. If there is no
* permanent pool configured, we just return Z_OK.
*/
int
int err_size)
{
char poolname[MAXPATHLEN];
int status;
int error;
/*
* Find the pool mentioned in the zone configuration, and bind to it.
*/
/*
* The property is not set on the zone, so the pool
* should be bound to the default pool. But that's
* already done by the kernel, so we can just return.
*/
return (Z_OK);
}
/*
* Not an error, even though it shouldn't be happening.
*/
return (Z_OK);
}
/*
* Don't do anything if pools aren't enabled.
*/
return (Z_POOLS_NOT_ACTIVE);
/*
* Try to provide a sane error message if the requested pool doesn't
* exist.
*/
PO_SUCCESS) {
}
(void) pool_conf_close(poolconf);
return (Z_NO_POOL);
/*
* Bind the zone to the pool.
*/
/* if bind fails, return poolname for the error msg */
return (Z_POOL_BIND);
}
return (Z_OK);
}
static boolean_t
svc_enabled(char *svc_name)
{
*scf_simple_prop_next_boolean(prop) != 0)
return (found);
}
/*
* If the zone has capped-memory, make sure the rcap service is enabled.
*/
int
{
if (!svc_enabled(RCAP_SERVICE) &&
return (Z_SYSTEM);
}
return (Z_OK);
}
/*
* Return true if pset has cpu range specified and poold is not enabled.
*/
{
struct zone_psettab pset_tab;
int err;
/* if no temporary pool configured, we're done */
if (err == Z_NO_ENTRY)
return (B_FALSE);
/* range not specified, no need for poold */
return (B_FALSE);
/* we have a range, check if poold service is enabled */
if (svc_enabled(POOLD_SERVICE))
return (B_FALSE);
return (B_TRUE);
}
static int
{
int status;
const char *sched_str;
return (Z_NO_POOL);
return (Z_NO_POOL);
PO_SUCCESS) {
return (Z_NO_POOL);
}
(void) pool_conf_close(poolconf);
return (Z_NO_POOL);
}
!= POC_INVAL) {
return (Z_TOO_BIG);
}
(void) pool_conf_close(poolconf);
return (Z_OK);
}
/*
* Get the default scheduling class for the zone. This will either be the
* class set on the zone's pool or the system default scheduling class.
*/
int
{
char poolname[MAXPATHLEN];
/* check if the zone's pool specified a sched class */
return (Z_OK);
}
return (Z_TOO_BIG);
return (Z_OK);
}
int
{
return (zonecfg_setent(handle));
}
int
{
char options_str[MAX_MNTOPT_STR];
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (err);
}
return (err);
}
return (err);
}
/* OK for options to be NULL */
sizeof (options_str)) != Z_OK)
break;
break;
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
int
{
return (zonecfg_setent(handle));
}
int
{
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
int
{
return (zonecfg_setent(handle));
}
int
{
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (err);
}
return (err);
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
int
{
return (zonecfg_setent(handle));
}
int
{
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
int
{
return (zonecfg_setent(handle));
}
int
{
struct zone_rctlvaltab *valptr;
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
sizeof (struct zone_rctlvaltab));
return (Z_NOMEM);
break;
break;
break;
break;
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
int
{
return (zonecfg_setent(handle));
}
int
{
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (err);
}
return (err);
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
/*
* The privileges available on the system and described in privileges(5)
* fall into four categories with respect to non-global zones:
*
* Default set of privileges considered safe for all non-global
* zones. These privileges are "safe" in the sense that a
* privileged process in the zone cannot affect processes in any
* other zone on the system.
*
* Set of privileges not currently permitted within a non-global
* zone. These privileges are considered by default, "unsafe,"
* and include ones which affect global resources (such as the
* system clock or physical memory) or are overly broad and cover
* more than one mechanism in the system. In other cases, there
* has not been sufficient virtualization in the parts of the
* system the privilege covers to allow its use within a
* non-global zone.
*
* Set of privileges required in order to get a zone booted and
* init(1M) started. These cannot be removed from the zone's
* privilege set.
*
* All other privileges are optional and are potentially useful for
* processes executing inside a non-global zone.
*
* When privileges are added to the system, a determination needs to be
* made as to which category the privilege belongs to. Ideally,
* privileges should be fine-grained enough and the mechanisms they cover
* virtualized enough so that they can be made available to non-global
* zones.
*/
/*
* Define some of the tokens that priv_str_to_set(3C) recognizes. Since
* the privilege string separator can be any character, although it is
* usually a comma character, define these here as well in the event that
* they change or are augmented in the future.
*/
#define BASIC_TOKEN "basic"
#define DEFAULT_TOKEN "default"
#define ZONE_TOKEN "zone"
#define TOKEN_PRIV_CHAR ','
#define TOKEN_PRIV_STR ","
typedef struct priv_node {
char *pn_priv; /* Privileges name */
} priv_node_t;
/* Privileges lists can differ across brands */
typedef struct priv_lists {
/* Privileges considered safe for all non-global zones of a brand */
struct priv_node *pl_default;
/* Privileges not permitted for all non-global zones of a brand */
struct priv_node *pl_prohibited;
/* Privileges required for all non-global zones of a brand */
struct priv_node *pl_required;
/*
* ip-type of the zone these privileges lists apply to.
* It is used to pass ip-type to the callback function,
* priv_lists_cb, which has no way of getting the ip-type.
*/
const char *pl_iptype;
} priv_lists_t;
static int
{
/* Skip this privilege if ip-type does not match */
return (0);
/* Allocate a new priv list node. */
return (-1);
return (-1);
}
/* Insert the new priv list node into the right list */
} else {
return (-1);
}
return (0);
}
static void
{
}
}
}
}
static int
const char *curr_iptype)
{
char brand[MAXNAMELEN];
return (Z_BRAND_ERROR);
} else {
}
return (Z_BRAND_ERROR);
return (Z_NOMEM);
}
/* construct the privilege lists */
return (Z_BRAND_ERROR);
}
return (Z_OK);
}
static int
{
return (Z_INVAL);
}
return (Z_OK);
}
int
{
int ret;
return (ret);
return (ret);
}
void
{
if (*str != '\0')
}
/*
* Verify that the supplied string is a valid privilege limit set for a
* non-global zone. This string must not only be acceptable to
* priv_str_to_set(3C) which parses it, but it also must resolve to a
* privilege set that includes certain required privileges and lacks
* certain prohibited privileges.
*/
static int
{
const char *token;
/*
* The verification of the privilege string occurs in several
* phases. In the first phase, the supplied string is scanned for
* the ZONE_TOKEN token which is not support as part of the
* "limitpriv" property.
*
* Duplicate the supplied privilege string since strtok_r(3C)
* tokenizes its input by null-terminating the tokens.
*/
return (Z_NOMEM);
return (Z_NOMEM);
else
return (Z_PRIV_UNKNOWN);
}
}
if (add_default) {
/*
* If DEFAULT_TOKEN was specified, a string needs to be
* built containing the privileges from the default, safe
* set along with those of the "limitpriv" property.
*/
*tmp = '\0';
} else {
}
/*
* In the next phase, attempt to convert the merged privilege
* string into a privilege set. In the case of an error, either
* there was a memory allocation failure or there was an invalid
* privilege token in the string. In either case, return an
* appropriate error code but in the event of an invalid token,
* allocate a string containing its name and return that back to
* the caller.
*/
return (Z_NOMEM);
*cp = '\0';
return (Z_NOMEM);
else
return (Z_PRIV_UNKNOWN);
}
/*
* Next, verify that none of the prohibited zone privileges are
* present in the merged privilege set.
*/
return (Z_NOMEM);
else
return (Z_PRIV_PROHIBITED);
}
}
/*
* Finally, verify that all of the required zone privileges are
* present in the merged privilege set.
*/
return (Z_NOMEM);
else
return (Z_PRIV_REQUIRED);
}
}
return (Z_OK);
}
/*
* Fill in the supplied privilege set with either the default, safe set of
* privileges suitable for a non-global zone, or one based on the
* "limitpriv" property in the zone's configuration.
*
* In the event of an invalid privilege specification in the
* configuration, a string is allocated and returned containing the
* "privilege" causing the issue. It is the caller's responsibility to
* free this memory when it is done with it.
*/
int
char **privname)
{
const char *curr_iptype;
/*
* Attempt to lookup the "limitpriv" property. If it does not
* exist or matches the string DEFAULT_TOKEN exactly, then the
* default, safe privilege set is returned.
*/
return (err);
return (err);
switch (iptype) {
case ZS_SHARED:
curr_iptype = "shared";
break;
case ZS_EXCLUSIVE:
curr_iptype = "exclusive";
break;
}
return (err);
return (err);
}
/*
* Check if the string DEFAULT_TOKEN is the first token in a list
* of privileges.
*/
else
return (err);
}
int
{
int err;
char *cp;
return (Z_INVAL);
*--cp = '\0';
if (zonepath[0] == '\0')
return (Z_OK);
}
/*
* First check the index file. Because older versions did not have
* a copy of the zone path, allow for it to be zero length, in which
* case we ignore this result and fall back to the XML files.
*/
cookie = setzoneent();
}
if (found)
break;
}
return (Z_OK);
/* Fall back to the XML files. */
return (Z_NOMEM);
/*
* Check the snapshot first: if a zone is running, its zonepath
* may have changed.
*/
return (err);
}
}
return (err);
}
int
{
int err;
/* This function makes sense for non-global zones only. */
return (Z_BOGUS_ZONE_NAME);
return (err);
return (Z_TOO_BIG);
return (Z_OK);
}
int
{
int err;
char myzone[MAXNAMELEN];
/*
* If we are not in the global zone, then we don't have the zone
* .xml files with the brand name available. Thus, we are going to
* have to ask the kernel for the information.
*/
if (myzoneid != GLOBAL_ZONEID) {
if (is_system_labeled()) {
return (Z_OK);
}
sizeof (myzone)) < 0)
return (Z_NO_ZONE);
return (Z_NO_ZONE);
if (err < 0)
return (Z_OK);
}
return (Z_OK);
}
return (Z_NOMEM);
return (err);
}
/*
* Return the appropriate root for the active /dev.
*/
int
{
int err;
char *suffix;
/* This function makes sense for non-global zones only. */
return (Z_BOGUS_ZONE_NAME);
return (err);
suffix = "/lu";
else
suffix = "/root";
return (Z_TOO_BIG);
return (Z_OK);
}
static zone_state_t
{
char zoneroot[MAXPATHLEN];
switch (kernel_state) {
case ZONE_IS_UNINITIALIZED:
case ZONE_IS_INITIALIZED:
/* The kernel will not return these two states */
return (ZONE_STATE_READY);
case ZONE_IS_READY:
/*
* it's a mounted scratch zone.
*/
sizeof (zoneroot)) >= 0) {
if (zlen > 3 &&
return (ZONE_STATE_MOUNTED);
}
return (ZONE_STATE_READY);
case ZONE_IS_BOOTING:
case ZONE_IS_RUNNING:
return (ZONE_STATE_RUNNING);
case ZONE_IS_SHUTTING_DOWN:
case ZONE_IS_EMPTY:
return (ZONE_STATE_SHUTTING_DOWN);
case ZONE_IS_DOWN:
case ZONE_IS_DYING:
case ZONE_IS_DEAD:
default:
return (ZONE_STATE_DOWN);
}
/* NOTREACHED */
}
int
{
char kernzone[ZONENAME_MAX];
return (Z_INVAL);
/*
* If we're looking at an alternate root, then we need to query the
* kernel using the scratch zone name.
*/
zone_id = -1;
}
} else {
}
/* check to see if zone is running */
if (zone_id != -1 &&
sizeof (status)) >= 0) {
return (Z_OK);
}
cookie = setzoneent();
}
if (found)
break;
}
}
int
{
return (Z_INVAL);
}
/*
* Get id (if any) for specified zone. There are four possible outcomes:
* - If the string corresponds to the numeric id of an active (booted)
* zone, sets *zip to the zone id and returns 0.
* - If the string corresponds to the name of an active (booted) zone,
* sets *zip to the zone id and returns 0.
* - If the string is a name in the configuration but is not booted,
* sets *zip to ZONE_ID_UNDEFINED and returns 0.
* - Otherwise, leaves *zip unchanged and returns -1.
*
* This function acts as an auxiliary filter on the function of the same
* name in libc; the linker binds to this version if libzonecfg exists,
* and the libc version if it doesn't. Any changes to this version of
* the function should probably be reflected in the libc version as well.
*/
int
{
char *cp;
int err;
/* first try looking for active zone by id */
errno = 0;
return (0);
}
/* then look for active zone by name */
return (0);
}
/* if in global zone, try looking up name in configuration database */
if (getzoneid() != GLOBAL_ZONEID ||
return (-1);
/* zone exists but isn't active */
*zip = ZONE_ID_UNDEFINED;
err = 0;
} else {
err = -1;
}
return (err);
}
char *
{
switch (state_num) {
case ZONE_STATE_CONFIGURED:
return (ZONE_STATE_STR_CONFIGURED);
case ZONE_STATE_INCOMPLETE:
return (ZONE_STATE_STR_INCOMPLETE);
case ZONE_STATE_INSTALLED:
return (ZONE_STATE_STR_INSTALLED);
case ZONE_STATE_READY:
return (ZONE_STATE_STR_READY);
case ZONE_STATE_MOUNTED:
return (ZONE_STATE_STR_MOUNTED);
case ZONE_STATE_RUNNING:
return (ZONE_STATE_STR_RUNNING);
case ZONE_STATE_SHUTTING_DOWN:
return (ZONE_STATE_STR_SHUTTING_DOWN);
case ZONE_STATE_DOWN:
return (ZONE_STATE_STR_DOWN);
default:
return ("unknown");
}
}
/*
* Given a UUID value, find an associated zone name. This is intended to be
* used by callers who set up some 'default' name (corresponding to the
* expected name for the zone) in the zonename buffer, and thus the function
* doesn't touch this buffer on failure.
*/
int
{
/*
* A small amount of subterfuge via casts is necessary here because
* libuuid doesn't use const correctly, but we don't want to export
* this brokenness to our clients.
*/
if (uuid_is_null(uuid))
return (Z_NO_ZONE);
return (Z_NO_ZONE);
break;
}
endzoneent(fp);
return (Z_OK);
} else {
return (Z_NO_ZONE);
}
}
/*
* Given a zone name, get its UUID. Returns a "NULL" UUID value if the zone
* exists but the file doesn't have a value set yet. Returns an error if the
* zone cannot be located.
*/
int
{
return (Z_NO_ZONE);
break;
}
endzoneent(fp);
return (Z_OK);
} else {
return (Z_NO_ZONE);
}
}
/*
* File-system convenience functions.
*/
zonecfg_valid_fs_type(const char *type)
{
/*
* We already know which FS types don't work.
*/
return (B_FALSE);
/*
* The caller may do more detailed verification to make sure other
* aspects of this filesystem type make sense.
*/
return (B_TRUE);
}
/*
* Generally uninteresting rctl convenience functions.
*/
int
{
unsigned long long ull;
char *endp;
/* Get the privilege */
priv = RCPRIV_BASIC;
} else {
/* Invalid privilege */
return (Z_INVAL);
}
/* deal with negative input; strtoull(3c) doesn't do what we want */
return (Z_INVAL);
/* Get the limit */
errno = 0;
/* parse failed */
return (Z_INVAL);
}
/* Get the action */
} else {
/* Invalid Action */
return (Z_INVAL);
}
return (Z_OK);
}
static int
{
/*
* Returning 1 here is our signal to zonecfg_is_rctl() that it is
* indeed an rctl name recognized by the system.
*/
}
zonecfg_is_rctl(const char *name)
{
}
zonecfg_valid_rctlname(const char *name)
{
const char *c;
return (B_FALSE);
return (B_FALSE);
if (!isalpha(*c) && *c != '-')
return (B_FALSE);
}
return (B_TRUE);
}
{
if (priv != RCPRIV_PRIVILEGED)
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
{
if (!zonecfg_valid_rctlblk(rctlblk))
return (B_FALSE);
if (!zonecfg_valid_rctlname(name))
return (B_FALSE);
return (B_TRUE); /* not an rctl on this system */
/*
* Make sure the proposed value isn't greater than the current system
* value.
*/
return (B_FALSE); /* shouldn't happen */
}
return (B_FALSE);
/*
* Make sure the proposed action is allowed.
*/
if ((global_flags & RCTL_GLOBAL_DENY_NEVER) &&
return (B_FALSE);
if ((global_flags & RCTL_GLOBAL_DENY_ALWAYS) &&
return (B_FALSE);
return (B_TRUE);
}
/*
* There is always a race condition between reading the initial copy of
* a zones state and its state changing. We address this by providing
* zonecfg_notify_critical_enter and zonecfg_noticy_critical_exit functions.
* When zonecfg_critical_enter is called, sets the state field to LOCKED
* and aquires biglock. Biglock protects against other threads executing
* critical_enter and the state field protects against state changes during
* the critical period.
*
* If any state changes occur, zn_cb will set the failed field of the znotify
* structure. This will cause the critical_exit function to re-lock the
* channel and return an error. Since evsnts may be delayed, the critical_exit
* function "flushes" the queue by putting an event on the queue and waiting for
* zn_cb to notify critical_exit that it received the ping event.
*/
static const char *
{
int i = 0;
i++;
if (*in == 0)
return (NULL);
}
return (in);
}
static boolean_t
{
ZONE_EVENT_PING_SUBCLASS) == 0) {
return (B_TRUE);
} else {
return (B_FALSE);
}
}
static boolean_t
{
const char *sender;
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
static int
{
nvlist_t *l;
int zid;
char *zonename;
char *newstate;
char *oldstate;
int ret;
ZONE_EVENT_STATUS_SUBCLASS) == 0) {
if (sysevent_get_attr_list(ev, &l) != 0) {
return (EAGAIN);
}
return (0);
}
ret = 0;
== 0) &&
== 0) &&
}
zevtchan->zn_failure_count = 0;
nvlist_free(l);
return (ret);
} else {
/*
* We have received an event in an unknown subclass. Ignore.
*/
zevtchan->zn_failure_count = 0;
return (0);
}
}
static int
{
int error;
return (0);
}
return (0);
}
return (0);
} else {
return (0);
}
}
/*
* Every ENOMEM failure causes do_callback to increment
* zn_failure_count and every success causes it to
* set zn_failure_count to zero. If we got EAGAIN,
* we will sleep for zn_failure_count seconds and return
* EAGAIN to gpec to try again.
*
* After 55 seconds, or 10 try's we give up and drop the
* event.
*/
return (0);
}
}
return (error);
}
return (0);
}
abort();
return (0);
}
void
zonecfg_notify_critical_enter(void *h)
{
}
int
zonecfg_notify_critical_exit(void * h)
{
return (0);
}
return (1);
}
return (0);
}
void
zonecfg_notify_critical_abort(void *h)
{
/*
* Don't do anything about zn_lock. If it is held, it could only be
* held by zn_cb and it will be unlocked soon.
*/
}
void *
void *p)
{
int i = 1;
int r;
return (NULL);
zevtchan->zn_private = p;
goto out3;
goto out3;
}
goto out3;
}
0) != 0)
goto out2;
do {
/*
* At 4 digits the subscriber ID gets too long and we have
* no chance of successfully registering.
*/
if (i > 999)
goto out1;
getpid() % 999999l, i);
zevtchan, 0);
i++;
} while (r);
return (zevtchan);
out1:
out2:
out3:
return (NULL);
}
void
zonecfg_notify_unbind(void *handle)
{
int ret;
/*
* Check that all evc threads have gone away. This should be
* enforced by sysevent_evc_unbind.
*/
if (ret)
abort();
}
static int
{
int err;
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
static int
{
continue;
tabptr->zone_dataset_name)) {
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
struct zone_dstab *oldtabptr,
struct zone_dstab *newtabptr)
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
char dataset[MAXNAMELEN];
return (Z_INVAL);
return (err);
firstmatch = NULL;
continue;
dataset) == 0)) {
if (firstmatch == NULL)
firstmatch = cur;
else
return (Z_INSUFFICIENT_SPEC);
}
}
}
if (firstmatch == NULL)
return (Z_NO_RESOURCE_ID);
cur = firstmatch;
return (err);
return (Z_OK);
}
int
{
return (zonecfg_setent(handle));
}
int
{
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
/*
* Support for aliased rctls; that is, rctls that have simplified names in
* zonecfg. For example, max-lwps is an alias for a well defined zone.max-lwps
* rctl. If there are multiple existing values for one of these rctls or if
* there is a single value that does not match the well defined template (i.e.
* it has a different action) then we cannot treat the rctl as having an alias
* so we return Z_ALIAS_DISALLOW. That means that the rctl cannot be
* managed in zonecfg via an alias and that the standard rctl syntax must be
* used.
*
* The possible return values are:
* Z_NO_PROPERTY_ID - invalid alias name
* Z_ALIAS_DISALLOW - pre-existing, incompatible rctl definition
* Z_NO_ENTRY - no rctl is configured for this alias
* Z_OK - we got a valid rctl for the specified alias
*/
int
{
char savedname[MAXNAMELEN];
struct zone_rctlvaltab rctl;
int i;
int err;
break;
return (Z_NO_PROPERTY_ID);
return (err);
continue;
/*
* If we already saw one of these, we can't have an
* alias since we just found another.
*/
if (found)
return (Z_ALIAS_DISALLOW);
/*
* If we already have one value, we can't have
* an alias since we just found another.
*/
if (found_val)
return (Z_ALIAS_DISALLOW);
break;
break;
break;
}
/* check priv and action match the expected vals */
return (Z_ALIAS_DISALLOW);
}
}
if (found) {
return (Z_OK);
}
return (Z_NO_ENTRY);
}
int
{
int i;
struct zone_rctltab rctltab;
/*
* First check that we have a valid aliased rctl to remove.
* This will catch an rctl entry with non-standard values or
* multiple rctl values for this name. We need to ignore those
* rctl entries.
*/
return (Z_OK);
break;
return (Z_NO_RESOURCE_ID);
sizeof (rctltab.zone_rctl_name));
}
{
case Z_OK:
/*FALLTHRU*/
case Z_NO_ENTRY:
return (B_TRUE);
default:
return (B_FALSE);
}
}
int
{
int i;
int err;
struct zone_rctltab rctltab;
struct zone_rctlvaltab *rctlvaltab;
char buf[128];
return (Z_ALIAS_DISALLOW);
break;
return (Z_NO_RESOURCE_ID);
/* remove any pre-existing definition for this rctl */
sizeof (rctltab.zone_rctl_name));
return (Z_NOMEM);
sizeof (rctlvaltab->zone_rctlval_priv));
sizeof (rctlvaltab->zone_rctlval_limit));
sizeof (rctlvaltab->zone_rctlval_action));
return (err);
}
static int
{
int err;
return (err);
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
static int
{
int err;
return (err);
if (*pool_importance != '\0') {
return (err);
pool_importance)) != Z_OK)
return (err);
}
return (Z_OK);
}
static int
{
int err;
return (err);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
int res = Z_NO_RESOURCE_ID;
return (err);
break;
}
}
/*
* Once we have msets, we should check that a mset
* do not exist before we delete the tmp_pool data.
*/
return (err);
return (res);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
int res = Z_NO_ENTRY;
return (Z_INVAL);
return (err);
/* this is an optional component */
return (err);
}
return (err);
}
return (err);
}
}
}
return (res);
}
int
{
int err;
return (err);
(void) zonecfg_endent(handle);
return (err);
}
static int
{
int err;
!= Z_OK)
return (err);
return (Z_OK);
}
int
{
int err;
return (err);
continue;
return (Z_OK);
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
/* it is ok if there is no mcap entry */
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
return (Z_INVAL);
return (err);
continue;
return (err);
}
return (Z_OK);
}
return (Z_NO_ENTRY);
}
static int
{
int err;
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (Z_OK);
}
int
{
int err;
return (err);
(void) zonecfg_endent(handle);
return (err);
}
/*
* pkgs_avl is an AVL tree of pkgs. Each pkg element contains a
* zpe_patches_avl member which holds an AVL tree of patches for that pkg.
* The patch elements have the same zpe_patches_avl member, each of which can
* hold an AVL tree of patches that are obsoleted by the patch.
*
* The zone xml data contains DTD_ELEM_PACKAGE elements, followed by
* DTD_ELEM_PATCH elements. The DTD_ELEM_PATCH patch element applies to the
* DTD_ELEM_PACKAGE that precedes it. The DTD_ELEM_PATCH element may have
* child DTD_ELEM_OBSOLETES nodes associated with it. The DTD_ELEM_PACKAGE
* really should have had the DTD_ELEM_PATCH elements as children but it
* was not defined that way initially so we are stuck with the DTD definition
* now. However, we can safely assume the ordering for compatibility.
*/
int
{
int res;
char name[MAXNAMELEN];
char version[ZONE_PKG_VERSMAX];
return (Z_INVAL);
return (res);
res = Z_NO_ENTRY;
goto done;
}
goto done;
goto done;
if ((pkg = (zone_pkg_entry_t *)
goto done;
}
goto done;
}
goto done;
}
} else {
}
char *p;
goto done;
if ((patch = (zone_pkg_entry_t *)
goto done;
}
dashp = p;
*p++ = '\0';
} else {
p = "";
}
goto done;
}
goto done;
}
*dashp = '-';
NULL, UU_DEFAULT);
goto done;
}
}
} else {
where);
}
/* Add any patches this patch obsoletes. */
!= 0)
continue;
goto done;
sizeof (zone_pkg_entry_t))) == NULL) {
goto done;
}
goto done;
}
/*
* The version doesn't matter for obsoleted
* patches.
*/
/*
* If this is the first obsolete patch, add an
* AVL tree to the parent patch element.
*/
goto done;
}
}
/* Insert obsolete patch into the AVL tree. */
pkg_pool);
} else {
}
}
}
}
done:
(void) zonecfg_endent(handle);
return (res);
}
int
{
return (zonecfg_setent(handle));
}
int
{
int err;
char buf[128];
return (Z_INVAL);
return (Z_NO_ENTRY);
break;
return (Z_NO_ENTRY);
}
return (err);
}
return (err);
}
return (err);
}
return (err);
}
return (err);
}
return (Z_OK);
}
int
{
return (zonecfg_endent(handle));
}
/*
* Maintain a space separated list of unique pkg names. PATH_MAX is used in
* the pkg code as the maximum size for a pkg name.
*/
static int
{
char *tmp;
/* space for str + 2 spaces + NULL */
return (Z_NOMEM);
return (Z_OK);
}
return (Z_OK);
/* space for str + 1 space + NULL */
return (Z_NOMEM);
return (Z_OK);
}
/*
* Process a list of pkgs from an entry in the contents file, adding each pkg
* name to the list of pkgs.
*
* It is possible for the pkg name to be preceeded by a special character
* which indicates some bookkeeping information for pkging. Check if the
* first char is not an Alpha char. If so, skip over it.
*/
static int
{
char *p;
char **tmpp;
int i;
/* skip over any special pkg bookkeeping char */
if (!isalpha(*p)) {
p++;
break;
}
/* Check if the pkg is already in the list */
for (i = 0; i < pkg_cnt; i++) {
break;
}
if (i < pkg_cnt)
continue;
/* The pkg is not in the list; add it. */
break;
}
break;
}
pkg_cnt++;
}
return (res);
}
/*
* Process an entry from the contents file (type "directory"). If the
* directory path is in the list of ipds and is not under a lofs mount within
* the ipd then add the associated list of pkgs to the pkg list. The input
* parameter "entry" will be broken up by the parser within this function so
* its value will be modified when this function exits.
*
* The entries we are looking for will look something like:
* /usr d none 0755 root sys SUNWctpls SUNWidnl SUNWlibCf ....
*/
static int
char **pkg_warn)
{
char *f1;
char *f2;
char *lastp;
int i;
char *nlp;
return (Z_OK);
/* Check if this directory entry is in the list of ipds. */
char wildcard[MAXPATHLEN];
/*
* We want to match on the path and any other directory
* entries under this path. When we use FNM_PATHNAME then
* that means '/' will not be matched by a wildcard (*) so
* we omit FNM_PATHNAME on the call with the wildcard matching.
*/
/* It looks like we do want the pkgs for this path. */
break;
}
}
/* This entry did not match any of the ipds. */
return (Z_OK);
/*
* Check if there is a fs mounted under the ipd. If so, ignore this
* entry.
*/
char wildcard[MAXPATHLEN];
/* We should ignore this path. */
break;
}
}
/* If not null, then we matched an fs mount point so ignore entry. */
return (Z_OK);
/*
* We do want the pkgs for this entry. First, skip over the next 4
* fields in the entry so that we call add_pkg_list starting with the
* pkg names.
*/
;
/* If there are < 4 fields this entry is corrupt, just skip it. */
if (i < 4)
return (Z_OK);
/* strip newline from the line */
if (*nlp == '\n')
*nlp = '\0';
}
/*
* Read an entry from a pkginfo or contents file. Some of these lines can
* either be arbitrarily long or be continued by a backslash at the end of
* the line. This function coalesces lines that are longer than the read
* buffer, and lines that are continued, into one buffer which is returned.
* The caller must free this memory. NULL is returned when we hit EOF or
* if we run out of memory (errno is set to ENOMEM).
*/
static char *
{
char *start;
char *inp;
char *p;
int char_cnt = 0;
errno = 0;
return (NULL);
}
int len;
break;
}
else
break;
}
start = p;
}
}
return (start);
}
static void
{
int i;
for (i = 0; i < cnt; i++)
}
/*
* Get a list of the inherited pkg dirs or fs entries configured for the
* zone. The type parameter will be either ZONE_IPD or ZONE_FS.
*/
static int
{
int res;
struct zone_fstab fstab;
int cnt = 0;
int i;
} else {
}
return (res);
char **p;
break;
}
entries = p;
break;
}
cnt++;
}
(void) zonecfg_endipdent(handle);
else
(void) zonecfg_endfsent(handle);
/* Add a NULL terminating element. */
char **p;
} else {
entries = p;
}
}
for (i = 0; i < cnt; i++)
}
return (res);
}
return (Z_OK);
}
/*
* Get the list of inherited-pkg-dirs (ipd) for the zone and then get the
* list of pkgs that deliver into those dirs.
*/
static int
{
int res;
char **ipds;
char **fss;
int pkg_cnt = 0;
int i;
return (res);
return (res);
}
/* We only have to process the contents file if we have ipds. */
char *buf;
break;
}
"WARNING: package operation in progress "
"on the following packages:\n %s\n"),
pkg_warn);
}
}
}
} else {
}
return (res);
}
/*
* Return true if pkg_name is in the list of pkgs that deliver into an
* inherited pkg directory for the zone.
*/
static boolean_t
{
int i;
for (i = 0; i < cnt; i++) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Keep track of obsoleted patches for this specific patch. We don't need to
* keep track of the patch version since once a patch is obsoleted, all prior
* versions are also obsolete and there won't be any new versions.
*/
static int
{
0)) == NULL)
return (Z_NOMEM);
}
== NULL)
return (Z_NOMEM);
return (Z_NOMEM);
}
return (Z_OK);
}
/*
* Keep track of obsoleted patches. We don't need to keep track of the patch
* version since once a patch is obsoleted, all prior versions are also
* obsolete and there won't be any new versions.
*/
static int
{
return (Z_NOMEM);
return (Z_NOMEM);
}
return (Z_OK);
}
return (Z_OK);
}
/*
* Keep a list of patches for a pkg. If we see a newer version of a patch,
* we only keep track of the newer version.
*/
static void
{
/* Check if this is a newer version of a patch we already have. */
char *endptr;
return;
}
}
}
/*
* Check if a patch is on the list of obsoleted patches. We don't need to
* check the patch version since once a patch is obsoleted, all prior versions
* are also obsolete and there won't be any new versions.
*/
static boolean_t
{
return (B_TRUE);
return (B_FALSE);
}
/* ARGSUSED */
static int
{
char *endptr;
return (1);
return (-1);
return (0);
}
/*
* Parse the patchinfo string for the patch.
*
* We are parsing entries of the form:
* PATCH_INFO_121454-02=Installed: Wed Dec 7 07:13:51 PST 2005 From: mum \
* Obsoletes: 120777-03 121087-02 119108-07 Requires: 119575-02 \
* 119255-06 Incompatibles:
*
* A backed out patch will have "backed out\n" as the status. We should
* skip these patches. We also ignore any entries that seem to be
* corrupted. Obsolete patches are saved in the obs_patches parameter
* AVL list.
*/
static int
{
char *p;
char *lastp;
char *ep;
char *pvers;
return (Z_OK);
/* Skip over "PATCH_INFO_" to get the patch id. */
return (Z_OK);
*ep++ = '\0';
/* Ignore all but installed patches. */
return (Z_OK);
/* remove newline */
if (*lastp == '\n')
*lastp = '\0';
return (Z_NOMEM);
*pvers++ = '\0';
else
pvers = "";
return (Z_NOMEM);
}
return (Z_NOMEM);
}
/*
* Start with the first token. This will probably be "Installed:".
* If we can't tokenize this entry, just return.
*/
return (Z_OK);
do {
if (strcmp(p, "Installed:") == 0 ||
strcmp(p, "Requires:") == 0 ||
strcmp(p, "From:") == 0 ||
strcmp(p, "Incompatibles:") == 0) {
continue;
} else if (strcmp(p, "Obsoletes:") == 0) {
continue;
}
if (!add_info)
continue;
*pvers = '\0';
/*
* We save all of the obsolete patches in one big list in the
* obs_patches AVL tree so that we know not to output those as
* part of the sw dependencies. However, we also need to save
* the obsolete patch information for this sepcific patch so
* so that we can do the cross manifest patch checking
* correctly.
*/
return (Z_NOMEM);
return (Z_NOMEM);
return (Z_OK);
}
/*
* AVL walker callback used to add patch to XML manifest.
*
*/
static int
add_patch(void *e, void *p)
{
patch = e;
args = p;
/* skip this patch if it has been obsoleted */
return (UU_WALK_NEXT);
else
patch->patch_vers);
return (UU_WALK_DONE);
return (UU_WALK_DONE);
NULL);
!= Z_OK)
return (UU_WALK_DONE);
}
}
return (UU_WALK_NEXT);
}
static void
{
if (patches_avl != NULL) {
patch_node_t *p;
free(p->patch_vers);
if (p->obs_patches != NULL) {
}
}
free(p);
}
}
}
/*
* Add the unique, highest version patches that are associated with this pkg
* to the sw inventory on the handle.
*/
static int
{
int i;
int res;
== NULL)
return (Z_NOMEM);
for (i = 0; i < infop->zpi_patch_cnt; i++) {
return (res);
}
}
}
/*
* Keep track of the pkgs we have already processed so that we can quickly
* skip those pkgs while recursively doing dependents.
*/
static boolean_t
{
/*
* We need to add it. If we don't have memory we just skip
* this pkg since this routine improves performance but the
* algorithm is still correct without it.
*/
if ((pkg = (zone_pkg_entry_t *)
return (B_FALSE);
return (B_FALSE);
}
/* Insert pkg into the AVL tree. */
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Add the pkg to the sw inventory on the handle.
*/
static int
{
int err;
return (err);
return (err);
return (err);
return (Z_OK);
}
static void
{
if (infop->zpi_patch_cnt > 0) {
int i;
for (i = 0; i < infop->zpi_patch_cnt; i++)
}
}
/*
* Read the pkginfo file and populate the structure with the data we need
* from this pkg for a sw inventory.
*/
static int
{
char *buf;
int err = 0;
infop->zpi_patch_cnt = 0;
return (errno);
int len;
if ((infop->zpi_version =
break;
}
/* remove trailing newline */
== 0) {
char **p;
== NULL) {
break;
}
infop->zpi_patchinfo = p;
break;
}
infop->zpi_patch_cnt++;
}
}
/* Clean up anything we did manage to allocate. */
}
return (err);
}
/*
* Add any dependent pkgs to the list. The pkg depend file lists pkg
* dependencies, one per line with an entry that looks like:
* P SUNWcar Core Architecture, (Root)
* See the depend(4) man page.
*/
static int
{
char depend[MAXPATHLEN];
char *buf;
return (Z_OK);
return (Z_OK);
char *deppkg;
char *delims = " \t";
char pkginfo[MAXPATHLEN];
struct zone_pkginfo info;
if (*buf != 'P') {
continue;
}
/* Skip past the leading 'P '. */
continue;
}
/* If the pkg is already in the manifest don't add it again. */
continue;
}
continue;
}
break;
}
if (info.zpi_patch_cnt > 0)
}
free_pkginfo(&info);
break;
}
return (res);
}
/* ARGSUSED */
static int
{
}
static void
{
zone_pkg_entry_t *p;
free(p);
}
}
}
/*
* Take a software inventory of the global zone. We need to get the set of
* packages and patches that are on the global zone that the specified
* non-global zone depends on. The packages we need in the inventory are:
*
* - skip the package if SUNW_PKG_THISZONE is 'true'
* otherwise,
* - add the package if
* a) SUNW_PKG_ALLZONES is 'true',
* or
* b) any file delivered by the package is in a file system that is inherited
* from the global zone.
* If the zone does not inherit any file systems (whole root)
* then (b) will be skipped.
*
* For each of the packages that is being added to the inventory, we will also
* add its dependent packages to the inventory.
*
* For each of the packages that is being added to the inventory, we will also
* add all of the associated, unique patches to the inventory.
*
* See the comment for zonecfg_getpkgdata() for compatability restrictions on
* how we must save the XML representation of the software inventory.
*/
static int
{
char pkginfo[MAXPATHLEN];
int res;
struct zone_pkginfo info;
int pkg_cnt = 0;
goto done;
}
goto done;
}
goto done;
}
UU_DEFAULT)) == NULL) {
goto done;
}
/*
* The obs_patches AVL tree saves all of the obsolete patches so
* that we know not to output those as part of the sw dependencies.
*/
== NULL) {
goto done;
}
goto done;
}
goto done;
}
continue;
continue;
break;
}
if (!info.zpi_this_zone &&
(info.zpi_all_zones ||
/*
* Add dependents first so any patches will get
* associated with the right pkg in the xml file.
*/
if (info.zpi_patch_cnt > 0)
}
}
free_pkginfo(&info);
break;
}
done:
if (patches_pool != NULL)
return (res);
}
/*
* zonecfg_devwalk call-back function used during detach to generate the
* dev info in the manifest.
*/
static int
{
int err;
char buf[128];
return (err);
return (err);
return (err);
return (err);
return (err);
return (err);
return (Z_OK);
}
/*
* Get the information required to support detaching a zone. This is
* called on the source system when detaching (the detaching parameter should
* be set to true) and on the destination system before attaching (the
* detaching parameter should be false).
*
* that the software on the global zone can support the zone when we attach.
* To do this we take a software inventory of the global zone. We also
* have to keep track of the device configuration so that we can properly
* recreate it on the destination.
*/
int
{
int res;
return (res);
if (detaching)
return (res);
}