Ejemplo n.º 1
0
void
_fmd_init(fmd_hdl_t *hdl)
{
	sp_monitor_t *smp;
	int error;
	char *msg;

	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
		return;

	smp = fmd_hdl_zalloc(hdl, sizeof (sp_monitor_t), FMD_SLEEP);
	fmd_hdl_setspecific(hdl, smp);

	if ((smp->sm_hdl = ipmi_open(&error, &msg, IPMI_TRANSPORT_BMC, NULL))
	    == NULL) {
		/*
		 * If /dev/ipmi0 doesn't exist on the system, then unload the
		 * module without doing anything.
		 */
		if (error != EIPMI_BMC_OPEN_FAILED)
			fmd_hdl_abort(hdl, "failed to initialize IPMI "
			    "connection: %s\n", msg);
		fmd_hdl_debug(hdl, "failed to load: no IPMI connection "
		    "present");
		fmd_hdl_free(hdl, smp, sizeof (sp_monitor_t));
		fmd_hdl_unregister(hdl);
		return;
	}

	/*
	 * Attempt an initial uptime() call.  If the IPMI command is
	 * unrecognized, then this is an unsupported platform and the module
	 * should be unloaded.  Any other error is treated is transient failure.
	 */
	if ((error = ipmi_sunoem_uptime(smp->sm_hdl, &smp->sm_seconds,
	    &smp->sm_generation)) != 0 &&
	    ipmi_errno(smp->sm_hdl) == EIPMI_INVALID_COMMAND) {
		fmd_hdl_debug(hdl, "failed to load: uptime command "
		    "not supported");
		ipmi_close(smp->sm_hdl);
		fmd_hdl_free(hdl, smp, sizeof (sp_monitor_t));
		fmd_hdl_unregister(hdl);
		return;
	}

	smp->sm_interval = fmd_prop_get_int64(hdl, "interval");

	if (error == 0)
		fmd_hdl_debug(hdl, "successfully loaded, uptime = %u seconds "
		    "(generation %u)", smp->sm_seconds, smp->sm_generation);
	else
		fmd_hdl_debug(hdl, "successfully loaded, but uptime call "
		    "failed: %s", ipmi_errmsg(smp->sm_hdl));

	/*
	 * Setup the recurring timer.
	 */
	(void) fmd_timer_install(hdl, NULL, NULL, 0);
}
Ejemplo n.º 2
0
static void
gmem_dimm_free(fmd_hdl_t *hdl, gmem_dimm_t *dimm, int destroy)
{
	gmem_case_t *cc = &dimm->dimm_case;
	int i;
	gmem_mq_t *q;
	tstamp_t *tsp, *next;

	if (cc->cc_cp != NULL) {
		gmem_case_fini(hdl, cc->cc_cp, destroy);
		if (cc->cc_serdnm != NULL) {
			if (fmd_serd_exists(hdl, cc->cc_serdnm) &&
			    destroy)
				fmd_serd_destroy(hdl, cc->cc_serdnm);
			fmd_hdl_strfree(hdl, cc->cc_serdnm);
		}
	}

	gmem_fmri_fini(hdl, &dimm->dimm_asru, destroy);

	for (i = 0; i < GMEM_MAX_CKWDS; i++) {
		while ((q = gmem_list_next(&dimm->mq_root[i])) != NULL) {
			if (q->mq_serdnm != NULL) {
				if (fmd_serd_exists(hdl, q->mq_serdnm))
					fmd_serd_destroy(hdl, q->mq_serdnm);
				fmd_hdl_strfree(hdl, q->mq_serdnm);
				q->mq_serdnm = NULL;
			}

			for (tsp = gmem_list_next(&q->mq_dupce_tstamp);
			    tsp != NULL; tsp = next) {
				next = gmem_list_next(tsp);
				gmem_list_delete(&q->mq_dupce_tstamp,
				    &tsp->ts_l);
				fmd_hdl_free(hdl, tsp, sizeof (tstamp_t));
			}

			gmem_list_delete(&dimm->mq_root[i], q);
			fmd_hdl_free(hdl, q, sizeof (gmem_mq_t));
		}
	}

	if (destroy)
		fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);

	gmem_list_delete(&gmem.gm_dimms, dimm);
	fmd_hdl_free(hdl, dimm, sizeof (gmem_dimm_t));
}
Ejemplo n.º 3
0
nvlist_t *
cmd_fault_add_location(fmd_hdl_t *hdl, nvlist_t *flt, const char *locstr) {

	char *t, *s;

	if (nvlist_lookup_string(flt, FM_FAULT_LOCATION, &t) == 0)
		return (flt); /* already has location value */

	/* Replace occurrence of ": " with "/" to avoid confusing ILOM. */
	t = fmd_hdl_zalloc(hdl, strlen(locstr) + 1, FMD_SLEEP);
	s = strstr(locstr, ": ");
	if (s != NULL) {
		(void) strncpy(t, locstr, s - locstr);
		(void) strcat(t, "/");
		(void) strcat(t, s + 2);
	} else {
		(void) strcpy(t, locstr);
	}

	/* Also, remove any J number from end of this string. */
	s = strstr(t, "/J");
	if (s != NULL)
		*s = '\0';

	if (nvlist_add_string(flt, FM_FAULT_LOCATION, t) != 0)
		fmd_hdl_error(hdl, "unable to alloc location for fault\n");
	fmd_hdl_free(hdl, t, strlen(locstr) + 1);
	return (flt);
}
Ejemplo n.º 4
0
static void
cpumem_free(void *addr, size_t size)
{
    assert(cpumem_hdl != NULL);

    fmd_hdl_free(cpumem_hdl, addr, size);
}
Ejemplo n.º 5
0
static void
cma_page_free(fmd_hdl_t *hdl, cma_page_t *page)
{
    nvlist_free(page->pg_asru);
    nvlist_free(page->pg_rsrc);
    fmd_hdl_free(hdl, page, sizeof (cma_page_t));
}
Ejemplo n.º 6
0
static void
branch_free(fmd_hdl_t *hdl, cmd_branch_t *branch, int destroy)
{
	fmd_hdl_debug(hdl, "Free branch %s\n", branch->branch_unum);
	if (branch->branch_case.cc_cp != NULL) {
		if (destroy) {
			if (branch->branch_case.cc_serdnm != NULL) {
				fmd_serd_destroy(hdl,
				    branch->branch_case.cc_serdnm);
				fmd_hdl_strfree(hdl,
				    branch->branch_case.cc_serdnm);
				branch->branch_case.cc_serdnm = NULL;
			}
		}
		cmd_case_fini(hdl, branch->branch_case.cc_cp, destroy);
	}

	branch_dimmlist_free(hdl, branch);
	cmd_fmri_fini(hdl, &branch->branch_asru, destroy);

	if (destroy)
		fmd_buf_destroy(hdl, NULL, branch->branch_bufname);
	cmd_list_delete(&cmd.cmd_branches, branch);
	fmd_hdl_free(hdl, branch, sizeof (cmd_branch_t));
}
Ejemplo n.º 7
0
/*
 * Read back the persistent representation of an active case.
 */
