static int
_ipmi_monitoring_sel_parse_timestamped_oem_record (ipmi_monitoring_ctx_t c,
                                                   struct ipmi_monitoring_sel_record *s)
{
  uint32_t timestamp;
  uint32_t manufacturer_id;
  int ret;

  assert (c);
  assert (c->magic == IPMI_MONITORING_MAGIC);
  assert (s);

  if (ipmi_sel_parse_read_timestamp (c->sel_parse_ctx,
				     NULL,
				     0,
				     &timestamp) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_timestamp: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->timestamp = timestamp;
  
  if (ipmi_sel_parse_read_manufacturer_id (c->sel_parse_ctx,
					   NULL,
					   0,
					   &manufacturer_id) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_manufacturer_id: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->manufacturer_id = manufacturer_id;

  if ((ret = ipmi_sel_parse_read_oem (c->sel_parse_ctx,
				      NULL,
				      0,
                                      s->oem_data,
                                      IPMI_MONITORING_OEM_DATA_MAX)) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_oem: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->oem_data_len = ret;

  return (0);
}
static int
_ipmi_monitoring_sel_parse_non_timestamped_oem_record (ipmi_monitoring_ctx_t c,
                                                       struct ipmi_monitoring_sel_record *s)
{
  int ret;

  assert (c);
  assert (c->magic == IPMI_MONITORING_MAGIC);
  assert (s);

  if ((ret = ipmi_sel_parse_read_oem (c->sel_parse_ctx,
				      NULL,
				      0,
                                      s->oem_data,
                                      IPMI_MONITORING_OEM_DATA_MAX)) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_oem: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->oem_data_len = ret;

  return (0);
}
static void
_sel_parse_ctx_error_convert (ipmi_monitoring_ctx_t c)
{
  int errnum;

  assert (c);
  assert (c->magic == IPMI_MONITORING_MAGIC);
  assert (c->ipmi_ctx);
  assert (c->sel_parse_ctx);

  errnum = ipmi_sel_ctx_errnum (c->sel_parse_ctx);

  /* if callback error - assume we set as needed */
  if (errnum != IPMI_SEL_ERR_CALLBACK_ERROR)
    {
      if (errnum == IPMI_SEL_ERR_IPMI_ERROR)
        c->errnum = IPMI_MONITORING_ERR_IPMI_ERROR;
      else if (errnum == IPMI_SEL_ERR_OUT_OF_MEMORY)
        c->errnum = IPMI_MONITORING_ERR_OUT_OF_MEMORY;
      else if (errnum == IPMI_SEL_ERR_SYSTEM_ERROR)
        c->errnum = IPMI_MONITORING_ERR_SYSTEM_ERROR;
      else
        c->errnum = IPMI_MONITORING_ERR_INTERNAL_ERROR;
    }
}
/* return -1 on failout error, 0 on invalid data */
static int
_sel_parse_err_handle (pstdout_state_t pstate,
                       ipmi_sel_ctx_t sel_ctx,
                       int debug,
                       const char *func)
{
  assert (sel_ctx);
  assert (func);
  
  if (ipmi_sel_ctx_errnum (sel_ctx) == IPMI_SEL_ERR_INVALID_SEL_ENTRY)
    {
      /* most likely bad event data from remote system or user input */
      if (debug)
        PSTDOUT_FPRINTF (pstate,
                         stderr,
                         "Invalid data\n");

      return (0);
    }
  
  PSTDOUT_FPRINTF (pstate,
                   stderr,
                   "%s: %s\n",
                   func,
                   ipmi_sel_ctx_errormsg (sel_ctx));
  return (-1);
}
static int
_ipmi_monitoring_sel_parse_date_range (ipmi_sel_ctx_t ctx, void *callback_data)
{
  struct sel_parse_data *spd;
  uint8_t record_type;
  int record_type_class;

  assert (ctx);
  assert (callback_data);

  spd = (struct sel_parse_data *)callback_data;

  if (ipmi_sel_parse_read_record_type (spd->c->sel_parse_ctx,
				       NULL,
				       0,
				       &record_type) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record_id: %s",
                              ipmi_sel_ctx_errnum (spd->c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (spd->c);
      return (-1);
    }

  /* IPMI Workaround
   *
   * HP DL 380 G5
   *
   * Motherboard is reporting SEL Records of record type 0x00, which
   * is not a valid record type.
   */
  if (spd->sel_flags & IPMI_MONITORING_SEL_FLAGS_ASSUME_SYSTEM_EVENT_RECORD
      && !IPMI_SEL_RECORD_TYPE_VALID (record_type))
    record_type = IPMI_SEL_RECORD_TYPE_SYSTEM_EVENT_RECORD;
  
  record_type_class = ipmi_sel_record_type_class (record_type);

  if (record_type_class == IPMI_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD
      || record_type_class == IPMI_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD)
    {
      uint32_t timestamp;

      if (ipmi_sel_parse_read_timestamp (spd->c->sel_parse_ctx,
					 NULL,
					 0,
					 &timestamp) < 0)
        {
          IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_timestamp: %s",
                                  ipmi_sel_ctx_errnum (spd->c->sel_parse_ctx)));
          _sel_parse_ctx_error_convert (spd->c);
          return (-1);
        }

      if (timestamp >= spd->date_begin
          && timestamp <= spd->date_end)
        {
          if (_store_sel_record (spd->c, spd->sel_flags) < 0)
            return (-1);
        }
    }

  return (0);
}
static int
_ipmi_monitoring_sel_parse_sensor_types (ipmi_sel_ctx_t ctx, void *callback_data)
{
  struct sel_parse_data *spd;
  uint8_t record_type;
  int record_type_class;

  assert (ctx);
  assert (callback_data);

  spd = (struct sel_parse_data *)callback_data;

  if (ipmi_sel_parse_read_record_type (spd->c->sel_parse_ctx,
				       NULL,
				       0,
				       &record_type) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record_id: %s",
                              ipmi_sel_ctx_errnum (spd->c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (spd->c);
      return (-1);
    }

  /* IPMI Workaround
   *
   * HP DL 380 G5
   *
   * Motherboard is reporting SEL Records of record type 0x00, which
   * is not a valid record type.
   */
  if (spd->sel_flags & IPMI_MONITORING_SEL_FLAGS_ASSUME_SYSTEM_EVENT_RECORD
      && !IPMI_SEL_RECORD_TYPE_VALID (record_type))
    record_type = IPMI_SEL_RECORD_TYPE_SYSTEM_EVENT_RECORD;
  
  record_type_class = ipmi_sel_record_type_class (record_type);

  if (record_type_class == IPMI_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD)
    {
      uint8_t sel_sensor_type;
      unsigned int i;
      int sensor_type;
      int found = 0;

      if (ipmi_sel_parse_read_sensor_type (spd->c->sel_parse_ctx,
					   NULL,
					   0,
					   &sel_sensor_type) < 0)
        {
          IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_sensor_type: %s",
                                  ipmi_sel_ctx_errnum (spd->c->sel_parse_ctx)));
          _sel_parse_ctx_error_convert (spd->c);
          return (-1);
        }

      if ((sensor_type = ipmi_monitoring_get_sensor_type (spd->c, sel_sensor_type)) < 0)
        return (-1);
      
      for (i = 0; i < spd->sensor_types_len; i++)
        {
          if (spd->sensor_types[i] == sensor_type)
            {
              found++;
              break;
            }
        }
      
      if (found)
        {
          if (_store_sel_record (spd->c, spd->sel_flags) < 0)
            return (-1);
        }
    }

  return (0);
}
static int
_store_sel_record (ipmi_monitoring_ctx_t c, unsigned int sel_flags)
{
  struct ipmi_monitoring_sel_record *s = NULL;
  uint8_t sel_record[IPMI_SEL_RECORD_MAX_RECORD_LENGTH];
  int sel_record_len;
  uint16_t record_id;
  uint8_t record_type;
  int record_type_class;
  unsigned int sel_state;

  assert (c);
  assert (c->magic == IPMI_MONITORING_MAGIC);
  assert (c->sel_records);
  
  if (!(s = (struct ipmi_monitoring_sel_record *)malloc (sizeof (struct ipmi_monitoring_sel_record))))
    {
      IPMI_MONITORING_DEBUG (("malloc: %s", strerror (errno)));
      c->errnum = IPMI_MONITORING_ERR_OUT_OF_MEMORY;
      goto cleanup;
    }

  memset (s, '\0', sizeof (struct ipmi_monitoring_sel_record));

  if ((sel_record_len = ipmi_sel_parse_read_record (c->sel_parse_ctx,
                                                    sel_record,
                                                    IPMI_SEL_RECORD_MAX_RECORD_LENGTH)) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      goto cleanup;
    }
  
  if (ipmi_sel_parse_read_record_id (c->sel_parse_ctx,
				     NULL,
				     0,
				     &record_id) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record_id: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      goto cleanup;
    }

  if (ipmi_sel_parse_read_record_type (c->sel_parse_ctx,
				       NULL,
				       0,
				       &record_type) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record_id: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      goto cleanup;
    }

  /* IPMI Workaround
   *
   * HP DL 380 G5
   *
   * Motherboard is reporting SEL Records of record type 0x00, which
   * is not a valid record type.
   */
  if (sel_flags & IPMI_MONITORING_SEL_FLAGS_ASSUME_SYSTEM_EVENT_RECORD
      && !IPMI_SEL_RECORD_TYPE_VALID (record_type))
    record_type = IPMI_SEL_RECORD_TYPE_SYSTEM_EVENT_RECORD;

  s->record_id = record_id;
  s->record_type = record_type;

  if (ipmi_interpret_sel (c->interpret_ctx,
                          sel_record,
                          sel_record_len,
                          &sel_state) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_interpret_sel: %s",
                              ipmi_interpret_ctx_errormsg (c->interpret_ctx)));
      c->errnum = IPMI_MONITORING_ERR_INTERNAL_ERROR;
      goto cleanup;
    }

