libzonecfg.c revision 5749802bc1ab53eee0631759471dabfc4b455cd4
/*
* 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 2006 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 <netdb.h>
#include <libxml/xmlmemory.h>
#include <libdevinfo.h>
#include <dirent.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
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;
};
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 (Z_INVAL);
return (err);
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);
}
/*
* 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
{
char autobootstr[DTD_ENTITY_BOOL_LEN];
int ret;
sizeof (autobootstr))) != Z_OK)
return (ret);
else
return (ret);
}
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-- but the library has
* malfunctioned if it fails to validate, so it's an assert.
*/
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
{
char zname[ZONENAME_MAX];
char path[MAXPATHLEN];
char migpath[MAXPATHLEN];
int err = Z_SAVING_FILE;
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-- but the library has
* malfunctioned if it fails to validate, so it's an assert.
*/
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);
}
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
{
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 (Z_OK);
}
static int
{
int err;
return (err);
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;
int err;
continue;
if (match_match) {
/*
* Since we're succesfully deleting (or modifying)
* a device entry, we need to do device node cleanup
* on the next zone bootup, so we leave behind a
* historical record for zoneadmd to consume.
*/
return (err);
return (Z_OK);
}
}
return (Z_NO_RESOURCE_ID);
}
int
{
int err;
return (Z_INVAL);
return (err);
return (err);
return (Z_OK);
}
int
{
int err;
return (err);
continue;
}
return (Z_OK);
}
int
{
int err;
return (err);
return (Z_OK);
}
return (Z_NO_ENTRY);
}
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 is the set of devices which must be present in every zone. Users
* can augment this list with additional device rules in their zone
* configuration, but at present cannot remove any of the this set of
* standard devices. All matching is done by /dev pathname (the "/dev"
* part is implicit. Try to keep rules which match a large number of
* devices (like the pts rule) first.
*/
static const char *standard_devs[] = {
"pts/*",
"ptmx",
"random",
"urandom",
"poll",
"pool",
"kstat",
"zero",
"null",
"crypto",
"cryptoadm",
"ticots",
"ticotsord",
"ticlts",
"lo0",
"lo1",
"lo2",
"lo3",
"tty",
"logindmux",
"log",
"conslog",
"arp",
"tcp",
"tcp6",
"udp",
"udp6",
"sysevent",
#ifdef __sparc
"openprom",
#endif
"dtrace/*",
"zfs",
};
/*
* 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);
}
/*
* This routine is used to determine if a given device should appear in the
* zone represented by 'handle'. First it consults the list of "standard"
* zone devices. Then it scans the user-supplied device entries.
*/
int
struct zone_devtab *out_match)
{
int err;
char match[MAXPATHLEN];
const char **stdmatch;
return (Z_INVAL);
/*
* Check the "standard" devices which we require to be present.
*/
/*
* fnmatch gives us simple but powerful shell-style matching.
*/
if (!out_match)
return (Z_OK);
sizeof (out_match->zone_dev_match),
"/dev/%s", *stdmatch);
return (Z_OK);
}
}
/*
* We got no hits in the set of standard devices. On to the user
* supplied ones.
*/
return (err);
}
return (Z_NO_ENTRY);
char *m;
continue;
return (err);
}
m = match;
/*
* fnmatch gives us simple but powerful shell-style matching;
* but first, we need to strip out /dev/ from the matching rule.
*/
m += 5;
break;
}
}
if (!found)
return (Z_NO_ENTRY);
if (!out_match)
return (Z_OK);
sizeof (out_match->zone_dev_match));
return (Z_OK);
}
/*
* This routine answers the question: do we think <devpath> should be
* deleted during this zone's bootup?
*
* The criteria are:
* - Is there a matching rule for devpath? If yes, then NO.
* - Is this a CHR or BLK device node? If no, then NO.
* - Is there a deleted device entry which matches devpath? Then, YES
* - Else NO
*/
int
{
int err;
char match[MAXPATHLEN];
char fullpath[MAXPATHLEN];
char zonepath[MAXPATHLEN];
return (Z_INVAL);
/*
* If a matching rule exists for this device, then leave it alone.
*/
return (Z_OK);
/*
* lstat it. If it's a regular file, a directory, a link or
* something else miscellaneous, we'll be cautious and not
* touch it.
*/
return (err);
devpath);
return (Z_OK);
return (err);
}
return (Z_NO_ENTRY);
char *m;
continue;
return (err);
}
m = match;
/*
* fnmatch gives us simple but powerful shell-style matching;
* but first, we need to strip out /dev/ from the matching rule.
*/
m += 5;
break;
}
}
return (Z_OK);
}
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"));
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);
}
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 (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; those that
* are required in order for a non-global zone to boot, those which are in
* the default set of privileges available to non-global zones, those
* privileges which should not be allowed to be given to non-global zones
* and all other privileges, which are optional and 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.
*/
/*
* 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.
*/
static const char *required_priv_list[] = {
};
/*
* 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 other non-global zones on the
* system or in the global zone. Privileges which are considered by
* default, "unsafe", include ones which affect a global resource, such as
* the system clock or physical memory.
*/
static const char *default_priv_list[] = {
};
/*
* Set of privileges not currently permitted within a non-global zone.
* Some of these privileges 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.
*/
static const char *prohibited_priv_list[] = {
};
/*
* 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 ","
int
{
const char **strp;
return (Z_INVAL);
}
}
return (Z_OK);
}
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
{
char *cp;
char *lasts;
const char **strp;
char *tmp;
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)
{
char *cp;
int err;
int limitlen;
/*
* 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 (zonecfg_default_privset(privs));
}
/*
* 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);
}
static zone_state_t
{
char zoneroot[MAXPATHLEN];
switch (kernel_state) {
case ZONE_IS_UNINITIALIZED:
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));
}
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 (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;
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));
}
/*
* 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++;
/* 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") and if the
* directory path is in the list of paths, 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 *f1;
char *f2;
char *lastp;
int i;
return (Z_OK);
/* Check if this directory entry is in the list of paths. */
for (i = 0; i < cnt; i++) {
/*
* We do want the pkgs for this path. First, skip
* over the next 4 fields in the entry so that we call
* add_pkg_list starting with the pkg names.
*/
int j;
char *nlp;
for (j = 0; j < 4 &&
;
/*
* If there are < 4 fields this entry is corrupt,
* just skip it.
*/
if (j < 4)
return (Z_OK);
/* strip newline from the line */
if (*nlp == '\n')
*nlp = '\0';
break;
}
}
return (res);
}
/*
* 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 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;
struct zone_fstab fstab;
int ipd_cnt = 0;
int pkg_cnt = 0;
int i;
return (res);
char **p;
int len;
break;
}
ipds = p;
break;
}
ipd_cnt++;
break;
}
ipd_cnt++;
}
(void) zonecfg_endipdent(handle);
for (i = 0; i < ipd_cnt; i++)
return (res);
}
/* We only have to process the contents file if we have ipds. */
if (ipd_cnt > 0) {
char *buf;
&pkg_cnt);
break;
}
}
}
for (i = 0; i < ipd_cnt; i++)
} 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);
}
/*
* Start by adding the patch to the sw inventory on the handle.
*
* The info parameter will be the portion of the PATCH_INFO_ entry following
* the '='. For example:
* 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 an info line of "backed out\n". We should
* skip these patches.
*
* We also want to add the Obsolete and Incompatible patches to the
* sw inventory description of this patch.
*/
static int
{
int err;
char *p;
char *lastp;
return (Z_OK);
return (err);
return (err);
/*
* Start with the first token. This will probably be "Installed:".
* If we can't tokenize this entry, just return.
*/
return (Z_OK);
do {
char *nlp;
if (strcmp(p, "Installed:") == 0 ||
strcmp(p, "Requires:") == 0 ||
strcmp(p, "From:") == 0) {
continue;
} else if (strcmp(p, "Obsoletes:") == 0) {
continue;
} else if (strcmp(p, "Incompatibles:") == 0) {
continue;
}
if (!add_info)
continue;
/* strip newline from last patch in the line */
if (*nlp == '\n')
*nlp = '\0';
if (obsolete)
else
return (err);
return (Z_OK);
}
static boolean_t
{
char id[MAXNAMELEN];
!= Z_OK)
continue;
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Add the unique patches associated with this pkg to the sw inventory on the
* handle.
*
* We are processing 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:
*
*/
static int
{
int i;
for (i = 0; i < infop->zpi_patch_cnt; i++) {
char *p, *ep;
continue;
/* Skip over "PATCH_INFO_" to get the patch id. */
continue;
*ep = '\0';
if (unique_patch(handle, p))
break;
}
return (res);
}
/*
* 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);
}
/*
* 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 all of the associated, unique patches to the inventory.
*/
static int
{
char pkginfo[MAXPATHLEN];
int res;
struct zone_pkginfo info;
int pkg_cnt = 0;
return (res);
return (Z_OK);
}
continue;
continue;
break;
}
if (!info.zpi_this_zone &&
(info.zpi_all_zones ||
if (info.zpi_patch_cnt > 0)
}
}
free_pkginfo(&info);
break;
}
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);
}