usr/src/cmd/auto-install/auto_install.c
author Sue Sohn <Susan.Sohn@Oracle.COM>
Fri, 20 Aug 2010 11:31:18 -0600
changeset 862 e9f31f2f2f2d
parent 846 70dd9e819f25
child 867 cfbbedf29419
permissions -rw-r--r--
16423 Updates to AI schema should be made 15449 installadm add validates combined manifest against image-specific schema as well as schema in /usr/share/auto_install/ 6975043 separate criteria and ai manifest 6975686 installadm list shows value rather than range if lower bound is 0

/*
 * 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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <alloca.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libnvpair.h>
#include <locale.h>
#include <sys/param.h>
#include <sys/types.h>

#include "auto_install.h"
#include <ls_api.h>
#include <orchestrator_api.h>

/*
 * use presence of hidden file to indicate iSCSI boot installation
 * pending code refactoring to make less kludgy
 * see also ict.py
 */
#define	ISCSI_BOOT_INDICATOR_FILE "/.iscsi_boot"

static  boolean_t install_done = B_FALSE;
static	boolean_t install_failed = B_FALSE;

/* debug mode - disabled by default */
static	boolean_t debug_mode_enabled = B_FALSE;

int	install_error = 0;
install_params	params;

static boolean_t convert_to_sectors(auto_size_units_t,
    uint64_t, uint64_t *);

void auto_update_progress(om_callback_info_t *, uintptr_t);

static void
usage()
{
	(void) fprintf(stderr,
	    "usage: auto-install -d <diskname> | -p <profile>\n"
	    "\t-i - end installation before Target Instantiation\n"
	    "\t-I - end installation after Target Instantiation\n"
	    "\t-v - run the installer in verbose mode\n");
}

/*
 * enable_debug_mode()
 *
 * Description: Enable/disable debug mode
 *
 * Scope: private
 *
 * Parameters:
 *   enable: B_TRUE - enable debug mode
 *           B_FALSE - disable debug mode
 *
 * Returns: none
 */
static void
enable_debug_mode(boolean_t enable)
{
	debug_mode_enabled = enable;
}

/*
 * is_debug_mode_enabled()
 *
 * Description: Checks, if we run in debug mode
 *
 * Scope: private
 *
 * Parameters: none
 *
 * Returns:
 *   B_TRUE - debug mode enabled
 *   B_FALSE - debug mode disabled
 */
static boolean_t
is_debug_mode_enabled(void)
{
	return (debug_mode_enabled);
}

/*
 * auto_debug_print()
 * Description:	Posts debug message
 */
void
auto_debug_print(ls_dbglvl_t dbg_lvl, char *fmt, ...)
{
	va_list	ap;
	char	buf[MAXPATHLEN + 1] = "";

	va_start(ap, fmt);
	(void) vsnprintf(buf, MAXPATHLEN+1, fmt, ap);
	(void) ls_write_dbg_message("AI", dbg_lvl, buf);
	va_end(ap);
}

/*
 * auto_log_print()
 * Description:	Posts log message
 */
void
auto_log_print(char *fmt, ...)
{
	va_list	ap;
	char	buf[MAXPATHLEN + 1] = "";

	va_start(ap, fmt);
	(void) vsnprintf(buf, MAXPATHLEN+1, fmt, ap);
	(void) ls_write_log_message("AI", buf);
	va_end(ap);
}

/*
 * Callback that gets passed to om_perform_install.
 *
 * Sets the install_done variable when an install is
 * finished. If an install fails, it sets the install_failed
 * variable and also sets the install_error variable to
 * indicate the specific reason for the failure.
 */
void
auto_update_progress(om_callback_info_t *cb_data, uintptr_t app_data)
{
	if (cb_data->curr_milestone == -1) {
		install_error = cb_data->percentage_done;
		install_failed = B_TRUE;
	}

	if (cb_data->curr_milestone == OM_SOFTWARE_UPDATE &&
	    cb_data->percentage_done == 100)
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Transfer completed\n");

	if (cb_data->curr_milestone == OM_POSTINSTAL_TASKS &&
	    cb_data->percentage_done == 100)
		install_done = B_TRUE;
}

/*
 * auto_debug_dump_file()
 * Description: dumps a file using auto_debug_print()
 */
void
auto_debug_dump_file(ls_dbglvl_t level, char *filename)
{
	FILE *file_ptr;
	char buffer[MAXPATHLEN];

	/* Logfile does not exist.  Nothing to print. */
	if (access(filename, F_OK) < 0) {
		return;
	}

	if (access(filename, R_OK) < 0) {
		auto_debug_print(AUTO_DBGLVL_ERR,
		    "ddu errlog %s does not have read permissions.\n");
		return;
	}

	/* Use buffer to set up the command. */
	snprintf(buffer, MAXPATHLEN, "/usr/bin/cat %s", filename);
	if ((file_ptr = popen(buffer, "r")) == NULL) {
		auto_debug_print(AUTO_DBGLVL_ERR,
		    "Error opening ddu errlog %s to dump errors: %s\n",
		    filename, strerror(errno));
		return;
	}

	/* Reuse buffer to get the file data. */
	while (fgets(buffer, MAXPATHLEN, file_ptr) != NULL) {
		auto_debug_print(level, "%s", buffer);
	}
	(void) pclose(file_ptr);
}

/*
 * This function splits the passed in file into AI manifest and SC manifest
 *
 * Input:
 * char *input_file	- AI manifest file with embedded SC manifest
 *
 * Output:
 * char *ai_manifest	- Writes the AI manifest portion of the input to this
 *			  file name
 * char *sc_manifest	- Writes the SC manifest portion of the input to this
 *			  file name
 * Returns:
 * AUTO_VALID_MANIFEST (0)	- If the operation is successful
 * AUTO_INVALID_MANIFEST (-1)	- If the operation fails
 */
static int
auto_split_manifests(char *input_file, char *ai_manifest, char *sc_manifest)
{
	FILE		*ifp;	/* Input file */
	FILE		*aifp;	/* AI manifest */
	FILE		*scfp;	/* SC manifest */
	boolean_t	writing_ai_manifest = B_TRUE;
	boolean_t	writing_sc_manifest = B_FALSE;
	char		buf[BUFSIZ];


	if (input_file == NULL || ai_manifest == NULL || sc_manifest == NULL) {
		return (AUTO_INVALID_MANIFEST);
	}

	if (access(input_file, F_OK) != 0) {
		return (AUTO_INVALID_MANIFEST);
	}

	/*
	 * Open the input file in read-only mode
	 */
	ifp = fopen(input_file, "r");
	if (ifp == NULL) {
		auto_log_print(gettext("Cannot open AI manifest %s\n"),
		    input_file);
		return (AUTO_INVALID_MANIFEST);
	}

	/*
	 * Open the output files in write mode
	 */
	aifp = fopen(ai_manifest, "w");
	if (aifp == NULL) {
		auto_log_print(gettext("Cannot open AI manifest %s\n"),
		    ai_manifest);
		return (AUTO_INVALID_MANIFEST);
	}

	scfp = fopen(sc_manifest, "w");
	if (scfp == NULL) {
		auto_log_print(gettext("Cannot open SC manifest %s\n"),
		    sc_manifest);
		return (AUTO_INVALID_MANIFEST);
	}

	while (fgets(buf, sizeof (buf), ifp) != NULL) {

		/*
		 * The SC manifest begins with <?xml version='1.0'?> and
		 * ends with the line "</service_bundle>". It is embedded
		 * in the input file between <sc_embedded_manifest ...>
		 * and  </sc_embedded_manifest>.
		 */
		if (strstr(buf, SC_EMBEDDED_BEGIN_MARKER) != NULL) {
			writing_ai_manifest = B_FALSE;
			continue;
		}
		if (strstr(buf, SC_EMBEDDED_END_MARKER) != NULL) {
			writing_ai_manifest = B_TRUE;
			continue;
		}
		if ((strstr(buf, SC_MANIFEST_BEGIN_MARKER) != NULL) &&
		    (! writing_ai_manifest)) {
			writing_sc_manifest = B_TRUE;

			/*
			 * XML is pretty strict about format of XML prolog.
			 * It is optional, but if present, no leading comments
			 * or whitespace characters are allowed.
			 * Assure this by replacing the whole first line
			 * of SC manifest with following string:
			 * "<?xml version='1.0'?>\n"
			 */
			fputs(SC_MANIFEST_BEGIN_MARKER, scfp);
			fputs("\n", scfp);
			continue;
		}
		if (writing_ai_manifest) {
			fputs(buf, aifp);
			continue;
		} else if (writing_sc_manifest) {
			if (strstr(buf, SC_MANIFEST_END_MARKER) != NULL) {
				writing_sc_manifest = B_FALSE;
			}
			fputs(buf, scfp);
			continue;
		}
	}

	fclose(ifp);
	fclose(aifp);
	fclose(scfp);
	return (AUTO_VALID_MANIFEST);
}

