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 519e1255fScy152378 * Common Development and Distribution License (the "License"). 619e1255fScy152378 * 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 437c478bd9Sstevel@tonic-gate * the benefit of fmd-self-diagnosis and also in schemes/fmd for fmdump(1M). 447c478bd9Sstevel@tonic-gate */ 457c478bd9Sstevel@tonic-gate ssize_t 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 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 68*35f59e50SStephen Hanson if (!fmd.d_loaded) 69*35f59e50SStephen Hanson return (1); 70*35f59e50SStephen Hanson 717c478bd9Sstevel@tonic-gate if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) { 7219e1255fScy152378 rv = mp->mod_vers != NULL && 7319e1255fScy152378 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 807c478bd9Sstevel@tonic-gate static int 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 91*35f59e50SStephen Hanson if (!fmd.d_loaded) 92*35f59e50SStephen Hanson return (FMD_OBJ_STATE_UNKNOWN); 93*35f59e50SStephen 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 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 113*35f59e50SStephen Hanson if (!fmd.d_loaded) 114*35f59e50SStephen Hanson return (FMD_SERVICE_STATE_UNKNOWN); 115*35f59e50SStephen 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 12425c6ff4bSstephh static int 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 134*35f59e50SStephen Hanson if (!fmd.d_loaded) 135*35f59e50SStephen Hanson return (0); 136*35f59e50SStephen 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 /*ARGSUSED*/ 146d9638e54Smws static nvlist_t * 147d9638e54Smws fmd_scheme_notranslate(nvlist_t *fmri, nvlist_t *auth) 148d9638e54Smws { 149d9638e54Smws (void) nvlist_xdup(fmri, &fmri, &fmd.d_nva); 150d9638e54Smws return (fmri); 151d9638e54Smws } 152d9638e54Smws 1537c478bd9Sstevel@tonic-gate static long 1547c478bd9Sstevel@tonic-gate fmd_scheme_notsup(void) 1557c478bd9Sstevel@tonic-gate { 1567c478bd9Sstevel@tonic-gate return (fmd_set_errno(EFMD_FMRI_NOTSUP)); 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate static int 1607c478bd9Sstevel@tonic-gate fmd_scheme_nop(void) 1617c478bd9Sstevel@tonic-gate { 1627c478bd9Sstevel@tonic-gate return (0); 1637c478bd9Sstevel@tonic-gate } 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate /* 1667c478bd9Sstevel@tonic-gate * Default values for the scheme ops. If a scheme function is not defined in 1677c478bd9Sstevel@tonic-gate * the module, then this operation is implemented using the default function. 1687c478bd9Sstevel@tonic-gate */ 1697c478bd9Sstevel@tonic-gate static const fmd_scheme_ops_t _fmd_scheme_default_ops = { 1707c478bd9Sstevel@tonic-gate (int (*)())fmd_scheme_nop, /* sop_init */ 1717c478bd9Sstevel@tonic-gate (void (*)())fmd_scheme_nop, /* sop_fini */ 1727c478bd9Sstevel@tonic-gate (ssize_t (*)())fmd_scheme_notsup, /* sop_nvl2str */ 1737c478bd9Sstevel@tonic-gate (int (*)())fmd_scheme_nop, /* sop_expand */ 1747c478bd9Sstevel@tonic-gate (int (*)())fmd_scheme_notsup, /* sop_present */ 17525c6ff4bSstephh (int (*)())fmd_scheme_notsup, /* sop_replaced */ 17625c6ff4bSstephh (int (*)())fmd_scheme_notsup, /* sop_service_state */ 1777c478bd9Sstevel@tonic-gate (int (*)())fmd_scheme_notsup, /* sop_unusable */ 178d9638e54Smws (int (*)())fmd_scheme_notsup, /* sop_contains */ 179d9638e54Smws fmd_scheme_notranslate /* sop_translate */ 1807c478bd9Sstevel@tonic-gate }; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate static const fmd_scheme_ops_t _fmd_scheme_builtin_ops = { 1837c478bd9Sstevel@tonic-gate (int (*)())fmd_scheme_nop, /* sop_init */ 1847c478bd9Sstevel@tonic-gate (void (*)())fmd_scheme_nop, /* sop_fini */ 1857c478bd9Sstevel@tonic-gate fmd_scheme_fmd_nvl2str, /* sop_nvl2str */ 1867c478bd9Sstevel@tonic-gate (int (*)())fmd_scheme_nop, /* sop_expand */ 1877c478bd9Sstevel@tonic-gate fmd_scheme_fmd_present, /* sop_present */ 18825c6ff4bSstephh fmd_scheme_fmd_replaced, /* sop_replaced */ 18925c6ff4bSstephh fmd_scheme_fmd_service_state, /* sop_service_state */ 1907c478bd9Sstevel@tonic-gate fmd_scheme_fmd_unusable, /* sop_unusable */ 191d9638e54Smws (int (*)())fmd_scheme_notsup, /* sop_contains */ 192d9638e54Smws fmd_scheme_notranslate /* sop_translate */ 1937c478bd9Sstevel@tonic-gate }; 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate /* 1967c478bd9Sstevel@tonic-gate * Scheme ops descriptions. These names and offsets are used by the function 1977c478bd9Sstevel@tonic-gate * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t. 1987c478bd9Sstevel@tonic-gate */ 1997c478bd9Sstevel@tonic-gate static const fmd_scheme_opd_t _fmd_scheme_ops[] = { 2007c478bd9Sstevel@tonic-gate { "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) }, 2017c478bd9Sstevel@tonic-gate { "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) }, 2027c478bd9Sstevel@tonic-gate { "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) }, 2037c478bd9Sstevel@tonic-gate { "fmd_fmri_expand", offsetof(fmd_scheme_ops_t, sop_expand) }, 2047c478bd9Sstevel@tonic-gate { "fmd_fmri_present", offsetof(fmd_scheme_ops_t, sop_present) }, 20525c6ff4bSstephh { "fmd_fmri_replaced", offsetof(fmd_scheme_ops_t, sop_replaced) }, 20625c6ff4bSstephh { "fmd_fmri_service_state", offsetof(fmd_scheme_ops_t, 20725c6ff4bSstephh sop_service_state) }, 2087c478bd9Sstevel@tonic-gate { "fmd_fmri_unusable", offsetof(fmd_scheme_ops_t, sop_unusable) }, 2097c478bd9Sstevel@tonic-gate { "fmd_fmri_contains", offsetof(fmd_scheme_ops_t, sop_contains) }, 210d9638e54Smws { "fmd_fmri_translate", offsetof(fmd_scheme_ops_t, sop_translate) }, 2117c478bd9Sstevel@tonic-gate { NULL, 0 } 2127c478bd9Sstevel@tonic-gate }; 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate static fmd_scheme_t * 2157c478bd9Sstevel@tonic-gate fmd_scheme_create(const char *name) 2167c478bd9Sstevel@tonic-gate { 2177c478bd9Sstevel@tonic-gate fmd_scheme_t *sp = fmd_alloc(sizeof (fmd_scheme_t), FMD_SLEEP); 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&sp->sch_lock, NULL); 2207c478bd9Sstevel@tonic-gate (void) pthread_cond_init(&sp->sch_cv, NULL); 2217c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&sp->sch_opslock, NULL); 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate sp->sch_next = NULL; 2247c478bd9Sstevel@tonic-gate sp->sch_name = fmd_strdup(name, FMD_SLEEP); 2257c478bd9Sstevel@tonic-gate sp->sch_dlp = NULL; 2267c478bd9Sstevel@tonic-gate sp->sch_refs = 1; 2277c478bd9Sstevel@tonic-gate sp->sch_loaded = 0; 2287c478bd9Sstevel@tonic-gate sp->sch_ops = _fmd_scheme_default_ops; 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate return (sp); 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate static void 2347c478bd9Sstevel@tonic-gate fmd_scheme_destroy(fmd_scheme_t *sp) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&sp->sch_lock)); 2377c478bd9Sstevel@tonic-gate ASSERT(sp->sch_refs == 0); 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate if (sp->sch_dlp != NULL) { 2407c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_FMRI, "dlclose scheme %s", sp->sch_name)); 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate if (sp->sch_ops.sop_fini != NULL) 2437c478bd9Sstevel@tonic-gate sp->sch_ops.sop_fini(); 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate (void) dlclose(sp->sch_dlp); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate fmd_strfree(sp->sch_name); 2497c478bd9Sstevel@tonic-gate fmd_free(sp, sizeof (fmd_scheme_t)); 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate fmd_scheme_hash_t * 2537c478bd9Sstevel@tonic-gate fmd_scheme_hash_create(const char *rootdir, const char *dirpath) 2547c478bd9Sstevel@tonic-gate { 2557c478bd9Sstevel@tonic-gate fmd_scheme_hash_t *shp; 2567c478bd9Sstevel@tonic-gate char path[PATH_MAX]; 2577c478bd9Sstevel@tonic-gate fmd_scheme_t *sp; 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate shp = fmd_alloc(sizeof (fmd_scheme_hash_t), FMD_SLEEP); 2607c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/%s", rootdir, dirpath); 2617c478bd9Sstevel@tonic-gate shp->sch_dirpath = fmd_strdup(path, FMD_SLEEP); 2627c478bd9Sstevel@tonic-gate (void) pthread_rwlock_init(&shp->sch_rwlock, NULL); 2637c478bd9Sstevel@tonic-gate shp->sch_hashlen = fmd.d_str_buckets; 2647c478bd9Sstevel@tonic-gate shp->sch_hash = fmd_zalloc(sizeof (fmd_scheme_t *) * 2657c478bd9Sstevel@tonic-gate shp->sch_hashlen, FMD_SLEEP); 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate sp = fmd_scheme_create(FM_FMRI_SCHEME_FMD); 2687c478bd9Sstevel@tonic-gate sp->sch_ops = _fmd_scheme_builtin_ops; 2697c478bd9Sstevel@tonic-gate sp->sch_loaded = FMD_B_TRUE; 2707c478bd9Sstevel@tonic-gate shp->sch_hash[fmd_strhash(sp->sch_name) % shp->sch_hashlen] = sp; 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate return (shp); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate void 2767c478bd9Sstevel@tonic-gate fmd_scheme_hash_destroy(fmd_scheme_hash_t *shp) 2777c478bd9Sstevel@tonic-gate { 2787c478bd9Sstevel@tonic-gate fmd_scheme_t *sp, *np; 2797c478bd9Sstevel@tonic-gate uint_t i; 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate for (i = 0; i < shp->sch_hashlen; i++) { 2827c478bd9Sstevel@tonic-gate for (sp = shp->sch_hash[i]; sp != NULL; sp = np) { 2837c478bd9Sstevel@tonic-gate np = sp->sch_next; 2847c478bd9Sstevel@tonic-gate sp->sch_next = NULL; 2857c478bd9Sstevel@tonic-gate fmd_scheme_hash_release(shp, sp); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate } 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate fmd_free(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen); 2907c478bd9Sstevel@tonic-gate fmd_strfree(shp->sch_dirpath); 2917c478bd9Sstevel@tonic-gate fmd_free(shp, sizeof (fmd_scheme_hash_t)); 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate void 2957c478bd9Sstevel@tonic-gate fmd_scheme_hash_trygc(fmd_scheme_hash_t *shp) 2967c478bd9Sstevel@tonic-gate { 2977c478bd9Sstevel@tonic-gate fmd_scheme_t *sp, *np; 2987c478bd9Sstevel@tonic-gate uint_t i; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate if (shp == NULL || pthread_rwlock_trywrlock(&shp->sch_rwlock) != 0) 3017c478bd9Sstevel@tonic-gate return; /* failed to acquire lock: just skip garbage collect */ 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate for (i = 0; i < shp->sch_hashlen; i++) { 3047c478bd9Sstevel@tonic-gate for (sp = shp->sch_hash[i]; sp != NULL; sp = np) { 3057c478bd9Sstevel@tonic-gate np = sp->sch_next; 3067c478bd9Sstevel@tonic-gate sp->sch_next = NULL; 3077c478bd9Sstevel@tonic-gate fmd_scheme_hash_release(shp, sp); 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate bzero(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen); 3127c478bd9Sstevel@tonic-gate (void) pthread_rwlock_unlock(&shp->sch_rwlock); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate static int 3167c478bd9Sstevel@tonic-gate fmd_scheme_rtld_init(fmd_scheme_t *sp) 3177c478bd9Sstevel@tonic-gate { 3187c478bd9Sstevel@tonic-gate const fmd_scheme_opd_t *opd; 3197c478bd9Sstevel@tonic-gate void *p; 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) { 3227c478bd9Sstevel@tonic-gate if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL) 3237c478bd9Sstevel@tonic-gate *(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p; 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate return (0); 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate fmd_scheme_t * 3307c478bd9Sstevel@tonic-gate fmd_scheme_hash_xlookup(fmd_scheme_hash_t *shp, const char *name, uint_t h) 3317c478bd9Sstevel@tonic-gate { 3327c478bd9Sstevel@tonic-gate fmd_scheme_t *sp; 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&shp->sch_rwlock)); 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate for (sp = shp->sch_hash[h]; sp != NULL; sp = sp->sch_next) { 3377c478bd9Sstevel@tonic-gate if (strcmp(sp->sch_name, name) == 0) 3387c478bd9Sstevel@tonic-gate break; 3397c478bd9Sstevel@tonic-gate } 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate return (sp); 3427c478bd9Sstevel@tonic-gate } 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate /* 3457c478bd9Sstevel@tonic-gate * Lookup a scheme module by name and return with a reference placed on it. We 3467c478bd9Sstevel@tonic-gate * use the scheme hash to cache "negative" entries (e.g. missing modules) as 3477c478bd9Sstevel@tonic-gate * well so this function always returns successfully with a non-NULL scheme. 3487c478bd9Sstevel@tonic-gate * The caller is responsible for applying fmd_scheme_hash_release() afterward. 3497c478bd9Sstevel@tonic-gate */ 3507c478bd9Sstevel@tonic-gate fmd_scheme_t * 3517c478bd9Sstevel@tonic-gate fmd_scheme_hash_lookup(fmd_scheme_hash_t *shp, const char *name) 3527c478bd9Sstevel@tonic-gate { 3537c478bd9Sstevel@tonic-gate fmd_scheme_t *sp, *nsp = NULL; 3547c478bd9Sstevel@tonic-gate uint_t h; 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate /* 3577c478bd9Sstevel@tonic-gate * Grab the hash lock as reader and look for the appropriate scheme. 3587c478bd9Sstevel@tonic-gate * If the scheme isn't yet loaded, allocate a new scheme and grab the 3597c478bd9Sstevel@tonic-gate * hash lock as writer to insert it (after checking again for it). 3607c478bd9Sstevel@tonic-gate */ 3617c478bd9Sstevel@tonic-gate (void) pthread_rwlock_rdlock(&shp->sch_rwlock); 3627c478bd9Sstevel@tonic-gate h = fmd_strhash(name) % shp->sch_hashlen; 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) { 3657c478bd9Sstevel@tonic-gate (void) pthread_rwlock_unlock(&shp->sch_rwlock); 3667c478bd9Sstevel@tonic-gate nsp = fmd_scheme_create(name); 3677c478bd9Sstevel@tonic-gate (void) pthread_rwlock_wrlock(&shp->sch_rwlock); 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) { 3707c478bd9Sstevel@tonic-gate nsp->sch_next = shp->sch_hash[h]; 3717c478bd9Sstevel@tonic-gate shp->sch_hash[h] = sp = nsp; 3727c478bd9Sstevel@tonic-gate } else { 3737c478bd9Sstevel@tonic-gate fmd_scheme_hash_release(shp, nsp); 3747c478bd9Sstevel@tonic-gate nsp = NULL; 3757c478bd9Sstevel@tonic-gate } 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* 3797c478bd9Sstevel@tonic-gate * Grab the scheme lock so it can't disappear and then drop the hash 3807c478bd9Sstevel@tonic-gate * lock so that other lookups in the scheme hash can proceed. 3817c478bd9Sstevel@tonic-gate */ 3827c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&sp->sch_lock); 3837c478bd9Sstevel@tonic-gate (void) pthread_rwlock_unlock(&shp->sch_rwlock); 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate /* 3867c478bd9Sstevel@tonic-gate * If we created the scheme, compute its path and try to load it. If 3877c478bd9Sstevel@tonic-gate * we found an existing scheme, wait until its loaded bit is set. Once 3887c478bd9Sstevel@tonic-gate * we're done with either operation, increment sch_refs and return. 3897c478bd9Sstevel@tonic-gate */ 3907c478bd9Sstevel@tonic-gate if (nsp != NULL) { 3917c478bd9Sstevel@tonic-gate char path[PATH_MAX]; 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), 3947c478bd9Sstevel@tonic-gate "%s/%s.so", shp->sch_dirpath, sp->sch_name); 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate TRACE((FMD_DBG_FMRI, "dlopen scheme %s", sp->sch_name)); 3977c478bd9Sstevel@tonic-gate sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW); 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate if (sp->sch_dlp == NULL) { 4007c478bd9Sstevel@tonic-gate fmd_error(EFMD_FMRI_SCHEME, 4017c478bd9Sstevel@tonic-gate "failed to load fmri scheme %s: %s\n", path, 4027c478bd9Sstevel@tonic-gate dlerror()); 4037c478bd9Sstevel@tonic-gate } else if (fmd_scheme_rtld_init(sp) != 0 || 4047c478bd9Sstevel@tonic-gate sp->sch_ops.sop_init() != 0) { 4057c478bd9Sstevel@tonic-gate fmd_error(EFMD_FMRI_SCHEME, 4067c478bd9Sstevel@tonic-gate "failed to initialize fmri scheme %s", path); 4077c478bd9Sstevel@tonic-gate (void) dlclose(sp->sch_dlp); 4087c478bd9Sstevel@tonic-gate sp->sch_dlp = NULL; 4097c478bd9Sstevel@tonic-gate sp->sch_ops = _fmd_scheme_default_ops; 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate sp->sch_loaded = FMD_B_TRUE; /* set regardless of success */ 4137c478bd9Sstevel@tonic-gate sp->sch_refs++; 4147c478bd9Sstevel@tonic-gate ASSERT(sp->sch_refs != 0); 415d9638e54Smws 4167c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&sp->sch_cv); 417d9638e54Smws (void) pthread_mutex_unlock(&sp->sch_lock); 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate } else { 4207c478bd9Sstevel@tonic-gate while (!sp->sch_loaded) 4217c478bd9Sstevel@tonic-gate (void) pthread_cond_wait(&sp->sch_cv, &sp->sch_lock); 4227c478bd9Sstevel@tonic-gate 4237c478bd9Sstevel@tonic-gate sp->sch_refs++; 4247c478bd9Sstevel@tonic-gate ASSERT(sp->sch_refs != 0); 4257c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&sp->sch_lock); 4267c478bd9Sstevel@tonic-gate } 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate return (sp); 4297c478bd9Sstevel@tonic-gate } 4307c478bd9Sstevel@tonic-gate 4317c478bd9Sstevel@tonic-gate /* 4327c478bd9Sstevel@tonic-gate * Release the hold on a scheme obtained using fmd_scheme_hash_lookup(). 4337c478bd9Sstevel@tonic-gate * We take 'shp' for symmetry and in case we need to use it in future work. 4347c478bd9Sstevel@tonic-gate */ 4357c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4367c478bd9Sstevel@tonic-gate void 4377c478bd9Sstevel@tonic-gate fmd_scheme_hash_release(fmd_scheme_hash_t *shp, fmd_scheme_t *sp) 4387c478bd9Sstevel@tonic-gate { 4397c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&sp->sch_lock); 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate ASSERT(sp->sch_refs != 0); 4427c478bd9Sstevel@tonic-gate if (--sp->sch_refs == 0) 4437c478bd9Sstevel@tonic-gate fmd_scheme_destroy(sp); 4447c478bd9Sstevel@tonic-gate else 4457c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&sp->sch_lock); 4467c478bd9Sstevel@tonic-gate } 447