root/usr/src/lib/libinstzones/common/zones.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */


/*
 * Module:      zones.c
 * Group:       libinstzones
 * Description: Provide "zones" interface for install consolidation code
 *
 * Public Methods:
 *  z_create_zone_admin_file - Given a location to create the file, and
 *      optionally an existing administration file, generate an
 *      administration file that can be used to perform "non-interactive"
 *      operations in a non-global zone.
 *  z_free_zone_list - free contents of zoneList_t object
 *  z_get_nonglobal_zone_list - return zoneList_t object describing all
 *      non-global native zones
 *  z_get_nonglobal_zone_list_by_brand - return zoneList_t object describing
 *      all non-global zones matching the list of zone brands passed in.
 *  z_free_brand_list - free contents of a zoneBrandList_t object
 *  z_make_brand_list - return a zoneBrandList_t object describing the list
 *      of all zone brands passed in.
 *  z_get_zonename - return the name of the current zone
 *  z_global_only - Determine if the global zone is only zone on the spec list
 *  z_lock_this_zone - lock this zone
 *  z_lock_zones - lock specified zones
 *  z_mount_in_lz - Mount global zone directory in specified zone's root file
 *      system
 *  z_non_global_zones_exist - Determine if any non-global native zones exist
 *  z_on_zone_spec - Determine if named zone is on the zone_spec list
 *  z_running_in_global_zone - Determine if running in the "global" zone
 *  z_set_output_functions - Link program specific output functions
 *  z_set_zone_root - Set root for zones library operations
 *  z_set_zone_spec - Set list of zones on which actions will be performed
 *  z_umount_lz_mount - Unmount directory mounted with z_mount_in_lz
 *  z_unlock_this_zone - unlock this zone
 *  z_unlock_zones - unlock specified zones
 *  z_verify_zone_spec - Verify list of zones on which actions will be performed
 *  z_zlist_change_zone_state - Change the current state of the specified zone
 *  z_zlist_get_current_state - Determine the current kernel state of the
 *      specified zone
 *  z_zlist_get_original_state - Return the original kernal state of the
 *      specified zone
 *  z_zlist_get_scratch - Determine name of scratch zone
 *  z_zlist_get_zonename - Determine name of specified zone
 *  z_zlist_get_zonepath - Determine zonepath of specified zone
 *  z_zlist_restore_zone_state - Return the zone to the state it was originally
 *      in
 *  z_zone_exec - Execute a Unix command in a specified zone and return results
 *  z_zones_are_implemented - Determine if any zone operations can be performed
 *  z_is_zone_branded - determine if zone has a non-native brand
 *  z_is_zone_brand_in_list - determine if the zone's brand matches the
 *      brand list passed in.
 *  z_brands_are_implemented - determine if branded zones are implemented on
 *                      this system
 */

/*
 * System includes
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <stropts.h>
#include <wait.h>
#include <zone.h>
#include <sys/brand.h>
#include <libintl.h>
#include <locale.h>
#include <libzonecfg.h>
#include <libcontract.h>
#include <sys/contract/process.h>
#include <sys/ctfs.h>
#include <assert.h>
#include <dlfcn.h>
#include <link.h>
#include <time.h>

/*
 * local includes
 */

/*
 * When _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA is defined,
 * instzones_lib.h will define the z_global_data structure.
 * Otherwise an extern to the structure is inserted.
 */

#define _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA
#include "instzones_lib.h"
#include "zones_strings.h"

/*
 * Private structures
 */

#define CLUSTER_BRAND_NAME      "cluster"

/* maximum number of arguments to exec() call */

#define UUID_FORMAT     "%02d%02d%02d%03d-%02d%02d%02d%d-%016llx"

/*
 * Library Function Prototypes
 */

#define streq(a, b) (strcmp((a), (b)) == 0)

/*
 * Local Function Prototypes
 */

/*
 * global internal (private) declarations
 */

/*
 * *****************************************************************************
 * global external (public) functions
 * *****************************************************************************
 */

/*
 * Name:        z_create_zone_admin_file
 * Description: Given a location to create the file, and optionally an existing
 *              administration file, generate an administration file that
 *              can be used to perform "non-interactive" operations in a
 *              non-global zone.
 * Arguments:   a_zoneAdminFilename - pointer to string representing the
 *                      full path of zone admin file to create
 *              a_userAdminFilename - pointer to string representing the path
 *                      to an existing "user" administration file - the
 *                      administration file created will contain the
 *                      settings contained in this file, modified as
 *                      appropriate to supress any interaction;
 *                      If this is == NULL then the administration file
 *                      created will not contain any extra settings
 * Returns:     boolean_t
 *                      == B_TRUE - admin file created
 *                      == B_FALSE - failed to create admin file
 */

boolean_t
z_create_zone_admin_file(char *a_zoneAdminFilename, char *a_userAdminFilename)
{
        FILE    *zFp;
        FILE    *uFp = (FILE *)NULL;

        /* entry assertions */

        assert(a_zoneAdminFilename != NULL);
        assert(*a_zoneAdminFilename != '\0');

        /* create temporary zone admin file */

        zFp = fopen(a_zoneAdminFilename, "w");
        if (zFp == (FILE *)NULL) {
                return (B_FALSE);
        }

        /* open user admin file if specified */

        if (a_userAdminFilename != (char *)NULL) {
                uFp = fopen(a_userAdminFilename, "r");
        }

        /* create default admin file for zone pkg ops if no user admin file */

        if (uFp == (FILE *)NULL) {
                /* create default admin file */
                (void) fprintf(zFp, "action=nocheck\nauthentication=nocheck\n"
                    "basedir=default\nconflict=nocheck\nidepend=nocheck\n"
                    "instance=unique\npartial=nocheck\nrdepend=nocheck\n"
                    "runlevel=nocheck\nsetuid=nocheck\nspace=nocheck\n"
                    "mail=\n");
        } else for (;;) {
                /* copy user admin file substitute/change appropriate entries */
                char    buf[LINE_MAX+1];
                char    *p;

                /* read next line of user admin file */

                p = fgets(buf, sizeof (buf), uFp);
                if (p == (char *)NULL) {
                        (void) fclose(uFp);
                        break;
                }

                /* modify / replace / accept as appropriate */

                if (strncmp(buf, "instance=quit", 13) == 0) {
                        (void) fprintf(zFp, "%s", "instance=unique\n");
                        /*LINTED*/
                } else if (strncmp(buf, "keystore=", 9) == 0) {
                } else if (strncmp(buf, "action=", 7) == 0) {
                        (void) fprintf(zFp, "action=nocheck\n");
                } else if (strncmp(buf, "authentication=", 15) == 0) {
                        (void) fprintf(zFp, "authentication=nocheck\n");
                } else if (strncmp(buf, "conflict=", 9) == 0) {
                        (void) fprintf(zFp, "conflict=nocheck\n");
                } else if (strncmp(buf, "idepend=", 8) == 0) {
                        (void) fprintf(zFp, "idepend=nocheck\n");
                } else if (strncmp(buf, "mail=", 5) == 0) {
                        (void) fprintf(zFp, "mail=\n");
                } else if (strncmp(buf, "partial=", 8) == 0) {
                        (void) fprintf(zFp, "partial=nocheck\n");
                } else if (strncmp(buf, "rdepend=", 8) == 0) {
                        (void) fprintf(zFp, "rdepend=nocheck\n");
                } else if (strncmp(buf, "runlevel=", 9) == 0) {
                        (void) fprintf(zFp, "runlevel=nocheck\n");
                } else if (strncmp(buf, "setuid=", 7) == 0) {
                        (void) fprintf(zFp, "setuid=nocheck\n");
                } else if (strncmp(buf, "space=", 6) == 0) {
                        (void) fprintf(zFp, "space=nocheck\n");
                } else {
                        (void) fprintf(zFp, "%s", buf);
                }
        }

        /* close admin file and return success */

        (void) fclose(zFp);
        return (B_TRUE);
}

/*
 * Name:        z_brands_are_implemented
 * Description: Determine if any branded zones may be present
 * Arguments:   void
 * Returns:     boolean_t
 *                      == B_TRUE - branded zones are supported
 *                      == B_FALSE - branded zones are not supported
 */

