/* * Look up the diagcode for this case and cache it in ci_code. If no suspects * were defined for this case or if the lookup fails, the event dictionary or * module code is broken, and we set the event code to a precomputed default. */ static const char * fmd_case_mkcode(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_susp_t *cis; char **keys, **keyp; const char *s; ASSERT(MUTEX_HELD(&cip->ci_lock)); ASSERT(cip->ci_state >= FMD_CASE_SOLVED); fmd_free(cip->ci_code, cip->ci_codelen); cip->ci_codelen = cip->ci_mod->mod_codelen; cip->ci_code = fmd_zalloc(cip->ci_codelen, FMD_SLEEP); keys = keyp = alloca(sizeof (char *) * (cip->ci_nsuspects + 1)); for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) { if (nvlist_lookup_string(cis->cis_nvl, FM_CLASS, keyp) == 0) keyp++; } *keyp = NULL; /* mark end of keys[] array for libdiagcode */ if (cip->ci_nsuspects == 0 || fmd_module_dc_key2code( cip->ci_mod, keys, cip->ci_code, cip->ci_codelen) != 0) { (void) fmd_conf_getprop(fmd.d_conf, "nodiagcode", &s); fmd_free(cip->ci_code, cip->ci_codelen); cip->ci_codelen = strlen(s) + 1; cip->ci_code = fmd_zalloc(cip->ci_codelen, FMD_SLEEP); (void) strcpy(cip->ci_code, s); } return (cip->ci_code); }
/* * Take a snapshot of the case hash by placing an additional hold on each * member in an auxiliary array, and then call 'func' for each case. */ void fmd_case_hash_apply(fmd_case_hash_t *chp, void (*func)(fmd_case_t *, void *), void *arg) { fmd_case_impl_t *cp, **cps, **cpp; uint_t cpc, i; (void) pthread_rwlock_rdlock(&chp->ch_lock); cps = cpp = fmd_alloc(chp->ch_count * sizeof (fmd_case_t *), FMD_SLEEP); cpc = chp->ch_count; for (i = 0; i < chp->ch_hashlen; i++) { for (cp = chp->ch_hash[i]; cp != NULL; cp = cp->ci_next) { fmd_case_hold((fmd_case_t *)cp); *cpp++ = cp; } } ASSERT(cpp == cps + cpc); (void) pthread_rwlock_unlock(&chp->ch_lock); for (i = 0; i < cpc; i++) { func((fmd_case_t *)cps[i], arg); fmd_case_rele((fmd_case_t *)cps[i]); } fmd_free(cps, cpc * sizeof (fmd_case_t *)); }
static void fmd_ckpt_destroy(fmd_ckpt_t *ckp) { if (ckp->ckp_buf != NULL) fmd_free(ckp->ckp_buf, ckp->ckp_size); if (ckp->ckp_fd >= 0) (void) close(ckp->ckp_fd); }
static void fmd_ckpt_save_module(fmd_ckpt_t *ckp, fmd_module_t *mp) { fcf_secidx_t bufsec = FCF_SECIDX_NONE; fcf_module_t fcfm; fmd_case_t *cp; uint_t n; for (cp = fmd_list_next(&mp->mod_cases); cp; cp = fmd_list_next(cp)) fmd_ckpt_save_case(ckp, cp); if ((n = fmd_serd_hash_count(&mp->mod_serds)) != 0) { size_t size = sizeof (fcf_serd_t) * n; fcf_serd_t *serds = ckp->ckp_arg = fmd_alloc(size, FMD_SLEEP); fmd_serd_hash_apply(&mp->mod_serds, (fmd_serd_eng_f *)fmd_ckpt_save_serd, ckp); (void) fmd_ckpt_section(ckp, serds, FCF_SECT_SERD, size); fmd_free(serds, size); } if ((n = fmd_buf_hash_count(&mp->mod_bufs)) != 0) { size_t size = sizeof (fcf_buf_t) * n; fcf_buf_t *bufs = ckp->ckp_arg = fmd_alloc(size, FMD_SLEEP); fmd_buf_hash_apply(&mp->mod_bufs, (fmd_buf_f *)fmd_ckpt_save_buf, ckp); bufsec = fmd_ckpt_section(ckp, bufs, FCF_SECT_BUFS, size); fmd_free(bufs, size); } fcfm.fcfm_name = fmd_ckpt_string(ckp, mp->mod_name); fcfm.fcfm_path = fmd_ckpt_string(ckp, mp->mod_path); fcfm.fcfm_desc = fmd_ckpt_string(ckp, mp->mod_info->fmdi_desc); fcfm.fcfm_vers = fmd_ckpt_string(ckp, mp->mod_info->fmdi_vers); fcfm.fcfm_bufs = bufsec; (void) fmd_ckpt_section(ckp, &fcfm, FCF_SECT_MODULE, sizeof (fcf_module_t)); }
/*PRINTFLIKE2*/ static int fmd_ckpt_inval(fmd_ckpt_t *ckp, const char *format, ...) { va_list ap; va_start(ap, format); fmd_verror(EFMD_CKPT_INVAL, format, ap); va_end(ap); fmd_free(ckp->ckp_buf, ckp->ckp_size); return (fmd_set_errno(EFMD_CKPT_INVAL)); }
void fmd_thread_destroy(fmd_thread_t *tp, int flag) { if (flag == FMD_THREAD_JOIN && tp->thr_tid != pthread_self() && pthread_join(tp->thr_tid, NULL) != 0) { fmd_error(EFMD_MOD_JOIN, "failed to join thread for module " "%s (tid %u)\n", tp->thr_mod->mod_name, tp->thr_tid); } (void) pthread_mutex_lock(&fmd.d_thr_lock); fmd_list_delete(&fmd.d_thr_list, tp); (void) pthread_mutex_unlock(&fmd.d_thr_lock); fmd_trace_destroy(tp->thr_trdata); fmd_free(tp, sizeof (fmd_thread_t)); }
static fmd_thread_t * fmd_thread_create_cmn(fmd_module_t *mp, fmd_thread_f *func, void *arg, int isdoor) { fmd_thread_t *tp = fmd_alloc(sizeof (fmd_thread_t), FMD_SLEEP); sigset_t oset, nset; int err; tp->thr_mod = mp; tp->thr_func = func; tp->thr_arg = arg; tp->thr_trdata = fmd_trace_create(); tp->thr_trfunc = (fmd_tracebuf_f *)fmd.d_thr_trace; tp->thr_errdepth = 0; tp->thr_isdoor = isdoor; (void) sigfillset(&nset); (void) sigdelset(&nset, SIGABRT); /* always unblocked for fmd_panic() */ if (!isdoor) (void) sigdelset(&nset, fmd.d_thr_sig); /* fmd_thr_signal() */ (void) pthread_sigmask(SIG_SETMASK, &nset, &oset); err = pthread_create(&tp->thr_tid, NULL, fmd_thread_start, tp); (void) pthread_sigmask(SIG_SETMASK, &oset, NULL); if (err != 0) { fmd_free(tp, sizeof (fmd_thread_t)); return (NULL); } (void) pthread_mutex_lock(&fmd.d_thr_lock); fmd_list_append(&fmd.d_thr_list, tp); (void) pthread_mutex_unlock(&fmd.d_thr_lock); return (tp); }
void fmd_strfree(char *s) { if (s != NULL) fmd_free(s, strlen(s) + 1); }
/* * Destroy the case hash. Unlike most of our hash tables, no active references * are kept by the case hash itself; all references come from other subsystems. * The hash must be destroyed after all modules are unloaded; if anything was * present in the hash it would be by definition a reference count leak. */ void fmd_case_hash_destroy(fmd_case_hash_t *chp) { fmd_free(chp->ch_hash, sizeof (void *) * chp->ch_hashlen); fmd_free(chp, sizeof (fmd_case_hash_t)); }
void fmd_destroy(fmd_t *dp) { fmd_module_t *mp; fmd_case_t *cp; int core; (void) fmd_conf_getprop(fmd.d_conf, "core", &core); fmd_rpc_fini(); fmd_dr_fini(); if (dp->d_xprt_ids != NULL) fmd_xprt_suspend_all(); /* * Unload the self-diagnosis module first. This ensures that it does * not get confused as we start unloading other modules, etc. We must * hold the dispq lock as a writer while doing so since it uses d_self. */ if (dp->d_self != NULL) { fmd_module_t *self; (void) pthread_rwlock_wrlock(&dp->d_disp->dq_lock); self = dp->d_self; dp->d_self = NULL; (void) pthread_rwlock_unlock(&dp->d_disp->dq_lock); fmd_module_unload(self); fmd_module_rele(self); } /* * Unload modules in reverse order *except* for the root module, which * is first in the list. This allows it to keep its thread and trace. */ for (mp = fmd_list_prev(&dp->d_mod_list); mp != dp->d_rmod; ) { fmd_module_unload(mp); mp = fmd_list_prev(mp); } if (dp->d_mod_hash != NULL) { fmd_modhash_destroy(dp->d_mod_hash); dp->d_mod_hash = NULL; } /* * Close both log files now that modules are no longer active. We must * set these pointers to NULL in case any subsequent errors occur. */ if (dp->d_errlog != NULL) { fmd_log_rele(dp->d_errlog); dp->d_errlog = NULL; } if (dp->d_fltlog != NULL) { fmd_log_rele(dp->d_fltlog); dp->d_fltlog = NULL; } /* * Now destroy the resource cache: each ASRU contains a case reference, * which may in turn contain a pointer to a referenced owning module. */ if (dp->d_asrus != NULL) { fmd_asru_hash_destroy(dp->d_asrus); dp->d_asrus = NULL; } /* * Now that all data structures that refer to modules are torn down, * no modules should be remaining on the module list except for d_rmod. * If we trip one of these assertions, we're missing a rele somewhere. */ ASSERT(fmd_list_prev(&dp->d_mod_list) == dp->d_rmod); ASSERT(fmd_list_next(&dp->d_mod_list) == dp->d_rmod); /* * Now destroy the root module. We clear its thread key first so any * calls to fmd_trace() inside of the module code will be ignored. */ (void) pthread_setspecific(dp->d_key, NULL); fmd_module_lock(dp->d_rmod); while ((cp = fmd_list_next(&dp->d_rmod->mod_cases)) != NULL) fmd_case_discard(cp); fmd_module_unlock(dp->d_rmod); fmd_free(dp->d_rmod->mod_stats, sizeof (fmd_modstat_t)); dp->d_rmod->mod_stats = NULL; (void) pthread_mutex_lock(&dp->d_rmod->mod_lock); dp->d_rmod->mod_flags |= FMD_MOD_FINI; (void) pthread_mutex_unlock(&dp->d_rmod->mod_lock); fmd_module_rele(dp->d_rmod); ASSERT(fmd_list_next(&dp->d_mod_list) == NULL); /* * Now destroy the remaining global data structures. If 'core' was * set to true, force a core dump so we can check for memory leaks. */ if (dp->d_cases != NULL) fmd_case_hash_destroy(dp->d_cases); if (dp->d_disp != NULL) fmd_dispq_destroy(dp->d_disp); if (dp->d_timers != NULL) fmd_timerq_destroy(dp->d_timers); if (dp->d_schemes != NULL) fmd_scheme_hash_destroy(dp->d_schemes); if (dp->d_xprt_ids != NULL) fmd_idspace_destroy(dp->d_xprt_ids); if (dp->d_errstats != NULL) { fmd_free(dp->d_errstats, sizeof (fmd_stat_t) * (EFMD_END - EFMD_UNKNOWN)); } if (dp->d_conf != NULL) fmd_conf_close(dp->d_conf); if (dp->d_topo != NULL) topo_close(dp->d_topo); nvlist_free(dp->d_auth); (void) nv_alloc_fini(&dp->d_nva); dp->d_clockops->fto_fini(dp->d_clockptr); (void) pthread_key_delete(dp->d_key); bzero(dp, sizeof (fmd_t)); if (core) fmd_panic("forcing core dump at user request\n"); }
void fmd_trace_destroy(fmd_tracebuf_t *tbp) { fmd_free(tbp->tb_buf, tbp->tb_size * tbp->tb_recs); fmd_free(tbp, sizeof (fmd_tracebuf_t)); }
static void fmd_ckpt_save_case(fmd_ckpt_t *ckp, fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_item_t *cit; fmd_case_susp_t *cis; fcf_case_t fcfc; uint_t n; fcf_secidx_t bufsec = FCF_SECIDX_NONE; fcf_secidx_t evsec = FCF_SECIDX_NONE; fcf_secidx_t nvsec = FCF_SECIDX_NONE; fcf_secidx_t prsec = FCF_SECIDX_NONE; if (cip->ci_xprt != NULL) return; /* do not checkpoint cases from remote transports */ if ((n = fmd_buf_hash_count(&cip->ci_bufs)) != 0) { size_t size = sizeof (fcf_buf_t) * n; fcf_buf_t *bufs = ckp->ckp_arg = fmd_alloc(size, FMD_SLEEP); fmd_buf_hash_apply(&cip->ci_bufs, (fmd_buf_f *)fmd_ckpt_save_buf, ckp); bufsec = fmd_ckpt_section(ckp, bufs, FCF_SECT_BUFS, size); fmd_free(bufs, size); } if (cip->ci_principal != NULL) { prsec = fmd_ckpt_section(ckp, NULL, FCF_SECT_EVENTS, sizeof (fcf_event_t)); fmd_ckpt_save_event(ckp, cip->ci_principal); } if (cip->ci_nitems != 0) { evsec = fmd_ckpt_section(ckp, NULL, FCF_SECT_EVENTS, sizeof (fcf_event_t) * cip->ci_nitems); for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) fmd_ckpt_save_event(ckp, cit->cit_event); } if (cip->ci_nsuspects != 0) { nvsec = fmd_ckpt_section(ckp, NULL, FCF_SECT_NVLISTS, cip->ci_nvsz); for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) fmd_ckpt_save_nvlist(ckp, cis->cis_nvl); } fcfc.fcfc_uuid = fmd_ckpt_string(ckp, cip->ci_uuid); fcfc.fcfc_bufs = bufsec; fcfc.fcfc_principal = prsec; fcfc.fcfc_events = evsec; fcfc.fcfc_suspects = nvsec; switch (cip->ci_state) { case FMD_CASE_UNSOLVED: fcfc.fcfc_state = FCF_CASE_UNSOLVED; break; case FMD_CASE_SOLVED: fcfc.fcfc_state = FCF_CASE_SOLVED; break; case FMD_CASE_CLOSE_WAIT: fcfc.fcfc_state = FCF_CASE_CLOSE_WAIT; break; default: fmd_panic("case %p (%s) has invalid state %u", (void *)cp, cip->ci_uuid, cip->ci_state); } (void) fmd_ckpt_section(ckp, &fcfc, FCF_SECT_CASE, sizeof (fcf_case_t)); }
static int fmd_ckpt_open(fmd_ckpt_t *ckp, fmd_module_t *mp) { struct stat64 st; uint64_t seclen; uint_t i; int err; bzero(ckp, sizeof (fmd_ckpt_t)); ckp->ckp_mp = mp; (void) snprintf(ckp->ckp_src, PATH_MAX, "%s/%s", mp->mod_ckpt, mp->mod_name); if ((ckp->ckp_fd = open(ckp->ckp_src, O_RDONLY)) == -1) return (-1); /* failed to open checkpoint file */ if (fstat64(ckp->ckp_fd, &st) == -1) { err = errno; (void) close(ckp->ckp_fd); return (fmd_set_errno(err)); } ckp->ckp_buf = fmd_alloc(st.st_size, FMD_SLEEP); ckp->ckp_hdr = (void *)ckp->ckp_buf; ckp->ckp_size = read(ckp->ckp_fd, ckp->ckp_buf, st.st_size); if (ckp->ckp_size != st.st_size || ckp->ckp_size < sizeof (fcf_hdr_t) || ckp->ckp_size != ckp->ckp_hdr->fcfh_filesz) { err = ckp->ckp_size == (size_t)-1L ? errno : EFMD_CKPT_SHORT; fmd_free(ckp->ckp_buf, st.st_size); (void) close(ckp->ckp_fd); return (fmd_set_errno(err)); } (void) close(ckp->ckp_fd); ckp->ckp_fd = -1; /* * Once we've read in a consistent copy of the FCF file and we're sure * the header can be accessed, go through it and make sure everything * is valid. We also check that unused bits are zero so we can expand * to use them safely in the future and support old files if needed. */ if (bcmp(&ckp->ckp_hdr->fcfh_ident[FCF_ID_MAG0], FCF_MAG_STRING, FCF_MAG_STRLEN) != 0) return (fmd_ckpt_inval(ckp, "bad checkpoint magic string\n")); if (ckp->ckp_hdr->fcfh_ident[FCF_ID_MODEL] != FCF_MODEL_NATIVE) return (fmd_ckpt_inval(ckp, "bad checkpoint data model\n")); if (ckp->ckp_hdr->fcfh_ident[FCF_ID_ENCODING] != FCF_ENCODE_NATIVE) return (fmd_ckpt_inval(ckp, "bad checkpoint data encoding\n")); if (ckp->ckp_hdr->fcfh_ident[FCF_ID_VERSION] != FCF_VERSION_1) { return (fmd_ckpt_inval(ckp, "bad checkpoint version %u\n", ckp->ckp_hdr->fcfh_ident[FCF_ID_VERSION])); } for (i = FCF_ID_PAD; i < FCF_ID_SIZE; i++) { if (ckp->ckp_hdr->fcfh_ident[i] != 0) { return (fmd_ckpt_inval(ckp, "bad checkpoint padding at id[%d]", i)); } } if (ckp->ckp_hdr->fcfh_flags & ~FCF_FL_VALID) return (fmd_ckpt_inval(ckp, "bad checkpoint flags\n")); if (ckp->ckp_hdr->fcfh_pad != 0) return (fmd_ckpt_inval(ckp, "reserved field in use\n")); if (ckp->ckp_hdr->fcfh_hdrsize < sizeof (fcf_hdr_t) || ckp->ckp_hdr->fcfh_secsize < sizeof (fcf_sec_t)) { return (fmd_ckpt_inval(ckp, "bad header and/or section size\n")); } seclen = (uint64_t)ckp->ckp_hdr->fcfh_secnum * (uint64_t)ckp->ckp_hdr->fcfh_secsize; if (ckp->ckp_hdr->fcfh_secoff > ckp->ckp_size || seclen > ckp->ckp_size || ckp->ckp_hdr->fcfh_secoff + seclen > ckp->ckp_size || ckp->ckp_hdr->fcfh_secoff + seclen < ckp->ckp_hdr->fcfh_secoff) return (fmd_ckpt_inval(ckp, "truncated section headers\n")); if (!IS_P2ALIGNED(ckp->ckp_hdr->fcfh_secoff, sizeof (uint64_t)) || !IS_P2ALIGNED(ckp->ckp_hdr->fcfh_secsize, sizeof (uint64_t))) return (fmd_ckpt_inval(ckp, "misaligned section headers\n")); /* * Once the header is validated, iterate over the section headers * ensuring that each one is valid w.r.t. offset, alignment, and size. * We also pick up the string table pointer during this pass. */ ckp->ckp_secp = (void *)(ckp->ckp_buf + ckp->ckp_hdr->fcfh_secoff); ckp->ckp_secs = ckp->ckp_hdr->fcfh_secnum; for (i = 0; i < ckp->ckp_secs; i++) { fcf_sec_t *sp = (void *)(ckp->ckp_buf + ckp->ckp_hdr->fcfh_secoff + ckp->ckp_hdr->fcfh_secsize * i); const fmd_ckpt_desc_t *dp = &_fmd_ckpt_sections[sp->fcfs_type]; if (sp->fcfs_flags != 0) { return (fmd_ckpt_inval(ckp, "section %u has invalid " "section flags (0x%x)\n", i, sp->fcfs_flags)); } if (sp->fcfs_align & (sp->fcfs_align - 1)) { return (fmd_ckpt_inval(ckp, "section %u has invalid " "alignment (%u)\n", i, sp->fcfs_align)); } if (sp->fcfs_offset & (sp->fcfs_align - 1)) { return (fmd_ckpt_inval(ckp, "section %u is not properly" " aligned (offset %llu)\n", i, sp->fcfs_offset)); } if (sp->fcfs_entsize != 0 && (sp->fcfs_entsize & (sp->fcfs_align - 1)) != 0) { return (fmd_ckpt_inval(ckp, "section %u has misaligned " "entsize %u\n", i, sp->fcfs_entsize)); } if (sp->fcfs_offset > ckp->ckp_size || sp->fcfs_size > ckp->ckp_size || sp->fcfs_offset + sp->fcfs_size > ckp->ckp_size || sp->fcfs_offset + sp->fcfs_size < sp->fcfs_offset) { return (fmd_ckpt_inval(ckp, "section %u has corrupt " "size or offset\n", i)); } if (sp->fcfs_type >= sizeof (_fmd_ckpt_sections) / sizeof (_fmd_ckpt_sections[0])) { return (fmd_ckpt_inval(ckp, "section %u has unknown " "section type %u\n", i, sp->fcfs_type)); } if (sp->fcfs_align != dp->secd_align) { return (fmd_ckpt_inval(ckp, "section %u has align %u " "(not %u)\n", i, sp->fcfs_align, dp->secd_align)); } if (sp->fcfs_size < dp->secd_size || sp->fcfs_entsize < dp->secd_entsize) { return (fmd_ckpt_inval(ckp, "section %u has short " "size or entsize\n", i)); } switch (sp->fcfs_type) { case FCF_SECT_STRTAB: if (ckp->ckp_strs != NULL) { return (fmd_ckpt_inval(ckp, "multiple string " "tables are present in checkpoint file\n")); } ckp->ckp_strs = (char *)ckp->ckp_buf + sp->fcfs_offset; ckp->ckp_strn = sp->fcfs_size; if (ckp->ckp_strs[ckp->ckp_strn - 1] != '\0') { return (fmd_ckpt_inval(ckp, "string table %u " "is missing terminating nul byte\n", i)); } break; case FCF_SECT_MODULE: if (ckp->ckp_modp != NULL) { return (fmd_ckpt_inval(ckp, "multiple module " "sects are present in checkpoint file\n")); } ckp->ckp_modp = sp; break; } } /* * Ensure that the first section is an empty one of type FCF_SECT_NONE. * This is done to ensure that links can use index 0 as a null section. */ if (ckp->ckp_secs == 0 || ckp->ckp_secp->fcfs_type != FCF_SECT_NONE || ckp->ckp_secp->fcfs_entsize != 0 || ckp->ckp_secp->fcfs_size != 0) { return (fmd_ckpt_inval(ckp, "section 0 is not of the " "appropriate size and/or attributes (SECT_NONE)\n")); } if (ckp->ckp_modp == NULL) { return (fmd_ckpt_inval(ckp, "no module section found in file\n")); } return (0); }