usr/src/lib/liborchestrator/target_discovery.c
author Alasdair Lumsden <al@everycity.co.uk>
Sat, 10 Sep 2011 02:50:50 +0000
branchoi_151a
changeset 1430 398513b48d4c
parent 719 79c3fdbda756
permissions -rw-r--r--
1490 oi_151a installer Territory dropdown contains blank entries

/*
 * 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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/types.h>
#include <libnvpair.h>
#include "td_api.h"
#include "libdiskmgt.h"

#include "orchestrator_private.h"

static int offset_compare(const void *p1, const void *p2);
static	char	*get_solaris_release_string(nvlist_t *list, int slice);
static	char	*get_not_upgradeable_zone_list(nvlist_t *list);
static	char	*get_svm_components(nvlist_t *attr_list);

/*
 * start_td_disk_discovery
 * This function will call the target discovery module and populate
 * Orchestrator cache.
 * Output:	The number of devices discovered will be returned.
 * Return:	OM_SUCCESS, if the target discovery can be started successfully
 *		td_error, if the target discovery can't be started.
 */

int
start_td_disk_discover(int *ndisks)
{
	td_errno_t	ret;
	int		num;

	ret = td_discover(TD_OT_DISK, &num);
	if (ret) {
		/*
		 * If there are no disks on the system, return success
		 * so that orchestrator can set the correct error number
		 * for no disks
		 */
		if (ret == TD_E_NO_DEVICE) {
			om_debug_print(OM_DBGLVL_ERR,
			    "No disks on the system\n");
		} else {
			om_debug_print(OM_DBGLVL_ERR, "Can't discover disks\n");
			return (ret);
		}
	}

	*ndisks = num;
	return (OM_SUCCESS);
}

/*
 * get_td_disk_info_discover
 * This function will get the disk information from TD module and populate
 * Orchestrator cache.
 * Input:	Number of devices discovered
 *		Callback function to report progress to the caller
 * Output:	Number of disks that can be read
 * Return:	Linked list of disk_target_t with disk information filled out.
 */
disk_target_t *
get_td_disk_info_discover(int *ndisks, om_callback_t cb)
{
	om_callback_info_t	cb_data;
	uintptr_t		app_data = 0;
	int			i;
	int			num, bad;
	disk_target_t		*disks, *dt, *tmpdt;

	num = *ndisks;
	bad = 0;
	tmpdt = NULL;
	disks = NULL;

	/*
	 * if passed number of disks is not positive, there is no point
	 * in continuing the disk discovery
	 */
	if (num <= 0) {
		om_set_error(OM_NO_DISKS_FOUND);
		return (NULL);
	}

	/*
	 * Initialize the fixed values for disk info discover callback
	 */
	if (cb != NULL) {
		cb_data.callback_type = OM_TARGET_TARGET_DISCOVERY;
		cb_data.num_milestones = 4;
		cb_data.curr_milestone = OM_DISK_DISCOVERY;
	}

	for (i = 1; i <= num; i++) {
		/*
		 * Get the disk information
		 */
		dt = enumerate_next_disk();
		/*
		 * We are not getting disk_info for a disk
		 * So don't count this disk.
		 */
		if (dt == NULL) {
			bad++;
			continue;
		}
		/*
		 * Link the node to the end of the disk_target list
		 */
		if (tmpdt == NULL) {
			tmpdt = dt;
			disks = dt;
		} else {
			tmpdt->next = dt;
			tmpdt = tmpdt->next;
		}
		/*
		 * Issue a callback, if the callback function is given
		 */
		if (cb != NULL) {
			cb_data.percentage_done = (i*100)/num;
			cb(&cb_data, app_data);
		}
	}
	*ndisks = num - bad;
	if (tmpdt != NULL) {
		tmpdt->next = NULL;
	}

	/*
	 * If percentage_done is not 100%, send a callback to indicate
	 * that it is completed.
	 */
	if (cb != NULL && cb_data.percentage_done != 100) {
		cb_data.percentage_done = 100;
		cb(&cb_data, app_data);
	}
	return (disks);
}

/*
 * get_td_disk_parts_discover
 * This function discovers the partition information for each disk and populate
 * Orchestrator cache.
 * Input:	disk_target_t *, the list of disks discovered
 *		Callback function to report progress to the caller
 * Output:	The disk partitions of each of the disk in the system
 *		is discovered and saved in orchestartor cache
 * Return:	None
 */
void
get_td_disk_parts_discover(disk_target_t *disks, om_callback_t cb)
{
	om_callback_info_t	cb_data;
	uintptr_t		app_data = 0;
	disk_target_t		*dt;
	int			i;

	if (disks == NULL) {
		return;
	}

	/*
	 * Initialize the fixed values for disk parition discover callback
	 */
	if (cb != NULL) {
		cb_data.callback_type = OM_TARGET_TARGET_DISCOVERY;
		cb_data.num_milestones = 4;
		cb_data.curr_milestone = OM_PARTITION_DISCOVERY;
	}

	for (dt = disks, i = 1; dt != NULL; dt = dt->next, i++) {
		/*
		 * Now get the partitions for this disk
		 */
		dt->dparts = enumerate_partitions(dt->dinfo.disk_name);
		/*
		 * Issue a callback, if the callback function is given
		 */
		if (cb != NULL) {
			cb_data.percentage_done = (i*100)/disks_total;
			cb(&cb_data, app_data);
		}
	}
	/*
	 * If percentage_done is not 100%, send a callback to indicate
	 * that it is completed.
	 */
	if (cb != NULL && cb_data.percentage_done != 100) {
		cb_data.percentage_done = 100;
		cb(&cb_data, app_data);
	}
}