/*
 * Create a file that contains the list
 * of packages to be installed or removed.
 *
 * Parameters:
 *   hardcode - if set to B_TRUE, hardcode the list of packages. This is for
 *              testing purposes only, when AI engine is not provided with
 *              AI manifest.
 *
 *   pkg_list_type - specify list of packages to be obtained -
 *                   install or remove.
 *
 *   pkg_list_file - output file where the package list will be saved
 *
 * Returns:
 *	AUTO_INSTALL_SUCCESS for success
 *	AUTO_INSTALL_FAILURE for failure
 *	AUTO_INSTALL_EMPTY_LIST - 'remove' list is empty
 */
static int
create_package_list_file(boolean_t hardcode,
    auto_package_list_type_t pkg_list_type, char *pkg_list_file)
{
	FILE *fp;
	char **package_list;
	int i, num_packages = 0;
	int ret = AUTO_INSTALL_SUCCESS;

	if ((fp = fopen(pkg_list_file, "wb")) == NULL) {
		auto_debug_print(AUTO_DBGLVL_ERR,
		    "Couldn't open file %s for storing list of packages\n",
		    pkg_list_file);

		return (AUTO_INSTALL_FAILURE);
	}

	auto_debug_print(AUTO_DBGLVL_INFO,
	    "File %s successfully opened - list of packages to be %s "
	    "will be saved there\n", pkg_list_file,
	    pkg_list_type == AI_PACKAGE_LIST_INSTALL ? "installed" : "removed");

	/*
	 * When invoked in test mode (without AI manifest), lists of packages
	 * are hardcoded
	 */

	if (hardcode) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Hardcoded list of packages will be generated\n");

		if (pkg_list_type == AI_PACKAGE_LIST_INSTALL) {
			if (fputs(AI_TEST_PACKAGE_LIST_INSTALL, fp) == EOF)
				ret = AUTO_INSTALL_FAILURE;
		} else {
			if (fputs(AI_TEST_PACKAGE_LIST_REMOVE, fp) == EOF)
				ret = AUTO_INSTALL_FAILURE;
		}

		(void) fclose(fp);
		return (ret);
	}

	/*
	 * Obtain list of packages to be installed or removed from AI manifest.
	 *
	 * With respect to install list, there are two tags supported for
	 * specifying list of packages in order to keep backward compatibility.
	 * Try new tag first. If it is not specified, then try the old one.
	 */
	if (pkg_list_type == AI_PACKAGE_LIST_INSTALL) {
		package_list = ai_get_manifest_packages(&num_packages,
		    AIM_PACKAGE_INSTALL_NAME);

		if (package_list == NULL) {
			/* If no package list given, use default */
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "No install package list given, using default\n");

			num_packages = 4;
			package_list = malloc((num_packages + 1) * sizeof (char *));
			package_list[0] = strdup("pkg:/SUNWcsd");
			package_list[1] = strdup("pkg:/SUNWcs");
			package_list[2] = strdup("pkg:/babel_install");
			package_list[3] = strdup("pkg:/entire");
			package_list[4] = NULL;
		}

		auto_log_print(gettext(
		    "list of packages to be installed is:\n"));
	} else {
		package_list = ai_get_manifest_packages(&num_packages,
		    AIM_PACKAGE_REMOVE_NAME);
		if (package_list == NULL) {
			auto_debug_print(AUTO_DBGLVL_INFO,
			    "List of packages to be removed is empty\n");

			(void) fclose(fp);
			return (AUTO_INSTALL_EMPTY_LIST);
		}

		auto_log_print(gettext(
		    "list of packages to be removed is:\n"));
	}

	/*
	 * Save list of packages to the file
	 */
	for (i = 0; i < num_packages; i++) {
		auto_log_print("%s\n", package_list[i]);

		if (fputs(package_list[i], fp) == EOF) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Write to %s file failed\n", pkg_list_file);

			ret = AUTO_INSTALL_FAILURE;
			break;
		}

		if (fputs("\n", fp) == EOF) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Write to %s file failed\n", pkg_list_file);

			ret = AUTO_INSTALL_FAILURE;
			break;
		}
	}

	(void) fclose(fp);
	return (ret);
}

/*
 * Create/delete/preserve vtoc slices as specified
 * in the manifest
 */
static int
auto_modify_target_slices(auto_slice_info *asi, uint8_t install_slice_id)
{
	for (; asi->slice_action[0] != '\0'; asi++) {
		uint64_t slice_size_sec;

		auto_debug_print(AUTO_DBGLVL_INFO,
		    "slice action %s, size=%lld units=%s\n",
		    asi->slice_action, asi->slice_size,
		    CONVERT_UNITS_TO_TEXT(asi->slice_size_units));

		if (!convert_to_sectors(asi->slice_size_units,
		    asi->slice_size, &slice_size_sec)) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "conversion failure from %lld %s to sectors\n",
			    asi->slice_size,
			    CONVERT_UNITS_TO_TEXT(asi->slice_size_units));
			return (AUTO_INSTALL_FAILURE);
		}
		if (strcmp(asi->slice_action, "create") == 0) {
			om_slice_tag_type_t slice_tag;

			if (asi->slice_number == install_slice_id)
				slice_tag = OM_ROOT;
			else
				slice_tag = OM_UNASSIGNED;
			if (!om_create_slice(asi->slice_number, slice_size_sec,
			    slice_tag, asi->on_existing))
				return (AUTO_INSTALL_FAILURE);
		} else if (strcmp(asi->slice_action, "delete") == 0) {
			if (!om_delete_slice(asi->slice_number))
				return (AUTO_INSTALL_FAILURE);
		} else if (strcmp(asi->slice_action, "preserve") == 0) {
			if (!om_preserve_slice(asi->slice_number))
				return (AUTO_INSTALL_FAILURE);
		}
	}
	return (AUTO_INSTALL_SUCCESS);
}

/*
 * convert value to sectors given basic unit size
 * TODO uint64_t overflow check
 */