boolean_t
z_brands_are_implemented(void)
{
static  boolean_t       _brandsImplementedDetermined = B_FALSE;
static  boolean_t       _brandsAreImplemented = B_FALSE;

        /* if availability has not been determined, cache it now */

        if (!_brandsImplementedDetermined) {
                _brandsImplementedDetermined = B_TRUE;
                _brandsAreImplemented = _z_brands_are_implemented();
                if (_brandsAreImplemented) {
                        _z_echoDebug(DBG_BRANDS_ARE_IMPLEMENTED);
                } else {
                        _z_echoDebug(DBG_BRANDS_NOT_IMPLEMENTED);
                }
        }

        /* return cached answer */

        return (_brandsAreImplemented);
}

/*
 * Name:        z_free_zone_list
 * Description: free contents of zoneList_t object
 * Arguments:   a_zlst - handle to zoneList_t object to free
 * Returns:     void
 */

void
z_free_zone_list(zoneList_t a_zlst)
{
        int     numzones;

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return;
        }

        /* free each entry in the zone list */

        for (numzones = 0; a_zlst[numzones]._zlName != (char *)NULL;
            numzones++) {
                zoneListElement_t *zelm = &a_zlst[numzones];

                /* free zone name string */

                free(zelm->_zlName);

                /* free zonepath string */

                if (zelm->_zlPath != (char *)NULL) {
                        free(zelm->_zlPath);
                }

        }

        /* free handle to the list */

        free(a_zlst);
}

/*
 * Name:        z_get_nonglobal_zone_list
 * Description: return zoneList_t object describing all non-global
 *              native zones - branded zones are not included in list
 * Arguments:   None.
 * Returns:     zoneList_t
 *                      == NULL - error, list could not be generated
 *                      != NULL - success, list returned
 * NOTE:        Any zoneList_t returned is placed in new storage for the
 *              calling function. The caller must use 'z_free_zone_list' to
 *              dispose of the storage once the list is no longer needed.
 */

zoneList_t
z_get_nonglobal_zone_list(void)
{
        zoneList_t zones;
        zoneBrandList_t *brands = NULL;

        if ((brands = z_make_brand_list("native cluster", " ")) == NULL)
                return (NULL);

        zones = z_get_nonglobal_zone_list_by_brand(brands);

        z_free_brand_list(brands);

        return (zones);
}

/*
 * Name:        z_free_brand_list
 * Description: Free contents of zoneBrandList_t object
 * Arguments:   brands - pointer to zoneBrandList_t object to free
 * Returns:     void
 */
void
z_free_brand_list(zoneBrandList_t *brands)
{
        while (brands != NULL) {
                zoneBrandList_t *temp = brands;
                free(brands->string_ptr);
                brands = brands->next;
                free(temp);
        }
}

/*
 * Name:        z_make_brand_list
 * Description: Given a string with a list of brand name delimited by
 *              the delimeter passed in, build a zoneBrandList_t structure
 *              with the list of brand names and return it to the caller.
 * Arguments:
 *              brands - const char pointer to string list of brand names
 *              delim - const char pointer to string representing the
 *                      delimeter for brands string.
 * Returns:     zoneBrandList_t *
 *                      == NULL - error, list could not be generated
 *                      != NULL - success, list returned
 * NOTE:        Any zoneBrandList_t returned is placed in new storage for the
 *              calling function.  The caller must use 'z_free_brand_list' to
 *              dispose of the storage once the list is no longer needed.
 */
zoneBrandList_t *
z_make_brand_list(const char *brands, const char *delim)
{
        zoneBrandList_t *brand = NULL, *head = NULL;
        char            *blist = NULL;
        char            *str = NULL;

        if ((blist = strdup(brands)) == NULL)
                return (NULL);

        if ((str = strtok(blist, delim)) != NULL) {
                if ((brand = (zoneBrandList_t *)
                    malloc(sizeof (struct _zoneBrandList))) == NULL) {
                        return (NULL);
                }

                head = brand;
                brand->string_ptr = strdup(str);
                brand->next = NULL;

                while ((str = strtok(NULL, delim)) != NULL) {
                        if ((brand->next = (zoneBrandList_t *)
                            malloc(sizeof (struct _zoneBrandList))) == NULL) {
                                return (NULL);
                        }

                        brand = brand->next;
                        brand->string_ptr = strdup(str);
                        brand->next = NULL;
                }
        }

        free(blist);
        return (head);
}

/*
 * Name:        z_get_nonglobal_zone_list_by_brand
 * Description: return zoneList_t object describing all non-global
 *              zones matching the list of brands passed in.
 * Arguments:   brands - The list of zone brands to look for.
 * Returns:     zoneList_t
 *                      == NULL - error, list could not be generated
 *                      != NULL - success, list returned
 * NOTE:        Any zoneList_t returned is placed in new storage for the
 *              calling function. The caller must use 'z_free_zone_list' to
 *              dispose of the storage once the list is no longer needed.
 */
zoneList_t
z_get_nonglobal_zone_list_by_brand(zoneBrandList_t *brands)
{
        FILE            *zoneIndexFP;
        int             numzones = 0;
        struct zoneent  *ze;
        zoneList_t      zlst = NULL;
        FILE            *mapFP;
        char            zonename[ZONENAME_MAX];
        zone_spec_t     *zent;

        /* if zones are not implemented, return empty list */

        if (!z_zones_are_implemented()) {
                return ((zoneList_t)NULL);
        }

        /*
         * Open the zone index file.  Note that getzoneent_private() handles
         * NULL.
         */
        zoneIndexFP = setzoneent();

        mapFP = zonecfg_open_scratch("", B_FALSE);

        /* index file open; scan all zones; see if any are at least installed */

        while ((ze = getzoneent_private(zoneIndexFP)) != NULL) {
                zone_state_t    st;

                /* skip the global zone */

                if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0) {
                        free(ze);
                        continue;
                }

                /*
                 * skip any zones with brands not on the brand list
                 */
                if (!z_is_zone_brand_in_list(ze->zone_name, brands)) {
                        free(ze);
                        continue;
                }

                /*
                 * If the user specified an explicit zone list, then ignore any
                 * zones that aren't on that list.
                 */
                if ((zent = _z_global_data._zone_spec) != NULL) {
                        while (zent != NULL) {
                                if (strcmp(zent->zl_name, ze->zone_name) == 0)
                                        break;
                                zent = zent->zl_next;
                        }
                        if (zent == NULL) {
                                free(ze);
                                continue;
                        }
                }

                /* non-global zone: create entry for this zone */

                if (numzones == 0) {
                        zlst = (zoneList_t)_z_calloc(
                            sizeof (zoneListElement_t)*2);
                } else {
                        zlst = (zoneList_t)_z_realloc(zlst,
                            sizeof (zoneListElement_t)*(numzones+2));
                        (void) memset(&zlst[numzones], 0L,
                            sizeof (zoneListElement_t)*2);
                }

                /*
                 * remember the zone name, zonepath and the current
                 * zone state of the zone.
                 */
                zlst[numzones]._zlName = _z_strdup(ze->zone_name);
                zlst[numzones]._zlPath = _z_strdup(ze->zone_path);
                zlst[numzones]._zlOrigInstallState = ze->zone_state;
                zlst[numzones]._zlCurrInstallState = ze->zone_state;

                /* get the zone kernel status */

                if (zone_get_state(ze->zone_name, &st) != Z_OK) {
                        st = ZONE_STATE_INCOMPLETE;
                }

                _z_echoDebug(DBG_ZONES_NGZ_LIST_STATES,
                    ze->zone_name, ze->zone_state, st);

                /*
                 * For a scratch zone, we need to know the kernel zone name.
                 */
                if (zonecfg_in_alt_root() && mapFP != NULL &&
                    zonecfg_find_scratch(mapFP, ze->zone_name,
                    zonecfg_get_root(), zonename, sizeof (zonename)) != -1) {
                        free(zlst[numzones]._zlScratchName);
                        zlst[numzones]._zlScratchName = _z_strdup(zonename);
                }

                /*
                 * remember the current kernel status of the zone.
                 */

                zlst[numzones]._zlOrigKernelStatus = st;
                zlst[numzones]._zlCurrKernelStatus = st;

                numzones++;
                free(ze);
        }

        /* close the index file */
        endzoneent(zoneIndexFP);

        if (mapFP != NULL)
                zonecfg_close_scratch(mapFP);

        /* return generated list */

        return (zlst);
}