static zfs_case_t *
zfs_case_unserialize(fmd_hdl_t *hdl, fmd_case_t *cp)
{
	zfs_case_t *zcp;

	zcp = fmd_hdl_zalloc(hdl, sizeof (zfs_case_t), FMD_SLEEP);
	zcp->zc_case = cp;

	fmd_buf_read(hdl, cp, CASE_DATA, &zcp->zc_data,
	    sizeof (zcp->zc_data));

	if (zcp->zc_data.zc_version > CASE_DATA_VERSION_SERD) {
		fmd_hdl_free(hdl, zcp, sizeof (zfs_case_t));
		return (NULL);
	}

	/*
	 * fmd_buf_read() will have already zeroed out the remainder of the
	 * buffer, so we don't have to do anything special if the version
	 * doesn't include the SERD engine name.
	 */

	if (zcp->zc_data.zc_has_remove_timer)
		zcp->zc_remove_timer = fmd_timer_install(hdl, zcp,
		    NULL, zfs_remove_timeout);

	uu_list_node_init(zcp, &zcp->zc_node, zfs_case_pool);
	(void) uu_list_insert_before(zfs_cases, NULL, zcp);

	fmd_case_setspecific(hdl, cp, zcp);

	return (zcp);
}
Ejemplo n.º 8
0
static void
cmd_dimm_free(fmd_hdl_t *hdl, cmd_dimm_t *dimm, int destroy)
{
	cmd_case_t *cc = &dimm->dimm_case;

#ifdef sun4v
	cmd_branch_t *branch;
#endif
	if (cc->cc_cp != NULL) {
		cmd_case_fini(hdl, cc->cc_cp, destroy);
		if (cc->cc_serdnm != NULL) {
			if (fmd_serd_exists(hdl, cc->cc_serdnm) &&
			    destroy)
				fmd_serd_destroy(hdl, cc->cc_serdnm);
			fmd_hdl_strfree(hdl, cc->cc_serdnm);
		}
	}

	if (dimm->dimm_bank != NULL)
		cmd_bank_remove_dimm(hdl, dimm->dimm_bank, dimm);
#ifdef sun4v
	branch = cmd_branch_lookup_by_unum(hdl, dimm->dimm_unum);
	if (branch != NULL)
		cmd_branch_remove_dimm(hdl, branch, dimm);
#endif

	cmd_fmri_fini(hdl, &dimm->dimm_asru, destroy);

	if (destroy)
		fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
	cmd_list_delete(&cmd.cmd_dimms, dimm);
	fmd_hdl_free(hdl, dimm, sizeof (cmd_dimm_t));
}
Ejemplo n.º 9
0
/*
 * Teardown any transport infrastructure after all connections are closed.
 * Return 0 for success, or nonzero for failure.
 */
