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