/*
 * cmd_entity.c
 *
 * A command interpreter for OpenIPMI
 *
 * Author: MontaVista Software, Inc.
 *         Corey Minyard <minyard@mvista.com>
 *         source@mvista.com
 *
 * Copyright 2004 MontaVista Software Inc.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2 of
 *  the License, or (at your option) any later version.
 *
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <OpenIPMI/ipmiif.h>
#include <OpenIPMI/ipmi_cmdlang.h>
#include <OpenIPMI/ipmi_fru.h>
#include <OpenIPMI/ipmi_err.h>

/* Internal includes, do not use in your programs */
#include <OpenIPMI/internal/ipmi_malloc.h>

/* Don't pollute the namespace iwth ipmi_fru_t. */
void ipmi_cmdlang_dump_fru_info(ipmi_cmd_info_t *cmd_info, ipmi_fru_t *fru);

void ipmi_cmdlang_sensor_change(enum ipmi_update_e op,
				ipmi_entity_t      *entity,
				ipmi_sensor_t      *sensor,
				void               *cb_data);
void ipmi_cmdlang_control_change(enum ipmi_update_e op,
				 ipmi_entity_t      *entity,
				 ipmi_control_t      *control,
				 void               *cb_data);

static void
entity_iterate_handler(ipmi_entity_t *entity, ipmi_entity_t *parent,
		       void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    if (cmdlang->err)
	return;

    ipmi_entity_get_name(parent, entity_name, sizeof(entity_name));

    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
}

static void
entity_child_handler(ipmi_entity_t *parent, ipmi_entity_t *entity,
		     void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    if (cmdlang->err)
	return;

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
    if (ipmi_entity_get_is_parent(entity)) {
	ipmi_cmdlang_down(cmd_info);
	ipmi_entity_iterate_children(entity, entity_child_handler, cmd_info);
	ipmi_cmdlang_up(cmd_info);
    }
}

static void
entity_tree_handler(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    if (cmdlang->err)
	return;

    if (ipmi_entity_get_is_child(entity))
	return;

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
    if (ipmi_entity_get_is_parent(entity)) {
	ipmi_cmdlang_down(cmd_info);
	ipmi_entity_iterate_children(entity, entity_child_handler, cmd_info);
	ipmi_cmdlang_up(cmd_info);
    }
}

