/*
 * cmd_sensor.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 <stdio.h>
#include <OpenIPMI/ipmiif.h>
#include <OpenIPMI/ipmi_cmdlang.h>

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

static void
sensor_list_handler(ipmi_entity_t *entity, ipmi_sensor_t *sensor,
		    void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    if (cmdlang->err)
	return;

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

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

static void
sensor_list(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);
    ipmi_cmdlang_out(cmd_info, "Sensors", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_entity_iterate_sensors(entity, sensor_list_handler, cmd_info);
    ipmi_cmdlang_up(cmd_info);
    ipmi_cmdlang_up(cmd_info);
}

static void
sensor_dump(ipmi_sensor_t *sensor, ipmi_cmd_info_t *cmd_info)
{
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             num, lun;
    char            *str;
    const char      *cstr;
    int             event_support;
    int             event_reading_type;
    int             len;
    int             rv;
    int             val;

    event_reading_type = ipmi_sensor_get_event_reading_type(sensor);

    ipmi_sensor_get_num(sensor, &lun, &num);
    ipmi_cmdlang_out_int(cmd_info, "LUN", lun);
    ipmi_cmdlang_out_int(cmd_info, "Number", num);
    ipmi_cmdlang_out_int(cmd_info, "Event Reading Type",
		     ipmi_sensor_get_event_reading_type(sensor));
    ipmi_cmdlang_out(cmd_info, "Event Reading Type Name",
		     ipmi_sensor_get_event_reading_type_string(sensor));
    ipmi_cmdlang_out_int(cmd_info, "Type",
			 ipmi_sensor_get_sensor_type(sensor));
    ipmi_cmdlang_out(cmd_info, "Type Name",
		     ipmi_sensor_get_sensor_type_string(sensor));
    val = ipmi_sensor_get_sensor_direction(sensor);
    if (val != IPMI_SENSOR_DIRECTION_UNSPECIFIED)
	ipmi_cmdlang_out(cmd_info, "Direction",
			 ipmi_get_sensor_direction_string(val));
    
    event_support = ipmi_sensor_get_event_support(sensor);
    switch (event_support) {
    case IPMI_EVENT_SUPPORT_PER_STATE:
	ipmi_cmdlang_out(cmd_info, "Event Support", "per state");
	break;
    case IPMI_EVENT_SUPPORT_ENTIRE_SENSOR:
	ipmi_cmdlang_out(cmd_info, "Event Support", "entire sensor");
	break;
    case IPMI_EVENT_SUPPORT_GLOBAL_ENABLE:
	ipmi_cmdlang_out(cmd_info, "Event Support", "global");
	break;
    default:
	break;
    }

    ipmi_cmdlang_out_bool(cmd_info, "Init Scanning",
			 ipmi_sensor_get_sensor_init_scanning(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Init Events",
			 ipmi_sensor_get_sensor_init_events(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Init Thresholds",
			 ipmi_sensor_get_sensor_init_thresholds(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Init Hysteresis",
			 ipmi_sensor_get_sensor_init_hysteresis(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Init Type",
			 ipmi_sensor_get_sensor_init_type(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Init Power Up Events",
			 ipmi_sensor_get_sensor_init_pu_events(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Init Power Up Scanning",
			 ipmi_sensor_get_sensor_init_pu_scanning(sensor));

    ipmi_cmdlang_out_bool(cmd_info, "Ignore If No Entity",
			 ipmi_sensor_get_ignore_if_no_entity(sensor));
    ipmi_cmdlang_out_bool(cmd_info, "Auto Rearm",
			 ipmi_sensor_get_supports_auto_rearm(sensor));
    ipmi_cmdlang_out_int(cmd_info, "OEM1",
			 ipmi_sensor_get_oem1(sensor));

    len = ipmi_sensor_get_id_length(sensor);
    if (len) {
	str = ipmi_mem_alloc(len);
	if (!str) {
	    cmdlang->err = ENOMEM;
	    cmdlang->errstr = "Out of memory";
	    goto out_err;
	}
	len = ipmi_sensor_get_id(sensor, str, len);
	ipmi_cmdlang_out_type(cmd_info, "Id",
			      ipmi_sensor_get_id_type(sensor),
			      str, len);
	ipmi_mem_free(str);
    }

    if (event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD) {
	int access = ipmi_sensor_get_threshold_access(sensor);
	enum ipmi_thresh_e          thresh;
	enum ipmi_event_value_dir_e value_dir;
	enum ipmi_event_dir_e       dir;
	int                         rv;
	char                        th_name[50];
	double                      dval;

	ipmi_cmdlang_out(cmd_info, "Threshold Access",
			 ipmi_get_threshold_access_support_string(access));

	for (thresh = IPMI_LOWER_NON_CRITICAL;
	     thresh <= IPMI_UPPER_NON_RECOVERABLE; 
	     thresh++)
	{
	    rv = ipmi_sensor_threshold_reading_supported(sensor, thresh, &val);
	    if ((rv) || !val)
		continue;

	    ipmi_cmdlang_out(cmd_info, "Threshold", NULL);
	    ipmi_cmdlang_down(cmd_info);
	    ipmi_cmdlang_out(cmd_info, "Name",
			     ipmi_get_threshold_string(thresh));
	    rv = ipmi_sensor_threshold_readable(sensor, thresh, &val);
	    if (rv)
		val = 0;
	    ipmi_cmdlang_out_bool(cmd_info, "Readable", val);
	    rv = ipmi_sensor_threshold_settable(sensor, thresh, &val);
	    if (rv)
		val = 0;
	    ipmi_cmdlang_out_bool(cmd_info, "Settable", val);

	    for (value_dir = IPMI_GOING_LOW;
		 value_dir <= IPMI_GOING_HIGH;
		 value_dir++)
	    {
		for (dir = IPMI_ASSERTION;
		     dir <= IPMI_DEASSERTION;
		     dir++)
		{
		    rv = ipmi_sensor_threshold_event_supported(sensor,
							       thresh,
							       value_dir,
							       dir,
							       &val);
		    if (rv || !val) continue;

		    snprintf(th_name, sizeof(th_name), "%s %s",
			     ipmi_get_value_dir_string(value_dir),
			     ipmi_get_event_dir_string(dir));
		    ipmi_cmdlang_out(cmd_info, "Supports", th_name);
		}
	    }
	    ipmi_cmdlang_up(cmd_info);
	}

	val = ipmi_sensor_get_hysteresis_support(sensor);
	ipmi_cmdlang_out(cmd_info, "Hysteresis Support",
			 ipmi_get_hysteresis_support_string(val));

#if 0
	/* FIXME - no accuracy handling */
	int ipmi_sensor_get_accuracy(ipmi_sensor_t *sensor, int val,
				     double *accuracy);