/*
 * Name:        z_get_zonename
 * Description: return the name of the current zone
 * Arguments:   void
 * Returns:     char *
 *                      - pointer to string representing the name of the current
 *                      zone
 * NOTE:        Any string returned is placed in new storage for the
 *              calling function. The caller must use 'Free' to dispose
 *              of the storage once the string is no longer needed.
 */

char *
z_get_zonename(void)
{
        ssize_t         zonenameLen;
        char            zonename[ZONENAME_MAX];
        zoneid_t        zoneid = (zoneid_t)-1;

        /* if zones are not implemented, return "" */

        if (!z_zones_are_implemented()) {
                return (_z_strdup(""));
        }

        /* get the zone i.d. of the current zone */

        zoneid = getzoneid();

        /* get the name of the current zone */

        zonenameLen = getzonenamebyid(zoneid, zonename, sizeof (zonename));

        /* return "" if could not get zonename */

        if (zonenameLen < 1) {
                return (_z_strdup(""));
        }

        return (_z_strdup(zonename));
}

/*
 * Name:        z_global_only
 * Description: Determine if the global zone is only zone on the spec list.
 * Arguments:   None
 * Returns:     B_TRUE if global zone is the only zone on the list,
 *              B_FALSE otherwise.
 */

boolean_t
z_global_only(void)
{
        /* return true if zones are not implemented - treate as global zone */

        if (!z_zones_are_implemented()) {
                return (B_TRUE);
        }

        /* return true if this is the global zone */

        if (_z_global_data._zone_spec != NULL &&
            _z_global_data._zone_spec->zl_next == NULL &&
            strcmp(_z_global_data._zone_spec->zl_name, GLOBAL_ZONENAME) == 0) {
                return (B_TRUE);
        }

        /* return false - not the global zone */

        return (B_FALSE);
}

/*
 * Name:        z_lock_this_zone
 * Description: lock this zone
 * Arguments:   a_lflags - [RO, *RO] - (ZLOCKS_T)
 *                      Flags indicating which locks to acquire
 * Returns:     boolean_t
 *                      == B_TRUE - success specified locks acquired
 *                      == B_FALSE - failure specified locks not acquired
 * NOTE: the lock objects for "this zone" are maintained internally.
 */

boolean_t
z_lock_this_zone(ZLOCKS_T a_lflags)
{
        boolean_t       b;
        char            *zoneName;
        pid_t           pid = (pid_t)0;

        /* entry assertions */

        assert(a_lflags != ZLOCKS_NONE);

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_LCK_THIS, a_lflags);

        zoneName = z_get_zonename();
        pid = getpid();

        /* lock zone administration */

        if (a_lflags & ZLOCKS_ZONE_ADMIN) {
                b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks,
                    zoneName, LOBJ_ZONEADMIN, pid,
                    MSG_ZONES_LCK_THIS_ZONEADM,
                    ERR_ZONES_LCK_THIS_ZONEADM);
                if (!b) {
                        (void) free(zoneName);
                        return (B_FALSE);
                }
        }

        /* lock package administration always */

        if (a_lflags & ZLOCKS_PKG_ADMIN) {
                b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks,
                    zoneName, LOBJ_PKGADMIN, pid,
                    MSG_ZONES_LCK_THIS_PKGADM,
                    ERR_ZONES_LCK_THIS_PKGADM);
                if (!b) {
                        (void) z_unlock_this_zone(a_lflags);
                        (void) free(zoneName);
                        return (B_FALSE);
                }
        }

        (void) free(zoneName);

        return (B_TRUE);
}

/*
 * Name:        z_lock_zones
 * Description: lock specified zones
 * Arguments:   a_zlst - zoneList_t object describing zones to lock
 *              a_lflags - [RO, *RO] - (ZLOCKS_T)
 *                      Flags indicating which locks to acquire
 * Returns:     boolean_t
 *                      == B_TRUE - success, zones locked
 *                      == B_FALSE - failure, zones not locked
 */

boolean_t
z_lock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags)
{
        boolean_t       b;
        int             i;

        /* entry assertions */

        assert(a_lflags != ZLOCKS_NONE);

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_LCK_ZONES, a_lflags);

        /* if zones are not implemented, return TRUE */

        if (z_zones_are_implemented() == B_FALSE) {
                _z_echoDebug(DBG_ZONES_LCK_ZONES_UNIMP);
                return (B_TRUE);
        }

        /* lock this zone first before locking other zones */

        b = z_lock_this_zone(a_lflags);
        if (b == B_FALSE) {
                return (b);
        }

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                _z_echoDebug(DBG_ZONES_LCK_ZONES_NOZONES);
                return (B_FALSE);
        }

        /* zones exist */

        _z_echoDebug(DBG_ZONES_LCK_ZONES_EXIST);

        /*
         * lock each listed zone that is currently running
         */

        for (i = 0; (a_zlst[i]._zlName != (char *)NULL); i++) {
                /* ignore zone if already locked */
                if (a_zlst[i]._zlStatus & ZST_LOCKED) {
                        continue;
                }

                /* ignore zone if not running */
                if (a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_RUNNING &&
                    a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_MOUNTED) {
                        continue;
                }

                /*
                 * mark zone locked - if interrupted out during lock, an attempt
                 * will be made to release the lock
                 */
                a_zlst[i]._zlStatus |= ZST_LOCKED;

                /* lock this zone */
                b = _z_lock_zone(&a_zlst[i], a_lflags);

                /* on failure unlock all zones and return error */
                if (b != B_TRUE) {
                        _z_program_error(ERR_ZONES_LCK_ZONES_FAILED,
                            a_zlst[i]._zlName);
                        (void) z_unlock_zones(a_zlst, a_lflags);
                        return (B_FALSE);
                }
        }

        /* success */

        return (B_TRUE);
}

/*
 * Name:        z_mount_in_lz
 * Description: Mount global zone directory in specified zone's root file system
 * Arguments:   r_lzMountPoint - pointer to handle to string - on success, the
 *                      full path to the mount point relative to the global zone
 *                      root file system is returned here - this is needed to
 *                      unmount the directory when it is no longer needed
 *              r_lzRootPath - pointer to handle to string - on success, the
 *                      full path to the mount point relative to the specified
 *                      zone's root file system is returned here - this is
 *                      passed to any command executing in the specified zone to
 *                      access the directory mounted
 *              a_zoneName - pointer to string representing the name of the zone
 *                      to mount the specified global zone directory in
 *              a_gzPath - pointer to string representing the full absolute path
 *                      of the global zone directory to LOFS mount inside of the
 *                      specified non-global zone
 *              a_mountPointPrefix - pointer to string representing the prefix
 *                      to be used when creating the mount point name in the
 *                      specified zone's root directory
 * Returns:     boolean_t
 *                      == B_TRUE - global zone directory mounted successfully
 *                      == B_FALSE - failed to mount directory in specified zone
 * NOTE:        Any strings returned is placed in new storage for the
 *              calling function. The caller must use 'Free' to dispose
 *              of the storage once the strings are no longer needed.
 */