/*
 * get_td_disk_slices_discover
 * This function discovers the VTOC slices information for each disk
 * and populate Orchestrator cache.
 * Input:	disk_target_t *, the list of disks discovered
 * Output:	The disk slices of each of the disk in the system
 *		is discovered and saved in orchestartor cache
 * Return:	None
 */
void
get_td_disk_slices_discover(disk_target_t *disks, om_callback_t cb)
{
	om_callback_info_t	cb_data;
	uintptr_t		app_data = 0;
	disk_target_t		*dt;
	int			i;

	if (disks == NULL) {
		return;
	}

	/*
	 * Initialize the fixed values for disk parition discover callback
	 */
	if (cb != NULL) {
		cb_data.callback_type = OM_TARGET_TARGET_DISCOVERY;
		cb_data.num_milestones = 4;
		cb_data.curr_milestone = OM_SLICE_DISCOVERY;
	}

	for (dt = disks, i = 1; dt != NULL; dt = dt->next, i++) {
		/*
		 * Now get the partitions for this disk
		 */
		dt->dslices = enumerate_slices(dt->dinfo.disk_name);
		/*
		 * Issue a callback, if the callback function is given
		 */
		if (cb != NULL) {
			cb_data.percentage_done = (i*100)/disks_total;
			cb(&cb_data, app_data);
		}
	}
	/*
	 * If percentage_done is not 100%, send a callback to indicate
	 * that it is completed.
	 */
	if (cb != NULL && cb_data.percentage_done != 100) {
		cb_data.percentage_done = 100;
		cb(&cb_data, app_data);
	}
}

/*
 * get_td_solaris_instances
 * This function will get the solaris instances found on the system
 * from TD module and populate Orchestrator cache.
 * Input:	None
 * Output:	None
 * Return:	Linked list of upgrade_info_t pointers with solaris instances
 *		information.
 */
upgrade_info_t *
get_td_solaris_instances(om_callback_t cb)
{
	om_callback_info_t	cb_data;
	uintptr_t		app_data = 0;
	int			ret, num;
	int			i;
	upgrade_info_t		*ut, *tmput;
	upgrade_info_t		*instances;

	/*
	 * Initialize the fixed values for disk info discover callback
	 */
	if (cb != NULL) {
		cb_data.callback_type = OM_TARGET_TARGET_DISCOVERY;
		cb_data.num_milestones = 4;
		cb_data.curr_milestone = OM_UPGRADE_TARGET_DISCOVERY;
	}

	tmput = NULL;
	instances = NULL;
	ret = td_discover(TD_OT_OS, &num);
	if (ret < 0 || num <= 0) {
		/*
		 * send a callback to indicate that it is over
		 */
		if (cb != NULL) {
			cb_data.percentage_done = 100;
			cb(&cb_data, app_data);
		}
		return (NULL);
	}

	for (i = 0; i < num; i++) {
		/*
		 * Get the disk information
		 */
		ut = enumerate_next_instance();
		/*
		 * We are not getting instance information for an instance
		 */
		if (ut == NULL) {
			continue;
		}
		/*
		 * Link the node to the end of the disk_target list
		 */
		if (tmput == NULL) {
			tmput = ut;
			instances = ut;
		} else {
			tmput->next = ut;
			tmput = tmput->next;
		}
		/*
		 * Issue a callback, if the callback function is given
		 */
		if (cb != NULL) {
			cb_data.percentage_done = (i*100)/num;
			cb(&cb_data, app_data);
		}
	}
	if (tmput != NULL) {
		tmput->next = NULL;
	}
	/*
	 * If percentage_done is not 100%, send a callback to indicate
	 * that it is completed.
	 */
	if (cb != NULL && cb_data.percentage_done != 100) {
		cb_data.percentage_done = 100;
		cb(&cb_data, app_data);
	}
	return (instances);
}

/*
 * send_discovery_complete_callback
 * This function sends a callback to indicate that the target discovery
 * is completed. This is needed if there are no disks discovered.
 * Input:	None
 * Output:	None
 * Return:	None
 */
void
send_discovery_complete_callback(om_callback_t cb)
{
	om_callback_info_t	cb_data;
	uintptr_t		app_data = 0;

	/*
	 * Send a callbacks to indicate all discovery is done
	 * OM_UPGRADE_TARGET_DISCOVERY is the last milestone.
	 */
	if (cb != NULL) {
		cb_data.callback_type = OM_TARGET_TARGET_DISCOVERY;
		cb_data.num_milestones = 4;
		cb_data.curr_milestone = OM_UPGRADE_TARGET_DISCOVERY;
		cb_data.percentage_done = 100;
		cb(&cb_data, app_data);
	}
}

/*
 * enumerate_next_disk
 * This function will get the disk information from TD module.
 * Input:	None
 * Output:	None
 * Return:	Pointer to disk_target_t with disk information for the next
 *		available disk from the Target Discovery.
 */
