OpenSolaris_b135/cmd/mms/mm/common/mm_task.c

Compare this file to the similar file:
Show the results in this format:

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


#include <sys/types.h>
#include <mms_list.h>
#include <mms_parser.h>
#include <libpq-fe.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <syslog.h>
#include <mms_trace.h>
#include <mms_strapp.h>
#include "mm_db.h"
#include "mm.h"
#include "mm_util.h"
#include "mm_sql.h"
#include "mm_commands.h"
#include "mm_sql.h"
#include "mm_sql_impl.h"
#include "mm_task.h"

static char *_SrcFile = __FILE__;

#define	MM_BE_ERROR 0
#define	MM_BE_DISPATCH 1
#define	MM_BE_BLOCKING 2
#define	MM_BE_OK 3

cmi_drive_list_t *
tm_return_drive_ptr(char *drive, cmi_cart_list_t *cart) {
	/* if drive is NULL return the 1st drive in the list */
	cmi_drive_list_t	*cur_drive = NULL;

	if (drive == NULL) {
		cur_drive = mms_list_head(&cart->cmi_drive_list);
		if (cur_drive == NULL) {
			mms_trace(MMS_ERR,
			    "1st drive was NULL");
		}
		return (cur_drive);

	}

	mms_list_foreach(&cart->cmi_drive_list, cur_drive) {
		if (strcmp(cur_drive->cmi_drive_name,
			drive) == 0) {
			return (cur_drive);
		}
	}
	return (NULL);
}

cmi_cart_list_t *
tm_return_cart_ptr(char *cart_id, cmd_mount_info_t *mount_info) {
	cmi_cart_list_t	*cur_cart;
	mms_list_foreach(&mount_info->cmi_cart_list, cur_cart) {
		if (strcmp(cur_cart->cmi_cart_id,
			cart_id) == 0) {
			return (cur_cart);
		}
	}
	return (NULL);
}


char *
tm_return_dm_name(char *drive_name, char *host, mm_data_t *mm_data) {
	/* Return the dm name for drive_name on host */
	mm_db_t			*db = &mm_data->mm_db_tm;
	char			*dm_name = NULL;
	if (mm_db_exec(HERE, db,
		    "select \"DMName\" from \"DM\" "
		    "where \"DM\".\"DriveName\" = '%s' and "
		    " pg_host_ident(\"DMTargetHost\") "
		    "= pg_host_ident('%s');",
		    drive_name, host) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "db error, tm_return_dm_name");
		return (NULL);
	}
	if (PQntuples(db->mm_db_results) != 1) {
		mms_trace(MMS_ERR,
		    "row num mismatch , tm_return_dm_name");
		return (NULL);
	}
	dm_name = mms_strapp(dm_name, PQgetvalue(db->mm_db_results, 0, 0));
	mm_clear_db(&db->mm_db_results);
	return (dm_name);
}

int
tm_can_dispatch_mount(mm_command_t *cmd, mm_data_t *mm_data)
{

	/* Use this function for both unmout and mounts */
	mm_db_t			*db = &mm_data->mm_db_tm;

	mm_wka_t		*mm_wka = cmd->wka_ptr;


	int			rc;

	mms_trace(MMS_DEVP,
	    "tm_can_dispatch_mount");

	rc = mm_mount_ready(mm_wka, cmd, db, 0);
	switch (rc) {

	case MM_MOUNT_ERROR:
		/* Error code should be set */
		mms_trace(MMS_ERR,
		    "internal error, mm_mount_ready");
		cmd->cmd_remove = 1;
		mm_send_text(mm_wka->mm_wka_conn, cmd->cmd_buf);
		return (0);

	case MM_MOUNT_READY:
		/* The mount is ready, mount_info should be set */
		/* continue to state 1 */
		cmd->cmd_state = 1;
		mms_trace(MMS_DEVP,
		    "mount is ready to go, "
		    "continue to state 1");
		MM_SET_FLAG(cmd->cmd_flags, MM_CMD_DISPATCHABLE);
		return (1);

	case MM_MOUNT_NEED_UNLOAD:
		/* this immediate mount needs to */
		/* wait for an unload */
		cmd->cmd_state = 1;
		mms_trace(MMS_DEVP,
		    "mount waiting "
		    "for unload to complete");
		return (1);

	case MM_MOUNT_NOT_READY:
		/* Error code should be set */
		mms_trace(MMS_ERR,
		    "blocking mount not ready, "
		    "wait longer and try later");
		return (0);

	default:
		mms_trace(MMS_ERR,
		    "bad rc mm_mount_ready");
		mm_system_error(cmd,
		    "bad rc mm_mount_ready");
		cmd->cmd_remove = 1;
		mm_send_text(mm_wka->mm_wka_conn, cmd->cmd_buf);
		return (0);
	}

}



PGresult* tm_get_inuse_carts(mm_command_t *cmd,  mm_data_t *mm_data) {
	/* returns ordered list of inuse candidate cartridges */
	/* for task_id */

	mm_db_t			*db = &mm_data->mm_db_tm;
	char			*task_id = cmd->cmd_uuid;

	if (mm_db_exec(HERE, db, "select \"CartridgeID\" "
			"from \"CARTRIDGE\" "
			"where \"CartridgeStatus\" = 'in use' "
			"and \"CartridgeID\" "
			"in (select distinct \"CartridgeID\" "
			"from \"TASKCARTRIDGE\" "
			"where \"TaskID\" = '%s') "
			"order by \"CartridgeNumberMounts\";",
			task_id) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "db_error, tm_get_inuse_carts");
		return (NULL);
	}
	return (db->mm_db_results);
}

int
tm_can_dispatch_unmount(mm_command_t *cmd, mm_data_t *mm_data)
{
	mm_db_t			*db = &mm_data->mm_db_tm;
	mm_wka_t		*mm_wka = cmd->wka_ptr;

	int			rc;

	mms_trace(MMS_DEVP,
	    "tm_can_dispatch_unmount");

	rc = mm_unmount_ready(mm_wka, cmd, db);
	switch (rc) {

	case MM_UNMOUNT_ERROR:
		/* Error code should be set */
		mms_trace(MMS_ERR,
		    "internal error, mm_unmount_ready");
		cmd->cmd_remove = 1;
		mm_send_text(mm_wka->mm_wka_conn, cmd->cmd_buf);
		return (0);

	case MM_UNMOUNT_READY:
		/* The unmount is ready, unmount_info should be set */
		/* continue to state 1 */
		cmd->cmd_state = 1;
		mms_trace(MMS_DEVP,
		    "unmount is ready to go, "
		    "continue to state 1");
		return (1);

	case MM_UNMOUNT_NOT_READY:
		/* Error code should be set */
		mms_trace(MMS_ERR,
		    "blocking unmount not ready, "
		    "wait longer and try later");
		return (0);
	default:
		mms_trace(MMS_ERR,
		    "bad rc mm_unmount_ready");
		mm_system_error(cmd,
		    "bad rc mm_unmount_ready");
		cmd->cmd_remove = 1;
		mm_send_text(mm_wka->mm_wka_conn, cmd->cmd_buf);
		return (0);
	}

}

int
mm_be_remove_non_ready(mm_command_t *cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;
	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;
	mms_list_foreach(&mount_info->cmi_cart_list, cart) {
		if (cart->cmi_cart_not_ready) {
			cart->cmi_remove_cart = 1;
		}
		mms_list_foreach(&cart->cmi_drive_list, drive) {
			if (drive->cmi_drive_not_ready) {
				drive->cmi_remove_drive = 1;
			}
		}
	}
	mm_mount_clean_candidates(cmd);
	return (0);
}

