# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # SUBDIRS = software-diagnosis software-response include ../../../Makefile.subdirs # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # CMN_SRCS = common/sw_main_cmn.c SMF_CMN_SRCS = subsidiary/smf/smf_util.c SMF_DE_SRCS = subsidiary/smf/smf_diag.c $(SMF_CMN_SRCS) SMF_RP_SRCS = subsidiary/smf/smf_response.c $(SMF_CMN_SRCS) PANIC_DE_SRCS = subsidiary/panic/panic_diag.c /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SW_H #define _SW_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include /* * We have two real fmd modules - software-diagnosis and software-response. * Each hosts a number of subsidiary diagnosis engines and response agents, * although these are not fmd modules as such (the intention is to avoid * a proliferation of small C diagnosis and response modules). * * Subsidiary "modules" are not loaded as normal fmd modules are. Instead * each of the real modules software-diagnosis and software-response includes * an array listing the subsidiaries it hosts, and when the real module * is loaded by fmd it iterates over this list to "load" subsidiaries by * calling their nominated init function. */ /* Maximum number of subsidiary "modules" */ #define SW_SUB_MAX 10 /* Maximum number of supported timers across all subsidiaries */ #define SW_TIMER_MAX 20 /* * A subsidiary must perform fmd_hdl_subscribe calls for all events of * interest to it. These are typically performed during its init * function. All subscription callbacks funnel through the shared * fmdo_recv entry point; that function walks through the dispatch list * for each subsidiary and performs a callback for the first matching entry of * each subsidiary. The init entry point for each subsidiary * returns a pointer to an array of struct sw_disp applicable for that * entity. * * Note that the framework does *not* perform any fmd_hdl_subscribe calls * on behalf of the subsidiary - the swd_classpat member below is used * in routing events, not in establishing subscriptions for them. A * subsidiary can subscribe to say "ireport.foo.a" and "ireport.foo.b" * but could elect to nominate a common handler for those via a single * struct sw_disp with swd_classpat of "ireport.foo.*". */ typedef void sw_dispfunc_t(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *, void *); struct sw_disp { const char *swd_classpat; /* event classes to callback for */ sw_dispfunc_t *swd_func; /* callback function */ void *swd_arg; /* opaque argument to callback */ }; /* * A diagnosis or response subsidiary must provide a struct sw_subinfo with * all its pertinent information; a pointer to this structure must be * included in the array of struct sw_subinfo pointers in each of * software-diagnosis and software-response. * * swsub_name * This should be chosen to be unique to this subsidiary; * by convention it should also be the name prefix used in any fmd * buffers the subsidiary creates. * * swsub_casetype * A diagnosis subsidiary solves cases using swde_case_* below, and it * must specify in swsub_casetype the type of case it solves. A response * subsidiary must specify SW_CASE_NONE here. A subsidiary may only solve * at most one type of case, and no two subsidiaries must solve the same * case type. We use the case type to associate a subsidiary owner of * the fmd case that is really owned by the host module. * * swsub_init * The initialization function for this subsidiary, akin to the * _fmd_init in a traditional fmd module. This must not be NULL. * * When the host diagnosis/response module initializes the _fmd_init * entry point will call the swsub_init function for each subsidiary * in turn. The fmd handle has already been registered and timers are * available for installation (see below); the swsub_init function must * return a pointer to a NULL-terminated array of struct sw_disp * describing the event dispatch preferences for that module, and fill * an integer we pass with the number of entries in that array (including * the terminating NULL entry). The swsub_init function also receives * a subsidiary-unique id_t assigned by the framework that it should * keep a note of for use in timer installation (see below); this id * should not be persisted to checkpoint data. * * swsub_fini * When the host module _fmd_fini is called it will call this function * for each subsidiary. A subsidiary can specify NULL here. * * swsub_timeout * This is the timeout function to call for expired timers installed by * this subsidiary. See sw_timer_{install,remove} below. May be * NULL if no timers are used by this subsidiary. * * swsub_case_close * This function is called when a case "owned" by a subsidiary * is the subject of an fmdo_close callback. Can be NULL, and * must be NULL for a subsidiary with case type SW_CASE_NONE (such * as a response subsidiary). * * swsub_case_verify * This is called during _fmd_init of the host module. The host module * iterates over all cases that it owns and calls the verify function * for the real owner which may choose to close cases if they no longer * apply. Can be NULL, and must be NULL for a subsidiary with case * type SW_CASE_NONE. */ /* * sw_casetype values are persisted to checkpoints - do not change values. */ enum sw_casetype { SW_CASE_NONE = 0x0ca5e000, SW_CASE_SMF, SW_CASE_PANIC }; /* * Returns for swsub_init. The swsub_fini entry point will only be * called for subsidiaries that returned SW_SUB_INIT_SUCCESS on init. */ #define SW_SUB_INIT_SUCCESS 0 #define SW_SUB_INIT_FAIL_VOLUNTARY 1 /* chose not to init */ #define SW_SUB_INIT_FAIL_ERROR 2 /* error prevented init */ typedef void swsub_case_close_func_t(fmd_hdl_t *, fmd_case_t *); typedef int sw_case_vrfy_func_t(fmd_hdl_t *, fmd_case_t *); struct sw_subinfo { const char *swsub_name; enum sw_casetype swsub_casetype; int (*swsub_init)(fmd_hdl_t *, id_t, const struct sw_disp **, int *); void (*swsub_fini)(fmd_hdl_t *); void (*swsub_timeout)(fmd_hdl_t *, id_t, void *); swsub_case_close_func_t *swsub_case_close; sw_case_vrfy_func_t *swsub_case_verify; }; /* * List sw_subinfo for each subsidiary diagnosis and response "module" here */ extern const struct sw_subinfo smf_diag_info; extern const struct sw_subinfo smf_response_info; extern const struct sw_subinfo panic_diag_info; /* * Timers - as per the fmd module API but with an additional id_t argument * specifying the unique id of the subsidiary installing the timer (provided * to the subsidiary in its swsub_init call). */ extern id_t sw_timer_install(fmd_hdl_t *, id_t, void *, fmd_event_t *, hrtime_t); extern void sw_timer_remove(fmd_hdl_t *, id_t, id_t); /* * The software-diagnosis subsidiaries can open and solve cases; to do so * they must use the following wrappers to the usual fmd module API case * management functions. We need this so that a subsidiary can iterate * over *its* cases (fmd_case_next would iterate over those of other * subsidiaries), receive in the subsidiary a callback when a case it opened * is closed, etc. The subsidiary can use other fmd module API members * for case management, such as fmd_case_add_ereport. * * Each subsidiary opens cases of its own unique type, identified by * the sw_casetype enumeration. The values used in this enumeration * must never change - they are written to checkpoint state. * * swde_case_open * Opens a new case of the correct subsidiary type for the given * subsidiary id. If a uuid string is provided then open a case * with that uuid using fmd_case_open_uuid, allowing case uuid * to match some relevant uuid that was received in one of the * events that has led us to open this case. * * If the subsidiarywishes to associate some persistent * case data with the new case thenit can fmd_hdl_alloc and complete a * suitably-packed serialization structure and include a pointer to it * in the call to sw_case_open together with the structure size and * structure version. The framework will create a new fmd buffer (named * for you, based on the case type) and write the structure out to disk; * when the module or fmd is restarted this structure is restored from * disk for you and reassociated with the case - use swde_case_data to * retrieve a pointer to it. * * swde_case_first, swde_case_next * A subsidiary DE can iterate over its cases using swde_case_first and * swde_case_next. For swde_case_first quote the subsidiary id; * for swde_case_next quote the last case returned. * * swde_case_data * Returns a pointer to the previously-serialized case data, and fills * a uint32_t with the version of that serialized data. * * swde_case_data_write * Whenever a subsidiary modifies its persistent data structure * it must call swde_case_data_write to indicate that the associated * fmd buffer is dirty and needs to be rewritten. * * swde_case_data_upgrade * If the subsidiary ever revs its persistent structure it needs to call * swde_case_data_upgrade to register the new version and structure size, * and write the structure out to a reallocated fmd buffer; the old * case data structure (if any) will be freed. A subsidiary may use * this interface to migrate old persistence structures restored from * checkpoint - swde_case_data will return a version number below the * current. */ extern fmd_case_t *swde_case_open(fmd_hdl_t *, id_t, char *, uint32_t, void *, size_t); extern fmd_case_t *swde_case_first(fmd_hdl_t *, id_t); extern fmd_case_t *swde_case_next(fmd_hdl_t *, fmd_case_t *); extern void *swde_case_data(fmd_hdl_t *, fmd_case_t *, uint32_t *); extern void swde_case_data_write(fmd_hdl_t *, fmd_case_t *); extern void swde_case_data_upgrade(fmd_hdl_t *, fmd_case_t *, uint32_t, void *, size_t); #ifdef __cplusplus } #endif #endif /* _SW_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SW_IMPL_H #define _SW_IMPL_H #include "sw.h" /* * The common code between software-response and software-diagnosis * needs somewhere to track the subsidaries that have "registered", * their dispatch tables etc. In the _fmd_init of each module we * call the shared sw_fmd_init code, and there we allocate a * struct sw_modspecific and assign this as the fmd fodule-specific * data with fmd_hdl_setspecific. */ struct sw_modspecific { int swms_dispcnt; const struct sw_subinfo *(*swms_subinfo)[SW_SUB_MAX]; const struct sw_disp *(*swms_disptbl)[SW_SUB_MAX]; pthread_mutex_t swms_timerlock; struct { int swt_state; /* slot in use? */ id_t swt_timerid; /* fmd_timer_install result */ id_t swt_ownerid; /* subsidiary owner id */ } swms_timers[SW_TIMER_MAX]; }; #define SW_TMR_INUSE 1 #define SW_TMR_RMVD 0 #define SW_TMR_UNTOUCHED -1 extern swsub_case_close_func_t *sw_sub_case_close_func(fmd_hdl_t *, enum sw_casetype); extern sw_case_vrfy_func_t *sw_sub_case_vrfy_func(fmd_hdl_t *, enum sw_casetype); /* * Software DE fmdo_close entry point. */ extern void swde_close(fmd_hdl_t *, fmd_case_t *); /* * Shared functions for software-diagnosis and software-response fmd * module implementation using shared code. Subsidiaries do not need * to call these functions. * * sw_fmd_init is called from _fmd_init of the two modules, to do most of * the real work of initializing the subsidiaries etc. * * sw_fmd_fini is called from _fmd_fini and calls the swsub_fini * function of each subsidiary after uninstalling all timers. * * sw_recv is the fmdo_recv entry point; it checks the event against * the dispatch table of each subsidiary and dispatches the first * match for each module. * * sw_timeout is the fmdo_timeout entry point; it looks up the unique id_t * of the subsidiary that installed the timer (via sw_timer_install in which * the id is quoted) and calls the swsub_timeout function for that subsidiary. * * swde_case_init and swde_case_fini initialize and finalize the * software-diagnosis case-tracking infrastructure; swde_case_init * is responsible for unserializing case state. * * sw_id_to_casetype take a subsidiary id and returns the case type it * registered with. */ extern int sw_fmd_init(fmd_hdl_t *, const fmd_hdl_info_t *, const struct sw_subinfo *(*)[SW_SUB_MAX]); extern void sw_fmd_fini(fmd_hdl_t *); extern void sw_recv(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *); extern void sw_timeout(fmd_hdl_t *, id_t, void *); extern void swde_case_init(fmd_hdl_t *); extern void swde_case_fini(fmd_hdl_t *); enum sw_casetype sw_id_to_casetype(fmd_hdl_t *, id_t); #endif /* _SW_IMPL_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Code shared by software-diagnosis and software-response modules. * The fmd module linkage info for the two modules lives in swde_main.c * (for software-diagnosis) and swrp_main.c (for software-response). */ #include "../common/sw_impl.h" /* * Each subsidiary that is hosted is assigned a unique subsidiary id. These * macros convert between the id of a subsidiary and the index used in keeping * track of subsidiaries. Outside of this file these ids should remain * opaque. */ #define ID2IDX(id) ((int)((id) & 0xff0000) >> 16) #define IDX2ID(i) ((id_t)((i) << 16) | 0x1d000000) #define SUBIDVALID(msinfo, id) (((int)(id) & 0xff00ffff) == 0x1d000000 && \ ID2IDX(id) < (msinfo)->swms_dispcnt) static struct { fmd_stat_t sw_recv_total; fmd_stat_t sw_recv_match; fmd_stat_t sw_recv_callback; } sw_stats = { { "sw_recv_total", FMD_TYPE_UINT64, "total events received" }, { "sw_recv_match", FMD_TYPE_UINT64, "events matching some subsidiary" }, { "sw_recv_callback", FMD_TYPE_UINT64, "callbacks to all subsidiaries" }, }; #define BUMPSTAT(stat) sw_stats.stat.fmds_value.ui64++ #define BUMPSTATN(stat, n) sw_stats.stat.fmds_value.ui64 += (n) /* * ========================== Event Receipt ================================= * * The fmdo_recv entry point. See which sub de/response agents have a * matching subscription and callback for the first match from each. * The sub de/response agents should dispatch *all* their subscriptions * via their registered dispatch table, including things like list.repaired. */ void sw_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) { struct sw_modspecific *msinfo; int calls = 0; int mod; BUMPSTAT(sw_recv_total); msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); /* * For each sub module that has a matching class pattern call the * registered callback for that sub DE. Only one match per sub module * is allowed (the first match in its table, others are not checked). */ for (mod = 0; mod < msinfo->swms_dispcnt; mod++) { const struct sw_disp *dp; sw_dispfunc_t *dispf = NULL; for (dp = (*msinfo->swms_disptbl)[mod]; dp != NULL && dp->swd_classpat != NULL; dp++) { if (fmd_nvl_class_match(hdl, nvl, dp->swd_classpat)) { dispf = dp->swd_func; break; } } if (dispf != NULL) { calls++; (*dispf)(hdl, ep, nvl, class, dp->swd_arg); } } BUMPSTAT(sw_recv_match); if (calls) BUMPSTATN(sw_recv_callback, calls); } /* * ========================== Timers ======================================== * * A subsidiary can install a timer; it must pass an additional argument * identifying itself so that we can hand off to the appropriate * swsub_timeout function in the fmdo_timeout entry point when the timer fires. */ id_t sw_timer_install(fmd_hdl_t *hdl, id_t who, void *arg, fmd_event_t *ep, hrtime_t hrt) { struct sw_modspecific *msinfo; const struct sw_subinfo **subinfo; const struct sw_subinfo *sip; int slot, chosen = -1; id_t timerid; msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); if (!SUBIDVALID(msinfo, who)) fmd_hdl_abort(hdl, "sw_timer_install: invalid subid %d\n", who); subinfo = *msinfo->swms_subinfo; sip = subinfo[ID2IDX(who)]; if (sip-> swsub_timeout == NULL) fmd_hdl_abort(hdl, "sw_timer_install: no swsub_timeout\n"); /* * Look for a slot. Module entry points are single-threaded * in nature, but if someone installs a timer from a door * service function we're contended. */ (void) pthread_mutex_lock(&msinfo->swms_timerlock); for (slot = 0; slot < SW_TIMER_MAX; slot++) { if (msinfo->swms_timers[slot].swt_state != SW_TMR_INUSE) { chosen = slot; break; } } if (chosen == -1) fmd_hdl_abort(hdl, "timer slots exhausted\n"); msinfo->swms_timers[chosen].swt_state = SW_TMR_INUSE; msinfo->swms_timers[chosen].swt_ownerid = who; msinfo->swms_timers[chosen].swt_timerid = timerid = fmd_timer_install(hdl, arg, ep, hrt); (void) pthread_mutex_unlock(&msinfo->swms_timerlock); return (timerid); } /* * Look for a timer installed by a given subsidiary matching timerid. */ static int subtimer_find(struct sw_modspecific *msinfo, id_t who, id_t timerid) { int slot; for (slot = 0; slot < SW_TIMER_MAX; slot++) { if (msinfo->swms_timers[slot].swt_state == SW_TMR_INUSE && (who == -1 || msinfo->swms_timers[slot].swt_ownerid == who) && msinfo->swms_timers[slot].swt_timerid == timerid) return (slot); } return (-1); } void sw_timer_remove(fmd_hdl_t *hdl, id_t who, id_t timerid) { struct sw_modspecific *msinfo; const struct sw_subinfo **subinfo; const struct sw_subinfo *sip; int slot; msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); if (!SUBIDVALID(msinfo, who)) fmd_hdl_abort(hdl, "sw_timer_remove: invalid subid\n"); subinfo = *msinfo->swms_subinfo; sip = subinfo[ID2IDX(who)]; (void) pthread_mutex_lock(&msinfo->swms_timerlock); if ((slot = subtimer_find(msinfo, who, timerid)) == -1) fmd_hdl_abort(hdl, "sw_timer_remove: timerid %d not found " "for %s\n", timerid, sip->swsub_name); fmd_timer_remove(hdl, timerid); msinfo->swms_timers[slot].swt_state = SW_TMR_RMVD; (void) pthread_mutex_unlock(&msinfo->swms_timerlock); } /* * The fmdo_timeout entry point. */ void sw_timeout(fmd_hdl_t *hdl, id_t timerid, void *arg) { struct sw_modspecific *msinfo; const struct sw_subinfo **subinfo; const struct sw_subinfo *sip; id_t owner; int slot; msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); (void) pthread_mutex_lock(&msinfo->swms_timerlock); if ((slot = subtimer_find(msinfo, -1, timerid)) == -1) fmd_hdl_abort(hdl, "sw_timeout: timerid %d not found\n"); (void) pthread_mutex_unlock(&msinfo->swms_timerlock); owner = msinfo->swms_timers[slot].swt_ownerid; if (!SUBIDVALID(msinfo, owner)) fmd_hdl_abort(hdl, "sw_timeout: invalid subid\n"); subinfo = *msinfo->swms_subinfo; sip = subinfo[ID2IDX(owner)]; sip->swsub_timeout(hdl, timerid, arg); } /* * ========================== sw_subinfo access ============================= */ enum sw_casetype sw_id_to_casetype(fmd_hdl_t *hdl, id_t who) { struct sw_modspecific *msinfo; const struct sw_subinfo **subinfo; const struct sw_subinfo *sip; msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); if (!SUBIDVALID(msinfo, who)) fmd_hdl_abort(hdl, "sw_id_to_casetype: invalid subid %d\n", who); subinfo = *msinfo->swms_subinfo; sip = subinfo[ID2IDX(who)]; if ((sip->swsub_casetype & SW_CASE_NONE) != SW_CASE_NONE) fmd_hdl_abort(hdl, "sw_id_to_casetype: bad case type %d " "for %s\n", sip->swsub_casetype, sip->swsub_name); return (sip->swsub_casetype); } /* * Given a case type lookup the struct sw_subinfo for the subsidiary * that opens cases of that type. */ static const struct sw_subinfo * sw_subinfo_bycase(fmd_hdl_t *hdl, enum sw_casetype type) { struct sw_modspecific *msinfo; const struct sw_subinfo **subinfo; const struct sw_subinfo *sip; int i; msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); subinfo = *msinfo->swms_subinfo; for (i = 0; i < SW_SUB_MAX; i++) { sip = subinfo[i]; if (sip->swsub_casetype == type) return (sip); } return (NULL); } /* * Find the case close function for the given case type; can be NULL. */ swsub_case_close_func_t * sw_sub_case_close_func(fmd_hdl_t *hdl, enum sw_casetype type) { const struct sw_subinfo *sip; if ((sip = sw_subinfo_bycase(hdl, type)) == NULL) fmd_hdl_abort(hdl, "sw_sub_case_close_func: case type " "%d not found\n", type); return (sip->swsub_case_close); } /* * Find the case verify function for the given case type; can be NULL. */ sw_case_vrfy_func_t * sw_sub_case_vrfy_func(fmd_hdl_t *hdl, enum sw_casetype type) { const struct sw_subinfo *sip; if ((sip = sw_subinfo_bycase(hdl, type)) == NULL) fmd_hdl_abort(hdl, "sw_sub_case_vrfy_func: case type " "%d not found\n", type); return (sip->swsub_case_verify); } /* * ========================== Initialization ================================ * * The two modules - software-diagnosis and software-response - call * sw_fmd_init from their _fmd_init entry points. */ static void sw_add_callbacks(fmd_hdl_t *hdl, const char *who, const struct sw_disp *dp, int nelem, struct sw_modspecific *msinfo) { int i; (*msinfo->swms_disptbl)[msinfo->swms_dispcnt++] = dp; if (dp == NULL) return; /* subsidiary failed init */ /* check that the nelem'th entry is the NULL termination */ if (dp[nelem - 1].swd_classpat != NULL || dp[nelem - 1].swd_func != NULL || dp[nelem - 1].swd_arg != NULL) fmd_hdl_abort(hdl, "subsidiary %s dispatch table not NULL-" "terminated\n", who); /* now validate the entries; we allow NULL handlers */ for (i = 0; i < nelem - 1; i++) { if (dp[i].swd_classpat == NULL) fmd_hdl_abort(hdl, "subsidiary %s dispatch table entry " "%d has a NULL pattern or function\n", who, i); } } int sw_fmd_init(fmd_hdl_t *hdl, const fmd_hdl_info_t *hdlinfo, const struct sw_subinfo *(*subsid)[SW_SUB_MAX]) { struct sw_modspecific *msinfo; int i; if (fmd_hdl_register(hdl, FMD_API_VERSION, hdlinfo) != 0) return (0); if (fmd_prop_get_int32(hdl, "enable") != B_TRUE) { fmd_hdl_debug(hdl, "%s disabled though .conf file setting\n", hdlinfo->fmdi_desc); fmd_hdl_unregister(hdl); return (0); } msinfo = fmd_hdl_zalloc(hdl, sizeof (*msinfo), FMD_SLEEP); msinfo->swms_subinfo = subsid; msinfo->swms_disptbl = fmd_hdl_zalloc(hdl, SW_SUB_MAX * sizeof (struct sw_disp *), FMD_SLEEP); (void) pthread_mutex_init(&msinfo->swms_timerlock, NULL); for (i = 0; i < SW_TIMER_MAX; i++) msinfo->swms_timers[i].swt_state = SW_TMR_UNTOUCHED; fmd_hdl_setspecific(hdl, (void *)msinfo); (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (sw_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&sw_stats); /* * Initialize subsidiaries. Each must make any subscription * requests it needs and return a pointer to a NULL-terminated * callback dispatch table and an indication of the number of * entries in that table including the NULL termination entry. */ for (i = 0; i < SW_SUB_MAX; i++) { const struct sw_subinfo *sip = (*subsid)[i]; const struct sw_disp *dp; char dbgbuf[80]; int nelem = -1; int initrslt; if (!sip || sip->swsub_name == NULL) break; initrslt = (*sip->swsub_init)(hdl, IDX2ID(i), &dp, &nelem); (void) snprintf(dbgbuf, sizeof (dbgbuf), "subsidiary %d (id 0x%lx) '%s'", i, IDX2ID(i), sip->swsub_name); switch (initrslt) { case SW_SUB_INIT_SUCCESS: if (dp == NULL || nelem < 1) fmd_hdl_abort(hdl, "%s returned dispatch " "table 0x%p and nelem %d\n", dbgbuf, dp, nelem); fmd_hdl_debug(hdl, "%s initialized\n", dbgbuf); sw_add_callbacks(hdl, sip->swsub_name, dp, nelem, msinfo); break; case SW_SUB_INIT_FAIL_VOLUNTARY: fmd_hdl_debug(hdl, "%s chose not to initialize\n", dbgbuf); sw_add_callbacks(hdl, sip->swsub_name, NULL, -1, msinfo); break; case SW_SUB_INIT_FAIL_ERROR: fmd_hdl_debug(hdl, "%s failed to initialize " "because of an error\n", dbgbuf); sw_add_callbacks(hdl, sip->swsub_name, NULL, -1, msinfo); break; default: fmd_hdl_abort(hdl, "%s returned out-of-range result " "%d\n", dbgbuf, initrslt); break; } } return (1); } void sw_fmd_fini(fmd_hdl_t *hdl) { const struct sw_subinfo **subinfo; struct sw_modspecific *msinfo; int i; msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl); subinfo = *msinfo->swms_subinfo; (void) pthread_mutex_lock(&msinfo->swms_timerlock); for (i = 0; i < SW_TIMER_MAX; i++) { if (msinfo->swms_timers[i].swt_state != SW_TMR_INUSE) continue; fmd_timer_remove(hdl, msinfo->swms_timers[i].swt_timerid); msinfo->swms_timers[i].swt_state = SW_TMR_RMVD; } (void) pthread_mutex_unlock(&msinfo->swms_timerlock); (void) pthread_mutex_destroy(&msinfo->swms_timerlock); for (i = 0; i < msinfo->swms_dispcnt; i++) { const struct sw_subinfo *sip = subinfo[i]; if ((*msinfo->swms_disptbl)[i] == NULL) continue; /* swsub_init did not succeed */ if (sip->swsub_fini != NULL) (*sip->swsub_fini)(hdl); } fmd_hdl_free(hdl, msinfo->swms_disptbl, SW_SUB_MAX * sizeof (struct sw_disp *)); fmd_hdl_setspecific(hdl, NULL); fmd_hdl_free(hdl, msinfo, sizeof (*msinfo)); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # MODULE = software-diagnosis CLASS = common include ../Makefile.com # # Sources for the primary software-diagnosis module # SWDE_SRCS = swde_main.c swde_case.c # # Sources for subsidiary diagnosis "modules" that we host. These should # be listed in ../Makefile.com # SUBDE_SRCS = $(SMF_DE_SRCS) $(PANIC_DE_SRCS) # # All sources for softtware-diagnosis # SRCS = $(SWDE_SRCS) $(CMN_SRCS:%=../%) $(SUBDE_SRCS:%=../%) include ../../../Makefile.plugin CFLAGS += $(INCS) LINTFLAGS += $(INCS) LDLIBS += -L$(ROOTLIB)/fm -ltopo -luutil -luuid -lkstat LDFLAGS += -R/usr/lib/fm CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # # # Configuration for the software-diagnosis diagnosis engine. # # # Dictionaries in use by software-diagnosis. The SMF dictionary *must* # be listed before the SUNOS dictionary so that the smf maintenance # defect is found in SMF instead of SUNOS. # dictionary SMF dictionary SUNOS /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include "../common/sw_impl.h" /* * We maintain a single list of all active cases across all * subsidary diagnosis "modules". We also offer some serialization * services to them. * * To open a new case a subsidiary engine should use swde_case_open * indicating the subsidiary id (from which we lookup the enum sw_casetype) * and, optionally, a pointer to a structure for serialization and its size. * * For each case opened with swde_case_open we maintain an swde_case_t * structure in-core. Embedded in this is the swde_case_data_t with * information we need to keep track of and manage this case - it's * case type, buffer name used for the sub-de-private data (if any) * and the size of the sub-de-private structure. It is this * embedded structure which is serialized as the "casedata" buffer, * while the subsidiary-private structure is serialized into another buffer * "casedata_". * * The subsidiary-private data structure, if any, is required to start * with a uint32_t recording the data structure version. This * version is also specified as an argument to swde_case_open, and * we note it in the "casedata" buffer we write out and require * a match on restore. * * When we unserialize we restore our management structure as well as * the sub-de-private structure. * * Here's how serialization works: * * In swde_case_open we create a case data buffer for the case named * SW_CASE_DATA_BUFNAME. We write the buffer out after filling in the * structure version and recording the type of case this is, and if there * is data for the subsidiary then we call swde_subdata to note the * size and version of that data in the case data buf and then to create * and write the subdata in a buffer named SW_CASE_DATA_BUFNAME_. * * If the subsidiary updates its case data it is required to call * swde_case_data_write. This just calls fmd_buf_write for the subsidiary * buffer name. * * A subsidiary can retrieve its private data buffer for a case using * swde_case_data. This also fills a uint32_t with the version of the * buffer that we have for this subsidiary; if that is an old version * the subsidiary can cast appropriately and/or upgrade the buffer as * below. * * When the host module is reloaded it calls swde_case_init to iterate * through all cases we own. For each we call swde_case_unserialize * which restores our case tracking data and any subsidiary-private * data that our case data notes. We then call swde_case_verify which * calls any registered verify function in the subsidiary owner, and if this * returns 0 the case is closed. * * After initial write, we don't usually have to update the * SW_CASE_DATA_BUFNAME buffer unless the subsidiary changes the size or * version of its private buffer. To do that the subsidiary must call * swde_case_data_upgrade. In that function we destroy the old subsidiary * buffer and, if there is still a subsidiary data structure, create a * new buffer appropriately sized and call swde_subdata to write it out * after updating our case structure with new size etc. Finally we write * out our updated case data structure. */ #define SW_CASE_DATA_BUFNAME "casedata" #define SW_CASE_DATA_VERSION_INITIAL 1 #define SW_CASE_DATA_BUFNAMELEN 18 /* 8 + 1 + 8 + 1 */ typedef struct swde_case_data { uint32_t sc_version; /* buffer structure version */ int32_t sc_type; /* enum sw_casetype */ uint32_t sc_sub_bufvers; /* version expected in subsidiary */ char sc_sub_bufname[SW_CASE_DATA_BUFNAMELEN]; /* subsidiary bufname */ int32_t sc_sub_bufsz; /* subsidiary structure size */ } swde_case_data_t; #define SW_CASE_DATA_VERSION SW_CASE_DATA_VERSION_INITIAL /* * In-core case structure. */ typedef struct swde_case { fmd_case_t *swc_fmdcase; /* fmd case handle */ swde_case_data_t swc_data; /* case data for serialization */ void *swc_subdata; /* subsidiary data for serialization */ } swde_case_t; static void swde_case_associate(fmd_hdl_t *hdl, fmd_case_t *cp, swde_case_t *scp, void *subdata) { scp->swc_fmdcase = cp; scp->swc_subdata = subdata; fmd_case_setspecific(hdl, cp, scp); } static void swde_case_unserialize(fmd_hdl_t *hdl, fmd_case_t *cp) { swde_case_t *scp; swde_case_data_t *datap; void *subdata; size_t sz; scp = fmd_hdl_zalloc(hdl, sizeof (*scp), FMD_SLEEP); datap = &scp->swc_data; fmd_buf_read(hdl, cp, SW_CASE_DATA_BUFNAME, datap, sizeof (*datap)); if (datap->sc_version > SW_CASE_DATA_VERSION_INITIAL) { fmd_hdl_free(hdl, scp, sizeof (*scp)); return; } if ((sz = datap->sc_sub_bufsz) != 0) { subdata = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); fmd_buf_read(hdl, cp, datap->sc_sub_bufname, subdata, sz); if (*((uint32_t *)subdata) != datap->sc_sub_bufvers) { fmd_hdl_abort(hdl, "unserialize: expected subdata " "version %u but received %u\n", datap->sc_sub_bufvers, *((uint32_t *)subdata)); } } swde_case_associate(hdl, cp, scp, subdata); } static void swde_subdata(fmd_hdl_t *hdl, fmd_case_t *cp, enum sw_casetype type, swde_case_t *scp, uint32_t subdata_vers, void *subdata, size_t subdata_sz) { swde_case_data_t *datap = &scp->swc_data; if (*((uint32_t *)subdata) != subdata_vers) fmd_hdl_abort(hdl, "swde_subdata: subdata version " "does not match argument\n"); (void) snprintf(datap->sc_sub_bufname, sizeof (datap->sc_sub_bufname), "%s_%08x", SW_CASE_DATA_BUFNAME, type); datap->sc_sub_bufsz = subdata_sz; datap->sc_sub_bufvers = subdata_vers; fmd_buf_create(hdl, cp, datap->sc_sub_bufname, subdata_sz); fmd_buf_write(hdl, cp, datap->sc_sub_bufname, subdata, subdata_sz); } fmd_case_t * swde_case_open(fmd_hdl_t *hdl, id_t who, char *req_uuid, uint32_t subdata_vers, void *subdata, size_t subdata_sz) { enum sw_casetype ct = sw_id_to_casetype(hdl, who); swde_case_data_t *datap; swde_case_t *scp; fmd_case_t *cp; if (ct == SW_CASE_NONE) fmd_hdl_abort(hdl, "swde_case_open for type SW_CASE_NONE\n"); if (subdata != NULL && subdata_sz <= sizeof (uint32_t) || subdata_sz != 0 && subdata == NULL) fmd_hdl_abort(hdl, "swde_case_open: bad subdata\n", ct); scp = fmd_hdl_zalloc(hdl, sizeof (*scp), FMD_SLEEP); datap = &scp->swc_data; if (req_uuid == NULL) { cp = fmd_case_open(hdl, (void *)scp); } else { cp = fmd_case_open_uuid(hdl, req_uuid, (void *)scp); if (cp == NULL) { fmd_hdl_free(hdl, scp, sizeof (*scp)); return (NULL); } } fmd_buf_create(hdl, cp, SW_CASE_DATA_BUFNAME, sizeof (*datap)); datap->sc_version = SW_CASE_DATA_VERSION_INITIAL; datap->sc_type = ct; if (subdata) swde_subdata(hdl, cp, ct, scp, subdata_vers, subdata, subdata_sz); fmd_buf_write(hdl, cp, SW_CASE_DATA_BUFNAME, datap, sizeof (*datap)); swde_case_associate(hdl, cp, scp, subdata); return (cp); } /* * fmdo_close entry point for software-diagnosis */ void swde_close(fmd_hdl_t *hdl, fmd_case_t *cp) { swde_case_t *scp = fmd_case_getspecific(hdl, cp); swde_case_data_t *datap = &scp->swc_data; swsub_case_close_func_t *closefunc; if ((closefunc = sw_sub_case_close_func(hdl, datap->sc_type)) != NULL) closefunc(hdl, cp); /* * Now that the sub-de has had a chance to clean up, do some ourselves. * Note that we free the sub-de-private subdata structure. */ if (scp->swc_subdata) { fmd_hdl_free(hdl, scp->swc_subdata, datap->sc_sub_bufsz); fmd_buf_destroy(hdl, cp, datap->sc_sub_bufname); } fmd_buf_destroy(hdl, cp, SW_CASE_DATA_BUFNAME); fmd_hdl_free(hdl, scp, sizeof (*scp)); } fmd_case_t * swde_case_first(fmd_hdl_t *hdl, id_t who) { enum sw_casetype ct = sw_id_to_casetype(hdl, who); swde_case_t *scp; fmd_case_t *cp; if (ct == SW_CASE_NONE) fmd_hdl_abort(hdl, "swde_case_first for type SW_CASE_NONE\n"); for (cp = fmd_case_next(hdl, NULL); cp; cp = fmd_case_next(hdl, cp)) { scp = fmd_case_getspecific(hdl, cp); if (scp->swc_data.sc_type == ct) break; } return (cp); } fmd_case_t * swde_case_next(fmd_hdl_t *hdl, fmd_case_t *lastcp) { swde_case_t *scp; fmd_case_t *cp; int ct; if (lastcp == NULL) fmd_hdl_abort(hdl, "swde_case_next called for NULL lastcp\n"); scp = fmd_case_getspecific(hdl, lastcp); ct = scp->swc_data.sc_type; cp = lastcp; while ((cp = fmd_case_next(hdl, cp)) != NULL) { scp = fmd_case_getspecific(hdl, cp); if (scp->swc_data.sc_type == ct) break; } return (cp); } void * swde_case_data(fmd_hdl_t *hdl, fmd_case_t *cp, uint32_t *svp) { swde_case_t *scp = fmd_case_getspecific(hdl, cp); swde_case_data_t *datap = &scp->swc_data; if (svp != NULL && scp->swc_subdata) *svp = datap->sc_sub_bufvers; return (scp->swc_subdata); } void swde_case_data_write(fmd_hdl_t *hdl, fmd_case_t *cp) { swde_case_t *scp = fmd_case_getspecific(hdl, cp); swde_case_data_t *datap = &scp->swc_data; if (scp->swc_subdata == NULL) return; fmd_buf_write(hdl, cp, scp->swc_data.sc_sub_bufname, scp->swc_subdata, datap->sc_sub_bufsz); } void swde_case_data_upgrade(fmd_hdl_t *hdl, fmd_case_t *cp, uint32_t subdata_vers, void *subdata, size_t subdata_sz) { swde_case_t *scp = fmd_case_getspecific(hdl, cp); swde_case_data_t *datap = &scp->swc_data; if (scp->swc_subdata) { fmd_buf_destroy(hdl, cp, datap->sc_sub_bufname); fmd_hdl_free(hdl, scp->swc_subdata, datap->sc_sub_bufsz); scp->swc_subdata = NULL; datap->sc_sub_bufsz = 0; datap->sc_sub_bufname[0] = '\0'; } if (subdata != NULL) { scp->swc_subdata = subdata; swde_subdata(hdl, cp, datap->sc_type, scp, subdata_vers, subdata, subdata_sz); } fmd_buf_write(hdl, scp->swc_fmdcase, SW_CASE_DATA_BUFNAME, datap, sizeof (*datap)); } static void swde_case_verify(fmd_hdl_t *hdl, fmd_case_t *cp) { swde_case_t *scp = fmd_case_getspecific(hdl, cp); swde_case_data_t *datap = &scp->swc_data; sw_case_vrfy_func_t *vrfy_func; if ((vrfy_func = sw_sub_case_vrfy_func(hdl, datap->sc_type)) != NULL) { if (vrfy_func(hdl, cp) == 0) fmd_case_close(hdl, cp); } } void swde_case_init(fmd_hdl_t *hdl) { fmd_case_t *cp; for (cp = fmd_case_next(hdl, NULL); cp; cp = fmd_case_next(hdl, cp)) { swde_case_unserialize(hdl, cp); swde_case_verify(hdl, cp); } } /*ARGSUSED*/ void swde_case_fini(fmd_hdl_t *hdl) { } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include "../common/sw_impl.h" static const fmd_prop_t swde_props[] = { { "enable", FMD_TYPE_BOOL, "true" }, { NULL, 0, NULL } }; static const fmd_hdl_ops_t swde_ops = { sw_recv, /* fmdo_recv - provided by common code */ sw_timeout, /* fmdo_timeout - provided by common code */ swde_close, /* fmdo_close */ NULL, /* fmdo_stats */ NULL, /* fmdo_gc */ NULL, /* fmdo_send */ NULL /* fmdo_topo */ }; const fmd_hdl_info_t swde_info = { "Software Diagnosis engine", "0.1", &swde_ops, swde_props }; /* * Subsidiary diagnosis "modules" that we host. */ static const struct sw_subinfo *subde[SW_SUB_MAX] = { &smf_diag_info, &panic_diag_info }; void _fmd_init(fmd_hdl_t *hdl) { if (sw_fmd_init(hdl, &swde_info, &subde)) swde_case_init(hdl); } void _fmd_fini(fmd_hdl_t *hdl) { swde_case_fini(hdl); sw_fmd_fini(hdl); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # MODULE = software-response CLASS = common include ../Makefile.com SWRP_SRCS = swrp_main.c SUBRP_SRCS = $(SMF_RP_SRCS) SRCS = $(SWRP_SRCS) $(CMN_SRCS:%=../%) $(SUBRP_SRCS:%=../%) include ../../../Makefile.plugin CFLAGS += $(INCS) LINTFLAGS += $(INCS) LDLIBS += -L$(ROOTLIB)/fm -ltopo -lscf LDFLAGS += -R/usr/lib/fm # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # # # Configuration for the software-diagnosis diagnosis engine. # subscribe list.repaired /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include "../common/sw_impl.h" static const fmd_prop_t swrp_props[] = { { "enable", FMD_TYPE_BOOL, "true" }, { NULL, 0, NULL } }; static const fmd_hdl_ops_t swrp_ops = { sw_recv, /* fmdo_recv - provided by common code */ sw_timeout, /* fmdo_timeout */ NULL, /* fmdo_close */ NULL, /* fmdo_stats */ NULL, /* fmdo_gc */ NULL, /* fmdo_send */ NULL /* fmdo_topo */ }; const fmd_hdl_info_t swrp_info = { "Software Response Agent", "0.1", &swrp_ops, swrp_props }; /* * Subsidiary response "modules" that we host. */ static const struct sw_subinfo *subrp[SW_SUB_MAX] = { &smf_response_info, }; void _fmd_init(fmd_hdl_t *hdl) { (void) sw_fmd_init(hdl, &swrp_info, &subrp); } void _fmd_fini(fmd_hdl_t *hdl) { sw_fmd_fini(hdl); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _PANIC_H #define _PANIC_H #ifdef __cplusplus extern "C" { #endif #define SW_SUNOS_PANIC_DETECTED "ireport.os.sunos.panic.dump_pending_on_device" #define SW_SUNOS_PANIC_FAILURE "ireport.os.sunos.panic.savecore_failure" #define SW_SUNOS_PANIC_AVAIL "ireport.os.sunos.panic.dump_available" #define SW_SUNOS_PANIC_DEFECT "defect.sunos.kernel.panic" extern char *sw_panic_fmri2str(fmd_hdl_t *, nvlist_t *); #ifdef __cplusplus } #endif #endif /* _PANIC_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Panic software-diagnosis subsidiary * * We model a system panic as a defect diagnosis in FMA. When a system * panicks, savecore publishes events which we subscribe to here. * * Our driving events are all raised by savecore, run either from * startup of the dumpadm service or interactively at the command line. * The following describes the logic for the handling of these events. * * On reboot after panic we will run savecore as part of the dumpadm * service startup; we run savecore even if savecore is otherwise * disabled (ie dumpadm -n in effect) - we run savecore -c to check for * a valid dump and raise the initial event. * * If savecore (or savecore -c) observes a valid dump pending on the * device, it raises a "dump_pending_on_device" event provided this * was not an FMA-initiated panic (for those we will replay ereports * from the dump device as usual and make a diagnosis from those; we do * not need to open a case for the panic). We subscribe to the * "dump_pending_on_device" event and use that to open a case; we * open a case requesting the same case uuid as the panic dump image * has for the OS instance uuid - if that fails because of a duplicate * uuid then we have already opened a case for this panic so no need * to open another. * * Included in the "dump_pending_on_device" event is an indication of * whether or not dumpadm is enabled. If not (dumpadm -n in effect) * then we do not expect any further events regarding this panic * until such time as the admin runs savecore manually (if ever). * So in this case we solve the case immediately after open. If/when * subsequent events arrive when savecore is run manually, we will toss * them. * * If dumpadm is enabled then savecore, run from dumpadm service startup, * will attempt to process the dump - either to copy it off the dump * device (if saving compressed) or to uncompress it off the dump device. * If this succeeds savecore raises a "dump_available" event which * includes information on the directory it was saved in, the instance * number, image uuid, compressed form or not, and whether the dump * was complete (as per the dumphdr). If the savecore fails for * some reason then it exits and raises a "savecore_failure" event. * These two events are raised even for FMA-initiated panics. * * We subscribe to both the "dump_available" and "savecore_failed" events, * and in the handling thereof we will close the case opened earlier (if * this is not an FMA-initiated panic). On receipt of the initial * "dump_available" event we also arm a timer for +10 minutes if * dumpadm is enabled - if no "dump_available" or "savecore_failed" arrives * in that time we will solve the case on timeout. * * When the timer fires we check whether the initial event for each panic * case was received more than 30 minutes ago; if it was we solve the case * with what we have. If we're still within the waiting period we rearm * for a further 10 minutes. The timer is shared by all cases that we * create, which is why the fire interval is shorter than the maximum time * we are prepared to wait. */ #include #include #include #include #include "../../common/sw.h" #include "panic.h" #define MAX_STRING_LEN 160 static id_t myid; static id_t mytimerid; /* * Our serialization structure type. */ #define SWDE_PANIC_CASEDATA_VERS 1 typedef struct swde_panic_casedata { uint32_t scd_vers; /* must be first member */ uint64_t scd_receive_time; /* when we first knew of this panic */ size_t scd_nvlbufsz; /* size of following buffer */ /* packed attr nvlist follows */ } swde_panic_casedata_t; static struct { fmd_stat_t swde_panic_diagnosed; fmd_stat_t swde_panic_badclass; fmd_stat_t swde_panic_noattr; fmd_stat_t swde_panic_unexpected_fm_panic; fmd_stat_t swde_panic_badattr; fmd_stat_t swde_panic_badfmri; fmd_stat_t swde_panic_noinstance; fmd_stat_t swde_panic_nouuid; fmd_stat_t swde_panic_dupuuid; fmd_stat_t swde_panic_nocase; fmd_stat_t swde_panic_notime; fmd_stat_t swde_panic_nopanicstr; fmd_stat_t swde_panic_nodumpdir; fmd_stat_t swde_panic_nostack; fmd_stat_t swde_panic_incomplete; fmd_stat_t swde_panic_failed; fmd_stat_t swde_panic_basecasedata; fmd_stat_t swde_panic_failsrlz; } swde_panic_stats = { { "swde_panic_diagnosed", FMD_TYPE_UINT64, "panic defects published" }, { "swde_panic_badclass", FMD_TYPE_UINT64, "incorrect event class received" }, { "swde_panic_noattr", FMD_TYPE_UINT64, "malformed event - missing attr nvlist" }, { "swde_panic_unexpected_fm_panic", FMD_TYPE_UINT64, "dump available for an fm_panic()" }, { "swde_panic_badattr", FMD_TYPE_UINT64, "malformed event - invalid attr list" }, { "swde_panic_badfmri", FMD_TYPE_UINT64, "malformed event - fmri2str fails" }, { "swde_panic_noinstance", FMD_TYPE_UINT64, "malformed event - no instance number" }, { "swde_panic_nouuid", FMD_TYPE_UINT64, "malformed event - missing uuid" }, { "swde_panic_dupuuid", FMD_TYPE_UINT64, "duplicate events received" }, { "swde_panic_nocase", FMD_TYPE_UINT64, "case missing for uuid" }, { "swde_panic_notime", FMD_TYPE_UINT64, "missing crash dump time" }, { "swde_panic_nopanicstr", FMD_TYPE_UINT64, "missing panic string" }, { "swde_panic_nodumpdir", FMD_TYPE_UINT64, "missing crashdump save directory" }, { "swde_panic_nostack", FMD_TYPE_UINT64, "missing panic stack" }, { "swde_panic_incomplete", FMD_TYPE_UINT64, "missing panic incomplete" }, { "swde_panic_failed", FMD_TYPE_UINT64, "missing panic failed" }, { "swde_panic_badcasedata", FMD_TYPE_UINT64, "bad case data during timeout" }, { "swde_panic_failsrlz", FMD_TYPE_UINT64, "failures to serialize case data" }, }; #define BUMPSTAT(stat) swde_panic_stats.stat.fmds_value.ui64++ static nvlist_t * panic_sw_fmri(fmd_hdl_t *hdl, char *object) { nvlist_t *fmri; nvlist_t *sw_obj; int err = 0; fmri = fmd_nvl_alloc(hdl, FMD_SLEEP); err |= nvlist_add_uint8(fmri, FM_VERSION, FM_SW_SCHEME_VERSION); err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW); sw_obj = fmd_nvl_alloc(hdl, FMD_SLEEP); err |= nvlist_add_string(sw_obj, FM_FMRI_SW_OBJ_PATH, object); err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_OBJ, sw_obj); nvlist_free(sw_obj); if (!err) return (fmri); else return (0); } static const char *dumpfiles[2] = { "unix.%lld", "vmcore.%lld" }; static const char *dumpfiles_comp[2] = { "vmdump.%lld", NULL}; static void swde_panic_solve(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *attr, fmd_event_t *ep, boolean_t savecore_success) { char *dumpdir, *path, *uuid; nvlist_t *defect, *rsrc; nvpair_t *nvp; int i; /* * Attribute members to include in event-specific defect * payload. Some attributes will not be present for some * cases - e.g., if we timed out and solved the case without * a "dump_available" report. */ const char *toadd[] = { "os-instance-uuid", /* same as case uuid */ "panicstr", /* for initial classification work */ "panicstack", /* for initial classification work */ "crashtime", /* in epoch time */ "panic-time", /* Formatted crash time */ }; if (ep != NULL) fmd_case_add_ereport(hdl, cp, ep); /* * As a temporary solution we create and fmri in the sw scheme * in panic_sw_fmri. This should become a generic fmri constructor * * We need to user a resource FMRI which will have a sufficiently * unique string representation such that fmd will not see * repeated panic diagnoses (all using the same defect class) * as duplicates and discard later cases. We can't actually diagnose * the panic to anything specific (e.g., a path to a module and * function/line etc therein). We could pick on a generic * representative such as /kernel/genunix but that could lead * to misunderstanding. So we choose a path based on * and the OS instance UUID - "/.". * There's no file at that path (*) but no matter. We can't use * /vmdump.N or similar because if savecore is disabled * or failed we don't have any file or instance number. * * (*) Some day it would seem tidier to keep all files to do * with a single crash (unix/vmcore/vmdump, analysis output etc) * in a distinct directory, and /. seems like a good * choice. For compatability we'd symlink into it. So that is * another reason for this choice - some day it may exist! */ (void) nvlist_lookup_string(attr, "dumpdir", &dumpdir); (void) nvlist_lookup_string(attr, "os-instance-uuid", &uuid); path = alloca(strlen(dumpdir) + 1 + 1 + 36 + 1); /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */ (void) sprintf(path, "%s/.%s", dumpdir, uuid); rsrc = panic_sw_fmri(hdl, path); defect = fmd_nvl_create_defect(hdl, SW_SUNOS_PANIC_DEFECT, 100, rsrc, NULL, rsrc); nvlist_free(rsrc); (void) nvlist_add_boolean_value(defect, "savecore-succcess", savecore_success); if (savecore_success) { boolean_t compressed; int64_t instance; const char **pathfmts; char buf[2][32]; int files = 0; char *arr[2]; int i; (void) nvlist_lookup_int64(attr, "instance", &instance); (void) nvlist_lookup_boolean_value(attr, "compressed", &compressed); pathfmts = compressed ? &dumpfiles_comp[0] : &dumpfiles[0]; for (i = 0; i < 2; i++) { if (pathfmts[i] == NULL) { arr[i] = NULL; continue; } (void) snprintf(buf[i], 32, pathfmts[i], instance); arr[i] = buf[i]; files++; } (void) nvlist_add_string(defect, "dump-dir", dumpdir); (void) nvlist_add_string_array(defect, "dump-files", arr, files); } else { char *rsn; if (nvlist_lookup_string(attr, "failure-reason", &rsn) == 0) (void) nvlist_add_string(defect, "failure-reason", rsn); } /* * Not all attributes will necessarily be available - eg if * dumpadm was not enabled there'll be no instance and dumpdir. */ for (i = 0; i < sizeof (toadd) / sizeof (toadd[0]); i++) { if (nvlist_lookup_nvpair(attr, toadd[i], &nvp) == 0) (void) nvlist_add_nvpair(defect, nvp); } fmd_case_add_suspect(hdl, cp, defect); fmd_case_solve(hdl, cp); /* * Close the case. Do no free casedata - framework does that for us * on closure callback. */ fmd_case_close(hdl, cp); BUMPSTAT(swde_panic_diagnosed); } /*ARGSUSED*/ static void swde_panic_timeout(fmd_hdl_t *hdl, id_t timerid, void *data) { fmd_case_t *cp = swde_case_first(hdl, myid); swde_panic_casedata_t *cdp; time_t now = time(NULL); nvlist_t *attr; int remain = 0; uint32_t vers; while (cp != NULL) { cdp = swde_case_data(hdl, cp, &vers); if (vers != SWDE_PANIC_CASEDATA_VERS) fmd_hdl_abort(hdl, "case data version confused\n"); if (now > cdp->scd_receive_time + 30 * 60) { if (nvlist_unpack((char *)cdp + sizeof (*cdp), cdp->scd_nvlbufsz, &attr, 0) == 0) { swde_panic_solve(hdl, cp, attr, NULL, B_FALSE); nvlist_free(attr); } else { BUMPSTAT(swde_panic_basecasedata); fmd_case_close(hdl, cp); } } else { remain++; } cp = swde_case_next(hdl, cp); } if (remain) { mytimerid = sw_timer_install(hdl, myid, NULL, NULL, 10ULL * NANOSEC * 60); } } /* * Our verify entry point is called for each of our open cases during * module load. We must return 0 for the case to be closed by our caller, * or 1 to keep it (or if we have already closed it during this call). */ static int swde_panic_vrfy(fmd_hdl_t *hdl, fmd_case_t *cp) { swde_panic_casedata_t *cdp; time_t now = time(NULL); nvlist_t *attr; uint32_t vers; cdp = swde_case_data(hdl, cp, &vers); if (vers != SWDE_PANIC_CASEDATA_VERS) return (0); /* case will be closed */ if (now > cdp->scd_receive_time + 30 * 60) { if (nvlist_unpack((char *)cdp + sizeof (*cdp), cdp->scd_nvlbufsz, &attr, 0) == 0) { swde_panic_solve(hdl, cp, attr, NULL, B_FALSE); nvlist_free(attr); return (1); /* case already closed */ } else { return (0); /* close case */ } } if (mytimerid != 0) mytimerid = sw_timer_install(hdl, myid, NULL, NULL, 10ULL * NANOSEC * 60); return (1); /* retain case */ } /* * Handler for ireport.os.sunos.panic.dump_pending_on_device. * * A future RFE should try adding a means of avoiding diagnosing repeated * defects on panic loops, which would just add to the mayhem and potentially * log lots of calls through ASR. Panics with similar enough panic * strings and/or stacks should not diagnose to new defects with some * period of time, for example. */ /*ARGSUSED*/ void swde_panic_detected(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, void *arg) { boolean_t fm_panic, expect_savecore; swde_panic_casedata_t *cdp; nvlist_t *attr; fmd_case_t *cp; char *fmribuf; char *uuid; size_t sz; fmd_hdl_debug(hdl, "swde_panic_detected\n"); if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) { BUMPSTAT(swde_panic_noattr); return; } if (nvlist_lookup_string(attr, "os-instance-uuid", &uuid) != 0) { BUMPSTAT(swde_panic_nouuid); return; } fmd_hdl_debug(hdl, "swde_panic_detected: OS instance %s\n", uuid); if (nvlist_lookup_boolean_value(attr, "fm-panic", &fm_panic) != 0 || fm_panic == B_TRUE) { BUMPSTAT(swde_panic_unexpected_fm_panic); return; } /* * Prepare serialization data to be associated with a new * case. Our serialization data consists of a swde_panic_casedata_t * structure followed by a packed nvlist of the attributes of * the initial event. */ if (nvlist_size(attr, &sz, NV_ENCODE_NATIVE) != 0) { BUMPSTAT(swde_panic_failsrlz); return; } cdp = fmd_hdl_zalloc(hdl, sizeof (*cdp) + sz, FMD_SLEEP); fmribuf = (char *)cdp + sizeof (*cdp); cdp->scd_vers = SWDE_PANIC_CASEDATA_VERS; cdp->scd_receive_time = time(NULL); cdp->scd_nvlbufsz = sz; /* * Open a case with UUID matching the the panicking kernel, add this * event to the case. */ if ((cp = swde_case_open(hdl, myid, uuid, SWDE_PANIC_CASEDATA_VERS, cdp, sizeof (*cdp) + sz)) == NULL) { BUMPSTAT(swde_panic_dupuuid); fmd_hdl_debug(hdl, "swde_case_open returned NULL - dup?\n"); fmd_hdl_free(hdl, cdp, sizeof (*cdp) + sz); return; } fmd_case_setprincipal(hdl, cp, ep); if (nvlist_lookup_boolean_value(attr, "will-attempt-savecore", &expect_savecore) != 0 || expect_savecore == B_FALSE) { fmd_hdl_debug(hdl, "savecore not being attempted - " "solve now\n"); swde_panic_solve(hdl, cp, attr, ep, B_FALSE); return; } /* * We expect to see either a "dump_available" or a "savecore_failed" * event before too long. In case that never shows up, for whatever * reason, we want to be able to solve the case anyway. */ fmd_case_add_ereport(hdl, cp, ep); (void) nvlist_pack(attr, &fmribuf, &sz, NV_ENCODE_NATIVE, 0); swde_case_data_write(hdl, cp); if (mytimerid == 0) { mytimerid = sw_timer_install(hdl, myid, NULL, ep, 10ULL * NANOSEC * 60); fmd_hdl_debug(hdl, "armed timer\n"); } else { fmd_hdl_debug(hdl, "timer already armed\n"); } } /* * savecore has now run and saved a crash dump to the filesystem. It is * either a compressed dump (vmdump.n) or uncompressed {unix.n, vmcore.n} * Savecore has raised an ireport to say the dump is there. */ /*ARGSUSED*/ void swde_panic_savecore_done(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, void *arg) { boolean_t savecore_success = (arg != NULL); boolean_t fm_panic; nvlist_t *attr; fmd_case_t *cp; char *uuid; fmd_hdl_debug(hdl, "savecore_done (%s)\n", savecore_success ? "success" : "fail"); if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) { BUMPSTAT(swde_panic_noattr); return; } if (nvlist_lookup_boolean_value(attr, "fm-panic", &fm_panic) != 0 || fm_panic == B_TRUE) { return; /* not expected, but just in case */ } if (nvlist_lookup_string(attr, "os-instance-uuid", &uuid) != 0) { BUMPSTAT(swde_panic_nouuid); return; } /* * Find the case related to the panicking kernel; our cases have * the same uuid as the crashed OS image. */ cp = fmd_case_uulookup(hdl, uuid); if (!cp) { /* Unable to find the case. */ fmd_hdl_debug(hdl, "savecore_done: can't find case for " "image %s\n", uuid); BUMPSTAT(swde_panic_nocase); return; } fmd_hdl_debug(hdl, "savecore_done: solving case %s\n", uuid); swde_panic_solve(hdl, cp, attr, ep, savecore_success); } const struct sw_disp swde_panic_disp[] = { { SW_SUNOS_PANIC_DETECTED, swde_panic_detected, NULL }, { SW_SUNOS_PANIC_AVAIL, swde_panic_savecore_done, (void *)1 }, { SW_SUNOS_PANIC_FAILURE, swde_panic_savecore_done, NULL }, /* * Something has to subscribe to every fault * or defect diagnosed in fmd. We do that here, but throw it away. */ { SW_SUNOS_PANIC_DEFECT, NULL, NULL }, { NULL, NULL, NULL } }; /*ARGSUSED*/ int swde_panic_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp, int *nelemp) { myid = id; if (getzoneid() != GLOBAL_ZONEID) return (SW_SUB_INIT_FAIL_VOLUNTARY); (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (swde_panic_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&swde_panic_stats); fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_DETECTED); fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_FAILURE); fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_AVAIL); *dpp = &swde_panic_disp[0]; *nelemp = sizeof (swde_panic_disp) / sizeof (swde_panic_disp[0]); return (SW_SUB_INIT_SUCCESS); } void swde_panic_fini(fmd_hdl_t *hdl) { if (mytimerid) sw_timer_remove(hdl, myid, mytimerid); } const struct sw_subinfo panic_diag_info = { "panic diagnosis", /* swsub_name */ SW_CASE_PANIC, /* swsub_casetype */ swde_panic_init, /* swsub_init */ swde_panic_fini, /* swsub_fini */ swde_panic_timeout, /* swsub_timeout */ NULL, /* swsub_case_close */ swde_panic_vrfy, /* swsub_case_vrfy */ }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SMF_H #define _SMF_H #ifdef __cplusplus extern "C" { #endif #define TRANCLASS(leaf) "ireport.os.smf.state-transition." leaf #define SW_SMF_MAINT_DEFECT "defect.sunos.smf.svc.maintenance" extern char *sw_smf_svcfmri2str(fmd_hdl_t *, nvlist_t *); extern char *sw_smf_svcfmri2shortstr(fmd_hdl_t *, nvlist_t *); #ifdef __cplusplus } #endif #endif /* _SMF_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * SMF software-diagnosis subsidiary * * We model service instances in maintenance state as a defect diagnosis * in FMA. When an instance transitions to maintenance state the SMF * graph engine publishes an event which we subscribe to here, and diagnose * a corresponding defect. * * We always solve a case immediately after opening it. But we leave the * case close action to the response agent which needs to cache case UUIDs. * So in the normal case, where software-response is loaded and operational, * our cases will transition to CLOSED state moments after we solve them. * But if fmd restarts in the interim or if software-response is not loaded * then our cases may hang around in SOLVED state for a while, which means * we could iterate over them on receipt of new events. But we don't - * we blindly solve a new case for every new maintenance event received, * and leave it to the fmd duplicate detection and history-based diagnosis * logic to do the right thing. * * Our sibling SMF response subsidiary propogates fmadm-initiated repairs * into SMF, and svcadm-initiated clears back into FMA. In both cases * the case is moved on to the RESOLVED state, even if fmd is unable to * verify that the service is out of maintenance state (i.e., no longer * isolated). If the service immediately re-enters maintenance state then * we diagnose a fresh case. The history-based diagnosis changes in fmd * "do the right thing" and avoid throwing away new cases as duplicates * of old ones hanging around in the "resolved but not all usable again" * state. */ #include #include #include #include "../../common/sw.h" #include "smf.h" static id_t myid; static struct { fmd_stat_t swde_smf_diagnosed; fmd_stat_t swde_smf_bad_class; fmd_stat_t swde_smf_no_attr; fmd_stat_t swde_smf_bad_attr; fmd_stat_t swde_smf_bad_fmri; fmd_stat_t swde_smf_no_uuid; fmd_stat_t swde_smf_no_reason_short; fmd_stat_t swde_smf_no_reason_long; fmd_stat_t swde_smf_no_svcname; fmd_stat_t swde_smf_admin_maint_drop; fmd_stat_t swde_smf_bad_nvlist_pack; fmd_stat_t swde_smf_dupuuid; } swde_smf_stats = { { "swde_smf_diagnosed", FMD_TYPE_UINT64, "maintenance state defects published" }, { "swde_smf_bad_class", FMD_TYPE_UINT64, "incorrect event class received" }, { "swde_smf_no_attr", FMD_TYPE_UINT64, "malformed event - missing attr nvlist" }, { "swde_smf_bad_attr", FMD_TYPE_UINT64, "malformed event - invalid attr list" }, { "swde_smf_bad_fmri", FMD_TYPE_UINT64, "malformed event - fmri2str fails" }, { "swde_smf_no_uuid", FMD_TYPE_UINT64, "malformed event - missing uuid" }, { "swde_smf_no_reason_short", FMD_TYPE_UINT64, "SMF transition event had no reason-short" }, { "swde_smf_no_reason_long", FMD_TYPE_UINT64, "SMF transition event had no reason-long" }, { "swde_smf_no_svcname", FMD_TYPE_UINT64, "SMF transition event had no svc-string" }, { "swde_smf_admin_maint_drop", FMD_TYPE_UINT64, "maintenance transitions requested by admin - no diagnosis" }, { "swde_smf_bad_nvlist_pack", FMD_TYPE_UINT64, "failed nvlist_size or nvlist_pack" }, { "swde_smf_dupuuid", FMD_TYPE_UINT64, "duplicate events received" }, }; #define SWDE_SMF_CASEDATA_VERS 1 typedef struct swde_smf_casedata { uint32_t scd_vers; /* must be first member */ size_t scd_nvlbufsz; /* size of following buffer */ /* packed fmri nvlist follows */ } swde_smf_casedata_t; #define BUMPSTAT(stat) swde_smf_stats.stat.fmds_value.ui64++ /*ARGSUSED*/ void swde_smf_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, void *arg) { char *rsn = NULL, *rsnl = NULL, *svcname = NULL; nvlist_t *attr, *svcfmri, *defect; swde_smf_casedata_t *cdp; fmd_case_t *cp; char *fmribuf; char *uuid; size_t sz; if (!fmd_nvl_class_match(hdl, nvl, TRANCLASS("maintenance"))) { BUMPSTAT(swde_smf_bad_class); return; } if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) { BUMPSTAT(swde_smf_no_attr); return; } if (nvlist_lookup_string(nvl, FM_IREPORT_UUID, &uuid) != 0) { BUMPSTAT(swde_smf_no_uuid); return; } if (nvlist_lookup_nvlist(attr, "svc", &svcfmri) != 0) { BUMPSTAT(swde_smf_bad_attr); return; } if (nvlist_lookup_string(attr, "reason-short", &rsn) != 0) { BUMPSTAT(swde_smf_no_reason_short); return; } if (nvlist_lookup_string(attr, "reason-long", &rsnl) != 0) { BUMPSTAT(swde_smf_no_reason_long); return; } if (nvlist_lookup_string(attr, "svc-string", &svcname) != 0) { BUMPSTAT(swde_smf_no_svcname); return; } if (strcmp(rsn, "administrative_request") == 0) { BUMPSTAT(swde_smf_admin_maint_drop); return; } /* * Our case checkpoint data, version 1. */ if (nvlist_size(svcfmri, &sz, NV_ENCODE_NATIVE) != 0) { BUMPSTAT(swde_smf_bad_nvlist_pack); return; } cdp = fmd_hdl_zalloc(hdl, sizeof (*cdp) + sz, FMD_SLEEP); cdp->scd_vers = SWDE_SMF_CASEDATA_VERS; fmribuf = (char *)cdp + sizeof (*cdp); cdp->scd_nvlbufsz = sz; (void) nvlist_pack(svcfmri, &fmribuf, &sz, NV_ENCODE_NATIVE, 0); /* * Open a case with UUID matching the originating event, and no * associated serialization data. Create a defect and add it to * the case, and link the originating event to the case. This * call will return NULL if a case with the requested UUID already * exists, which would mean we are processing an event twice so * we can discard. */ if ((cp = swde_case_open(hdl, myid, uuid, SWDE_SMF_CASEDATA_VERS, (void *)cdp, sizeof (*cdp) + sz)) == NULL) { BUMPSTAT(swde_smf_dupuuid); fmd_hdl_free(hdl, cdp, sizeof (*cdp) + sz); return; } defect = fmd_nvl_create_defect(hdl, SW_SMF_MAINT_DEFECT, 100, svcfmri, NULL, svcfmri); if (rsn != NULL) (void) nvlist_add_string(defect, "reason-short", rsn); if (rsnl != NULL) (void) nvlist_add_string(defect, "reason-long", rsnl); if (svcname != NULL) (void) nvlist_add_string(defect, "svc-string", svcname); fmd_case_add_suspect(hdl, cp, defect); fmd_case_add_ereport(hdl, cp, ep); /* * Now solve the case, and immediately close it. Although the * resource is already isolated (SMF put it in maintenance state) * we do not immediately close the case here - our sibling response * logic will do that after caching the case UUID. */ fmd_case_solve(hdl, cp); BUMPSTAT(swde_smf_diagnosed); } /* * In the normal course of events we keep in sync with SMF through the * maintenance enter/clear events it raises. Even if a maintenance * state is cleared using svcadm while fmd is not running, the event * will pend and be consumed when fmd does start and we'll close the * case (in the response agent). * * But is is possible for discontinuities to produce some confusion: * * - if an instance is in maintenance state (and so shown in svcs -x * and fmadm faulty output) at the time we clone a new boot * environment then when we boot the new BE we can be out of * sync if the instance is cleared when we boot there * * - meddling with /var/fm state - eg manual clear of files there, * or restore of old state * * So as an extra guard we have a case verify function which is called * at fmd restart (module load for software-diagnosis). We must * return 0 to close the case, non-zero to retain it. */ int swde_smf_vrfy(fmd_hdl_t *hdl, fmd_case_t *cp) { swde_smf_casedata_t *cdp; nvlist_t *svcfmri; uint32_t v; int rv; cdp = swde_case_data(hdl, cp, &v); if (cdp == NULL || v != 1) return (0); /* bad or damaged - just close */ if (nvlist_unpack((char *)cdp + sizeof (*cdp), cdp->scd_nvlbufsz, &svcfmri, 0) != 0) return (0); /* ditto */ switch (fmd_nvl_fmri_service_state(hdl, svcfmri)) { case FMD_SERVICE_STATE_UNUSABLE: /* * Keep case iff in maintenance state */ rv = 1; break; default: /* * Discard the case for all other states - cleared, * service no longer exists, ... whatever. */ rv = 0; break; } nvlist_free(svcfmri); return (rv); } const struct sw_disp swde_smf_disp[] = { { TRANCLASS("maintenance"), swde_smf_recv, NULL }, { NULL, NULL, NULL } }; /*ARGSUSED*/ int swde_smf_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp, int *nelemp) { myid = id; (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (swde_smf_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&swde_smf_stats); fmd_hdl_subscribe(hdl, TRANCLASS("maintenance")); *dpp = &swde_smf_disp[0]; *nelemp = sizeof (swde_smf_disp) / sizeof (swde_smf_disp[0]); return (SW_SUB_INIT_SUCCESS); } const struct sw_subinfo smf_diag_info = { "smf diagnosis", /* swsub_name */ SW_CASE_SMF, /* swsub_casetype */ swde_smf_init, /* swsub_init */ NULL, /* swsub_fini */ NULL, /* swsub_timeout */ NULL, /* swsub_case_close */ swde_smf_vrfy, /* swsub_case_vrfy */ }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * SMF software-response subsidiary */ #include #include #include #include #include #include "../../common/sw.h" #include "smf.h" static struct { fmd_stat_t swrp_smf_repairs; fmd_stat_t swrp_smf_clears; fmd_stat_t swrp_smf_closed; fmd_stat_t swrp_smf_wrongclass; fmd_stat_t swrp_smf_badlist; fmd_stat_t swrp_smf_badresource; fmd_stat_t swrp_smf_badclrevent; fmd_stat_t swrp_smf_noloop; fmd_stat_t swrp_smf_suppressed; fmd_stat_t swrp_smf_cachefull; } swrp_smf_stats = { { "swrp_smf_repairs", FMD_TYPE_UINT64, "repair events received for propogation to SMF" }, { "swrp_smf_clears", FMD_TYPE_UINT64, "notifications from SMF of exiting maint state" }, { "swrp_smf_closed", FMD_TYPE_UINT64, "cases closed" }, { "swrp_smf_wrongclass", FMD_TYPE_UINT64, "unexpected event class received" }, { "swrp_smf_badlist", FMD_TYPE_UINT64, "list event with invalid structure" }, { "swrp_smf_badresource", FMD_TYPE_UINT64, "list.repaired with smf fault but bad svc fmri" }, { "swrp_smf_badclrevent", FMD_TYPE_UINT64, "maint clear event from SMF malformed" }, { "swrp_smf_noloop", FMD_TYPE_UINT64, "avoidance of smf->fmd->smf repairs propogations" }, { "swrp_smf_suppressed", FMD_TYPE_UINT64, "not propogated to smf because no longer in maint" }, { "swrp_smf_cachefull", FMD_TYPE_UINT64, "uuid cache full" }, }; #define BUMPSTAT(stat) swrp_smf_stats.stat.fmds_value.ui64++ #define CACHE_NENT_INC 16 #define CACHE_NENT_MAX 128 struct smf_uuid_cache_ent { char uuid[37]; char fmristr[90]; uint8_t mark; }; #define CACHE_VERSION 1 struct smf_uuid_cache { uint32_t version; /* Version */ uint32_t nentries; /* Real size of array below */ struct smf_uuid_cache_ent entry[1]; /* Cache entries */ }; static struct smf_uuid_cache *uuid_cache; #define UUID_CACHE_BUFNAME "uuid_cache" static void uuid_cache_grow(fmd_hdl_t *hdl) { struct smf_uuid_cache *newcache; size_t newsz; uint32_t n; n = (uuid_cache == NULL ? 0 : uuid_cache->nentries) + CACHE_NENT_INC; newsz = sizeof (struct smf_uuid_cache) + (n - 1) * sizeof (struct smf_uuid_cache_ent); newcache = fmd_hdl_zalloc(hdl, newsz, FMD_SLEEP); newcache->version = CACHE_VERSION; newcache->nentries = n; if (uuid_cache != NULL) { uint32_t oldn = uuid_cache->nentries; size_t oldsz = sizeof (struct smf_uuid_cache) + (oldn - 1) * sizeof (struct smf_uuid_cache_ent); bcopy(&uuid_cache->entry[0], &newcache->entry[0], oldsz); fmd_hdl_free(hdl, uuid_cache, oldsz); fmd_buf_destroy(hdl, NULL, UUID_CACHE_BUFNAME); } uuid_cache = newcache; fmd_buf_create(hdl, NULL, UUID_CACHE_BUFNAME, newsz); } static void uuid_cache_persist(fmd_hdl_t *hdl) { size_t sz = sizeof (struct smf_uuid_cache) + (uuid_cache->nentries - 1) * sizeof (struct smf_uuid_cache_ent); fmd_buf_write(hdl, NULL, UUID_CACHE_BUFNAME, uuid_cache, sz); } /* * Garbage-collect the uuid cache. Any cases that are already resolved * we do not need an entry for. If a case is not resolved but the * service involved in that case is no longer in maintenance state * then we've lost sync somehow, so repair the asru (which will * also resolve the case). */ static void uuid_cache_gc(fmd_hdl_t *hdl) { struct smf_uuid_cache_ent *entp; topo_hdl_t *thp = NULL; nvlist_t *svcfmri; char *svcname; int err, i; for (i = 0; i < uuid_cache->nentries; i++) { entp = &uuid_cache->entry[i]; if (entp->uuid[0] == '\0') continue; if (fmd_case_uuisresolved(hdl, entp->uuid)) { bzero(entp->uuid, sizeof (entp->uuid)); bzero(entp->fmristr, sizeof (entp->fmristr)); entp->mark = 0; } else { if (thp == NULL) thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION); if (topo_fmri_str2nvl(thp, entp->fmristr, &svcfmri, &err) != 0) { fmd_hdl_error(hdl, "str2nvl failed for %s\n", entp->fmristr); continue; } if (fmd_nvl_fmri_service_state(hdl, svcfmri) != FMD_SERVICE_STATE_UNUSABLE) { svcname = sw_smf_svcfmri2shortstr(hdl, svcfmri); (void) fmd_repair_asru(hdl, entp->fmristr); fmd_hdl_strfree(hdl, svcname); } nvlist_free(svcfmri); } } if (thp) fmd_hdl_topo_rele(hdl, thp); uuid_cache_persist(hdl); } static void uuid_cache_restore(fmd_hdl_t *hdl) { size_t sz = fmd_buf_size(hdl, NULL, UUID_CACHE_BUFNAME); if (sz == 0) return; uuid_cache = fmd_hdl_alloc(hdl, sz, FMD_SLEEP); fmd_buf_read(hdl, NULL, UUID_CACHE_BUFNAME, uuid_cache, sz); /* * Garbage collect now, not just for tidiness but also to help * fmd and smf state stay in sync at module startup. */ uuid_cache_gc(hdl); } /* * Add the UUID of an SMF maintenance defect case to our cache and * record the associated full svc FMRI string for the case. */ static void swrp_smf_cache_add(fmd_hdl_t *hdl, char *uuid, char *fmristr) { struct smf_uuid_cache_ent *entp = NULL; int gced = 0; int i; if (uuid_cache == NULL) uuid_cache_grow(hdl); /* * If we somehow already have an entry for this uuid then * return leaving it undisturbed. */ for (i = 0; i < uuid_cache->nentries; i++) { if (strcmp(uuid, uuid_cache->entry[i].uuid) == 0) return; } scan: for (i = 0; i < uuid_cache->nentries; i++) { if (uuid_cache->entry[i].uuid[0] == '\0') { entp = &uuid_cache->entry[i]; break; } } if (entp == NULL) { uint32_t oldn = uuid_cache->nentries; /* * Before growing the cache we try again after first * garbage-collecting the existing cache for any cases * that are confirmed as resolved. */ if (!gced) { uuid_cache_gc(hdl); gced = 1; goto scan; } if (oldn < CACHE_NENT_MAX) { uuid_cache_grow(hdl); entp = &uuid_cache->entry[oldn]; } else { BUMPSTAT(swrp_smf_cachefull); return; } } (void) strncpy(entp->uuid, uuid, sizeof (entp->uuid)); (void) strncpy(entp->fmristr, fmristr, sizeof (entp->fmristr)); uuid_cache_persist(hdl); } /* * Mark cache entry/entries as resolved - if they match in either uuid * (if not NULL) or fmristr (if not NULL) mark as resolved. Return 1 iff * an entry that matched on uuid was already marked, otherwise (entry * matched on either, matched on uuid but not marked, not found). */ static int swrp_smf_cache_mark(fmd_hdl_t *hdl, char *uuid, char *fmristr) { int dirty = 0; int rv = 0; int i; if (uuid_cache == NULL) return (0); for (i = 0; i < uuid_cache->nentries; i++) { struct smf_uuid_cache_ent *entp = &uuid_cache->entry[i]; if (entp->uuid[0] == '\0') continue; if (uuid && strcmp(uuid, entp->uuid) == 0) { if (entp->mark) rv = 1; entp->mark = 1; dirty++; } else if (fmristr && strcmp(fmristr, entp->fmristr) == 0) { entp->mark = 1; dirty++; } } if (dirty) uuid_cache_persist(hdl); return (rv); } /* * We will receive list events for cases we are not interested in. Test * that this list has exactly one suspect and that it matches the maintenance * defect. Return the defect to the caller in the second argument, * and the defect resource element in the third arg. */ static int suspect_is_maint_defect(fmd_hdl_t *hdl, nvlist_t *nvl, nvlist_t **defectnvl, nvlist_t **rsrcnvl) { nvlist_t **faults; uint_t nfaults; if (nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &faults, &nfaults) != 0) { BUMPSTAT(swrp_smf_badlist); return (0); } if (nfaults != 1 || !fmd_nvl_class_match(hdl, faults[0], SW_SMF_MAINT_DEFECT)) return (0); if (nvlist_lookup_nvlist(faults[0], FM_FAULT_RESOURCE, rsrcnvl) != 0) { BUMPSTAT(swrp_smf_badlist); return (0); } *defectnvl = faults[0]; return (1); } /* * Received newly-diagnosed list.suspect events that are for the * maintenane defect we diagnose. Close the case (the resource was already * isolated by SMF) after cachng the case UUID. */ /*ARGSUSED*/ static void swrp_smf_cacheuuid(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, void *arg) { nvlist_t *defect, *rsrc; char *fmristr, *uuid; if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0) { BUMPSTAT(swrp_smf_badlist); return; } if (!suspect_is_maint_defect(hdl, nvl, &defect, &rsrc)) return; if ((fmristr = sw_smf_svcfmri2str(hdl, rsrc)) == NULL) { BUMPSTAT(swrp_smf_badlist); return; } swrp_smf_cache_add(hdl, uuid, fmristr); fmd_hdl_strfree(hdl, fmristr); if (!fmd_case_uuclosed(hdl, uuid)) { fmd_case_uuclose(hdl, uuid); BUMPSTAT(swrp_smf_closed); } } /*ARGSUSED*/ static void swrp_smf2fmd(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, void *arg) { nvlist_t *attr, *fmri; char *fromstate; char *fmristr; if (!fmd_nvl_class_match(hdl, nvl, TRANCLASS("*"))) { BUMPSTAT(swrp_smf_wrongclass); return; } if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0 || nvlist_lookup_string(attr, "from-state", &fromstate) != 0) { BUMPSTAT(swrp_smf_badclrevent); return; } /* * Filter those not describing a transition out of maintenance. */ if (strcmp(fromstate, "maintenance") != 0) return; if (nvlist_lookup_nvlist(attr, "svc", &fmri) != 0) { BUMPSTAT(swrp_smf_badclrevent); return; } if ((fmristr = sw_smf_svcfmri2str(hdl, fmri)) == NULL) { BUMPSTAT(swrp_smf_badclrevent); return; } /* * Mark any UUID for a case against this service as resolved * in our cache. When we fmd_repair_asru below fmd will emit * a list.repaired as a result, and our handling of that event * must not propogate the repair towards SMF (since the repair * was initiated via SMF itself and not via fmadm). */ (void) swrp_smf_cache_mark(hdl, NULL, fmristr); (void) fmd_repair_asru(hdl, fmristr); fmd_hdl_strfree(hdl, fmristr); BUMPSTAT(swrp_smf_clears); } /*ARGSUSED*/ static void swrp_fmd2smf(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, void *arg) { char *fmristr, *shrtfmristr; nvlist_t *defect, *rsrc; char *uuid; int already; if (strcmp(class, FM_LIST_REPAIRED_CLASS) != 0) { BUMPSTAT(swrp_smf_wrongclass); return; } if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0) { BUMPSTAT(swrp_smf_badlist); return; } if (!suspect_is_maint_defect(hdl, nvl, &defect, &rsrc)) return; if ((fmristr = sw_smf_svcfmri2str(hdl, rsrc)) == NULL) { BUMPSTAT(swrp_smf_badresource); return; } already = swrp_smf_cache_mark(hdl, uuid, fmristr); fmd_hdl_strfree(hdl, fmristr); /* * If the cache already had a marked entry for this UUID then * this is a list.repaired arising from a SMF-initiated maintenance * clear (propogated with fmd_repair_asru above which then results * in a list.repaired) and so we should not propogate the repair * back towards SMF. But do still force the case to RESOLVED state in * case fmd is unable to confirm the service no longer in maintenance * state (it may have failed again) so that a new case can be opened. */ fmd_case_uuresolved(hdl, uuid); if (already) { BUMPSTAT(swrp_smf_noloop); return; } /* * Only propogate to SMF if we can see that service still * in maintenance state. We're not synchronized with SMF * and this state could change at any time, but if we can * see it's not in maintenance state then things are obviously * moving (e.g., external svcadm active) so we don't poke * at SMF otherwise we confuse things or duplicate operations. */ if (fmd_nvl_fmri_service_state(hdl, rsrc) == FMD_SERVICE_STATE_UNUSABLE) { shrtfmristr = sw_smf_svcfmri2shortstr(hdl, rsrc); if (shrtfmristr != NULL) { (void) smf_restore_instance(shrtfmristr); fmd_hdl_strfree(hdl, shrtfmristr); BUMPSTAT(swrp_smf_repairs); } else { BUMPSTAT(swrp_smf_badresource); } } else { BUMPSTAT(swrp_smf_suppressed); } } const struct sw_disp swrp_smf_disp[] = { { TRANCLASS("*"), swrp_smf2fmd, NULL }, { FM_LIST_SUSPECT_CLASS, swrp_smf_cacheuuid, NULL }, { FM_LIST_REPAIRED_CLASS, swrp_fmd2smf, NULL }, { NULL, NULL, NULL } }; /*ARGSUSED*/ int swrp_smf_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp, int *nelemp) { (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (swrp_smf_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&swrp_smf_stats); uuid_cache_restore(hdl); /* * We need to subscribe to all SMF transition class events because * we need to look inside the payload to see which events indicate * a transition out of maintenance state. */ fmd_hdl_subscribe(hdl, TRANCLASS("*")); /* * Subscribe to the defect class diagnosed for maintenance events. * The module will then receive list.suspect events including * these defects, and in our dispatch table above we list routing * for list.suspect. */ fmd_hdl_subscribe(hdl, SW_SMF_MAINT_DEFECT); *dpp = &swrp_smf_disp[0]; *nelemp = sizeof (swrp_smf_disp) / sizeof (swrp_smf_disp[0]); return (SW_SUB_INIT_SUCCESS); } /*ARGSUSED*/ void swrp_smf_fini(fmd_hdl_t *hdl) { } const struct sw_subinfo smf_response_info = { "smf repair", /* swsub_name */ SW_CASE_NONE, /* swsub_casetype */ swrp_smf_init, /* swsub_init */ swrp_smf_fini, /* swsub_fini */ NULL, /* swsub_timeout */ NULL, /* swsub_case_close */ NULL, /* swsub_case_vrfy */ }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * SMF software diagnosis engine components. */ #include #include #include "../../common/sw.h" #include "smf.h" /* * Given a "svc' scheme FMRI in nvlist form, produce a string form * of the FMRI (with no short-hand). */ char * sw_smf_svcfmri2str(fmd_hdl_t *hdl, nvlist_t *fmri) { char *fmristr = NULL; topo_hdl_t *thp; char *topostr; int err; thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION); if (topo_fmri_nvl2str(thp, fmri, &topostr, &err) == 0) { fmristr = fmd_hdl_strdup(hdl, (const char *)topostr, FMD_SLEEP); topo_hdl_strfree(thp, topostr); } fmd_hdl_topo_rele(hdl, thp); return (fmristr); /* caller must fmd_hdl_strfree */ } /* * Given a "svc" scheme FMRI in nvlist form, produce a short-hand form * string FMRI "svc:/..." as generally used in SMF cmdline output. */ char * sw_smf_svcfmri2shortstr(fmd_hdl_t *hdl, nvlist_t *fmri) { char *name, *inst, *bufp, *fullname; size_t len; if (nvlist_lookup_string(fmri, FM_FMRI_SVC_NAME, &name) != 0 || nvlist_lookup_string(fmri, FM_FMRI_SVC_INSTANCE, &inst) != 0) return (NULL); len = strlen(name) + strlen(inst) + 8; bufp = fmd_hdl_alloc(hdl, len, FMD_SLEEP); (void) snprintf(bufp, len, "svc:/%s:%s", name, inst); fullname = fmd_hdl_strdup(hdl, bufp, FMD_SLEEP); fmd_hdl_free(hdl, bufp, len); return (fullname); /* caller must fmd_hdl_strfree */ }