static void
entity_tree(ipmi_domain_t *domain, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    char             domain_name[IPMI_DOMAIN_NAME_LEN];

    ipmi_domain_get_name(domain, domain_name, sizeof(domain_name));
    ipmi_cmdlang_out(cmd_info, "Domain", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", domain_name);
    ipmi_cmdlang_out(cmd_info, "Entities", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_domain_iterate_entities(domain, entity_tree_handler, cmd_info);
    ipmi_cmdlang_up(cmd_info);
    ipmi_cmdlang_up(cmd_info);
}

static void
entity_list_handler(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    if (cmdlang->err)
	return;

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
}

static void
entity_list(ipmi_domain_t *domain, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    char             domain_name[IPMI_DOMAIN_NAME_LEN];

    ipmi_domain_get_name(domain, domain_name, sizeof(domain_name));
    ipmi_cmdlang_out(cmd_info, "Domain", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", domain_name);
    ipmi_cmdlang_out(cmd_info, "Entities", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_domain_iterate_entities(domain, entity_list_handler, cmd_info);
    ipmi_cmdlang_up(cmd_info);
    ipmi_cmdlang_up(cmd_info);
}

static void
entity_dump(ipmi_entity_t *entity, ipmi_cmd_info_t *cmd_info)
{
    enum ipmi_dlr_type_e type;
    static char          *ent_types[] = { "unknown", "mc", "fru",
					  "generic", "invalid" };
    int                  length;
    unsigned int         val;

    type = ipmi_entity_get_type(entity);
    if (type > IPMI_ENTITY_GENERIC)
	type = IPMI_ENTITY_GENERIC + 1;
    ipmi_cmdlang_out(cmd_info, "Type", ent_types[type]);

    ipmi_cmdlang_out_bool(cmd_info, "Present", ipmi_entity_is_present(entity));
    ipmi_cmdlang_out_bool(cmd_info, "Presence sensor always there",
			 ipmi_entity_get_presence_sensor_always_there(entity));
    ipmi_cmdlang_out_bool(cmd_info, "Hot swappable",
			  ipmi_entity_hot_swappable(entity));
    if (ipmi_entity_hot_swappable(entity)) {
	ipmi_cmdlang_out_bool(cmd_info, "Supports managed hot swap",
			      ipmi_entity_supports_managed_hot_swap(entity));
    }

    if (ipmi_entity_get_is_child(entity)) {
	ipmi_cmdlang_out(cmd_info, "Parents", NULL);
	ipmi_cmdlang_down(cmd_info);
	ipmi_entity_iterate_parents(entity, entity_iterate_handler, cmd_info);
	ipmi_cmdlang_up(cmd_info);
    }
    if (ipmi_entity_get_is_parent(entity)) {
	ipmi_cmdlang_out(cmd_info, "Children", NULL);
	ipmi_cmdlang_down(cmd_info);
	ipmi_entity_iterate_children(entity, entity_iterate_handler, cmd_info);
	ipmi_cmdlang_up(cmd_info);
    }

    if (ipmi_entity_get_physical_slot_num(entity, &val) == 0)
	ipmi_cmdlang_out_int(cmd_info, "Physical Slot", val);

    length = ipmi_entity_get_id_length(entity);
    if (length &&
	(ipmi_entity_get_id_type(entity) == IPMI_ASCII_STR && length > 1))
    {
	char *str = ipmi_mem_alloc(length);

	if (str) {
	    length = ipmi_entity_get_id(entity, str, length);
	    ipmi_cmdlang_out_type(cmd_info, "Id",
				  ipmi_entity_get_id_type(entity),
				  str, length);
	    ipmi_mem_free(str);
	}
    }
    ipmi_cmdlang_out(cmd_info, "Entity ID String",
		     ipmi_entity_get_entity_id_string(entity));

    switch (type) {
    case IPMI_ENTITY_MC:
	ipmi_cmdlang_out_int(cmd_info, "Channel",
			     ipmi_entity_get_channel(entity));
	ipmi_cmdlang_out_int(cmd_info, "LUN", ipmi_entity_get_lun(entity));
	ipmi_cmdlang_out_hex(cmd_info, "OEM", ipmi_entity_get_oem(entity));
	ipmi_cmdlang_out_hex(cmd_info, "Slave Address",
			     ipmi_entity_get_slave_address(entity));
	ipmi_cmdlang_out_bool(cmd_info, "ACPI_system_power_notify_required",
		    ipmi_entity_get_ACPI_system_power_notify_required(entity));
	ipmi_cmdlang_out_bool(cmd_info, "ACPI_device_power_notify_required",
		    ipmi_entity_get_ACPI_device_power_notify_required(entity));
	ipmi_cmdlang_out_bool(cmd_info, "controller_logs_init_agent_errors",
		    ipmi_entity_get_controller_logs_init_agent_errors(entity));
	ipmi_cmdlang_out_bool(cmd_info, "log_init_agent_errors_accessing",
		    ipmi_entity_get_log_init_agent_errors_accessing(entity));
	ipmi_cmdlang_out_bool(cmd_info, "global_init",
			      ipmi_entity_get_global_init(entity));
	ipmi_cmdlang_out_bool(cmd_info, "chassis_device",
			      ipmi_entity_get_chassis_device(entity));
	ipmi_cmdlang_out_bool(cmd_info, "bridge",
			      ipmi_entity_get_bridge(entity));
	ipmi_cmdlang_out_bool(cmd_info, "IPMB_event_generator",
			      ipmi_entity_get_IPMB_event_generator(entity));
	ipmi_cmdlang_out_bool(cmd_info, "IPMB_event_receiver",
			      ipmi_entity_get_IPMB_event_receiver(entity));
	ipmi_cmdlang_out_bool(cmd_info, "FRU_inventory_device",
			      ipmi_entity_get_FRU_inventory_device(entity));
	ipmi_cmdlang_out_bool(cmd_info, "SEL_device",
			      ipmi_entity_get_SEL_device(entity));
	ipmi_cmdlang_out_bool(cmd_info, "SDR_repository_device",
			      ipmi_entity_get_SDR_repository_device(entity));
	ipmi_cmdlang_out_bool(cmd_info, "sensor_device",
			      ipmi_entity_get_sensor_device(entity));
	break;

    case IPMI_ENTITY_FRU:
	ipmi_cmdlang_out_int(cmd_info, "Channel",
			     ipmi_entity_get_channel(entity));
	ipmi_cmdlang_out_int(cmd_info, "LUN", ipmi_entity_get_lun(entity));
	ipmi_cmdlang_out_hex(cmd_info, "OEM", ipmi_entity_get_oem(entity));
	ipmi_cmdlang_out_hex(cmd_info, "access_address",
			 ipmi_entity_get_access_address(entity));
	ipmi_cmdlang_out_hex(cmd_info, "private_bus_id",
			 ipmi_entity_get_private_bus_id(entity));
	ipmi_cmdlang_out_int(cmd_info, "device_type",
			 ipmi_entity_get_device_type(entity));
	ipmi_cmdlang_out_int(cmd_info, "device_modifier",
			 ipmi_entity_get_device_modifier(entity));
	ipmi_cmdlang_out_bool(cmd_info, "is_logical_fru",
			 ipmi_entity_get_is_logical_fru(entity));
	ipmi_cmdlang_out_hex(cmd_info, "fru_device_id",
			 ipmi_entity_get_fru_device_id(entity));
	break;

    case IPMI_ENTITY_GENERIC:
	ipmi_cmdlang_out_int(cmd_info, "Channel",
			     ipmi_entity_get_channel(entity));
	ipmi_cmdlang_out_int(cmd_info, "LUN", ipmi_entity_get_lun(entity));
	ipmi_cmdlang_out_hex(cmd_info, "OEM", ipmi_entity_get_oem(entity));
	ipmi_cmdlang_out_hex(cmd_info, "access_address",
			 ipmi_entity_get_access_address(entity));
	ipmi_cmdlang_out_hex(cmd_info, "private_bus_id",
			 ipmi_entity_get_private_bus_id(entity));
	ipmi_cmdlang_out_int(cmd_info, "device_type",
			 ipmi_entity_get_device_type(entity));
	ipmi_cmdlang_out_int(cmd_info, "device_modifier",
			 ipmi_entity_get_device_modifier(entity));
	ipmi_cmdlang_out_hex(cmd_info, "slave_address",
			 ipmi_entity_get_slave_address(entity));
	ipmi_cmdlang_out_int(cmd_info, "address_span",
			 ipmi_entity_get_address_span(entity));
	break;

    default:
	break;
    }
}

static void
entity_info(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    ipmi_cmdlang_out(cmd_info, "Entity", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
    entity_dump(entity, cmd_info);
    ipmi_cmdlang_up(cmd_info);
}

static void
fru_info(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_fru_t      *fru;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    ipmi_cmdlang_out(cmd_info, "Entity", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", entity_name);

    /* We cheat here and don't call the entity functions, but that
       allows us to reuse the FRU output functions.  If you are
       looking at this for an example DON'T DO THIS IN YOUR CODE. */
    fru = ipmi_entity_get_fru(entity);
    if (fru)
	ipmi_cmdlang_dump_fru_info(cmd_info, fru);
    ipmi_cmdlang_up(cmd_info);
}

static void
entity_hs_get_act_time_done(ipmi_entity_t  *entity,
			    int            err,
			    ipmi_timeout_t val,
			    void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading entity hot-swap activate time";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_get_act_time_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Entity", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
    ipmi_cmdlang_out_timeout(cmd_info, "Auto-Activation Time", val);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_get_act_time(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_get_auto_activate_time(entity,
					    entity_hs_get_act_time_done,
					    cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error getting auto activate time";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_get_act_time)";
    }
}

static void
entity_hs_set_act_time_done(ipmi_entity_t  *entity,
			    int            err,
			    void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error setting entity hot-swap activate time";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_set_act_time_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Set act time", entity_name);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_set_act_time(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;
    ipmi_timeout_t  val;
    int             curr_arg = ipmi_cmdlang_get_curr_arg(cmd_info);
    int             argc = ipmi_cmdlang_get_argc(cmd_info);
    char            **argv = ipmi_cmdlang_get_argv(cmd_info);

    if ((argc - curr_arg) < 1) {
	cmdlang->errstr = "Not enough parameters";
	cmdlang->err = EINVAL;
	goto out_err;
    }

    ipmi_cmdlang_get_timeout(argv[curr_arg], &val, cmd_info);
    if (cmdlang->err) {
	cmdlang->errstr = "time invalid";
	goto out_err;
    }
    curr_arg++;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_set_auto_activate_time(entity,
					    val,
					    entity_hs_set_act_time_done,
					    cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error setting auto activate time";
	goto out_err;
    }
    return;

 out_err:
    ipmi_entity_get_name(entity, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_entity.c(entity_hs_set_act_time)";
}

static void
entity_hs_get_deact_time_done(ipmi_entity_t  *entity,
			      int            err,
			      ipmi_timeout_t val,
			      void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading entity hot-swap deactivate time";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_get_deact_time_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Entity", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
    ipmi_cmdlang_out_timeout(cmd_info, "Auto-Deactivation Time", val);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_get_deact_time(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_get_auto_deactivate_time(entity,
					    entity_hs_get_deact_time_done,
					    cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error getting auto deactivate time";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_get_deact_time)";
    }
}

static void
entity_hs_set_deact_time_done(ipmi_entity_t  *entity,
			      int            err,
			      void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error setting entity hot-swap deactivate time";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_set_deact_time_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Set deact time", entity_name);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_set_deact_time(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;
    ipmi_timeout_t  val;
    int             curr_arg = ipmi_cmdlang_get_curr_arg(cmd_info);
    int             argc = ipmi_cmdlang_get_argc(cmd_info);
    char            **argv = ipmi_cmdlang_get_argv(cmd_info);

    if ((argc - curr_arg) < 1) {
	cmdlang->errstr = "Not enough parameters";
	cmdlang->err = EINVAL;
	goto out_err;
    }

    ipmi_cmdlang_get_timeout(argv[curr_arg], &val, cmd_info);
    if (cmdlang->err) {
	cmdlang->errstr = "time invalid";
	goto out_err;
    }
    curr_arg++;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_set_auto_deactivate_time(entity,
					      val,
					      entity_hs_set_deact_time_done,
					      cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error setting auto deactivate time";
	goto out_err;
    }
    return;

 out_err:
    ipmi_entity_get_name(entity, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_entity.c(entity_hs_set_deact_time)";
}

static void
entity_hs_activation_request_done(ipmi_entity_t  *entity,
				  int            err,
				  void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error from entity hot-swap activation request";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_activation_request_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Activation requested", entity_name);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_activation_request(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_set_activation_requested
	(entity,
	 entity_hs_activation_request_done,
	 cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error sending activation request";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_activation_request)";
    }
}

static void
entity_hs_activate_done(ipmi_entity_t  *entity,
				int            err,
				void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error from entity hot-swap activate";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_activate_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Activated", entity_name);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_activate(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_activate(entity,
			      entity_hs_activate_done,
			      cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error sending activate";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_activate)";
    }
}

static void
entity_hs_deactivate_done(ipmi_entity_t  *entity,
				int            err,
				void           *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error from entity hot-swap deactivate";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_deactivate_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Deactivated", entity_name);

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_deactivate(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_deactivate(entity,
				entity_hs_deactivate_done,
				cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error sending deactivate";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_deactivate)";
    }
}

static void
entity_hs_state_done(ipmi_entity_t             *entity,
		     int                       err,
		     enum ipmi_hot_swap_states state,
		     void                      *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading hot-swap state";
	cmdlang->err = err;
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(entity_hs_state_done)";
	goto out;
    }

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
    ipmi_cmdlang_out(cmd_info, "Entity", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", entity_name);
    ipmi_cmdlang_out(cmd_info, "State", ipmi_hot_swap_state_name(state));

 out:
    ipmi_cmdlang_unlock(cmd_info);
    ipmi_cmdlang_cmd_info_put(cmd_info);
}

static void
entity_hs_state(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_entity_get_hot_swap_state(entity,
					entity_hs_state_done,
					cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error getting hot-swap state";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_get_state)";
    }
}

static void
entity_hs_check(ipmi_entity_t *entity, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             rv;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    rv = ipmi_entity_check_hot_swap_state(entity);
    if (rv) {
	cmdlang->err = rv;
	cmdlang->errstr = "Error checking hot-swap state";
	ipmi_entity_get_name(entity, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_entity.c(entity_hs_check)";
    } else {
      ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
      ipmi_cmdlang_out(cmd_info, "Check started", entity_name);
    }
}


static void fru_change(enum ipmi_update_werr_e op,
		       int                     err,
		       ipmi_entity_t           *entity,
		       void                    *cb_data)
{
    char            *errstr;
    int             rv;
    ipmi_cmd_info_t *evi;
    ipmi_fru_t      *fru;
    char            entity_name[IPMI_ENTITY_NAME_LEN];
    char            errbuf[32];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    evi = ipmi_cmdlang_alloc_event_info();
    if (!evi) {
	rv = ENOMEM;
	errstr = "Out of memory";
	goto out_err;
    }

    ipmi_cmdlang_out(evi, "Object Type", "Entity FRU");
    ipmi_cmdlang_out(evi, "Name", entity_name);

    switch (op) {
    case IPMIE_ADDED:
	ipmi_cmdlang_out(evi, "Operation", "Add");
	if (ipmi_cmdlang_get_evinfo()) {
	    ipmi_cmdlang_down(evi);
	    fru = ipmi_entity_get_fru(entity);
	    if (fru)
		ipmi_cmdlang_dump_fru_info(evi, fru);
	    ipmi_cmdlang_up(evi);
	}
	break;

    case IPMIE_DELETED:
	ipmi_cmdlang_out(evi, "Operation", "Delete");
	break;

    case IPMIE_CHANGED:
	ipmi_cmdlang_out(evi, "Operation", "Change");
	if (ipmi_cmdlang_get_evinfo()) {
	    ipmi_cmdlang_down(evi);
	    fru = ipmi_entity_get_fru(entity);
	    if (fru)
		ipmi_cmdlang_dump_fru_info(evi, fru);
	    ipmi_cmdlang_up(evi);
	}
	break;

    case IPMIE_ERROR:
	ipmi_cmdlang_out(evi, "Operation", "Error");
	ipmi_cmdlang_global_err(entity_name,
				"Error fetching FRU data",
				ipmi_get_error_string(err, errbuf,
						      sizeof(errbuf)),
				err);
	break;
    }

    ipmi_cmdlang_cmd_info_put(evi);
    return;

 out_err:
    ipmi_cmdlang_global_err(entity_name,
			    "cmd_entity.c(fru_change)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);
}

static int
presence_change(ipmi_entity_t *entity,
		int           present,
		void          *cb_data,
		ipmi_event_t  *event)
{
    char            *errstr;
    int             rv;
    ipmi_cmd_info_t *evi;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    evi = ipmi_cmdlang_alloc_event_info();
    if (!evi) {
	rv = ENOMEM;
	errstr = "Out of memory";
	goto out_err;
    }

    ipmi_cmdlang_out(evi, "Object Type", "Entity");
    ipmi_cmdlang_out(evi, "Name", entity_name);
    ipmi_cmdlang_out(evi, "Operation", "Presence Change");
    ipmi_cmdlang_out_bool(evi, "Present", present);

    if (event) {
	ipmi_cmdlang_out(evi, "Event", NULL);
	ipmi_cmdlang_down(evi);
	ipmi_cmdlang_event_out(event, evi);
	ipmi_cmdlang_up(evi);
    }

    ipmi_cmdlang_cmd_info_put(evi);
    return IPMI_EVENT_NOT_HANDLED;

 out_err:
    ipmi_cmdlang_global_err(entity_name,
			    "cmd_entity.c(presence_change)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);

    return IPMI_EVENT_NOT_HANDLED;
}

static void fully_up(ipmi_entity_t      *entity,
		     void               *cb_data)
{
    char            *errstr;
    int             rv;
    ipmi_cmd_info_t *evi;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    evi = ipmi_cmdlang_alloc_event_info();
    if (!evi) {
	rv = ENOMEM;
	errstr = "Out of memory";
	goto out_err;
    }

    ipmi_cmdlang_out(evi, "Object Type", "Entity");
    ipmi_cmdlang_out(evi, "Name", entity_name);
    ipmi_cmdlang_out(evi, "Operation", "Fully Up");
    ipmi_cmdlang_cmd_info_put(evi);
    return;

 out_err:
    ipmi_cmdlang_global_err(entity_name,
			    "cmd_entity.c(fully_up)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);
}

static int
entity_hot_swap(ipmi_entity_t             *entity,
		enum ipmi_hot_swap_states last_state,
		enum ipmi_hot_swap_states curr_state,
		void                      *cb_data,
		ipmi_event_t              *event)
{
    char            *errstr;
    int             rv;
    ipmi_cmd_info_t *evi;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    evi = ipmi_cmdlang_alloc_event_info();
    if (!evi) {
	rv = ENOMEM;
	errstr = "Out of memory";
	goto out_err;
    }

    ipmi_cmdlang_out(evi, "Object Type", "Entity");
    ipmi_cmdlang_out(evi, "Name", entity_name);
    ipmi_cmdlang_out(evi, "Operation", "Hot-Swap Change");
    ipmi_cmdlang_out(evi, "Last State", 
		     ipmi_hot_swap_state_name(last_state));
    ipmi_cmdlang_out(evi, "State", ipmi_hot_swap_state_name(curr_state));

    if (event) {
	ipmi_cmdlang_out(evi, "Event", NULL);
	ipmi_cmdlang_down(evi);
	ipmi_cmdlang_event_out(event, evi);
	ipmi_cmdlang_up(evi);
    }

    ipmi_cmdlang_cmd_info_put(evi);
    return IPMI_EVENT_NOT_HANDLED;

 out_err:
    ipmi_cmdlang_global_err(entity_name,
			    "cmd_entity.c(entity_hot_swap)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);

    return IPMI_EVENT_NOT_HANDLED;
}

void
ipmi_cmdlang_entity_change(enum ipmi_update_e op,
			   ipmi_domain_t      *domain,
			   ipmi_entity_t      *entity,
			   void               *cb_data)
{
    char            *errstr;
    int             rv;
    ipmi_cmd_info_t *evi;
    char            entity_name[IPMI_ENTITY_NAME_LEN];

    ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));

    evi = ipmi_cmdlang_alloc_event_info();
    if (!evi) {
	rv = ENOMEM;
	errstr = "Out of memory";
	goto out_err;
    }

    ipmi_cmdlang_out(evi, "Object Type", "Entity");
    ipmi_cmdlang_out(evi, "Name", entity_name);

    switch (op) {
    case IPMI_ADDED:
	ipmi_cmdlang_out(evi, "Operation", "Add");
	if (ipmi_cmdlang_get_evinfo())
	    entity_dump(entity, evi);

	rv = ipmi_entity_add_sensor_update_handler(entity,
						   ipmi_cmdlang_sensor_change,
						   entity);
	if (rv) {
	    errstr = "ipmi_entity_add_sensor_update_handler";
	    goto out_err;
	}
	rv = ipmi_entity_add_fru_update_werr_handler(entity,
						     fru_change,
						     entity);
	if (rv) {
	    errstr = "ipmi_entity_add_control_fru_handler";
	    goto out_err;
	}
	rv = ipmi_entity_add_presence_handler(entity,
					      presence_change,
					      NULL);
	if (rv) {
	    errstr = "ipmi_entity_add_presence_handler";
	    goto out_err;
	}
	rv = ipmi_entity_add_fully_up_handler(entity,
					      fully_up,
					      NULL);
	if (rv) {
	    errstr = "ipmi_entity_add_presence_handler";
	    goto out_err;
	}
	rv = ipmi_entity_add_control_update_handler
	    (entity,
	     ipmi_cmdlang_control_change,
	     entity);
	if (rv) {
	    errstr = "ipmi_entity_add_control_update_handler";
	    goto out_err;
	}
	rv = ipmi_entity_add_hot_swap_handler(entity,
					      entity_hot_swap,
					      NULL);
	if (rv) {
	    errstr = "ipmi_entity_add_hot_swap_handler";
	    goto out_err;
	}
	break;

	case IPMI_DELETED:
	    ipmi_cmdlang_out(evi, "Operation", "Delete");
	    break;

	case IPMI_CHANGED:
	    ipmi_cmdlang_out(evi, "Operation", "Change");
	    if (ipmi_cmdlang_get_evinfo())
		entity_dump(entity, evi);
	    break;
    }

    ipmi_cmdlang_cmd_info_put(evi);
    return;

 out_err:
    ipmi_cmdlang_global_err(entity_name,
			    "cmd_entity.c(ipmi_cmdlang_entity_change)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);
}

static ipmi_cmdlang_cmd_t *entity_cmds, *hs_cmds;

static ipmi_cmdlang_init_t cmds_entity[] =
{
    { "entity", NULL,
      "- Commands dealing with entities",
      NULL, NULL, &entity_cmds },
    { "list", &entity_cmds,
      "- List all the entities in the system",
      ipmi_cmdlang_domain_handler, entity_list, NULL },
    { "tree", &entity_cmds,
      "- List all the entities in the system in their tree structure",
      ipmi_cmdlang_domain_handler, entity_tree, NULL },
    { "info", &entity_cmds,
      "<entity> - Dump information about an entity",
      ipmi_cmdlang_entity_handler, entity_info, NULL },
    { "fru", &entity_cmds,
      "<entity> - Dump FRU information about an entity",
      ipmi_cmdlang_entity_handler, fru_info, NULL },
    { "hs", &entity_cmds,
      "- Commands dealing with hot-swap",
      NULL, NULL, &hs_cmds },
    { "get_act_time", &hs_cmds,
      "<entity> - Get the hot-swap auto-activate time",
      ipmi_cmdlang_entity_handler, entity_hs_get_act_time, NULL },
    { "set_act_time", &hs_cmds,
      "<entity> - Set the hot-swap auto-activate time",
      ipmi_cmdlang_entity_handler, entity_hs_set_act_time, NULL },
    { "get_deact_time", &hs_cmds,
      "<entity> - Get the hot-swap auto-deactivate time",
      ipmi_cmdlang_entity_handler, entity_hs_get_deact_time, NULL },
    { "set_deact_time", &hs_cmds,
      "<entity> - Set the hot-swap auto-deactivate time",
      ipmi_cmdlang_entity_handler, entity_hs_set_deact_time, NULL },
    { "activation_request", &hs_cmds,
      "<entity> Act like a user requested an"
      " activation of the entity.  This is generally equivalent to"
      " closing the handle latch or something like that.",
      ipmi_cmdlang_entity_handler, entity_hs_activation_request, NULL },
    { "activate", &hs_cmds,
      "<entity> - activate the given entity",
      ipmi_cmdlang_entity_handler, entity_hs_activate, NULL },
    { "deactivate", &hs_cmds,
      "<entity> - deactivate the given entity",
      ipmi_cmdlang_entity_handler, entity_hs_deactivate, NULL },
    { "state", &hs_cmds,
      "<entity> - Return the current hot-swap state of the given entity",
      ipmi_cmdlang_entity_handler, entity_hs_state, NULL },
    { "check", &hs_cmds,
      "<entity> - Check the hot-swap state of the entity.  This will"
      " not return anything, but will generate an event if the state"
      " is wrong",
      ipmi_cmdlang_entity_handler, entity_hs_check, NULL },
};
#define CMDS_ENTITY_LEN (sizeof(cmds_entity)/sizeof(ipmi_cmdlang_init_t))

int
ipmi_cmdlang_entity_init(os_handler_t *os_hnd)
{
    return ipmi_cmdlang_reg_table(cmds_entity, CMDS_ENTITY_LEN);
}