boolean_t
z_mount_in_lz(char **r_lzMountPoint, char **r_lzRootPath, char *a_zoneName,
        char *a_gzPath, char *a_mountPointPrefix)
{
        char            lzRootPath[MAXPATHLEN] = {'\0'};
        char            uuid[MAXPATHLEN] = {'\0'};
        char            gzMountPoint[MAXPATHLEN] = {'\0'};
        char            lzMountPoint[MAXPATHLEN] = {'\0'};
        hrtime_t        hretime;
        int             err;
        int             slen;
        struct tm       tstruct;
        time_t          thetime;
        zoneid_t        zid;

        /* entry assertions */

        assert(a_zoneName != (char *)NULL);
        assert(*a_zoneName != '\0');
        assert(a_gzPath != (char *)NULL);
        assert(*a_gzPath != '\0');
        assert(r_lzMountPoint != (char **)NULL);
        assert(r_lzRootPath != (char **)NULL);

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_MOUNT_IN_LZ_ENTRY, a_zoneName, a_gzPath);

        /* reset returned non-global zone mount point path handle */

        *r_lzMountPoint = (char *)NULL;
        *r_lzRootPath = (char *)NULL;

        /* if zones are not implemented, return FALSE */

        if (z_zones_are_implemented() == B_FALSE) {
                return (B_FALSE);
        }

        /* error if global zone path is not absolute */

        if (*a_gzPath != '/') {
                _z_program_error(ERR_GZPATH_NOT_ABSOLUTE, a_gzPath);
                return (B_FALSE);
        }

        /* error if global zone path does not exist */

        if (_z_is_directory(a_gzPath) != 0) {
                _z_program_error(ERR_GZPATH_NOT_DIR, a_gzPath, strerror(errno));
                return (B_FALSE);
        }

        /* verify that specified non-global zone exists */

        err = zone_get_id(a_zoneName, &zid);
        if (err != Z_OK) {
                _z_program_error(ERR_GET_ZONEID, a_zoneName,
                    zonecfg_strerror(err));
                return (B_FALSE);
        }

        /* obtain global zone path to non-global zones root file system */

        err = zone_get_rootpath(a_zoneName, lzRootPath, sizeof (lzRootPath));
        if (err != Z_OK) {
                _z_program_error(ERR_NO_ZONE_ROOTPATH, a_zoneName,
                    zonecfg_strerror(err));
                return (B_FALSE);
        }

        if (lzRootPath[0] == '\0') {
                _z_program_error(ERR_ROOTPATH_EMPTY, a_zoneName);
                return (B_FALSE);
        }

        /*
         * lofs resolve the non-global zone's root path first in case
         * its in a path that's been lofs mounted read-only.
         */
        z_resolve_lofs(lzRootPath, sizeof (lzRootPath));

        /* verify that the root path exists */

        if (_z_is_directory(lzRootPath) != 0) {
                _z_program_error(ERR_LZROOT_NOTDIR, lzRootPath,
                    strerror(errno));
                return (B_FALSE);
        }

        /*
         * generate a unique key - the key is the same length as unique uid
         * but contains different information that is as unique as can be made;
         * include current hires time (nanosecond real timer). Such a unique
         * i.d. will look like:
         *              0203104092-1145345-0004e94d6af481a0
         */

        hretime = gethrtime();

        thetime = time((time_t *)NULL);
        (void) localtime_r(&thetime, &tstruct);

        slen = snprintf(uuid, sizeof (uuid),
            UUID_FORMAT,
            tstruct.tm_mday, tstruct.tm_mon, tstruct.tm_year,
            tstruct.tm_yday, tstruct.tm_hour, tstruct.tm_min,
            tstruct.tm_sec,     tstruct.tm_wday, hretime);
        if (slen > sizeof (uuid)) {
                _z_program_error(ERR_GZMOUNT_SNPRINTFUUID_FAILED,
                    UUID_FORMAT, sizeof (uuid));
                return (B_FALSE);
        }

        /* create the global zone mount point */

        slen = snprintf(gzMountPoint, sizeof (gzMountPoint), "%s/.SUNW_%s_%s",
            lzRootPath,
            a_mountPointPrefix ? a_mountPointPrefix : "zones", uuid);
        if (slen > sizeof (gzMountPoint)) {
                _z_program_error(ERR_GZMOUNT_SNPRINTFGMP_FAILED,
                    "%s/.SUNW_%s_%s", lzRootPath,
                    a_mountPointPrefix ? a_mountPointPrefix : "zones",
                    uuid, sizeof (gzMountPoint));
                return (B_FALSE);
        }

        slen = snprintf(lzMountPoint, sizeof (lzMountPoint), "%s",
            gzMountPoint+strlen(lzRootPath));
        if (slen > sizeof (lzMountPoint)) {
                _z_program_error(ERR_GZMOUNT_SNPRINTFLMP_FAILED,
                    "%s", gzMountPoint+strlen(lzRootPath),
                    sizeof (lzMountPoint));
                return (B_FALSE);
        }

        _z_echoDebug(DBG_MNTPT_NAMES, a_gzPath, a_zoneName, gzMountPoint,
            lzMountPoint);

        /* error if the mount point already exists */

        if (_z_is_directory(gzMountPoint) == 0) {
                _z_program_error(ERR_ZONEROOT_NOTDIR, gzMountPoint,
                    a_zoneName, strerror(errno));
                return (B_FALSE);
        }

        /* create the temporary mount point */

        if (mkdir(gzMountPoint, 0600) != 0) {
                _z_program_error(ERR_MNTPT_MKDIR, gzMountPoint, a_zoneName,
                    strerror(errno));
                return (B_FALSE);
        }

        /* mount the global zone path on the non-global zone root file system */

        err = mount(a_gzPath, gzMountPoint, MS_RDONLY|MS_DATA, "lofs",
            (char *)NULL, 0, (char *)NULL, 0);
        if (err != 0) {
                _z_program_error(ERR_GZMOUNT_FAILED, a_gzPath,
                    gzMountPoint, a_zoneName, strerror(errno));
                return (B_FALSE);
        }

        /* success - return both mountpoints to caller */

        *r_lzMountPoint = _z_strdup(gzMountPoint);

        *r_lzRootPath = _z_strdup(lzMountPoint);

        /* return success */

        return (B_TRUE);
}

/*
 * Name:        z_non_global_zones_exist
 * Description: Determine if any non-global native zones exist
 * Arguments:   None.
 * Returns:     boolean_t
 *      == B_TRUE - at least one non-global native zone exists
 *      == B_FALSE - no non-global native zone exists
 */

boolean_t
z_non_global_zones_exist(void)
{
        FILE            *zoneIndexFP;
        boolean_t       anyExist = B_FALSE;
        struct zoneent  *ze;
        zone_spec_t     *zent;

        /* if zones are not implemented, return FALSE */

        if (z_zones_are_implemented() == B_FALSE) {
                return (B_FALSE);
        }

        /* determine if any zones are configured */
        zoneIndexFP = setzoneent();
        if (zoneIndexFP == NULL) {
                return (B_FALSE);
        }

        /* index file open; scan all zones; see if any are at least installed */

        while ((ze = getzoneent_private(zoneIndexFP)) != NULL) {
                /*
                 * If the user specified an explicit zone list, then ignore any
                 * zones that aren't on that list.
                 */
                if ((zent = _z_global_data._zone_spec) != NULL) {
                        while (zent != NULL) {
                                if (strcmp(zent->zl_name, ze->zone_name) == 0)
                                        break;
                                zent = zent->zl_next;
                        }
                        if (zent == NULL) {
                                free(ze);
                                continue;
                        }
                }

                /* skip the global zone */
                if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0) {
                        free(ze);
                        continue;
                }

                /* skip any branded zones */
                if (z_is_zone_branded(ze->zone_name)) {
                        free(ze);
                        continue;
                }

                /* is this zone installed? */
                if (ze->zone_state >= ZONE_STATE_INSTALLED) {
                        free(ze);
                        anyExist = B_TRUE;
                        break;
                }
                free(ze);
        }

        /* close the index file */

        endzoneent(zoneIndexFP);

        /* return results */

        return (anyExist);
}

/*
 * Name:        z_on_zone_spec
 * Description: Determine if named zone is on the zone_spec list.
 * Arguments:   Pointer to name to test.
 * Returns:     B_TRUE if named zone is on the list or if the user specified
 *              no list at all (all zones is the default), B_FALSE otherwise.
 */

