xref: /titanic_50/usr/src/cmd/fm/fmd/common/fmd_asru.c (revision 711890bc9379ceea66272dc8d4981812224ea86e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/fm/protocol.h>
31 #include <uuid/uuid.h>
32 
33 #include <dirent.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <alloca.h>
37 
38 #include <fmd_alloc.h>
39 #include <fmd_string.h>
40 #include <fmd_error.h>
41 #include <fmd_subr.h>
42 #include <fmd_protocol.h>
43 #include <fmd_event.h>
44 #include <fmd_conf.h>
45 #include <fmd_fmri.h>
46 #include <fmd_dispq.h>
47 #include <fmd_case.h>
48 #include <fmd_module.h>
49 #include <fmd_asru.h>
50 
51 #include <fmd.h>
52 
53 static const char *const _fmd_asru_events[] = {
54 	FMD_RSRC_CLASS "asru.ok",		/* UNUSABLE=0 FAULTED=0 */
55 	FMD_RSRC_CLASS "asru.degraded",		/* UNUSABLE=0 FAULTED=1 */
56 	FMD_RSRC_CLASS "asru.unknown",		/* UNUSABLE=1 FAULTED=0 */
57 	FMD_RSRC_CLASS "asru.faulted"		/* UNUSABLE=1 FAULTED=1 */
58 };
59 
60 static const char *const _fmd_asru_snames[] = {
61 	"uf", "uF", "Uf", "UF"			/* same order as above */
62 };
63 
64 static fmd_asru_t *
65 fmd_asru_create(fmd_asru_hash_t *ahp, const char *uuid,
66     const char *name, nvlist_t *fmri)
67 {
68 	fmd_asru_t *ap = fmd_alloc(sizeof (fmd_asru_t), FMD_SLEEP);
69 	char *s;
70 
71 	(void) pthread_mutex_init(&ap->asru_lock, NULL);
72 	(void) pthread_cond_init(&ap->asru_cv, NULL);
73 
74 	ap->asru_next = NULL;
75 	ap->asru_name = fmd_strdup(name, FMD_SLEEP);
76 	(void) nvlist_xdup(fmri, &ap->asru_fmri, &fmd.d_nva);
77 	ap->asru_root = fmd_strdup(ahp->ah_dirpath, FMD_SLEEP);
78 	ap->asru_uuid = fmd_strdup(uuid, FMD_SLEEP);
79 	ap->asru_uuidlen = ap->asru_uuid ? strlen(ap->asru_uuid) : 0;
80 	ap->asru_log = NULL;
81 	ap->asru_refs = 1;
82 	ap->asru_flags = 0;
83 	ap->asru_case = NULL;
84 	ap->asru_event = NULL;
85 
86 	if (nvlist_lookup_string(ap->asru_fmri, FM_FMRI_SCHEME, &s) == 0 &&
87 	    strcmp(s, FM_FMRI_SCHEME_FMD) == 0)
88 		ap->asru_flags |= FMD_ASRU_INTERNAL;
89 
90 	return (ap);
91 }
92 
93 static void
94 fmd_asru_destroy(fmd_asru_t *ap)
95 {
96 	ASSERT(MUTEX_HELD(&ap->asru_lock));
97 	ASSERT(ap->asru_refs == 0);
98 
99 	if (ap->asru_log != NULL)
100 		fmd_log_rele(ap->asru_log);
101 
102 	if (ap->asru_case != NULL)
103 		fmd_case_rele(ap->asru_case);
104 
105 	nvlist_free(ap->asru_event);
106 	fmd_strfree(ap->asru_name);
107 	nvlist_free(ap->asru_fmri);
108 	fmd_strfree(ap->asru_root);
109 	fmd_free(ap->asru_uuid, ap->asru_uuidlen + 1);
110 	fmd_free(ap, sizeof (fmd_asru_t));
111 }
112 
113 static void
114 fmd_asru_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_t *ap)
115 {
116 	uint_t h = fmd_strhash(ap->asru_name) % ahp->ah_hashlen;
117 
118 	ASSERT(RW_WRITE_HELD(&ahp->ah_lock));
119 	ap->asru_next = ahp->ah_hash[h];
120 	ahp->ah_hash[h] = ap;
121 	ahp->ah_count++;
122 }
123 
124 static fmd_asru_t *
125 fmd_asru_hold(fmd_asru_t *ap)
126 {
127 	(void) pthread_mutex_lock(&ap->asru_lock);
128 	ap->asru_refs++;
129 	ASSERT(ap->asru_refs != 0);
130 	(void) pthread_mutex_unlock(&ap->asru_lock);
131 	return (ap);
132 }
133 
134 /*
135  * Lookup an asru in the hash by name and place a hold on it.  If the asru is
136  * not found, no entry is created and NULL is returned.  This internal function
137  * is for callers who have the ah_lock held and is used by lookup_name below.
138  */
139 fmd_asru_t *
140 fmd_asru_hash_lookup(fmd_asru_hash_t *ahp, const char *name)
141 {
142 	fmd_asru_t *ap;
143 	uint_t h;
144 
145 	ASSERT(RW_LOCK_HELD(&ahp->ah_lock));
146 	h = fmd_strhash(name) % ahp->ah_hashlen;
147 
148 	for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) {
149 		if (strcmp(ap->asru_name, name) == 0)
150 			break;
151 	}
152 
153 	if (ap != NULL)
154 		(void) fmd_asru_hold(ap);
155 	else
156 		(void) fmd_set_errno(EFMD_ASRU_NOENT);
157 
158 	return (ap);
159 }
160 
161 static void
162 fmd_asru_hash_recreate(fmd_log_t *lp, fmd_event_t *ep, fmd_asru_hash_t *ahp)
163 {
164 	nvlist_t *nvl = FMD_EVENT_NVL(ep);
165 	char *case_uuid = NULL, *case_code = NULL;
166 	char *name = NULL;
167 	ssize_t namelen;
168 
169 	nvlist_t *fmri, *flt, *flt_copy;
170 	boolean_t f, u, m;
171 	fmd_asru_t *ap;
172 	int ps, us;
173 
174 	/*
175 	 * Extract the resource FMRI and most recent values of 'faulty' and
176 	 * 'unusable' from the event log.  If the event is malformed, return.
177 	 */
178 	if (nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &fmri) != 0 ||
179 	    nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_FAULTY, &f) != 0 ||
180 	    nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_UNUSABLE, &u) != 0) {
181 		fmd_error(EFMD_ASRU_EVENT, "failed to reload asru %s: "
182 		    "invalid event log record\n", lp->log_name);
183 		ahp->ah_error = EFMD_ASRU_EVENT;
184 		return;
185 	}
186 
187 	/*
188 	 * Check to see if the resource is still present in the system.  If
189 	 * so, then update the value of the unusable bit based on the current
190 	 * system configuration.  If not, then either keep the entry in our
191 	 * cache if it is recent, or return and discard it if it is too old.
192 	 */
193 	if ((ps = fmd_fmri_present(fmri)) == -1) {
194 		fmd_error(EFMD_ASRU_FMRI, "failed to locate %s", lp->log_name);
195 		ahp->ah_error = EFMD_ASRU_FMRI;
196 		return;
197 	}
198 
199 	if (ps) {
200 		if ((us = fmd_fmri_unusable(fmri)) == -1) {
201 			fmd_error(EFMD_ASRU_FMRI, "failed to update "
202 			    "status of asru %s", lp->log_name);
203 			u = FMD_B_FALSE;
204 		} else
205 			u = us != 0;
206 
207 	} else if ((hrtime_t)(lp->log_stat.st_atime -
208 	    lp->log_stat.st_mtime) * NANOSEC < ahp->ah_lifetime) {
209 		u = FMD_B_TRUE;	/* not present; set unusable */
210 	} else
211 		return;		/* too old; discard this log */
212 
213 	/*
214 	 * In order to insert the ASRU into our hash, convert the FMRI from
215 	 * nvlist form into a string form and assign this name to the ASRU.
216 	 */
217 	if ((namelen = fmd_fmri_nvl2str(fmri, NULL, 0)) == -1 ||
218 	    (name = fmd_alloc(namelen + 1, FMD_NOSLEEP)) == NULL ||
219 	    fmd_fmri_nvl2str(fmri, name, namelen + 1) == -1) {
220 		fmd_error(EFMD_ASRU_FMRI,
221 		    "failed to reload asru %s", lp->log_name);
222 		if (name != NULL)
223 			fmd_free(name, namelen + 1);
224 		ahp->ah_error = EFMD_ASRU_FMRI;
225 		return;
226 	}
227 
228 	/*
229 	 * Look to see if the ASRU already exists in the hash: if it does and
230 	 * the existing ASRU entry is unusable but the duplicate is not, then
231 	 * delete the existing entry and continue on using the new entry; if
232 	 * the new entry is no "better", return an error and ignore it.
233 	 */
234 	if ((ap = fmd_asru_hash_lookup(ahp, name)) != NULL) {
235 		if (!u && (ap->asru_flags & FMD_ASRU_UNUSABLE)) {
236 			(void) fmd_asru_hash_delete_name(ahp, name);
237 			fmd_asru_hash_release(ahp, ap);
238 		} else {
239 			fmd_error(EFMD_ASRU_DUP, "removing duplicate asru "
240 			    "log %s for %s\n", lp->log_name, name);
241 			fmd_free(name, namelen + 1);
242 			fmd_asru_hash_release(ahp, ap);
243 			ahp->ah_error = EFMD_ASRU_DUP;
244 			return;
245 		}
246 	}
247 
248 	ap = fmd_asru_create(ahp, fmd_strbasename(lp->log_name), name, fmri);
249 	fmd_free(name, namelen + 1);
250 	ap->asru_flags |= FMD_ASRU_RECREATED;
251 
252 	if (ps)
253 		ap->asru_flags |= FMD_ASRU_PRESENT;
254 	if (f)
255 		ap->asru_flags |= FMD_ASRU_FAULTY;
256 	if (u)
257 		ap->asru_flags |= FMD_ASRU_UNUSABLE;
258 
259 	if (nvlist_lookup_boolean_value(nvl,
260 	    FM_SUSPECT_MESSAGE, &m) == 0 && m == B_FALSE)
261 		ap->asru_flags |= FMD_ASRU_INVISIBLE;
262 
263 	/*
264 	 * If the ASRU is present and the Faulty bit is set and a case event is
265 	 * saved in the log, attempt to recreate the case in the CLOSED state.
266 	 * If the case is already present, fmd_case_recreate() will return it.
267 	 * If not, we'll create a new orphaned case, in which case we use the
268 	 * ASRU event to insert a suspect into the partially-restored case.
269 	 */
270 	(void) nvlist_lookup_string(nvl, FM_RSRC_ASRU_UUID, &case_uuid);
271 	(void) nvlist_lookup_string(nvl, FM_RSRC_ASRU_CODE, &case_code);
272 
273 	if (ps > 0 && !(ap->asru_flags & FMD_ASRU_INTERNAL) &&
274 	    (ap->asru_flags & FMD_ASRU_FAULTY) && case_uuid != NULL &&
275 	    nvlist_lookup_nvlist(nvl, FM_RSRC_ASRU_EVENT, &flt) == 0) {
276 
277 		fmd_module_lock(fmd.d_rmod);
278 
279 		ap->asru_case = fmd_case_recreate(fmd.d_rmod, NULL,
280 		    FMD_CASE_CLOSED, case_uuid, case_code);
281 
282 		if (ap->asru_case != NULL)
283 			fmd_case_hold(ap->asru_case);
284 
285 		fmd_module_unlock(fmd.d_rmod);
286 
287 		if (ap->asru_case != NULL && fmd_case_orphaned(ap->asru_case)) {
288 			(void) nvlist_xdup(flt, &ap->asru_event, &fmd.d_nva);
289 			(void) nvlist_xdup(flt, &flt_copy, &fmd.d_nva);
290 			fmd_case_recreate_suspect(ap->asru_case, flt_copy);
291 		}
292 	}
293 
294 	ASSERT(!(ap->asru_flags & FMD_ASRU_VALID));
295 	ap->asru_flags |= FMD_ASRU_VALID;
296 	fmd_asru_hash_insert(ahp, ap);
297 
298 	TRACE((FMD_DBG_ASRU, "asru %s recreated as %p (%s)", ap->asru_uuid,
299 	    (void *)ap, _fmd_asru_snames[ap->asru_flags & FMD_ASRU_STATE]));
300 }
301 
302 static void
303 fmd_asru_hash_discard(fmd_asru_hash_t *ahp, const char *uuid, int err)
304 {
305 	char src[PATH_MAX], dst[PATH_MAX];
306 
307 	(void) snprintf(src, PATH_MAX, "%s/%s", ahp->ah_dirpath, uuid);
308 	(void) snprintf(dst, PATH_MAX, "%s/%s-", ahp->ah_dirpath, uuid);
309 
310 	if (err != 0)
311 		err = rename(src, dst);
312 	else
313 		err = unlink(src);
314 
315 	if (err != 0 && errno != ENOENT)
316 		fmd_error(EFMD_ASRU_EVENT, "failed to rename log %s", src);
317 }
318 
319 /*
320  * Open a saved log file and restore it into the ASRU hash.  If we can't even
321  * open the log, rename the log file to <uuid>- to indicate it is corrupt.  If
322  * fmd_log_replay() fails, we either delete the file (if it has reached the
323  * upper limit on cache age) or rename it for debugging if it was corrupted.
324  */
325 static void
326 fmd_asru_hash_logopen(fmd_asru_hash_t *ahp, const char *uuid)
327 {
328 	fmd_log_t *lp = fmd_log_tryopen(ahp->ah_dirpath, uuid, FMD_LOG_ASRU);
329 	uint_t n;
330 
331 	if (lp == NULL) {
332 		fmd_asru_hash_discard(ahp, uuid, errno);
333 		return;
334 	}
335 
336 	ahp->ah_error = 0;
337 	n = ahp->ah_count;
338 
339 	fmd_log_replay(lp, (fmd_log_f *)fmd_asru_hash_recreate, ahp);
340 	fmd_log_rele(lp);
341 
342 	if (ahp->ah_count == n)
343 		fmd_asru_hash_discard(ahp, uuid, ahp->ah_error);
344 }
345 
346 void
347 fmd_asru_hash_refresh(fmd_asru_hash_t *ahp)
348 {
349 	struct dirent *dp;
350 	DIR *dirp;
351 	int zero;
352 
353 	if ((dirp = opendir(ahp->ah_dirpath)) == NULL) {
354 		fmd_error(EFMD_ASRU_NODIR,
355 		    "failed to open asru cache directory %s", ahp->ah_dirpath);
356 		return;
357 	}
358 
359 	(void) fmd_conf_getprop(fmd.d_conf, "rsrc.zero", &zero);
360 
361 	(void) pthread_rwlock_wrlock(&ahp->ah_lock);
362 
363 	while ((dp = readdir(dirp)) != NULL) {
364 		if (dp->d_name[0] == '.')
365 			continue; /* skip "." and ".." */
366 
367 		if (zero)
368 			fmd_asru_hash_discard(ahp, dp->d_name, 0);
369 		else if (!fmd_strmatch(dp->d_name, "*-"))
370 			fmd_asru_hash_logopen(ahp, dp->d_name);
371 	}
372 
373 	(void) pthread_rwlock_unlock(&ahp->ah_lock);
374 	(void) closedir(dirp);
375 }
376 
377 /*
378  * If the resource is present and faulty but not unusable, replay the fault
379  * event that caused it be marked faulty.  This will cause the agent
380  * subscribing to this fault class to again disable the resource.
381  */
382 /*ARGSUSED*/
383 static void
384 fmd_asru_hash_replay_asru(fmd_asru_t *ap, void *data)
385 {
386 	fmd_event_t *e;
387 	nvlist_t *nvl;
388 	char *class;
389 
390 	if (ap->asru_event != NULL && (ap->asru_flags & (FMD_ASRU_STATE |
391 	    FMD_ASRU_PRESENT)) == (FMD_ASRU_FAULTY | FMD_ASRU_PRESENT)) {
392 
393 		fmd_dprintf(FMD_DBG_ASRU,
394 		    "replaying fault event for %s", ap->asru_name);
395 
396 		(void) nvlist_xdup(ap->asru_event, &nvl, &fmd.d_nva);
397 		(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
398 
399 		(void) nvlist_add_string(nvl, FMD_EVN_UUID,
400 		    ((fmd_case_impl_t *)ap->asru_case)->ci_uuid);
401 
402 		e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
403 		fmd_dispq_dispatch(fmd.d_disp, e, class);
404 	}
405 }
406 
407 void
408 fmd_asru_hash_replay(fmd_asru_hash_t *ahp)
409 {
410 	fmd_asru_hash_apply(ahp, fmd_asru_hash_replay_asru, NULL);
411 }
412 
413 fmd_asru_hash_t *
414 fmd_asru_hash_create(const char *root, const char *dir)
415 {
416 	fmd_asru_hash_t *ahp;
417 	char path[PATH_MAX];
418 
419 	ahp = fmd_alloc(sizeof (fmd_asru_hash_t), FMD_SLEEP);
420 	(void) pthread_rwlock_init(&ahp->ah_lock, NULL);
421 	ahp->ah_hashlen = fmd.d_str_buckets;
422 	ahp->ah_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP);
423 	(void) snprintf(path, sizeof (path), "%s/%s", root, dir);
424 	ahp->ah_dirpath = fmd_strdup(path, FMD_SLEEP);
425 	(void) fmd_conf_getprop(fmd.d_conf, "rsrc.age", &ahp->ah_lifetime);
426 	ahp->ah_count = 0;
427 	ahp->ah_error = 0;
428 
429 	return (ahp);
430 }
431 
432 void
433 fmd_asru_hash_destroy(fmd_asru_hash_t *ahp)
434 {
435 	fmd_asru_t *ap, *np;
436 	uint_t i;
437 
438 	for (i = 0; i < ahp->ah_hashlen; i++) {
439 		for (ap = ahp->ah_hash[i]; ap != NULL; ap = np) {
440 			np = ap->asru_next;
441 			ap->asru_next = NULL;
442 			fmd_asru_hash_release(ahp, ap);
443 		}
444 	}
445 
446 	fmd_strfree(ahp->ah_dirpath);
447 	fmd_free(ahp->ah_hash, sizeof (void *) * ahp->ah_hashlen);
448 	fmd_free(ahp, sizeof (fmd_asru_hash_t));
449 }
450 
451 /*
452  * Take a snapshot of the ASRU database by placing an additional hold on each
453  * member in an auxiliary array, and then call 'func' for each ASRU.
454  */
455 void
456 fmd_asru_hash_apply(fmd_asru_hash_t *ahp,
457     void (*func)(fmd_asru_t *, void *), void *arg)
458 {
459 	fmd_asru_t *ap, **aps, **app;
460 	uint_t apc, i;
461 
462 	(void) pthread_rwlock_rdlock(&ahp->ah_lock);
463 
464 	aps = app = fmd_alloc(ahp->ah_count * sizeof (fmd_asru_t *), FMD_SLEEP);
465 	apc = ahp->ah_count;
466 
467 	for (i = 0; i < ahp->ah_hashlen; i++) {
468 		for (ap = ahp->ah_hash[i]; ap != NULL; ap = ap->asru_next)
469 			*app++ = fmd_asru_hold(ap);
470 	}
471 
472 	ASSERT(app == aps + apc);
473 	(void) pthread_rwlock_unlock(&ahp->ah_lock);
474 
475 	for (i = 0; i < apc; i++) {
476 		func(aps[i], arg);
477 		fmd_asru_hash_release(ahp, aps[i]);
478 	}
479 
480 	fmd_free(aps, apc * sizeof (fmd_asru_t *));
481 }
482 
483 /*
484  * Lookup an asru in the hash by name and place a hold on it.  If the asru is
485  * not found, no entry is created and NULL is returned.
486  */
487 fmd_asru_t *
488 fmd_asru_hash_lookup_name(fmd_asru_hash_t *ahp, const char *name)
489 {
490 	fmd_asru_t *ap;
491 
492 	(void) pthread_rwlock_rdlock(&ahp->ah_lock);
493 	ap = fmd_asru_hash_lookup(ahp, name);
494 	(void) pthread_rwlock_unlock(&ahp->ah_lock);
495 
496 	return (ap);
497 }
498 
499 /*
500  * Lookup an asru in the hash and place a hold on it.  If 'create' is true, an
501  * absent entry will be created for the caller; otherwise NULL is returned.
502  */
503 fmd_asru_t *
504 fmd_asru_hash_lookup_nvl(fmd_asru_hash_t *ahp, nvlist_t *fmri, int create)
505 {
506 	fmd_asru_t *ap;
507 	char *name = NULL;
508 	ssize_t namelen;
509 	uint_t h;
510 
511 	/*
512 	 * In order to lookup the ASRU in our hash, convert the FMRI from
513 	 * nvlist form into a string form using the scheme module.
514 	 */
515 	if ((namelen = fmd_fmri_nvl2str(fmri, NULL, 0)) == -1 ||
516 	    (name = fmd_alloc(namelen + 1, FMD_NOSLEEP)) == NULL ||
517 	    fmd_fmri_nvl2str(fmri, name, namelen + 1) == -1) {
518 		if (name != NULL)
519 			fmd_free(name, namelen + 1);
520 		return (NULL);
521 	}
522 
523 	/*
524 	 * If we must create the asru, grab the rwlock as a writer; otherwise
525 	 * reader is sufficient.  Then search the hash for the given asru name.
526 	 * If we didn't find the asru in the hash and we need to create it,
527 	 * create and insert the asru with ahp->ah_lock held and hash it in.
528 	 * We'll then drop the rwlock and proceed to initializing the asru.
529 	 */
530 	if (create)
531 		(void) pthread_rwlock_wrlock(&ahp->ah_lock);
532 	else
533 		(void) pthread_rwlock_rdlock(&ahp->ah_lock);
534 
535 	h = fmd_strhash(name) % ahp->ah_hashlen;
536 
537 	for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) {
538 		if (strcmp(ap->asru_name, name) == 0)
539 			break;
540 	}
541 
542 	if (ap == NULL && create == FMD_B_TRUE) {
543 		ap = fmd_asru_create(ahp, NULL, name, fmri);
544 		fmd_asru_hash_insert(ahp, ap);
545 		(void) pthread_mutex_lock(&ap->asru_lock);
546 	} else
547 		create = FMD_B_FALSE;
548 
549 	(void) pthread_rwlock_unlock(&ahp->ah_lock);
550 	fmd_free(name, namelen + 1);
551 
552 	/*
553 	 * If 'create' is still true, then we need to initialize the asru log;
554 	 * If 'create' is false and an asru was found, we must cond_wait for
555 	 * the FMD_ASRU_VALID bit to be set before returning.  In both cases,
556 	 * we increment asru_refs for the caller.
557 	 */
558 	if (create == FMD_B_TRUE) {
559 		uuid_t uuid;
560 
561 		ASSERT(MUTEX_HELD(&ap->asru_lock));
562 		ASSERT(ap->asru_uuid == NULL && ap->asru_log == NULL);
563 
564 		/*
565 		 * Generate a UUID for the ASRU.  libuuid cleverly gives us no
566 		 * interface for specifying or learning the buffer size.  Sigh.
567 		 * The spec says 36 bytes but we use a tunable just to be safe.
568 		 */
569 		(void) fmd_conf_getprop(fmd.d_conf,
570 		    "uuidlen", &ap->asru_uuidlen);
571 
572 		ap->asru_uuid = fmd_zalloc(ap->asru_uuidlen + 1, FMD_SLEEP);
573 		uuid_generate(uuid);
574 		uuid_unparse(uuid, ap->asru_uuid);
575 
576 		ASSERT(!(ap->asru_flags & FMD_ASRU_VALID));
577 		ap->asru_flags |= FMD_ASRU_VALID;
578 
579 		ap->asru_refs++;
580 		ASSERT(ap->asru_refs != 0);
581 		(void) pthread_cond_broadcast(&ap->asru_cv);
582 		(void) pthread_mutex_unlock(&ap->asru_lock);
583 
584 		TRACE((FMD_DBG_ASRU, "asru %s created as %p",
585 		    ap->asru_uuid, (void *)ap));
586 
587 	} else if (ap != NULL) {
588 		(void) pthread_mutex_lock(&ap->asru_lock);
589 
590 		while (!(ap->asru_flags & FMD_ASRU_VALID))
591 			(void) pthread_cond_wait(&ap->asru_cv, &ap->asru_lock);
592 
593 		ap->asru_refs++;
594 		ASSERT(ap->asru_refs != 0);
595 		(void) pthread_mutex_unlock(&ap->asru_lock);
596 	}
597 
598 	return (ap);
599 }
600 
601 /*
602  * Release the reference count on an asru obtained using fmd_asru_hash_lookup.
603  * We take 'ahp' for symmetry and in case we need to use it in future work.
604  */
605 /*ARGSUSED*/
606 void
607 fmd_asru_hash_release(fmd_asru_hash_t *ahp, fmd_asru_t *ap)
608 {
609 	(void) pthread_mutex_lock(&ap->asru_lock);
610 
611 	ASSERT(ap->asru_refs != 0);
612 	if (--ap->asru_refs == 0)
613 		fmd_asru_destroy(ap);
614 	else
615 		(void) pthread_mutex_unlock(&ap->asru_lock);
616 }
617 
618 int
619 fmd_asru_hash_delete_name(fmd_asru_hash_t *ahp, const char *name)
620 {
621 	fmd_asru_t *ap, **pp;
622 	char path[PATH_MAX];
623 	uint_t h;
624 
625 	(void) pthread_rwlock_wrlock(&ahp->ah_lock);
626 
627 	h = fmd_strhash(name) % ahp->ah_hashlen;
628 	pp = &ahp->ah_hash[h];
629 
630 	for (ap = *pp; ap != NULL; ap = ap->asru_next) {
631 		if (strcmp(ap->asru_name, name) == 0)
632 			break;
633 		else
634 			pp = &ap->asru_next;
635 	}
636 
637 	if (ap != NULL) {
638 		*pp = ap->asru_next;
639 		ap->asru_next = NULL;
640 		ASSERT(ahp->ah_count != 0);
641 		ahp->ah_count--;
642 	}
643 
644 	(void) pthread_rwlock_unlock(&ahp->ah_lock);
645 
646 	if (ap == NULL)
647 		return (fmd_set_errno(EFMD_ASRU_NOENT));
648 
649 	/*
650 	 * If we found a matching ASRU, unlink its log file and then release
651 	 * the hash entry.  Note that it may still be referenced if another
652 	 * thread is manipulating it; this is ok because once we unlink, the
653 	 * log file will not be restored, and the log data will be freed when
654 	 * all of the referencing threads release their respective references.
655 	 */
656 	(void) snprintf(path, sizeof (path),
657 	    "%s/%s", ahp->ah_dirpath, ap->asru_uuid);
658 
659 	if (unlink(path) != 0)
660 		fmd_error(EFMD_ASRU_UNLINK, "failed to unlink asru %s", path);
661 
662 	fmd_asru_hash_release(ahp, ap);
663 	return (0);
664 }
665 
666 static void
667 fmd_asru_logevent(fmd_asru_t *ap)
668 {
669 	boolean_t f = (ap->asru_flags & FMD_ASRU_FAULTY) != 0;
670 	boolean_t u = (ap->asru_flags & FMD_ASRU_UNUSABLE) != 0;
671 	boolean_t m = (ap->asru_flags & FMD_ASRU_INVISIBLE) == 0;
672 
673 	fmd_case_impl_t *cip;
674 	fmd_event_t *e;
675 	fmd_log_t *lp;
676 	nvlist_t *nvl;
677 	char *class;
678 
679 	ASSERT(MUTEX_HELD(&ap->asru_lock));
680 	cip = (fmd_case_impl_t *)ap->asru_case;
681 
682 	if ((lp = ap->asru_log) == NULL)
683 		lp = fmd_log_open(ap->asru_root, ap->asru_uuid, FMD_LOG_ASRU);
684 
685 	if (lp == NULL)
686 		return; /* can't log events if we can't open the log */
687 
688 	nvl = fmd_protocol_rsrc_asru(_fmd_asru_events[f | (u << 1)],
689 	    ap->asru_fmri, cip ? cip->ci_uuid : NULL,
690 	    cip ? cip->ci_code : NULL, f, u, m, ap->asru_event);
691 
692 	(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
693 	e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
694 
695 	fmd_event_hold(e);
696 	fmd_log_append(lp, e, NULL);
697 	fmd_event_rele(e);
698 
699 	/*
700 	 * For now, we close the log file after every update to conserve file
701 	 * descriptors and daemon overhead.  If this becomes a performance
702 	 * issue this code can change to keep a fixed-size LRU cache of logs.
703 	 */
704 	fmd_log_rele(lp);
705 	ap->asru_log = NULL;
706 }
707 
708 int
709 fmd_asru_setflags(fmd_asru_t *ap, uint_t sflag, fmd_case_t *cp, nvlist_t *nvl)
710 {
711 	fmd_case_t *old_case = NULL;
712 	nvlist_t *old_nvl = NULL;
713 	uint_t nstate, ostate;
714 	boolean_t msg;
715 
716 	ASSERT(!(sflag & ~FMD_ASRU_STATE));
717 	ASSERT(sflag != FMD_ASRU_STATE);
718 
719 	(void) pthread_mutex_lock(&ap->asru_lock);
720 
721 	ostate = ap->asru_flags & FMD_ASRU_STATE;
722 	ap->asru_flags |= sflag;
723 	nstate = ap->asru_flags & FMD_ASRU_STATE;
724 
725 	if (nstate == ostate) {
726 		(void) pthread_mutex_unlock(&ap->asru_lock);
727 		return (0);
728 	}
729 
730 	if (cp != NULL && cp != ap->asru_case) {
731 		old_case = ap->asru_case;
732 		fmd_case_hold_locked(cp);
733 		ap->asru_case = cp;
734 		old_nvl = ap->asru_event;
735 		(void) nvlist_xdup(nvl, &ap->asru_event, &fmd.d_nva);
736 	}
737 
738 	if (nvl != NULL && nvlist_lookup_boolean_value(nvl,
739 	    FM_SUSPECT_MESSAGE, &msg) == 0 && msg == B_FALSE)
740 		ap->asru_flags |= FMD_ASRU_INVISIBLE;
741 
742 	TRACE((FMD_DBG_ASRU, "asru %s %s->%s", ap->asru_uuid,
743 	    _fmd_asru_snames[ostate], _fmd_asru_snames[nstate]));
744 
745 	fmd_asru_logevent(ap);
746 
747 	(void) pthread_cond_broadcast(&ap->asru_cv);
748 	(void) pthread_mutex_unlock(&ap->asru_lock);
749 
750 	if (old_case != NULL)
751 		fmd_case_rele(old_case);
752 
753 	if (old_nvl != NULL)
754 		nvlist_free(old_nvl);
755 
756 	return (1);
757 }
758 
759 int
760 fmd_asru_clrflags(fmd_asru_t *ap, uint_t sflag, fmd_case_t *cp, nvlist_t *nvl)
761 {
762 	fmd_case_t *old_case = NULL;
763 	nvlist_t *old_nvl = NULL;
764 	uint_t nstate, ostate;
765 
766 	ASSERT(!(sflag & ~FMD_ASRU_STATE));
767 	ASSERT(sflag != FMD_ASRU_STATE);
768 
769 	(void) pthread_mutex_lock(&ap->asru_lock);
770 
771 	ostate = ap->asru_flags & FMD_ASRU_STATE;
772 	ap->asru_flags &= ~sflag;
773 	nstate = ap->asru_flags & FMD_ASRU_STATE;
774 
775 	if (nstate == ostate) {
776 		(void) pthread_mutex_unlock(&ap->asru_lock);
777 		return (0);
778 	}
779 
780 	if (cp != NULL && cp != ap->asru_case) {
781 		old_case = ap->asru_case;
782 		fmd_case_hold_locked(cp);
783 		ap->asru_case = cp;
784 		old_nvl = ap->asru_event;
785 		(void) nvlist_xdup(nvl, &ap->asru_event, &fmd.d_nva);
786 	}
787 
788 	TRACE((FMD_DBG_ASRU, "asru %s %s->%s", ap->asru_uuid,
789 	    _fmd_asru_snames[ostate], _fmd_asru_snames[nstate]));
790 
791 	fmd_asru_logevent(ap);
792 
793 	if (cp == NULL && (sflag & FMD_ASRU_FAULTY)) {
794 		old_case = ap->asru_case;
795 		ap->asru_case = NULL;
796 		old_nvl = ap->asru_event;
797 		ap->asru_event = NULL;
798 	}
799 
800 	(void) pthread_cond_broadcast(&ap->asru_cv);
801 	(void) pthread_mutex_unlock(&ap->asru_lock);
802 
803 	if (old_case != NULL) {
804 		if (cp == NULL && (sflag & FMD_ASRU_FAULTY))
805 			fmd_case_update(old_case);
806 		fmd_case_rele(old_case);
807 	}
808 
809 	if (old_nvl != NULL)
810 		nvlist_free(old_nvl);
811 
812 	return (1);
813 }
814 
815 /*
816  * Report the current known state of the ASRU by refreshing its unusable status
817  * based upon the routines provided by the scheme module.  If the unusable bit
818  * is different, we do *not* generate a state change here because that change
819  * may be unrelated to fmd activities and therefore we have no case or event.
820  * The absence of the transition is harmless as this function is only provided
821  * for RPC observability and fmd's clients are only concerned with ASRU_FAULTY.
822  */
823 int
824 fmd_asru_getstate(fmd_asru_t *ap)
825 {
826 	int us, st;
827 
828 	if (!(ap->asru_flags & FMD_ASRU_INTERNAL) &&
829 	    fmd_fmri_present(ap->asru_fmri) <= 0)
830 		return (0); /* do not report non-fmd non-present resources */
831 
832 	us = fmd_fmri_unusable(ap->asru_fmri);
833 	st = ap->asru_flags & FMD_ASRU_STATE;
834 
835 	if (us > 0)
836 		st |= FMD_ASRU_UNUSABLE;
837 	else if (us == 0)
838 		st &= ~FMD_ASRU_UNUSABLE;
839 
840 	return (st);
841 }
842