disk_target_t *
enumerate_next_disk()
{
	nvlist_t	*attr_list;
	td_errno_t	ret;
	char		*str;
	uint32_t	bsize;
	uint32_t	value;
	uint32_t	nsect;
	uint32_t	nheads;
	uint64_t	nblocks;
	disk_target_t	*dt;

	/*
	 * Call the function to get the next disk from Target discovery
	 * module
	 */
	ret = td_get_next(TD_OT_DISK);

	if (ret != 0) {
		return (NULL);
	}

	/*
	 * Get the list of attributes available for this disk
	 */
	attr_list = td_attributes_get(TD_OT_DISK);

	if (attr_list == NULL) {
		return (NULL);
	}

	/*
	 * if the device type is not FIXED, ignore the entry
	 * we don't want to count cdrom, floopy etc.
	 */
	if (nvlist_lookup_uint32(attr_list, TD_DISK_ATTR_MTYPE,
	    &value) == OM_SUCCESS) {
		if (value != TD_MT_FIXED) {
			nvlist_free(attr_list);
			return (NULL);
		}
	} else {
		nvlist_free(attr_list);
		return (NULL);
	}

	/*
	 * We got a disk. Now allocate space for disk_target_t
	 */
	dt = (disk_target_t *)calloc(1, sizeof (disk_target_t));
	if (dt == NULL) {
		nvlist_free(attr_list);
		return (NULL);
	}

	dt->dparts = NULL;
	dt->dslices = NULL;

	/*
	 * Get the disk name
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_DISK_ATTR_NAME, &str) == OM_SUCCESS) {
		dt->dinfo.disk_name = strdup(str);
	} else {
		dt->dinfo.disk_name = strdup("");
	}
	if (dt->dinfo.disk_name == NULL) {
		om_set_error(OM_NO_SPACE);
		goto end_return;
	}

	/*
	 * Get volume name. It is optional. If not available, disk_volname
	 * will remain NULL. calloc(3C) above took care of the intialization.
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_DISK_ATTR_VOLNAME, &str) == OM_SUCCESS) {
		dt->dinfo.disk_volname = strdup(str);

		if (dt->dinfo.disk_volname == NULL) {
			om_set_error(OM_NO_SPACE);
			goto end_return;
		}
	}

	/*
	 * Get device ID
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_DISK_ATTR_DEVID, &str) == OM_SUCCESS) {
		dt->dinfo.disk_devid = strdup(str);

		if (dt->dinfo.disk_devid == NULL) {
			om_set_error(OM_NO_SPACE);
			goto end_return;
		}
	}

	/*
	 * Get device path
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_DISK_ATTR_DEVICEPATH, &str) == OM_SUCCESS) {
		dt->dinfo.disk_device_path = strdup(str);

		if (dt->dinfo.disk_device_path == NULL) {
			om_set_error(OM_NO_SPACE);
			goto end_return;
		}
	}

	/*
	 * Get the disk type (controller type)
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_DISK_ATTR_CTYPE, &str) == OM_SUCCESS) {
		dt->dinfo.disk_type = ctype_to_disktype_enum(str);
	} else {
		dt->dinfo.disk_type = OM_DTYPE_UNKNOWN;
	}

	/*
	 * Is the disk removable (like USB)
	 */
	if (nvlist_lookup_boolean(attr_list,
	    TD_DISK_ATTR_REMOVABLE) == OM_SUCCESS) {
		dt->dinfo.removable = B_TRUE;
	} else {
		dt->dinfo.removable = B_FALSE;
	}

	/*
	 * Get the disk label
	 */
	if (nvlist_lookup_uint32(attr_list, TD_DISK_ATTR_LABEL,
	    &value) == OM_SUCCESS) {
		if (value & TD_DISK_LABEL_VTOC) {
			dt->dinfo.label = OM_LABEL_VTOC;
		} else if (value & TD_DISK_LABEL_GPT) {
			dt->dinfo.label = OM_LABEL_GPT;
		} else if (value & TD_DISK_LABEL_FDISK) {
			dt->dinfo.label = OM_LABEL_FDISK;
		} else {
			dt->dinfo.label = OM_LABEL_UNKNOWN;
		}
	} else {
		dt->dinfo.label = OM_LABEL_UNKNOWN;
	}

	/*
	 * Calculate the total size of the disk
	 */
	if (nvlist_lookup_uint32(attr_list,
	    TD_DISK_ATTR_BLOCKSIZE, &bsize) != OM_SUCCESS) {
		bsize = 0;
	}

	if (nvlist_lookup_uint64(attr_list,
	    TD_DISK_ATTR_SIZE, &nblocks) != OM_SUCCESS) {
		nblocks = 0;
	}

	if (bsize == 0 || nblocks == 0) {
		om_log_print("Ignoring %s because of bad Geometry\n",
		    dt->dinfo.disk_name);
		goto end_return;
	}

	dt->dinfo.disk_size_sec = (bsize * nblocks)/512;
	dt->dinfo.disk_size_total = dt->dinfo.disk_size =
	    (bsize * nblocks)/ONEMB;

	/*
	 * If disk is too big, adjust usable disk size
	 */
	if (dt->dinfo.disk_size > om_get_max_usable_disk_size()) {
		dt->dinfo.disk_size = om_get_max_usable_disk_size();

		/*
		 * display in MB for debugging purposes, but post log
		 * message using more convenient GB
		 */

		om_debug_print(OM_DBGLVL_WARN,
		    "Disk %s is too big (%luMiB),"
		    " usable size will be adjusted to %luMiB\n",
		    dt->dinfo.disk_name, dt->dinfo.disk_size_total,
		    dt->dinfo.disk_size);

		om_log_print(
		    "Disk %s is too big (%luGiB),"
		    " usable size will be adjusted to %luGiB\n",
		    dt->dinfo.disk_name,
		    dt->dinfo.disk_size_total / ONE_GB_TO_MB,
		    dt->dinfo.disk_size / ONE_GB_TO_MB);
	}

	/*
	 * Calculate the size of 1 cylinder in blocks/sectors
	 * One sector = 512 bytes
	 * 1 cylinder = (number of sectors per track) * (number of heads)
	 */
	if (nvlist_lookup_uint32(attr_list,
	    TD_DISK_ATTR_NHEADS, &nheads) != OM_SUCCESS) {
		dt->dinfo.disk_cyl_size = 0;
		om_log_print("Ignoring %s because of bad Geometry\n",
		    dt->dinfo.disk_name);

		goto end_return;
	}

	if (nvlist_lookup_uint32(attr_list,
	    TD_DISK_ATTR_NSECTORS, &nsect) != OM_SUCCESS) {
		dt->dinfo.disk_cyl_size = 0;
		om_log_print("Ignoring %s because of bad Geometry\n",
		    dt->dinfo.disk_name);

		goto end_return;
	}

	dt->dinfo.disk_cyl_size = nsect * nheads;

	/*
	 * Set whether the disk is the default boot disk
	 */
	if (nvlist_lookup_boolean(attr_list,
	    TD_DISK_ATTR_CURRBOOT) == OM_SUCCESS) {
		dt->dinfo.boot_disk = B_TRUE;
	} else {
		dt->dinfo.boot_disk = B_FALSE;
	}

	/*
	 * Get the manufacturer
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_DISK_ATTR_VENDOR, &str) == OM_SUCCESS) {
		dt->dinfo.vendor = strdup(str);
	} else {
		dt->dinfo.vendor = strdup(OM_UNKNOWN_STRING);
	}
	if (dt->dinfo.vendor == NULL) {
		om_set_error(OM_NO_SPACE);
		goto end_return;
	}

	/*
	 * Currently Target discovery can't get the disk serial number
	 */
	dt->dinfo.serial_number = strdup(OM_UNKNOWN_STRING);
	if (dt->dinfo.serial_number == NULL) {
		om_set_error(OM_NO_SPACE);
		goto end_return;
	}
	/*
	 * we are done. Free the nvpair list and return the disk
	 */
	nvlist_free(attr_list);
	return (dt);
end_return:
	nvlist_free(attr_list);
	free(dt->dinfo.serial_number);
	free(dt->dinfo.vendor);
	free(dt->dinfo.disk_name);
	free(dt);
	return (NULL);
}