#endif

	rv = ipmi_sensor_get_nominal_reading(sensor, &dval);
	if (!rv)
	    ipmi_cmdlang_out_double(cmd_info, "Nominal Reading", dval);
	rv = ipmi_sensor_get_normal_max(sensor, &dval);
	if (!rv)
	    ipmi_cmdlang_out_double(cmd_info, "Normal Max", dval);
	rv = ipmi_sensor_get_normal_min(sensor, &dval);
	if (!rv)
	    ipmi_cmdlang_out_double(cmd_info, "Normal Min", dval);
	rv = ipmi_sensor_get_sensor_max(sensor, &dval);
	if (!rv)
	    ipmi_cmdlang_out_double(cmd_info, "Sensor Max", dval);
	rv = ipmi_sensor_get_sensor_min(sensor, &dval);
	if (!rv)
	    ipmi_cmdlang_out_double(cmd_info, "Sensor Min", dval);

	ipmi_cmdlang_out_int(cmd_info, "Base Unit",
			     ipmi_sensor_get_base_unit(sensor));
	ipmi_cmdlang_out(cmd_info, "Base Unit Name",
			 ipmi_sensor_get_base_unit_string(sensor));
	cstr = ipmi_sensor_get_rate_unit_string(sensor);
	if (strlen(cstr)) {
	    ipmi_cmdlang_out_int(cmd_info, "Rate Unit",
				 ipmi_sensor_get_rate_unit(sensor));
	    ipmi_cmdlang_out(cmd_info, "Rate Unit Name", cstr);
	}
	switch (ipmi_sensor_get_modifier_unit_use(sensor)) {
	case IPMI_MODIFIER_UNIT_BASE_DIV_MOD:
	    ipmi_cmdlang_out(cmd_info, "Modifier Use", "/");
	    ipmi_cmdlang_out_int(cmd_info, "Modifier Unit",
				 ipmi_sensor_get_modifier_unit(sensor));
	    ipmi_cmdlang_out(cmd_info, "Modifier Unit Name",
			     ipmi_sensor_get_modifier_unit_string(sensor));
	    break;
		
	case IPMI_MODIFIER_UNIT_BASE_MULT_MOD:
	    ipmi_cmdlang_out(cmd_info, "Modifier Use", "*");
	    ipmi_cmdlang_out_int(cmd_info, "Modifier Unit",
				 ipmi_sensor_get_modifier_unit(sensor));
	    ipmi_cmdlang_out(cmd_info, "Modifier Unit Name",
			     ipmi_sensor_get_modifier_unit_string(sensor));
	    break;

	default:
	    break;
	}
	if (ipmi_sensor_get_percentage(sensor))
	    ipmi_cmdlang_out(cmd_info, "Percentage", "%");
    } else {
	int                   event;
	enum ipmi_event_dir_e dir;

	for (event=0; event<15; event++) {
	    rv = ipmi_sensor_discrete_event_readable(sensor, event, &val);
	    if (rv || !val)
		continue;
	    ipmi_cmdlang_out(cmd_info, "Event", NULL);
	    ipmi_cmdlang_down(cmd_info);
	    ipmi_cmdlang_out_int(cmd_info, "Offset", event);
	    cstr = ipmi_sensor_reading_name_string(sensor, event);
	    if (strcmp(cstr, "unknown") != 0)
		ipmi_cmdlang_out(cmd_info, "Name", cstr);
	    
	    for (dir = IPMI_ASSERTION;
		 dir <= IPMI_DEASSERTION;
		 dir++)
	    {
		rv = ipmi_sensor_discrete_event_supported(sensor,
							  event,
							  dir,
							  &val);
		if (rv || !val) continue;

		ipmi_cmdlang_out(cmd_info, "Supports",
				 ipmi_get_event_dir_string(dir));
	    }
	    ipmi_cmdlang_up(cmd_info);
	}
    }
    return;

 out_err:
    ipmi_sensor_get_name(sensor, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_sensor.c(sensor_dump)";
}

