4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
31 #include <fmd_alloc.h>
32 #include <fmd_error.h>
34 #include <fmd_string.h>
35 #include <fmd_scheme.h>
37 #include <fmd_module.h>
42 * The fmd resource scheme, used for fmd modules, must be implemented here for
43 * the benefit of fmd-self-diagnosis and also in schemes/fmd for fmdump(1M).
46 fmd_scheme_fmd_nvl2str(nvlist_t
*nvl
, char *buf
, size_t buflen
)
50 if (nvlist_lookup_string(nvl
, FM_FMRI_FMD_NAME
, &name
) != 0)
51 return (fmd_fmri_set_errno(EINVAL
));
53 return (snprintf(buf
, buflen
,
54 "%s:///module/%s", FM_FMRI_SCHEME_FMD
, name
));
58 fmd_scheme_fmd_present(nvlist_t
*nvl
)
64 if (nvlist_lookup_string(nvl
, FM_FMRI_FMD_NAME
, &name
) != 0 ||
65 nvlist_lookup_string(nvl
, FM_FMRI_FMD_VERSION
, &version
) != 0)
66 return (fmd_fmri_set_errno(EINVAL
));
71 if ((mp
= fmd_modhash_lookup(fmd
.d_mod_hash
, name
)) != NULL
) {
72 rv
= mp
->mod_vers
!= NULL
&&
73 strcmp(mp
->mod_vers
, version
) == 0;
81 fmd_scheme_fmd_replaced(nvlist_t
*nvl
)
87 if (nvlist_lookup_string(nvl
, FM_FMRI_FMD_NAME
, &name
) != 0 ||
88 nvlist_lookup_string(nvl
, FM_FMRI_FMD_VERSION
, &version
) != 0)
89 return (fmd_fmri_set_errno(EINVAL
));
92 return (FMD_OBJ_STATE_UNKNOWN
);
94 if ((mp
= fmd_modhash_lookup(fmd
.d_mod_hash
, name
)) != NULL
) {
95 rv
= mp
->mod_vers
!= NULL
&&
96 strcmp(mp
->mod_vers
, version
) == 0;
100 return (rv
? FMD_OBJ_STATE_STILL_PRESENT
: FMD_OBJ_STATE_REPLACED
);
104 fmd_scheme_fmd_service_state(nvlist_t
*nvl
)
110 if (nvlist_lookup_string(nvl
, FM_FMRI_FMD_NAME
, &name
) != 0)
111 return (fmd_fmri_set_errno(EINVAL
));
114 return (FMD_SERVICE_STATE_UNKNOWN
);
116 if ((mp
= fmd_modhash_lookup(fmd
.d_mod_hash
, name
)) != NULL
) {
117 rv
= mp
->mod_error
!= 0;
121 return (rv
? FMD_SERVICE_STATE_UNUSABLE
: FMD_SERVICE_STATE_OK
);
125 fmd_scheme_fmd_unusable(nvlist_t
*nvl
)
131 if (nvlist_lookup_string(nvl
, FM_FMRI_FMD_NAME
, &name
) != 0)
132 return (fmd_fmri_set_errno(EINVAL
));
137 if ((mp
= fmd_modhash_lookup(fmd
.d_mod_hash
, name
)) != NULL
) {
138 rv
= mp
->mod_error
!= 0;
147 fmd_scheme_notranslate(nvlist_t
*fmri
, nvlist_t
*auth
)
149 (void) nvlist_xdup(fmri
, &fmri
, &fmd
.d_nva
);
154 fmd_scheme_notsup(void)
156 return (fmd_set_errno(EFMD_FMRI_NOTSUP
));
166 * Default values for the scheme ops. If a scheme function is not defined in
167 * the module, then this operation is implemented using the default function.
169 static const fmd_scheme_ops_t _fmd_scheme_default_ops
= {
170 (int (*)())fmd_scheme_nop
, /* sop_init */
171 (void (*)())fmd_scheme_nop
, /* sop_fini */
172 (ssize_t (*)())fmd_scheme_notsup
, /* sop_nvl2str */
173 (int (*)())fmd_scheme_nop
, /* sop_expand */
174 (int (*)())fmd_scheme_notsup
, /* sop_present */
175 (int (*)())fmd_scheme_notsup
, /* sop_replaced */
176 (int (*)())fmd_scheme_notsup
, /* sop_service_state */
177 (int (*)())fmd_scheme_notsup
, /* sop_unusable */
178 (int (*)())fmd_scheme_notsup
, /* sop_contains */
179 fmd_scheme_notranslate
/* sop_translate */
182 static const fmd_scheme_ops_t _fmd_scheme_builtin_ops
= {
183 (int (*)())fmd_scheme_nop
, /* sop_init */
184 (void (*)())fmd_scheme_nop
, /* sop_fini */
185 fmd_scheme_fmd_nvl2str
, /* sop_nvl2str */
186 (int (*)())fmd_scheme_nop
, /* sop_expand */
187 fmd_scheme_fmd_present
, /* sop_present */
188 fmd_scheme_fmd_replaced
, /* sop_replaced */
189 fmd_scheme_fmd_service_state
, /* sop_service_state */
190 fmd_scheme_fmd_unusable
, /* sop_unusable */
191 (int (*)())fmd_scheme_notsup
, /* sop_contains */
192 fmd_scheme_notranslate
/* sop_translate */
196 * Scheme ops descriptions. These names and offsets are used by the function
197 * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t.
199 static const fmd_scheme_opd_t _fmd_scheme_ops
[] = {
200 { "fmd_fmri_init", offsetof(fmd_scheme_ops_t
, sop_init
) },
201 { "fmd_fmri_fini", offsetof(fmd_scheme_ops_t
, sop_fini
) },
202 { "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t
, sop_nvl2str
) },
203 { "fmd_fmri_expand", offsetof(fmd_scheme_ops_t
, sop_expand
) },
204 { "fmd_fmri_present", offsetof(fmd_scheme_ops_t
, sop_present
) },
205 { "fmd_fmri_replaced", offsetof(fmd_scheme_ops_t
, sop_replaced
) },
206 { "fmd_fmri_service_state", offsetof(fmd_scheme_ops_t
,
207 sop_service_state
) },
208 { "fmd_fmri_unusable", offsetof(fmd_scheme_ops_t
, sop_unusable
) },
209 { "fmd_fmri_contains", offsetof(fmd_scheme_ops_t
, sop_contains
) },
210 { "fmd_fmri_translate", offsetof(fmd_scheme_ops_t
, sop_translate
) },
214 static fmd_scheme_t
*
215 fmd_scheme_create(const char *name
)
217 fmd_scheme_t
*sp
= fmd_alloc(sizeof (fmd_scheme_t
), FMD_SLEEP
);
219 (void) pthread_mutex_init(&sp
->sch_lock
, NULL
);
220 (void) pthread_cond_init(&sp
->sch_cv
, NULL
);
221 (void) pthread_mutex_init(&sp
->sch_opslock
, NULL
);
224 sp
->sch_name
= fmd_strdup(name
, FMD_SLEEP
);
228 sp
->sch_ops
= _fmd_scheme_default_ops
;
234 fmd_scheme_destroy(fmd_scheme_t
*sp
)
236 ASSERT(MUTEX_HELD(&sp
->sch_lock
));
237 ASSERT(sp
->sch_refs
== 0);
239 if (sp
->sch_dlp
!= NULL
) {
240 TRACE((FMD_DBG_FMRI
, "dlclose scheme %s", sp
->sch_name
));
242 if (sp
->sch_ops
.sop_fini
!= NULL
)
243 sp
->sch_ops
.sop_fini();
245 (void) dlclose(sp
->sch_dlp
);
248 fmd_strfree(sp
->sch_name
);
249 fmd_free(sp
, sizeof (fmd_scheme_t
));
253 fmd_scheme_hash_create(const char *rootdir
, const char *dirpath
)
255 fmd_scheme_hash_t
*shp
;
259 shp
= fmd_alloc(sizeof (fmd_scheme_hash_t
), FMD_SLEEP
);
260 (void) snprintf(path
, sizeof (path
), "%s/%s", rootdir
, dirpath
);
261 shp
->sch_dirpath
= fmd_strdup(path
, FMD_SLEEP
);
262 (void) pthread_rwlock_init(&shp
->sch_rwlock
, NULL
);
263 shp
->sch_hashlen
= fmd
.d_str_buckets
;
264 shp
->sch_hash
= fmd_zalloc(sizeof (fmd_scheme_t
*) *
265 shp
->sch_hashlen
, FMD_SLEEP
);
267 sp
= fmd_scheme_create(FM_FMRI_SCHEME_FMD
);
268 sp
->sch_ops
= _fmd_scheme_builtin_ops
;
269 sp
->sch_loaded
= FMD_B_TRUE
;
270 shp
->sch_hash
[fmd_strhash(sp
->sch_name
) % shp
->sch_hashlen
] = sp
;
276 fmd_scheme_hash_destroy(fmd_scheme_hash_t
*shp
)
278 fmd_scheme_t
*sp
, *np
;
281 for (i
= 0; i
< shp
->sch_hashlen
; i
++) {
282 for (sp
= shp
->sch_hash
[i
]; sp
!= NULL
; sp
= np
) {
285 fmd_scheme_hash_release(shp
, sp
);
289 fmd_free(shp
->sch_hash
, sizeof (fmd_scheme_t
*) * shp
->sch_hashlen
);
290 fmd_strfree(shp
->sch_dirpath
);
291 fmd_free(shp
, sizeof (fmd_scheme_hash_t
));
295 fmd_scheme_hash_trygc(fmd_scheme_hash_t
*shp
)
297 fmd_scheme_t
*sp
, *np
;
300 if (shp
== NULL
|| pthread_rwlock_trywrlock(&shp
->sch_rwlock
) != 0)
301 return; /* failed to acquire lock: just skip garbage collect */
303 for (i
= 0; i
< shp
->sch_hashlen
; i
++) {
304 for (sp
= shp
->sch_hash
[i
]; sp
!= NULL
; sp
= np
) {
307 fmd_scheme_hash_release(shp
, sp
);
311 bzero(shp
->sch_hash
, sizeof (fmd_scheme_t
*) * shp
->sch_hashlen
);
312 (void) pthread_rwlock_unlock(&shp
->sch_rwlock
);
316 fmd_scheme_rtld_init(fmd_scheme_t
*sp
)
318 const fmd_scheme_opd_t
*opd
;
321 for (opd
= _fmd_scheme_ops
; opd
->opd_name
!= NULL
; opd
++) {
322 if ((p
= dlsym(sp
->sch_dlp
, opd
->opd_name
)) != NULL
)
323 *(void **)((uintptr_t)&sp
->sch_ops
+ opd
->opd_off
) = p
;
330 fmd_scheme_hash_xlookup(fmd_scheme_hash_t
*shp
, const char *name
, uint_t h
)
334 ASSERT(RW_LOCK_HELD(&shp
->sch_rwlock
));
336 for (sp
= shp
->sch_hash
[h
]; sp
!= NULL
; sp
= sp
->sch_next
) {
337 if (strcmp(sp
->sch_name
, name
) == 0)
345 * Lookup a scheme module by name and return with a reference placed on it. We
346 * use the scheme hash to cache "negative" entries (e.g. missing modules) as
347 * well so this function always returns successfully with a non-NULL scheme.
348 * The caller is responsible for applying fmd_scheme_hash_release() afterward.
351 fmd_scheme_hash_lookup(fmd_scheme_hash_t
*shp
, const char *name
)
353 fmd_scheme_t
*sp
, *nsp
= NULL
;
357 * Grab the hash lock as reader and look for the appropriate scheme.
358 * If the scheme isn't yet loaded, allocate a new scheme and grab the
359 * hash lock as writer to insert it (after checking again for it).
361 (void) pthread_rwlock_rdlock(&shp
->sch_rwlock
);
362 h
= fmd_strhash(name
) % shp
->sch_hashlen
;
364 if ((sp
= fmd_scheme_hash_xlookup(shp
, name
, h
)) == NULL
) {
365 (void) pthread_rwlock_unlock(&shp
->sch_rwlock
);
366 nsp
= fmd_scheme_create(name
);
367 (void) pthread_rwlock_wrlock(&shp
->sch_rwlock
);
369 if ((sp
= fmd_scheme_hash_xlookup(shp
, name
, h
)) == NULL
) {
370 nsp
->sch_next
= shp
->sch_hash
[h
];
371 shp
->sch_hash
[h
] = sp
= nsp
;
373 fmd_scheme_hash_release(shp
, nsp
);
379 * Grab the scheme lock so it can't disappear and then drop the hash
380 * lock so that other lookups in the scheme hash can proceed.
382 (void) pthread_mutex_lock(&sp
->sch_lock
);
383 (void) pthread_rwlock_unlock(&shp
->sch_rwlock
);
386 * If we created the scheme, compute its path and try to load it. If
387 * we found an existing scheme, wait until its loaded bit is set. Once
388 * we're done with either operation, increment sch_refs and return.
393 (void) snprintf(path
, sizeof (path
),
394 "%s/%s.so", shp
->sch_dirpath
, sp
->sch_name
);
396 TRACE((FMD_DBG_FMRI
, "dlopen scheme %s", sp
->sch_name
));
397 sp
->sch_dlp
= dlopen(path
, RTLD_LOCAL
| RTLD_NOW
);
399 if (sp
->sch_dlp
== NULL
) {
400 fmd_error(EFMD_FMRI_SCHEME
,
401 "failed to load fmri scheme %s: %s\n", path
,
403 } else if (fmd_scheme_rtld_init(sp
) != 0 ||
404 sp
->sch_ops
.sop_init() != 0) {
405 fmd_error(EFMD_FMRI_SCHEME
,
406 "failed to initialize fmri scheme %s", path
);
407 (void) dlclose(sp
->sch_dlp
);
409 sp
->sch_ops
= _fmd_scheme_default_ops
;
412 sp
->sch_loaded
= FMD_B_TRUE
; /* set regardless of success */
414 ASSERT(sp
->sch_refs
!= 0);
416 (void) pthread_cond_broadcast(&sp
->sch_cv
);
417 (void) pthread_mutex_unlock(&sp
->sch_lock
);
420 while (!sp
->sch_loaded
)
421 (void) pthread_cond_wait(&sp
->sch_cv
, &sp
->sch_lock
);
424 ASSERT(sp
->sch_refs
!= 0);
425 (void) pthread_mutex_unlock(&sp
->sch_lock
);
432 * Release the hold on a scheme obtained using fmd_scheme_hash_lookup().
433 * We take 'shp' for symmetry and in case we need to use it in future work.
437 fmd_scheme_hash_release(fmd_scheme_hash_t
*shp
, fmd_scheme_t
*sp
)
439 (void) pthread_mutex_lock(&sp
->sch_lock
);
441 ASSERT(sp
->sch_refs
!= 0);
442 if (--sp
->sch_refs
== 0)
443 fmd_scheme_destroy(sp
);
445 (void) pthread_mutex_unlock(&sp
->sch_lock
);