  if (sel_state == IPMI_INTERPRET_STATE_NOMINAL)
    s->sel_state = IPMI_MONITORING_STATE_NOMINAL;
  else if (sel_state == IPMI_INTERPRET_STATE_WARNING)
    s->sel_state = IPMI_MONITORING_STATE_WARNING;
  else if (sel_state == IPMI_INTERPRET_STATE_CRITICAL)
    s->sel_state = IPMI_MONITORING_STATE_CRITICAL;
  else if (sel_state == IPMI_INTERPRET_STATE_UNKNOWN)
    s->sel_state = IPMI_MONITORING_STATE_UNKNOWN;

  record_type_class = ipmi_sel_record_type_class (record_type);

  if (record_type_class == IPMI_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD)
    {
      s->record_type_class = IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD;

      if (_ipmi_monitoring_sel_parse_system_event_record (c, s, sel_flags) < 0)
        goto cleanup;
    }
  else if (record_type_class == IPMI_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD)
    {
      s->record_type_class = IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD;

      if (_ipmi_monitoring_sel_parse_timestamped_oem_record (c, s) < 0)
        goto cleanup;
    }
  else if (record_type_class == IPMI_SEL_RECORD_TYPE_CLASS_NON_TIMESTAMPED_OEM_RECORD)
    {
      s->record_type_class = IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_NON_TIMESTAMPED_OEM_RECORD;

      if (_ipmi_monitoring_sel_parse_non_timestamped_oem_record (c, s) < 0)
        goto cleanup;
    }
  else
    s->record_type_class = IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_UNKNOWN;
  