int
tm_be_unmount_ready(mm_wka_t *mm_wka, mm_command_t *cmd, mm_db_t *db,
		mm_command_t *end_cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;

	PGresult		*cart_results;
	int			cart_rows;

	int			found_cart_drive = 0;
	int			found_ready_cart_drive = 0;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;

	/* Do drive/cart/lib selection using path matching */
	if ((cart_results = mm_unmount_cart_results(mm_wka,
		cmd, db)) == NULL) {
		mm_system_error(end_cmd,
			"error getting candidate "
			"cartridge");
		/* No carts or error */
		return (MM_UNMOUNT_ERROR);
	}

	cart_rows = PQntuples(cart_results);
	mms_trace(MMS_DEVP, "Number of Cartridges is %d", cart_rows);
	if (cart_rows == 0) {
		/* No Cartridge Matches Error */
		mms_trace(MMS_INFO,
		    "match statment in mount "
		    "didn't match any cartridge/volumes");
		mm_response_error(end_cmd,
				ECLASS_EXPLICIT, ENOMATCH,
				MM_5052_MSG,
				NULL);
		mm_clear_db(&cart_results);
		return (MM_UNMOUNT_ERROR);
	}

	/* Create the list objects for */
	/* every cartridge/drive candidate */
	if (mm_mount_init_candidates(cmd, cart_results,
				    db)) {
		mms_trace(MMS_ERR,
		    "error initializing candidate lists");
		/* err buf should be set by mm_mount_init */
		/* so return and remove */
		mm_system_error(end_cmd,
				"error init candidate lists");
		return (MM_UNMOUNT_ERROR);
	}
	mms_trace(MMS_DEVP, "candidate list created, check availability ");

	/* Check the availability of the candidates */
	if (mm_mount_check_candidates(mm_wka, cmd,
				    db)) {
		mms_trace(MMS_ERR,
		    "error checking candidate lists");
		mm_system_error(end_cmd,
				"error checking candidate lists");
		return (MM_UNMOUNT_ERROR);
	}
	mms_trace(MMS_DEVP, "done checking list");

	/* Print mount information */
	mm_print_mount_summary(mm_wka, cmd);
	found_cart_drive = 0;
	found_ready_cart_drive = 0;

	mms_list_foreach(&mount_info->cmi_cart_list, cart) {
		mms_list_foreach(&cart->cmi_drive_list, drive) {
			found_cart_drive = 1;
			if ((cart->cmi_cart_not_ready == 0) &&
			    (drive->cmi_drive_not_ready == 0)) {
				found_ready_cart_drive = 1;
			}
		}
	}


	/* If there is a library or drive error */
	/* the error buff has already been set */
	if (found_cart_drive == 0) {
		mms_trace(MMS_ERR,
		    "No candidate "
		    "cartridge/library/drive "
		    "combination found");
		mm_response_error(end_cmd,
		    ECLASS_EXPLICIT,
		    "ENOSOLUTIONS",
		    MM_5105_MSG,
		    NULL);
		return (MM_UNMOUNT_ERROR);
	}
	if (found_ready_cart_drive == 0) {
		mms_trace(MMS_ERR,
		    "candidate "
		    "cartridge/library/drive "
		    "combination found, but is not ready");
		mm_response_error(end_cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);
		return (MM_UNMOUNT_NOT_READY);
	}

	return (MM_UNMOUNT_READY);

}

int
tm_be_mount_ready(mm_wka_t *mm_wka, mm_command_t *cmd, mm_db_t *db,
		mm_command_t *end_cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;

	PGresult		*cart_results;
	int			cart_rows;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;


	int			found_cart_drive = 0;
	int			found_ready_cart_drive = 0;

	char			*err_text = NULL;


	/* Need to set end_cmd buf correctly for any MM_MOUNT_ERROR return */


	/* Do drive/cart/lib selection using path matching */
	if ((cart_results = mm_mount_cart_results(mm_wka,
		cmd, db)) == NULL) {
		mm_system_error(end_cmd,
			"error getting candidate "
			"cartridge");
		/* No carts or error */
		return (MM_MOUNT_ERROR);
	}

	cart_rows = PQntuples(cart_results);
	mms_trace(MMS_DEVP, "Number of Cartridges is %d", cart_rows);

	if (cart_rows == 0) {
		/* No Cartridge Matches Error */
		mms_trace(MMS_INFO,
		    "match statment in mount "
		    "didn't match any cartridge/volumes");
		mm_response_error(end_cmd,
				ECLASS_EXPLICIT, ENOMATCH,
				MM_5052_MSG,
				NULL);
		mm_clear_db(&cart_results);
		return (MM_MOUNT_ERROR);
	}
	/* Create the list objects for */
	/* every cartridge/drive candidate */
	if (mm_mount_init_candidates(cmd, cart_results,
				    db)) {
		mms_trace(MMS_ERR,
		    "error initializing candidate lists");
		/* err buf should be set by mm_mount_init */
		/* so return and remove */
		mm_system_error(end_cmd,
				"error init candidate lists");
		mm_clear_db(&cart_results);
		return (MM_MOUNT_ERROR);
	}
	mms_trace(MMS_DEVP, "candidate list created, check availability ");
	mm_clear_db(&cart_results);

	/* Check the availability of the candidates */
	if (mm_mount_check_candidates(mm_wka, cmd,
				    db)) {
		mms_trace(MMS_ERR,
		    "error checking candidate lists");
		mm_system_error(end_cmd,
				"error checking candidate lists");
		return (MM_MOUNT_ERROR);
	}
	mms_trace(MMS_DEVP, "done checking list");

	/* Print mount information */
	mm_print_mount_summary(mm_wka, cmd);


	/*
	 * For blocking mounts,
	 * check that at least some cart/drive combo exists
	 * if that cart/drive exists, but is not ready, return not ready
	 *
	 * for immediate mounts, check that at least one combination exists
	 */

	found_cart_drive = 0;
	found_ready_cart_drive = 0;

	mms_list_foreach(&mount_info->cmi_cart_list, cart) {
		mms_list_foreach(&cart->cmi_drive_list, drive) {
			found_cart_drive = 1;
			if ((cart->cmi_cart_not_ready == 0) &&
			    (drive->cmi_drive_not_ready == 0)) {
				found_ready_cart_drive = 1;
			}
		}
	}


	/* If there is a library or drive error */
	/* the error buff has already been set */
	if (found_cart_drive == 0) {
		mms_trace(MMS_ERR,
		    "No candidate "
		    "cartridge/library/drive "
		    "combination found");

		/* If this cmd has a retry error class */
		/* Send a retry error */
		if (strcmp(cmd->cmd_eclass, ECLASS_RETRY) == 0) {
			/* This is a retry error */
			mms_trace(MMS_ERR,
			    "Error is from a retry error");

			mm_response_error(end_cmd,
			    ECLASS_RETRY,
			    "ETMPUNAVAIL",
			    MM_5104_MSG,
			    NULL);
			return (MM_MOUNT_ERROR);
		}
		err_text = mm_return_err_text(cmd->cmd_err_ptr);
		mm_response_error(end_cmd,
		    ECLASS_EXPLICIT,
		    "ENOSOLUTIONS",
		    MM_5110_MSG,
		    "err_text", err_text,
		    NULL);
		free(err_text);
		return (MM_MOUNT_ERROR);
	}
	if (found_ready_cart_drive == 0) {
		mms_trace(MMS_ERR,
		    "candidate "
		    "cartridge/library/drive "
		    "combination found, but is not ready");

		mm_response_error(end_cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);

		return (MM_MOUNT_NOT_READY);
	}

	return (MM_MOUNT_READY);
}

#define	TM_CANCEL_RESPONSE "response task[\"%s\"] cancelled;"

void
mm_cancel_cmd_buf(mm_command_t *cmd) {
	char			*buf = NULL;
	buf = mms_strnew(TM_CANCEL_RESPONSE, cmd->cmd_task);
	SQL_CHK_LEN(&cmd->cmd_buf, 0,
	    &cmd->cmd_bufsize, strlen(buf) + 1);
	strcpy(cmd->cmd_buf, buf);
	free(buf);
	buf = NULL;
	return;
no_mem:
	MM_ABORT_NO_MEM();
}

void
tm_be_cancel_all(mm_command_t *cmd) {
	mm_command_t		*cur_cmd;

	/* Send cancel for all commands in this group */
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		mm_cancel_cmd_buf(cur_cmd);

		mm_send_text(cur_cmd->wka_ptr->mm_wka_conn,
			cur_cmd->cmd_buf);
	}

}