static boolean_t
convert_to_sectors(auto_size_units_t units, uint64_t src,
    uint64_t *psecs)
{
	if (psecs == NULL)
		return (B_FALSE);
	switch (units) {
		case AI_SIZE_UNITS_SECTORS:
			*psecs = src;
			break;
		case AI_SIZE_UNITS_MEGABYTES:
			*psecs = src*2048;
			break;
		case AI_SIZE_UNITS_GIGABYTES:
			*psecs = src*2048*1024; /* sec=>MB=>GB */
			break;
		case AI_SIZE_UNITS_TERABYTES:
			*psecs = src*2048*1024*1024; /* sec=>MB=>GB=>TB */
			break;
		default:
			return (B_FALSE);
	}
	if (units != AI_SIZE_UNITS_SECTORS)
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "converting from %lld %s to %lld sectors\n",
		    src, CONVERT_UNITS_TO_TEXT(units), *psecs);
	return (B_TRUE);
}

#ifndef	__sparc
/*
 * Create/delete/preserve fdisk partitions as specifed
 * in the manifest
 * Note that the partition size is converted using the units specified
 *	for both create and delete actions
 */
static int
auto_modify_target_partitions(auto_partition_info *api)
{
	for (; api->partition_action[0] != '\0'; api++) {
		uint64_t partition_size_sec;

		auto_debug_print(AUTO_DBGLVL_INFO,
		    "partition action %s, size=%lld units=%s logical? %s\n",
		    api->partition_action, api->partition_size,
		    CONVERT_UNITS_TO_TEXT(api->partition_size_units),
		    api->partition_is_logical ? "yes" : "no");

		if (!convert_to_sectors(api->partition_size_units,
		    api->partition_size, &partition_size_sec)) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "conversion failure from %lld %s to sectors\n",
			    api->partition_size,
			    CONVERT_UNITS_TO_TEXT(api->partition_size_units));
			return (AUTO_INSTALL_FAILURE);
		}
		if (strcmp(api->partition_action, "create") == 0) {
			if (!om_create_partition(api->partition_type,
			    api->partition_start_sector,
			    partition_size_sec, B_FALSE,
			    api->partition_is_logical))
				return (AUTO_INSTALL_FAILURE);
		} else if (strcmp(api->partition_action, "delete") == 0) {
			if (!om_delete_partition(api->partition_number,
			    api->partition_start_sector, partition_size_sec))
				return (AUTO_INSTALL_FAILURE);
		}
	}
	return (AUTO_INSTALL_SUCCESS);
}
#endif

/*
 * Initialize the image area with default publisher
 * Set the nv-list for configuring default publisher to be used
 * with the installation. This passes the publisher name and url along
 * mount point (/a) and action (initilaize pkg image area). The transfer module
 * will use these parameters and calls the appropriate pkg commands to
 * initialize the pkg imag area and setup the default publisher
 */
static int
configure_ips_init_nv_list(nvlist_t **attr, auto_repo_info_t *repo)
{
	if (nvlist_add_uint32(*attr, TM_ATTR_MECHANISM,
	    TM_PERFORM_IPS) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_ATTR_MECHANISM failed\n");
		return (-1);
	}
	if (nvlist_add_uint32(*attr, TM_IPS_ACTION,
	    TM_IPS_INIT) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TMP_IPS_ACTION failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr, TM_IPS_INIT_MNTPT,
	    INSTALLED_ROOT_DIR) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_MNTPT failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr, TM_IPS_PKG_URL, repo->url) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_URL failed\n");
		return (-1);
	}

	auto_log_print(gettext("installation will be performed "
	    "from %s (%s)\n"), repo->url, repo->publisher);

	if (nvlist_add_string(*attr, TM_IPS_PKG_AUTH, repo->publisher)
	    != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_AUTH failed\n");
		return (-1);
	}

	if (nvlist_add_string(*attr, TM_IPS_INIT_RETRY_TIMEOUT,
	    TM_IPS_INIT_TIMEOUT_DEFAULT) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_RETRY_TIMEOUT failed\n");
		return (-1);
	}

	/*
	 * We need to ask IPS to force creating IPS image, since when
	 * default path is chosen, IPS refuses to create the image.
	 * The reason is that even if we created empty BE to be
	 * populated by IPS, it contains ZFS shared and non-shared
	 * datasets mounted on appropriate mount points. And
	 * IPS complains in the case the target mount point contains
	 * subdirectories.
	 */

	if (nvlist_add_boolean_value(*attr,
	    TM_IPS_IMAGE_CREATE_FORCE, B_TRUE) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_IMAGE_CREATE_FORCE failed\n");
		return (-1);
	}
	return (0);
}

/*
 * configure_ips_addl_publisher_nv_list
 * Set the nv-list for configuring additional publisher(s) to be used
 * with the installation. The nv_list contains the publisher name and url along
 * with mount point (/a) and action (set-publisher). The transfer module
 * will use these parameters and calls the appropriate pkg commands to
 * setup additional publisher.
 */
static int
configure_ips_addl_publisher_nv_list(
    nvlist_t **attr, auto_repo_info_t *repo)
{
	if (nvlist_add_uint32(*attr, TM_ATTR_MECHANISM,
	    TM_PERFORM_IPS) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_ATTR_MECHANISM failed\n");
		return (-1);
	}
	if (nvlist_add_uint32(*attr, TM_IPS_ACTION,
	    TM_IPS_SET_AUTH) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TMP_IPS_ACTION failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr, TM_IPS_INIT_MNTPT,
	    INSTALLED_ROOT_DIR) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_MNTPT failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr, TM_IPS_ALT_URL, repo->url) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_URL failed\n");
		return (-1);
	}

	auto_log_print(gettext("Using addditional repository "
	    "from %s (%s)\n"), repo->url, repo->publisher);

	if (nvlist_add_string(*attr, TM_IPS_ALT_AUTH, repo->publisher)
	    != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_AUTH failed\n");
		return (-1);
	}

	return (0);
}

/*
 * configure_ips_mirror_nv_list
 * Set the nv-list for configuring a mirror to either the default repository
 * or any additional repository to be used with the installation. The nv_list
 * contains the publisher name and url along with mount point (/a) and action
 * (set-publisher). The transfer module will use these parameters and calls
 * appropriate pkg commands to setup the mirror.
 */
static int
configure_ips_mirror_nv_list(nvlist_t **attr, char *publisher, char *mirror_url)
{
	if (publisher == NULL || mirror_url == NULL) {
		return (-1);
	}
	auto_log_print(gettext("using mirror at %s for publisher %s\n"),
	    mirror_url, publisher);

	if (nvlist_add_uint32(*attr,
	    TM_ATTR_MECHANISM, TM_PERFORM_IPS) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_ATTR_MECHANISM failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr,
	    TM_IPS_INIT_MNTPT, INSTALLED_ROOT_DIR) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_MNTPT failed\n");
		return (-1);
	}
	if (nvlist_add_uint32(*attr,
	    TM_IPS_ACTION, TM_IPS_SET_AUTH) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TMP_IPS_ACTION failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr,
	    TM_IPS_ALT_URL, mirror_url) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_ALT_URL failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr,
	    TM_IPS_ALT_AUTH, publisher) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_ALT_AUTH failed\n");
		return (-1);
	}
	if (nvlist_add_string(*attr,
	    TM_IPS_MIRROR_FLAG, TM_IPS_SET_MIRROR) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_MIRROR_FLAG failed\n");
		return (-1);
	}
	auto_log_print(gettext("Using the mirror %s for the publisher %s\n"),
	    mirror_url, publisher);

	return (0);
}