static void
log_partition_map(partition_info_t *pinfo)
{
	int ipar;

om_debug_print(OM_DBGLVL_INFO, "log_partition_map - after sorting\n");
	om_debug_print(OM_DBGLVL_INFO,
	    "id\ttype\torder\tsector offset\tsize in sectors\n");
	for (ipar = 0; ipar < OM_NUMPART; ipar++) {
		om_debug_print(OM_DBGLVL_INFO, "%d\t%02X\t%2d\t%lld\t%lld\n",
		    pinfo[ipar].partition_id,
		    pinfo[ipar].partition_type,
		    pinfo[ipar].partition_order,
		    pinfo[ipar].partition_offset_sec,
		    pinfo[ipar].partition_size_sec);
	}
}

/*
 * enumerate_partitions
 * This function will get all the partitions of a disk from TD module
 * Input:	char *diskname, the name of the disk, whose partitions are
 *		required.
 * Output:	None
 * Return:	disk_parts_t *, pointer to disk partition information
 *		of the requested disk.
 */
disk_parts_t *
enumerate_partitions(char *disk_name)
{
	nvlist_t	**attr_list, **save_attr_list;
	int		i;
	int		num, bad;
	uint8_t		part, part_id;
	char		*str;
	char		*ptr;
	uint32_t	value;
	disk_parts_t	*dp;

	/*
	 * Partitions are defined only for X86
	 */
	if (!is_system_x86()) {
		return (NULL);
	}

	if (disk_name == NULL) {
		return (NULL);
	}

	/*
	 * Get all the partitions of this disk
	 */
	attr_list = td_discover_partition_by_disk(disk_name, &num);
	if (num <= 0) {
		if (attr_list != NULL) {
			td_attribute_list_free(attr_list);
		}
		return (NULL);
	}

	/*
	 * save the attr list so that it can be freed later.
	 */
	save_attr_list = attr_list;
	/*
	 * We found some partitions, allocate space
	 */
	dp = (disk_parts_t *)calloc(1, sizeof (disk_parts_t));
	if (dp == NULL) {
		om_set_error(OM_NO_SPACE);
		goto enp_return;
	}

	/*
	 * Set the disk name
	 */
	dp->disk_name = strdup(disk_name);
	if (dp->disk_name == NULL) {
		om_set_error(OM_NO_SPACE);
		goto enp_return;
	}

	bad = 0;
	for (i = 0; i < num; i++, attr_list++) {
		/*
		 * If can't get the partition name, ignore that partition.
		 */
		if (nvlist_lookup_string(*attr_list,
		    TD_PART_ATTR_NAME, &str) != OM_SUCCESS) {
			bad++;
			continue;
		}

		/*
		 * If the partition name is not ending with pX
		 * ignore this partition
		 */
		ptr = str + strlen(str) - 2;
		if (*ptr == 'p') {
			ptr++;
		} else {
			bad++;
			om_debug_print(OM_DBGLVL_WARN, BAD_DISK_SLICE, str);
			om_log_print(BAD_DISK_SLICE, str);
			continue;
		}
		/*
		 * The partition will is of the form cXtXdXpX
		 */
		errno = 0;
		part_id = (int)strtol(ptr, (char **)NULL, 10);
		if (errno != 0 || part_id < 1 || part_id > OM_NUMPART) {
			/*
			 * Log the information
			 */
			bad++;
			om_debug_print(OM_DBGLVL_WARN, BAD_DISK_SLICE, str);
			om_log_print(BAD_DISK_SLICE, str);
			continue;
		}

		/*
		 * pinfo is an array with each element representing a partition
		 * in the range of 1-OM_NUMPART, which includes all possible
		 * primary or logical partitions. pinfo is indexed by fdisk
		 * partition number (ctdpN), but zero-based,
		 * so device /dev/rdsk/ctdpN goes in pinfo[N-1],
		 *	and pinfo[N-1].partition_id = N
		 *
		 * NOTE: terribly confusing struct element misnomers with
		 *	respect to the fdisk man page:
		 * - the N in ctdpN corresponds to struct element partition_id
		 * - the fdisk ID corresponds to struct element partition_type
		 * - struct element partition_id != fdisk ID!!!
		 */

		part = part_id - 1;
		dp->pinfo[part].partition_id = part_id;
		/*
		 * Get the bootable flag
		 */
		if (nvlist_lookup_uint32(*attr_list,
		    TD_PART_ATTR_BOOTID, &value) == OM_SUCCESS) {
			dp->pinfo[part].active =
			    value & ACTIVE ? B_TRUE : B_FALSE;
		} else {
			dp->pinfo[part].active = B_FALSE;
		}

		/*
		 * Get the partition type
		 */
		if (nvlist_lookup_uint32(*attr_list,
		    TD_PART_ATTR_TYPE, &value) == OM_SUCCESS) {
			dp->pinfo[part].partition_type = (uint8_t)value;
		} else {
			dp->pinfo[part].partition_type = 0;
		}

		/*
		 * Get the content type
		 */
		if (nvlist_lookup_uint32(*attr_list,
		    TD_PART_ATTR_CONTENT, &value) == OM_SUCCESS) {
			if (value == TD_PART_CONTENT_LSWAP) {
				dp->pinfo[part].content_type =
				    OM_CTYPE_LINUXSWAP;
			} else {
				dp->pinfo[part].content_type =
				    OM_CTYPE_UNKNOWN;
			}
		} else {
			dp->pinfo[part].content_type = OM_CTYPE_UNKNOWN;
		}

		/*
		 * Get the starting block
		 */
		if (nvlist_lookup_uint32(*attr_list,
		    TD_PART_ATTR_START, &value) == OM_SUCCESS) {
			dp->pinfo[part].partition_offset =
			    value/BLOCKS_TO_MB;

			dp->pinfo[part].partition_offset_sec = value;
		} else {
			dp->pinfo[part].partition_offset =
			    dp->pinfo[part].partition_offset_sec = 0;
		}

		/*
		 * The size value is number of blocks.
		 * Each block is 512 bytes.
		 * Get the size of the partition in MB
		 */
		if (nvlist_lookup_uint32(*attr_list,
		    TD_PART_ATTR_SIZE, &value) == OM_SUCCESS) {
			dp->pinfo[part].partition_size
			    = value/BLOCKS_TO_MB;

			dp->pinfo[part].partition_size_sec = value;
		} else {
			dp->pinfo[part].partition_size =
			    dp->pinfo[part].partition_size_sec = 0;
		}
	}
	/*
	 * We are finished with attr_list. Free the resources
	 */
	td_attribute_list_free(save_attr_list);

	/*
	 * Sort the disk partitions based on offset value.
	 * This will be useful for functions that validate the partitions
	 */
	num -= bad;
	sort_partitions_by_offset(dp, num);
	log_partition_map(dp->pinfo);
	return (dp);
enp_return:
	td_attribute_list_free(save_attr_list);
	local_free_part_info(dp);
	return (NULL);
}