int
tm_be_set_mount(mm_command_t *cmd, mm_db_t *db,
		mm_command_t *end_cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;

	char			*drive_to_unload = NULL;
	char			*lib_to_unload = NULL;



	/* cmd is the mount command */
	MM_SET_FLAG(cmd->cmd_flags,
		    MM_CMD_DISPATCHABLE);
	cmd->cmd_state = 1;

	/* This code is the same code as is in mm_set_immediate_mount */
	/* make a common function?? */
	if (mm_mount_candidate_loaded(cmd)) {
		mms_trace(MMS_DEVP,
		    "a candidate cartridge is loaded");
		mount_info->cmi_mount_type =
		    MM_CANDIDATE_LOADED;
	} else if (mm_mount_open_drive(cmd)) {
		mms_trace(MMS_DEVP,
		    "open drive found");
		mount_info->cmi_mount_type =
		    MM_OPEN_DRIVE;
	} else if (mm_mount_loaded_drive(cmd, db, &drive_to_unload,
				&lib_to_unload)) {
		/* 2 mount time */
		/* these mounts need an unmount, then mount */
		if (drive_to_unload == NULL &&
		    lib_to_unload == NULL) {
			mount_info->cmi_mount_type =
			    MM_UNMOUNT_DRIVE;
			mms_trace(MMS_DEVP,
			    "drive loaded with non-candidate "
			    "must unload 1st");
			/* Need to set up parent command */
			/* return as dispatch depend */
			mms_trace(MMS_DEVP,
			    "%s needs unload to complete first",
			    mount_info->cmi_drive);
			MM_UNSET_FLAG(cmd->cmd_flags,
			    MM_CMD_DISPATCHABLE);
		} else {
			mms_trace(MMS_DEVP,
			    "candidate loaded in non-candidate drive "
			    "must unload 1st");
			/* Need to set up parent command */
			/* return as dispatch depend */
			mms_trace(MMS_DEVP,
			    "%s needs unload to complete first",
			    drive_to_unload);
			mount_info->cmi_mount_type =
			    MM_UNMOUNT_CART;
			mount_info->cmi_first_drive =
			    strdup(drive_to_unload);
			mount_info->cmi_first_lib =
			    strdup(lib_to_unload);
			MM_UNSET_FLAG(cmd->cmd_flags,
			    MM_CMD_DISPATCHABLE);
			free(drive_to_unload);
			free(lib_to_unload);
		}
	} else if (mm_unmount_2_drive(cmd, db)) {
		/* 3 mount time  */
		/* Candidate cart is mounted, */
		/* the only candidate drive is loaded with non-candidate */
		/* need to unmount candidate cart, unmount candidate drive */
		/* then mount candidate cart/drive */
		MM_UNSET_FLAG(cmd->cmd_flags, MM_CMD_DISPATCHABLE);

	} else {
		mms_trace(MMS_ERR,
		    "no drives found due to other candidates");
		mm_set_least_severe(cmd);
		if (cmd->cmd_err_ptr == NULL) {
			mms_trace(MMS_ERR,
			    "cmd has no errors, set ENOMATCH");
			mm_response_error(end_cmd,
			    ECLASS_EXPLICIT, ENOMATCH,
			    MM_5052_MSG,
			    NULL);
		}
		return (MM_BE_ERROR);
	}

	/* Select Is done - print the results */
	mms_trace(MMS_DEVP, "Cart/Lib/Drive selection "
		"complete for task %s",
		cmd->cmd_uuid);
	mms_trace(MMS_DEVP, "Cartridge ID is %s",
		mount_info->cmi_cartridge);
	mms_trace(MMS_DEVP, "Library is %s",
		mount_info->cmi_library);
	mms_trace(MMS_DEVP, "Drive is %s",
		mount_info->cmi_drive);
	mms_trace(MMS_DEVP, "DM is %s",
		mount_info->cmi_dm);

	/* since this mount will work, clear the error flags for it */
	if (cmd->cmd_eclass != NULL) {
		free(cmd->cmd_eclass);
		cmd->cmd_eclass = NULL;
	}
	if (cmd->cmd_ecode != NULL) {
		free(cmd->cmd_ecode);
		cmd->cmd_ecode = NULL;
	}
	cmd->cmd_mount_info.cmi_mount_ok = 1;

	return (MM_BE_DISPATCH);
}

int
tm_be_set_unmount(mm_wka_t *mm_wka, mm_command_t *cmd,
	mm_command_t *end_cmd) {
	cmd_mount_info_t	*mount_info = &cmd->cmd_mount_info;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;

	char			*cur_cartid = NULL;
	char			*cur_library = NULL;
	char			*cur_dm = NULL;
	char			*cur_drive = NULL;

	/* cmd is the unmount command */

	cart = mms_list_head(&mount_info->cmi_cart_list);
	if (cart == NULL) {
		mm_response_error(end_cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);
		if (mm_wka->wka_begin_end.be_mode ==
		    ACCESS_MODE_BLOCKING) {
			return (MM_BE_BLOCKING);
		}
		mms_trace(MMS_ERR,
		    "unmount has no candidates"
		    " due to other unmount");
		return (MM_BE_ERROR);
	}
	cur_library = cart->cmi_library;
	cur_cartid = cart->cmi_cart_id;

	drive = mms_list_head(&cart->cmi_drive_list);
	if (drive == NULL) {
		mm_response_error(end_cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);
		if (mm_wka->wka_begin_end.be_mode ==
		    ACCESS_MODE_BLOCKING) {
			return (MM_BE_BLOCKING);
		}
		mms_trace(MMS_ERR,
		    "unmount has no candidates"
		    " due to other unmount");
		return (MM_BE_ERROR);
	}

	cur_drive = drive->cmi_drive_name;
	cur_dm = drive->cmi_dm_name;


	mm_set_mount_info_cart(cur_cartid,
			mount_info);
	mm_set_mount_info_drive(cur_drive,
			mount_info);
	mm_set_mount_info_dm(cur_dm,
			mount_info);
	mm_set_mount_info_library(cur_library,
			mount_info);

	/* Select Is done - print the results */
	mms_trace(MMS_DEVP, "Cart/Lib/Drive selection "
	    "complete for task %s",
	    cmd->cmd_uuid);
	mms_trace(MMS_DEVP, "Cartridge ID is %s",
	    mount_info->cmi_cartridge);
	mms_trace(MMS_DEVP, "Library is %s",
	    mount_info->cmi_library);
	mms_trace(MMS_DEVP, "Drive is %s",
	    mount_info->cmi_drive);
	mms_trace(MMS_DEVP, "DM is %s",
	    mount_info->cmi_dm);

	return (MM_BE_DISPATCH);
}

void
tm_be_rm_error_candidates(mm_command_t *cmd, mm_command_t *err_cmd) {
	/* remove candidates set in err_cmd */
	/* from the beginend candidatea list in cmd */

	mm_cmd_err_t	*err = err_cmd->cmd_err_ptr;
	mm_command_t		*cur_cmd;
	cmd_mount_info_t	*cur_mount_info;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;

	mms_list_t			*cart_list;

	int			seen_same = 0;
	mm_cmd_err_t		*cur_err = NULL;

	mms_trace(MMS_DEVP,
	    "tm_be_rm_error_candidates: ");
	if (err == NULL) {
		mms_trace(MMS_ERR,
		    "tm_be_rm_error_candidates: "
		    "error ptr set to NULL");
		return;
	}

	mms_trace(MMS_DEVP,
	    "  Error to remove:");
	mm_print_err(err);

	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		/* same command */
		if (cur_cmd == err_cmd) {
			seen_same = 1;
			continue;
		}
		if (seen_same == 0) {
			continue;
		}

		/* check cur_cmd's error */
		mms_list_foreach(&cur_cmd->cmd_err_list, cur_err) {
			if (mm_same_err(err, cur_err)) {
			cur_err->err_already_used = 1;
			mms_trace(MMS_DEVP,
			    "  matched an err");
			}
		}
		cur_cmd->cmd_err_ptr = NULL;
		mm_set_least_severe(cur_cmd);

		cur_mount_info = &cur_cmd->cmd_mount_info;
		cart_list = &cur_mount_info->cmi_cart_list;
		mms_list_foreach(cart_list, cart) {
			/* Check this cart */
			if ((err->retry_cart != NULL) &&
			    (cart->cmi_cart_id != NULL) &&
			    (strcmp(err->retry_cart,
			    cart->cmi_cart_id) == 0)) {
				cart->cmi_cart_used = 1;
				mms_trace(MMS_DEVP,
				    "tm_be_rm_error_candidates: "
				    "set a cart as used");
			}
			mms_list_foreach(&cart->cmi_drive_list, drive) {
				/* check this drive */
				if ((err->retry_drive != NULL) &&
				    (drive->cmi_drive_name != NULL) &&
				    (strcmp(err->retry_drive,
				    drive->cmi_drive_name) == 0)) {
					drive->cmi_drive_used = 1;
					mms_trace(MMS_DEVP,
					    "tm_be_rm_error_candidates: "
					    "set a drive as used");
				}
			}
		}
	}
}