static void
sensor_info(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

    ipmi_cmdlang_out(cmd_info, "Sensor", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", sensor_name);
    sensor_dump(sensor, cmd_info);
    ipmi_cmdlang_up(cmd_info);
}

static void
read_sensor(ipmi_sensor_t             *sensor,
	    int                       err,
	    enum ipmi_value_present_e value_present,
	    unsigned int              raw_val,
	    double                    val,
	    ipmi_states_t             *states,
	    void                      *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    enum ipmi_thresh_e thresh;
    char               sensor_name[IPMI_SENSOR_NAME_LEN];
    int                rv;

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading sensor";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(read_sensor)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

    ipmi_cmdlang_out(cmd_info, "Sensor", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", sensor_name);

    ipmi_cmdlang_out_bool(cmd_info, "Event Messages Enabled",
			  ipmi_is_event_messages_enabled(states));
    ipmi_cmdlang_out_bool(cmd_info, "Sensor Scanning Enabled",
			  ipmi_is_sensor_scanning_enabled(states));
    ipmi_cmdlang_out_bool(cmd_info, "Initial Update In Progress",
			  ipmi_is_initial_update_in_progress(states));

    switch (value_present) {
    case IPMI_BOTH_VALUES_PRESENT:
	ipmi_cmdlang_out_double(cmd_info, "Value", val);
	/* FALLTHRU */
    case IPMI_RAW_VALUE_PRESENT:
	ipmi_cmdlang_out_hex(cmd_info, "Raw Value", raw_val);
    default:
	break;
    }	

    for (thresh = IPMI_LOWER_NON_CRITICAL;
	 thresh <= IPMI_UPPER_NON_RECOVERABLE; 
	 thresh++)
    {
	int ival;

	rv = ipmi_sensor_threshold_reading_supported(sensor, thresh, &ival);
	if ((rv) || !ival)
	    continue;

	ipmi_cmdlang_out(cmd_info, "Threshold", NULL);
	ipmi_cmdlang_down(cmd_info);
	ipmi_cmdlang_out(cmd_info, "Name",
			 ipmi_get_threshold_string(thresh));
	ipmi_cmdlang_out_bool(cmd_info, "Out Of Range",
			      ipmi_is_threshold_out_of_range(states, thresh));
	ipmi_cmdlang_up(cmd_info);
    }
    ipmi_cmdlang_up(cmd_info);

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

static void
read_sensor_states(ipmi_sensor_t *sensor,
		   int           err,
		   ipmi_states_t *states,
		   void          *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int             i;
    char            sensor_name[IPMI_SENSOR_NAME_LEN];
    int             rv;

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading sensor";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(read_sensor_states)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

    ipmi_cmdlang_out(cmd_info, "Sensor", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", sensor_name);

    ipmi_cmdlang_out_bool(cmd_info, "Event Messages Enabled",
			  ipmi_is_event_messages_enabled(states));
    ipmi_cmdlang_out_bool(cmd_info, "Sensor Scanning Enabled",
			  ipmi_is_sensor_scanning_enabled(states));
    ipmi_cmdlang_out_bool(cmd_info, "Initial Update In Progress",
			  ipmi_is_initial_update_in_progress(states));
    for (i=0; i<15; i++) {
	int        ival;
	const char *str;

	rv = ipmi_sensor_discrete_event_readable(sensor, i, &ival);
	if ((rv) || !ival)
	    continue;

	ipmi_cmdlang_out(cmd_info, "Event", NULL);
	ipmi_cmdlang_down(cmd_info);
	ipmi_cmdlang_out_int(cmd_info, "Offset", i);
	str = ipmi_sensor_reading_name_string(sensor, i);
	if (strcmp(str, "unknown") != 0)
	    ipmi_cmdlang_out(cmd_info, "Name", str);
	ipmi_cmdlang_out_bool(cmd_info, "Set", ipmi_is_state_set(states, i));
	ipmi_cmdlang_up(cmd_info);
    }
    ipmi_cmdlang_up(cmd_info);

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

static void
sensor_get(ipmi_sensor_t *sensor, 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);
    if (ipmi_sensor_get_event_reading_type(sensor)
	== IPMI_EVENT_READING_TYPE_THRESHOLD)
    {
	rv = ipmi_sensor_get_reading(sensor, read_sensor, cmd_info);
    } else {
	rv = ipmi_sensor_get_states(sensor, read_sensor_states, cmd_info);
    }
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error reading sensor";
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get)";
    }
}

static void
sensor_rearm_done(ipmi_sensor_t *sensor,
		  int           err,
		  void          *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error rearming sensor";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_rearm_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Rearm done", sensor_name);

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

static void
sensor_rearm(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int                rv;
    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);
    int                global;
    ipmi_event_state_t *s = NULL;

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

    if (strcmp(argv[curr_arg], "global") == 0) {
	global = 1;
    } else {
	global = 0;
	s = ipmi_mem_alloc(ipmi_event_state_size());
	if (!s) {
	    cmdlang->errstr = "Out of memory";
	    cmdlang->err = ENOMEM;
	    goto out_err;
	}
	ipmi_event_state_init(s);

	if (ipmi_sensor_get_event_reading_type(sensor)
	    == IPMI_EVENT_READING_TYPE_THRESHOLD)
	{
	    enum ipmi_thresh_e          thresh;
	    enum ipmi_event_value_dir_e value_dir;
	    enum ipmi_event_dir_e       dir;

	    while (curr_arg < argc) {
		ipmi_cmdlang_get_threshold_ev(argv[curr_arg], &thresh,
					      &value_dir, &dir, cmd_info);
		if (cmdlang->err) {
		    goto out_err;
		}
		ipmi_threshold_event_set(s, thresh, value_dir, dir);
		curr_arg++;
	    }
	} else {
	    int                   offset;
	    enum ipmi_event_dir_e dir;
	    
	    while (curr_arg < argc) {
		ipmi_cmdlang_get_discrete_ev(argv[curr_arg], &offset,
					     &dir, cmd_info);
		if (cmdlang->err) {
		    goto out_err;
		}
		ipmi_discrete_event_set(s, offset, dir);
		curr_arg++;
	    }
	}
    }

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_sensor_rearm(sensor, global, s, sensor_rearm_done, cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error rearming sensor";
	goto out_err;
    }

    if (s)
	ipmi_mem_free(s);
    return;

 out_err:
    ipmi_sensor_get_name(sensor, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_sensor.c(sensor_rearm)";
    if (s)
	ipmi_mem_free(s);
}

static void
sensor_get_thresholds_done(ipmi_sensor_t     *sensor,
			   int               err,
			   ipmi_thresholds_t *th,
			   void              *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               sensor_name[IPMI_SENSOR_NAME_LEN];
    enum ipmi_thresh_e thresh;
    int                rv;

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading sensor thresholds";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get_thresholds_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Sensor", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", sensor_name);

    for (thresh = IPMI_LOWER_NON_CRITICAL;
	 thresh <= IPMI_UPPER_NON_RECOVERABLE; 
	 thresh++)
    {
	int ival;
	double dval;

	rv = ipmi_sensor_threshold_reading_supported(sensor, thresh, &ival);
	if ((rv) || !ival)
	    continue;

	ipmi_cmdlang_out(cmd_info, "Threshold", NULL);
	ipmi_cmdlang_down(cmd_info);
	ipmi_cmdlang_out(cmd_info, "Name",
			 ipmi_get_threshold_string(thresh));
	rv = ipmi_threshold_get(th, thresh, &dval);
	if (rv)
	    continue;
	ipmi_cmdlang_out_double(cmd_info, "Value", dval);
	ipmi_cmdlang_up(cmd_info);
    }
    ipmi_cmdlang_up(cmd_info);

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

static void
sensor_get_thresholds(ipmi_sensor_t *sensor, 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_sensor_get_thresholds(sensor, sensor_get_thresholds_done,
				    cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error getting thresholds";
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get_thresholds)";
    }
}

static void
sensor_set_thresholds_done(ipmi_sensor_t *sensor,
			   int           err,
			   void          *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error setting sensor thresholds";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_set_thresholds_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Thresholds set", sensor_name);

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

static void
sensor_set_thresholds(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int                rv;
    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);
    ipmi_thresholds_t  *th = NULL;
    enum ipmi_thresh_e thresh;
    double             val;

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

    th = ipmi_mem_alloc(ipmi_thresholds_size());
    if (!th) {
	cmdlang->errstr = "Out of memory";
	cmdlang->err = ENOMEM;
	goto out_err;
    }
    ipmi_thresholds_init(th);
    while (curr_arg < argc) {
	ipmi_cmdlang_get_threshold(argv[curr_arg], &thresh, cmd_info);
	if (cmdlang->err) {
	    cmdlang->errstr = "Invalid threshold";
	    goto out_err;
	}
	curr_arg++;

	ipmi_cmdlang_get_double(argv[curr_arg], &val, cmd_info);
	if (cmdlang->err) {
	    cmdlang->errstr = "Invalid threshold value";
	    goto out_err;
	}
	curr_arg++;

	rv = ipmi_threshold_set(th, sensor, thresh, val);
	if (rv) {
	    cmdlang->errstr = "Error setting value";
	    cmdlang->err = rv;
	    goto out_err;
	}
    }

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_sensor_set_thresholds(sensor, th, sensor_set_thresholds_done,
				    cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error setting thresholds";
	goto out_err;
    }
    ipmi_mem_free(th);
    return;

 out_err:
    ipmi_sensor_get_name(sensor, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_sensor.c(sensor_set_thresholds)";
    if (th)
	ipmi_mem_free(th);
}

static void
sensor_get_hysteresis_done(ipmi_sensor_t *sensor,
			   int           err,
			   unsigned int  positive_hysteresis,
			   unsigned int  negative_hysteresis,
			   void          *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading sensor hysteresis";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get_hysteresis_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Sensor", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", sensor_name);

    ipmi_cmdlang_out_int(cmd_info, "Positive Hysteresis",
			 positive_hysteresis);
    ipmi_cmdlang_out_int(cmd_info, "Negative Hysteresis",
			 negative_hysteresis);

    ipmi_cmdlang_up(cmd_info);
    

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

static void
sensor_get_hysteresis(ipmi_sensor_t *sensor, 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_sensor_get_hysteresis(sensor, sensor_get_hysteresis_done,
				    cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error getting hysteresis";
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get_hysteresis)";
    }
}

static void
sensor_set_hysteresis_done(ipmi_sensor_t *sensor,
			   int           err,
			   void          *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error setting sensor hysteresis";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_set_hysteresis_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Hysteresis set", sensor_name);

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

static void
sensor_set_hysteresis(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int                rv;
    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);
    int                pos, neg;

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

    ipmi_cmdlang_get_int(argv[curr_arg], &pos, cmd_info);
    if (cmdlang->err) {
	cmdlang->errstr = "Invalid positive hysteresis";
	goto out_err;
    }
    curr_arg++;

    ipmi_cmdlang_get_int(argv[curr_arg], &neg, cmd_info);
    if (cmdlang->err) {
	cmdlang->errstr = "Invalid negative hysteresis";
	goto out_err;
    }
    curr_arg++;

    ipmi_cmdlang_cmd_info_get(cmd_info);
    rv = ipmi_sensor_set_hysteresis(sensor, pos, neg,
				    sensor_set_hysteresis_done, cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error setting hysteresis";
	goto out_err;
    }

 out_err:
    ipmi_sensor_get_name(sensor, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_sensor.c(sensor_set_hysteresis)";
}

static void
sensor_get_event_enables_done(ipmi_sensor_t      *sensor,
			      int                err,
			      ipmi_event_state_t *states,
			      void               *cb_data)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char               sensor_name[IPMI_SENSOR_NAME_LEN];
    int                rv;
    int                val;

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error reading sensor event enables";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get_event_enables_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Sensor", NULL);
    ipmi_cmdlang_down(cmd_info);
    ipmi_cmdlang_out(cmd_info, "Name", sensor_name);

    ipmi_cmdlang_out_bool(cmd_info, "Event Messages Enabled",
			  ipmi_event_state_get_events_enabled(states));
    ipmi_cmdlang_out_bool(cmd_info, "Sensor Scanning Enabled",
			  ipmi_event_state_get_scanning_enabled(states));
    ipmi_cmdlang_out_bool(cmd_info, "Busy",
			  ipmi_event_state_get_busy(states));

    if (ipmi_sensor_get_event_reading_type(sensor)
	== IPMI_EVENT_READING_TYPE_THRESHOLD)
    {
	enum ipmi_thresh_e          thresh;
	enum ipmi_event_value_dir_e value_dir;
	enum ipmi_event_dir_e       dir;

	for (thresh = IPMI_LOWER_NON_CRITICAL;
	     thresh <= IPMI_UPPER_NON_RECOVERABLE; 
	     thresh++)
	{
	    for (value_dir = IPMI_GOING_LOW;
		 value_dir <= IPMI_GOING_HIGH;
		 value_dir++)
	    {
		for (dir = IPMI_ASSERTION;
		     dir <= IPMI_DEASSERTION;
		     dir++)
		{
		    char th_name[50];

		    rv = ipmi_sensor_threshold_event_supported(sensor,
							       thresh,
							       value_dir,
							       dir,
							       &val);
		    if (rv || !val) continue;

		    ipmi_cmdlang_out(cmd_info, "Threshold", NULL);
		    ipmi_cmdlang_down(cmd_info);
		    snprintf(th_name, sizeof(th_name), "%s %s %s",
			     ipmi_get_threshold_string(thresh),
			     ipmi_get_value_dir_string(value_dir),
			     ipmi_get_event_dir_string(dir));
		    ipmi_cmdlang_out(cmd_info, "Name", th_name);
		    ipmi_cmdlang_out_bool(cmd_info, "Enabled",
					  ipmi_is_threshold_event_set
					  (states, thresh, value_dir, dir));
		    ipmi_cmdlang_up(cmd_info);
		}
	    }
	}
    } else {
	int        offset;
	const char *str;

	for (offset=0; offset<15; offset++) {
	    rv = ipmi_sensor_discrete_event_readable(sensor, offset, &val);
	    if (rv || !val)
		continue;
	    ipmi_cmdlang_out(cmd_info, "Event", NULL);
	    ipmi_cmdlang_down(cmd_info);
	    ipmi_cmdlang_out_int(cmd_info, "Offset", offset);
	    str = ipmi_sensor_reading_name_string(sensor, offset);
	    if (strcmp(str, "unknown") != 0)
		ipmi_cmdlang_out(cmd_info, "Name", str);
	    
	    rv = ipmi_sensor_discrete_event_supported(sensor,
						      offset,
						      IPMI_ASSERTION,
						      &val);
	    if (!rv && val) {
		ipmi_cmdlang_out_bool(cmd_info, "Assertion Enabled",
				      ipmi_is_discrete_event_set
				      (states, offset, IPMI_ASSERTION));
	    }
	    rv = ipmi_sensor_discrete_event_supported(sensor,
						      offset,
						      IPMI_DEASSERTION,
						      &val);
	    if (!rv && val) {
		ipmi_cmdlang_out_bool(cmd_info, "Deassertion Enabled",
				      ipmi_is_discrete_event_set
				      (states, offset, IPMI_DEASSERTION));
	    }
	    ipmi_cmdlang_up(cmd_info);
	}
    }

    ipmi_cmdlang_up(cmd_info);
    

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

static void
sensor_get_event_enables(ipmi_sensor_t *sensor, 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_sensor_get_event_enables(sensor, sensor_get_event_enables_done,
				       cmd_info);
    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error getting event enables";
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_get_event_enables)";
    }
}