boolean_t
z_on_zone_spec(const char *zonename)
{
        zone_spec_t     *zent;

        /* entry assertions */

        assert(zonename != NULL);
        assert(*zonename != '\0');

        /* return true if zones not implemented or no zone spec list defined */

        if (!z_zones_are_implemented() || _z_global_data._zone_spec == NULL) {
                return (B_TRUE);
        }

        /* return true if named zone is on the zone spec list */

        for (zent = _z_global_data._zone_spec;
            zent != NULL; zent = zent->zl_next) {
                if (strcmp(zent->zl_name, zonename) == 0)
                        return (B_TRUE);
        }

        /* named zone is not on the zone spec list */

        return (B_FALSE);
}

/*
 * Name:        z_running_in_global_zone
 * Description: Determine if running in the "global" zone
 * Arguments:   void
 * Returns:     boolean_t
 *                      == B_TRUE - running in global zone
 *                      == B_FALSE - not running in global zone
 */

boolean_t
z_running_in_global_zone(void)
{
        static  boolean_t       _zoneIdDetermined = B_FALSE;
        static  boolean_t       _zoneIsGlobal = B_FALSE;

        /* if ID has not been determined, cache it now */

        if (!_zoneIdDetermined) {
                _zoneIdDetermined = B_TRUE;
                _zoneIsGlobal = _z_running_in_global_zone();
        }

        return (_zoneIsGlobal);
}

/*
 * Name:        z_set_output_functions
 * Description: Link program specific output functions to this library.
 * Arguments:   a_echo_fcn - (_z_printf_fcn_t)
 *                      Function to call to cause "normal operation" messages
 *                      to be output/displayed
 *              a_echo_debug_fcn - (_z_printf_fcn_t)
 *                      Function to call to cause "debugging" messages
 *                      to be output/displayed
 *              a_progerr_fcn - (_z_printf_fcn_t)
 *                      Function to call to cause "program error" messages
 *                      to be output/displayed
 * Returns:     void
 * NOTE:        If NULL is specified for any function, then the functionality
 *              associated with that function is disabled.
 * NOTE:        The function pointers provided must call a function that
 *              takes two arguments:
 *                      function(char *format, char *message)
 *              Any registered function will be called like:
 *                      function("%s", "message")
 */

void
z_set_output_functions(_z_printf_fcn_t a_echo_fcn,
    _z_printf_fcn_t a_echo_debug_fcn,
    _z_printf_fcn_t a_progerr_fcn)
{
        _z_global_data._z_echo = a_echo_fcn;
        _z_global_data._z_echo_debug = a_echo_debug_fcn;
        _z_global_data._z_progerr = a_progerr_fcn;
}

/*
 * Name:        z_set_zone_root
 * Description: Set root for zones library operations
 * Arguments:   Path to root of boot environment containing zone; must be
 *              absolute.
 * Returns:     None.
 * NOTE:        Must be called before performing any zone-related operations.
 *              (Currently called directly by set_inst_root() during -R
 *              argument handling.)
 */

void
z_set_zone_root(const char *zroot)
{
        char *rootdir;

        /* if zones are not implemented, just return */

        if (!z_zones_are_implemented())
                return;

        /* entry assertions */

        assert(zroot != NULL);

        rootdir = _z_strdup((char *)zroot);
        z_canoninplace(rootdir);

        if (strcmp(rootdir, "/") == 0) {
                rootdir[0] = '\0';
        }

        /* free any existing cached root path */
        if (*_z_global_data._z_root_dir != '\0') {
                free(_z_global_data._z_root_dir);
                _z_global_data._z_root_dir = NULL;
        }

        /* store duplicate of new zone root path */

        if (*rootdir != '\0') {
                _z_global_data._z_root_dir = _z_strdup(rootdir);
        } else {
                _z_global_data._z_root_dir = "";
        }

        /* set zone root path */

        zonecfg_set_root(rootdir);

        free(rootdir);
}

/*
 * Name:        z_set_zone_spec
 * Description: Set list of zones on which actions will be performed.
 * Arguments:   Whitespace-separated list of zone names.
 * Returns:     0 on success, -1 on error.
 * NOTES:       Will call _z_program_error if argument can't be parsed or
 *              memory not available.
 */

int
z_set_zone_spec(const char *zlist)
{
        const char      *zend;
        ptrdiff_t       zlen;
        zone_spec_t     *zent;
        zone_spec_t     *zhead;
        zone_spec_t     **znextp = &zhead;

        /* entry assertions */

        assert(zlist != NULL);

        /* parse list to zone_spec_t list, store in global data */

        for (;;) {
                while (isspace(*zlist)) {
                        zlist++;
                }
                if (*zlist == '\0') {
                        break;
                }
                for (zend = zlist; *zend != '\0'; zend++) {
                        if (isspace(*zend)) {
                                break;
                        }
                }
                zlen = ((ptrdiff_t)zend) - ((ptrdiff_t)zlist);
                if (zlen >= ZONENAME_MAX) {
                        _z_program_error(ERR_ZONE_NAME_ILLEGAL, zlen, zlist);
                        return (-1);
                }
                zent = _z_malloc(sizeof (*zent));
                (void) memcpy(zent->zl_name, zlist, zlen);
                zent->zl_name[zlen] = '\0';
                zent->zl_used = B_FALSE;
                *znextp = zent;
                znextp = &zent->zl_next;
                zlist = zend;
        }
        *znextp = NULL;

        if (zhead == NULL) {
                _z_program_error(ERR_ZONE_LIST_EMPTY);
                return (-1);
        }

        _z_global_data._zone_spec = zhead;
        return (0);
}

/*
 * Name:        z_umount_lz_mount
 * Description: Unmount directory mounted with z_mount_in_lz
 * Arguments:   a_lzMountPointer - pointer to string returned by z_mount_in_lz
 * Returns:     boolean_t
 *                      == B_TRUE - successfully unmounted directory
 *                      == B_FALSE - failed to unmount directory
 */

boolean_t
z_umount_lz_mount(char *a_lzMountPoint)
{
        int     err;

        /* entry assertions */

        assert(a_lzMountPoint != (char *)NULL);
        assert(*a_lzMountPoint != '\0');

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_UNMOUNT_FROM_LZ_ENTRY, a_lzMountPoint);

        /* if zones are not implemented, return TRUE */

        if (z_zones_are_implemented() == B_FALSE) {
                return (B_FALSE);
        }

        /* error if global zone path is not absolute */

        if (*a_lzMountPoint != '/') {
                _z_program_error(ERR_LZMNTPT_NOT_ABSOLUTE, a_lzMountPoint);
                return (B_FALSE);
        }

        /* verify mount point exists */

        if (_z_is_directory(a_lzMountPoint) != 0) {
                _z_program_error(ERR_LZMNTPT_NOTDIR, a_lzMountPoint,
                    strerror(errno));
                return (B_FALSE);
        }

        /* unmount */

        err = umount2(a_lzMountPoint, 0);
        if (err != 0) {
                _z_program_error(ERR_GZUMOUNT_FAILED, a_lzMountPoint,
                    strerror(errno));
                return (B_FALSE);
        }

        /* remove the mount point */

        (void) remove(a_lzMountPoint);

        /* return success */

        return (B_TRUE);
}

/*
 * Name:        z_unlock_this_zone
 * Description: unlock this zone
 * Arguments:   a_lflags - [RO, *RO] - (ZLOCKS_T)
 *                      Flags indicating which locks to release
 * Returns:     boolean_t
 *                      == B_TRUE - success specified locks released
 *                      == B_FALSE - failure specified locks may not be released
 * NOTE: the lock objects for "this zone" are maintained internally.
 */

boolean_t
z_unlock_this_zone(ZLOCKS_T a_lflags)
{
        boolean_t       b;
        boolean_t       errors = B_FALSE;
        char            *zoneName;

        /* entry assertions */

        assert(a_lflags != ZLOCKS_NONE);

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_ULK_THIS, a_lflags);

        /* return if no objects locked */

        if ((_z_global_data._z_ObjectLocks == (char *)NULL) ||
            (*_z_global_data._z_ObjectLocks == '\0')) {
                return (B_TRUE);
        }

        zoneName = z_get_zonename();

        /* unlock package administration */

        if (a_lflags & ZLOCKS_PKG_ADMIN) {
                b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks,
                    zoneName, LOBJ_PKGADMIN, ERR_ZONES_ULK_THIS_PACKAGE);
                if (!b) {
                        errors = B_TRUE;
                }
        }

        /* unlock zone administration */

        if (a_lflags & ZLOCKS_ZONE_ADMIN) {
                b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks,
                    zoneName, LOBJ_ZONEADMIN, ERR_ZONES_ULK_THIS_ZONES);
                if (!b) {
                        errors = B_TRUE;
                }
        }

        (void) free(zoneName);
        return (!errors);
}