/*
 * Install the target based on the criteria specified in
 * ai.xml.
 *
 * NOTE: ai_validate_manifest() MUST have been called prior
 * to calling this function.
 *
 * RETURNS:
 *	AUTO_INSTALL_SUCCESS on success
 *	AUTO_INSTALL_FAILURE on failure
 */
static int
install_from_manifest()
{
	char *p = NULL;
	auto_disk_info adi;
	auto_swap_device_info adsi;
	auto_dump_device_info addi;
	auto_sc_params asp;
	int status;
	int return_status = AUTO_INSTALL_FAILURE;
	uint8_t install_slice_id;
	int ita = 0;
	int number = 0;
	/*
	 * pointers to heap - free later if not NULL
	 */
	auto_slice_info *asi = NULL;
#ifndef	__sparc
	auto_partition_info *api = NULL;
#endif
	char *diskname = NULL;
	nvlist_t *install_attr = NULL, **transfer_attr = NULL;
	char *proxy = NULL;
	auto_repo_info_t	*default_ips_repo = NULL;
	auto_repo_info_t	*addl_ips_repo = NULL;
	auto_repo_info_t	*rptr;
	auto_mirror_repo_t  *mptr;
	int ret = AUTO_INSTALL_SUCCESS;
	char iscsi_devnam[MAXNAMELEN] = "";

	/*
	 * Start out by getting the install target and
	 * validating that target
	 */
	bzero(&adi, sizeof (auto_disk_info));
	ret = ai_get_manifest_disk_info(&adi);
	if (ret == AUTO_INSTALL_FAILURE) {
		auto_log_print(gettext("disk info manifest error\n"));
		return (AUTO_INSTALL_FAILURE);
	}

	/*
	 * Retrieve device swap information if specified
	 */
	bzero(&adsi, sizeof (auto_swap_device_info));
	ret = ai_get_manifest_swap_device_info(&adsi);
	if (ret == AUTO_INSTALL_FAILURE) {
		auto_log_print(gettext("device swap manifest error\n"));
		return (AUTO_INSTALL_FAILURE);
	}

	/*
	 * Retrieve device dump information if specified
	 */
	bzero(&addi, sizeof (auto_dump_device_info));
	ret = ai_get_manifest_dump_device_info(&addi);
	if (ret == AUTO_INSTALL_FAILURE) {
		auto_log_print(gettext("device dump manifest error\n"));
		return (AUTO_INSTALL_FAILURE);
	}

	/*
	 * grab target slice number
	 */
	install_slice_id = adi.install_slice_number;

	/*
	 * if iSCSI target requested, mount it through iSCSI initiator
	 */
	ret = mount_iscsi_target_if_requested(&adi,
	    iscsi_devnam, sizeof (iscsi_devnam));
	if (ret == -1) {
		auto_log_print(gettext("iSCSI boot target device error\n"));
		return (AUTO_INSTALL_FAILURE);
	}
	/*
	 * if iSCSI device was discovered and mounted,
	 *	write iSCSI boot marker file for ICT reference
	 */
	if (iscsi_devnam[0] == '\0') { /* no iSCSI target mounted */
		/*
		 * make sure indicator file not there from previous run
		 */
		errno = 0;
		if (unlink(ISCSI_BOOT_INDICATOR_FILE) != 0 &&
		    errno != ENOENT) {
			auto_log_print(gettext(
			    "Could not delete " ISCSI_BOOT_INDICATOR_FILE
			    " to indicate no iSCSI boot target\n"));
			return (AUTO_INSTALL_FAILURE);
		}
	} else { /* iSCSI target mounted - indicate for ICT */
		FILE *fd;
		/*
		 * take device name from iSCSI target as selected
		 * install device
		 */
		(void) strncpy(adi.diskname, iscsi_devnam,
		    sizeof (adi.diskname));
		/*
		 * create marker to signal ICT to enable nwam
		 * in service repository
		 */
		fd = fopen(ISCSI_BOOT_INDICATOR_FILE, "w");
		if (fd == NULL) {
			auto_log_print(gettext(
			    "Could not create " ISCSI_BOOT_INDICATOR_FILE
			    " to indicate iSCSI boot target\n"));
			return (AUTO_INSTALL_FAILURE);
		}
		/*
		 * write device name - used for debugging only
		 */
		(void) fputs(iscsi_devnam, fd);
		(void) fclose(fd);
	}

	/*
	 * Initiate target discovery and wait until it is finished
	 */

	if (auto_target_discovery() != AUTO_TD_SUCCESS) {
		auto_log_print(gettext("Automated installation failed in "
		    "Target Discovery module\n"));

		auto_log_print(gettext("Please see previous messages for more "
		    "details\n"));

		return (AUTO_INSTALL_FAILURE);
	}

	/*
	 * given manifest input and discovery information,
	 *	select a target disk for the installation
	 */
	if (auto_select_install_target(&diskname, &adi) != AUTO_TD_SUCCESS) {
		auto_log_print(gettext("ai target device not found\n"));
		return (AUTO_INSTALL_FAILURE);
	}

	auto_log_print(gettext("Disk name selected for installation is %s\n"),
	    diskname);
#ifndef	__sparc
	/*
	 * Configure the partitions as specified in the
	 * manifest
	 */
	api = ai_get_manifest_partition_info(&status);
	if (status != 0) {
		auto_debug_print(AUTO_DBGLVL_ERR,
		    "failed to process manifest due to illegal value\n");
		goto error_ret;
	}
	if (api == NULL)
		auto_log_print(gettext("no manifest partition "
		    "information found\n"));
	else {
		if (auto_modify_target_partitions(api) !=
		    AUTO_INSTALL_SUCCESS) {
			auto_log_print(gettext("failed to modify partition(s) "
			    "specified in the manifest\n"));
			goto error_ret;
		}

		/* we're done with futzing with partitions, free the memory */
		free(api);
		api = NULL; /* don't release later */
	}

	/*
	 * if no partition exists and no partitions were specified in manifest,
	 *	there is no info about partitions for TI,
	 *	so create info table from scratch
	 */
	om_create_target_partition_info_if_absent();

	/* finalize modified partition table for TI to apply to target disk */
	if (!om_finalize_fdisk_info_for_TI()) {
		auto_log_print(gettext("failed to finalize fdisk info\n"));
		return (AUTO_INSTALL_FAILURE);
	}
#endif
	/*
	 * Configure the vtoc slices as specified in the
	 * manifest
	 */
	asi = ai_get_manifest_slice_info(&status);
	if (status != 0) {
		auto_debug_print(AUTO_DBGLVL_ERR,
		    "failed to process manifest due to illegal value\n");
		goto error_ret;
	}
	if (asi == NULL)
		auto_log_print(gettext(
		    "no manifest slice information found\n"));
	else {
		if (auto_modify_target_slices(asi, install_slice_id) !=
		    AUTO_INSTALL_SUCCESS) {
			auto_log_print(gettext(
			    "failed to modify slice(s) specified "
			    "in the manifest\n"));
			goto error_ret;
		}

		/* we're done with futzing with slices, free the memory */
		free(asi);
		asi = NULL;	/* already freed */
	}

	/* finalize modified vtoc for TI to apply to target disk partition */
	if (!om_finalize_vtoc_for_TI(install_slice_id)) {
		auto_log_print(gettext("failed to finalize vtoc info\n"));
		goto error_ret;
	}

	if (nvlist_alloc(&install_attr, NV_UNIQUE_NAME, 0) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "nvlist allocation failed\n");
		goto error_ret;
	}

	if (nvlist_add_uint8(install_attr, OM_ATTR_INSTALL_TYPE,
	    OM_INITIAL_INSTALL) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_INSTALL_TYPE failed\n");
		goto error_ret;
	}

	if (nvlist_add_string(install_attr, OM_ATTR_DISK_NAME,
	    diskname) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_DISK_NAME failed\n");
		goto error_ret;
	}
	free(diskname);
	diskname = NULL;	/* already freed */

	/*
	 * Parse the SC (system configuration manifest)
	 */
	auto_log_print(gettext("Parsing system configuration manifest\n"));

	bzero(&asp, sizeof (auto_sc_params));
	if (auto_parse_sc_manifest(SC_MANIFEST_FILE, &asp) !=
	    AUTO_INSTALL_SUCCESS) {
		auto_log_print(gettext("Automated Installation failed in"
		    " parser module\n"));
		auto_log_print(gettext("Invalid System Configuration manifest"
		    " provided\n"));

		goto error_ret;
	}

	/*
	 * if no hostname provided in SC manifest, use "opensolaris"
	 */
	if (nvlist_add_string(install_attr, OM_ATTR_HOST_NAME,
	    asp.hostname == NULL ? "opensolaris" : asp.hostname) != 0) {
		auto_log_print(gettext("Setting of OM_ATTR_HOST_NAME"
		    " failed\n"));
		goto error_ret;
	}

	if (nvlist_add_string(install_attr, OM_ATTR_TIMEZONE_INFO,
	    asp.timezone) != 0) {
		auto_log_print(gettext("Setting of OM_ATTR_TIMEZONE_INFO"
		    " failed\n"));
		goto error_ret;
	}
	if (asp.timezone != NULL && *asp.timezone != '\0' &&
	    om_set_time_zone(asp.timezone) != OM_SUCCESS) {
		auto_log_print(gettext("The time zone in the installed system"
		    " will not be the timezone specified in the SC manifest"
		    " (%s)\n"), asp.timezone);
		om_set_error(OM_SUCCESS);	/* reset Orchestrator errno */
	}

	if (nvlist_add_string(install_attr, OM_ATTR_DEFAULT_LOCALE,
	    "C") != 0) {
		auto_log_print(gettext("Setting of OM_ATTR_DEFAULT_LOCALE"
		    " failed\n"));
		goto error_ret;
	}

	/*
	 * If proxy is specified, set the http_proxy environemnet variable for
	 * IPS to use
	 */
	p = ai_get_manifest_http_proxy();
	if (p != NULL) {
		int proxy_len;

		proxy_len = strlen("http_proxy=") + strlen(p) + 1;
		proxy = malloc(proxy_len);
		snprintf(proxy, proxy_len, "%s%s", "http_proxy=", p);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting http_proxy environment variable to %s\n", p);
		if (putenv(proxy)) {
			auto_debug_print(AUTO_DBGLVL_INFO,
			    "Setting of http_proxy environment variable failed:"
			    " %s\n", strerror(errno));
			goto error_ret;
		}
	}
	/*
	 * Get the IPS default publisher, mirrors for the default publisher,
	 * additional publishers and mirrors for each additinal publishers.
	 * Based on the data, the space for nv list allocated to perform
	 * Transfer initialization
	 */
	default_ips_repo = ai_get_default_repo_info();
	if (default_ips_repo == NULL) {
		auto_log_print(gettext("IPS default publisher is not "
		    "specified\n"));
		goto error_ret;
	}

	number = 1; /* For the default publisher */
	/*
	 * Count the mirrors
	 */
	for (mptr = default_ips_repo ->mirror_repo; mptr != NULL;
	    mptr = mptr->next_mirror) {
		number++;
	}
	addl_ips_repo = ai_get_additional_repo_info();
	if (addl_ips_repo == NULL) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "No additional IPS publishers specified\n");
	}

	/*
	 * Count the number of additional repos and its mirrors
	 */
	for (rptr =  addl_ips_repo; rptr != NULL; rptr = rptr->next_repo) {
		number++;
		for (mptr = rptr->mirror_repo; mptr != NULL;
		    mptr = mptr->next_mirror) {
			number++;
		}
	}

	/*
	 * Allocate enough pointer space for any possible TM initialization
	 * 	number of publishers and their mirrors
	 *	+ Packages to be installed
	 *	+ Packages to be removed
	 */
	transfer_attr = calloc(number+2, sizeof (nvlist_t *));
	if (transfer_attr == NULL) {
		goto error_ret;
	}

	ita = 0;
	if (nvlist_alloc(&transfer_attr[ita], NV_UNIQUE_NAME, 0) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "nvlist allocation failed\n");
		goto error_ret;
	}
	/*
	 * Initialize the image pkg area and setup default publisher
	 */
	status = configure_ips_init_nv_list(
	    &transfer_attr[ita], default_ips_repo);
	if (status != SUCCESS) {
		goto error_ret;
	}

	/*
	 * Setup the mirrors for the default publisher one at a time
	 */
	for (mptr = default_ips_repo->mirror_repo;
	    mptr != NULL; mptr = mptr->next_mirror) {
		char    *publisher;
		char    *mirror_url;

		ita++;
		publisher = default_ips_repo->publisher;
		mirror_url = mptr->mirror_url;
		if (nvlist_alloc(&transfer_attr[ita], NV_UNIQUE_NAME, 0) != 0) {
			auto_debug_print(AUTO_DBGLVL_INFO,
			    "nvlist allocation failed\n");
			return (-1);
		}
		status = configure_ips_mirror_nv_list(&transfer_attr[ita],
		    publisher, mirror_url);
		if (status != SUCCESS) {
			goto error_ret;
		}
	}

	/*
	 * Configure the additional publisher(s)
	 */
	for (rptr = addl_ips_repo; rptr != NULL; rptr = rptr->next_repo) {
		ita++;
		if (nvlist_alloc(&transfer_attr[ita],
		    NV_UNIQUE_NAME, 0) != 0) {
			auto_debug_print(AUTO_DBGLVL_INFO,
			    "nvlist allocation failed\n");
			goto error_ret;
		}
		status = configure_ips_addl_publisher_nv_list
		    (&transfer_attr[ita], rptr);
		if (status != SUCCESS) {
			goto error_ret;
		}

		/*
		 * Setup mirrors (if any) for each additional publisher
		 */
		for (mptr = rptr->mirror_repo;
		    mptr != NULL; mptr = mptr->next_mirror) {
			char    *publisher;
			char    *mirror_url;

			ita++;
			publisher = rptr->publisher;
			mirror_url = mptr->mirror_url;
			if (nvlist_alloc(&transfer_attr[ita],
			    NV_UNIQUE_NAME, 0) != 0) {
				auto_debug_print(AUTO_DBGLVL_INFO,
				    "nvlist allocation failed\n");
				return (-1);
			}
			status = configure_ips_mirror_nv_list(
			    &transfer_attr[ita], publisher, mirror_url);
			if (status != SUCCESS) {
				goto error_ret;
			}
		}
	}

	/*
	 * Get the list of packages and add it to the nv_list
	 */
	ita++;
	if (nvlist_alloc(&transfer_attr[ita], NV_UNIQUE_NAME, 0) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "nvlist allocation failed\n");
		goto error_ret;
	}
	if (nvlist_add_uint32(transfer_attr[ita], TM_ATTR_MECHANISM,
	    TM_PERFORM_IPS) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_ATTR_MECHANISM failed\n");
		goto error_ret;
	}
	if (nvlist_add_uint32(transfer_attr[ita], TM_IPS_ACTION,
	    TM_IPS_RETRIEVE) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TMP_IPS_ACTION failed\n");
		goto error_ret;
	}
	if (nvlist_add_string(transfer_attr[ita], TM_IPS_INIT_MNTPT,
	    INSTALLED_ROOT_DIR) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_MNTPT failed\n");
		goto error_ret;
	}

	/*
	 * list out the list of packages to be installed
	 * from the manifest and add it into a file
	 */
	if (create_package_list_file(B_FALSE, AI_PACKAGE_LIST_INSTALL,
	    AUTO_INSTALL_PKG_LIST_FILE) != AUTO_INSTALL_SUCCESS) {
		auto_log_print(gettext("Failed to create a file with list "
		    "of packages to be installed\n"));
		goto error_ret;
	}
	if (nvlist_add_string(transfer_attr[ita], TM_IPS_PKGS,
	    AUTO_INSTALL_PKG_LIST_FILE) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKGS failed\n");
		goto error_ret;
	}

	/*
	 * if debug mode enabled, run 'pkg install' in verbose mode
	 */
	if (is_debug_mode_enabled()) {
		if (nvlist_add_boolean_value(transfer_attr[ita],
		    TM_IPS_VERBOSE_MODE, B_TRUE) != 0) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Setting of TM_IPS_VERBOSE_MODE failed\n");
			goto error_ret;
		}
	}

	/*
	 * Since this operation is optional (list of packages
	 * to be removed might be empty), before we start to
	 * populate nv list with attributes, determine if there
	 * is anything to do.
	 */
	ret = create_package_list_file(B_FALSE, AI_PACKAGE_LIST_REMOVE,
	    AUTO_REMOVE_PKG_LIST_FILE);

	if (ret == AUTO_INSTALL_FAILURE) {
		auto_log_print(gettext("Failed to create a file with list "
		    "of packages to be removed\n"));
		goto error_ret;
	} else if (ret == AUTO_INSTALL_EMPTY_LIST) {
		auto_log_print(gettext("No packages specified to be removed "
		    "from installed system\n"));
	} else {
		/*
		 * allocate nv list
		 */
		ita++;

		if (nvlist_alloc(&transfer_attr[ita], NV_UNIQUE_NAME, 0) != 0) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "nvlist allocation failed\n");
			goto error_ret;
		}

		/* select IPS transfer mechanism */
		if (nvlist_add_uint32(transfer_attr[ita], TM_ATTR_MECHANISM,
		    TM_PERFORM_IPS) != 0) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Setting of TM_ATTR_MECHANISM failed\n");
			goto error_ret;
		}

		/* specify 'uninstall' action */
		if (nvlist_add_uint32(transfer_attr[ita], TM_IPS_ACTION,
		    TM_IPS_UNINSTALL) != 0) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Setting of TMP_IPS_ACTION failed\n");
			goto error_ret;
		}

		/*  set target mountpoint */
		if (nvlist_add_string(transfer_attr[ita], TM_IPS_INIT_MNTPT,
		    INSTALLED_ROOT_DIR) != 0) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Setting of TM_IPS_INIT_MNTPT failed\n");
			goto error_ret;
		}

		/*  provide list of packages to be removed */
		if (nvlist_add_string(transfer_attr[ita], TM_IPS_PKGS,
		    AUTO_REMOVE_PKG_LIST_FILE) != 0) {
			auto_debug_print(AUTO_DBGLVL_ERR,
			    "Setting of TM_IPS_PKGS failed\n");
			goto error_ret;
		}

		/*
		 * if debug mode enabled, run 'pkg uninstall' in verbose mode
		 */
		if (is_debug_mode_enabled()) {
			if (nvlist_add_boolean_value(transfer_attr[ita],
			    TM_IPS_VERBOSE_MODE, B_TRUE) != 0) {
				auto_debug_print(AUTO_DBGLVL_ERR,
				    "Setting of TM_IPS_VERBOSE_MODE failed\n");
				goto error_ret;
			}
		}
	}

	if (nvlist_add_nvlist_array(install_attr, OM_ATTR_TRANSFER,
	    transfer_attr, ita + 1) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_TRANSFER failed\n");
		goto error_ret;
	}

	/* Add requested swap size */
	if (adsi.swap_size >= 0) {
		if (nvlist_add_int32(install_attr, OM_ATTR_SWAP_SIZE,
		    adsi.swap_size) != 0) {
			nvlist_free(install_attr);
			auto_debug_print(AUTO_DBGLVL_INFO,
			    "Setting of OM_ATTR_SWAP_SIZE failed\n");
			return (AUTO_INSTALL_FAILURE);
		}
	}

	/* Add requested dump device size */
	if (addi.dump_size >= 0) {
		if (nvlist_add_int32(install_attr, OM_ATTR_DUMP_SIZE,
		    addi.dump_size) != 0) {
			nvlist_free(install_attr);
			auto_debug_print(AUTO_DBGLVL_INFO,
			    "Setting of OM_ATTR_DUMP_SIZE failed\n");
			return (AUTO_INSTALL_FAILURE);
		}
	}

	status = om_perform_install(install_attr, auto_update_progress);
	if (status == OM_FAILURE) { /* synchronous failure before threading */
		install_error = om_errno;
		install_failed = B_TRUE;
	}
	/* wait for thread to report final status */
	while (!install_done && !install_failed)
		sleep(10);

	/*
	 * If the installation failed, report where or/and why the failure
	 * happened
	 */

	if (install_failed) {
		/*
		 * Check if valid failure code was returned - if not, log only
		 * error code itself instead of descriptive strings
		 */

		if (!om_is_valid_failure_code(install_error)) {
			auto_log_print(gettext("Automated Installation failed"
			    " with unknown error code %d\n"), install_error);
		} else {
			char	*err_str;

			/* Where the failure happened */
			if ((err_str =
			    om_get_failure_source(install_error)) != NULL)
				auto_log_print(gettext("Automated Installation"
				    " failed in %s module\n"), err_str);

			/* Why the failure happened */
			if ((err_str =
			    om_get_failure_reason(install_error)) != NULL)
				auto_log_print(gettext("%s\n"), err_str);
		}
	} else {
		return_status = AUTO_INSTALL_SUCCESS;
	}

