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