/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <atomic.h>
#include <errno.h>
#include <libdevinfo.h>
#include <libsysevent.h>
#include <pthread.h>
#include <smbios.h>
#include <string.h>
#include <sys/fm/protocol.h>
#include <sys/sysevent/eventdefs.h>
#include "asr.h"
#include "asr_mem.h"
#include "asr_nvl.h"
#include "asr_err.h"
#define LATEST_TOPO "/var/fm/fmd/topo/latest"
static pthread_mutex_t asr_topo_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* Generates a new topology
*/
static topo_hdl_t *
asr_topo_gen(asr_handle_t *ah)
{
int err = 0;
topo_hdl_t *th = NULL;
char *id;
(void) pthread_mutex_lock(&asr_topo_lock);
if ((th = topo_open(TOPO_VERSION, NULL, &err)) == NULL) {
(void) asr_error(EASR_TOPO,
"failed to alloc topo handle: %s", topo_strerror(err));
goto finally;
}
if ((id = topo_snap_load(th, LATEST_TOPO, &err)) == NULL) {
topo_close(th);
th = NULL;
(void) asr_error(EASR_TOPO,
"failed to load libtopo snapshot: %s", topo_strerror(err));
goto finally;
}
asr_log_debug(ah, "Loaded topology reference %s", id);
topo_hdl_strfree(th, id);
finally:
(void) pthread_mutex_unlock(&asr_topo_lock);
return (th);
}
/*
* Given an FMRI (in nvlist form), convert it to a string. Simply wrap around
* topo_fmri_nvl2str, but fallback to something if we don't have an appropriate
* libtopo plugin.
*/
char *
asr_topo_fmri2str(topo_hdl_t *thp, nvlist_t *fmri)
{
int err;
char *scheme, *fmristr;
char *buf;
size_t len;
if (topo_fmri_nvl2str(thp, fmri, &fmristr, &err) == 0) {
buf = asr_strdup(fmristr);
topo_hdl_strfree(thp, fmristr);
return (buf);
}
if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) {
(void) asr_error(EASR_FM, "unknown FMRI scheme");
return (NULL);
}
len = snprintf(NULL, 0, "%s://unknown", scheme);
if ((buf = asr_zalloc(len + 1)) == NULL) {
(void) asr_error(EASR_NOMEM, "unable to alloc fmri string");
return (NULL);
}
(void) snprintf(buf, len + 1, "%s://unknown", scheme);
return (buf);
}
/*
* Walks the FM topology using the given walker callback function and
* callback data.
*/
int
asr_topo_walk(asr_handle_t *ah, topo_walk_cb_t walker, void *data)
{
int err = 0;
topo_hdl_t *th;
topo_walk_t *twp = NULL;
asr_topo_enum_data_t tdata;
if ((th = asr_topo_gen(ah)) == NULL)
return (ASR_FAILURE);
tdata.asr_hdl = ah;
tdata.asr_topoh = th;
tdata.asr_data = data;
if ((twp = topo_walk_init(
th, FM_FMRI_SCHEME_HC, walker, &tdata, &err)) == NULL)
goto finally;
if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) {
asr_log_warn(ah, "topo_error: %s", topo_strerror(err));
err = EASR_TOPO;
}
finally:
if (twp != NULL)
topo_walk_fini(twp);
topo_close(th);
return (err);
}
/*
* Gets the FMA host authority information from fmtopo
*/
int
asr_topo_auth(asr_handle_t *ah, nvlist_t **auth)
{
topo_hdl_t *th;
if ((th = asr_topo_gen(ah)) == NULL)
return (ASR_FAILURE);
*auth = asr_nvl_dup(topo_hdl_auth(th));
topo_close(th);
return (*auth == NULL ? ASR_FAILURE : ASR_OK);
}
/*
* Gets a system prop property and sets it to the given asrprop name.
*/
static int
asr_topo_prom_prop(asr_handle_t *ah, char *promprop, char *asrprop)
{
di_prom_handle_t promh = DI_PROM_HANDLE_NIL;
di_node_t rooth = DI_NODE_NIL;
unsigned char *bufp;
int result = ASR_FAILURE;
if ((promh = di_prom_init()) == NULL)
return (result);
if ((rooth = di_init("/", DINFOPROP)) == NULL) {
di_prom_fini(promh);
return (result);
}
if (di_prom_prop_lookup_bytes(promh, rooth, promprop, &bufp) != -1)
result = asr_setprop_str(ah, asrprop, (char *)bufp);
di_fini(rooth);
di_prom_fini(promh);
return (result);
}
/*
* Sets the ASR system id property from the prom or smbios.
*/
int
asr_topo_set_system_id(asr_handle_t *ah)
{
smbios_hdl_t *shp = NULL;
const char *csn = NULL;
int result = ASR_FAILURE;
if (asr_topo_prom_prop(ah, "chassis-sn", ASR_PROP_SYSTEM_ID) == 0)
return (ASR_OK);
if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) == NULL)
return (ASR_FAILURE);
if ((csn = smbios_csn(shp)) != NULL)
result = asr_setprop_str(ah, ASR_PROP_SYSTEM_ID, csn);
smbios_close(shp);
return (result);
}
/*
* Sets the ASR product name from the prom or smbios.
*/
int
asr_topo_set_product_name(asr_handle_t *ah)
{
smbios_hdl_t *shp = NULL;
id_t id;
smbios_system_t s1;
smbios_info_t s2;
int result = ASR_FAILURE;
if (asr_topo_prom_prop(ah, "name", ASR_PROP_PRODUCT_NAME) == 0)
return (ASR_OK);
if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) == NULL)
return (ASR_FAILURE);
if (((id = smbios_info_system(shp, &s1)) != SMB_ERR) &&
(smbios_info_common(shp, id, &s2) != SMB_ERR) &&
(strcmp(s2.smbi_product, SMB_DEFAULT1) != 0) &&
(strcmp(s2.smbi_product, SMB_DEFAULT2) != 0))
result = asr_setprop_str(ah, ASR_PROP_PRODUCT_NAME,
s2.smbi_product);
smbios_close(shp);
return (result);
}