  /* achu: should come before list_append to avoid having a freed entry on the list */
  if (c->callback)
    {
      c->callback_sel_record = s;
      if ((*c->callback)(c, c->callback_data) < 0)
        {
          IPMI_MONITORING_DEBUG (("callback error"));
          c->errnum = IPMI_MONITORING_ERR_CALLBACK_ERROR;
          goto cleanup;
        }
    }

  if (!list_append (c->sel_records, s))
    {
      IPMI_MONITORING_DEBUG (("list_append: %s", strerror (errno)));
      c->errnum = IPMI_MONITORING_ERR_INTERNAL_ERROR;
      goto cleanup;
    }

  return (0);

 cleanup:
  if (s)
    {
      free (s->event_offset_string);
      free (s);
    }
  return (-1);
}
static int
_ipmi_monitoring_sel_parse_system_event_record (ipmi_monitoring_ctx_t c,
                                                struct ipmi_monitoring_sel_record *s,
						unsigned int sel_flags)
{
  uint32_t timestamp;
  uint8_t sel_sensor_type;
  uint8_t sensor_number;
  uint8_t event_direction;
  uint8_t event_offset;
  uint8_t event_type_code;
  uint8_t event_data1;
  uint8_t event_data2;
  uint8_t event_data3;
  char event_offset_string[IPMI_MONITORING_SEL_EVENT_OFFSET_STRING_MAX + 1];
  int sensor_type;
  unsigned int sel_string_flags;
  int ret;

  assert (c);
  assert (c->magic == IPMI_MONITORING_MAGIC);
  assert (s);

  if (ipmi_sel_parse_read_timestamp (c->sel_parse_ctx,
				     NULL,
				     0,
				     &timestamp) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_timestamp: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->timestamp = timestamp;

  if (ipmi_sel_parse_read_sensor_type (c->sel_parse_ctx,
				       NULL,
				       0,
				       &sel_sensor_type) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_sensor_type: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }

  if ((sensor_type = ipmi_monitoring_get_sensor_type (c, sel_sensor_type)) < 0)
    return (-1);

  s->sensor_type = sensor_type;

  if (ipmi_sel_parse_read_sensor_number (c->sel_parse_ctx,
					 NULL,
					 0,
					 &sensor_number) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_sensor_number: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }

  s->sensor_number = sensor_number;
  
  if (ipmi_sel_parse_read_event_direction (c->sel_parse_ctx,
					   NULL,
					   0,
					   &event_direction) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_event_direction: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }

  if (event_direction == IPMI_SEL_RECORD_ASSERTION_EVENT)
    s->event_direction = IPMI_MONITORING_SEL_EVENT_DIRECTION_ASSERTION;
  else
    s->event_direction = IPMI_MONITORING_SEL_EVENT_DIRECTION_DEASSERTION;
      
  if (ipmi_sel_parse_read_event_data1_offset_from_event_reading_type_code (c->sel_parse_ctx,
									   NULL,
									   0,
									   &event_offset) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_event_data1_offset_from_event_reading_type_code: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->event_offset = event_offset;

  if (ipmi_sel_parse_read_event_type_code (c->sel_parse_ctx,
					   NULL,
					   0,
					   &event_type_code) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_event_type_code: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->event_type_code = event_type_code;

  if ((s->event_offset_type = _get_event_offset_type (c,
                                                      event_type_code,
                                                      sel_sensor_type)) < 0)
    return (-1);

  if (ipmi_sel_parse_read_event_data1 (c->sel_parse_ctx,
				       NULL,
				       0,
				       &event_data1) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_event_data1: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->event_data1 = event_data1;

  if (ipmi_sel_parse_read_event_data2 (c->sel_parse_ctx,
				       NULL,
				       0,
				       &event_data2) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_event_data2: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->event_data2 = event_data2;

  if (ipmi_sel_parse_read_event_data3 (c->sel_parse_ctx,
				       NULL,
				       0,
				       &event_data3) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_event_data3: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  s->event_data3 = event_data3;

  sel_string_flags = IPMI_SEL_STRING_FLAGS_IGNORE_UNAVAILABLE_FIELD | IPMI_SEL_STRING_FLAGS_OUTPUT_NOT_AVAILABLE;
  if (sel_flags & IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES)
    sel_string_flags |= IPMI_SEL_STRING_FLAGS_ENTITY_SENSOR_NAMES;
  
  if (ipmi_sel_parse_read_record_string (c->sel_parse_ctx,
                                         "%s",
					 NULL,
					 0,
                                         s->sensor_name,
                                         IPMI_MONITORING_MAX_SENSOR_NAME_LENGTH,
                                         sel_string_flags) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record_string: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }

  memset (event_offset_string, '\0', IPMI_MONITORING_SEL_EVENT_OFFSET_STRING_MAX + 1);

  sel_string_flags = IPMI_SEL_STRING_FLAGS_IGNORE_UNAVAILABLE_FIELD | IPMI_SEL_STRING_FLAGS_OUTPUT_NOT_AVAILABLE;

  if ((ret = ipmi_sel_parse_read_record_string (c->sel_parse_ctx,
                                                "%e",
						NULL,
						0,
                                                event_offset_string,
                                                IPMI_MONITORING_SEL_EVENT_OFFSET_STRING_MAX,
						sel_string_flags)) < 0)
    {
      IPMI_MONITORING_DEBUG (("ipmi_sel_parse_read_record_string: %s",
                              ipmi_sel_ctx_errnum (c->sel_parse_ctx)));
      _sel_parse_ctx_error_convert (c);
      return (-1);
    }
  
  if (ret)
    {
      if (!(s->event_offset_string = strdup (event_offset_string)))
        {
          IPMI_MONITORING_DEBUG (("strdup: %s", strerror (errno)));
          c->errnum = IPMI_MONITORING_ERR_OUT_OF_MEMORY;
          return (-1);
        }
    }
  else
    {
      /* return empty string */
      if (!(s->event_offset_string = strdup ("")))
        {
          IPMI_MONITORING_DEBUG (("strdup: %s", strerror (errno)));
          c->errnum = IPMI_MONITORING_ERR_OUT_OF_MEMORY;
          return (-1);
        }
    }

  return (0);
}