/*
 * Name:        z_unlock_zones
 * Description: unlock specified zones
 * Arguments:   a_zlst - zoneList_t object describing zones to unlock
 *              a_lflags - [RO, *RO] - (ZLOCKS_T)
 *                      Flags indicating which locks to release
 * Returns:     boolean_t
 *                      == B_TRUE - success, zones unlocked
 *                      == B_FALSE - failure, zones not unlocked
 */

boolean_t
z_unlock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags)
{
        boolean_t       b;
        boolean_t       errors = B_FALSE;
        int             i;

        /* entry assertions */

        assert(a_lflags != ZLOCKS_NONE);

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_ULK_ZONES, a_lflags);

        /* if zones are not implemented, return TRUE */

        if (z_zones_are_implemented() == B_FALSE) {
                _z_echoDebug(DBG_ZONES_ULK_ZONES_UNIMP);
                return (B_TRUE);
        }

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                _z_echoDebug(DBG_ZONES_ULK_ZONES_NOZONES);
                /* unlock this zone before returning */
                return (z_unlock_this_zone(a_lflags));
        }

        /* zones exist */

        _z_echoDebug(DBG_ZONES_ULK_ZONES_EXIST);

        /*
         * unlock each listed zone that is currently running
         */

        for (i = 0; (a_zlst[i]._zlName != (char *)NULL); i++) {
                /* ignore zone if not locked */
                if (!(a_zlst[i]._zlStatus & ZST_LOCKED)) {
                        continue;
                }

                /* ignore zone if not running */
                if (a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_RUNNING &&
                    a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_MOUNTED) {
                        continue;
                }

                /* unlock this zone */
                b = _z_unlock_zone(&a_zlst[i], a_lflags);

                if (b != B_TRUE) {
                        errors = B_TRUE;
                } else {
                        /* mark zone as unlocked */
                        a_zlst[i]._zlStatus &= ~ZST_LOCKED;
                }
        }

        /* unlock this zone */

        if (z_unlock_this_zone(a_lflags) != B_TRUE) {
                errors = B_TRUE;
        }

        return (errors);
}

/*
 * Name:        z_verify_zone_spec
 * Description: Verify list of zones on which actions will be performed.
 * Arguments:   None.
 * Returns:     0 on success, -1 on error.
 * NOTES:       Will call _z_program_error if there are zones on the specified
 *              list that don't exist on the system. Requires that
 *              z_set_zone_root is called first (if it is called at all).
 */

int
z_verify_zone_spec(void)
{
        FILE            *zoneIndexFP;
        boolean_t       errors;
        char            zoneIndexPath[MAXPATHLEN];
        struct zoneent  *ze;
        zone_spec_t     *zent;

        if (!z_zones_are_implemented()) {
                _z_program_error(ERR_ZONES_NOT_IMPLEMENTED);
                return (-1);
        }

        zoneIndexFP = setzoneent();
        if (zoneIndexFP == NULL) {
                _z_program_error(ERR_ZONEINDEX_OPEN, zoneIndexPath,
                    strerror(errno));
                return (-1);
        }

        while ((ze = getzoneent_private(zoneIndexFP)) != NULL) {
                for (zent = _z_global_data._zone_spec;
                    zent != NULL; zent = zent->zl_next) {
                        if (strcmp(zent->zl_name, ze->zone_name) == 0) {
                                zent->zl_used = B_TRUE;
                                break;
                        }
                }
                free(ze);
        }
        endzoneent(zoneIndexFP);

        errors = B_FALSE;
        for (zent = _z_global_data._zone_spec;
            zent != NULL; zent = zent->zl_next) {
                if (!zent->zl_used) {
                        _z_program_error(ERR_ZONE_NONEXISTENT, zent->zl_name);
                        errors = B_TRUE;
                }
        }
        return (errors ? -1 : 0);
}

/*
 * Name:        z_zlist_change_zone_state
 * Description: Change the current state of the specified zone
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to return the
 *              a_newState - the state to put the specified zone in
 * Returns:     boolean_t
 *                      == B_TRUE - the zone is in the new state
 *                      == B_FALSE - unable to transition the zone to the
 *                              specified state
 * NOTE:        This changes the "current kernel" state of the specified
 *              zone. For example, to boot the zone, change the state
 *              to "ZONE_STATE_RUNNING". To halt the zone, change the
 *              state to "ZONE_STATE_INSTALLED".
 */

boolean_t
z_zlist_change_zone_state(zoneList_t a_zlst, int a_zoneIndex,
        zone_state_t a_newState)
{
        int     i;

        /* entry debugging info */

        _z_echoDebug(DBG_ZONES_CHG_Z_STATE_ENTRY, a_zoneIndex, a_newState);

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return (B_FALSE);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (B_FALSE);
        }

        /* return success if the zone is already in this state */

        if (a_zlst[i]._zlCurrKernelStatus == a_newState) {
                return (B_TRUE);
        }

        /* take action on new state to set zone to */

        _z_echoDebug(DBG_ZONES_CHG_Z_STATE, a_zlst[i]._zlName,
            a_zlst[i]._zlCurrKernelStatus, a_newState);

        switch (a_newState) {
        case ZONE_STATE_RUNNING:
        case ZONE_STATE_MOUNTED:
                /* these states mean "boot the zone" */
                return (_z_make_zone_running(&a_zlst[i]));

        case ZONE_STATE_DOWN:
        case ZONE_STATE_INSTALLED:
                /* these states mean "halt the zone" */
                return (_z_make_zone_down(&a_zlst[i]));

        case ZONE_STATE_READY:
                return (_z_make_zone_ready(&a_zlst[i]));

        case ZONE_STATE_CONFIGURED:
        case ZONE_STATE_INCOMPLETE:
        case ZONE_STATE_SHUTTING_DOWN:
        default:
                /* do not know how to change zone to this state */
                return (B_FALSE);
        }
}

/*
 * Name:        z_is_zone_branded
 * Description: Determine whether zone has a non-native brand
 * Arguments:   a_zoneName - name of the zone to check for branding
 * Returns:     boolean_t
 *                      == B_TRUE - zone has a non-native brand
 *                      == B_FALSE - zone is native
 */
boolean_t
z_is_zone_branded(char *zoneName)
{
        char                    brandname[MAXNAMELEN];
        int                     err;

        /* if zones are not implemented, return FALSE */
        if (!z_zones_are_implemented()) {
                return (B_FALSE);
        }

        /* if brands are not implemented, return FALSE */
        if (!z_brands_are_implemented()) {
                return (B_FALSE);
        }

        err = zone_get_brand(zoneName, brandname, sizeof (brandname));
        if (err != Z_OK) {
                _z_program_error(ERR_BRAND_GETBRAND, zonecfg_strerror(err));
                return (B_FALSE);
        }

        /*
         * Both "native" and "cluster" are native brands
         * that use the standard facilities in the areas
         * of packaging/installation/update.
         */
        if (streq(brandname, NATIVE_BRAND_NAME) ||
            streq(brandname, CLUSTER_BRAND_NAME)) {
                return (B_FALSE);
        } else {
                return (B_TRUE);
        }
}

/*
 * Name:        z_is_zone_brand_in_list
 * Description: Determine whether zone's brand has a match in the list
 *              brands passed in.
 * Arguments:   zoneName - name of the zone to check for branding
 *              list - list of brands to check the zone against
 * Returns:     boolean_t
 *                      == B_TRUE - zone has a matching brand
 *                      == B_FALSE - zone brand is not in list
 */
