xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_scheme.c (revision bbf21555)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
519e1255fScy  * Common Development and Distribution License (the "License").
619e1255fScy  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21d9638e54Smws 
227c478bd9Sstevel@tonic-gate /*
23e58a33b6SStephen Hanson  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <limits.h>
277c478bd9Sstevel@tonic-gate #include <stddef.h>
287c478bd9Sstevel@tonic-gate #include <unistd.h>
297c478bd9Sstevel@tonic-gate #include <dlfcn.h>
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <fmd_alloc.h>
327c478bd9Sstevel@tonic-gate #include <fmd_error.h>
337c478bd9Sstevel@tonic-gate #include <fmd_subr.h>
347c478bd9Sstevel@tonic-gate #include <fmd_string.h>
357c478bd9Sstevel@tonic-gate #include <fmd_scheme.h>
367c478bd9Sstevel@tonic-gate #include <fmd_fmri.h>
377c478bd9Sstevel@tonic-gate #include <fmd_module.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include <fmd.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * The fmd resource scheme, used for fmd modules, must be implemented here for
43*bbf21555SRichard Lowe  * the benefit of fmd-self-diagnosis and also in schemes/fmd for fmdump(8).
447c478bd9Sstevel@tonic-gate  */
457c478bd9Sstevel@tonic-gate ssize_t
fmd_scheme_fmd_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)467c478bd9Sstevel@tonic-gate fmd_scheme_fmd_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
477c478bd9Sstevel@tonic-gate {
487c478bd9Sstevel@tonic-gate 	char *name;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
517c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate 	return (snprintf(buf, buflen,
547c478bd9Sstevel@tonic-gate 	    "%s:///module/%s", FM_FMRI_SCHEME_FMD, name));
557c478bd9Sstevel@tonic-gate }
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate static int
fmd_scheme_fmd_present(nvlist_t * nvl)587c478bd9Sstevel@tonic-gate fmd_scheme_fmd_present(nvlist_t *nvl)
597c478bd9Sstevel@tonic-gate {
607c478bd9Sstevel@tonic-gate 	char *name, *version;
617c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
62e58a33b6SStephen Hanson 	int rv = 1;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0 ||
657c478bd9Sstevel@tonic-gate 	    nvlist_lookup_string(nvl, FM_FMRI_FMD_VERSION, &version) != 0)
667c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
677c478bd9Sstevel@tonic-gate 
6835f59e50SStephen Hanson 	if (!fmd.d_loaded)
6935f59e50SStephen Hanson 		return (1);
7035f59e50SStephen Hanson 
717c478bd9Sstevel@tonic-gate 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
7219e1255fScy 		rv = mp->mod_vers != NULL &&
7319e1255fScy 		    strcmp(mp->mod_vers, version) == 0;
747c478bd9Sstevel@tonic-gate 		fmd_module_rele(mp);
757c478bd9Sstevel@tonic-gate 	}
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 	return (rv);
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate 
8025c6ff4bSstephh static int
fmd_scheme_fmd_replaced(nvlist_t * nvl)8125c6ff4bSstephh fmd_scheme_fmd_replaced(nvlist_t *nvl)
8225c6ff4bSstephh {
8325c6ff4bSstephh 	char *name, *version;
8425c6ff4bSstephh 	fmd_module_t *mp;
85e58a33b6SStephen Hanson 	int rv = 1;
8625c6ff4bSstephh 
8725c6ff4bSstephh 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0 ||
8825c6ff4bSstephh 	    nvlist_lookup_string(nvl, FM_FMRI_FMD_VERSION, &version) != 0)
8925c6ff4bSstephh 		return (fmd_fmri_set_errno(EINVAL));
9025c6ff4bSstephh 
9135f59e50SStephen Hanson 	if (!fmd.d_loaded)
9235f59e50SStephen Hanson 		return (FMD_OBJ_STATE_UNKNOWN);
9335f59e50SStephen Hanson 
9425c6ff4bSstephh 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
9525c6ff4bSstephh 		rv = mp->mod_vers != NULL &&
9625c6ff4bSstephh 		    strcmp(mp->mod_vers, version) == 0;
9725c6ff4bSstephh 		fmd_module_rele(mp);
9825c6ff4bSstephh 	}
9925c6ff4bSstephh 
100e58a33b6SStephen Hanson 	return (rv ? FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED);
10125c6ff4bSstephh }
10225c6ff4bSstephh 
10325c6ff4bSstephh static int
fmd_scheme_fmd_service_state(nvlist_t * nvl)10425c6ff4bSstephh fmd_scheme_fmd_service_state(nvlist_t *nvl)
10525c6ff4bSstephh {
10625c6ff4bSstephh 	char *name;
10725c6ff4bSstephh 	fmd_module_t *mp;
10825c6ff4bSstephh 	int rv = 1;
10925c6ff4bSstephh 
11025c6ff4bSstephh 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
11125c6ff4bSstephh 		return (fmd_fmri_set_errno(EINVAL));
11225c6ff4bSstephh 
11335f59e50SStephen Hanson 	if (!fmd.d_loaded)
11435f59e50SStephen Hanson 		return (FMD_SERVICE_STATE_UNKNOWN);
11535f59e50SStephen Hanson 
11625c6ff4bSstephh 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
11725c6ff4bSstephh 		rv = mp->mod_error != 0;
11825c6ff4bSstephh 		fmd_module_rele(mp);
11925c6ff4bSstephh 	}
12025c6ff4bSstephh 
12125c6ff4bSstephh 	return (rv ? FMD_SERVICE_STATE_UNUSABLE : FMD_SERVICE_STATE_OK);
12225c6ff4bSstephh }
12325c6ff4bSstephh 
1247c478bd9Sstevel@tonic-gate static int
fmd_scheme_fmd_unusable(nvlist_t * nvl)1257c478bd9Sstevel@tonic-gate fmd_scheme_fmd_unusable(nvlist_t *nvl)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	char *name;
1287c478bd9Sstevel@tonic-gate 	fmd_module_t *mp;
1297c478bd9Sstevel@tonic-gate 	int rv = 1;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
1327c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1337c478bd9Sstevel@tonic-gate 
13435f59e50SStephen Hanson 	if (!fmd.d_loaded)
13535f59e50SStephen Hanson 		return (0);
13635f59e50SStephen Hanson 
1377c478bd9Sstevel@tonic-gate 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
1387c478bd9Sstevel@tonic-gate 		rv = mp->mod_error != 0;
1397c478bd9Sstevel@tonic-gate 		fmd_module_rele(mp);
1407c478bd9Sstevel@tonic-gate 	}
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	return (rv);
1437c478bd9Sstevel@tonic-gate }
1447c478bd9Sstevel@tonic-gate 
145d9638e54Smws static nvlist_t *
fmd_scheme_notranslate(nvlist_t * fmri,nvlist_t * auth __unused)1461743a90dSToomas Soome fmd_scheme_notranslate(nvlist_t *fmri, nvlist_t *auth __unused)
147d9638e54Smws {
148d9638e54Smws 	(void) nvlist_xdup(fmri, &fmri, &fmd.d_nva);
149d9638e54Smws 	return (fmri);
150d9638e54Smws }
151d9638e54Smws 
1521743a90dSToomas Soome static ssize_t
fmd_scheme_notsup_nvl2str(nvlist_t * fmri __unused,char * arg1 __unused,size_t arg2 __unused)1531743a90dSToomas Soome fmd_scheme_notsup_nvl2str(nvlist_t *fmri __unused, char *arg1 __unused,
1541743a90dSToomas Soome     size_t arg2 __unused)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	return (fmd_set_errno(EFMD_FMRI_NOTSUP));
1577c478bd9Sstevel@tonic-gate }
1587c478bd9Sstevel@tonic-gate 
1591743a90dSToomas Soome static int
fmd_scheme_notsup(nvlist_t * fmri __unused)1601743a90dSToomas Soome fmd_scheme_notsup(nvlist_t *fmri __unused)
1611743a90dSToomas Soome {
1621743a90dSToomas Soome 	return (fmd_set_errno(EFMD_FMRI_NOTSUP));
1631743a90dSToomas Soome }
1641743a90dSToomas Soome 
1651743a90dSToomas Soome static int
fmd_scheme_notsup2(nvlist_t * fmri1 __unused,nvlist_t * fmri2 __unused)1661743a90dSToomas Soome fmd_scheme_notsup2(nvlist_t *fmri1 __unused, nvlist_t *fmri2 __unused)
1671743a90dSToomas Soome {
1681743a90dSToomas Soome 	return (fmd_set_errno(EFMD_FMRI_NOTSUP));
1691743a90dSToomas Soome }
1701743a90dSToomas Soome 
1711743a90dSToomas Soome static void
fmd_scheme_vnop(void)1721743a90dSToomas Soome fmd_scheme_vnop(void)
1731743a90dSToomas Soome {
1741743a90dSToomas Soome }
1751743a90dSToomas Soome 
1767c478bd9Sstevel@tonic-gate static int
fmd_scheme_nop(void)1777c478bd9Sstevel@tonic-gate fmd_scheme_nop(void)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	return (0);
1807c478bd9Sstevel@tonic-gate }
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate /*
1837c478bd9Sstevel@tonic-gate  * Default values for the scheme ops.  If a scheme function is not defined in
1847c478bd9Sstevel@tonic-gate  * the module, then this operation is implemented using the default function.
1857c478bd9Sstevel@tonic-gate  */
1867c478bd9Sstevel@tonic-gate static const fmd_scheme_ops_t _fmd_scheme_default_ops = {
1871743a90dSToomas Soome 	.sop_init = fmd_scheme_nop,
1881743a90dSToomas Soome 	.sop_fini = fmd_scheme_vnop,
1891743a90dSToomas Soome 	.sop_nvl2str = fmd_scheme_notsup_nvl2str,
1901743a90dSToomas Soome 	.sop_expand = fmd_scheme_notsup,
1911743a90dSToomas Soome 	.sop_present = fmd_scheme_notsup,
1921743a90dSToomas Soome 	.sop_replaced = fmd_scheme_notsup,
1931743a90dSToomas Soome 	.sop_service_state = fmd_scheme_notsup,
1941743a90dSToomas Soome 	.sop_unusable = fmd_scheme_notsup,
1951743a90dSToomas Soome 	.sop_contains = fmd_scheme_notsup2,
1961743a90dSToomas Soome 	.sop_translate = fmd_scheme_notranslate
1977c478bd9Sstevel@tonic-gate };
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate static const fmd_scheme_ops_t _fmd_scheme_builtin_ops = {
2001743a90dSToomas Soome 	.sop_init = fmd_scheme_nop,
2011743a90dSToomas Soome 	.sop_fini = fmd_scheme_vnop,
2021743a90dSToomas Soome 	.sop_nvl2str = fmd_scheme_fmd_nvl2str,
2031743a90dSToomas Soome 	.sop_expand = fmd_scheme_notsup,
2041743a90dSToomas Soome 	.sop_present = fmd_scheme_fmd_present,
2051743a90dSToomas Soome 	.sop_replaced = fmd_scheme_fmd_replaced,
2061743a90dSToomas Soome 	.sop_service_state = fmd_scheme_fmd_service_state,
2071743a90dSToomas Soome 	.sop_unusable = fmd_scheme_fmd_unusable,
2081743a90dSToomas Soome 	.sop_contains = fmd_scheme_notsup2,
2091743a90dSToomas Soome 	.sop_translate = fmd_scheme_notranslate
2107c478bd9Sstevel@tonic-gate };
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate  * Scheme ops descriptions.  These names and offsets are used by the function
2147c478bd9Sstevel@tonic-gate  * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t.
2157c478bd9Sstevel@tonic-gate  */
2167c478bd9Sstevel@tonic-gate static const fmd_scheme_opd_t _fmd_scheme_ops[] = {
2177c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) },
2187c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) },
2197c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) },
2207c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_expand", offsetof(fmd_scheme_ops_t, sop_expand) },
2217c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_present", offsetof(fmd_scheme_ops_t, sop_present) },
22225c6ff4bSstephh 	{ "fmd_fmri_replaced", offsetof(fmd_scheme_ops_t, sop_replaced) },
22325c6ff4bSstephh 	{ "fmd_fmri_service_state", offsetof(fmd_scheme_ops_t,
22425c6ff4bSstephh 	    sop_service_state) },
2257c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_unusable", offsetof(fmd_scheme_ops_t, sop_unusable) },
2267c478bd9Sstevel@tonic-gate 	{ "fmd_fmri_contains", offsetof(fmd_scheme_ops_t, sop_contains) },
227d9638e54Smws 	{ "fmd_fmri_translate", offsetof(fmd_scheme_ops_t, sop_translate) },
2287c478bd9Sstevel@tonic-gate 	{ NULL, 0 }
2297c478bd9Sstevel@tonic-gate };
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate static fmd_scheme_t *
fmd_scheme_create(const char * name)2327c478bd9Sstevel@tonic-gate fmd_scheme_create(const char *name)
2337c478bd9Sstevel@tonic-gate {
2347c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp = fmd_alloc(sizeof (fmd_scheme_t), FMD_SLEEP);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&sp->sch_lock, NULL);
2377c478bd9Sstevel@tonic-gate 	(void) pthread_cond_init(&sp->sch_cv, NULL);
2387c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_init(&sp->sch_opslock, NULL);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	sp->sch_next = NULL;
2417c478bd9Sstevel@tonic-gate 	sp->sch_name = fmd_strdup(name, FMD_SLEEP);
2427c478bd9Sstevel@tonic-gate 	sp->sch_dlp = NULL;
2437c478bd9Sstevel@tonic-gate 	sp->sch_refs = 1;
2447c478bd9Sstevel@tonic-gate 	sp->sch_loaded = 0;
2457c478bd9Sstevel@tonic-gate 	sp->sch_ops = _fmd_scheme_default_ops;
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	return (sp);
2487c478bd9Sstevel@tonic-gate }
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate static void
fmd_scheme_destroy(fmd_scheme_t * sp)2517c478bd9Sstevel@tonic-gate fmd_scheme_destroy(fmd_scheme_t *sp)
2527c478bd9Sstevel@tonic-gate {
2537c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->sch_lock));
2547c478bd9Sstevel@tonic-gate 	ASSERT(sp->sch_refs == 0);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	if (sp->sch_dlp != NULL) {
2577c478bd9Sstevel@tonic-gate 		TRACE((FMD_DBG_FMRI, "dlclose scheme %s", sp->sch_name));
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 		if (sp->sch_ops.sop_fini != NULL)
2607c478bd9Sstevel@tonic-gate 			sp->sch_ops.sop_fini();
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 		(void) dlclose(sp->sch_dlp);
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	fmd_strfree(sp->sch_name);
2667c478bd9Sstevel@tonic-gate 	fmd_free(sp, sizeof (fmd_scheme_t));
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate fmd_scheme_hash_t *
fmd_scheme_hash_create(const char * rootdir,const char * dirpath)2707c478bd9Sstevel@tonic-gate fmd_scheme_hash_create(const char *rootdir, const char *dirpath)
2717c478bd9Sstevel@tonic-gate {
2727c478bd9Sstevel@tonic-gate 	fmd_scheme_hash_t *shp;
2737c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
2747c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	shp = fmd_alloc(sizeof (fmd_scheme_hash_t), FMD_SLEEP);
2777c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s/%s", rootdir, dirpath);
2787c478bd9Sstevel@tonic-gate 	shp->sch_dirpath = fmd_strdup(path, FMD_SLEEP);
2797c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_init(&shp->sch_rwlock, NULL);
2807c478bd9Sstevel@tonic-gate 	shp->sch_hashlen = fmd.d_str_buckets;
2817c478bd9Sstevel@tonic-gate 	shp->sch_hash = fmd_zalloc(sizeof (fmd_scheme_t *) *
2827c478bd9Sstevel@tonic-gate 	    shp->sch_hashlen, FMD_SLEEP);
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	sp = fmd_scheme_create(FM_FMRI_SCHEME_FMD);
2857c478bd9Sstevel@tonic-gate 	sp->sch_ops = _fmd_scheme_builtin_ops;
2867c478bd9Sstevel@tonic-gate 	sp->sch_loaded = FMD_B_TRUE;
2877c478bd9Sstevel@tonic-gate 	shp->sch_hash[fmd_strhash(sp->sch_name) % shp->sch_hashlen] = sp;
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	return (shp);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate void
fmd_scheme_hash_destroy(fmd_scheme_hash_t * shp)2937c478bd9Sstevel@tonic-gate fmd_scheme_hash_destroy(fmd_scheme_hash_t *shp)
2947c478bd9Sstevel@tonic-gate {
2957c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp, *np;
2967c478bd9Sstevel@tonic-gate 	uint_t i;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	for (i = 0; i < shp->sch_hashlen; i++) {
2997c478bd9Sstevel@tonic-gate 		for (sp = shp->sch_hash[i]; sp != NULL; sp = np) {
3007c478bd9Sstevel@tonic-gate 			np = sp->sch_next;
3017c478bd9Sstevel@tonic-gate 			sp->sch_next = NULL;
3027c478bd9Sstevel@tonic-gate 			fmd_scheme_hash_release(shp, sp);
3037c478bd9Sstevel@tonic-gate 		}
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	fmd_free(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen);
3077c478bd9Sstevel@tonic-gate 	fmd_strfree(shp->sch_dirpath);
3087c478bd9Sstevel@tonic-gate 	fmd_free(shp, sizeof (fmd_scheme_hash_t));
3097c478bd9Sstevel@tonic-gate }
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate void
fmd_scheme_hash_trygc(fmd_scheme_hash_t * shp)3127c478bd9Sstevel@tonic-gate fmd_scheme_hash_trygc(fmd_scheme_hash_t *shp)
3137c478bd9Sstevel@tonic-gate {
3147c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp, *np;
3157c478bd9Sstevel@tonic-gate 	uint_t i;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	if (shp == NULL || pthread_rwlock_trywrlock(&shp->sch_rwlock) != 0)
3187c478bd9Sstevel@tonic-gate 		return; /* failed to acquire lock: just skip garbage collect */
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	for (i = 0; i < shp->sch_hashlen; i++) {
3217c478bd9Sstevel@tonic-gate 		for (sp = shp->sch_hash[i]; sp != NULL; sp = np) {
3227c478bd9Sstevel@tonic-gate 			np = sp->sch_next;
3237c478bd9Sstevel@tonic-gate 			sp->sch_next = NULL;
3247c478bd9Sstevel@tonic-gate 			fmd_scheme_hash_release(shp, sp);
3257c478bd9Sstevel@tonic-gate 		}
3267c478bd9Sstevel@tonic-gate 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	bzero(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen);
3297c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&shp->sch_rwlock);
3307c478bd9Sstevel@tonic-gate }
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate static int
fmd_scheme_rtld_init(fmd_scheme_t * sp)3337c478bd9Sstevel@tonic-gate fmd_scheme_rtld_init(fmd_scheme_t *sp)
3347c478bd9Sstevel@tonic-gate {
3357c478bd9Sstevel@tonic-gate 	const fmd_scheme_opd_t *opd;
3367c478bd9Sstevel@tonic-gate 	void *p;
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) {
3397c478bd9Sstevel@tonic-gate 		if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL)
3407c478bd9Sstevel@tonic-gate 			*(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p;
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	return (0);
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate fmd_scheme_t *
fmd_scheme_hash_xlookup(fmd_scheme_hash_t * shp,const char * name,uint_t h)3477c478bd9Sstevel@tonic-gate fmd_scheme_hash_xlookup(fmd_scheme_hash_t *shp, const char *name, uint_t h)
3487c478bd9Sstevel@tonic-gate {
3497c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&shp->sch_rwlock));
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	for (sp = shp->sch_hash[h]; sp != NULL; sp = sp->sch_next) {
3547c478bd9Sstevel@tonic-gate 		if (strcmp(sp->sch_name, name) == 0)
3557c478bd9Sstevel@tonic-gate 			break;
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	return (sp);
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate /*
3627c478bd9Sstevel@tonic-gate  * Lookup a scheme module by name and return with a reference placed on it.  We
3637c478bd9Sstevel@tonic-gate  * use the scheme hash to cache "negative" entries (e.g. missing modules) as
3647c478bd9Sstevel@tonic-gate  * well so this function always returns successfully with a non-NULL scheme.
3657c478bd9Sstevel@tonic-gate  * The caller is responsible for applying fmd_scheme_hash_release() afterward.
3667c478bd9Sstevel@tonic-gate  */
3677c478bd9Sstevel@tonic-gate fmd_scheme_t *
fmd_scheme_hash_lookup(fmd_scheme_hash_t * shp,const char * name)3687c478bd9Sstevel@tonic-gate fmd_scheme_hash_lookup(fmd_scheme_hash_t *shp, const char *name)
3697c478bd9Sstevel@tonic-gate {
3707c478bd9Sstevel@tonic-gate 	fmd_scheme_t *sp, *nsp = NULL;
3717c478bd9Sstevel@tonic-gate 	uint_t h;
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	/*
3747c478bd9Sstevel@tonic-gate 	 * Grab the hash lock as reader and look for the appropriate scheme.
3757c478bd9Sstevel@tonic-gate 	 * If the scheme isn't yet loaded, allocate a new scheme and grab the
3767c478bd9Sstevel@tonic-gate 	 * hash lock as writer to insert it (after checking again for it).
3777c478bd9Sstevel@tonic-gate 	 */
3787c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_rdlock(&shp->sch_rwlock);
3797c478bd9Sstevel@tonic-gate 	h = fmd_strhash(name) % shp->sch_hashlen;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) {
3827c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&shp->sch_rwlock);
3837c478bd9Sstevel@tonic-gate 		nsp = fmd_scheme_create(name);
3847c478bd9Sstevel@tonic-gate 		(void) pthread_rwlock_wrlock(&shp->sch_rwlock);
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) {
3877c478bd9Sstevel@tonic-gate 			nsp->sch_next = shp->sch_hash[h];
3887c478bd9Sstevel@tonic-gate 			shp->sch_hash[h] = sp = nsp;
3897c478bd9Sstevel@tonic-gate 		} else {
3907c478bd9Sstevel@tonic-gate 			fmd_scheme_hash_release(shp, nsp);
3917c478bd9Sstevel@tonic-gate 			nsp = NULL;
3927c478bd9Sstevel@tonic-gate 		}
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	/*
3967c478bd9Sstevel@tonic-gate 	 * Grab the scheme lock so it can't disappear and then drop the hash
3977c478bd9Sstevel@tonic-gate 	 * lock so that other lookups in the scheme hash can proceed.
3987c478bd9Sstevel@tonic-gate 	 */
3997c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&sp->sch_lock);
4007c478bd9Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&shp->sch_rwlock);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	/*
4037c478bd9Sstevel@tonic-gate 	 * If we created the scheme, compute its path and try to load it.  If
4047c478bd9Sstevel@tonic-gate 	 * we found an existing scheme, wait until its loaded bit is set.  Once
4057c478bd9Sstevel@tonic-gate 	 * we're done with either operation, increment sch_refs and return.
4067c478bd9Sstevel@tonic-gate 	 */
4077c478bd9Sstevel@tonic-gate 	if (nsp != NULL) {
4087c478bd9Sstevel@tonic-gate 		char path[PATH_MAX];
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path),
4117c478bd9Sstevel@tonic-gate 		    "%s/%s.so", shp->sch_dirpath, sp->sch_name);
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 		TRACE((FMD_DBG_FMRI, "dlopen scheme %s", sp->sch_name));
4147c478bd9Sstevel@tonic-gate 		sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 		if (sp->sch_dlp == NULL) {
4177c478bd9Sstevel@tonic-gate 			fmd_error(EFMD_FMRI_SCHEME,
4187c478bd9Sstevel@tonic-gate 			    "failed to load fmri scheme %s: %s\n", path,
4197c478bd9Sstevel@tonic-gate 			    dlerror());
4207c478bd9Sstevel@tonic-gate 		} else if (fmd_scheme_rtld_init(sp) != 0 ||
4217c478bd9Sstevel@tonic-gate 		    sp->sch_ops.sop_init() != 0) {
4227c478bd9Sstevel@tonic-gate 			fmd_error(EFMD_FMRI_SCHEME,
4237c478bd9Sstevel@tonic-gate 			    "failed to initialize fmri scheme %s", path);
4247c478bd9Sstevel@tonic-gate 			(void) dlclose(sp->sch_dlp);
4257c478bd9Sstevel@tonic-gate 			sp->sch_dlp = NULL;
4267c478bd9Sstevel@tonic-gate 			sp->sch_ops = _fmd_scheme_default_ops;
4277c478bd9Sstevel@tonic-gate 		}
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 		sp->sch_loaded = FMD_B_TRUE; /* set regardless of success */
4307c478bd9Sstevel@tonic-gate 		sp->sch_refs++;
4317c478bd9Sstevel@tonic-gate 		ASSERT(sp->sch_refs != 0);
432d9638e54Smws 
4337c478bd9Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&sp->sch_cv);
434d9638e54Smws 		(void) pthread_mutex_unlock(&sp->sch_lock);
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	} else {
4377c478bd9Sstevel@tonic-gate 		while (!sp->sch_loaded)
4387c478bd9Sstevel@tonic-gate 			(void) pthread_cond_wait(&sp->sch_cv, &sp->sch_lock);
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 		sp->sch_refs++;
4417c478bd9Sstevel@tonic-gate 		ASSERT(sp->sch_refs != 0);
4427c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&sp->sch_lock);
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	return (sp);
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate /*
4497c478bd9Sstevel@tonic-gate  * Release the hold on a scheme obtained using fmd_scheme_hash_lookup().
4507c478bd9Sstevel@tonic-gate  * We take 'shp' for symmetry and in case we need to use it in future work.
4517c478bd9Sstevel@tonic-gate  */
4527c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4537c478bd9Sstevel@tonic-gate void
fmd_scheme_hash_release(fmd_scheme_hash_t * shp,fmd_scheme_t * sp)4547c478bd9Sstevel@tonic-gate fmd_scheme_hash_release(fmd_scheme_hash_t *shp, fmd_scheme_t *sp)
4557c478bd9Sstevel@tonic-gate {
4567c478bd9Sstevel@tonic-gate 	(void) pthread_mutex_lock(&sp->sch_lock);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	ASSERT(sp->sch_refs != 0);
4597c478bd9Sstevel@tonic-gate 	if (--sp->sch_refs == 0)
4607c478bd9Sstevel@tonic-gate 		fmd_scheme_destroy(sp);
4617c478bd9Sstevel@tonic-gate 	else
4627c478bd9Sstevel@tonic-gate 		(void) pthread_mutex_unlock(&sp->sch_lock);
4637c478bd9Sstevel@tonic-gate }
464