dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fm / fmd / common / fmd_scheme.c
blob4ddad713a615d6fe169f507643fb1e38eaeb5955
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <limits.h>
27 #include <stddef.h>
28 #include <unistd.h>
29 #include <dlfcn.h>
31 #include <fmd_alloc.h>
32 #include <fmd_error.h>
33 #include <fmd_subr.h>
34 #include <fmd_string.h>
35 #include <fmd_scheme.h>
36 #include <fmd_fmri.h>
37 #include <fmd_module.h>
39 #include <fmd.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).
45 ssize_t
46 fmd_scheme_fmd_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
48 char *name;
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));
57 static int
58 fmd_scheme_fmd_present(nvlist_t *nvl)
60 char *name, *version;
61 fmd_module_t *mp;
62 int rv = 1;
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));
68 if (!fmd.d_loaded)
69 return (1);
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;
74 fmd_module_rele(mp);
77 return (rv);
80 static int
81 fmd_scheme_fmd_replaced(nvlist_t *nvl)
83 char *name, *version;
84 fmd_module_t *mp;
85 int rv = 1;
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));
91 if (!fmd.d_loaded)
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;
97 fmd_module_rele(mp);
100 return (rv ? FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED);
103 static int
104 fmd_scheme_fmd_service_state(nvlist_t *nvl)
106 char *name;
107 fmd_module_t *mp;
108 int rv = 1;
110 if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
111 return (fmd_fmri_set_errno(EINVAL));
113 if (!fmd.d_loaded)
114 return (FMD_SERVICE_STATE_UNKNOWN);
116 if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
117 rv = mp->mod_error != 0;
118 fmd_module_rele(mp);
121 return (rv ? FMD_SERVICE_STATE_UNUSABLE : FMD_SERVICE_STATE_OK);
124 static int
125 fmd_scheme_fmd_unusable(nvlist_t *nvl)
127 char *name;
128 fmd_module_t *mp;
129 int rv = 1;
131 if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0)
132 return (fmd_fmri_set_errno(EINVAL));
134 if (!fmd.d_loaded)
135 return (0);
137 if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) {
138 rv = mp->mod_error != 0;
139 fmd_module_rele(mp);
142 return (rv);
145 /*ARGSUSED*/
146 static nvlist_t *
147 fmd_scheme_notranslate(nvlist_t *fmri, nvlist_t *auth)
149 (void) nvlist_xdup(fmri, &fmri, &fmd.d_nva);
150 return (fmri);
153 static long
154 fmd_scheme_notsup(void)
156 return (fmd_set_errno(EFMD_FMRI_NOTSUP));
159 static int
160 fmd_scheme_nop(void)
162 return (0);
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) },
211 { NULL, 0 }
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);
223 sp->sch_next = NULL;
224 sp->sch_name = fmd_strdup(name, FMD_SLEEP);
225 sp->sch_dlp = NULL;
226 sp->sch_refs = 1;
227 sp->sch_loaded = 0;
228 sp->sch_ops = _fmd_scheme_default_ops;
230 return (sp);
233 static void
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));
252 fmd_scheme_hash_t *
253 fmd_scheme_hash_create(const char *rootdir, const char *dirpath)
255 fmd_scheme_hash_t *shp;
256 char path[PATH_MAX];
257 fmd_scheme_t *sp;
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;
272 return (shp);
275 void
276 fmd_scheme_hash_destroy(fmd_scheme_hash_t *shp)
278 fmd_scheme_t *sp, *np;
279 uint_t i;
281 for (i = 0; i < shp->sch_hashlen; i++) {
282 for (sp = shp->sch_hash[i]; sp != NULL; sp = np) {
283 np = sp->sch_next;
284 sp->sch_next = NULL;
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));
294 void
295 fmd_scheme_hash_trygc(fmd_scheme_hash_t *shp)
297 fmd_scheme_t *sp, *np;
298 uint_t i;
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) {
305 np = sp->sch_next;
306 sp->sch_next = NULL;
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);
315 static int
316 fmd_scheme_rtld_init(fmd_scheme_t *sp)
318 const fmd_scheme_opd_t *opd;
319 void *p;
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;
326 return (0);
329 fmd_scheme_t *
330 fmd_scheme_hash_xlookup(fmd_scheme_hash_t *shp, const char *name, uint_t h)
332 fmd_scheme_t *sp;
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)
338 break;
341 return (sp);
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.
350 fmd_scheme_t *
351 fmd_scheme_hash_lookup(fmd_scheme_hash_t *shp, const char *name)
353 fmd_scheme_t *sp, *nsp = NULL;
354 uint_t h;
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;
372 } else {
373 fmd_scheme_hash_release(shp, nsp);
374 nsp = NULL;
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.
390 if (nsp != NULL) {
391 char path[PATH_MAX];
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,
402 dlerror());
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);
408 sp->sch_dlp = NULL;
409 sp->sch_ops = _fmd_scheme_default_ops;
412 sp->sch_loaded = FMD_B_TRUE; /* set regardless of success */
413 sp->sch_refs++;
414 ASSERT(sp->sch_refs != 0);
416 (void) pthread_cond_broadcast(&sp->sch_cv);
417 (void) pthread_mutex_unlock(&sp->sch_lock);
419 } else {
420 while (!sp->sch_loaded)
421 (void) pthread_cond_wait(&sp->sch_cv, &sp->sch_lock);
423 sp->sch_refs++;
424 ASSERT(sp->sch_refs != 0);
425 (void) pthread_mutex_unlock(&sp->sch_lock);
428 return (sp);
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.
435 /*ARGSUSED*/
436 void
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);
444 else
445 (void) pthread_mutex_unlock(&sp->sch_lock);