static void
sensor_set_event_enables_done(ipmi_sensor_t *sensor,
			      int           err,
			      void          *cb_data)
{
    ipmi_cmd_info_t *cmd_info = cb_data;
    ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_cmdlang_lock(cmd_info);
    if (err) {
	cmdlang->errstr = "Error setting event enables";
	cmdlang->err = err;
	ipmi_sensor_get_name(sensor, cmdlang->objstr,
			     cmdlang->objstr_len);
	cmdlang->location = "cmd_sensor.c(sensor_set_event_enables_done)";
	goto out;
    }

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
    ipmi_cmdlang_out(cmd_info, "Event enables set", sensor_name);

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

enum ev_en_kind { ev_en_set, ev_en_enable, ev_en_disable };

static void
mod_event_enables(ipmi_sensor_t *sensor, void *cb_data, enum ev_en_kind kind)
{
    ipmi_cmd_info_t    *cmd_info = cb_data;
    ipmi_cmdlang_t     *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
    int                rv;
    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);
    ipmi_event_state_t *s = NULL;

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

    s = ipmi_mem_alloc(ipmi_states_size());
    if (!s) {
	cmdlang->errstr = "Out of memory";
	cmdlang->err = ENOMEM;
	goto out_err;
    }
    ipmi_event_state_init(s);

    if (strcmp(argv[curr_arg], "msg") == 0)
	ipmi_event_state_set_events_enabled(s, 1);
    else if (strcmp(argv[curr_arg], "nomsg") == 0)
	ipmi_event_state_set_events_enabled(s, 0);
    else {
	cmdlang->errstr = "Invalid message enable setting";
	cmdlang->err = EINVAL;
	goto out_err;
    }
    curr_arg++;

    if (strcmp(argv[curr_arg], "scan") == 0)
	ipmi_event_state_set_scanning_enabled(s, 1);
    else if (strcmp(argv[curr_arg], "noscan") == 0)
	ipmi_event_state_set_scanning_enabled(s, 0);
    else {
	cmdlang->errstr = "Invalid scanning enable setting";
	cmdlang->err = EINVAL;
	goto out_err;
    }
    curr_arg++;

    if (ipmi_sensor_get_event_reading_type(sensor)
	== IPMI_EVENT_READING_TYPE_THRESHOLD)
    {
	while (curr_arg < argc) {
	    enum ipmi_thresh_e          thresh;
	    enum ipmi_event_value_dir_e value_dir;
	    enum ipmi_event_dir_e       dir;

	    ipmi_cmdlang_get_threshold_ev(argv[curr_arg], &thresh,
					  &value_dir, &dir, cmd_info);
	    if (cmdlang->err) {
		goto out_err;
	    }
	    ipmi_threshold_event_set(s, thresh, value_dir, dir);
	    curr_arg++;
	}
    } else {
	while (curr_arg < argc) {
	    int                   offset;
	    enum ipmi_event_dir_e dir;

	    ipmi_cmdlang_get_discrete_ev(argv[curr_arg], &offset,
					 &dir, cmd_info);
	    if (cmdlang->err) {
		goto out_err;
	    }
	    ipmi_discrete_event_set(s, offset, dir);
	    curr_arg++;
	}
    }

    ipmi_cmdlang_cmd_info_get(cmd_info);
    switch (kind) {
    case ev_en_set:
	rv = ipmi_sensor_set_event_enables(sensor, s,
					   sensor_set_event_enables_done,
					   cmd_info);
	break;
    case ev_en_enable:
	rv = ipmi_sensor_enable_events(sensor, s,
				       sensor_set_event_enables_done,
				       cmd_info);
	break;
    case ev_en_disable:
	rv = ipmi_sensor_disable_events(sensor, s,
					sensor_set_event_enables_done,
					cmd_info);
	break;
    default:
	rv = EINVAL;
    }

    if (rv) {
	ipmi_cmdlang_cmd_info_put(cmd_info);
	cmdlang->err = rv;
	cmdlang->errstr = "Error setting event enables";
	goto out_err;
    }
    ipmi_mem_free(s);
    return;

 out_err:
    ipmi_sensor_get_name(sensor, cmdlang->objstr,
			 cmdlang->objstr_len);
    cmdlang->location = "cmd_sensor.c(sensor_set_event_enables)";
    if (s)
	ipmi_mem_free(s);
}

