/*
 * (C) Copyright IBM Corp. 2004, 2005   All Rights Reserved.
 *
 * main.c
 *
 * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access.
 * It determines the ALUA state of a device and prints a priority value to
 * stdout.
 *
 * Author(s): Jan Kunigk
 *            S. Bader <shbader@de.ibm.com>
 *
 * This file is released under the GPL.
 */
#include <stdio.h>

#include "debug.h"
#include "prio.h"
#include "structs.h"

#include "alua.h"

#define ALUA_PRIO_NOT_SUPPORTED			1
#define ALUA_PRIO_RTPG_FAILED			2
#define ALUA_PRIO_GETAAS_FAILED			3
#define ALUA_PRIO_TPGS_FAILED			4
#define ALUA_PRIO_NO_INFORMATION		5

static const char * aas_string[] = {
	[AAS_OPTIMIZED]		= "active/optimized",
	[AAS_NON_OPTIMIZED]	= "active/non-optimized",
	[AAS_STANDBY]		= "standby",
	[AAS_UNAVAILABLE]	= "unavailable",
	[AAS_LBA_DEPENDENT]	= "logical block dependent",
	[AAS_RESERVED]		= "ARRAY BUG: invalid TPGs state!",
	[AAS_OFFLINE]		= "offline",
	[AAS_TRANSITIONING]	= "transitioning between states",
};

static const char *aas_print_string(int rc)
{
	rc &= 0x7f;

	if (rc & 0x70)
		return aas_string[AAS_RESERVED];
	rc &= 0x0f;
	if (rc > AAS_RESERVED && rc < AAS_OFFLINE)
		return aas_string[AAS_RESERVED];
	else
		return aas_string[rc];
}

int
get_alua_info(struct path * pp)
{
	int	rc;
	int	tpg;
	bool	diff_tpg;

	tpg = get_target_port_group(pp);
	if (tpg < 0) {
		rc = get_target_port_group_support(pp);
		if (rc < 0)
			return -ALUA_PRIO_TPGS_FAILED;
		if (rc == TPGS_NONE)
			return -ALUA_PRIO_NOT_SUPPORTED;
		return -ALUA_PRIO_RTPG_FAILED;
	}
	diff_tpg = (pp->tpg_id != GROUP_ID_UNDEF && pp->tpg_id != tpg);
	pp->tpg_id = tpg;
	condlog((diff_tpg) ? 2 : 4, "%s: reported target port group is %i",
		pp->dev, tpg);
	rc = get_asymmetric_access_state(pp, tpg);
	if (rc < 0) {
		condlog(2, "%s: get_asymmetric_access_state returned %d",
			__func__, rc);
		return -ALUA_PRIO_GETAAS_FAILED;
	}

	condlog(3, "%s: aas = %02x [%s]%s", pp->dev, rc, aas_print_string(rc),
		(rc & 0x80) ? " [preferred]" : "");
	return rc;
}

int get_exclusive_pref_arg(char *args)
{
	char *ptr;

	if (args == NULL)
		return 0;
	ptr = strstr(args, "exclusive_pref_bit");
	if (!ptr)
		return 0;
	if (ptr[18] != '\0' && ptr[18] != ' ' && ptr[18] != '\t')
		return 0;
	if (ptr != args && ptr[-1] != ' ' && ptr[-1] != '\t')
		return 0;
	return 1;
}

int getprio (struct path * pp, char * args)
{
	int rc;
	int aas;
	int priopath;
	int exclusive_pref;

	if (pp->fd < 0)
		return -ALUA_PRIO_NO_INFORMATION;

	exclusive_pref = get_exclusive_pref_arg(args);
	rc = get_alua_info(pp);
	if (rc >= 0) {
		aas = (rc & 0x0f);
		priopath = (rc & 0x80);
		switch(aas) {
		case AAS_OPTIMIZED:
			rc = 50;
			break;
		case AAS_NON_OPTIMIZED:
			rc = 10;
			break;
		case AAS_LBA_DEPENDENT:
			rc = 5;
			break;
		case AAS_STANDBY:
			rc = 1;
			break;
		default:
			rc = 0;
		}
		if (priopath && (aas != AAS_OPTIMIZED || exclusive_pref))
			rc += 80;
	} else {
		switch(-rc) {
		case ALUA_PRIO_NOT_SUPPORTED:
			condlog(0, "%s: alua not supported", pp->dev);
			break;
		case ALUA_PRIO_RTPG_FAILED:
			condlog(0, "%s: couldn't get target port group", pp->dev);
			break;
		case ALUA_PRIO_GETAAS_FAILED:
			condlog(0, "%s: couldn't get asymmetric access state", pp->dev);
			break;
		case ALUA_PRIO_TPGS_FAILED:
			condlog(3, "%s: couldn't get supported alua states", pp->dev);
			break;
		}
	}
	return rc;
}