/*
 * enumerate_slices
 * This function will get all the slices of a disk from TD module
 * Input:	char *diskname, the name of the disk, whose slices are
 *		required.
 * Output:	None
 * Return:	disk_slices_t *, pointer to disk slices information
 *		of the requested disk.
 * Note:	It is possible to have more than one solaris partitions in the
 *		case of X86 and hence more than one set of slices. Because of
 *		Name space issues these slices will have the same name. So we
 *		can return only one set of slices for each disk.
 *		If one of the solaris is an active partition, the slices
 *		corresponding to the partition is returned. If none of the
 *		partitions are active, the result is undefined.
 *		Having more than one solaris partition is considered to be
 *		illegal configuration.
 */
disk_slices_t *
enumerate_slices(char *disk_name)
{
	disk_slices_t	*ds;
	nvlist_t	**attr_list, **save_attr_list;
	int		i;
	int		num, bad;
	char		*str;
	uint32_t	value;
	uint64_t	big;

	/*
	 * start the VTOC slices discovery.
	 */

	if (disk_name == NULL) {
		return (NULL);
	}

	ds = NULL;
	/*
	 * Get all the partitions of this disk
	 */
	attr_list = td_discover_slice_by_disk(disk_name, &num);
	/*
	 * Save the attr_list since it will be advanced through the loop
	 */
	save_attr_list = attr_list;
	if (num > 0) {
		/*
		 * Found some slices. Allocate space
		 */
		ds = (disk_slices_t *)calloc(1, sizeof (disk_slices_t));
		if (ds == NULL) {
			om_set_error(OM_NO_SPACE);
			goto ens_return;
		}
		ds->disk_name = strdup(disk_name);
		if (ds->disk_name == NULL) {
			om_set_error(OM_NO_SPACE);
			goto ens_return;
		}
		/*
		 * How to get the partition_id?
		 */
		ds->partition_id = OM_PARTITION_UNKNOWN;

		bad = 0;

		/*
		 * Enumerate through the list of slices associated with given
		 * disk and save slice information in allocated disk_slices_t
		 * data structure. There is only space for NDKMAP slices, so
		 * make sure we don't overflow.
		 *
		 * Actually, if more than NDKMAP slices were reported as
		 * associated with given disk, something went wrong during
		 * target discovery - we shouldn't continue at this point
		 */
		if (num > NDKMAP) {
			om_debug_print(OM_DBGLVL_ERR,
			    "%d slices were reported as associated with disk %s"
			    " which is more than we could store (NDKMAP=%d)\n",
			    num, disk_name, NDKMAP);

			om_set_error(OM_TD_DISCOVERY_FAILED);
			goto ens_return;
		}

		for (i = 0; i < num; i++, attr_list++) {
			/*
			 * Get the slice name
			 */
			if (nvlist_lookup_string(*attr_list,
			    TD_SLICE_ATTR_NAME, &str) != OM_SUCCESS) {
				bad++;
				continue;
			}

			/*
			 * Get the slice number
			 */
			if (nvlist_lookup_uint32(*attr_list,
			    TD_SLICE_ATTR_INDEX, &value) == OM_SUCCESS) {
				ds->sinfo[i].slice_id = (uint8_t)value;
			} else {
				ds->sinfo[i].slice_id = OM_SLICE_UNKNOWN;
			}

			/*
			 * Get the starting block
			 */
			if (nvlist_lookup_uint64(*attr_list,
			    TD_SLICE_ATTR_START, &big) == OM_SUCCESS) {
				ds->sinfo[i].slice_offset = big;
			} else {
				ds->sinfo[i].slice_offset = 0;
			}

			/*
			 * Get the size of the slice in MB
			 */
			if (nvlist_lookup_uint64(*attr_list,
			    TD_SLICE_ATTR_SIZE, &big) == OM_SUCCESS) {
				ds->sinfo[i].slice_size = big;
			} else {
				ds->sinfo[i].slice_size = 0;
			}

			/*
			 * Get the slice flag
			 */
			if (nvlist_lookup_uint32(*attr_list,
			    TD_SLICE_ATTR_FLAG, &value) == OM_SUCCESS) {
				ds->sinfo[i].flags = (uint8_t)value;
			} else {
				ds->sinfo[i].flags = 0;
			}

			/*
			 * Get the slice tag
			 */
			if (nvlist_lookup_uint32(*attr_list,
			    TD_SLICE_ATTR_TAG, &value) == OM_SUCCESS) {
				ds->sinfo[i].tag = value;
			} else {
				ds->sinfo[i].tag = 0;
			}
		}
		/*
		 * We are finished with attr_list. Free the resources
		 */
		td_attribute_list_free(save_attr_list);
	}
	return (ds);
ens_return:
	free(ds->disk_name);
	free(ds);
	td_attribute_list_free(save_attr_list);
	return (NULL);
}