static void
sensor_set_event_enables(ipmi_sensor_t *sensor, void *cb_data)
{
    mod_event_enables(sensor, cb_data, ev_en_set);
}

static void
sensor_enable_events(ipmi_sensor_t *sensor, void *cb_data)
{
    mod_event_enables(sensor, cb_data, ev_en_enable);
}

static void
sensor_disable_events(ipmi_sensor_t *sensor, void *cb_data)
{
    mod_event_enables(sensor, cb_data, ev_en_disable);
}

static int
sensor_discrete_event_handler(ipmi_sensor_t         *sensor,
			      enum ipmi_event_dir_e dir,
			      int                   offset,
			      int                   severity,
			      int                   prev_severity,
			      void                  *cb_data,
			      ipmi_event_t          *event)
{
    ipmi_cmd_info_t *evi;
    char            sensor_name[IPMI_SENSOR_NAME_LEN];
    int             rv;
    char            *errstr;

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

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

    ipmi_cmdlang_out(evi, "Object Type", "Sensor");
    ipmi_cmdlang_out(evi, "Name", sensor_name);
    ipmi_cmdlang_out(evi, "Operation", "Event");
    ipmi_cmdlang_out_int(evi, "Offset", offset);
    ipmi_cmdlang_out(evi, "Direction", ipmi_get_event_dir_string(dir));
    ipmi_cmdlang_out_int(evi, "Severity", severity);
    ipmi_cmdlang_out_int(evi, "Previous Severity", prev_severity);
    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(sensor_name,
			    "cmd_sensor.c(sensor_discrete_event_handler)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);
    return IPMI_EVENT_NOT_HANDLED;
}

static int
sensor_threshold_event_handler(ipmi_sensor_t               *sensor,
			       enum ipmi_event_dir_e       dir,
			       enum ipmi_thresh_e          threshold,
			       enum ipmi_event_value_dir_e high_low,
			       enum ipmi_value_present_e   value_present,
			       unsigned int                raw_value,
			       double                      value,
			       void                        *cb_data,
			       ipmi_event_t                *event)
{
    ipmi_cmd_info_t *evi;
    char            sensor_name[IPMI_SENSOR_NAME_LEN];
    int             rv;
    char            *errstr;

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

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

    ipmi_cmdlang_out(evi, "Object Type", "Sensor");
    ipmi_cmdlang_out(evi, "Name", sensor_name);
    ipmi_cmdlang_out(evi, "Operation", "Event");
    ipmi_cmdlang_out(evi, "Threshold", ipmi_get_threshold_string(threshold));
    ipmi_cmdlang_out(evi, "High/Low", ipmi_get_value_dir_string(high_low));
    ipmi_cmdlang_out(evi, "Direction", ipmi_get_event_dir_string(dir));
    switch (value_present) {
    case IPMI_BOTH_VALUES_PRESENT:
	ipmi_cmdlang_out_double(evi, "Value", value);
	/* FALLTHRU */
    case IPMI_RAW_VALUE_PRESENT:
	ipmi_cmdlang_out_int(evi, "Raw Value", raw_value);
	break;

    default:
	break;
    }
    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(sensor_name,
			    "cmd_sensor.c(sensor_threshold_event_handler)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);
    return IPMI_EVENT_NOT_HANDLED;
}

void
ipmi_cmdlang_sensor_change(enum ipmi_update_e op,
			   ipmi_entity_t      *entity,
			   ipmi_sensor_t      *sensor,
			   void               *cb_data)
{
    char            *errstr;
    int             rv;
    ipmi_cmd_info_t *evi;
    char            sensor_name[IPMI_SENSOR_NAME_LEN];

    ipmi_sensor_get_name(sensor, sensor_name, sizeof(sensor_name));

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

    ipmi_cmdlang_out(evi, "Object Type", "Sensor");
    ipmi_cmdlang_out(evi, "Name", sensor_name);

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

	if (ipmi_sensor_get_event_reading_type(sensor)
	    == IPMI_EVENT_READING_TYPE_THRESHOLD)
	{
	    rv = ipmi_sensor_add_threshold_event_handler
		(sensor,
		 sensor_threshold_event_handler,
		 NULL);
	} else {
	    rv = ipmi_sensor_add_discrete_event_handler
		(sensor,
		 sensor_discrete_event_handler,
		 NULL);
	}
	if (rv) {
	    ipmi_cmdlang_global_err(sensor_name,
				    "cmd_sensor.c(ipmi_cmdlang_sensor_change)",
				    "Unable to set event handler for sensor",
				    rv);
	}
	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())
		sensor_dump(sensor, evi);
	    break;
    }

    ipmi_cmdlang_cmd_info_put(evi);
    return;

 out_err:
    ipmi_cmdlang_global_err(sensor_name,
			    "cmd_sensor.c(ipmi_cmdlang_sensor_change)",
			    errstr, rv);
    if (evi)
	ipmi_cmdlang_cmd_info_put(evi);
}