boolean_t
z_is_zone_brand_in_list(char *zoneName, zoneBrandList_t *list)
{
        char                    brandname[MAXNAMELEN];
        int                     err;
        zoneBrandList_t         *sp;

        if (zoneName == NULL || list == NULL)
                return (B_FALSE);

        /* if zones are not implemented, return FALSE */
        if (!z_zones_are_implemented()) {
                return (B_FALSE);
        }

        /* if brands are not implemented, return FALSE */
        if (!z_brands_are_implemented()) {
                return (B_FALSE);
        }

        err = zone_get_brand(zoneName, brandname, sizeof (brandname));
        if (err != Z_OK) {
                _z_program_error(ERR_BRAND_GETBRAND, zonecfg_strerror(err));
                return (B_FALSE);
        }

        for (sp = list; sp != NULL; sp = sp->next) {
                if (sp->string_ptr != NULL &&
                    strcmp(sp->string_ptr, brandname) == 0) {
                        return (B_TRUE);
                }
        }

        return (B_FALSE);
}

/*
 * Name:        z_zlist_get_current_state
 * Description: Determine the current kernel state of the specified zone
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to return
 * Returns:     zone_state_t
 *                      The current state of the specified zone is returned
 */

zone_state_t
z_zlist_get_current_state(zoneList_t a_zlst, int a_zoneIndex)
{
        int     i;

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return (ZONE_STATE_INCOMPLETE);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (ZONE_STATE_INCOMPLETE);
        }

        /* return selected zone's current kernel state */

        _z_echoDebug(DBG_ZONES_GET_ZONE_STATE,
            a_zlst[i]._zlName ? a_zlst[i]._zlName : "",
            a_zlst[i]._zlCurrKernelStatus);

        return (a_zlst[i]._zlCurrKernelStatus);
}

/*
 * Name:        z_zlist_get_original_state
 * Description: Return the original kernal state of the specified zone
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to return the
 * Returns:     zone_state_t
 *                      The original state of the specified zone is returned.
 *                      This is the state of the zone when the zoneList_t
 *                      object was first generated.
 */

zone_state_t
z_zlist_get_original_state(zoneList_t a_zlst, int a_zoneIndex)
{
        int     i;

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return (ZONE_STATE_INCOMPLETE);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (ZONE_STATE_INCOMPLETE);
        }

        /* return selected zone's original kernel state */

        return (a_zlst[i]._zlOrigKernelStatus);
}

/*
 * Name:        z_zlist_get_scratch
 * Description: Determine name of scratch zone
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to use
 * Return:      char *
 *                      == NULL - zone name could not be determined
 *                      != NULL - pointer to string representing scratch zone
 * NOTE:        Any name returned is placed in static storage that must
 *              NEVER be free()ed by the caller.
 */

char *
z_zlist_get_scratch(zoneList_t a_zlst, int a_zoneIndex)
{
        int     i;

        /* ignore empty list */

        if (a_zlst == NULL)
                return (NULL);

        /* find the specified zone in the list */

        for (i = 0; i != a_zoneIndex; i++) {
                if (a_zlst[i]._zlName == NULL)
                        return (NULL);
        }

        /* return selected zone's scratch name */

        return (a_zlst[i]._zlScratchName == NULL ? a_zlst[i]._zlName :
            a_zlst[i]._zlScratchName);
}

/*
 * Name:        z_zlist_get_zonename
 * Description: Determine name of specified zone
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to return the
 * Return:      char *
 *                      == NULL - zone name could not be determined
 *                      != NULL - pointer to string representing zone name
 * NOTE:        Any zoneList_t returned is placed in static storage that must
 *              NEVER be free()ed by the caller.
 */

char *
z_zlist_get_zonename(zoneList_t a_zlst, int a_zoneIndex)
{
        int     i;

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return ((char *)NULL);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (NULL);
        }

        /* return selected zone's name */

        return (a_zlst[i]._zlName);
}

/*
 * Name:        z_zlist_get_zonepath
 * Description: Determine zonepath of specified zone
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to return
 * Return:      char *
 *                      == NULL - zonepath could not be determined
 *                      != NULL - pointer to string representing zonepath
 * NOTE:        Any zoneList_t returned is placed in static storage that must
 *              NEVER be free()ed by the caller.
 */

char *
z_zlist_get_zonepath(zoneList_t a_zlst, int a_zoneIndex)
{
        int     i;

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return ((char *)NULL);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (NULL);
        }

        /* return selected zone's zonepath */

        return (a_zlst[i]._zlPath);
}

boolean_t
z_zlist_is_zone_runnable(zoneList_t a_zlst, int a_zoneIndex)
{
        int     i;

        /* if zones are not implemented, return error */

        if (z_zones_are_implemented() == B_FALSE) {
                return (B_FALSE);
        }

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return (B_FALSE);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (B_FALSE);
        }

        /* choose based on current state */

        switch (a_zlst[i]._zlCurrKernelStatus) {
        case ZONE_STATE_RUNNING:
        case ZONE_STATE_MOUNTED:
                /* already running */
                return (B_TRUE);

        case ZONE_STATE_INSTALLED:
        case ZONE_STATE_DOWN:
        case ZONE_STATE_READY:
        case ZONE_STATE_SHUTTING_DOWN:
                /* return false if the zone cannot be booted */

                if (a_zlst[i]._zlStatus & ZST_NOT_BOOTABLE) {
                        return (B_FALSE);
                }

                return (B_TRUE);

        case ZONE_STATE_CONFIGURED:
        case ZONE_STATE_INCOMPLETE:
        default:
                /* cannot transition (boot) these states */
                return (B_FALSE);
        }
}

/*
 * Name:        z_zlist_restore_zone_state
 * Description: Return the zone to the state it was originally in
 * Arguments:   a_zlst - handle to zoneList_t object describing all zones
 *              a_zoneIndex - index into a_zlst of the zone to return the
 * Returns:     boolean_t
 *                      == B_TRUE - the zone's state has been restored
 *                      == B_FALSE - unable to transition the zone to its
 *                              original state
 */

boolean_t
z_zlist_restore_zone_state(zoneList_t a_zlst, int a_zoneIndex)
{
        int             i;

        /* ignore empty list */

        if (a_zlst == (zoneList_t)NULL) {
                return (B_FALSE);
        }

        /* find the specified zone in the list */

        for (i = 0; (i != a_zoneIndex) &&
            (a_zlst[i]._zlName != (char *)NULL); i++)
                ;

        /* return error if the specified zone does not exist */

        if (a_zlst[i]._zlName == (char *)NULL) {
                return (B_FALSE);
        }

        /* transition the zone back to its original state */

        return (z_zlist_change_zone_state(a_zlst,
            a_zoneIndex, a_zlst[i]._zlOrigKernelStatus));
}

/*
 * Name:        z_zone_exec
 * Description: Execute a Unix command in a specified zone and return results
 * Arguments:   a_zoneName - pointer to string representing the name of the zone
 *                      to execute the specified command in
 *              a_path - pointer to string representing the full path *in the
 *                      non-global zone named by a_zoneName* of the Unix command
 *                      to be executed
 *              a_argv[] - Pointer to array of character strings representing
 *                      the arguments to be passed to the Unix command. The list
 *                      must be termianted with an element that is (char *)NULL
 *              NOTE: a_argv[0] is the "command name" passed to the command
 *              a_stdoutPath - Pointer to string representing the path to a file
 *                      into which all output to "stdout" from the Unix command
 *                      is placed.
 *                      == (char *)NULL - leave stdout open and pass through
 *                      == "/dev/null" - discard stdout output
 *              a_strerrPath - Pointer to string representing the path to a file
 *                      into which all output to "stderr" from the Unix command
 *                      is placed.
 *                      == (char *)NULL - leave stderr open and pass through
 *                      == "/dev/null" - discard stderr output
 *              a_fds - Pointer to array of integers representing file
 *                      descriptors to remain open during the call - all
 *                      file descriptors above STDERR_FILENO not in this
 *                      list will be closed.
 * Returns:     int
 *                      The return (exit) code from the specified Unix command
 *                      Special return codes:
 *                      -1 : failure to exec process
 *                      -2 : could not create contract for greenline
 *                      -3 : fork() failed
 *                      -4 : could not open stdout capture file
 *                      -5 : error from 'waitpid' other than EINTR
 *                      -6 : zones are not supported
 * NOTE:        All file descriptores other than 0, 1 and 2 are closed except
 *              for those file descriptors listed in the a_fds array.
 */