void
tm_be_rm_mount_candidates(mm_command_t *cmd, mm_command_t *set_cmd) {
	/* remove candidates set in set_cmd */
	/* from the beginend candidatea list in cmd */
	mm_command_t		*cur_cmd;
	cmd_mount_info_t	*set_mount_info = &set_cmd->cmd_mount_info;
	cmd_mount_info_t	*cur_mount_info;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;

	mms_list_t			*cart_list;

	int			seen_same = 0;

	mms_trace(MMS_DEVP,
	    "set %s and %s as used candidates",
	    set_mount_info->cmi_cartridge,
	    set_mount_info->cmi_drive);
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		/* same command */
		if (cur_cmd == set_cmd) {
			seen_same = 1;
			continue;
		}
		if (seen_same == 0) {
			continue;
		}
		cur_mount_info = &cur_cmd->cmd_mount_info;
		cart_list = &cur_mount_info->cmi_cart_list;
		mms_list_foreach(cart_list, cart) {
			if (strcmp(set_mount_info->cmi_cartridge,
				cart->cmi_cart_id) == 0) {
				cart->cmi_cart_used = 1;
			}
			mms_list_foreach(&cart->cmi_drive_list, drive) {
				if (strcmp(set_mount_info->cmi_drive,
					drive->cmi_drive_name) == 0) {
					drive->cmi_drive_used = 1;
				}
			}
		}
	}
}

void
tm_be_rm_unmount_candidates(mm_command_t *cmd, mm_command_t *set_cmd) {
	/* remove candidates set in set_cmd */
	/* from the beginend candidatea list in cmd */
	mm_command_t		*cur_cmd;
	cmd_mount_info_t	*set_mount_info = &set_cmd->cmd_mount_info;
	cmd_mount_info_t	*cur_mount_info;
	cmi_cart_list_t		*cart = NULL;

	cmi_drive_list_t	*drive = NULL;


	mms_list_t			*cart_list;

	int			seen_same = 0;

	mms_trace(MMS_DEVP,
	    "remove %s and %s from candidate lists",
	    set_mount_info->cmi_cartridge,
	    set_mount_info->cmi_drive);
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_mount_cmd_func) {
			/* not an unmount */
			continue;
		}
		/* same command */
		if (cur_cmd == set_cmd) {
			seen_same = 1;
			continue;
		}
		if (seen_same == 0) {
			continue;
		}
		cur_mount_info = &cur_cmd->cmd_mount_info;
		cart_list = &cur_mount_info->cmi_cart_list;
		mms_list_foreach(cart_list, cart) {
			if (strcmp(set_mount_info->cmi_cartridge,
				cart->cmi_cart_id) == 0) {
				cart->cmi_remove_cart = 1;
			}
			mms_list_foreach(&cart->cmi_drive_list, drive) {
				if (strcmp(set_mount_info->cmi_drive,
					drive->cmi_drive_name) == 0) {
					drive->cmi_remove_drive = 1;
				}
			}
		}
		mm_mount_clean_candidates(cur_cmd);
	}
}

int
tm_be_is_in_unmount(char *drive_name, mm_command_t *end_cmd) {
	mm_command_t		*cur_cmd;

	mms_list_foreach(&end_cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_mount_cmd_func) {
			/* not an unmount */
			continue;
		}
		if (strcmp(cur_cmd->cmd_mount_info.cmi_drive,
			drive_name) == 0) {
			return (1);
		}
	}
	return (0);
}

