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