int
etm_xport_fini(fmd_hdl_t *hdl, etm_xport_hdl_t tlhdl)
{
	exs_hdl_t *hp = (exs_hdl_t *)tlhdl;
	exs_hdl_t *xp, **ppx;

	(void) pthread_mutex_lock(&List_lock);

	ppx = &Exh_head;

	for (xp = *ppx; xp; xp = xp->h_next) {
		if (xp != hp)
			ppx = &xp->h_next;
		else
			break;
	}

	if (xp != hp) {
		(void) pthread_mutex_unlock(&List_lock);
		fmd_hdl_abort(hdl, "xport - fini failed, tlhdl %p not on list",
		    (void *)hp);
	}

	*ppx = hp->h_next;
	hp->h_next = NULL;

	if (hp->h_tid != EXS_TID_FREE) {
		hp->h_quit = 1;
		fmd_thr_signal(hdl, hp->h_tid);
		fmd_thr_destroy(hdl, hp->h_tid);
	}

	if (hp->h_server.c_sd != EXS_SD_FREE)
		(void) close(hp->h_server.c_sd);

	if (hp->h_client.c_sd != EXS_SD_FREE)
		(void) close(hp->h_client.c_sd);

	fmd_hdl_strfree(hdl, hp->h_endpt_id);
	fmd_hdl_free(hdl, hp, sizeof (exs_hdl_t));

	if (Exh_head == NULL) {
		/* Undo one-time initializations */
		exs_filter_fini(hdl);

		/* Destroy the accept/listen thread */
		if (Acc_tid != EXS_TID_FREE) {
			Acc_quit = 1;
			fmd_thr_signal(hdl, Acc_tid);
			fmd_thr_destroy(hdl, Acc_tid);
		}

		if (Acc.c_sd != EXS_SD_FREE)
			EXS_CLOSE_CLR(Acc);
	}

	(void) pthread_mutex_unlock(&List_lock);

	return (0);
}
Ejemplo n.º 10
0
static void
cma_cpu_free(fmd_hdl_t *hdl, cma_cpu_t *cpu)
{
	if (cpu->cpu_fmri != NULL)
		nvlist_free(cpu->cpu_fmri);
	if (cpu->cpu_uuid != NULL)
		fmd_hdl_strfree(hdl, cpu->cpu_uuid);
	fmd_hdl_free(hdl, cpu, sizeof (cma_cpu_t));
}
Ejemplo n.º 11
0
static void
branch_dimmlist_free(fmd_hdl_t *hdl, cmd_branch_t *branch)
{
	cmd_branch_memb_t *bm;

	while ((bm = cmd_list_next(&branch->branch_dimms)) != NULL) {
		cmd_list_delete(&branch->branch_dimms, bm);
		fmd_hdl_free(hdl, bm, sizeof (cmd_branch_memb_t));
	}
}
Ejemplo n.º 12
0
void
_fmd_fini(fmd_hdl_t *hdl)
{
	sp_monitor_t *smp = fmd_hdl_getspecific(hdl);

	if (smp) {
		ipmi_close(smp->sm_hdl);
		fmd_hdl_free(hdl, smp, sizeof (sp_monitor_t));
	}
}
Ejemplo n.º 13
0
static void
free_notify_prefs(fmd_hdl_t *hdl, nvlist_t **prefs, uint_t nprefs)
{
	int i;

	for (i = 0; i < nprefs; i++) {
		nvlist_free(prefs[i]);
	}

	fmd_hdl_free(hdl, prefs, sizeof (nvlist_t *) * nprefs);
}
Ejemplo n.º 14
0
static int
get_notify_prefs(fmd_hdl_t *hdl, nvlist_t *ev_nvl, nvlist_t ***pref_nvl,
    uint_t *nprefs)
{
	nvlist_t *top_nvl, **np_nvlarr, *mech_nvl;
	nvlist_t **tmparr;
	int ret, i;
	uint_t nelem, nslelem;

	if ((ret = smf_notify_get_params(&top_nvl, ev_nvl)) != SCF_SUCCESS) {
		ret = scf_error();
		if (ret != SCF_ERROR_NOT_FOUND) {
			fmd_hdl_debug(hdl, "Error looking up notification "
			    "preferences (%s)", scf_strerror(ret));
			return (ret);
		}
		return (ret);
	}

	if (nvlist_lookup_nvlist_array(top_nvl, SCF_NOTIFY_PARAMS, &np_nvlarr,
	    &nelem) != 0) {
		fmd_hdl_debug(hdl, "Malformed preference nvlist\n");
		ret = SCF_ERROR_INVALID_ARGUMENT;
		goto pref_done;
	}

	tmparr = fmd_hdl_alloc(hdl, nelem * sizeof (nvlist_t *), FMD_SLEEP);
	nslelem = 0;

	for (i = 0; i < nelem; i++) {
		if (nvlist_lookup_nvlist(np_nvlarr[i], "syslog", &mech_nvl)
		    == 0)
			tmparr[nslelem++] = fmd_nvl_dup(hdl, mech_nvl,
			    FMD_SLEEP);
	}

	if (nslelem != 0) {
		size_t sz = nslelem * sizeof (nvlist_t *);

		*pref_nvl = fmd_hdl_zalloc(hdl, sz, FMD_SLEEP);
		*nprefs = nslelem;
		bcopy(tmparr, *pref_nvl, sz);
		ret = 0;
	} else {
		*pref_nvl = NULL;
		*nprefs = 0;
		ret = SCF_ERROR_NOT_FOUND;
	}

	fmd_hdl_free(hdl, tmparr, nelem * sizeof (nvlist_t *));
pref_done:
	nvlist_free(top_nvl);
	return (ret);
}
Ejemplo n.º 15
0
static void
etm_xport_free_addr(fmd_hdl_t *hdl, etm_xport_addr_t addr)
{
    if (addr == NULL) {
        etm_xport_stats.xport_free_addr_badargs.fmds_value.ui64++;
        return;
    }

    fmd_hdl_free(hdl, addr, sizeof (_etm_xport_addr_t));

} /* etm_xport_free_addr() */
Ejemplo n.º 16
0
etm_xport_conn_t
etm_xport_close(fmd_hdl_t *hdl, etm_xport_conn_t conn)
{
    etm_xport_conn_t	rv;	/* ret val */
    _etm_xport_conn_t	*_conn;	/* connection handle */
    int			nev;	/* -errno val */

    _conn = conn;

    rv = _conn;	/* assume success */

    if ((nev = etm_xport_valid_conn(_conn)) < 0) {
        _conn = NULL;
        rv = NULL;
        goto func_ret;
    }

    /* close the device node */

    (void) pthread_mutex_lock(&etm_xport_vldc_lock);

    if (close(_conn->fd) < 0) {
        /* errno assumed set by above call */
        etm_xport_stats.xport_os_close_fail.fmds_value.ui64++;
        nev = (-errno);
        rv = NULL;
    }

    if (use_vldc && (_conn == etm_xport_vldc_conn)) {
        etm_xport_vldc_conn = NULL;
    }

    (void) pthread_mutex_unlock(&etm_xport_vldc_lock);

func_ret:

    /* cleanup the connection */

    if (_conn != NULL) {
        etm_xport_free_addr(hdl, _conn->addr);
        _conn->addr = NULL;
        _conn->magic_num = 0;
        _conn->fd = -1;
        fmd_hdl_free(hdl, _conn, sizeof (_etm_xport_conn_t));
    }

    if (rv == NULL) {
        errno = (-nev);
    }
    return (rv);

} /* etm_xport_close() */
Ejemplo n.º 17
0
static cmd_branch_t *
branch_wrapv0(fmd_hdl_t *hdl, cmd_branch_pers_t *pers, size_t psz)
{
	cmd_branch_t *branch;

	if (psz != sizeof (cmd_branch_pers_t)) {
		fmd_hdl_abort(hdl, "size of state doesn't match size of "
		    "version 0 state (%u bytes).\n",
		    sizeof (cmd_branch_pers_t));
	}

	branch = fmd_hdl_zalloc(hdl, sizeof (cmd_branch_t), FMD_SLEEP);
	bcopy(pers, branch, sizeof (cmd_branch_pers_t));
	fmd_hdl_free(hdl, pers, psz);
	return (branch);
}
Ejemplo n.º 18
0
void
etm_xport_free_addrv(fmd_hdl_t *hdl, etm_xport_addr_t *addrv)
{
    _etm_xport_addr_t	**_addrv;	/* vector of addrs */
    int			i;		/* vector index */

    if (addrv == NULL) {
        etm_xport_stats.xport_free_addrv_badargs.fmds_value.ui64++;
        return;
    }

    _addrv = (void*)addrv;

    for (i = 0; _addrv[i] != NULL; i++) {
        etm_xport_free_addr(hdl, _addrv[i]);
        _addrv[i] = NULL;
    }
    fmd_hdl_free(hdl, _addrv, (i + 1) * sizeof (_etm_xport_addr_t *));

} /* etm_xport_free_addrv() */
Ejemplo n.º 19
0
void
cmd_branch_remove_dimm(fmd_hdl_t *hdl, cmd_branch_t *branch, cmd_dimm_t *dimm)
{
	cmd_branch_memb_t *bm;

	fmd_hdl_debug(hdl, "Detaching dimm %s from branch %s\n",
	    dimm->dimm_unum, branch->branch_unum);

	for (bm = cmd_list_next(&branch->branch_dimms); bm != NULL;
	    bm = cmd_list_next(bm)) {
		if (bm->dimm == dimm) {
			cmd_list_delete(&branch->branch_dimms, bm);
			fmd_hdl_free(hdl, bm, sizeof (cmd_branch_memb_t));
			return;
		}
	}

	fmd_hdl_abort(hdl,
	    "Attempt to disconnect dimm from non-parent branch\n");
}
Ejemplo n.º 20
0
void
cmd_dimm_destroy(fmd_hdl_t *hdl, cmd_dimm_t *dimm)
{
	int i;
	cmd_mq_t *q;

	for (i = 0; i < CMD_MAX_CKWDS; i++) {
		while ((q = cmd_list_next(&dimm->mq_root[i])) != NULL) {
			if (q->mq_serdnm != NULL) {
				if (fmd_serd_exists(hdl, q->mq_serdnm)) {
					fmd_serd_destroy(hdl, q->mq_serdnm);
				}
				fmd_hdl_strfree(hdl, q->mq_serdnm);
				q->mq_serdnm = NULL;
			}
			cmd_list_delete(&dimm->mq_root[i], q);
			fmd_hdl_free(hdl, q, sizeof (cmd_mq_t));
		}
	}

	fmd_stat_destroy(hdl, 1, &(dimm->dimm_retstat));
	cmd_dimm_free(hdl, dimm, FMD_B_TRUE);
}
Ejemplo n.º 21
0
int
etm_xport_fini(fmd_hdl_t *hdl)
{
    fmd_hdl_debug(hdl, "info: xport finalizing\n");

    if (use_vldc && (etm_xport_vldc_conn != NULL)) {
        (void) etm_xport_close(hdl, etm_xport_vldc_conn);
        etm_xport_vldc_conn = NULL;
    }

    /* free any long standing properties from FMD */

    fmd_prop_free_string(hdl, etm_xport_addrs);

    /* cleanup the intermediate read buffer */

    if (etm_xport_irb_tail != etm_xport_irb_head) {
        fmd_hdl_debug(hdl, "warning: xport %d bytes stale data\n",
                      (int)(etm_xport_irb_tail - etm_xport_irb_head));
    }
    fmd_hdl_free(hdl, etm_xport_irb_area, 2 * etm_xport_irb_mtu_sz);
    etm_xport_irb_area = NULL;
    etm_xport_irb_head = NULL;
    etm_xport_irb_tail = NULL;
    etm_xport_irb_mtu_sz = 0;

    /* cleanup statistics from FMD */

    (void) fmd_stat_destroy(hdl,
                            sizeof (etm_xport_stats) / sizeof (fmd_stat_t),
                            (fmd_stat_t *)&etm_xport_stats);

    fmd_hdl_debug(hdl, "info: xport finalized ok\n");
    return (0);

} /* etm_xport_fini() */
Ejemplo n.º 22
0
etm_xport_conn_t
etm_xport_open(fmd_hdl_t *hdl, etm_xport_addr_t addr)
{
    _etm_xport_addr_t	*_addr;		/* address handle */
    _etm_xport_conn_t	*_conn;		/* connection handle */
    ssize_t			n;		/* gen use */

    if ((n = etm_xport_valid_addr(addr)) < 0) {
        errno = (-n);
        return (NULL);
    }

    _addr = etm_xport_dup_addr(hdl, addr);

    /* allocate a connection handle and start populating it */

    _conn = fmd_hdl_zalloc(hdl, sizeof (_etm_xport_conn_t), FMD_SLEEP);

    (void) pthread_mutex_lock(&etm_xport_vldc_lock);

    if (use_vldc == 0 || etm_xport_vldc_conn == NULL) {
        if ((_conn->fd = open(_addr->fn,
                              ETM_XPORT_OPEN_FLAGS, 0)) == -1) {
            /* errno assumed set by above call */
            etm_xport_free_addr(hdl, _addr);
            fmd_hdl_free(hdl, _conn, sizeof (_etm_xport_conn_t));
            etm_xport_stats.xport_os_open_fail.fmds_value.ui64++;
            (void) pthread_mutex_unlock(&etm_xport_vldc_lock);
            return (NULL);
        }
    }

    if (use_vldc && etm_xport_vldc_conn == NULL) {
        vldc_opt_op_t op;

        /* Set the channel to reliable mode */
        op.op_sel = VLDC_OP_SET;
        op.opt_sel = VLDC_OPT_MODE;
        op.opt_val = LDC_MODE_RELIABLE;

        if (ioctl(_conn->fd, VLDC_IOCTL_OPT_OP, &op) != 0) {
            /* errno assumed set by above call */
            (void) close(_conn->fd);
            etm_xport_free_addr(hdl, _addr);
            fmd_hdl_free(hdl, _conn, sizeof (_etm_xport_conn_t));
            etm_xport_stats.xport_os_ioctl_fail.fmds_value.ui64++;
            (void) pthread_mutex_unlock(&etm_xport_vldc_lock);
            return (NULL);
        }

        etm_xport_vldc_conn = _conn;
    } else if (use_vldc && etm_xport_vldc_conn != NULL) {
        _conn->fd = dup(etm_xport_vldc_conn->fd);
    }

    (void) pthread_mutex_unlock(&etm_xport_vldc_lock);

    /* return the fully formed connection handle */

    _conn->magic_num = ETM_XPORT_DD_MAGIC_CONN;
    _conn->addr = _addr;

    return (_conn);

} /* etm_xport_open() */
Ejemplo n.º 23
0
nvlist_t *
cmd_mkboard_fru(fmd_hdl_t *hdl, char *frustr, char *serialstr, char *partstr) {

	char *nac, *nac_name;
	int n, i, len;
	nvlist_t *fru, **hc_list;

	if (frustr == NULL)
		return (NULL);

	if ((nac_name = strstr(frustr, "MB")) == NULL)
		return (NULL);

	len = strlen(nac_name) + 1;

	nac = fmd_hdl_zalloc(hdl, len, FMD_SLEEP);
	(void) strcpy(nac, nac_name);

	n = cmd_count_components(nac, '/');

	fmd_hdl_debug(hdl, "cmd_mkboard_fru: nac=%s components=%d\n", nac, n);

	hc_list = fmd_hdl_zalloc(hdl, sizeof (nvlist_t *)*n, FMD_SLEEP);

	for (i = 0; i < n; i++) {
		(void) nvlist_alloc(&hc_list[i],
		    NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE, 0);
	}

	if (cmd_breakup_components(nac, "/", hc_list) < 0) {
		for (i = 0; i < n; i++) {
			if (hc_list[i] != NULL)
			    nvlist_free(hc_list[i]);
		}
		fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
		fmd_hdl_free(hdl, nac, len);
		return (NULL);
	}

	if (nvlist_alloc(&fru, NV_UNIQUE_NAME, 0) != 0) {
		for (i = 0; i < n; i++) {
			if (hc_list[i] != NULL)
			    nvlist_free(hc_list[i]);
		}
		fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
		fmd_hdl_free(hdl, nac, len);
		return (NULL);
	}

	if (nvlist_add_uint8(fru, FM_VERSION, FM_HC_SCHEME_VERSION) != 0 ||
	    nvlist_add_string(fru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0 ||
	    nvlist_add_string(fru, FM_FMRI_HC_ROOT, "") != 0 ||
	    nvlist_add_uint32(fru, FM_FMRI_HC_LIST_SZ, n) != 0 ||
	    nvlist_add_nvlist_array(fru, FM_FMRI_HC_LIST, hc_list, n) != 0) {
		for (i = 0; i < n; i++) {
			if (hc_list[i] != NULL)
			    nvlist_free(hc_list[i]);
		}
		fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
		fmd_hdl_free(hdl, nac, len);
		nvlist_free(fru);
		return (NULL);
	}

	for (i = 0; i < n; i++) {
		if (hc_list[i] != NULL)
		    nvlist_free(hc_list[i]);
	}
	fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
	fmd_hdl_free(hdl, nac, len);

	if ((serialstr != NULL &&
	    nvlist_add_string(fru, FM_FMRI_HC_SERIAL_ID, serialstr) != 0) ||
	    (partstr != NULL &&
	    nvlist_add_string(fru, FM_FMRI_HC_PART, partstr) != 0)) {
		nvlist_free(fru);
		return (NULL);
	}

	return (fru);
}