int
tm_be_init_mount(mm_command_t *end_cmd, mm_db_t *db) {
	/* this function needs to initialize the mount cmd's */
	/* Candidate lists using the selected carts/drives */
	/* in the unmount commands */
	/* tm_be_match_mount will match mounts to unmounts */

	PGresult		*cart_results;
	int			cart_rows;

	mm_wka_t		*mm_wka = end_cmd->wka_ptr;

	mm_command_t		*cur_cmd;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;
	mms_list_t			*cart_list;

	mms_list_foreach(&end_cmd->cmd_beginend_list, cur_cmd) {
		/* Do drive/cart/lib selection using path matching */
		if ((cart_results = mm_mount_cart_results(mm_wka,
					cur_cmd, db)) == NULL) {
			mm_system_error(end_cmd,
				"error getting candidate "
				"cartridge");
			/* No carts or error */
			return (MM_BE_ERROR);
		}
		cart_rows = PQntuples(cart_results);
		mms_trace(MMS_DEVP, "Number of Cartridges is %d", cart_rows);

		if (cart_rows == 0) {
			/* No Cartridge Matches Error */
			mms_trace(MMS_INFO,
			    "match statment in mount "
			    "didn't match any cartridge/volumes");
			mm_response_error(end_cmd,
				ECLASS_EXPLICIT, ENOMATCH,
				MM_5052_MSG,
				NULL);
			mm_clear_db(&cart_results);
			return (MM_BE_ERROR);
		}

		/* Create the list objects for */
		/* every cartridge/drive candidate */
		if (mm_mount_init_candidates(cur_cmd, cart_results,
				db)) {
			mms_trace(MMS_ERR,
			    "error initializing candidate lists");
			/* err buf should be set by mm_mount_init */
			/* so return and remove */
			mm_system_error(end_cmd,
				"error init candidate lists");
			mm_clear_db(&cart_results);
			return (MM_BE_ERROR);
		}
		mm_clear_db(&cart_results);
	}

	mms_trace(MMS_DEVP,
	    "keep only drive candidates selected in unmount cmd");
	/* Keep only the drive candidates selected in a unmount command */
	mms_list_foreach(&end_cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_unmount_cmd_func) {
			/* not an mount */
			continue;
		}
		cart_list = &cur_cmd->cmd_mount_info.cmi_cart_list;
		mms_list_foreach(cart_list, cart) {
			mms_list_foreach(&cart->cmi_drive_list, drive) {
				mms_trace(MMS_DEVP,
				    "  check if %s is selected in an unmount",
				    drive->cmi_drive_name);
				if (tm_be_is_in_unmount(drive->cmi_drive_name,
							end_cmd) == 0) {
					mms_trace(MMS_DEVP,
					    "    remove this drive");
					drive->cmi_remove_drive = 1;
				} else {
					mms_trace(MMS_DEVP,
					    "    keep this drive");
					drive->cmi_remove_drive = 0;
				}
			}
		}
		mm_mount_clean_candidates(cur_cmd);
	}
	return (MM_BE_OK);

}
int
tm_be_match_mount(mm_command_t *mnt_cmd, mm_command_t *end_cmd, mm_db_t *db) {
	/* select the cart drive for this mnt */
	mm_wka_t		*mm_wka = end_cmd->wka_ptr;
	cmd_mount_info_t	*mount_info = &mnt_cmd->cmd_mount_info;

	cmi_cart_list_t		*cart = NULL;
	cmi_drive_list_t	*drive = NULL;

	char			*cur_cartid = NULL;
	char			*cur_library = NULL;
	char			*cur_dm = NULL;
	char			*cur_drive = NULL;

	mm_command_t		*unmnt_cmd1 = NULL;
	mm_command_t		*unmnt_cmd2 = NULL;

	mm_command_t		*cur_cmd;
	int			found_unmount = 0;

	mms_trace(MMS_DEVP,
	    "tm_be_match_mount");
	MM_UNSET_FLAG(mnt_cmd->cmd_flags,
		    MM_CMD_DISPATCHABLE);
	mnt_cmd->cmd_state = 1;


	cart = mms_list_head(&mount_info->cmi_cart_list);
	if (cart == NULL) {
		mm_response_error(end_cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);
		if (mm_wka->wka_begin_end.be_mode ==
		    ACCESS_MODE_BLOCKING) {
			return (MM_BE_BLOCKING);
		}
		mms_trace(MMS_ERR,
		    "mount has no candidates");
		return (MM_BE_ERROR);
	}
	cur_library = cart->cmi_library;
	cur_cartid = cart->cmi_cart_id;

	drive = mms_list_head(&cart->cmi_drive_list);
	if (drive == NULL) {
		mm_response_error(end_cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);
		if (mm_wka->wka_begin_end.be_mode ==
		    ACCESS_MODE_BLOCKING) {
			return (MM_BE_BLOCKING);
		}
		mms_trace(MMS_ERR,
		    "mount has no candidates");
		return (MM_BE_ERROR);
	}

	cur_drive = drive->cmi_drive_name;
	cur_dm = drive->cmi_dm_name;

	mm_set_mount_info_cart(cur_cartid,
			mount_info);
	mm_set_mount_info_drive(cur_drive,
			mount_info);
	mm_set_mount_info_dm(cur_dm,
			mount_info);
	mm_set_mount_info_library(cur_library,
			mount_info);

	/* Select Is done - print the results */
	mms_trace(MMS_DEVP, "Cart/Lib/Drive selection "
	    "complete for task %s",
	    mnt_cmd->cmd_uuid);
	mms_trace(MMS_DEVP, "Cartridge ID is %s",
	    mount_info->cmi_cartridge);
	mms_trace(MMS_DEVP, "Library is %s",
	    mount_info->cmi_library);
	mms_trace(MMS_DEVP, "Drive is %s",
	    mount_info->cmi_drive);
	mms_trace(MMS_DEVP, "DM is %s",
	    mount_info->cmi_dm);


	mm_remove_all_depend(mnt_cmd);
	mms_list_foreach(&end_cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_mount_cmd_func) {
			/* not an unmount */
			continue;
		}
		if (strcmp(mount_info->cmi_drive,
			    cur_cmd->cmd_mount_info.cmi_drive) == 0) {
			/* same drive */
			mm_add_depend(mnt_cmd, cur_cmd);
			found_unmount = 1;
			unmnt_cmd1 = cur_cmd;
		}
	}
	if (found_unmount == 0) {
		mm_system_error(end_cmd,
			"couldn't find unmount for this mount");
		mms_trace(MMS_ERR,
		    "couldn't find unmount for this mount");
		return (MM_BE_ERROR);
	}


	/* Is this cart currently loaded */
	if (mm_db_exec(HERE, db,
		"select \"LibraryName\",\"DriveName\" "
		"from \"DRIVE\" where \"DRIVE\"."
		"\"CartridgePCL\" = (select \"CartridgePCL\" "
		"from \"CARTRIDGE\" where \"CartridgeID\" = '%s');",
	    mount_info->cmi_cartridge) != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "tm_be_match_mount: "
		    "db error getting drive info");
		mm_sql_db_err_rsp_new(end_cmd, db);
		return (MM_BE_ERROR);
	}

	if (PQntuples(db->mm_db_results) == 0) {
		/* Cur cart is not loaded */
		mms_trace(MMS_DEVP,
		    "cartid %s not found in a drive",
		    mount_info->cmi_cartridge);
	} else {
		mms_trace(MMS_DEVP,
		    "need to unmount %s, %s "
		    "before mount ",
		    PQgetvalue(db->mm_db_results, 0, 0),
		    PQgetvalue(db->mm_db_results, 0, 1));

		unmnt_cmd2 = mm_return_unload(
			PQgetvalue(db->mm_db_results, 0, 0),
			PQgetvalue(db->mm_db_results, 0, 1),
			mm_wka->mm_data);
		if (unmnt_cmd2 == NULL) {
			mm_system_error(end_cmd,
				"couldn't find unmount for this mount");
			mms_trace(MMS_ERR,
			    "couldn't find unmount for this mount");
			return (MM_BE_ERROR);
		}
		MM_SET_FLAG(unmnt_cmd1->cmd_flags,
			MM_CMD_DISPATCHABLE);
		MM_UNSET_FLAG(mnt_cmd->cmd_flags, MM_CMD_DISPATCHABLE);
		MM_UNSET_FLAG(unmnt_cmd2->cmd_flags, MM_CMD_DISPATCHABLE);
		mms_trace(MMS_DEVP,
		    "unmnt_cmd1->cmd_name == %s (%p)",
		    unmnt_cmd1->cmd_name,
		    unmnt_cmd1);
		mms_trace(MMS_DEVP,
		    "unmnt_cmd2->cmd_name == %s (%p)",
		    unmnt_cmd2->cmd_name, unmnt_cmd2);
		mms_trace(MMS_DEVP,
		    "mnt_cmd->cmd_name == %s (%p)",
		    mnt_cmd->cmd_name, mnt_cmd);
		mm_remove_all_depend(mnt_cmd);
		mm_add_depend(unmnt_cmd1, unmnt_cmd2);
		mm_add_depend(unmnt_cmd2, mnt_cmd);
	}
	mm_clear_db(&db->mm_db_results);
	mms_trace(MMS_DEVP,
	    "mount finished setting up");
	return (MM_BE_OK);
}

int
tm_be_pairs(mm_command_t *cmd, mm_data_t *mm_data) {
	/* This is what the function may return */
	/* MM_BE_ERROR */
	/* MM_BE_BLOCKING */
	/* MM_BE_DISPATCH */
	/* For BLOCKING and MMS_ERROR, end cmd error buf */
	/* Should be set */

	mm_db_t			*db = &mm_data->mm_db_tm;
	mm_wka_t		*mm_wka = cmd->wka_ptr;
	mm_command_t		*cur_cmd;
	int			rc;
	mms_trace(MMS_DEVP,
	    "tm_be_pairs");

	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_mount_cmd_func) {
			/* not an unmount */
			continue;
		}

		rc = tm_be_unmount_ready(cur_cmd->wka_ptr, cur_cmd, db, cmd);
		switch (rc) {

		case MM_UNMOUNT_ERROR:
			/* Error code should be set */
			mms_trace(MMS_ERR,
			    "internal error, mm_unmount_ready");
			return (MM_BE_ERROR);

		case MM_UNMOUNT_READY:
			mms_trace(MMS_DEVP,
			    "unmount is ready to go, ");
			break;

		case MM_UNMOUNT_NOT_READY:
			if (mm_wka->wka_begin_end.be_mode ==
				ACCESS_MODE_IMMEDIATE) {
				/* Error code should be set */
				mms_trace(MMS_ERR,
				    "immediate begin-end group "
				    "is not ready");
				return (MM_BE_ERROR);
			}
			/* Error code should be set */
			mms_trace(MMS_ERR,
			    "blocking unmount not ready, "
			    "wait longer and try later");
			return (MM_BE_BLOCKING);
		default:
			mms_trace(MMS_ERR,
			    "bad rc mm_unmount_ready");
			mm_system_error(cmd,
				"bad rc mm_unmount_ready");
			return (MM_BE_ERROR);
		}
	}
	mms_trace(MMS_DEVP,
	    "unmounts prepared");
	/* Unmounts prepared/partially ready */
	/* now determine exactly which tapes to unmount */
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_mount_cmd_func) {
			/* not an unmount */
			continue;
		}
		mms_trace(MMS_DEVP, "set up a unmount for dispatch");
		rc = tm_be_set_unmount(cur_cmd->wka_ptr,
				cur_cmd, cmd);
		if (rc == MM_BE_ERROR) {
			mms_trace(MMS_ERR, "error setting up unmount");
			/* error set for end command already */
			return (MM_BE_ERROR);
		} else if (rc == MM_BE_BLOCKING) {
			mms_trace(MMS_ERR,
			    "blocking mount not ready, "
			    "wait longer and try later");
			return (MM_BE_BLOCKING);
		}
		/* remove cmi_cart and cmi_drive from */
		/* above from the remaining */
		/* unmount candidates */
		tm_be_rm_unmount_candidates(cmd, cur_cmd);
		MM_SET_FLAG(cur_cmd->cmd_flags,
			    MM_CMD_DISPATCHABLE);
		cur_cmd->cmd_state = 1;
		cur_cmd->cmd_mount_info.cui_physical = 1;
	}
	mms_trace(MMS_DEVP,
	    "unmounts cart/drive selected and set");
	/* All unmount cmds have been set up */
	/* set up each mount using the candidates in unmounts */
	if (tm_be_init_mount(cmd, db) == MM_BE_ERROR) {
		mms_trace(MMS_ERR, "error init mounts for unmounts");
		/* error set for end command already */
		return (MM_BE_ERROR);
	}
	mms_trace(MMS_DEVP,
	    "mounts initialized");
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_unmount_cmd_func) {
			/* not an mount */
			continue;
		}
		/* tm_be_match_mount will match this */
		/* mount with an unmount */
		MM_UNSET_FLAG(cur_cmd->cmd_flags,
			    MM_CMD_DISPATCHABLE);
		cur_cmd->cmd_state = 1;
		rc = tm_be_match_mount(cur_cmd, cmd, db);
		if (rc == MM_BE_ERROR) {
			mms_trace(MMS_ERR, "error setting up unmount");
			/* error set for end command already */
			return (MM_BE_ERROR);
		}

	}

	/* All mounts have been setup, */
	/* create the TASK objects */
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		mm_set_mount_objs(cur_cmd, db);
	}
	/* Put these mounts on the command queue */
	tm_be_add_mounts(cmd);

	/* All mounts are set and ready to go */
	return (MM_BE_DISPATCH);

}