static ipmi_cmdlang_cmd_t *sensor_cmds;

static ipmi_cmdlang_init_t cmds_sensor[] =
{
    { "sensor", NULL,
      "- Commands dealing with sensors",
      NULL, NULL, &sensor_cmds },
    { "list", &sensor_cmds,
      "- List all the entities in the system",
      ipmi_cmdlang_entity_handler, sensor_list, NULL },
    { "info", &sensor_cmds,
      "<sensor> - Dump information about an sensor",
      ipmi_cmdlang_sensor_handler, sensor_info, NULL },
    { "get", &sensor_cmds,
      "<sensor> - Get the sensor's current reading",
      ipmi_cmdlang_sensor_handler, sensor_get, NULL },
    { "rearm", &sensor_cmds,
      "<sensor> global | <thresholds> | <discrete states> - "
      " Rearm the sensor.  If global is specified, then rearm"
      " all events in the sensor.  If it is a threshold sensor, then"
      " put in a list of thresholds of the form '[ul][ncr][hl][ad]"
      " where [ul] means upper or lower, [ncr] means non-critical,"
      " critical, or non-recoverable, [hl] means going high or going"
      " low, and [ad] means assertion or deassertion.  If it is a"
      " discrete sensor, then the form is <num>[ad] where the number"
      " is the offset and [ad] means assertion or deassertion",
      ipmi_cmdlang_sensor_handler, sensor_rearm, NULL },
    { "get_thresholds", &sensor_cmds,
      "<sensor> - Get the sensor's thresholds",
      ipmi_cmdlang_sensor_handler, sensor_get_thresholds, NULL },
    { "set_thresholds", &sensor_cmds,
      "<sensor> <threshold> <value> ... - Set the sensor's thresholds to"
      " the given values.  If a threshold is not specified, it will not"
      " be modified.  Thresholds are un, uc, ur, lr, lc, ln.  The u stands"
      " for upper, l for lower, n for non-critical, c for critical, and"
      " r for non-recoverable.  The value is floating point.",
      ipmi_cmdlang_sensor_handler, sensor_set_thresholds, NULL },
    { "get_hysteresis", &sensor_cmds,
      "<sensor> - Get the sensor's hysteresis values",
      ipmi_cmdlang_sensor_handler, sensor_get_hysteresis, NULL },
    { "set_hysteresis", &sensor_cmds,
      "<sensor> <pos hyst> <neg hyst> - Set the sensor's"
      " hysteresis to the given values.  These are raw integer"
      " value; hystersis is specified as a raw value and it cannot be"
      " converted to floating point because the function may be"
      " non-linear.",
      ipmi_cmdlang_sensor_handler, sensor_set_hysteresis, NULL },
    { "get_event_enables", &sensor_cmds,
      "<sensor> - Get the sensor's event enable values",
      ipmi_cmdlang_sensor_handler, sensor_get_event_enables, NULL },
    { "set_event_enables", &sensor_cmds,
      "<sensor> msg|nomsg scan|noscan [<event> [<event> ...]]- Set the"
      " sensor's event enable values.  This turns sensor messages and"
      " scanning on and off and will enable all the listed events and"
      " disable all over events.  The"
      " events are in the same format as the rearm subcommand and depend"
      " on the sensor type.  See the rearm command for details.",
      ipmi_cmdlang_sensor_handler, sensor_set_event_enables, NULL },
    { "enable_events", &sensor_cmds,
      "<sensor> msg|nomsg scan|noscan [<event> [<event> ...]]- Enable"
      " event enable values.  This turns sensor messages and"
      " scanning on and off and will enable all the listed events.  The"
      " events are in the same format as the rearm subcommand and depend"
      " on the sensor type.  See the rearm command for details.  This will"
      " only enable the given events, the other events will be left alone.",
      ipmi_cmdlang_sensor_handler, sensor_enable_events, NULL },
    { "disable_events", &sensor_cmds,
      "<sensor> msg|nomsg scan|noscan [<event> [<event> ...]]- Disable"
      " event enable values.  This turns sensor messages and"
      " scanning on and off and will disable all the listed events.  The"
      " events are in the same format as the rearm subcommand and depend"
      " on the sensor type.  See the rearm command for details.  This will"
      " only disable the given events, the other events will be left alone.",
      ipmi_cmdlang_sensor_handler, sensor_disable_events, NULL },
};
#define CMDS_SENSOR_LEN (sizeof(cmds_sensor)/sizeof(ipmi_cmdlang_init_t))

int
ipmi_cmdlang_sensor_init(os_handler_t *os_hnd)
{
    return ipmi_cmdlang_reg_table(cmds_sensor, CMDS_SENSOR_LEN);
}