error_ret:	/* free all memory - may have jumped here upon error */
	if (proxy != NULL)
		free(proxy);
#ifndef	__sparc
	if (api != NULL)
		free(api);
#endif
	if (asi != NULL)
		free(asi);
	if (diskname != NULL)
		free(diskname);
	free_repo_info_list(default_ips_repo);
	free_repo_info_list(addl_ips_repo);
	if (install_attr != NULL)
		nvlist_free(install_attr);
	if (transfer_attr != NULL) {
		int i;

		for (i = 0; i <= ita; i++)
			if (transfer_attr[i] != NULL)
				nvlist_free(transfer_attr[i]);
		free(transfer_attr);
	}
	return (return_status);
}

/*
 * Install the target based on the specified diskname
 * or if no diskname is specified, install it based on
 * the criteria specified in ai.xml.
 *
 * Returns
 *	AUTO_INSTALL_SUCCESS on a successful install
 *	AUTO_INSTALL_FAILURE on a failed install
 */
static int
auto_perform_install(char *diskname)
{
	nvlist_t	*install_attr, *transfer_attr[2];
	int 		status;

	/*
	 * No disk specified on command line
	 *  - perform installation based on manifest information instead
	 */

	if (*diskname == '\0')
		return (install_from_manifest());

	/*
	 * Install to disk specified on command line
	 *
	 * Initiate target discovery and wait until it is finished
	 */

	if (auto_target_discovery() != AUTO_TD_SUCCESS) {
		auto_log_print(gettext("Automated installation failed in "
		    "Target Discovery module\n"));

		auto_log_print(gettext("Please see previous messages for more "
		    "details\n"));

		return (AUTO_INSTALL_FAILURE);
	}

	/*
	 * We're installing on the specified diskname
	 * Since this is usually called from a test
	 * program, we hardcode the various system
	 * configuration parameters
	 */

	if (auto_select_install_target(&diskname, NULL) != 0) {
		auto_log_print(gettext("Error: Target disk name %s is "
		    "not valid\n"), diskname);
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_alloc(&install_attr, NV_UNIQUE_NAME, 0) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "nvlist allocation failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_uint8(install_attr, OM_ATTR_INSTALL_TYPE,
	    OM_INITIAL_INSTALL) != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_INSTALL_TYPE failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_DISK_NAME,
	    diskname) != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_DISK_NAME failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_ROOT_PASSWORD,
	    om_encrypt_passwd("opensolaris", "root")) != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_ROOT_PASSWORD failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_USER_NAME,
	    "fool") != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_USER_NAME failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_USER_PASSWORD,
	    om_encrypt_passwd("ass", "fool")) != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_USER_PASSWORD failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_LOGIN_NAME,
	    "fool") != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_LOGIN_NAME failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_HOST_NAME,
	    "opensolaris") != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_HOST_NAME failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(install_attr, OM_ATTR_DEFAULT_LOCALE,
	    "C") != 0) {
		nvlist_free(install_attr);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_DEFAULT_LOCALE failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_alloc(&transfer_attr[0], NV_UNIQUE_NAME, 0) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "nvlist allocation failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_uint32(transfer_attr[0], TM_ATTR_MECHANISM,
	    TM_PERFORM_IPS) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_ATTR_MECHANISM failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_uint32(transfer_attr[0], TM_IPS_ACTION,
	    TM_IPS_INIT) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TMP_IPS_ACTION failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(transfer_attr[0], TM_IPS_INIT_MNTPT,
	    INSTALLED_ROOT_DIR) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_MNTPT failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(transfer_attr[0], TM_IPS_PKG_URL,
	    "http://ipkg.sfbay:10004") != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_URL failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(transfer_attr[0], TM_IPS_PKG_AUTH,
	    "ipkg.sfbay") != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_AUTH failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_alloc(&transfer_attr[1], NV_UNIQUE_NAME, 0) != 0) {
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "nvlist allocation failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_uint32(transfer_attr[1], TM_ATTR_MECHANISM,
	    TM_PERFORM_IPS) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		nvlist_free(transfer_attr[1]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_ATTR_MECHANISM failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_uint32(transfer_attr[1], TM_IPS_ACTION,
	    TM_IPS_RETRIEVE) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		nvlist_free(transfer_attr[1]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TMP_IPS_ACTION failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(transfer_attr[1], TM_IPS_INIT_MNTPT,
	    INSTALLED_ROOT_DIR) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		nvlist_free(transfer_attr[1]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_INIT_MNTPT failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (create_package_list_file(B_TRUE, AI_PACKAGE_LIST_INSTALL,
	    AUTO_INSTALL_PKG_LIST_FILE) != AUTO_INSTALL_SUCCESS) {
		auto_log_print(gettext("Failed to create a file with list "
		    "of packages to be installed\n"));
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_string(transfer_attr[1], TM_IPS_PKGS,
	    AUTO_INSTALL_PKG_LIST_FILE) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		nvlist_free(transfer_attr[1]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of TM_IPS_PKG_URL failed\n");
		return (AUTO_INSTALL_FAILURE);
	}

	if (nvlist_add_nvlist_array(install_attr, OM_ATTR_TRANSFER,
	    transfer_attr, 2) != 0) {
		nvlist_free(install_attr);
		nvlist_free(transfer_attr[0]);
		nvlist_free(transfer_attr[1]);
		auto_debug_print(AUTO_DBGLVL_INFO,
		    "Setting of OM_ATTR_TRANSFER failed\n");
		return (AUTO_INSTALL_FAILURE);
	}
	status = om_perform_install(install_attr, auto_update_progress);

	while (!install_done && !install_failed)
		sleep(10);

	nvlist_free(install_attr);
	nvlist_free(transfer_attr[0]);
	nvlist_free(transfer_attr[1]);

	if (install_failed || status != OM_SUCCESS)
		return (AUTO_INSTALL_FAILURE);
	else
		return (AUTO_INSTALL_SUCCESS);
}

/*
 * Function:	auto_get_disk_name_from_slice
 * Description: Convert a conventional disk name into the internal canonical
 *		form. Remove the trailing index reference. The return status
 *		reflects whether or not the 'src' name is valid.
 *
 *				src			 dst
 *			---------------------------------------
 *			[/dev/rdsk/]c0t0d0s0	->	c0t0d0
 *			[/dev/rdsk/]c0t0d0p0	->	c0t0d0
 *			[/dev/rdsk/]c0d0s0	->	c0d0
 *			[/dev/rdsk/]c0d0p0	->	c0d0
 *
 * Scope:	public
 * Parameters:	dst	- used to retrieve cannonical form of drive name
 *			  ("" if not valid)
 *		src	- name of drive to be processed (see table above)
 * Return:	 0	- valid disk name
 *		-1	- invalid disk name
 */
static void
auto_get_disk_name_from_slice(char *dst, char *src)
{
	char		name[MAXPATHLEN];
	char		*cp;

	*dst = '\0';

	(void) strcpy(name, src);
	/*
	 * The slice could be like s2 or s10
	 */
	cp = name + strlen(name) - 3;
	if (*cp) {
		if (*cp == 'p' || *cp == 's') {
			*cp = '\0';
		} else {
			cp++;
			if (*cp == 'p' || *cp == 's') {
				*cp = '\0';
			}
		}
	}

	/* It could be full pathname like /dev/dsk/disk_name */
	if ((cp = strrchr(name, '/')) != NULL) {
		cp++;
		(void) strcpy(dst, cp);
	} else {
		/* Just the disk name is provided, so return the name */
		(void) strcpy(dst, name);
	}
}

int
main(int argc, char **argv)
{
	int		opt;
	extern char 	*optarg;
	char		profile[MAXNAMELEN];
	char		diskname[MAXNAMELEN];
	char		slicename[MAXNAMELEN];
	int		num_du_pkgs_installed;
	boolean_t	auto_reboot_enabled = B_FALSE;
	nvlist_t	*ls_init_attr = NULL;
	boolean_t	auto_install_failed = B_FALSE;
	int		retries;

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	profile[0] = '\0';
	slicename[0] = '\0';
	while ((opt = getopt(argc, argv, "vd:Iip:")) != -1) {
		switch (opt) {
		case 'd': /* target disk name for testing only */
			(void) strlcpy(slicename, optarg, sizeof (slicename));
			break;
		case 'I': /* break after Target Instantiation for testing */
			om_set_breakpoint(OM_breakpoint_after_TI);
			break;
		case 'i': /* break before Target Instantiation for testing */
			om_set_breakpoint(OM_breakpoint_before_TI);
			break;
		case 'p': /* profile is provided */
			(void) strlcpy(profile, optarg, sizeof (profile));
			break;
		case 'v': /* debug verbose mode enabled */
			enable_debug_mode(B_TRUE);
			break;
		default:
			usage();
			exit(AI_EXIT_FAILURE);
		}
	}

	if (profile[0] == '\0' && slicename[0] == '\0') {
		usage();
		exit(AI_EXIT_FAILURE);
	}

	/*
	 * initialize logging service - increase verbosity level
	 * if installer was invoked in debug mode
	 * print error messages to stderr, since we don't have
	 * logging service available at this point
	 */
	if (is_debug_mode_enabled()) {
		if (nvlist_alloc(&ls_init_attr, NV_UNIQUE_NAME, 0) != 0) {
			(void) fprintf(stderr,
			    "nvlist allocation failed for ls_init_attrs\n");

			exit(AI_EXIT_FAILURE);
		}

		if (nvlist_add_int16(ls_init_attr, LS_ATTR_DBG_LVL,
		    LS_DBGLVL_INFO) != 0) {
			(void) fprintf(stderr,
			    "Setting LS_ATTR_DBG_LVL failed\n");

			nvlist_free(ls_init_attr);
			exit(AI_EXIT_FAILURE);
		}
	}

	if (ls_init(ls_init_attr) != LS_E_SUCCESS) {
		(void) fprintf(stderr,
		    "Couldn't initialize logging service\n");

		nvlist_free(ls_init_attr);
		exit(AI_EXIT_FAILURE);
	}

	/* release nvlist, since it is no longer needed */
	nvlist_free(ls_init_attr);

	if (profile[0] != '\0') {
		char	*ai_auto_reboot;

		/*
		 * We are passed in an AI manifest with an embedded
		 * SC manifest, both in DTD format. We want to
		 * separate them.
		 */
		if (auto_split_manifests(profile, AI_MANIFEST_FILE,
		    SC_MANIFEST_FILE) != AUTO_VALID_MANIFEST) {
			auto_log_print(gettext("Auto install failed. Invalid "
			    "manifest file %s specified\n"), profile);
			exit(AI_EXIT_FAILURE_AIM);
		}

		/*
		 * Validate the AI manifest. If it validates, set
		 * it up in an in-memory tree so searches can be
		 * done on it in the future to retrieve the values
		 */
		if (ai_create_manifest_image(AI_MANIFEST_FILE) ==
		    AUTO_VALID_MANIFEST) {
			auto_log_print(gettext("%s manifest created\n"),
			    profile);
		} else {
			auto_log_print(gettext("Auto install failed. Error "
			    "creating manifest %s\n"), profile);
			exit(AI_EXIT_FAILURE_AIM);
		}

		if (ai_setup_manifest_image() == AUTO_VALID_MANIFEST) {
			auto_log_print(gettext(
			    "%s manifest setup and validated\n"), profile);
		} else {
			char *setup_err = gettext("Auto install failed. Error "
			    "setting up and validating manifest %s\n");
			auto_log_print(setup_err, profile);
			(void) fprintf(stderr, setup_err, profile);
			exit(AI_EXIT_FAILURE_AIM);
		}

		/*
		 * Install any drivers required for installation, in the
		 * booted environment.
		 */

		/*
		 * First boolean: do not honor noinstall flag.
		 * Second boolean: do not update the boot archive.
		 */
		num_du_pkgs_installed =
		    ai_du_get_and_install("/", B_FALSE, B_FALSE);

		/*
		 * Note: Print no messages if num_du_pkgs_installed = 0
		 * This means no packages and no errors, or no-op.
		 */
		if (num_du_pkgs_installed > 0) {
			auto_log_print(gettext("All additional "
			    "driver packages successfully installed "
			    "to booted installation environment.\n"));
		} else if (num_du_pkgs_installed < 0) {
			char *du_warning = gettext("Warning: some additional "
			    "driver packages could not be installed\n"
			    "  to booted installation environment.\n"
			    "  These drivers may or may not be required for "
			    "the installation to proceed.\n"
			    "  Will continue anyway...\n");
			auto_log_print(du_warning);
			(void) fprintf(stderr, du_warning);
		}

		diskname[0] = '\0';

		/*
		 * Since valid manifest was provided, check if automated reboot
		 * feature is enabled.
		 */

		ai_auto_reboot = ai_get_manifest_element_value(AIM_AUTO_REBOOT);

		if (ai_auto_reboot != NULL) {
			if (strcasecmp(ai_auto_reboot, "true") == 0) {
				auto_log_print(
				    gettext("Auto reboot enabled\n"));

				auto_reboot_enabled = B_TRUE;
			} else {
				auto_log_print(
				    gettext("Auto reboot disabled\n"));
			}
		}
	}

	if (slicename[0] != '\0') {
		auto_get_disk_name_from_slice(diskname, slicename);
	}

	if (auto_perform_install(diskname) != AUTO_INSTALL_SUCCESS) {
		(void) fprintf(stderr, "Automated Installation failed\n");

		auto_install_failed = B_TRUE;
	} else {
		/*
		 * Install additional drivers on target.
		 * First boolean: honor noinstall flag.
		 * Second boolean: update boot archive.
		 */
		num_du_pkgs_installed =
		    ai_du_install(INSTALLED_ROOT_DIR, B_TRUE, B_TRUE);
		if (num_du_pkgs_installed < 0) {
			char *tgt_inst_err = gettext("Basic installation was "
			    "successful.  However, there was an error\n");
			auto_log_print(tgt_inst_err, profile);
			(void) fprintf(stderr, tgt_inst_err);
			tgt_inst_err = gettext("installing at least one "
			    "additional driver package on target.\n");
			auto_log_print(tgt_inst_err, profile);
			(void) fprintf(stderr, tgt_inst_err);
			tgt_inst_err = gettext("Please verify that all driver "
			    "packages required for reboot are installed "
			    "before rebooting.\n");
			auto_log_print(tgt_inst_err, profile);
			(void) fprintf(stderr, tgt_inst_err);
			auto_install_failed = B_TRUE;
		}
	}

	if (! auto_install_failed) {

		if (auto_reboot_enabled) {
			printf(gettext("Automated Installation succeeded."
			    " System will be rebooted now\n"));

			auto_log_print(gettext("Automated Installation"
			    " succeeded. System will be rebooted now\n"));
		} else {
			printf(gettext("Automated Installation succeeded. You"
			    " may wish to reboot the system at this time\n"));

			auto_log_print(gettext("Automated Installation"
			    " succeeded. You may wish to reboot the system"
			    " at this time\n"));
		}
	}

	(void) ai_teardown_manifest_state();

	/*
	 * Transfer /tmp/install_log file now that it is complete.
	 * Subsequent messages are not captured in copy of log file
	 * tranfered to destination.
	 */

	if (access(INSTALLED_ROOT_DIR, F_OK) == 0) {
		if (ls_transfer("/", INSTALLED_ROOT_DIR) != LS_E_SUCCESS) {
			auto_log_print(gettext(
			    "Could not transfer log file to the target\n"));
		}
	}

	/*
	 * If the installation failed, abort now and let the user inspect
	 * the system
	 */

	if (auto_install_failed)
		exit(AI_EXIT_FAILURE);

	/*
	 * Unmount installed boot environment
	 */
	if (om_unmount_target_be() != OM_SUCCESS) {
		auto_log_print(gettext(
		    "Could not unmount target boot environment.\n"));

		auto_install_failed = B_TRUE;
	}

	/*
	 * Exit with return codes reflecting the result of the installation:
	 *  AI_EXIT_SUCCESS - installation succeeded, don't reboot automatically
	 *  AI_EXIT_AUTO_REBOOT - installation succeeded, reboot automatically
	 */

	if (auto_install_failed)
		exit(AI_EXIT_FAILURE);

	if (auto_reboot_enabled)
		exit(AI_EXIT_AUTO_REBOOT);

	exit(AI_EXIT_SUCCESS);
}