void tm_be_add_mounts(mm_command_t *cmd) {
	mm_command_t	*next;
	mm_command_t	*cur;

	for (cur = mms_list_head(&cmd->cmd_beginend_list);
	    cur != NULL;
	    cur = next) {
		next = mms_list_next(&cmd->cmd_beginend_list, cur);

		mms_list_remove(&cmd->cmd_beginend_list,
			    cur);
		pthread_mutex_lock(&cmd->wka_ptr->mm_data->mm_queue_mutex);
		mms_list_insert_tail(&cmd->wka_ptr->mm_data->mm_cmd_queue, cur);
		pthread_mutex_unlock(&cmd->wka_ptr->mm_data->mm_queue_mutex);
	}
}

void
mm_be_order_mount_cmds(mm_command_t *cmd)
{
	mm_command_t		*be_cmd;
	mm_command_t		*next_cmd;

	/* Put mount commands with already loaded cartridges on */
	/* front of begin end mount list. */

	for (be_cmd = mms_list_head(&cmd->cmd_beginend_list);
	    be_cmd != NULL;
	    be_cmd = next_cmd) {
		next_cmd = mms_list_next(&cmd->cmd_beginend_list, be_cmd);
		if (mm_mount_candidate_loaded(be_cmd)) {
			mms_list_remove(&cmd->cmd_beginend_list, be_cmd);
			mms_list_insert_head(&cmd->cmd_beginend_list, be_cmd);
		}
	}
}

int
tm_be_mounts(mm_command_t *cmd, mm_data_t *mm_data) {
	mm_db_t			*db = &mm_data->mm_db_tm;
	mm_wka_t		*mm_wka = cmd->wka_ptr;
	mm_command_t		*cur_cmd;
	int			rc;

	/* Errors */
	int			mount_has_error = 0;

	char			*err_text = NULL;

	/* This is the function can return */
	/* MM_BE_ERROR */
	/* MM_BE_BLOCKING */
	/* MM_BE_DISPATCH */
	/* For BLOCKING and MMS_ERROR, end cmd error buf */
	/* Should be set */

	mms_trace(MMS_DEVP,
	    "tm_be_mounts");


	/* Instead of returning set mount_has_error or mount_would_block */
	/* After calling for ever mount generate the correct error code */
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		cur_cmd->cmd_mount_info.cmi_mount_ok = 0;
		rc = tm_be_mount_ready(cur_cmd->wka_ptr, cur_cmd, db, cmd);
		switch (rc) {

		case MM_MOUNT_ERROR:
			/* Error code should be set */
			mms_trace(MMS_ERR,
			    "tm_be_mounts: "
			    "internal error, tm_be_mount_ready");
			mount_has_error = 1;
			break;

		case MM_MOUNT_READY:
			/* The mount is ready, mount_info should be set */
			/* continue to state 1 */
			mms_trace(MMS_DEVP,
			    "tm_be_mounts: "
			    "mount is ready to go ");
			cur_cmd->cmd_mount_info.cmi_mount_ok = 1;
			break;

		case MM_MOUNT_NEED_UNLOAD:
			/* this immediate mount needs to */
			/* wait for an unload */
			mms_trace(MMS_DEVP,
			    "tm_be_mounts: "
			    "mount needs to wait "
			    "for unload to complete");
			cur_cmd->cmd_mount_info.cmi_mount_ok = 1;
			break;

		case MM_MOUNT_NOT_READY:
			mount_has_error = 1;
			mms_trace(MMS_ERR,
			    "tm_be_mounts: "
			    "mount not ready, "
			    "wait longer and try later");
				break;
		default:
			mms_trace(MMS_ERR,
			    "tm_be_mounts: "
			    "bad rc mm_mount_ready");
			mm_system_error(cmd,
				"bad rc mm_mount_ready");
			return (MM_BE_ERROR);
		}
	}


	/* Determine the correct error code to use for the end command */
	/* Each mount in the group with an error will have ecode and eclass */
	if (mount_has_error) {
		mms_trace(MMS_ERR,
		    "at least one mount had "
		    "an error for this begin-end");

		/* at least one mount had an error */
		/* for each mount set the least sever errror */
		/* if any mount has a error more severe than retry */
		/* return error for the end command */
		/* since this mount will not work for immediate or blocking */
		mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
			if (cur_cmd->cmd_mount_info.cmi_mount_ok) {
				mms_trace(MMS_ERR,
				    "tm_be_mounts: "
				    "this mount is ok, no errors");
			} else {
				mms_trace(MMS_ERR,
				    "tm_be_mounts: "
				    "this mount has errors, set least severe");

				mm_set_least_severe(cur_cmd);
				if (strcmp(cur_cmd->cmd_eclass,
				    ECLASS_RETRY) != 0) {
					/* this is not a retry error class */
					/* return error for this end command */
					mms_trace(MMS_ERR,
					    "at least one mount's "
					    "least severe error is"
					    " more severe than retry, "
					    "this begin-end group "
					    "will not work");
					/* One or more mount has errors */
					/* that are non retry */
					err_text =
					    mm_return_err_text(cur_cmd->
					    cmd_err_ptr);
					mm_response_error(cmd,
					    ECLASS_EXPLICIT,
					    "ENOSOLUTIONS",
					    MM_5110_MSG,
					    "err_text", err_text,
					    NULL);
					free(err_text);
					return (MM_BE_ERROR);
				}
			}
		}



	}


	mm_be_order_mount_cmds(cmd);

	/* All mounts are either ok or retry */
	/* Determine which error to return/block */
	/* for immediate, return retry or nosolutions */
	/* for blocking, block or return nosolutions */


	/* Mount candidate lists have been set up, */
	/* divide the resources and set up the exact */
	/* Drive/cartridge combination */

	mount_has_error = 0;
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {

		mms_trace(MMS_DEVP, "set up a mount for dispatch");

		/* tm_be_set_mount returns MM_BE_ERROR or MM_BE_DISPATCH */

		if (cur_cmd->cmd_mount_info.cmi_mount_ok) {
			rc = tm_be_set_mount(cur_cmd,
			    db, cmd);
		} else {
			rc = MM_BE_ERROR;
		}

		if (rc == MM_BE_ERROR) {
			mount_has_error = 1;
			/* If the cur err ptr is null or */
			/* pointing to a non-retry error class */
			/* This begin-end group cannot succeede */
			if ((cur_cmd->cmd_err_ptr == NULL) ||
			    (strcmp(cur_cmd->cmd_err_ptr->eclass,
			    ECLASS_RETRY) != 0)) {
				mms_trace(MMS_ERR,
				    "tm_be_mounts: "
				    "There are no valid "
				    "solutions to this mount ");

				err_text =
				    mm_return_err_text(cur_cmd->
				    cmd_err_ptr);
				mm_response_error(cmd,
				    ECLASS_EXPLICIT,
				    "ENOSOLUTIONS",
				    MM_5110_MSG,
				    "err_text", err_text,
				    NULL);
				free(err_text);
				return (MM_BE_ERROR);
			}
			tm_be_rm_error_candidates(cmd, cur_cmd);
		} else {
			tm_be_rm_mount_candidates(cmd, cur_cmd);
		}
	}

	if (mount_has_error) {
		/* If this is immediate, return retry error class for the end */
		/* if this is blocking, then block for the whole group */
		/* Set error for retry */
		mm_response_error(cmd,
		    ECLASS_RETRY,
		    "ETMPUNAVAIL",
		    MM_5104_MSG,
		    NULL);
		return (MM_BE_BLOCKING);
	}
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		if (mm_dispatch_now(mm_wka, cur_cmd, db)) {
			/* error should be set */
			mms_trace(MMS_ERR,
			    "error setting up mount for dispatch");
		}
	}
	/* All mounts have been setup, */
	/* create the TASK objects */
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		mm_set_mount_objs(cur_cmd, db);
	}
	/* Put these mounts on the command queue */
	tm_be_add_mounts(cmd);

	/* All mounts are set and ready to go */
	return (MM_BE_DISPATCH);
}