/*
 * enumerate_next_instance
 * This function will get the next upgrade target information from TD module.
 * Input:	None
 * Output:	None
 * Return:	Pointer to upgrade_info_t with Solaris instance information
 *		for the next available solaris instances from the Target
 *		Discovery module.
 */
upgrade_info_t *
enumerate_next_instance()
{
	nvlist_t	*attr_list;
	td_errno_t	ret;
	char		*str;
	char		*ptr = NULL;
	uint32_t	value;
	upgrade_info_t *ut;

	/*
	 * Call the function to get the next disk from Target discovery
	 * module
	 */
	ret = td_get_next(TD_OT_OS);

	if (ret) {
		return (NULL);
	}

	/*
	 * Get the list of attributes available for this disk
	 */
	attr_list = td_attributes_get(TD_OT_OS);

	if (attr_list == NULL) {
		return (NULL);
	}

	/*
	 * We got a disk. Now allocate space for disk_target_t
	 */
	ut = (upgrade_info_t *)calloc(1, sizeof (upgrade_info_t));
	if (ut == NULL) {
		nvlist_free(attr_list);
		return (NULL);
	}

	/*
	 * Assume the instance type is UFS for Dwarf
	 */
	ut->instance_type = OM_INSTANCE_UFS;
	ut->zones_installed = B_FALSE;
	ut->upgradable = B_TRUE;

	if (ut->instance_type == OM_INSTANCE_UFS) {
		ut->instance.uinfo.svm_configured = B_FALSE;
	}
	/*
	 * Get the slice name like c0d0s0.
	 * extract the disk and slice.
	 * If the diskname is not valid, return NULL
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_OS_ATTR_SLICE_NAME, &str) == OM_SUCCESS) {
		ut->instance.uinfo.disk_name = strdup(str);
		if (ut->instance.uinfo.disk_name == NULL) {
			goto eni_return;
		}
		if (just_the_disk_name(ut->instance.uinfo.disk_name, str) < 0) {
			goto eni_return;
		}
		/*
		 * slice could be single digit or two digits
		 */
		ptr = str + strlen(str) - 3;
		if (*ptr == 's') {
			ptr++;
		} else if (*(ptr+1) == 's') {
			ptr += 2;
		} else {
			goto eni_return;
		}
		errno = 0;
		ut->instance.uinfo.slice = strtol(ptr, (char **)NULL, 10);
		if (errno != 0) {
			goto eni_return;
		}
	} else {
		/*
		 * No upgrade target. We can't proceed
		 */
		om_set_error(OM_NO_UPGRADE_TARGET_NAME);
		goto eni_return;
	}

	/*
	 * Get the Release information
	 */
	ut->solaris_release = get_solaris_release_string(attr_list,
	    ut->instance.uinfo.slice);
	if (ut->solaris_release == NULL) {
		goto eni_return;
	}
	/*
	 * Non upgradeable zones if it is available
	 */
	ut->incorrect_zone_list = get_not_upgradeable_zone_list(attr_list);
	if (ut->incorrect_zone_list == NULL &&
	    om_get_error() == OM_NO_SPACE) {
		goto eni_return;
	}

	/*
	 * Get the upgradable attribute
	 */
	if (nvlist_lookup_uint32(attr_list, TD_OS_ATTR_NOT_UPGRADEABLE,
	    &value) == OM_SUCCESS) {
		if (value == 0) {
			ut->upgradable = B_TRUE;
		} else {
			ut->upgradable =  B_FALSE;
			ut->upgrade_message_id =
			    convert_td_value_to_om_upgrade_message(&value);
		}
	} else {
		/*
		 * not upgradable is not set by target discovery. So set the
		 * upgradable to true;
		 */
		ut->upgradable = B_TRUE;
	}

	/*
	 * Get the SVM related values from TD
	 */
	if (nvlist_lookup_string(attr_list,
	    TD_OS_ATTR_MD_COMPS, &str) == OM_SUCCESS) {
		ut->instance.uinfo.svm_configured = B_TRUE;
		ut->instance.uinfo.svm_info = get_svm_components(attr_list);
		if (ut->instance.uinfo.svm_info == NULL &&
		    om_get_error() == OM_NO_SPACE) {
			goto eni_return;
		}
	}
	/*
	 * we are done. Free the nvpair list and return the disk
	 */
	nvlist_free(attr_list);
	return (ut);
