platform.c revision 24db46411fd54f70c35b94bb952eb7ba040e43b4
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* platform.c -- interfaces to the platform's configuration information
*
* this platform.c allows eft to run on Solaris systems.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <dirent.h>
#include <libnvpair.h>
#include <dlfcn.h>
#include <unistd.h>
#include <errno.h>
#include <stropts.h>
#include <fm/fmd_fmri.h>
#include "alloc.h"
#include "out.h"
#include "tree.h"
#include "itree.h"
#include "ipath.h"
#include "ptree.h"
#include "fme.h"
#include "stable.h"
#include "eval.h"
#include "config.h"
#include "platform.h"
/*
* Lastcfg points to the last configuration snapshot we made. If we
* need to make a dev to hc scheme conversion of an event path, we use
* the last snapshot as a best guess. If we don't have a last snapshot
* we take one and save it in Initcfg below.
*/
static topo_hdl_t *Eft_topo_hdl;
/*
* Initcfg points to any config snapshot we have to make prior
* to starting our first fme.
*/
void *
{
return (p);
}
void
topo_use_free(void *p)
{
alloc_free(p, NULL, 0);
}
/*ARGSUSED*/
static void *
{
}
/*ARGSUSED*/
static void
{
alloc_free(p, NULL, 0);
}
const nv_alloc_ops_t Eft_nv_alloc_ops = {
NULL, /* nv_ao_init() */
NULL, /* nv_ao_fini() */
alloc_nv_alloc, /* nv_ao_alloc() */
alloc_nv_free, /* nv_ao_free() */
NULL /* nv_ao_reset() */
};
static char *Root;
static char *Mach;
static char *Plat;
static char tmpbuf[MAXPATHLEN];
static char numbuf[MAXPATHLEN];
/*
* platform_globals -- set global variables based on sysinfo() calls
*/
static void
{
}
static void
{
}
/*
* platform_init -- perform any platform-specific initialization
*/
void
platform_init(void)
{
}
void
platform_fini(void)
{
}
}
(void) nv_alloc_fini(&Eft_nv_hdl);
}
/*
* hc_fmri_nodeize -- convert hc-scheme FMRI to eft compatible format
*
* this is an internal platform.c helper routine
*/
static struct node *
{
const char *sname;
char *ename;
char *eid;
int e, r;
/*
* to do with it if it weren't /? For now, we don't bother
* even looking it up.
*/
/*
* Get the hc-list of elements in the FMRI
*/
return (NULL);
}
for (e = 0; e < hc_nprs; e++) {
if (r != 0) {
/* probably should bail */
continue;
}
else
}
return (pathtree);
}
/*
* platform_getpath -- extract eft-compatible path from ereport
*/
struct node *
{
/*
* For now we assume the "path" part of the error report is
* the detector FMRI
*/
return (NULL);
}
return (NULL);
}
/*
* later, if FM_FMRI_SCHEME_DEV or FM_FMRI_SCHEME_CPU
* we can look and perform a reverse translation into
* an hc node
*/
int isdev = 0;
isdev = 1;
"(scheme is %s, expect %s or %s or %s)",
return (NULL);
}
if (isdev == 1 &&
return (NULL);
} else if (isdev == 0 &&
return (NULL);
}
/*
* If we haven't taken a config snapshot yet, we need
* to do so now. The call to config_snapshot() has the
* side-effect of setting Lastcfg. We squirrel away the
* pointer to this snapshot so we may free it later.
*/
"XFILE: cannot snapshot configuration");
return (NULL);
}
/*
* Look up the path or cpu id in the last config snapshot.
*/
if (isdev == 1 &&
"device path matching %s.", path);
else if (isdev == 0 &&
"cpu-id matching %u.", id);
return (ret);
}
return (hc_fmri_nodeize(dfmri));
}
/* Allocate space for raw config strings in chunks of this size */
#define STRSBUFLEN 512
/*
* cfgadjust -- Make sure the amount we want to add to the raw config string
* buffer will fit, and if not, increase the size of the buffer.
*/
static void
{
}
}
static char *
{
int i, err;
return (NULL);
!= 0) {
return (NULL);
}
tmpbuf[0] = '\0';
for (i = 0; i < nhc; ++i) {
if (err) {
return (NULL);
}
/* conversion to number failed? */
return (NULL);
}
}
return (tmpbuf);
}
static void
{
/*
* malformed prop nvpair
*/
return;
/*
* We can only handle properties of string type
*/
switch (nvpair_type(pv_nvp)) {
case DATA_TYPE_STRING:
break;
case DATA_TYPE_NVLIST:
/*
* At least try to collect the protocol
* properties
*/
"string");
return;
} else {
}
break;
case DATA_TYPE_UINT64:
/*
* Convert uint64 to hex strings
*/
break;
default:
"%s", propn);
return;
}
/* = & NULL */
}
/*
* cfgcollect -- Assemble raw configuration data in string form suitable
* for checkpointing.
*/
static int
{
return (TOPO_WALK_ERR);
/*
* Collect properties
*
* eversholt should support alternate property types
* Better yet, topo properties could be represented as
* a packed nvlist
*/
continue;
continue;
/* Get property name */
if (strcmp(TOPO_PROP_VAL_NAME,
nvpair_name(pv_nvp)) == 0)
(void) nvpair_value_string(pv_nvp,
&propn);
/*
* Get property value
*/
if (strcmp(TOPO_PROP_VAL_VAL,
nvpair_name(pv_nvp)) == 0)
pv_nvp);
}
}
}
return (TOPO_WALK_NEXT);
}
/*
* platform_config_snapshot -- gather a snapshot of the current configuration
*/
struct cfgdata *
platform_config_snapshot(void)
{
int err;
/*
* If the DR generation number has changed,
* we need to grab a new snapshot, otherwise we
* can simply point them at the last config.
*/
return (Lastcfg);
}
/* we're getting a new config, so clean up the last one */
topo_strerror(err));
}
"tree");
}
return (Lastcfg);
}
static const char *
{
const char *fmristr;
/*
* The first order of business is to find the resource in the
* config database so we can examine properties associated with
* that node.
*/
return (NULL);
}
return (NULL);
}
return (fmristr);
}
static nvlist_t *
{
const char *fmristr;
int err;
return (NULL);
topo_strerror(err));
return (NULL);
}
return (fmri);
}
static void
{
const char *modstr;
int err;
/*
* Defects aren't required to have ASRUs defined with
* them in the eversholt fault tree, so usually we'll be
* creating original FMRIs here. If the ASRU
* is defined when we get here, we won't replace it.
*/
return;
/*
* Find the driver for this resource and use that to get
* a mod fmri for ASRU. There are no FRUs for defects.
*/
return;
return;
}
}
/*
* platform_units_translate
* This routines offers a chance for platform-specific rewrites of
* the hc scheme FRU and ASRUs associated with a suspect fault.
*/
/*ARGSUSED*/
void
{
/*
* Get our FMRIs from libtopo
*/
== NULL) {
} else {
}
/*
* If it is a defect we want to re-write the FRU as the pkg
* scheme fmri of the package containing the buggy driver, and
* the ASRU as the mod scheme fmri of the driver's kernel
* module.
*/
if (isdefect) {
return;
}
/*
* Find the TOPO_PROP_ASRU and TOPO_PROP_FRU properties
* for this resource if *dfltasru and *dfltfru are set
*/
== NULL) {
} else {
}
}
== NULL) {
} else {
}
}
}
/*
* platform_get_files -- return names of all files we should load
*
* search directories in dirname[] for all files with names ending with the
* substring fnstr. dirname[] should be a NULL-terminated array. fnstr
* may be set to "*" to indicate all files in a directory.
*
* if nodups is non-zero, then the first file of a given name found is
* the only file added to the list of names. for example if nodups is
* set and we're looking for .efts, and find a pci.eft in the dirname[0],
* then no pci.eft found in any of the other dirname[] entries will be
* included in the final list of names.
*
* this routine doesn't return NULL, even if no files are found (in that
* case, a char ** is returned with the first element NULL).
*/
static char **
{
int nfiles = 0; /* files found so far */
int slots = 0; /* char * slots allocated in files */
int i;
static char *nullav;
"platform_get_files: opendir failed for %s",
dirname[i]);
continue;
}
if (nodups != 0) {
if (lut_lookup(foundnames,
(void *)snm,
"platform_get_files: "
"skipping repeated name "
"%s/%s",
dirname[i],
snm);
continue;
}
(void *)snm,
(void *)snm,
NULL);
}
/* allocate ten more slots */
slots += 10;
slots * sizeof (char *));
}
/* prepend directory name and / */
}
}
}
if (foundnames != NULL)
if (nfiles == 0)
return (&nullav);
return (files);
}
/*
* search for files in a standard set of directories
*/
static char **
{
const char *dirlist[4];
char **flist;
/* Generic files that apply to any machine */
(void) snprintf(eftmachdir,
(void) snprintf(eftplatdir,
dirlist[0] = eftplatdir;
return (flist);
}
/*
* platform_run_poller -- execute a poller
*
* when eft needs to know if a polled ereport exists this routine
* is called so the poller code may be run in a platform-specific way.
* there's no return value from this routine -- either the polled ereport
* is generated (and delivered *before* this routine returns) or not.
* any errors, like "poller unknown" are considered platform-specific
* should be handled here rather than passing an error back up.
*/
/*ARGSUSED*/
void
platform_run_poller(const char *poller)
{
}
/*
* fork and execve path with argument array argv and environment array
* envp. data from stdout and stderr are placed in outbuf and errbuf,
* respectively.
*
* see execve(2) for more descriptions for path, argv and envp.
*/
static int
{
int rt = 0;
/*
* run the cmd and see if it failed. this function is *not* a
* generic command runner -- we depend on some knowledge we
* have about the commands we run. first of all, we expect
* errors to spew something to stdout, and that something is
* typically short enough to fit into a pipe so we can wait()
* for the command to complete and then fetch the error text
* from the pipe.
*/
return (1);
return (1);
} else if (pid) {
/* parent */
/* PHASE2 need to guard against hang in child? */
return (1);
/* check for stderr contents */
/*
* read failed even though ioctl indicated
* that nonzero bytes were available for
* reading
*/
return (1);
}
/*
* handle case where errbuf is not properly
* terminated
*/
} else if (WIFSIGNALED(wstat))
return (1);
return (1);
/* check for stdout contents */
/*
* read failed even though ioctl indicated
* that nonzero bytes were available for
* reading
*/
return (1);
}
/*
* handle case where outbuf is not properly
* terminated
*/
}
} else {
/* child */
_exit(1);
}
return (rt);
}
#define MAXDIGITIDX 23
static int
{
char *addthisarg = NULL;
return (0);
switch (np->t) {
case T_QUOTE:
break;
case T_LIST:
return (1);
/*
* only leftmost element of a list can provide the command
* name (after which *argc becomes 1)
*/
return (1);
break;
case T_FUNC:
case T_GLOBID:
case T_ASSIGN:
case T_CONDIF:
case T_CONDELSE:
case T_EQ:
case T_NE:
case T_LT:
case T_LE:
case T_GT:
case T_GE:
case T_BITAND:
case T_BITOR:
case T_BITXOR:
case T_BITNOT:
case T_LSHIFT:
case T_RSHIFT:
case T_AND:
case T_OR:
case T_NOT:
case T_ADD:
case T_SUB:
case T_MUL:
case T_DIV:
case T_MOD: {
0, &value))
return (1);
switch (value.t) {
case UINT64:
break;
case STRING:
break;
case NODEPTR :
break;
default:
"call: arglist2argv: unexpected result from"
" operation %s",
ptree_nodetype2str(np->t));
return (1);
}
break;
}
case T_NUM:
case T_TIMEVAL:
break;
case T_NAME:
break;
case T_EVENT:
break;
default:
ptree_nodetype2str(np->t));
return (1);
/*NOTREACHED*/
break;
}
/*
* first argument added is the command name.
*/
char **files;
/* do not proceed if number of files found != 1 */
}
if (addthisarg != NULL) {
/*
* make sure argv is long enough so it has a
* terminating element set to NULL
*/
*argvlen += 10;
sizeof (char *) * *argvlen);
}
(*argc)++;
}
return (0);
}
static int
{
char *envvalues[4];
char *none = "(none)";
int i;
*envc = 4;
/*
* make sure envp is long enough so it has a terminating element
* set to NULL
*/
} else {
/* large enough for max int */
}
}
return (0);
}
/*
* platform_call -- call an external function
*
* evaluate a user-defined function and place result in valuep. return 0
* if function evaluation was successful; 1 if otherwise.
*/
int
{
/*
* use rather short buffers. only the first string on outbuf[] is
* taken as output from the called function. any message in
* errbuf[] is echoed out as an error message.
*/
int i, ret;
/*
* np is the argument list. the user-defined function is the first
* element of the list.
*/
argc = 0;
argvlen = 0;
argc == 0)
return (1);
/*
* make sure program has executable bit set
*/
int exec_bit_set = 0;
exec_bit_set = 1;
exec_bit_set = 1;
exec_bit_set = 1;
if (exec_bit_set == 0)
argv[0]);
} else {
}
envc = 0;
envplen = 0;
return (1);
outbuf[0] = '\0';
errbuf[0] = '\0';
for (i = 0; i < envc; i++)
if (envp)
if (ret) {
"call: failure in fork + exec of %s", argv[0]);
} else {
char *ptr;
/* chomp the result */
*ptr = '\0';
break;
}
}
if (errbuf[0] != '\0') {
ret = 1;
"call: unexpected stderr output from %s: %s",
}
for (i = 0; i < argc; i++)
return (ret);
}
/*
* platform_confcall -- call a configuration database function
*
* returns result in *valuep, return 0 on success
*/
/*ARGSUSED*/
int
{
/* assume we're returning true */
valuep->v = 1;
/*
* We've detected a well-formed confcall() to rewrite
* an ASRU in a fault. We get here via lines like this
* in the eversholt rules:
*
* event fault.memory.page@dimm, FITrate=PAGE_FIT,
* ASRU=dimm, message=0,
* count=stat.page_fault@dimm,
* action=confcall("rewrite-ASRU");
*
* So first rewrite the resource in the fault. Any payload data
* following the FM_FMRI_HC_SPECIFIC member is used to expand the
* resource nvlist. Next, use libtopo to compute the ASRU from
* from the new resource.
*/
int err;
!= 0) {
"in fault event");
return (0);
}
"unable to allocate nvlist for resource rewrite");
return (0);
}
/*
* Loop until we run across asru-specific payload. All
* payload members prefixed "asru-" will be added to the
* hc-specific nvlist and removed from the original.
*/
"resource - nvlist_add_nvpair for "
return (0);
}
(void) nvlist_remove(Action_nvl,
} else {
}
}
"rewrite resource with HC specific data");
return (0);
}
return (0);
}
!= 0) {
"failed to remove old asru during rewrite");
return (0);
}
"unable to add re-written asru");
return (0);
}
} else {
}
return (0);
}
/*
* platform_get_eft_files -- return names of all eft files we should load
*
* this routine doesn't return NULL, even if no files are found (in that
* case, a char ** is returned with the first element NULL).
*/
char **
platform_get_eft_files(void)
{
}
void
platform_free_eft_files(char **flist)
{
char **f;
return; /* no files were found so we're done */
f = flist;
while (*f != NULL) {
FREE(*f);
f++;
}
}
void
{
/*
* cannot replace a non-NULL payloadnvp with a non-NULL nvlp
*/
payloadnvp = nvlp;
}
/*
* given array notation in inputstr such as "foo[1]" or "foo [ 1 ]" (spaces
* allowed), figure out the array name and index. return 0 if successful,
* nonzero if otherwise.
*/
static int
{
return (1);
/*
* return if array notation is not complete or if index is negative
*/
return (1);
}
/*
* search past any spaces between the name string and '['
*/
endname--;
*endname = '\0';
/*
* search until indexptr points to the first digit and indexend
* points to the last digit
*/
indexptr++;
indexend--;
return (0);
}
/*
* platform_payloadprop -- fetch a payload value
*
* XXX this function should be replaced and eval_func() should be
* XXX changed to use the more general platform_payloadprop_values().
*/
int
{
int not_array = 0;
unsigned int index = 0;
if (payloadnvp == NULL) {
propstr);
return (1);
}
/*
* first handle any embedded nvlists. if propstr is "foo.bar[2]"
* then lastnameptr should end up being "bar[2]" with basenvp set
* to the nvlist for "foo". (the search for "bar" within "foo"
* will be done later.)
*/
char *w;
int ier;
/*
* decompose nameslist into its component names while
* extracting the embedded nvlist
*/
lastnameptr, &basenvp);
} else {
/* handle array of nvlists */
if (ier == 0) {
ier = 1;
else
}
}
if (ier) {
" invalid list for %s (in %s)",
return (1);
}
lastnameptr = w;
}
} else {
}
/* if property is an array reference, extract array name and index */
if (not_array)
/* search for nvpair entry */
break;
}
return (1);
/*
* caller is interested in the existence of a property with
* this name, regardless of type or value
*/
return (0);
}
/*
* get to this point if we found an entry. figure out its data
* type and copy its value.
*/
return (0);
}
}
switch (nvpair_type(nvpair)) {
case DATA_TYPE_BOOLEAN:
case DATA_TYPE_BOOLEAN_VALUE: {
break;
}
case DATA_TYPE_BYTE: {
break;
}
case DATA_TYPE_STRING: {
char *val;
break;
}
case DATA_TYPE_INT8: {
break;
}
case DATA_TYPE_UINT8: {
break;
}
case DATA_TYPE_INT16: {
break;
}
case DATA_TYPE_UINT16: {
break;
}
case DATA_TYPE_INT32: {
break;
}
case DATA_TYPE_UINT32: {
break;
}
case DATA_TYPE_INT64: {
break;
}
case DATA_TYPE_UINT64: {
break;
}
case DATA_TYPE_BOOLEAN_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_BYTE_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_STRING_ARRAY: {
char **val;
goto invalid;
break;
}
case DATA_TYPE_INT8_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_UINT8_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_INT16_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_UINT16_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_INT32_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_UINT32_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_INT64_ARRAY: {
goto invalid;
break;
}
case DATA_TYPE_UINT64_ARRAY: {
goto invalid;
break;
}
default :
"platform_payloadprop: unsupported data type for %s",
propstr);
return (1);
}
return (0);
"platform_payloadprop: invalid array reference for %s", propstr);
return (1);
}
/*ARGSUSED*/
int
{
}
struct evalue *
{
char *nvpname;
*nvals = 0;
if (payloadnvp == NULL)
return (NULL);
/* search for nvpair entry */
break;
}
return (NULL); /* property not found */
switch (nvpair_type(nvpair)) {
case DATA_TYPE_NVLIST: {
&scheme) == 0) {
*nvals = 1;
retvals->v =
return (retvals);
}
}
return (NULL);
}
case DATA_TYPE_NVLIST_ARRAY: {
int i;
int hccount;
/*
* since we're only willing to handle hc fmri's, we
* must count them first before allocating retvals.
*/
return (NULL);
hccount = 0;
for (i = 0; i < nel; i++) {
&scheme) == 0 &&
hccount++;
}
}
if (hccount == 0)
return (NULL);
hccount = 0;
for (i = 0; i < nel; i++) {
&scheme) == 0 &&
hc_fmri_nodeize(nvap[i]);
hccount++;
}
}
return (retvals);
}
case DATA_TYPE_BOOLEAN:
case DATA_TYPE_BOOLEAN_VALUE: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_BYTE: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_STRING: {
char *val;
*nvals = 1;
return (retvals);
}
case DATA_TYPE_INT8: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_UINT8: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_INT16: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_UINT16: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_INT32: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_UINT32: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_INT64: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_UINT64: {
*nvals = 1;
return (retvals);
}
case DATA_TYPE_BOOLEAN_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_BYTE_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_STRING_ARRAY: {
char **val;
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_INT8_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_UINT8_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_INT16_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_UINT16_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_INT32_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_UINT32_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_INT64_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
case DATA_TYPE_UINT64_ARRAY: {
int i;
for (i = 0; i < nel; i++) {
}
return (retvals);
}
}
return (NULL);
}
/*
* When a list.repaired event is seen the following is called for
* each fault in the associated fault list to convert the given FMRI
* to an instanced path. Only hc scheme is supported.
*/
const struct ipath *
{
char *scheme;
return (NULL);
return (NULL);
}
sizeof (FM_FMRI_SCHEME_HC) - 1) != 0) {
"scheme %s", scheme);
return (NULL);
}
return (NULL); /* nodeize will already have whinged */
return (ip);
}