root/usr/src/lib/libinstzones/common/zones_states.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * Module:      zones_states.c
 * Group:       libinstzones
 * Description: Provide "zones" state interfaces for install consolidation code
 *
 * Public Methods:
 *
 *  z_make_zone_running - change state of non-global zone to "running"
 * _z_make_zone_ready - change state of non-global zone to "ready"
 * _z_make_zone_down - change state of non-global zone to "down"
 */

/*
 * 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 <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <stropts.h>
#include <libintl.h>
#include <locale.h>
#include <assert.h>

/*
 * local includes
 */

#include "instzones_lib.h"
#include "zones_strings.h"

/*
 * Private structures
 */

/*
 * Library Function Prototypes
 */

/*
 * Local Function Prototypes
 */

/*
 * global internal (private) declarations
 */

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

/*
 * Name:        _z_make_zone_running
 * Description: Given a zone element entry for the non-global zone to affect,
 *              change the state of that non-global zone to "running"
 * Arguments:   a_zlem - [RO, *RW] - (zoneListElement_t)
 *                      Zone list element describing the non-global zone to
 *                      make running
 * Returns:     boolean_t
 *                      B_TRUE - non-global zone state changed successfully
 *                      B_FALSE - failed to make the non-global zone run
 */

boolean_t
_z_make_zone_running(zoneListElement_t *a_zlem)
{
        FILE            *fp;
        argArray_t      *args;
        char             zonename[ZONENAME_MAX];
        char            *results = (char *)NULL;
        int             ret;
        int             status = 0;

        /* entry assertions */

        assert(a_zlem != NULL);

        /* act based on the zone's current kernel state */

        switch (a_zlem->_zlCurrKernelStatus) {
        case ZONE_STATE_RUNNING:
        case ZONE_STATE_MOUNTED:
                /* already running */
                return (B_TRUE);

        case ZONE_STATE_READY:
                /* This should never happen */
                if (zonecfg_in_alt_root())
                        return (B_FALSE);

                /*
                 * We're going to upset the zone anyway, so might as well just
                 * halt it now and fall through to normal mounting.
                 */

                _z_echoDebug(DBG_TO_ZONEHALT, a_zlem->_zlName);

                args = _z_new_args(5);          /* generate new arg list */
                (void) _z_add_arg(args, ZONEADM_CMD);
                (void) _z_add_arg(args, "-z");
                (void) _z_add_arg(args, a_zlem->_zlName);
                (void) _z_add_arg(args, "halt");

                ret = z_ExecCmdArray(&status, &results, (char *)NULL,
                    ZONEADM_CMD, _z_get_argv(args));

                /* free generated argument list */

                _z_free_args(args);

                if (ret != 0) {
                        _z_program_error(ERR_ZONEHALT_EXEC, ZONEADM_CMD,
                            strerror(errno));
                        free(results);
                        return (B_FALSE);
                }
                if (status != 0) {
                        if (status == -1) {
                                _z_program_error(ERR_ZONEBOOT_CMD_SIGNAL,
                                    ZONEADM_CMD, a_zlem->_zlName);
                        } else {
                                _z_program_error(ERR_ZONEBOOT_CMD_ERROR,
                                    ZONEADM_CMD, a_zlem->_zlName, status,
                                    results == NULL ? "" : "\n",
                                    results == NULL ? "" : results);
                        }
                        free(results);
                        return (B_FALSE);
                }

                free(results);

                a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED;
                /* FALLTHROUGH */

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

                if (a_zlem->_zlStatus & ZST_NOT_BOOTABLE) {
                        return (B_FALSE);
                }

                _z_echoDebug(DBG_TO_ZONERUNNING, a_zlem->_zlName);

                /* these states can be booted - do so */

                args = _z_new_args(10);         /* generate new arg list */
                (void) _z_add_arg(args, ZONEADM_CMD);
                if (zonecfg_in_alt_root()) {
                        (void) _z_add_arg(args, "-R");
                        (void) _z_add_arg(args, "%s",
                            (char *)zonecfg_get_root());
                }

                (void) _z_add_arg(args, "-z");
                (void) _z_add_arg(args, "%s", a_zlem->_zlName);
                (void) _z_add_arg(args, "mount");

                ret = z_ExecCmdArray(&status, &results, (char *)NULL,
                    ZONEADM_CMD, _z_get_argv(args));

                /* free generated argument list */

                _z_free_args(args);

                if (ret != 0) {
                        _z_program_error(ERR_ZONEBOOT_EXEC, ZONEADM_CMD,
                            strerror(errno));
                        free(results);
                        return (B_FALSE);
                }

                if (status != 0) {
                        if (status == -1) {
                                _z_program_error(ERR_ZONEBOOT_CMD_SIGNAL,
                                    ZONEADM_CMD, a_zlem->_zlName);
                        } else {
                                _z_program_error(ERR_ZONEBOOT_CMD_ERROR,
                                    ZONEADM_CMD, a_zlem->_zlName, status,
                                    results == NULL ? "" : "\n",
                                    results == NULL ? "" : results);
                        }
                        free(results);

                        /* remember this zone cannot be booted */

                        a_zlem->_zlStatus |= ZST_NOT_BOOTABLE;

                        return (B_FALSE);
                }
                free(results);

                if (zonecfg_in_alt_root()) {
                        if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL ||
                            zonecfg_find_scratch(fp, a_zlem->_zlName,
                            zonecfg_get_root(), zonename,
                            sizeof (zonename)) == -1) {
                                _z_program_error(ERR_ZONEBOOT_DIDNT_BOOT,
                                    a_zlem->_zlName);
                                if (fp != NULL)
                                        zonecfg_close_scratch(fp);
                                return (B_FALSE);
                        }
                        zonecfg_close_scratch(fp);
                        free(a_zlem->_zlScratchName);
                        a_zlem->_zlScratchName = _z_strdup(zonename);
                }
                a_zlem->_zlCurrKernelStatus = ZONE_STATE_MOUNTED;
                return (B_TRUE);

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

/*
 * Name:        _z_make_zone_ready
 * Description: Given a zone element entry for the non-global zone to affect,
 *              restore the ready state of the zone when the zone is currently
 *              in the running state.
 * Arguments:   a_zlem - [RO, *RW] - (zoneListElement_t)
 *                      Zone list element describing the non-global zone to
 *                      make ready
 * Returns:     boolean_t
 *                      B_TRUE - non-global zone state changed successfully
 *                      B_FALSE - failed to make the non-global zone ready
 */

boolean_t
_z_make_zone_ready(zoneListElement_t *a_zlem)
{
        argArray_t      *args;
        char            *results = (char *)NULL;
        int             status = 0;
        int             i;
        int             ret;
        zone_state_t    st;

        /* entry assertions */

        assert(a_zlem != (zoneListElement_t *)NULL);

        /* act based on the zone's current kernel state */

        switch (a_zlem->_zlCurrKernelStatus) {
        case ZONE_STATE_DOWN:
        case ZONE_STATE_READY:
                /* already down */
                return (B_TRUE);

        case ZONE_STATE_MOUNTED:
                _z_echoDebug(DBG_TO_ZONEUNMOUNT, a_zlem->_zlName);

                args = _z_new_args(10);         /* generate new arg list */
                (void) _z_add_arg(args, ZONEADM_CMD);
                (void) _z_add_arg(args, "-z");
                (void) _z_add_arg(args, "%s", a_zlem->_zlName);
                (void) _z_add_arg(args, "unmount");
                ret = z_ExecCmdArray(&status, &results, NULL,
                    ZONEADM_CMD, _z_get_argv(args));
                if (ret != 0) {
                        _z_program_error(ERR_ZONEUNMOUNT_EXEC,
                            ZONEADM_CMD, strerror(errno));
                        free(results);
                        _z_free_args(args);
                        return (B_FALSE);
                }
                if (status != 0) {
                        if (status == -1) {
                                _z_program_error(ERR_ZONEUNMOUNT_CMD_SIGNAL,
                                    ZONEADM_CMD, a_zlem->_zlName);
                        } else {
                                _z_program_error(ERR_ZONEUNMOUNT_CMD_ERROR,
                                    ZONEADM_CMD, a_zlem->_zlName, status,
                                    results == NULL ? "" : "\n",
                                    results == NULL ? "" : results);
                        }
                        if (results != NULL) {
                                free(results);
                        }
                        _z_free_args(args);
                        return (B_FALSE);
                }
                if (results != NULL) {
                        free(results);
                }
                _z_free_args(args);
                a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED;
                _z_echoDebug(DBG_TO_ZONEREADY, a_zlem->_zlName);

                args = _z_new_args(10);         /* generate new arg list */
                (void) _z_add_arg(args, ZONEADM_CMD);
                (void) _z_add_arg(args, "-z");
                (void) _z_add_arg(args, "%s", a_zlem->_zlName);
                (void) _z_add_arg(args, "ready");

                ret = z_ExecCmdArray(&status, &results, NULL,
                    ZONEADM_CMD, _z_get_argv(args));
                if (ret != 0) {
                        _z_program_error(ERR_ZONEREADY_EXEC, ZONEADM_CMD,
                            strerror(errno));
                        free(results);
                        _z_free_args(args);
                        return (B_FALSE);
                }
                if (status != 0) {
                        _z_program_error(ERR_ZONEREADY_CMDFAIL, ZONEADM_CMD,
                            a_zlem->_zlName, strerror(errno),
                            results == NULL ? "" : "\n",
                            results == NULL ? "" : results);
                        if (results != NULL) {
                                free(results);
                        }
                        _z_free_args(args);
                        return (B_FALSE);
                }
                if (results != NULL) {
                        free(results);
                }
                /* success - zone is now in the ready state */
                a_zlem->_zlCurrKernelStatus = ZONE_STATE_READY;
                return (B_TRUE);

        case ZONE_STATE_RUNNING:

                _z_echoDebug(DBG_TO_ZONEREADY, a_zlem->_zlName);

                args = _z_new_args(10);         /* generate new arg list */
                (void) _z_add_arg(args, ZONEADM_CMD);
                (void) _z_add_arg(args, "-z");
                (void) _z_add_arg(args, "%s", a_zlem->_zlName);
                (void) _z_add_arg(args, "ready");

                ret = z_ExecCmdArray(&status, &results, (char *)NULL,
                    ZONEADM_CMD, _z_get_argv(args));

                /* free generated argument list */

                _z_free_args(args);

                if (ret != 0) {
                        _z_program_error(ERR_ZONEREADY_EXEC, ZONEADM_CMD,
                            strerror(errno));
                        free(results);
                        _z_free_args(args);
                        return (B_FALSE);
                }
                if (status != 0) {
                        _z_program_error(ERR_ZONEREADY_CMDFAIL, ZONEADM_CMD,
                            a_zlem->_zlName, strerror(errno),
                            results == (char *)NULL ? "" : "\n",
                            results == (char *)NULL ? "" : results);
                        if (results != (char *)NULL) {
                                (void) free(results);
                        }
                        return (B_FALSE);
                }

                if (results != (char *)NULL) {
                        (void) free(results);
                }

                for (i = 0; i < MAX_RETRIES; i++) {
                        if (zone_get_state(a_zlem->_zlName, &st) != Z_OK) {
                                break;
                        }
                        if ((st == ZONE_STATE_DOWN) ||
                            (st == ZONE_STATE_INSTALLED)||
                            (st == ZONE_STATE_READY)) {
                                break;
                        }
                        (void) sleep(RETRY_DELAY_SECS);
                }

                /* failure if maximum retries reached */

                if (i >= MAX_RETRIES) {
                        _z_program_error(ERR_ZONEREADY_DIDNT_READY,
                            a_zlem->_zlName);
                        a_zlem->_zlCurrKernelStatus = st;
                        return (B_FALSE);
                }

                /* success - zone is now in the ready state  */

                a_zlem->_zlCurrKernelStatus = ZONE_STATE_READY;

                return (B_TRUE);

        case ZONE_STATE_INSTALLED:
        case ZONE_STATE_CONFIGURED:
        case ZONE_STATE_INCOMPLETE:
        case ZONE_STATE_SHUTTING_DOWN:
        default:
                return (B_FALSE);
        }
}

/*
 * Name:        _z_make_zone_down
 * Description: Given a zone element entry for the non-global zone to affect,
 *              change the state of that non-global zone to "down"
 * Arguments:   a_zlem - [RO, *RW] - (zoneListElement_t)
 *                      Zone list element describing the non-global zone to
 *                      make down
 * Returns:     boolean_t
 *                      B_TRUE - non-global zone state changed successfully
 *                      B_FALSE - failed to make the non-global zone down
 */

boolean_t
_z_make_zone_down(zoneListElement_t *a_zlem)
{
        argArray_t      *args;
        char            *results = (char *)NULL;
        int             status = 0;
        int             ret;

        /* entry assertions */

        assert(a_zlem != NULL);

        /* act based on the zone's current kernel state */

        switch (a_zlem->_zlCurrKernelStatus) {
        case ZONE_STATE_DOWN:
        case ZONE_STATE_READY:
        case ZONE_STATE_RUNNING:
                /* shouldn't be touched */
                return (B_TRUE);

        case ZONE_STATE_MOUNTED:

                _z_echoDebug(DBG_TO_ZONEHALT, a_zlem->_zlName);

                /* these states can be halted - do so */

                args = _z_new_args(10);         /* generate new arg list */
                (void) _z_add_arg(args, ZONEADM_CMD);

                if (zonecfg_in_alt_root()) {
                        (void) _z_add_arg(args, "-R");
                        (void) _z_add_arg(args, "%s",
                            (char *)zonecfg_get_root());
                }

                (void) _z_add_arg(args, "-z");
                (void) _z_add_arg(args, "%s", a_zlem->_zlName);
                (void) _z_add_arg(args, "unmount");

                ret = z_ExecCmdArray(&status, &results, (char *)NULL,
                    ZONEADM_CMD, _z_get_argv(args));

                /* free generated argument list */

                _z_free_args(args);

                if (ret != 0) {
                        _z_program_error(ERR_ZONEHALT_EXEC, ZONEADM_CMD,
                            strerror(errno));
                        free(results);
                        return (B_FALSE);
                }
                if (status != 0) {
                        if (status == -1) {
                                _z_program_error(ERR_ZONEBOOT_CMD_SIGNAL,
                                    ZONEADM_CMD, a_zlem->_zlName);
                        } else {
                                _z_program_error(ERR_ZONEBOOT_CMD_ERROR,
                                    ZONEADM_CMD, a_zlem->_zlName, status,
                                    results == NULL ? "" : "\n",
                                    results == NULL ? "" : results);
                        }
                        free(results);
                        return (B_FALSE);
                }

                free(results);

                a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED;
                /*
                 * Leave the scratch name in place because the upper level
                 * software may have used it to construct file names and the
                 * like.
                 */
                return (B_TRUE);

        case ZONE_STATE_INSTALLED:
        case ZONE_STATE_CONFIGURED:
        case ZONE_STATE_INCOMPLETE:
        case ZONE_STATE_SHUTTING_DOWN:
        default:
                return (B_FALSE);
        }
}

/*
 * Function:    UmountAllZones
 * Description: Unmount all mounted zones under a specified directory.
 *
 * Scope:   public
 * Parameters:  mntpnt  [RO, *RO]
 *          Non-NULL pointer to name of directory to be unmounted.
 * Return:   0  - successfull
 *      -1  - unmount failed; see errno for reason
 */
int
UmountAllZones(char *mntpnt) {

        zoneList_t  zlst;
        int      k;
        int  ret = 0;

        if (z_zones_are_implemented()) {

                z_set_zone_root(mntpnt);

                zlst = z_get_nonglobal_zone_list();
                if (zlst == (zoneList_t)NULL) {
                        return (0);
                }

                for (k = 0; z_zlist_get_zonename(zlst, k) != (char *)NULL;
                    k++) {
                        if (z_zlist_get_current_state(zlst, k) >
                            ZONE_STATE_INSTALLED) {
                                if (!z_zlist_change_zone_state(zlst, k,
                                    ZONE_STATE_INSTALLED)) {
                                        ret = -1;
                                        break;
                                }
                        }
                }

                /* Free zlst */
                z_free_zone_list(zlst);
        }

        return (ret);

}