int
z_zone_exec(const char *a_zoneName, const char *a_path, char *a_argv[],
        char *a_stdoutPath, char *a_stderrPath, int *a_fds)
{
        int                     final_status;
        int                     lerrno;
        int                     status;
        int                     tmpl_fd;
        pid_t                   child_pid;
        pid_t                   result_pid;
        struct sigaction        nact;
        struct sigaction        oact;
        void                    (*funcSighup)();
        void                    (*funcSigint)();

        /* if zones are not implemented, return TRUE */

        if (z_zones_are_implemented() == B_FALSE) {
                return (-6);    /* -6 : zones are not supported */
        }

        if ((tmpl_fd = _zexec_init_template()) == -1) {
                _z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno));
                return (-2);    /* -2 : could not create greenline contract */
        }

        /*
         * hold SIGINT/SIGHUP signals and reset signal received counter;
         * after the fork1() the parent and child need to setup their respective
         * interrupt handling and release the hold on the signals
         */

        (void) sighold(SIGINT);
        (void) sighold(SIGHUP);

        _z_global_data._z_SigReceived = 0;      /* no signals received */

        /*
         * fork off a new process to execute command in;
         * fork1() is used instead of vfork() so the child process can
         * perform operations that would modify the parent process if
         * vfork() were used
         */

        child_pid = fork1();

        if (child_pid < 0) {
                /*
                 * *************************************************************
                 * fork failed!
                 * *************************************************************
                 */

                (void) ct_tmpl_clear(tmpl_fd);
                (void) close(tmpl_fd);
                _z_program_error(ERR_FORK, strerror(errno));

                /* release hold on signals */

                (void) sigrelse(SIGHUP);
                (void) sigrelse(SIGINT);

                return (-3);    /* -3 : fork() failed */
        }

        if (child_pid == 0) {
                int     i;

                /*
                 * *************************************************************
                 * This is the forked (child) process
                 * *************************************************************
                 */

                (void) ct_tmpl_clear(tmpl_fd);
                (void) close(tmpl_fd);

                /* reset any signals to default */

                for (i = 0; i < NSIG; i++) {
                        (void) sigset(i, SIG_DFL);
                }

                /*
                 * close all file descriptors not in the a_fds list
                 */

                (void) fdwalk(&_z_close_file_descriptors, (void *)a_fds);

                /*
                 * if a file for stdout is present, open the file and use the
                 * file to capture stdout from the _zexec process
                 */

                if (a_stdoutPath != (char *)NULL) {
                        int     stdoutfd;

                        stdoutfd = open(a_stdoutPath,
                            O_WRONLY|O_CREAT|O_TRUNC, 0600);
                        if (stdoutfd < 0) {
                                _z_program_error(ERR_CAPTURE_FILE, a_stdoutPath,
                                    strerror(errno));
                                return (-4);
                        }

                        (void) dup2(stdoutfd, STDOUT_FILENO);
                        (void) close(stdoutfd);
                }

                /*
                 * if a file for stderr is present, open the file and use the
                 * file to capture stderr from the _zexec process
                 */

                if (a_stderrPath != (char *)NULL) {
                        int     stderrfd;

                        stderrfd = open(a_stderrPath,
                            O_WRONLY|O_CREAT|O_TRUNC, 0600);
                        if (stderrfd < 0) {
                                _z_program_error(ERR_CAPTURE_FILE, a_stderrPath,
                                    strerror(errno));
                                return (-4);
                        }

                        (void) dup2(stderrfd, STDERR_FILENO);
                        (void) close(stderrfd);
                }

                /* release all held signals */

                (void) sigrelse(SIGHUP);
                (void) sigrelse(SIGINT);

                /* execute command in the specified non-global zone */

                _exit(_zexec(a_zoneName, a_path, a_argv));
        }

        /*
         * *********************************************************************
         * This is the forking (parent) process
         * *********************************************************************
         */

        /* register child process i.d. so signal handlers can pass signal on */

        _z_global_data._z_ChildProcessId = child_pid;

        /*
         * setup signal handlers for SIGINT and SIGHUP and release hold
         */

        /* hook SIGINT to _z_sig_trap() */

        nact.sa_handler = _z_sig_trap;
        nact.sa_flags = SA_RESTART;
        (void) sigemptyset(&nact.sa_mask);

        if (sigaction(SIGINT, &nact, &oact) < 0) {
                funcSigint = SIG_DFL;
        } else {
                funcSigint = oact.sa_handler;
        }

        /* hook SIGHUP to _z_sig_trap() */

        nact.sa_handler = _z_sig_trap;
        nact.sa_flags = SA_RESTART;
        (void) sigemptyset(&nact.sa_mask);

        if (sigaction(SIGHUP, &nact, &oact) < 0) {
                funcSighup = SIG_DFL;
        } else {
                funcSighup = oact.sa_handler;
        }

        /* release hold on signals */

        (void) sigrelse(SIGHUP);
        (void) sigrelse(SIGINT);

        (void) ct_tmpl_clear(tmpl_fd);
        (void) close(tmpl_fd);

        /*
         * wait for the process to exit, reap child exit status
         */

        for (;;) {
                result_pid = waitpid(child_pid, &status, 0L);
                lerrno = (result_pid == -1 ? errno : 0);

                /* break loop if child process status reaped */

                if (result_pid != -1) {
                        break;
                }

                /* break loop if not interrupted out of waitpid */

                if (errno != EINTR) {
                        break;
                }
        }

        /* reset child process i.d. so signal handlers do not pass signals on */

        _z_global_data._z_ChildProcessId = -1;

        /*
         * If the child process terminated due to a call to exit(), then
         * set results equal to the 8-bit exit status of the child process;
         * otherwise, set the exit status to "-1" indicating that the child
         * exited via a signal.
         */

        if (WIFEXITED(status)) {
                final_status = WEXITSTATUS(status);
                if ((_z_global_data._z_SigReceived != 0) &&
                    (final_status == 0)) {
                        final_status = 1;
                }
        } else {
                final_status = -1;      /* -1 : failure to exec process */
        }

        /* determine proper exit code */

        if (result_pid == -1) {
                final_status = -5;      /* -5 : error from waitpid not EINTR */
        } else if (_z_global_data._z_SigReceived != 0) {
                final_status = -7;      /* -7 : interrupt received */
        }

        /*
         * reset signal handlers
         */

        /* reset SIGINT */

        nact.sa_handler = funcSigint;
        nact.sa_flags = SA_RESTART;
        (void) sigemptyset(&nact.sa_mask);

        (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);

        /* reset SIGHUP */

        nact.sa_handler = funcSighup;
        nact.sa_flags = SA_RESTART;
        (void) sigemptyset(&nact.sa_mask);

        (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);

        /*
         * if signal received during command execution, interrupt
         * this process now.
         */

        if (_z_global_data._z_SigReceived != 0) {
                (void) kill(getpid(), SIGINT);
        }

        /* set errno and return */

        errno = lerrno;

        return (final_status);
}

/*
 * Name:        z_zones_are_implemented
 * Description: Determine if any zone operations can be performed
 * Arguments:   void
 * Returns:     boolean_t
 *                      == B_TRUE - zone operations are available
 *                      == B_FALSE - no zone operations can be done
 */

boolean_t
z_zones_are_implemented(void)
{
        static  boolean_t       _zonesImplementedDetermined = B_FALSE;
        static  boolean_t       _zonesAreImplemented = B_FALSE;

        /* if availability has not been determined, cache it now */

        if (!_zonesImplementedDetermined) {
                _zonesImplementedDetermined = B_TRUE;
                _zonesAreImplemented = _z_zones_are_implemented();
                if (!_zonesAreImplemented) {
                        _z_echoDebug(DBG_ZONES_NOT_IMPLEMENTED);
                } else {
                        _z_echoDebug(DBG_ZONES_ARE_IMPLEMENTED);
                }
        }

        return (_zonesAreImplemented);
}