eni_return:
	nvlist_free(attr_list);
	free(ut->solaris_release);
	free(ut->instance.uinfo.disk_name);
	free(ut);
	return (NULL);
}

/*
 * ==================Private Functions ===============================
 */

/*
 * Local function to convert controller_type value returned by target discovery
 * module to disk_type enum
 */
om_disk_type_t
ctype_to_disktype_enum(char *ptr)
{
	if (ptr == NULL) {
		return (OM_DTYPE_UNKNOWN);
	}

	if (streq(ptr, DM_CTYPE_ATA)) {
		return (OM_DTYPE_ATA);
	}
	if (streq(ptr, DM_CTYPE_SCSI)) {
		return (OM_DTYPE_SCSI);
	}
	if (streq(ptr, DM_CTYPE_FIBRE)) {
		return (OM_DTYPE_FIBRE);
	}
	if (streq(ptr, DM_CTYPE_USB)) {
		return (OM_DTYPE_USB);
	}
	/*
	 * DM_CTYPE_SATA and DM_CTYPE_FIREWIRE not yet defined in libdiskmgt
	 * Once they are defined, we need to uncomment these lines
	 *
	 * if (streq(ptr, DM_CTYPE_SATA)) {
	 * return (OM_DTYPE_SATA);
	 * }
	 * if (streq(ptr, DM_CTYPE_FIREWIRE)) {
	 *	return (OM_DTYPE_FIREWIRE);
	 * }
	 */
	return (OM_DTYPE_UNKNOWN);
}

/*
 * Local function to convert TD module's "why an upgrade target cannot be
 * upgraded" Orchestrator defined upgrade message id.
 */
om_upgrade_message_t
convert_td_value_to_om_upgrade_message(uint32_t *value)
{
	struct	td_upgrade_fail_reasons	*reason;

	reason = (struct  td_upgrade_fail_reasons *)value;

	if (reason->root_not_mountable) {
		return (OM_UPGRADE_MOUNT_ROOT_FAILED);
	}
	if (reason->var_not_mountable) {
		return (OM_UPGRADE_MOUNT_VAR_FAILED);
	}
	if (reason->no_inst_release) {
		return (OM_UPGRADE_RELEASE_INFO_MISSING);
	}
	if (reason->no_cluster) {
		return (OM_UPGRADE_MISSING_CLUSTER_FILE);
	}
	if (reason->no_clustertoc) {
		return (OM_UPGRADE_MISSING_CLUSTERTOC_FILE);
	}
	if (reason->no_bootenvrc) {
		return (OM_UPGRADE_MISSING_BOOTENVRC_FILE);
	}
	if (reason->zones_not_upgradeable) {
		return (OM_UPGRADE_NG_ZONE_CONFIURE_PROBLEM);
	}
	if (reason->no_usr_packages) {
		return (OM_UPGRADE_INSTANCE_INCOMPLETE);
	}
	if (reason->no_version) {
		return (OM_UPGRADE_RELEASE_INFO_MISSING);
	}
	if (reason->svm_root_mirror) {
		return (OM_UPGRADE_INSTANCE_IS_MIRROR);
	}
	if (reason->wrong_metacluster) {
		return (OM_UPGRADE_WRONG_METACLUSTER);
	}
	if (reason->os_version_too_old) {
		return (OM_UPGRADE_RELEASE_NOT_SUPPORTED);
	}
	return (OM_UPGRADE_UNKNOWN_ERROR);
}

/*
 * This function takes a disk partitions (disk_partition_t *) and returns
 * a sorted disk partitions based on the way it is laid out in the disk
 * using the partition_offset value
 *
 * struct element partition_order will indicate the order, with the following
 * rules:
 * - Primary and extended partitions will be sorted first, with
 *	partition_order being assigned 1-FD_NUMPART
 * - Logical partitions follow, partition_order starting at FD_NUMPART+1
 */
void
sort_partitions_by_offset(disk_parts_t *dp_ptr, int num_part)
{
	disk_parts_t 	*dp;
	partition_info_t *pinfo[OM_NUMPART];
	int		i, j, partition_order;
	boolean_t	found_logical = B_FALSE;

	/*
	 * Copy the partition addresses to an array for qsort(3c)
	 */
	for (i = 0, j = 0; i < OM_NUMPART && j < num_part; i++)
		if (is_used_partition(&dp_ptr->pinfo[i]))
			pinfo[j++] = &dp_ptr->pinfo[i];
	/*
	 * Not ordered. Sort them
	 */
	qsort(&pinfo, j, sizeof (partition_info_t *), offset_compare);

	/*
	 * set partition order element, placing primary/extended first
	 * followed by any logical partitions at FD_NUMPART+1
	 */
	for (i = 0, partition_order = 1; i < j; i++) {
		if (IS_LOG_PAR(pinfo[i]->partition_id) && !found_logical) {
			found_logical = B_TRUE; /* once only */
			partition_order = FD_NUMPART + 1;
		}
		pinfo[i]->partition_order = partition_order++;
	}
}

/*
 * Comparison function for qsort use
 */