int
tm_be_cmd_has_unmounts(mm_command_t *cmd) {
	mm_command_t		*cur_cmd;
	int			unmount_count = 0;
	int			mount_count = 0;

	int			print_message = 1;
	/* Returns 1 if this has unmounts */
	/* Also makes sure unmounts are paird with mounts */
	if (print_message)
		mms_trace(MMS_DEVP,
		    "tm_be_cmd_has_unmounts");
	mms_list_foreach(&cmd->cmd_beginend_list, cur_cmd) {
		if (cur_cmd->cmd_func == mm_mount_cmd_func) {
			if (print_message)
				mms_trace(MMS_DEVP,
				    "saw a mount");
			mount_count ++;
		} else if (cur_cmd->cmd_func == mm_unmount_cmd_func) {
			if (print_message)
				mms_trace(MMS_DEVP,
				    "saw an unmount");
			unmount_count ++;
		}
	}
	if (unmount_count == 0)
		return (0);
	if (unmount_count == mount_count)
		return (1);
	mm_system_error(cmd,
		"num mounts does not match num unmounts");
	return (-1);
}

int
tm_can_dispatch_end(mm_command_t *cmd, mm_data_t *mm_data)
{
	mm_db_t			*db = &mm_data->mm_db_tm;
	mm_wka_t		*mm_wka = cmd->wka_ptr;

	int			rc;
	int			immediate = 0;
	int			unmount = 0;

	if (mm_wka->wka_begin_end.be_mode ==
	    ACCESS_MODE_IMMEDIATE)
		immediate = 1;

	/* Determine the begin-end type */
	/* Currently there are 2 types */
	/* 1. A group of all mounts */
	/* 2. A group of unmounts paired 1to1 w/mounts */
	if ((unmount = tm_be_cmd_has_unmounts(cmd)) == -1) {
		/* Error buf is set */
		goto cmd_error;
	}
	if (unmount == 1) {
		rc = tm_be_pairs(cmd, mm_data);
	} else {
		rc = tm_be_mounts(cmd, mm_data);
	}
	switch (rc) {
	case MM_BE_BLOCKING:
		if (immediate)
			goto cmd_error;
		return (0);
	case MM_BE_DISPATCH:
		/* Send success for end */
		mm_path_match_report(cmd, db);
		(void) mm_del_tm_cmd(db, cmd->cmd_uuid);
		mm_send_response(mm_wka->mm_wka_conn, cmd);
		return (1);
	case MM_BE_ERROR:
		/* Error buf is set */
		goto cmd_error;
	}


cmd_error:
	/* Error buf must be set */
	(void) mm_del_tm_cmd(db, cmd->cmd_uuid);
	cmd->cmd_remove = 1;
	mm_send_text(cmd->wka_ptr->mm_wka_conn,
	    cmd->cmd_buf);
	tm_be_cancel_all(cmd);
	return (0);
}

int
tm_can_dispatch(char *task_id, mm_data_t *mm_data)
{

	mms_list_t			*cmd_queue = &mm_data->mm_cmd_queue;

	mm_command_t		*cur_cmd;


	mms_trace(MMS_DEVP, "tm_can_dispatch");

	/* Find the command associated with this task */
	pthread_mutex_lock(&mm_data->mm_queue_mutex);
	mms_list_foreach(cmd_queue, cur_cmd) {
		if ((strcmp(task_id,
		    cur_cmd->cmd_uuid) == 0)) {
			/* task id matches */
			break;
		}

	}
	pthread_mutex_unlock(&mm_data->mm_queue_mutex);
	if (cur_cmd == NULL) {
		mms_trace(MMS_INFO, "Could not match task %s with a command",
		    task_id);
		return (0);
	}

	mms_trace(MMS_DEVP, "Matched Task with command - cmd is %s",
	    cur_cmd->cmd_uuid);
	if (cur_cmd->cmd_remove) {
		/* This command has already been marked for removal */
		/* skip and let main thread clean it up */
		mms_trace(MMS_DEVP,
		    "skip %s, marked for remove",
		    cur_cmd->cmd_uuid);
		return (0);
	}

	if (cur_cmd->cmd_func == mm_mount_cmd_func) {
		pthread_mutex_lock(&cur_cmd->wka_ptr->
		    wka_local_lock);
		/* Set cmd_dispatchable inside this func */
		if (tm_can_dispatch_mount(cur_cmd, mm_data)) {
			pthread_mutex_unlock(&cur_cmd->wka_ptr->
			    wka_local_lock);
			return (1);
		}
		pthread_mutex_unlock(&cur_cmd->wka_ptr->
		    wka_local_lock);
		return (0);
	} else if (cur_cmd->cmd_func == mm_unmount_cmd_func) {
		pthread_mutex_lock(&cur_cmd->wka_ptr->
		    wka_local_lock);
		if (tm_can_dispatch_unmount(cur_cmd, mm_data)) {
			/* set command as dispatchable */
			MM_SET_FLAG(cur_cmd->cmd_flags, MM_CMD_DISPATCHABLE);
			pthread_mutex_unlock(&cur_cmd->wka_ptr->
			    wka_local_lock);
			return (1);
		}
		pthread_mutex_unlock(&cur_cmd->wka_ptr->
		    wka_local_lock);

		return (0);
	} else if (cur_cmd->cmd_func == mm_end_cmd_func) {
		pthread_mutex_lock(&cur_cmd->wka_ptr->
		    wka_local_lock);
		if (tm_can_dispatch_end(cur_cmd, mm_data)) {
			/* set command as dispatchable */
			MM_SET_FLAG(cur_cmd->cmd_flags, MM_CMD_DISPATCHABLE);
			pthread_mutex_unlock(&cur_cmd->wka_ptr->
			    wka_local_lock);
			return (1);
		}
		pthread_mutex_unlock(&cur_cmd->wka_ptr->
		    wka_local_lock);
	} else {
		mms_trace(MMS_ERR,
		    "command is not a mount/unmount or end");
		return (0);
	}
	return (0);
}