static int
offset_compare(const void *p1, const void *p2)
{
	partition_info_t *i = *((partition_info_t **)p1);
	partition_info_t *j = *((partition_info_t **)p2);

	/*
	 * logicals come after primaries
	 */
	if (IS_LOG_PAR(i->partition_id) && !IS_LOG_PAR(j->partition_id))
		return (1);
	if (!IS_LOG_PAR(i->partition_id) && IS_LOG_PAR(j->partition_id))
		return (-1);
	/*
	 * order by partition offset
	 */
	if (i->partition_offset_sec > j->partition_offset_sec)
		return (1);
	if (i->partition_offset_sec < j->partition_offset_sec)
		return (-1);
	return (0);
}
/*
 * Get the version string (like Solaris 11), minor number, and the build_id
 * from the TD module and create the solaris release string to be displayed
 * to the user
 * The release string could be of one of the following form:
 * 'Solaris 2.7'
 * 'Solaris Nevada snv_56'
 * 'Solaris Nevada SXDE 09/07'
 */
static char *
get_solaris_release_string(nvlist_t *list, int slice)
{
	int	size;
	char	*version = NULL;
	char	*minor = NULL;
	char	*build_id = NULL;
	char	*solaris_release;
	char	solaris_version[32];

	/*
	 * Get the Release information
	 */
	size = 1;

	if (nvlist_lookup_string(list, TD_OS_ATTR_VERSION, &version)
	    == OM_SUCCESS) {
		/*
		 * We can't say Solaris 11 since it is not yet released
		 * if it is 11, we change it to Solaris Express
		 */
		if (streq(version, "Solaris_11")) {
			(void) strcpy(solaris_version, "Solaris Express");
		} else {
			(void) strcpy(solaris_version, version);
		}
		size += strlen(solaris_version);
		if (nvlist_lookup_string(list, TD_OS_ATTR_VERSION_MINOR,
		    &minor) == OM_SUCCESS) {
			size += strlen(minor) + 1;
		}
		if (nvlist_lookup_string(list, TD_OS_ATTR_BUILD_ID,
		    &build_id) == OM_SUCCESS) {
			size += strlen(build_id) + 1;
		}

		/*
		 * Add the size for "(S<slice>) (slice - max 2 digits)
		 * like (S4)
		 */
		size += 6;
		solaris_release = (char *)malloc(size);
		if (solaris_release == NULL) {
			return (NULL);
		}
		(void) strcpy(solaris_release, solaris_version);
		if (minor != NULL) {
			(void) strcat(solaris_release, ".");
			(void) strcat(solaris_release, minor);
		}
		if (build_id != NULL) {
			(void) strcat(solaris_release, " ");
			(void) strcat(solaris_release, build_id);
		}
		(void) sprintf(solaris_release +
		    strlen(solaris_release), " (S%d)", slice);
	} else if (nvlist_lookup_string(list, TD_OS_ATTR_BUILD_ID,
	    &build_id) == OM_SUCCESS) {
		solaris_release = strdup(build_id);
	} else {
		solaris_release = strdup(OM_UNKNOWN_STRING);
		if (solaris_release == NULL) {
			return (NULL);
		}
	}
	return (solaris_release);
}

/*
 * get_not_upgradeable_zone_list
 * Get the list of non-global zones which have issues that prevent them
 * being upgraded.
 */
static char *
get_not_upgradeable_zone_list(nvlist_t *attr_list)
{
	char		**str_array;
	uint32_t 	value;
	char		*zone_list = NULL;
	int		size = 1;

	om_set_error(OM_SUCCESS);
	/*
	 * Non upgradeable zones if it is available
	 */
	if (nvlist_lookup_string_array(attr_list,
	    TD_OS_ATTR_ZONES_NOT_UPGRADEABLE,
	    &str_array, &value) == OM_SUCCESS) {
		char	**ptr;

		/*
		 * Find the total number of characters in the list of zones.
		 * we have to copy them in to a string and return to the caller
		 * Add 1 for the space between zones.
		 */
		for (ptr = str_array; *ptr != NULL; ptr++) {
			size += strlen(*ptr) + 1;
		}
		zone_list = calloc(1, size);
		if (zone_list == NULL) {
			om_set_error(OM_NO_SPACE);
			return (NULL);
		}
		for (ptr = str_array; *ptr != NULL; ptr++) {
			(void) strcat(zone_list, *ptr);
			(void) strcat(zone_list, " ");
		}
	}
	return (zone_list);
}

/*
 * get_svm_components
 * If the root is mirror, get the slices that is part of root mirror
 */
static char *
get_svm_components(nvlist_t *attr_list)
{
	char		**str_array;
	uint32_t 	value;
	char		*svm_components = NULL;
	int		size = 1;
	boolean_t	first_time = B_TRUE;

	om_set_error(OM_SUCCESS);
	/*
	 * Get svm components
	 */
	if (nvlist_lookup_string_array(attr_list,
	    TD_SLICE_ATTR_MD_COMPS, &str_array, &value) == OM_SUCCESS) {
		char	**ptr;

		/*
		 * Find the total number of characters in the list of
		 * components. we have to copy them in to a string and return
		 * to the caller Add 1 for the space between components.
		 */
		for (ptr = str_array; *ptr != NULL; ptr++) {
			size += strlen(*ptr) + 1;
		}
		svm_components = calloc(1, size);
		if (svm_components == NULL) {
			om_set_error(OM_NO_SPACE);
			return (NULL);
		}
		for (ptr = str_array; *ptr != NULL; ptr++) {
			if (first_time) {
				(void) strcpy(svm_components, *ptr);
				first_time = B_FALSE;
			} else {
				(void) strcat(svm_components, *ptr);
			}
			(void) strcat(svm_components, " ");
		}
	}
	return (svm_components);
}