int
mm_get_tm_cmd(mm_data_t *mm_data)
{
	mm_db_t			*db = &mm_data->mm_db_tm;
	PGresult	 *tasks;
	int		num_tasks;
	int		num_dispatched = 0;
	int		i;

	/* used when a mount needs a drive unloaded 1st */


	mms_trace(MMS_DEVP, "mm_get_tm_cmd");

	mms_trace(MMS_DEVP, "Getting list of tasks...");

	/* Get ordered list of blocked tasks */
	if (mm_db_exec(HERE, db,
	    "select * from "
	    "(select \"TaskID\","
	    "\"TaskPriority\" from \"TASK\" "
	    "where \"TaskState\" = 'blocked' "
	    "order by \"TaskArrivalTime\") "
	    "as foo order by \"TaskPriority\" "
	    "desc;") != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "mm_get_tm_cmd: "
		    "db error getting task info");
		return (1);
	}

	tasks = db->mm_db_results;
	num_tasks = PQntuples(tasks);
	if (num_tasks == 0) {
		mms_trace(MMS_DEVP, "%d tasks found",
		    num_tasks);
		mm_clear_db(&tasks);
		mms_trace(MMS_DEVP, "TaskManager is Done");
		return (0);
	}
	mms_trace(MMS_DEVP, "%d tasks found, trying dispatch",
	    num_tasks);
	for (i = 0; i < num_tasks; i++) {
		/* Try to dispatch each task */
		mms_trace(MMS_DEVP, "    task %s, priority %s",
		    PQgetvalue(tasks, i, 0),
		    PQgetvalue(tasks, i, 1));

		if (tm_can_dispatch(PQgetvalue(tasks, i, 0),
		    mm_data) == 1) {
			/* Task ok for dispatch */
			mms_trace(MMS_INFO, "task %s ready for dispatch",
			    PQgetvalue(tasks, i, 0));
			num_dispatched ++;
		} else {
			/* Resources not available */
			mms_trace(MMS_DEVP, "task %s not ready",
			    PQgetvalue(tasks, i, 0));
		}
	}

	mms_trace(MMS_DEVP, "%d tasks dispatched",
	    num_dispatched);

	if (num_dispatched == 0) {
		mms_trace(MMS_DEVP, "TaskManager is Done");
		mm_clear_db(&tasks);
		return (0);
	}
	/* wakeup worker thread to do work */

	pthread_mutex_lock(&mm_data->mm_worker_mutex);
	mm_data->mm_work_todo = 1;
	pthread_cond_signal(&mm_data->mm_work_cv);
	pthread_mutex_unlock(&mm_data->mm_worker_mutex);


	mms_trace(MMS_DEVP, "TaskManager is Done");
	mm_clear_db(&tasks);
	return (0);

}

mm_db_rval_t
mm_set_tm_task(mm_db_t *db, mm_command_t *command)
{
	return (mm_new_tm_task(db, command, "blocked"));
}

mm_db_rval_t
mm_new_tm_task(mm_db_t *db, mm_command_t *command, char *state)
{
	mm_db_rval_t	 rc;
	char		*cmd_text;

	if ((cmd_text = mms_pn_build_cmd_text(command->cmd_root)) == NULL) {
		return (MM_DB_ERROR); /* out of memory */
	}
	rc = mm_db_exec(HERE, db, "INSERT INTO \"TASK\" "
	    "(\"TaskID\", \"TaskType\", \"ApplicationName\", "
	    "\"AIName\", \"TaskStatement\", \"ClientTaskID\", "
	    "\"TaskState\") VALUES "
	    "('%s', '%s', '%s', '%s', $$%s$$, '%s', '%s')",
	    command->cmd_uuid,
	    command->cmd_root->pn_string,
	    command->wka_ptr->wka_conn.cci_client,
	    command->wka_ptr->wka_conn.cci_instance,
	    cmd_text, command->cmd_task, state);
	free(cmd_text);
	return (rc);
}

mm_db_rval_t
mm_set_tm_cartridge(mm_db_t *db, char *taskid, char *cartridge_id)
{
	mm_db_rval_t	rc;
	PGresult	*task_results;

	rc = mm_db_exec(HERE, db,
	    "select * from \"TASKCARTRIDGE\" where "
	    "\"TaskID\" = '%s' and \"CartridgeID\" = '%s'",
	    taskid, cartridge_id);
	if (rc != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "mm_set_tm_cartridge: "
		    "db error getting info for"
		    " TASKCARTRIDGE");
		mm_clear_db(&db->mm_db_results);
		return (rc);
	}
	task_results = db->mm_db_results;
	if (PQntuples(task_results) == 0) {
		rc = mm_db_exec(HERE, db, "INSERT INTO \"TASKCARTRIDGE\" "
		    "(\"TaskID\", \"CartridgeID\") VALUES ('%s', '%s')",
		    taskid, cartridge_id);
		if (rc != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_set_tm_cartridge: "
			    "db error inserting TASKCARTRIDGE");
			mm_clear_db(&db->mm_db_results);
		}
	} else {
		if (rc == MM_DB_DATA) {
			rc = MM_DB_OK;
		}
	}
	mm_clear_db(&task_results);
	return (rc);
}

mm_db_rval_t
mm_set_tm_drive(mm_db_t *db, char *taskid, char *drive)
{
	mm_db_rval_t	rc;
	PGresult	*task_results;

	rc = mm_db_exec(HERE, db,
	    "select * from \"TASKDRIVE\" where "
	    "\"TaskID\" = '%s' and \"DriveName\" = '%s'",
	    taskid, drive);
	if (rc != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "mm_set_tm_drive: "
		    "db error getting info for"
		    " TASKDRIVE");
		mm_clear_db(&db->mm_db_results);
		return (rc);
	}
	task_results = db->mm_db_results;
	if (PQntuples(task_results) == 0) {
		rc = mm_db_exec(HERE, db, "INSERT INTO \"TASKDRIVE\" "
		    "(\"TaskID\", \"DriveName\") VALUES ('%s', '%s')",
		    taskid, drive);
		if (rc != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_set_tm_drive: "
			    "db error inserting TASKDRIVE");
			mm_clear_db(&db->mm_db_results);
		}
	} else {
		if (rc == MM_DB_DATA) {
			rc = MM_DB_OK;
		}
	}
	mm_clear_db(&task_results);
	return (rc);
}

mm_db_rval_t
mm_set_tm_library(mm_db_t *db, char *taskid, char *library)
{
	mm_db_rval_t	rc;
	PGresult	*task_results;

	rc = mm_db_exec(HERE, db,
	    "select * from \"TASKLIBRARY\" where "
	    "\"TaskID\" = '%s' and \"LibraryName\" = '%s'",
	    taskid, library);
	if (rc != MM_DB_DATA) {
		mms_trace(MMS_ERR,
		    "mm_set_tm_library: "
		    "db error getting info for"
		    " TASKLIBRARY");
		mm_clear_db(&db->mm_db_results);
		return (rc);
	}
	task_results = db->mm_db_results;
	if (PQntuples(task_results) == 0) {
		rc = mm_db_exec(HERE, db, "INSERT INTO \"TASKLIBRARY\" "
		    "(\"TaskID\", \"LibraryName\") VALUES ('%s', '%s')",
		    taskid, library);
		if (rc != MM_DB_OK) {
			mms_trace(MMS_ERR,
			    "mm_set_tm_library: "
			    "db error inserting TASKLIBRARY");
			mm_clear_db(&db->mm_db_results);
		}
	} else {
		if (rc == MM_DB_DATA) {
			rc = MM_DB_OK;
		}
	}
	mm_clear_db(&task_results);
	return (rc);
}

mm_db_rval_t
mm_set_tm_cmd_dispatched(mm_db_t *db, char *taskid)
{
	mm_db_rval_t	rc;

	rc = mm_db_exec(HERE, db, "UPDATE \"TASK\" "
	    "SET \"TaskState\" = 'dispatched' "
	    "WHERE \"TaskID\" = '%s'", taskid);

	return (rc);
}

mm_db_rval_t
mm_del_tm_cmd(mm_db_t *db, char *taskid)
{
	mm_db_rval_t	rc;

	rc = mm_db_exec(HERE, db, "DELETE FROM \"TASK\" "
	    "WHERE \"TaskID\" = '%s'", taskid);
	if (rc != MM_DB_OK) {
		mms_trace(MMS_ERR,
		    "mm_del_tm_cmd: "
		    "db error deleteing from TASK");
	}

	rc = mm_db_exec(HERE, db, "delete from \"REQUEST\" where "
	    "\"RequestingTaskID\" = '%s' and "
	    "\"RequestState\" != 'responded';", taskid);

	if (rc != MM_DB_OK) {
		mms_trace(MMS_ERR,
		    "mm_del_tm_cmd: "
		    "db error deleteing from REQUEST");
	}

	return (rc);
}

mm_db_rval_t
mm_chg_tm_cmd_priority(mm_db_t *db, char *taskid, int priority)
{
	mm_db_rval_t	rc;

	rc = mm_db_exec(HERE, db, "UPDATE \"TASK\" "
	    "SET \"TaskPriority\" = '%s' "
	    "WHERE \"TaskID\" = '%s'", priority, taskid);

	return (rc);
}