# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # Hammerhead: amd64-only SUBDIRS = $(MACH64) include ../Makefile.subdirs check : TARGET += check check : $(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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2015 RackTop Systems. # Copyright 2019, Joyent, Inc. # .KEEP_STATE: SRCS += fmd.c \ fmd_api.c \ fmd_alloc.c \ fmd_asru.c \ fmd_buf.c \ fmd_builtin.c \ fmd_case.c \ fmd_ckpt.c \ fmd_conf.c \ fmd_ctl.c \ fmd_dispq.c \ fmd_dr.c \ fmd_error.c \ fmd_event.c \ fmd_eventq.c \ fmd_fmri.c \ fmd_idspace.c \ fmd_list.c \ fmd_log.c \ fmd_main.c \ fmd_module.c \ fmd_nv.c \ fmd_proc.c \ fmd_protocol.c \ fmd_rpc.c \ fmd_rpc_adm.c \ fmd_rpc_api.c \ fmd_rtld.c \ fmd_scheme.c \ fmd_self.c \ fmd_serd.c \ fmd_string.c \ fmd_subr.c \ fmd_svc_adm.c \ fmd_svc_api.c \ fmd_sysevent.c \ fmd_thread.c \ fmd_time.c \ fmd_timerq.c \ fmd_topo.c \ fmd_trace.c \ fmd_ustat.c \ fmd_xdr_adm.c \ fmd_xdr_api.c \ fmd_xprt.c PROG = fmd MANIFEST = ../common/$(PROG).xml MAPFILE-DMOD = $(SRC)/cmd/mdb/common/modules/conf/mapfile-extern LOGADMFILE = $(PROG).logadm.conf LOGADMDIR = $(ROOT)/etc/logadm.d LOGADMENT = $(LOGADMDIR)/$(LOGADMFILE) $(LOGADMENT) : FILEMODE = 444 ROOTPDIR = $(ROOT)/usr/lib/fm/$(PROG) ROOTVDIR = $(ROOT)/var/fm/$(PROG) ROOTVSUB = $(ROOTVDIR)/ckpt $(ROOTVDIR)/rsrc $(ROOTVDIR)/xprt ROOTPROG = $(ROOTPDIR)/$(PROG) ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) ROOTMANIFEST = $(ROOTMANIFESTDIR)/$(PROG).xml OBJS = $(SRCS:%.c=%.o) # Hammerhead: pre-generated files are permanent source, do not delete # CLEANFILES += ../common/fmd_rpc_api.h ../common/fmd_rpc_adm.h # CLEANFILES += fmd_svc_adm.c fmd_svc_api.c fmd_xdr_adm.c fmd_xdr_api.c CLEANFILES += ../common/fmd_error.c DMOD = fmd.so ROOTDMOD = $(ROOT)/usr/lib/mdb/proc/$(DMOD) DMOD_SRCS = fmd_mdb.c DMOD_OBJS = $(DMOD_SRCS:%.c=%.o) HDRS = fmd_api.h fmd_fmri.h ROOTCDIR = $(ROOT)/etc/fm/$(PROG) ROOTHDIR = $(ROOT)/usr/include/fm ROOTHDRS = $(HDRS:%=$(ROOTHDIR)/%) $(ROOTHDRS) : FILEMODE = 0644 $(ROOTVSUB) : DIRMODE = 0755 $(ROOTMANIFEST) : FILEMODE = 0444 $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -I. -I../common CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) CFLAGS64 += $(CTF_FLAGS_64) $(CCVERBOSE) CERRWARN += -Wno-switch CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-clobbered $(RELEASE_BUILD)CERRWARN += -Wno-unused-but-set-variable CERRWARN += -Wno-unused-variable # Hammerhead: Suppress pointer cast warnings in legacy fmd code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast CERRWARN += -Wno-incompatible-pointer-types # not linted SMATCH=off $(PROG) : LDFLAGS += -R/usr/lib/fm $(PROG) : LDLIBS += -L$(ROOTLIB)/fm -ltopo -ldiagcode -lsysevent -lsmbios \ -luuid -lnvpair -lexacct -lnsl -lumem -ldevinfo -lfmd_msg $(DMOD) : CFLAGS64 += $(CC_PICFLAGS) # Hammerhead: fmd.so is an MDB dmod loaded at runtime - disable --no-undefined $(DMOD) : ZDEFS = $(DMOD) : LDFLAGS += $(ZTEXT) $(ZDEFS) $(MAPFILE-DMOD:%=-Wl,-M%) $(GSHARED) # # rpcgen(1) produces code that wants to be in the foreground if we're compiled # DEBUG, which isn't appropriate for our daemon. Forcibly undefine this code. # fmd_svc_adm.o fmd_svc_api.o : CPPFLAGS += -UDEBUG -URPC_SVC_FG fmd_xdr_adm.o fmd_xdr_api.o : CPPFLAGS += -UDEBUG -URPC_SVC_FG .NOTPARALLEL: .PARALLEL: $(OBJS) all: $(PROG) $(DMOD) install_h $(LOGADMDIR): $(INS.dir) $(LOGADMDIR)/%.conf: ../common/%.conf $(INS.file) # Hammerhead: --export-dynamic makes fmd API symbols visible to # dlopen'd fault management modules (GNU ld requirement). $(PROG): $(OBJS) $(LINK.c) -Wl,--export-dynamic $(OBJS) -o $@ $(LDLIBS) $(CTFMERGE) -L VERSION -o $@ $(OBJS) $(POST_PROCESS) $(DMOD): $(DMOD_OBJS) $(LINK.c) $(DMOD_OBJS) -o $@ $(LDLIBS) -lc $(POST_PROCESS) %.o: ../common/%.c $(COMPILE.c) $< $(CTFCONVERT_O) %.o: %.c $(COMPILE.c) $< $(CTFCONVERT_O) ../common/fmd_error.c: ../common/mkerror.sh ../common/fmd_error.h $(KSH93) ../common/mkerror.sh < ../common/fmd_error.h > $@ ../common/fmd_rpc_%.c: ../common/fmd_rpc_%.h ../common/fmd_rpc.c: ../common/fmd_rpc_adm.h ../common/fmd_rpc_api.h # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # fmd_rpc_*.h, fmd_svc_*.c, fmd_xdr_*.c are now committed source files. clean: $(RM) $(OBJS) $(DMOD_OBJS) $(RM) $(CLEANFILES) clobber: clean $(RM) $(PROG) $(DMOD) $(ROOT)/etc/fm: $(INS.dir) $(ROOTCDIR): $(ROOT)/etc/fm $(INS.dir) $(ROOTHDIR): $(INS.dir) $(ROOTHDIR)/%.h: ../common/%.h $(INS.file) $(ROOTMANIFESTDIR)/%.xml: ../common/%.xml $(INS.file) $(ROOT)/var/%: $(INS.dir) $(ROOTVDIR): $(ROOT)/var/fm $(INS.dir) $(ROOTVSUB): $(ROOTVDIR) $(INS.dir) $(ROOT)/usr/lib/fm: $(INS.dir) $(ROOTPDIR): $(ROOT)/usr/lib/fm $(INS.dir) $(ROOTPROG): $(ROOTPDIR) $(PROG) $(RM) $@; $(INS) -s -m 0555 -f $(@D) $(PROG) $(ROOT)/usr/lib/mdb/proc: $(ROOT)/usr/lib/mdb $(INS.dir) $(ROOTDMOD): $(ROOT)/usr/lib/mdb/proc $(DMOD) $(RM) $@; $(INS) -s -m 0555 -f $(@D) $(DMOD) install_h: $(ROOTHDIR) $(ROOTHDRS) install: all install_h $(ROOTPROG) $(ROOTDMOD) $(LOGADMDIR) $(LOGADMENT) \ $(ROOTCDIR) $(ROOTVDIR) $(ROOTVSUB) $(ROOTMANIFEST) check: $(CHKMANIFEST) # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # # Hammerhead: Include cmd Makefile first to set up standard build environment # This provides CPPFLAGS.master with proper include paths include $(SRC)/cmd/Makefile.cmd include ../Makefile.fmd include $(SRC)/cmd/Makefile.cmd.64 # Hammerhead: Ensure RPC headers are generated before building fmd_rpc.c # GNU Make requires explicit dependencies for generated headers ../common/fmd_rpc_adm.h: ../common/fmd_rpc_adm.x $(RPCGEN) -CMN -h -o $@ $< ../common/fmd_rpc_api.h: ../common/fmd_rpc_api.x $(RPCGEN) -CMN -h -o $@ $< fmd_rpc.o: ../common/fmd_rpc_adm.h ../common/fmd_rpc_api.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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const nv_alloc_ops_t fmd_nv_alloc_ops; /* see fmd_nv.c */ const char _fmd_version[] = "1.2"; /* daemon version string */ static char _fmd_plat[MAXNAMELEN]; /* native platform string */ static char _fmd_isa[MAXNAMELEN]; /* native instruction set */ static struct utsname _fmd_uts; /* native uname(2) info */ static char _fmd_psn[MAXNAMELEN]; /* product serial number */ static char _fmd_csn[MAXNAMELEN]; /* chassis serial number */ static char _fmd_prod[MAXNAMELEN]; /* product name string */ /* * Note: the configuration file path is ordered from most common to most host- * specific because new conf files are merged/override previous ones. The * module paths are in the opposite order, from most specific to most common, * because once a module is loaded fmd will not try to load over the same name. */ static const char _fmd_conf_path[] = "%r/usr/lib/fm/fmd:" "%r/usr/platform/%m/lib/fm/fmd:" "%r/usr/platform/%i/lib/fm/fmd:" "%r/etc/fm/fmd"; static const char _fmd_agent_path[] = "%r/usr/platform/%i/lib/fm/fmd/agents:" "%r/usr/platform/%m/lib/fm/fmd/agents:" "%r/usr/lib/fm/fmd/agents"; static const char _fmd_plugin_path[] = "%r/usr/platform/%i/lib/fm/fmd/plugins:" "%r/usr/platform/%m/lib/fm/fmd/plugins:" "%r/usr/lib/fm/fmd/plugins"; static const char _fmd_scheme_path[] = "usr/lib/fm/fmd/schemes"; static const fmd_conf_mode_t _fmd_cerror_modes[] = { { "unload", "unload offending client module", FMD_CERROR_UNLOAD }, { "stop", "stop daemon for debugger attach", FMD_CERROR_STOP }, { "abort", "abort daemon and force core dump", FMD_CERROR_ABORT }, { NULL, NULL, 0 } }; static const fmd_conf_mode_t _fmd_dbout_modes[] = { { "stderr", "send debug messages to stderr", FMD_DBOUT_STDERR }, { "syslog", "send debug messages to syslog", FMD_DBOUT_SYSLOG }, { NULL, NULL, 0 } }; static const fmd_conf_mode_t _fmd_debug_modes[] = { { "help", "display debugging modes and exit", FMD_DBG_HELP }, { "mod", "debug module load/unload/locking", FMD_DBG_MOD }, { "disp", "debug dispatch queue processing", FMD_DBG_DISP }, { "xprt", "debug transport-specific routines", FMD_DBG_XPRT }, { "evt", "debug event subsystem routines", FMD_DBG_EVT }, { "log", "debug log subsystem routines", FMD_DBG_LOG }, { "tmr", "debug timer subsystem routines", FMD_DBG_TMR }, { "fmri", "debug fmri subsystem routines", FMD_DBG_FMRI }, { "asru", "debug asru subsystem routines", FMD_DBG_ASRU }, { "case", "debug case subsystem routines", FMD_DBG_CASE }, { "ckpt", "debug checkpoint routines", FMD_DBG_CKPT }, { "rpc", "debug rpc service routines", FMD_DBG_RPC }, { "trace", "display matching trace calls", FMD_DBG_TRACE }, { "all", "enable all available debug modes", FMD_DBG_ALL }, { NULL, NULL, 0 } }; static int fmd_cerror_set(fmd_conf_param_t *pp, const char *value) { return (fmd_conf_mode_set(_fmd_cerror_modes, pp, value)); } static int fmd_dbout_set(fmd_conf_param_t *pp, const char *value) { return (fmd_conf_mode_set(_fmd_dbout_modes, pp, value)); } static int fmd_debug_set(fmd_conf_param_t *pp, const char *value) { int err = fmd_conf_mode_set(_fmd_debug_modes, pp, value); if (err == 0) fmd.d_fmd_debug = pp->cp_value.cpv_num; return (err); } static int fmd_trmode_set(fmd_conf_param_t *pp, const char *value) { fmd_tracebuf_f *func; if (strcasecmp(value, "none") == 0) func = fmd_trace_none; else if (strcasecmp(value, "lite") == 0) func = fmd_trace_lite; else if (strcasecmp(value, "full") == 0) func = fmd_trace_full; else return (fmd_set_errno(EFMD_CONF_INVAL)); fmd.d_thr_trace = func; pp->cp_value.cpv_ptr = func; return (0); } static void fmd_trmode_get(const fmd_conf_param_t *pp, void *ptr) { *((void **)ptr) = pp->cp_value.cpv_ptr; } static int fmd_clkmode_set(fmd_conf_param_t *pp, const char *value) { const fmd_timeops_t *ops; if (strcasecmp(value, "native") == 0) ops = &fmd_timeops_native; else if (strcasecmp(value, "simulated") == 0) ops = &fmd_timeops_simulated; else return (fmd_set_errno(EFMD_CONF_INVAL)); fmd.d_clockops = ops; pp->cp_value.cpv_ptr = (void *)ops; return (0); } static void fmd_clkmode_get(const fmd_conf_param_t *pp, void *ptr) { *((void **)ptr) = pp->cp_value.cpv_ptr; } static const fmd_conf_ops_t fmd_cerror_ops = { fmd_cerror_set, fmd_conf_mode_get, fmd_conf_notsup, fmd_conf_nop }; static const fmd_conf_ops_t fmd_dbout_ops = { fmd_dbout_set, fmd_conf_mode_get, fmd_conf_notsup, fmd_conf_nop }; static const fmd_conf_ops_t fmd_debug_ops = { fmd_debug_set, fmd_conf_mode_get, fmd_conf_notsup, fmd_conf_nop }; static const fmd_conf_ops_t fmd_trmode_ops = { fmd_trmode_set, fmd_trmode_get, fmd_conf_notsup, fmd_conf_nop }; static const fmd_conf_ops_t fmd_clkmode_ops = { fmd_clkmode_set, fmd_clkmode_get, fmd_conf_notsup, fmd_conf_nop }; static const fmd_conf_formal_t _fmd_conf[] = { { "agent.path", &fmd_conf_path, _fmd_agent_path }, /* path for agents */ { "alloc_msecs", &fmd_conf_uint32, "10" }, /* msecs before alloc retry */ { "alloc_tries", &fmd_conf_uint32, "3" }, /* max # of alloc retries */ { "product_sn", &fmd_conf_string, _fmd_psn }, /* product serial number */ { "chassis", &fmd_conf_string, _fmd_csn }, /* chassis serial number */ { "ckpt.dir", &fmd_conf_string, "var/fm/fmd/ckpt" }, /* ckpt directory path */ { "ckpt.dirmode", &fmd_conf_int32, "0755" }, /* ckpt directory perm mode */ { "ckpt.mode", &fmd_conf_int32, "0644" }, /* ckpt file perm mode */ { "ckpt.restore", &fmd_conf_bool, "true" }, /* restore checkpoints? */ { "ckpt.save", &fmd_conf_bool, "true" }, /* save checkpoints? */ { "ckpt.zero", &fmd_conf_bool, "false" }, /* zero checkpoints on start? */ { "client.buflim", &fmd_conf_size, "10m" }, /* client buffer space limit */ { "client.dbout", &fmd_dbout_ops, NULL }, /* client debug output sinks */ { "client.debug", &fmd_conf_bool, NULL }, /* client debug enable */ { "client.doorthrlim", &fmd_conf_uint32, "20" }, /* client door thread limit */ { "client.error", &fmd_cerror_ops, "unload" }, /* client error policy */ { "client.memlim", &fmd_conf_size, "10m" }, /* client allocation limit */ { "client.evqlim", &fmd_conf_uint32, "256" }, /* client event queue limit */ { "client.thrlim", &fmd_conf_uint32, "20" }, /* client aux thread limit */ { "client.thrsig", &fmd_conf_signal, "SIGUSR1" }, /* fmd_thr_signal() value */ { "client.tmrlim", &fmd_conf_uint32, "1024" }, /* client pending timer limit */ { "client.xprtlim", &fmd_conf_uint32, "256" }, /* client transport limit */ { "client.xprtlog", &fmd_conf_bool, NULL }, /* client transport logging? */ { "client.xprtqlim", &fmd_conf_uint32, "1024" }, /* client transport queue li */ { "clock", &fmd_clkmode_ops, "native" }, /* clock operation mode */ { "conf_path", &fmd_conf_path, _fmd_conf_path }, /* root config file path */ { "conf_file", &fmd_conf_string, "fmd.conf" }, /* root config file name */ { "core", &fmd_conf_bool, "false" }, /* force core dump on quit */ { "dbout", &fmd_dbout_ops, NULL }, /* daemon debug output sinks */ { "debug", &fmd_debug_ops, NULL }, /* daemon debugging flags */ { "dictdir", &fmd_conf_string, "usr/lib/fm/dict" }, /* default diagcode dir */ { "domain", &fmd_conf_string, NULL }, /* domain id for de auth */ { "fakenotpresent", &fmd_conf_uint32, "0" }, /* simulate rsrc not present */ { "fg", &fmd_conf_bool, "false" }, /* run daemon in foreground */ { "gc_interval", &fmd_conf_time, "1d" }, /* garbage collection intvl */ { "ids.avg", &fmd_conf_uint32, "4" }, /* desired idspace chain len */ { "ids.max", &fmd_conf_uint32, "1024" }, /* maximum idspace buckets */ { "isaname", &fmd_conf_string, _fmd_isa }, /* instruction set (uname -p) */ { "log.creator", &fmd_conf_string, "fmd" }, /* exacct log creator string */ { "log.error", &fmd_conf_string, "var/fm/fmd/errlog" }, /* error log path */ { "log.fault", &fmd_conf_string, "var/fm/fmd/fltlog" }, /* fault log path */ { "log.info", &fmd_conf_string, "var/fm/fmd/infolog" }, /* info log path */ { "log.info_hival", &fmd_conf_string, "var/fm/fmd/infolog_hival" }, /* hi pri */ { "log.minfree", &fmd_conf_size, "2m" }, /* min log fsys free space */ { "log.rsrc", &fmd_conf_string, "var/fm/fmd/rsrc" }, /* asru log dir path */ { "log.tryrotate", &fmd_conf_uint32, "10" }, /* max log rotation attempts */ { "log.waitrotate", &fmd_conf_time, "200ms" }, /* log rotation retry delay */ { "log.xprt", &fmd_conf_string, "var/fm/fmd/xprt" }, /* transport log dir */ { "machine", &fmd_conf_string, _fmd_uts.machine }, /* machine name (uname -m) */ { "nodiagcode", &fmd_conf_string, "-" }, /* diagcode to use if error */ { "repaircode", &fmd_conf_string, "-" }, /* diagcode for list.repaired */ { "resolvecode", &fmd_conf_string, "-" }, /* diagcode for list.resolved */ { "updatecode", &fmd_conf_string, "-" }, /* diagcode for list.updated */ { "osrelease", &fmd_conf_string, _fmd_uts.release }, /* release (uname -r) */ { "osversion", &fmd_conf_string, _fmd_uts.version }, /* version (uname -v) */ { "platform", &fmd_conf_string, _fmd_plat }, /* platform string (uname -i) */ { "plugin.close", &fmd_conf_bool, "true" }, /* dlclose plugins on fini */ { "plugin.path", &fmd_conf_path, _fmd_plugin_path }, /* path for plugin mods */ { "product", &fmd_conf_string, _fmd_prod }, /* product name string */ { "rootdir", &fmd_conf_string, "" }, /* root directory for paths */ { "rpc.adm.path", &fmd_conf_string, NULL }, /* FMD_ADM rendezvous file */ { "rpc.adm.prog", &fmd_conf_uint32, "100169" }, /* FMD_ADM rpc program num */ { "rpc.api.path", &fmd_conf_string, NULL }, /* FMD_API rendezvous file */ { "rpc.api.prog", &fmd_conf_uint32, "100170" }, /* FMD_API rpc program num */ { "rpc.rcvsize", &fmd_conf_size, "128k" }, /* rpc receive buffer size */ { "rpc.sndsize", &fmd_conf_size, "128k" }, /* rpc send buffer size */ { "rsrc.pollperiod", &fmd_conf_time, "1h" }, /* aged rsrcs poller period */ { "rsrc.age", &fmd_conf_time, "30d" }, /* max age of old rsrc log */ { "rsrc.zero", &fmd_conf_bool, "false" }, /* zero rsrc cache on start? */ { "schemedir", &fmd_conf_string, _fmd_scheme_path }, /* path for scheme mods */ { "self.name", &fmd_conf_string, "fmd-self-diagnosis" }, /* self-diag module */ { "self.dict", &fmd_conf_list, "FMD.dict" }, /* self-diag dictionary list */ { "server", &fmd_conf_string, _fmd_uts.nodename }, /* server id for de auth */ { "strbuckets", &fmd_conf_uint32, "211" }, /* size of string hashes */ #ifdef DEBUG { "trace.mode", &fmd_trmode_ops, "full" }, /* trace mode: none/lite/full */ #else { "trace.mode", &fmd_trmode_ops, "lite" }, /* trace mode: none/lite/full */ #endif { "trace.recs", &fmd_conf_uint32, "128" }, /* trace records per thread */ { "trace.frames", &fmd_conf_uint32, "16" }, /* max trace rec stack frames */ { "uuidlen", &fmd_conf_uint32, "36" }, /* UUID ASCII string length */ { "xprt.ttl", &fmd_conf_uint8, "1" }, /* default event time-to-live */ }; /* * Statistics maintained by fmd itself on behalf of various global subsystems. * NOTE: FMD_TYPE_STRING statistics should not be used here. If they are * required in the future, the FMD_ADM_MODGSTAT service routine must change. */ static fmd_statistics_t _fmd_stats = { { "errlog.replayed", FMD_TYPE_UINT64, "total events replayed from errlog" }, { "errlog.partials", FMD_TYPE_UINT64, "events partially committed in errlog" }, { "errlog.enospc", FMD_TYPE_UINT64, "events not appended to errlog (ENOSPC)" }, { "fltlog.enospc", FMD_TYPE_UINT64, "events not appended to fltlog (ENOSPC)" }, { "log.enospc", FMD_TYPE_UINT64, "events not appended to other logs (ENOSPC)" }, { "dr.gen", FMD_TYPE_UINT64, "dynamic reconfiguration generation" }, { "topo.gen", FMD_TYPE_UINT64, "topology snapshot generation" }, { "topo.drgen", FMD_TYPE_UINT64, "current topology DR generation number" }, }; /* * SMBIOS serial numbers can contain characters (particularly ':' and ' ') * that are invalid for the authority and can break FMRI parsing. We translate * any invalid characters to a safe '-', as well as trimming any leading or * trailing whitespace. Similarly, '/' can be found in some product names * so we translate that to '-'. */ void fmd_cleanup_auth_str(char *buf, const char *begin) { const char *end, *cp; char c; int i; end = begin + strlen(begin); while (begin < end && isspace(*begin)) begin++; while (begin < end && isspace(*(end - 1))) end--; if (begin >= end) return; cp = begin; for (i = 0; i < MAXNAMELEN - 1; i++) { if (cp >= end) break; c = *cp; if (c == ':' || c == '=' || c == '/' || isspace(c) || !isprint(c)) buf[i] = '-'; else buf[i] = c; cp++; } buf[i] = 0; } void fmd_create(fmd_t *dp, const char *arg0, const char *root, const char *conf) { fmd_conf_path_t *pap; char file[PATH_MAX]; const char *name, *psn, *csn; fmd_stat_t *sp; int i; smbios_hdl_t *shp; smbios_system_t s1; smbios_info_t s2; id_t id; di_prom_handle_t promh = DI_PROM_HANDLE_NIL; di_node_t rooth = DI_NODE_NIL; char *bufp; (void) sysinfo(SI_PLATFORM, _fmd_plat, sizeof (_fmd_plat)); (void) sysinfo(SI_ARCHITECTURE, _fmd_isa, sizeof (_fmd_isa)); (void) uname(&_fmd_uts); if ((shp = smbios_open(NULL, SMB_VERSION, 0, NULL)) != NULL) { if ((id = smbios_info_system(shp, &s1)) != SMB_ERR && smbios_info_common(shp, id, &s2) != SMB_ERR) fmd_cleanup_auth_str(_fmd_prod, s2.smbi_product); if ((psn = smbios_psn(shp)) != NULL) fmd_cleanup_auth_str(_fmd_psn, psn); if ((csn = smbios_csn(shp)) != NULL) fmd_cleanup_auth_str(_fmd_csn, csn); smbios_close(shp); } else if ((rooth = di_init("/", DINFOPROP)) != DI_NODE_NIL && (promh = di_prom_init()) != DI_PROM_HANDLE_NIL) { if (di_prom_prop_lookup_bytes(promh, rooth, "chassis-sn", (unsigned char **)&bufp) != -1) { fmd_cleanup_auth_str(_fmd_csn, bufp); } } if (promh != DI_PROM_HANDLE_NIL) di_prom_fini(promh); if (rooth != DI_NODE_NIL) di_fini(rooth); bzero(dp, sizeof (fmd_t)); dp->d_version = _fmd_version; dp->d_pname = fmd_strbasename(arg0); dp->d_pid = getpid(); if (pthread_key_create(&dp->d_key, NULL) != 0) fmd_error(EFMD_EXIT, "failed to create pthread key"); (void) pthread_mutex_init(&dp->d_xprt_lock, NULL); (void) pthread_mutex_init(&dp->d_err_lock, NULL); (void) pthread_mutex_init(&dp->d_thr_lock, NULL); (void) pthread_mutex_init(&dp->d_mod_lock, NULL); (void) pthread_mutex_init(&dp->d_stats_lock, NULL); (void) pthread_mutex_init(&dp->d_topo_lock, NULL); (void) pthread_rwlock_init(&dp->d_log_lock, NULL); (void) pthread_rwlock_init(&dp->d_hvilog_lock, NULL); (void) pthread_rwlock_init(&dp->d_ilog_lock, NULL); (void) pthread_mutex_init(&dp->d_fmd_lock, NULL); (void) pthread_cond_init(&dp->d_fmd_cv, NULL); /* * A small number of properties must be set manually before we open * the root configuration file. These include any settings for our * memory allocator and path expansion token values, because these * values are needed by the routines in fmd_conf.c itself. After * the root configuration file is processed, we reset these properties * based upon the latest values from the configuration file. */ dp->d_alloc_msecs = 10; dp->d_alloc_tries = 3; dp->d_str_buckets = 211; dp->d_rootdir = root ? root : ""; dp->d_platform = _fmd_plat; dp->d_machine = _fmd_uts.machine; dp->d_isaname = _fmd_isa; dp->d_conf = fmd_conf_open(conf, sizeof (_fmd_conf) / sizeof (_fmd_conf[0]), _fmd_conf, FMD_CONF_DEFER); if (dp->d_conf == NULL) { fmd_error(EFMD_EXIT, "failed to load required configuration properties\n"); } (void) fmd_conf_getprop(dp->d_conf, "alloc.msecs", &dp->d_alloc_msecs); (void) fmd_conf_getprop(dp->d_conf, "alloc.tries", &dp->d_alloc_tries); (void) fmd_conf_getprop(dp->d_conf, "strbuckets", &dp->d_str_buckets); (void) fmd_conf_getprop(dp->d_conf, "platform", &dp->d_platform); (void) fmd_conf_getprop(dp->d_conf, "machine", &dp->d_machine); (void) fmd_conf_getprop(dp->d_conf, "isaname", &dp->d_isaname); /* * Manually specified rootdirs override config files, so only update * d_rootdir based on the config files we parsed if no 'root' was set. */ if (root == NULL) (void) fmd_conf_getprop(dp->d_conf, "rootdir", &dp->d_rootdir); else (void) fmd_conf_setprop(dp->d_conf, "rootdir", dp->d_rootdir); /* * Once the base conf file properties are loaded, lookup the values * of $conf_path and $conf_file and merge in any other conf files. */ (void) fmd_conf_getprop(dp->d_conf, "conf_path", &pap); (void) fmd_conf_getprop(dp->d_conf, "conf_file", &name); for (i = 0; i < pap->cpa_argc; i++) { (void) snprintf(file, sizeof (file), "%s/%s", pap->cpa_argv[i], name); if (access(file, F_OK) == 0) fmd_conf_merge(dp->d_conf, file); } /* * Update the value of fmd.d_fg based on "fg". We cache this property * because it must be accessed deep within fmd at fmd_verror() time. * Update any other properties that must be cached for performance. */ (void) fmd_conf_getprop(fmd.d_conf, "fg", &fmd.d_fg); (void) fmd_conf_getprop(fmd.d_conf, "xprt.ttl", &fmd.d_xprt_ttl); /* * Initialize our custom libnvpair allocator and create an nvlist for * authority elements corresponding to this instance of the daemon. */ (void) nv_alloc_init(&dp->d_nva, &fmd_nv_alloc_ops); dp->d_auth = fmd_protocol_authority(); /* * The fmd_module_t for the root module must be created manually. Most * of it remains unused and zero, except for the few things we fill in. */ dp->d_rmod = fmd_zalloc(sizeof (fmd_module_t), FMD_SLEEP); dp->d_rmod->mod_name = fmd_strdup(dp->d_pname, FMD_SLEEP); dp->d_rmod->mod_fmri = fmd_protocol_fmri_module(dp->d_rmod); fmd_list_append(&dp->d_mod_list, dp->d_rmod); fmd_module_hold(dp->d_rmod); (void) pthread_mutex_init(&dp->d_rmod->mod_lock, NULL); (void) pthread_cond_init(&dp->d_rmod->mod_cv, NULL); (void) pthread_mutex_init(&dp->d_rmod->mod_stats_lock, NULL); dp->d_rmod->mod_thread = fmd_thread_xcreate(dp->d_rmod, pthread_self()); dp->d_rmod->mod_stats = fmd_zalloc(sizeof (fmd_modstat_t), FMD_SLEEP); dp->d_rmod->mod_ustat = fmd_ustat_create(); if (pthread_setspecific(dp->d_key, dp->d_rmod->mod_thread) != 0) fmd_error(EFMD_EXIT, "failed to attach main thread key"); if ((dp->d_stats = (fmd_statistics_t *)fmd_ustat_insert( dp->d_rmod->mod_ustat, FMD_USTAT_NOALLOC, sizeof (_fmd_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&_fmd_stats, NULL)) == NULL) fmd_error(EFMD_EXIT, "failed to initialize statistics"); (void) pthread_mutex_lock(&dp->d_rmod->mod_lock); dp->d_rmod->mod_flags |= FMD_MOD_INIT; (void) pthread_mutex_unlock(&dp->d_rmod->mod_lock); /* * In addition to inserting the _fmd_stats collection of program-wide * statistics, we also insert a statistic named after each of our * errors and update these counts in fmd_verror() (see fmd_subr.c). */ dp->d_errstats = sp = fmd_zalloc(sizeof (fmd_stat_t) * (EFMD_END - EFMD_UNKNOWN), FMD_SLEEP); for (i = 0; i < EFMD_END - EFMD_UNKNOWN; i++, sp++) { (void) snprintf(sp->fmds_name, sizeof (sp->fmds_name), "err.%s", strrchr(fmd_errclass(EFMD_UNKNOWN + i), '.') + 1); sp->fmds_type = FMD_TYPE_UINT64; } (void) fmd_ustat_insert(dp->d_rmod->mod_ustat, FMD_USTAT_NOALLOC, EFMD_END - EFMD_UNKNOWN, dp->d_errstats, NULL); } 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(); 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, B_FALSE); 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); fmd_topo_fini(); 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"); } /*ARGSUSED*/ static void fmd_gc(fmd_t *dp, id_t id, hrtime_t hrt) { hrtime_t delta; if (id != 0) { TRACE((FMD_DBG_MOD, "garbage collect start")); fmd_modhash_apply(dp->d_mod_hash, fmd_module_gc); TRACE((FMD_DBG_MOD, "garbage collect end")); (void) pthread_rwlock_rdlock(&dp->d_log_lock); fmd_log_update(dp->d_errlog); (void) pthread_rwlock_unlock(&dp->d_log_lock); (void) pthread_rwlock_rdlock(&dp->d_hvilog_lock); fmd_log_update(dp->d_hvilog); (void) pthread_rwlock_unlock(&dp->d_hvilog_lock); (void) pthread_rwlock_rdlock(&dp->d_ilog_lock); fmd_log_update(dp->d_ilog); (void) pthread_rwlock_unlock(&dp->d_ilog_lock); } (void) fmd_conf_getprop(dp->d_conf, "gc_interval", &delta); (void) fmd_timerq_install(dp->d_timers, dp->d_rmod->mod_timerids, (fmd_timer_f *)fmd_gc, dp, NULL, delta); } /*ARGSUSED*/ static void fmd_clear_aged_rsrcs(fmd_t *dp, id_t id, hrtime_t hrt) { hrtime_t period; fmd_asru_clear_aged_rsrcs(); (void) fmd_conf_getprop(dp->d_conf, "rsrc.pollperiod", &period); (void) fmd_timerq_install(dp->d_timers, dp->d_rmod->mod_timerids, (fmd_timer_f *)fmd_clear_aged_rsrcs, dp, NULL, period); } /* * Events are committed to the errlog after cases are checkpointed. If fmd * crashes before an event is ever associated with a module, this function will * be called to replay it to all subscribers. If fmd crashes in between the * subscriber checkpointing and committing the event in the error log, the * module will have seen the event and we don't want to replay it. So we look * for the event in all modules and transition it to the proper state. If * it is found, we commit it to the error log and do not replay it. The in- * memory case search used by fmd_module_contains() et al isn't particularly * efficient, but it is faster than doing read i/o's on every case event to * check their status or write i/o's on every event to replay to update states. * We can improve the efficiency of this lookup algorithm later if necessary. */ /*ARGSUSED*/ static void fmd_err_replay(fmd_log_t *lp, fmd_event_t *ep, fmd_t *dp) { fmd_module_t *mp; fmd_stat_t *sp; (void) pthread_mutex_lock(&dp->d_mod_lock); for (mp = fmd_list_next(&dp->d_mod_list); mp != NULL; mp = fmd_list_next(mp)) { if (fmd_module_contains(mp, ep)) { fmd_module_hold(mp); break; } } (void) pthread_mutex_unlock(&dp->d_mod_lock); if (mp != NULL) { fmd_event_commit(ep); fmd_module_rele(mp); sp = &dp->d_stats->ds_log_partials; } else { fmd_dispq_dispatch(dp->d_disp, ep, FMD_EVENT_DATA(ep)); sp = &dp->d_stats->ds_log_replayed; } (void) pthread_mutex_lock(&dp->d_stats_lock); sp->fmds_value.ui64++; (void) pthread_mutex_unlock(&dp->d_stats_lock); } void fmd_door_server(void *dip) { fmd_dprintf(FMD_DBG_XPRT, "door server starting for %p\n", dip); (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); (void) door_return(NULL, 0, NULL, 0); } /* * Custom door server create callback. Any fmd services that use doors will * require those threads to have their fmd-specific TSD initialized, etc. * Modules should use door_xcreate and derivatives such as * sysevent_evc_xsubscribe in order to use private doors that * avoid this global door server function (see fmd_api_module comments). */ static void fmd_door(door_info_t *dip) { if (fmd_thread_create(fmd.d_rmod, fmd_door_server, dip) == NULL) fmd_panic("failed to create server for door %p", (void *)dip); } /* * This signal handler is installed for the client.thrsig signal to be used to * force an auxiliary thread to wake up from a system call and return EINTR in * response to a module's use of fmd_thr_signal(). We also trace the event. */ static void fmd_signal(int sig) { TRACE((FMD_DBG_MOD, "module thread received sig #%d", sig)); } void fmd_run(fmd_t *dp, int pfd) { char *nodc_key[] = { FMD_FLT_NODC, NULL }; char *repair_key[] = { FM_LIST_REPAIRED_CLASS, NULL }; char *resolve_key[] = { FM_LIST_RESOLVED_CLASS, NULL }; char *update_key[] = { FM_LIST_UPDATED_CLASS, NULL }; char code_str[128]; struct sigaction act; int status = FMD_EXIT_SUCCESS; const char *name; fmd_conf_path_t *pap; fmd_event_t *e; int dbout; /* * Cache all the current debug property settings in d_fmd_debug, * d_fmd_dbout, d_hdl_debug, and d_hdl_dbout. If a given debug mask * is non-zero and the corresponding dbout mask is zero, set dbout * to a sensible default value based on whether we have daemonized. */ (void) fmd_conf_getprop(dp->d_conf, "dbout", &dbout); if (dp->d_fmd_debug != 0 && dbout == 0) dp->d_fmd_dbout = dp->d_fg? FMD_DBOUT_STDERR : FMD_DBOUT_SYSLOG; else dp->d_fmd_dbout = dbout; (void) fmd_conf_getprop(dp->d_conf, "client.debug", &dp->d_hdl_debug); (void) fmd_conf_getprop(dp->d_conf, "client.dbout", &dbout); if (dp->d_hdl_debug != 0 && dbout == 0) dp->d_hdl_dbout = dp->d_fg? FMD_DBOUT_STDERR : FMD_DBOUT_SYSLOG; else dp->d_hdl_dbout = dbout; /* * Initialize remaining major program data structures such as the * clock, dispatch queues, log files, module hash collections, etc. * This work is done here rather than in fmd_create() to permit the -o * command-line option to modify properties after fmd_create() is done. */ name = dp->d_rootdir != NULL && *dp->d_rootdir != '\0' ? dp->d_rootdir : NULL; /* * The clock must be initialized before fmd_topo_init() because * fmd_topo_update() calls fmd_time_gethrtime(). */ dp->d_clockptr = dp->d_clockops->fto_init(); fmd_topo_init(); dp->d_xprt_ids = fmd_idspace_create("xprt_ids", 1, INT_MAX); fmd_xprt_suspend_all(); (void) door_server_create(fmd_door); dp->d_rmod->mod_timerids = fmd_idspace_create(dp->d_pname, 1, 16); dp->d_timers = fmd_timerq_create(); dp->d_disp = fmd_dispq_create(); dp->d_cases = fmd_case_hash_create(); /* * The root module's mod_queue is created with limit zero, making it * act like /dev/null; anything inserted here is simply ignored. */ dp->d_rmod->mod_queue = fmd_eventq_create(dp->d_rmod, &dp->d_rmod->mod_stats->ms_evqstat, &dp->d_rmod->mod_stats_lock, 0); /* * Once our subsystems that use signals have been set up, install the * signal handler for the fmd_thr_signal() API. Verify that the signal * being used for this purpose doesn't conflict with something else. */ (void) fmd_conf_getprop(dp->d_conf, "client.thrsig", &dp->d_thr_sig); if (sigaction(dp->d_thr_sig, NULL, &act) != 0) { fmd_error(EFMD_EXIT, "invalid signal selected for " "client.thrsig property: %d\n", dp->d_thr_sig); } if (act.sa_handler != SIG_IGN && act.sa_handler != SIG_DFL) { fmd_error(EFMD_EXIT, "signal selected for client.thrsig " "property is already in use: %d\n", dp->d_thr_sig); } act.sa_handler = fmd_signal; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); (void) sigaction(dp->d_thr_sig, &act, NULL); (void) fmd_conf_getprop(dp->d_conf, "schemedir", &name); dp->d_schemes = fmd_scheme_hash_create(dp->d_rootdir, name); (void) fmd_conf_getprop(dp->d_conf, "log.rsrc", &name); dp->d_asrus = fmd_asru_hash_create(dp->d_rootdir, name); (void) fmd_conf_getprop(dp->d_conf, "log.error", &name); dp->d_errlog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_ERROR); (void) fmd_conf_getprop(dp->d_conf, "log.fault", &name); dp->d_fltlog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_FAULT); (void) fmd_conf_getprop(dp->d_conf, "log.info_hival", &name); dp->d_hvilog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_INFO); (void) fmd_conf_getprop(dp->d_conf, "log.info", &name); dp->d_ilog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_INFO); if (dp->d_asrus == NULL || dp->d_errlog == NULL || dp->d_fltlog == NULL) fmd_error(EFMD_EXIT, "failed to initialize log files\n"); /* * Before loading modules, create an empty control event which will act * as a global barrier for module event processing. Each module we * load successfully will insert it at their head of their event queue, * and then pause inside of fmd_ctl_rele() after dequeuing the event. * This module barrier is required for two reasons: * * (a) During module loading, the restoration of case checkpoints may * result in a list.* event being recreated for which the intended * subscriber has not yet loaded depending on the load order. Such * events could then result in spurious "no subscriber" errors. * * (b) During errlog replay, a sequence of errors from a long time ago * may be replayed, and the module may attempt to install relative * timers associated with one or more of these events. If errlog * replay were "racing" with active module threads, an event E1 * that resulted in a relative timer T at time E1 + N nsec could * fire prior to an event E2 being enqueued, even if the relative * time ordering was E1 < E2 < E1 + N, causing mis-diagnosis. */ dp->d_mod_event = e = fmd_event_create(FMD_EVT_CTL, FMD_HRT_NOW, NULL, fmd_ctl_init(NULL)); fmd_event_hold(e); /* * Once all data structures are initialized, we load all of our modules * in order according to class in order to load up any subscriptions. * Once built-in modules are loaded, we detach from our waiting parent. */ dp->d_mod_hash = fmd_modhash_create(); if (fmd_builtin_loadall(dp->d_mod_hash) != 0 && !dp->d_fg) fmd_error(EFMD_EXIT, "failed to initialize fault manager\n"); (void) fmd_conf_getprop(dp->d_conf, "self.name", &name); dp->d_self = fmd_modhash_lookup(dp->d_mod_hash, name); if (dp->d_self != NULL) { if (fmd_module_dc_key2code(dp->d_self, nodc_key, code_str, sizeof (code_str)) == 0) (void) fmd_conf_setprop(dp->d_conf, "nodiagcode", code_str); if (fmd_module_dc_key2code(dp->d_self, repair_key, code_str, sizeof (code_str)) == 0) (void) fmd_conf_setprop(dp->d_conf, "repaircode", code_str); if (fmd_module_dc_key2code(dp->d_self, resolve_key, code_str, sizeof (code_str)) == 0) (void) fmd_conf_setprop(dp->d_conf, "resolvecode", code_str); if (fmd_module_dc_key2code(dp->d_self, update_key, code_str, sizeof (code_str)) == 0) (void) fmd_conf_setprop(dp->d_conf, "updatecode", code_str); } fmd_rpc_init(); dp->d_running = 1; /* we are now officially an active fmd */ /* * Now that we're running, if a pipe fd was specified, write an exit * status to it to indicate that our parent process can safely detach. * Then proceed to loading the remaining non-built-in modules. */ if (pfd >= 0) (void) write(pfd, &status, sizeof (status)); /* * Before loading all modules, repopulate the ASRU cache from its * persistent repository on disk. Then during module loading, the * restoration of checkpoint files will reparent any active cases. */ fmd_asru_hash_refresh(dp->d_asrus); (void) fmd_conf_getprop(dp->d_conf, "plugin.path", &pap); fmd_modhash_loadall(dp->d_mod_hash, pap, &fmd_rtld_ops, ".so"); (void) fmd_conf_getprop(dp->d_conf, "agent.path", &pap); fmd_modhash_loadall(dp->d_mod_hash, pap, &fmd_proc_ops, NULL); dp->d_loaded = 1; /* modules are now loaded */ /* * With all modules loaded, replay fault events from the ASRU cache for * any ASRUs that must be retired, replay error events from the errlog * that did not finish processing the last time ran, and then release * the global module barrier by executing a final rele on d_mod_event. */ fmd_asru_hash_replay(dp->d_asrus); (void) pthread_rwlock_rdlock(&dp->d_log_lock); fmd_log_replay(dp->d_errlog, (fmd_log_f *)fmd_err_replay, dp); fmd_log_update(dp->d_errlog); (void) pthread_rwlock_unlock(&dp->d_log_lock); dp->d_mod_event = NULL; fmd_event_rele(e); /* * Now replay list.updated and list.repaired events */ fmd_case_repair_replay(); /* * Finally, awaken any threads associated with receiving events from * open transports and tell them to proceed with fmd_xprt_recv(). */ fmd_xprt_resume_all(); fmd_gc(dp, 0, 0); fmd_clear_aged_rsrcs(dp, 0, 0); (void) pthread_mutex_lock(&dp->d_fmd_lock); dp->d_booted = 1; (void) pthread_cond_broadcast(&dp->d_fmd_cv); (void) pthread_mutex_unlock(&dp->d_fmd_lock); } void fmd_help(fmd_t *dp) { const fmd_conf_mode_t *cmp; (void) printf("Usage: %s -o debug=mode[,mode]\n", dp->d_pname); for (cmp = _fmd_debug_modes; cmp->cm_name != NULL; cmp++) (void) printf("\t%s\t%s\n", cmp->cm_name, cmp->cm_desc); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_H #define _FMD_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include struct fmd_conf; /* see */ struct fmd_dispq; /* see */ struct fmd_timerq; /* see */ struct fmd_asru_hash; /* see */ struct fmd_scheme_hash; /* see */ struct fmd_case_hash; /* see */ struct fmd_modhash; /* see */ struct fmd_module; /* see */ struct fmd_log; /* see */ struct fmd_idspace; /* see */ struct topo_hdl; /* see */ typedef struct fmd_statistics { fmd_stat_t ds_log_replayed; /* number of events replayed from log */ fmd_stat_t ds_log_partials; /* number of events partially commit */ fmd_stat_t ds_err_enospc; /* number of events w/ ENOSPC errlog */ fmd_stat_t ds_flt_enospc; /* number of events w/ ENOSPC fltlog */ fmd_stat_t ds_oth_enospc; /* number of events w/ ENOSPC others */ fmd_stat_t ds_dr_gen; /* dynamic reconfiguration generation */ fmd_stat_t ds_topo_gen; /* topology snapshot generation */ fmd_stat_t ds_topo_drgen; /* topology DR generation */ } fmd_statistics_t; typedef struct fmd { const char *d_version; /* version string for fmd (see fmd.c) */ const char *d_pname; /* basename to use for messages */ pid_t d_pid; /* process-ID of current daemon */ pthread_key_t d_key; /* key for fmd's thread-specific data */ volatile int d_signal; /* signal indicating we should quit */ volatile int d_running; /* flag set when fmd_run() succeeds */ volatile int d_loaded; /* flag set when all modules loaded */ volatile int d_booted; /* flag set when fmd_run() completes */ uint_t d_fmd_debug; /* mask of fmd active debugging modes */ uint_t d_fmd_dbout; /* fmd debug output sinks (see below) */ uint_t d_hdl_debug; /* bool indicating if hdl debug is on */ uint_t d_hdl_dbout; /* hdl debug output sinks (see below) */ char *volatile d_panicstr; /* pointer to formatted panic message */ pthread_t d_panictid; /* tid of thread forcing a panic */ uint_t d_panicrefs; /* number of attempts to panic */ pthread_mutex_t d_xprt_lock; /* transport suspend lock */ uint_t d_xprt_suspend; /* transport suspend count */ uint_t d_xprt_ttl; /* transport default time-to-live */ struct fmd_idspace *d_xprt_ids; /* transport id hash */ const fmd_timeops_t *d_clockops; /* system clock ops vector */ void *d_clockptr; /* system clock private data */ pthread_mutex_t d_thr_lock; /* lock for d_thr_list */ fmd_list_t d_thr_list; /* list of all fmd_thread_t's */ fmd_tracebuf_f *d_thr_trace; /* thread trace buffer function */ int d_thr_sig; /* cached copy of client.thrsig */ pthread_mutex_t d_mod_lock; /* lock for d_mod_list */ fmd_list_t d_mod_list; /* list of modules in load order */ struct fmd_modhash *d_mod_hash; /* hash of modules by base name */ fmd_event_t *d_mod_event; /* boot event for module quiesce */ uint_t d_alloc_msecs; /* initial delay time for alloc retry */ uint_t d_alloc_tries; /* max # times to retry an allocation */ uint_t d_str_buckets; /* def # of buckets for string hashes */ const char *d_rootdir; /* root directory path */ const char *d_platform; /* platform name (uname -i) */ const char *d_machine; /* machine class name (uname -m) */ const char *d_isaname; /* processor ISA name (uname -p) */ void *d_sysev_hdl; /* legacy sysevent handle */ nv_alloc_t d_nva; /* libnvpair allocator handle */ nvlist_t *d_auth; /* FMRI authority nvlist */ pthread_mutex_t d_topo_lock; /* lock for topo hdl */ fmd_list_t d_topo_list; /* list of all topology snapshots */ struct fmd_conf *d_conf; /* global configuration properties */ uint_t d_fg; /* cached value of "fg" property */ fmd_statistics_t *d_stats; /* root module statistics collection */ pthread_mutex_t d_stats_lock; /* root module statistics lock */ struct fmd_module *d_rmod; /* root module for fmd's main thread */ struct fmd_module *d_self; /* self module for fmd's diagnosis */ pthread_mutex_t d_err_lock; /* lock for stderr and error stats */ fmd_stat_t *d_errstats; /* program-wide error statistics */ struct fmd_timerq *d_timers; /* timer queue for module timers */ struct fmd_dispq *d_disp; /* dispatch queue for incoming events */ struct fmd_scheme_hash *d_schemes; /* hash of fmri scheme modules */ struct fmd_asru_hash *d_asrus; /* hash of cached asru objects */ struct fmd_case_hash *d_cases; /* hash of active cases */ pthread_rwlock_t d_log_lock; /* log pointer lock (r=use, w=rotate) */ struct fmd_log *d_errlog; /* log file for error events */ struct fmd_log *d_fltlog; /* log file for fault events */ pthread_rwlock_t d_hvilog_lock; /* log pointer lock (r=use, w=rotate) */ struct fmd_log *d_hvilog; /* log file for hi value info events */ pthread_rwlock_t d_ilog_lock; /* log pointer lock (r=use, w=rotate) */ struct fmd_log *d_ilog; /* log file for info events */ pthread_cond_t d_fmd_cv; /* sync startup with rpc */ pthread_mutex_t d_fmd_lock; /* sync startup with rpc */ } fmd_t; /* * Exit status values used for the few places within fmd where we exit(2) or * return from main(). fmd only exits if a fatal error occurs during startup; * if anything else happens errors are reported and we just keep trucking. */ #define FMD_EXIT_SUCCESS 0 /* successful execution of daemon */ #define FMD_EXIT_ERROR 1 /* failed to initialize daemon */ #define FMD_EXIT_USAGE 2 /* syntax error on command-line */ /* * Values associated with fmd's client.error property, stored as a uint32_t. * By default, we unload bad clients; other values are for use by developers. */ #define FMD_CERROR_UNLOAD 0 /* unload module on error (default) */ #define FMD_CERROR_STOP 1 /* stop fmd for debugger attach */ #define FMD_CERROR_ABORT 2 /* abort fmd and generate core dump */ /* * Values associated with any *dbout (debug output sink) property, stored as * a uint32_t. Currently we permit syslog output and stderr output. */ #define FMD_DBOUT_SYSLOG 0x1 /* output to syslog(LOG_DEBUG) */ #define FMD_DBOUT_STDERR 0x2 /* output to stderr */ extern const char _fmd_version[]; extern fmd_t fmd; extern void fmd_door_server(void *); extern void fmd_create(fmd_t *, const char *, const char *, const char *); extern void fmd_destroy(fmd_t *); extern void fmd_run(fmd_t *, int); extern void fmd_help(fmd_t *); #ifdef __cplusplus } #endif #endif /* _FMD_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. # # Entries to be added to /etc/logadm.conf by svc:/application/logadm-upgrade # /var/fm/fmd/infolog -N -A 2y -S 50m -s 10m -M '/usr/sbin/fmadm -q rotate infolog && mv /var/fm/fmd/infolog.0- $nfile' /var/fm/fmd/infolog_hival -N -A 2y -S 50m -s 10m -M '/usr/sbin/fmadm -q rotate infolog_hival && mv /var/fm/fmd/infolog_hival.0- $nfile' /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include void * fmd_alloc(size_t size, int flags) { void *data = umem_alloc(size, UMEM_DEFAULT); uint_t try, lim, msecs; if (data != NULL || size == 0 || !(flags & FMD_SLEEP)) return (data); /* in common cases just return result */ lim = fmd.d_alloc_tries; msecs = fmd.d_alloc_msecs; for (try = 0; data == NULL && try < lim; try++) { (void) poll(NULL, 0, msecs); msecs *= 10; data = umem_alloc(size, UMEM_DEFAULT); } if (data == NULL) { fmd_modhash_tryapply(fmd.d_mod_hash, fmd_module_trygc); fmd_scheme_hash_trygc(fmd.d_schemes); data = umem_alloc(size, UMEM_DEFAULT); } if (data == NULL) fmd_panic("insufficient memory (%u bytes needed)\n", size); return (data); } void * fmd_zalloc(size_t size, int flags) { void *data = fmd_alloc(size, flags); if (data != NULL) bzero(data, size); return (data); } void fmd_free(void *data, size_t size) { umem_free(data, size); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_ALLOC_H #define _FMD_ALLOC_H #include #ifdef __cplusplus extern "C" { #endif #define FMD_NOSLEEP 0x0 /* do not sleep or retry if alloc fails */ #define FMD_SLEEP 0x1 /* sleep or retry if alloc fails */ extern void *fmd_alloc(size_t, int); extern void *fmd_zalloc(size_t, int); extern void fmd_free(void *, size_t); #ifdef __cplusplus } #endif #endif /* _FMD_ALLOC_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Table of configuration file variable types ops-vector pointers. We use this * to convert from the property description array specified by the module to an * array of fmd_conf_formal_t's. The order of this array must match the order * of #define values specified in (i.e. FMD_TYPE_BOOL must be 0). * For now, the fmd_conf_list and fmd_conf_path types are not supported as we * do not believe modules need them and they would require more complexity. */ static const fmd_conf_ops_t *const _fmd_prop_ops[] = { &fmd_conf_bool, /* FMD_TYPE_BOOL */ &fmd_conf_int32, /* FMD_TYPE_INT32 */ &fmd_conf_uint32, /* FMD_TYPE_UINT32 */ &fmd_conf_int64, /* FMD_TYPE_INT64 */ &fmd_conf_uint64, /* FMD_TYPE_UINT64 */ &fmd_conf_string, /* FMD_TYPE_STRING */ &fmd_conf_time, /* FMD_TYPE_TIME */ &fmd_conf_size, /* FMD_TYPE_SIZE */ }; static void fmd_api_verror(fmd_module_t *, int, const char *, va_list) __NORETURN; static void fmd_api_error(fmd_module_t *, int, const char *, ...) __NORETURN; /* * fmd_api_vxerror() provides the engine underlying the fmd_hdl_[v]error() API * calls and the fmd_api_[v]error() utility routine defined below. The routine * formats the error, optionally associated with a particular errno code 'err', * and logs it as an ereport associated with the calling module. Depending on * other optional properties, we also emit a message to stderr and to syslog. */ static void fmd_api_vxerror(fmd_module_t *mp, int err, const char *format, va_list ap) { int raw_err = err; nvlist_t *nvl; fmd_event_t *e; char *class, *msg; size_t len1, len2; char c; /* * fmd_api_vxerror() counts as both an error of class EFMD_MODULE * as well as an instance of 'err' w.r.t. our internal bean counters. */ (void) pthread_mutex_lock(&fmd.d_err_lock); fmd.d_errstats[EFMD_MODULE - EFMD_UNKNOWN].fmds_value.ui64++; if (err > EFMD_UNKNOWN && err < EFMD_END) fmd.d_errstats[err - EFMD_UNKNOWN].fmds_value.ui64++; (void) pthread_mutex_unlock(&fmd.d_err_lock); /* * Format the message using vsnprintf(). As usual, if the format has a * newline in it, it is printed alone; otherwise strerror() is added. */ if (strchr(format, '\n') != NULL) err = 0; /* err is not relevant in the message */ len1 = vsnprintf(&c, 1, format, ap); len2 = err != 0 ? snprintf(&c, 1, ": %s\n", fmd_strerror(err)) : 0; msg = fmd_alloc(len1 + len2 + 1, FMD_SLEEP); (void) vsnprintf(msg, len1 + 1, format, ap); if (err != 0) { (void) snprintf(&msg[len1], len2 + 1, ": %s\n", fmd_strerror(err)); } /* * Create an error event corresponding to the error, insert it into the * error log, and dispatch it to the fmd-self-diagnosis engine. */ if (mp != fmd.d_self && (raw_err != EFMD_HDL_ABORT || fmd.d_running)) { if ((c = msg[len1 + len2 - 1]) == '\n') msg[len1 + len2 - 1] = '\0'; /* strip \n for event */ nvl = fmd_protocol_moderror(mp, err, msg); if (c == '\n') msg[len1 + len2 - 1] = c; (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); (void) pthread_rwlock_rdlock(&fmd.d_log_lock); fmd_log_append(fmd.d_errlog, e, NULL); (void) pthread_rwlock_unlock(&fmd.d_log_lock); fmd_event_transition(e, FMD_EVS_ACCEPTED); fmd_event_commit(e); fmd_dispq_dispatch(fmd.d_disp, e, class); } /* * Similar to fmd_vdebug(), if the debugging switches are enabled we * echo the module name and message to stderr and/or syslog. Unlike * fmd_vdebug(), we also print to stderr if foreground mode is enabled. * We also print the message if a built-in module is aborting before * fmd has detached from its parent (e.g. default transport failure). */ if (fmd.d_fg || (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) || ( raw_err == EFMD_HDL_ABORT && !fmd.d_running)) { (void) pthread_mutex_lock(&fmd.d_err_lock); (void) fprintf(stderr, "%s: %s: %s", fmd.d_pname, mp->mod_name, msg); (void) pthread_mutex_unlock(&fmd.d_err_lock); } if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) { syslog(LOG_ERR | LOG_DAEMON, "%s ERROR: %s: %s", fmd.d_pname, mp->mod_name, msg); } fmd_free(msg, len1 + len2 + 1); } /*PRINTFLIKE3*/ static void fmd_api_xerror(fmd_module_t *mp, int err, const char *format, ...) { va_list ap; va_start(ap, format); fmd_api_vxerror(mp, err, format, ap); va_end(ap); } /* * fmd_api_verror() is a wrapper around fmd_api_vxerror() for API subroutines. * It calls fmd_module_unlock() on behalf of its caller, logs the error, and * then aborts the API call and the surrounding module entry point by doing an * fmd_module_abort(), which longjmps to the place where we entered the module. */ static void fmd_api_verror(fmd_module_t *mp, int err, const char *format, va_list ap) { if (fmd_module_locked(mp)) fmd_module_unlock(mp); fmd_api_vxerror(mp, err, format, ap); fmd_module_abort(mp, err); } /*PRINTFLIKE3*/ static void fmd_api_error(fmd_module_t *mp, int err, const char *format, ...) { va_list ap; va_start(ap, format); fmd_api_verror(mp, err, format, ap); va_end(ap); } /* * Common code for fmd_api_module_lock() and fmd_api_transport_impl(). This * code verifies that the handle is valid and associated with a proper thread. */ static fmd_module_t * fmd_api_module(fmd_hdl_t *hdl) { fmd_thread_t *tp; fmd_module_t *mp; /* * If our TSD is not present at all, this is either a serious bug or * someone has created a thread behind our back and is using fmd's API. * We can't call fmd_api_error() because we can't be sure that we can * unwind our state back to an enclosing fmd_module_dispatch(), so we * must panic instead. This is likely a module design or coding error. */ if ((tp = pthread_getspecific(fmd.d_key)) == NULL) { fmd_panic("fmd module api call made using " "client handle %p from unknown thread\n", (void *)hdl); } /* * If our TSD refers to the root module and is a non-private * door server thread, then it was created asynchronously at the * request of a module but is using now the module API as an * auxiliary module thread. We reset tp->thr_mod to the module * handle so it can act as a module thread. * * If more than one module uses non-private doors then the * "client handle is not valid" check below can fail since * door server threads for such doors can service *any* * non-private door. We use non-private door for legacy sysevent * alone. */ if (tp->thr_mod == fmd.d_rmod && tp->thr_func == &fmd_door_server) tp->thr_mod = (fmd_module_t *)hdl; if ((mp = tp->thr_mod) != (fmd_module_t *)hdl) { fmd_api_error(mp, EFMD_HDL_INVAL, "client handle %p is not valid\n", (void *)hdl); } if (mp->mod_flags & FMD_MOD_FAIL) { fmd_api_error(mp, EFMD_MOD_FAIL, "module has experienced an unrecoverable error\n"); } return (mp); } /* * fmd_api_module_lock() is used as a wrapper around fmd_module_lock() and a * common prologue to each fmd_api.c routine. It verifies that the handle is * valid and owned by the current server thread, locks the handle, and then * verifies that the caller is performing an operation on a registered handle. * If any tests fail, the entire API call is aborted by fmd_api_error(). */ static fmd_module_t * fmd_api_module_lock(fmd_hdl_t *hdl) { fmd_module_t *mp = fmd_api_module(hdl); fmd_module_lock(mp); if (mp->mod_info == NULL) { fmd_api_error(mp, EFMD_HDL_NOTREG, "client handle %p has not been registered\n", (void *)hdl); } return (mp); } /* * Utility function for API entry points that accept fmd_case_t's. We cast cp * to fmd_case_impl_t and check to make sure the case is owned by the caller. */ static fmd_case_impl_t * fmd_api_case_impl(fmd_module_t *mp, fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; if (cip == NULL || cip->ci_mod != mp) { fmd_api_error(mp, EFMD_CASE_OWNER, "case %p is invalid or not owned by caller\n", (void *)cip); } return (cip); } /* * Utility function for API entry points that accept fmd_xprt_t's. We cast xp * to fmd_transport_t and check to make sure the case is owned by the caller. * Note that we could make this check safer by actually walking mp's transport * list, but that requires holding the module lock and this routine needs to be * MT-hot w.r.t. auxiliary module threads. Ultimately any loadable module can * cause us to crash anyway, so we optimize for scalability over safety here. */ static fmd_xprt_impl_t * fmd_api_transport_impl(fmd_hdl_t *hdl, fmd_xprt_t *xp) { fmd_module_t *mp = fmd_api_module(hdl); fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; if (xip == NULL || xip->xi_queue->eq_mod != mp) { fmd_api_error(mp, EFMD_XPRT_OWNER, "xprt %p is invalid or not owned by caller\n", (void *)xp); } return (xip); } /* * fmd_hdl_register() is the one function which cannot use fmd_api_error() to * report errors, because that routine causes the module to abort. Failure to * register is instead handled by having fmd_hdl_register() return an error to * the _fmd_init() function and then detecting no registration when it returns. * So we use this routine for fmd_hdl_register() error paths instead. */ static int fmd_hdl_register_error(fmd_module_t *mp, int err) { if (fmd_module_locked(mp)) fmd_module_unlock(mp); fmd_api_xerror(mp, err, "failed to register"); return (fmd_set_errno(err)); } static void fmd_hdl_nop(void) { /* empty function for use with unspecified module entry points */ } int fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip) { fmd_thread_t *tp = pthread_getspecific(fmd.d_key); fmd_module_t *mp = tp->thr_mod; const fmd_prop_t *prop; const fmd_conf_path_t *pap; fmd_conf_formal_t *cfp; fmd_hdl_ops_t ops; const char *conf = NULL; char buf[PATH_MAX]; int i; if (mp != (fmd_module_t *)hdl) return (fmd_hdl_register_error(mp, EFMD_HDL_INVAL)); fmd_module_lock(mp); /* * First perform some sanity checks on our input. The API version must * be supported by FMD and the handle can only be registered once by * the module thread to which we assigned this client handle. The info * provided for the handle must be valid and have the minimal settings. */ if (version > FMD_API_VERSION_5) return (fmd_hdl_register_error(mp, EFMD_VER_NEW)); if (version < FMD_API_VERSION_1) return (fmd_hdl_register_error(mp, EFMD_VER_OLD)); if (mp->mod_conf != NULL) return (fmd_hdl_register_error(mp, EFMD_HDL_REG)); if (pthread_self() != mp->mod_thread->thr_tid) return (fmd_hdl_register_error(mp, EFMD_HDL_TID)); if (mip == NULL || mip->fmdi_desc == NULL || mip->fmdi_vers == NULL || mip->fmdi_ops == NULL) return (fmd_hdl_register_error(mp, EFMD_HDL_INFO)); /* * Copy the module's ops vector into a local variable to account for * changes in the module ABI. Then if any of the optional entry points * are NULL, set them to nop so we don't have to check before calling. */ bzero(&ops, sizeof (ops)); if (version < FMD_API_VERSION_3) bcopy(mip->fmdi_ops, &ops, offsetof(fmd_hdl_ops_t, fmdo_send)); else if (version < FMD_API_VERSION_4) bcopy(mip->fmdi_ops, &ops, offsetof(fmd_hdl_ops_t, fmdo_topo)); else bcopy(mip->fmdi_ops, &ops, sizeof (ops)); if (ops.fmdo_recv == NULL) ops.fmdo_recv = (void (*)())fmd_hdl_nop; if (ops.fmdo_timeout == NULL) ops.fmdo_timeout = (void (*)())fmd_hdl_nop; if (ops.fmdo_close == NULL) ops.fmdo_close = (void (*)())fmd_hdl_nop; if (ops.fmdo_stats == NULL) ops.fmdo_stats = (void (*)())fmd_hdl_nop; if (ops.fmdo_gc == NULL) ops.fmdo_gc = (void (*)())fmd_hdl_nop; if (ops.fmdo_send == NULL) ops.fmdo_send = (int (*)())fmd_hdl_nop; if (ops.fmdo_topo == NULL) ops.fmdo_topo = (void (*)())fmd_hdl_nop; /* * Make two passes through the property array to initialize the formals * to use for processing the module's .conf file. In the first pass, * we validate the types and count the number of properties. In the * second pass we copy the strings and fill in the appropriate ops. */ for (prop = mip->fmdi_props, i = 0; prop != NULL && prop->fmdp_name != NULL; prop++, i++) { if (prop->fmdp_type >= sizeof (_fmd_prop_ops) / sizeof (_fmd_prop_ops[0])) { fmd_api_xerror(mp, EFMD_HDL_PROP, "property %s uses invalid type %u\n", prop->fmdp_name, prop->fmdp_type); return (fmd_hdl_register_error(mp, EFMD_HDL_PROP)); } } mp->mod_argc = i; mp->mod_argv = fmd_zalloc(sizeof (fmd_conf_formal_t) * i, FMD_SLEEP); prop = mip->fmdi_props; cfp = mp->mod_argv; for (i = 0; i < mp->mod_argc; i++, prop++, cfp++) { cfp->cf_name = fmd_strdup(prop->fmdp_name, FMD_SLEEP); cfp->cf_ops = _fmd_prop_ops[prop->fmdp_type]; cfp->cf_default = fmd_strdup(prop->fmdp_defv, FMD_SLEEP); } /* * If this module came from an on-disk file, compute the name of the * corresponding .conf file and parse properties from it if it exists. */ if (mp->mod_path != NULL) { (void) strlcpy(buf, mp->mod_path, sizeof (buf)); (void) fmd_strdirname(buf); (void) strlcat(buf, "/", sizeof (buf)); (void) strlcat(buf, mp->mod_name, sizeof (buf)); (void) strlcat(buf, ".conf", sizeof (buf)); if (access(buf, F_OK) == 0) conf = buf; } if ((mp->mod_conf = fmd_conf_open(conf, mp->mod_argc, mp->mod_argv, 0)) == NULL) return (fmd_hdl_register_error(mp, EFMD_MOD_CONF)); fmd_conf_propagate(fmd.d_conf, mp->mod_conf, mp->mod_name); /* * Look up the list of the libdiagcode dictionaries associated with the * module. If none were specified, use the value from daemon's config. * We only fail if the module specified an explicit dictionary. */ (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap); if (pap->cpa_argc == 0 && mp->mod_ops == &fmd_bltin_ops) (void) fmd_conf_getprop(fmd.d_conf, "self.dict", &pap); for (i = 0; i < pap->cpa_argc; i++) { if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) { fmd_api_xerror(mp, errno, "failed to open dictionary %s", pap->cpa_argv[i]); return (fmd_hdl_register_error(mp, EFMD_MOD_CONF)); } } /* * Make a copy of the handle information and store it in mod_info. We * do not need to bother copying fmdi_props since they're already read. */ mp->mod_info = fmd_alloc(sizeof (fmd_hdl_info_t), FMD_SLEEP); mp->mod_info->fmdi_desc = fmd_strdup(mip->fmdi_desc, FMD_SLEEP); mp->mod_info->fmdi_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP); mp->mod_info->fmdi_ops = fmd_alloc(sizeof (fmd_hdl_ops_t), FMD_SLEEP); bcopy(&ops, (void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t)); mp->mod_info->fmdi_props = NULL; /* * Store a copy of module version in mp for fmd_scheme_fmd_present() */ if (mp->mod_vers == NULL) mp->mod_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP); /* * Allocate an FMRI representing this module. We'll use this later * if the module decides to publish any events (e.g. list.suspects). */ mp->mod_fmri = fmd_protocol_fmri_module(mp); /* * Any subscriptions specified in the conf file are now stored in the * corresponding property. Add all of these to the dispatch queue. */ (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap); for (i = 0; i < pap->cpa_argc; i++) { fmd_dispq_insert(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]); fmd_xprt_subscribe_all(pap->cpa_argv[i]); } /* * Unlock the module and restore any pre-existing module checkpoint. * If the checkpoint is missing or corrupt, we just keep going. */ fmd_module_unlock(mp); fmd_ckpt_restore(mp); return (0); } /* * If an auxiliary thread exists for the specified module at unregistration * time, send it an asynchronous cancellation to force it to exit and then * join with it (we expect this to either succeed quickly or return ESRCH). * Once this is complete we can destroy the associated fmd_thread_t data. */ static void fmd_module_thrcancel(fmd_idspace_t *ids, id_t id, fmd_module_t *mp) { fmd_thread_t *tp = fmd_idspace_getspecific(ids, id); /* * Door service threads are not cancellable (worse - if they're * waiting in door_return then that is interrupted, but they then spin * endlessly!). Non-private door service threads are not tracked * in the module thread idspace so it's only private server threads * created via fmd_doorthr_create that we'll encounter. In most * cases the module _fini should have tidied up (e.g., calling * sysevent_evc_unbind which will cleanup door threads if * sysevent_evc_xsubscribe was used). One case that does not * clean up is sysev_fini which explicitly does not unbind the * channel, so we must skip any remaining door threads here. */ if (tp->thr_isdoor) { fmd_dprintf(FMD_DBG_MOD, "not cancelling %s private door " "thread %u\n", mp->mod_name, tp->thr_tid); fmd_thread_destroy(tp, FMD_THREAD_NOJOIN); return; } fmd_dprintf(FMD_DBG_MOD, "cancelling %s auxiliary thread %u\n", mp->mod_name, tp->thr_tid); ASSERT(tp->thr_tid == id); (void) pthread_cancel(tp->thr_tid); (void) pthread_join(tp->thr_tid, NULL); fmd_thread_destroy(tp, FMD_THREAD_NOJOIN); } void fmd_module_unregister(fmd_module_t *mp) { fmd_conf_formal_t *cfp = mp->mod_argv; const fmd_conf_path_t *pap; fmd_case_t *cp; fmd_xprt_t *xp; int i; TRACE((FMD_DBG_MOD, "unregister %p (%s)", (void *)mp, mp->mod_name)); ASSERT(fmd_module_locked(mp)); /* * If any transports are still open, they have send threads that are * using the module handle: shut them down and join with these threads. */ while ((xp = fmd_list_next(&mp->mod_transports)) != NULL) fmd_xprt_destroy(xp); /* * If any auxiliary threads exist, they may be using our module handle, * and therefore could cause a fault as soon as we start destroying it. * Module writers should clean up any threads before unregistering: we * forcibly cancel any remaining auxiliary threads before proceeding. */ fmd_idspace_apply(mp->mod_threads, (void (*)())fmd_module_thrcancel, mp); if (mp->mod_error == 0) fmd_ckpt_save(mp); /* take one more checkpoint if needed */ /* * Delete any cases associated with the module (UNSOLVED, SOLVED, or * CLOSE_WAIT) as if fmdo_close() has finished processing them. */ while ((cp = fmd_list_next(&mp->mod_cases)) != NULL) fmd_case_delete(cp); fmd_ustat_delete_references(mp->mod_ustat); (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap); for (i = 0; i < pap->cpa_argc; i++) { fmd_xprt_unsubscribe_all(pap->cpa_argv[i]); fmd_dispq_delete(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]); } fmd_conf_close(mp->mod_conf); mp->mod_conf = NULL; for (i = 0; i < mp->mod_argc; i++, cfp++) { fmd_strfree((char *)cfp->cf_name); fmd_strfree((char *)cfp->cf_default); } fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc); mp->mod_argv = NULL; mp->mod_argc = 0; nvlist_free(mp->mod_fmri); mp->mod_fmri = NULL; fmd_strfree((char *)mp->mod_info->fmdi_desc); fmd_strfree((char *)mp->mod_info->fmdi_vers); fmd_free((void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t)); fmd_free(mp->mod_info, sizeof (fmd_hdl_info_t)); mp->mod_info = NULL; fmd_eventq_abort(mp->mod_queue); } void fmd_hdl_unregister(fmd_hdl_t *hdl) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_module_unregister(mp); fmd_module_unlock(mp); } void fmd_hdl_subscribe(fmd_hdl_t *hdl, const char *class) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (fmd_conf_setprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, class) == 0) { fmd_dispq_insert(fmd.d_disp, mp->mod_queue, class); fmd_xprt_subscribe_all(class); } fmd_module_unlock(mp); } void fmd_hdl_unsubscribe(fmd_hdl_t *hdl, const char *class) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (fmd_conf_delprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, class) == 0) { fmd_xprt_unsubscribe_all(class); fmd_dispq_delete(fmd.d_disp, mp->mod_queue, class); } fmd_module_unlock(mp); fmd_eventq_cancel(mp->mod_queue, FMD_EVT_PROTOCOL, (void *)class); } void fmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec) { fmd_module_t *mp = fmd_api_module_lock(hdl); mp->mod_spec = spec; fmd_module_unlock(mp); } void * fmd_hdl_getspecific(fmd_hdl_t *hdl) { fmd_module_t *mp = fmd_api_module_lock(hdl); void *spec = mp->mod_spec; fmd_module_unlock(mp); return (spec); } void fmd_hdl_opendict(fmd_hdl_t *hdl, const char *dict) { fmd_module_t *mp = fmd_api_module_lock(hdl); const fmd_conf_path_t *pap; int i; /* * Update the dictionary property in order to preserve the list of * pathnames and expand any % tokens in the path. Then retrieve the * new dictionary names from cpa_argv[] and open them one at a time. */ (void) fmd_conf_setprop(mp->mod_conf, FMD_PROP_DICTIONARIES, dict); (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap); ASSERT(pap->cpa_argc > mp->mod_dictc); for (i = mp->mod_dictc; i < pap->cpa_argc; i++) { if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) { fmd_api_error(mp, EFMD_MOD_DICT, "failed to open dictionary %s for module %s", pap->cpa_argv[i], mp->mod_name); } } fmd_module_unlock(mp); } topo_hdl_t * fmd_hdl_topo_hold(fmd_hdl_t *hdl, int v) { fmd_module_t *mp = fmd_api_module_lock(hdl); topo_hdl_t *thp; if (v != TOPO_VERSION) { fmd_api_error(mp, EFMD_MOD_TOPO, "libtopo version mismatch: " "fmd version %d != client version %d\n", TOPO_VERSION, v); } thp = fmd_module_topo_hold(mp); ASSERT(thp != NULL); fmd_module_unlock(mp); return (thp); } void fmd_hdl_topo_rele(fmd_hdl_t *hdl, topo_hdl_t *thp) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (fmd_module_topo_rele(mp, thp) != 0) fmd_api_error(mp, EFMD_MOD_TOPO, "failed to release invalid " "topo handle: %p\n", (void *)thp); fmd_module_unlock(mp); } static void * fmd_hdl_alloc_locked(fmd_module_t *mp, size_t size, int flags) { void *data; if (mp->mod_stats->ms_memlimit.fmds_value.ui64 - mp->mod_stats->ms_memtotal.fmds_value.ui64 < size) { fmd_api_error(mp, EFMD_HDL_NOMEM, "%s's allocation of %lu " "bytes exceeds module memory limit (%llu)\n", mp->mod_name, (ulong_t)size, (u_longlong_t) mp->mod_stats->ms_memtotal.fmds_value.ui64); } if ((data = fmd_alloc(size, flags)) != NULL) mp->mod_stats->ms_memtotal.fmds_value.ui64 += size; return (data); } void * fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags) { fmd_module_t *mp = fmd_api_module_lock(hdl); void *data; data = fmd_hdl_alloc_locked(mp, size, flags); fmd_module_unlock(mp); return (data); } void * fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags) { void *data = fmd_hdl_alloc(hdl, size, flags); if (data != NULL) bzero(data, size); return (data); } static void fmd_hdl_free_locked(fmd_module_t *mp, void *data, size_t size) { fmd_free(data, size); mp->mod_stats->ms_memtotal.fmds_value.ui64 -= size; } void fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_hdl_free_locked(mp, data, size); fmd_module_unlock(mp); } char * fmd_hdl_strdup(fmd_hdl_t *hdl, const char *s, int flags) { char *p; if (s != NULL) p = fmd_hdl_alloc(hdl, strlen(s) + 1, flags); else p = NULL; if (p != NULL) (void) strcpy(p, s); return (p); } void fmd_hdl_strfree(fmd_hdl_t *hdl, char *s) { if (s != NULL) fmd_hdl_free(hdl, s, strlen(s) + 1); } void fmd_hdl_vabort(fmd_hdl_t *hdl, const char *format, va_list ap) { fmd_api_verror(fmd_api_module_lock(hdl), EFMD_HDL_ABORT, format, ap); } /*PRINTFLIKE2*/ void fmd_hdl_abort(fmd_hdl_t *hdl, const char *format, ...) { fmd_module_t *mp = fmd_api_module_lock(hdl); va_list ap; va_start(ap, format); fmd_api_verror(mp, EFMD_HDL_ABORT, format, ap); va_end(ap); } void fmd_hdl_verror(fmd_hdl_t *hdl, const char *format, va_list ap) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_api_vxerror(mp, errno, format, ap); fmd_module_unlock(mp); } /*PRINTFLIKE2*/ void fmd_hdl_error(fmd_hdl_t *hdl, const char *format, ...) { va_list ap; va_start(ap, format); fmd_hdl_verror(hdl, format, ap); va_end(ap); } void fmd_hdl_vdebug(fmd_hdl_t *hdl, const char *format, va_list ap) { fmd_module_t *mp = fmd_api_module_lock(hdl); char *msg; size_t len; char c; if (!(fmd.d_hdl_debug)) { mp->mod_stats->ms_debugdrop.fmds_value.ui64++; fmd_module_unlock(mp); return; } len = vsnprintf(&c, 1, format, ap); if ((msg = fmd_alloc(len + 2, FMD_NOSLEEP)) == NULL) { mp->mod_stats->ms_debugdrop.fmds_value.ui64++; fmd_module_unlock(mp); return; } (void) vsnprintf(msg, len + 1, format, ap); if (msg[len - 1] != '\n') (void) strcpy(&msg[len], "\n"); if (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) { (void) pthread_mutex_lock(&fmd.d_err_lock); (void) fprintf(stderr, "%s DEBUG: %s: %s", fmd.d_pname, mp->mod_name, msg); (void) pthread_mutex_unlock(&fmd.d_err_lock); } if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) { syslog(LOG_DEBUG | LOG_DAEMON, "%s DEBUG: %s: %s", fmd.d_pname, mp->mod_name, msg); } fmd_free(msg, len + 2); fmd_module_unlock(mp); } /*PRINTFLIKE2*/ void fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...) { va_list ap; va_start(ap, format); fmd_hdl_vdebug(hdl, format, ap); va_end(ap); } int32_t fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name); int32_t value = 0; if (ops == &fmd_conf_bool || ops == &fmd_conf_int32 || ops == &fmd_conf_uint32) (void) fmd_conf_getprop(mp->mod_conf, name, &value); else if (ops != NULL) { fmd_api_error(mp, EFMD_PROP_TYPE, "property %s is not of int32 type\n", name); } else { fmd_api_error(mp, EFMD_PROP_DEFN, "property %s is not defined\n", name); } fmd_module_unlock(mp); return (value); } int64_t fmd_prop_get_int64(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name); int64_t value = 0; if (ops == &fmd_conf_int64 || ops == &fmd_conf_uint64 || ops == &fmd_conf_time || ops == &fmd_conf_size) (void) fmd_conf_getprop(mp->mod_conf, name, &value); else if (ops != NULL) { fmd_api_error(mp, EFMD_PROP_TYPE, "property %s is not of int64 type\n", name); } else { fmd_api_error(mp, EFMD_PROP_DEFN, "property %s is not defined\n", name); } fmd_module_unlock(mp); return (value); } char * fmd_prop_get_string(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name); char *value = NULL; const char *s; if (ops == &fmd_conf_string) { (void) fmd_conf_getprop(mp->mod_conf, name, &s); value = fmd_strdup(s, FMD_SLEEP); } else if (ops != NULL) { fmd_api_error(mp, EFMD_PROP_TYPE, "property %s is not of string type\n", name); } else { fmd_api_error(mp, EFMD_PROP_DEFN, "property %s is not defined\n", name); } fmd_module_unlock(mp); return (value); } void fmd_prop_free_string(fmd_hdl_t *hdl, char *s) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_strfree(s); fmd_module_unlock(mp); } fmd_stat_t * fmd_stat_create(fmd_hdl_t *hdl, uint_t flags, uint_t argc, fmd_stat_t *argv) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_stat_t *ep, *sp; if (flags & ~FMD_STAT_ALLOC) { fmd_api_error(mp, EFMD_STAT_FLAGS, "invalid flags 0x%x passed to fmd_stat_create\n", flags); } if ((sp = fmd_ustat_insert(mp->mod_ustat, flags | FMD_USTAT_VALIDATE, argc, argv, &ep)) == NULL) { fmd_api_error(mp, errno, "failed to publish stat '%s'", ep->fmds_name); } fmd_module_unlock(mp); return (sp); } void fmd_stat_destroy(fmd_hdl_t *hdl, uint_t argc, fmd_stat_t *argv) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_ustat_delete(mp->mod_ustat, argc, argv); fmd_module_unlock(mp); } void fmd_stat_setstr(fmd_hdl_t *hdl, fmd_stat_t *sp, const char *s) { char *str = fmd_strdup(s, FMD_SLEEP); fmd_module_t *mp = fmd_api_module_lock(hdl); if (sp->fmds_type != FMD_TYPE_STRING) { fmd_strfree(str); fmd_api_error(mp, EFMD_STAT_TYPE, "stat '%s' is not a string\n", sp->fmds_name); } fmd_strfree(sp->fmds_value.str); sp->fmds_value.str = str; fmd_module_unlock(mp); } fmd_case_t * fmd_case_open(fmd_hdl_t *hdl, void *data) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_t *cp = fmd_case_create(mp, NULL, data); fmd_module_unlock(mp); return (cp); } fmd_case_t * fmd_case_open_uuid(fmd_hdl_t *hdl, const char *uuidstr, void *data) { fmd_module_t *mp; fmd_case_t *cp; uint_t uuidlen; uuid_t uuid; mp = fmd_api_module_lock(hdl); (void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &uuidlen); if (uuidstr == NULL) { fmd_api_error(mp, EFMD_CASE_INVAL, "NULL uuid string\n"); } else if (strnlen(uuidstr, uuidlen + 1) != uuidlen) { fmd_api_error(mp, EFMD_CASE_INVAL, "invalid uuid string: '%s' " "(expected length %d)\n", uuidstr, uuidlen); } else if (uuid_parse((char *)uuidstr, uuid) == -1) { fmd_api_error(mp, EFMD_CASE_INVAL, "cannot parse uuid string: " "'%s'\n", uuidstr); } if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuidstr)) == NULL) { cp = fmd_case_create(mp, uuidstr, data); } else { fmd_case_rele(cp); cp = NULL; } fmd_module_unlock(mp); return (cp); /* May be NULL iff case already exists */ } void fmd_case_reset(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); if (cip->ci_state >= FMD_CASE_SOLVED) { fmd_api_error(mp, EFMD_CASE_STATE, "cannot solve %s: " "case is already solved or closed\n", cip->ci_uuid); } fmd_case_reset_suspects(cp); fmd_module_unlock(mp); } void fmd_case_solve(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); if (cip->ci_state >= FMD_CASE_SOLVED) { fmd_api_error(mp, EFMD_CASE_STATE, "cannot solve %s: " "case is already solved or closed\n", cip->ci_uuid); } fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED); fmd_module_unlock(mp); } void fmd_case_close(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */ fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED); fmd_module_unlock(mp); } const char * fmd_case_uuid(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); const char *uuid = cip->ci_uuid; fmd_module_unlock(mp); return (uuid); } fmd_case_t * fmd_case_uulookup(fmd_hdl_t *hdl, const char *uuid) { fmd_module_t *cmp, *mp = fmd_api_module_lock(hdl); fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid); if (cp != NULL) { cmp = ((fmd_case_impl_t *)cp)->ci_mod; fmd_case_rele(cp); } else cmp = NULL; fmd_module_unlock(mp); return (cmp == mp ? cp : NULL); } void fmd_case_uuclose(fmd_hdl_t *hdl, const char *uuid) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid); if (cp != NULL) { fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED); fmd_case_rele(cp); } fmd_module_unlock(mp); } int fmd_case_uuclosed(fmd_hdl_t *hdl, const char *uuid) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid); fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; int rv = FMD_B_TRUE; if (cip != NULL) { rv = cip->ci_state >= FMD_CASE_CLOSE_WAIT; fmd_case_rele(cp); } fmd_module_unlock(mp); return (rv); } void fmd_case_uuresolved(fmd_hdl_t *hdl, const char *uuid) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid); if (cp != NULL) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; /* * For a proxy, we notify the diagnosing side, and then * wait for it to send us back a list.resolved. */ if (cip->ci_xprt != NULL) fmd_xprt_uuresolved(cip->ci_xprt, cip->ci_uuid); else fmd_case_transition(cp, FMD_CASE_RESOLVED, 0); fmd_case_rele(cp); } fmd_module_unlock(mp); } int fmd_case_uuisresolved(fmd_hdl_t *hdl, const char *uuid) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid); fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; int rv = FMD_B_FALSE; if (cip != NULL) { rv = (cip->ci_state >= FMD_CASE_RESOLVED); fmd_case_rele(cp); } fmd_module_unlock(mp); return (rv); } static int fmd_case_instate(fmd_hdl_t *hdl, fmd_case_t *cp, uint_t state) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); int rv = cip->ci_state >= state; fmd_module_unlock(mp); return (rv); } int fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp) { return (fmd_case_instate(hdl, cp, FMD_CASE_SOLVED)); } int fmd_case_closed(fmd_hdl_t *hdl, fmd_case_t *cp) { return (fmd_case_instate(hdl, cp, FMD_CASE_CLOSE_WAIT)); } void fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep) { fmd_module_t *mp = fmd_api_module_lock(hdl); (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */ if (fmd_case_insert_event(cp, ep)) mp->mod_stats->ms_accepted.fmds_value.ui64++; fmd_module_unlock(mp); } void fmd_case_add_serd(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_serd_elem_t *sep; fmd_serd_eng_t *sgp; if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { fmd_api_error(mp, EFMD_SERD_NAME, "failed to add events from serd engine '%s'", name); } (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */ for (sep = fmd_list_next(&sgp->sg_list); sep != NULL; sep = fmd_list_next(sep)) { if (fmd_case_insert_event(cp, sep->se_event)) mp->mod_stats->ms_accepted.fmds_value.ui64++; } fmd_module_unlock(mp); } void fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); char *class; topo_hdl_t *thp; int err; nvlist_t *rsrc = NULL, *asru_prop = NULL, *asru = NULL, *fru = NULL; char *loc = NULL, *serial = NULL; if (cip->ci_state >= FMD_CASE_SOLVED) { fmd_api_error(mp, EFMD_CASE_STATE, "cannot add suspect to " "%s: case is already solved or closed\n", cip->ci_uuid); } if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0 || class == NULL || *class == '\0') { fmd_api_error(mp, EFMD_CASE_EVENT, "cannot add suspect to " "%s: suspect event is missing a class\n", cip->ci_uuid); } thp = fmd_module_topo_hold(mp); (void) nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc); (void) nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru); (void) nvlist_lookup_nvlist(nvl, FM_FAULT_FRU, &fru); if (rsrc != NULL) { if (strncmp(class, "defect", 6) == 0) { if (asru == NULL && topo_fmri_getprop(thp, rsrc, TOPO_PGROUP_IO, TOPO_IO_MODULE, rsrc, &asru_prop, &err) == 0 && nvlist_lookup_nvlist(asru_prop, TOPO_PROP_VAL_VAL, &asru) == 0) { (void) nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru); nvlist_free(asru_prop); (void) nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru); } } else { if (topo_fmri_asru(thp, rsrc, &asru, &err) == 0) { (void) nvlist_remove(nvl, FM_FAULT_ASRU, DATA_TYPE_NVLIST); (void) nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru); nvlist_free(asru); (void) nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru); } if (topo_fmri_fru(thp, rsrc, &fru, &err) == 0) { (void) nvlist_remove(nvl, FM_FAULT_FRU, DATA_TYPE_NVLIST); (void) nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru); nvlist_free(fru); (void) nvlist_lookup_nvlist(nvl, FM_FAULT_FRU, &fru); } } } /* * Try to find the location label for this resource */ if (strncmp(class, "defect", 6) != 0) { if (fru != NULL) (void) topo_fmri_label(thp, fru, &loc, &err); else if (rsrc != NULL) (void) topo_fmri_label(thp, rsrc, &loc, &err); if (loc != NULL) { (void) nvlist_remove(nvl, FM_FAULT_LOCATION, DATA_TYPE_STRING); (void) nvlist_add_string(nvl, FM_FAULT_LOCATION, loc); topo_hdl_strfree(thp, loc); } } /* * In some cases, serial information for the resource will not be * available at enumeration but may instead be available by invoking * a dynamic property method on the FRU. In order to ensure the serial * number is persisted properly in the ASRU cache, we'll fetch the * property, if it exists, and add it to the resource and fru fmris. * If the DE has not listed a fru in the suspect, see if we can * retrieve the serial from the resource instead. */ if (fru != NULL) { (void) topo_fmri_serial(thp, fru, &serial, &err); if (serial != NULL) { (void) nvlist_add_string(fru, "serial", serial); topo_hdl_strfree(thp, serial); } } err = fmd_module_topo_rele(mp, thp); ASSERT(err == 0); fmd_case_insert_suspect(cp, nvl); fmd_module_unlock(mp); } void fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); (void) pthread_mutex_lock(&cip->ci_lock); cip->ci_data = data; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_unlock(mp); } void * fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); void *data; (void) pthread_mutex_lock(&cip->ci_lock); data = cip->ci_data; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_unlock(mp); return (data); } void fmd_case_setprincipal(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep) { fmd_module_t *mp = fmd_api_module_lock(hdl); (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */ if (fmd_case_insert_principal(cp, ep)) mp->mod_stats->ms_accepted.fmds_value.ui64++; fmd_module_unlock(mp); } fmd_event_t * fmd_case_getprincipal(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp); fmd_event_t *ep; (void) pthread_mutex_lock(&cip->ci_lock); ep = cip->ci_principal; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_unlock(mp); return (ep); } fmd_case_t * fmd_case_next(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (cp != NULL) cp = fmd_list_next(fmd_api_case_impl(mp, cp)); else cp = fmd_list_next(&mp->mod_cases); fmd_module_unlock(mp); return (cp); } fmd_case_t * fmd_case_prev(fmd_hdl_t *hdl, fmd_case_t *cp) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (cp != NULL) cp = fmd_list_prev(fmd_api_case_impl(mp, cp)); else cp = fmd_list_prev(&mp->mod_cases); fmd_module_unlock(mp); return (cp); } /* * Utility function for fmd_buf_* routines. If a case is specified, use the * case's ci_bufs hash; otherwise use the module's global mod_bufs hash. */ static fmd_buf_hash_t * fmd_buf_gethash(fmd_module_t *mp, fmd_case_t *cp) { return (cp ? &fmd_api_case_impl(mp, cp)->ci_bufs : &mp->mod_bufs); } void fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp); fmd_buf_t *bp = fmd_buf_lookup(bhp, name); if (bp == NULL) { if (fmd_strbadid(name, FMD_B_TRUE) != NULL || size == 0) { fmd_api_error(mp, EFMD_BUF_INVAL, "cannot create '%s' " "(size %lu): %s\n", name, (ulong_t)size, fmd_strerror(EFMD_BUF_INVAL)); } if (mp->mod_stats->ms_buflimit.fmds_value.ui64 - mp->mod_stats->ms_buftotal.fmds_value.ui64 < size) { fmd_api_error(mp, EFMD_BUF_LIMIT, "cannot create '%s': " "buf limit exceeded (%llu)\n", name, (u_longlong_t) mp->mod_stats->ms_buflimit.fmds_value.ui64); } mp->mod_stats->ms_buftotal.fmds_value.ui64 += size; bp = fmd_buf_insert(bhp, name, size); } else { fmd_api_error(mp, EFMD_BUF_EXISTS, "cannot create '%s': buffer already exists\n", name); } if (cp != NULL) fmd_case_setdirty(cp); else fmd_module_setdirty(mp); fmd_module_unlock(mp); } void fmd_buf_destroy(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp); fmd_buf_t *bp = fmd_buf_lookup(bhp, name); if (bp != NULL) { mp->mod_stats->ms_buftotal.fmds_value.ui64 -= bp->buf_size; fmd_buf_delete(bhp, name); if (cp != NULL) fmd_case_setdirty(cp); else fmd_module_setdirty(mp); } fmd_module_unlock(mp); } void fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, void *buf, size_t size) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_buf_t *bp = fmd_buf_lookup(fmd_buf_gethash(mp, cp), name); if (bp == NULL) { fmd_api_error(mp, EFMD_BUF_NOENT, "no buf named '%s' is " "associated with %s\n", name, cp ? "case" : "module"); } bcopy(bp->buf_data, buf, MIN(bp->buf_size, size)); if (size > bp->buf_size) bzero((char *)buf + bp->buf_size, size - bp->buf_size); fmd_module_unlock(mp); } void fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, const void *buf, size_t size) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp); fmd_buf_t *bp = fmd_buf_lookup(bhp, name); if (bp == NULL) { if (fmd_strbadid(name, FMD_B_TRUE) != NULL || size == 0) { fmd_api_error(mp, EFMD_BUF_INVAL, "cannot write '%s' " "(size %lu): %s\n", name, (ulong_t)size, fmd_strerror(EFMD_BUF_INVAL)); } if (mp->mod_stats->ms_buflimit.fmds_value.ui64 - mp->mod_stats->ms_buftotal.fmds_value.ui64 < size) { fmd_api_error(mp, EFMD_BUF_LIMIT, "cannot write '%s': " "buf limit exceeded (%llu)\n", name, (u_longlong_t) mp->mod_stats->ms_buflimit.fmds_value.ui64); } mp->mod_stats->ms_buftotal.fmds_value.ui64 += size; bp = fmd_buf_insert(bhp, name, size); } else if (size > bp->buf_size) { fmd_api_error(mp, EFMD_BUF_OFLOW, "write to buf '%s' overflows buf size (%lu > %lu)\n", name, (ulong_t)size, (ulong_t)bp->buf_size); } bcopy(buf, bp->buf_data, MIN(bp->buf_size, size)); bp->buf_flags |= FMD_BUF_DIRTY; if (cp != NULL) fmd_case_setdirty(cp); else fmd_module_setdirty(mp); fmd_module_unlock(mp); } size_t fmd_buf_size(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp); fmd_buf_t *bp; size_t size; if ((bp = fmd_buf_lookup(bhp, name)) != NULL) size = bp->buf_size; else size = 0; fmd_module_unlock(mp); return (size); } void fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL) { fmd_api_error(mp, EFMD_SERD_EXISTS, "failed to create serd engine '%s': %s\n", name, fmd_strerror(EFMD_SERD_EXISTS)); } (void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t); fmd_module_setdirty(mp); fmd_module_unlock(mp); } void fmd_serd_destroy(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_serd_eng_delete(&mp->mod_serds, name); fmd_module_setdirty(mp); fmd_module_unlock(mp); } int fmd_serd_exists(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv = (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL); fmd_module_unlock(mp); return (rv); } void fmd_serd_reset(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_serd_eng_t *sgp; if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { fmd_api_error(mp, EFMD_SERD_NAME, "serd engine '%s' does not exist\n", name); } fmd_serd_eng_reset(sgp); fmd_module_setdirty(mp); fmd_module_unlock(mp); } int fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_serd_eng_t *sgp; int err; if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { fmd_api_error(mp, EFMD_SERD_NAME, "failed to add record to serd engine '%s'", name); } err = fmd_serd_eng_record(sgp, ep); if (sgp->sg_flags & FMD_SERD_DIRTY) fmd_module_setdirty(mp); fmd_module_unlock(mp); return (err); } int fmd_serd_fired(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_serd_eng_t *sgp; int err; if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { fmd_api_error(mp, EFMD_SERD_NAME, "serd engine '%s' does not exist\n", name); } err = fmd_serd_eng_fired(sgp); fmd_module_unlock(mp); return (err); } int fmd_serd_empty(fmd_hdl_t *hdl, const char *name) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_serd_eng_t *sgp; int empty; if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) { fmd_api_error(mp, EFMD_SERD_NAME, "serd engine '%s' does not exist\n", name); } empty = fmd_serd_eng_empty(sgp); fmd_module_unlock(mp); return (empty); } pthread_t fmd_thr_create(fmd_hdl_t *hdl, void (*func)(void *), void *arg) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_thread_t *tp; pthread_t tid; if (mp->mod_stats->ms_thrtotal.fmds_value.ui32 >= mp->mod_stats->ms_thrlimit.fmds_value.ui32) { fmd_api_error(mp, EFMD_THR_LIMIT, "%s request to create an " "auxiliary thread exceeds module thread limit (%u)\n", mp->mod_name, mp->mod_stats->ms_thrlimit.fmds_value.ui32); } if ((tp = fmd_thread_create(mp, func, arg)) == NULL) { fmd_api_error(mp, EFMD_THR_CREATE, "failed to create auxiliary thread"); } tid = tp->thr_tid; mp->mod_stats->ms_thrtotal.fmds_value.ui32++; (void) fmd_idspace_xalloc(mp->mod_threads, tid, tp); fmd_module_unlock(mp); return (tid); } void fmd_thr_destroy(fmd_hdl_t *hdl, pthread_t tid) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_thread_t *tp; int err; if (pthread_self() == tid) { fmd_api_error(mp, EFMD_THR_INVAL, "auxiliary thread tried to " "destroy itself (tid %u)\n", tid); } if ((tp = fmd_idspace_getspecific(mp->mod_threads, tid)) == NULL) { fmd_api_error(mp, EFMD_THR_INVAL, "auxiliary thread tried to " "destroy an invalid thread (tid %u)\n", tid); } /* * Wait for the specified thread to exit and then join with it. Since * the thread may need to make API calls in order to complete its work * we must sleep with the module lock unheld, and then reacquire it. */ fmd_module_unlock(mp); err = pthread_join(tid, NULL); mp = fmd_api_module_lock(hdl); /* * Since pthread_join() was called without the module lock held, if * multiple callers attempted to destroy the same auxiliary thread * simultaneously, one will succeed and the others will get ESRCH. * Therefore we silently ignore ESRCH but only allow the caller who * succeessfully joined with the auxiliary thread to destroy it. */ if (err != 0 && err != ESRCH) { fmd_api_error(mp, EFMD_THR_JOIN, "failed to join with auxiliary thread %u\n", tid); } if (err == 0) { fmd_thread_destroy(tp, FMD_THREAD_NOJOIN); mp->mod_stats->ms_thrtotal.fmds_value.ui32--; (void) fmd_idspace_free(mp->mod_threads, tid); } fmd_module_unlock(mp); } void fmd_thr_signal(fmd_hdl_t *hdl, pthread_t tid) { fmd_module_t *mp = fmd_api_module_lock(hdl); if (tid != mp->mod_thread->thr_tid && fmd_idspace_getspecific(mp->mod_threads, tid) == NULL) { fmd_api_error(mp, EFMD_THR_INVAL, "tid %u is not a valid " "thread id for module %s\n", tid, mp->mod_name); } (void) pthread_kill(tid, fmd.d_thr_sig); fmd_module_unlock(mp); } void fmd_thr_checkpoint(fmd_hdl_t *hdl) { fmd_module_t *mp = fmd_api_module_lock(hdl); pthread_t tid = pthread_self(); if (tid == mp->mod_thread->thr_tid || fmd_idspace_getspecific(mp->mod_threads, tid) == NULL) { fmd_api_error(mp, EFMD_THR_INVAL, "tid %u is not a valid " "auxiliary thread id for module %s\n", tid, mp->mod_name); } fmd_ckpt_save(mp); fmd_module_unlock(mp); } /*ARGSUSED3*/ int fmd_doorthr_create(door_info_t *dip, void *(*crf)(void *), void *crarg, void *cookie) { fmd_thread_t *old_tp, *new_tp; fmd_module_t *mp; pthread_t tid; /* * We're called either during initial door_xcreate or during * a depletion callback. In both cases the current thread * is already setup so we can retrieve the fmd_thread_t. * If not then we panic. The new thread will be associated with * the same module as the old. * * If dip == NULL we're being called as part of the * sysevent_bind_subscriber hack - see comments there. */ if ((old_tp = pthread_getspecific(fmd.d_key)) == NULL) fmd_panic("fmd_doorthr_create from unrecognized thread\n"); mp = old_tp->thr_mod; (void) fmd_api_module_lock((fmd_hdl_t *)mp); if (dip && mp->mod_stats->ms_doorthrtotal.fmds_value.ui32 >= mp->mod_stats->ms_doorthrlimit.fmds_value.ui32) { fmd_module_unlock(mp); (void) fmd_dprintf(FMD_DBG_XPRT, "door server %s for %p " "not attemped - at max\n", dip->di_attributes & DOOR_DEPLETION_CB ? "depletion callback" : "startup", (void *)dip); return (0); } if ((new_tp = fmd_doorthread_create(mp, (fmd_thread_f *)(uintptr_t)crf, crarg)) != NULL) { tid = new_tp->thr_tid; mp->mod_stats->ms_doorthrtotal.fmds_value.ui32++; (void) fmd_idspace_xalloc(mp->mod_threads, tid, new_tp); } fmd_module_unlock(mp); if (dip) { fmd_dprintf(FMD_DBG_XPRT, "door server startup for %p %s\n", (void *)dip, new_tp ? "successful" : "failed"); } return (new_tp ? 1 : -1); } /*ARGSUSED*/ void fmd_doorthr_setup(void *cookie) { (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); } id_t fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_modtimer_t *t; id_t id; if (delta < 0) { fmd_api_error(mp, EFMD_TIMER_INVAL, "timer delta %lld is not a valid interval\n", delta); } t = fmd_alloc(sizeof (fmd_modtimer_t), FMD_SLEEP); t->mt_mod = mp; t->mt_arg = arg; t->mt_id = -1; if ((id = fmd_timerq_install(fmd.d_timers, mp->mod_timerids, (fmd_timer_f *)fmd_module_timeout, t, ep, delta)) == -1) { fmd_free(t, sizeof (fmd_modtimer_t)); fmd_api_error(mp, EFMD_TIMER_LIMIT, "failed to install timer +%lld", delta); } fmd_module_unlock(mp); return (id); } void fmd_timer_remove(fmd_hdl_t *hdl, id_t id) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_modtimer_t *t; if (!fmd_idspace_valid(mp->mod_timerids, id)) { fmd_api_error(mp, EFMD_TIMER_INVAL, "id %ld is not a valid timer id\n", id); } /* * If the timer has not fired (t != NULL), remove it from the timer * queue. If the timer has fired (t == NULL), we could be in one of * two situations: a) we are processing the timer callback or b) * the timer event is on the module queue awaiting dispatch. For a), * fmd_timerq_remove() will wait for the timer callback function * to complete and queue an event for dispatch. For a) and b), * we cancel the outstanding timer event from the module's dispatch * queue. */ if ((t = fmd_timerq_remove(fmd.d_timers, mp->mod_timerids, id)) != NULL) fmd_free(t, sizeof (fmd_modtimer_t)); fmd_module_unlock(mp); fmd_eventq_cancel(mp->mod_queue, FMD_EVT_TIMEOUT, (void *)id); } static nvlist_t * fmd_nvl_create_suspect(fmd_hdl_t *hdl, const char *class, uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc, const char *pfx, boolean_t chkpfx) { fmd_module_t *mp; nvlist_t *nvl; mp = fmd_api_module_lock(hdl); if (class == NULL || class[0] == '\0' || chkpfx == B_TRUE && strncmp(class, pfx, strlen(pfx)) != 0) fmd_api_error(mp, EFMD_NVL_INVAL, "invalid %s class: '%s'\n", pfx, class ? class : "(empty)"); nvl = fmd_protocol_fault(class, certainty, asru, fru, rsrc, NULL); fmd_module_unlock(mp); return (nvl); } nvlist_t * fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc) { /* * We can't enforce that callers only specifiy classes matching * fault.* since there are already a number of modules that * use fmd_nvl_create_fault to create a defect event. Since * fmd_nvl_create_{fault,defect} are equivalent, for now anyway, * no harm is done. So call fmd_nvl_create_suspect with last * argument B_FALSE. */ return (fmd_nvl_create_suspect(hdl, class, certainty, asru, fru, rsrc, FM_FAULT_CLASS ".", B_FALSE)); } nvlist_t * fmd_nvl_create_defect(fmd_hdl_t *hdl, const char *class, uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc) { return (fmd_nvl_create_suspect(hdl, class, certainty, asru, fru, rsrc, FM_DEFECT_CLASS ".", B_TRUE)); } const nvlist_t * fmd_hdl_fmauth(fmd_hdl_t *hdl) { fmd_module_t *mp = fmd_api_module_lock(hdl); const nvlist_t *auth; auth = (const nvlist_t *)fmd.d_rmod->mod_fmri; fmd_module_unlock(mp); return (auth); } const nvlist_t * fmd_hdl_modauth(fmd_hdl_t *hdl) { fmd_module_t *mp = fmd_api_module_lock(hdl); const nvlist_t *auth; auth = (const nvlist_t *)mp->mod_fmri; fmd_module_unlock(mp); return (auth); } int fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern) { fmd_module_t *mp = fmd_api_module_lock(hdl); char *class; int rv; rv = (nvl != NULL && nvlist_lookup_string(nvl, FM_CLASS, &class) == 0 && fmd_strmatch(class, pattern)); fmd_module_unlock(mp); return (rv); } int fmd_nvl_fmri_expand(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_expand(nvl); fmd_module_unlock(mp); return (rv); } int fmd_nvl_fmri_present(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_present(nvl); fmd_module_unlock(mp); if (rv < 0) { fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for " "fmd_nvl_fmri_present\n"); } return (rv); } int fmd_nvl_fmri_replaced(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_replaced(nvl); fmd_module_unlock(mp); return (rv); } int fmd_nvl_fmri_unusable(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_unusable(nvl); fmd_module_unlock(mp); if (rv < 0) { fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for " "fmd_nvl_fmri_unusable\n"); } return (rv); } int fmd_nvl_fmri_retire(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_retire(nvl); fmd_module_unlock(mp); return (rv); } int fmd_nvl_fmri_unretire(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_unretire(nvl); fmd_module_unlock(mp); return (rv); } int fmd_nvl_fmri_service_state(fmd_hdl_t *hdl, nvlist_t *nvl) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } rv = fmd_fmri_service_state(nvl); if (rv < 0) rv = fmd_fmri_unusable(nvl) ? FMD_SERVICE_STATE_UNUSABLE : FMD_SERVICE_STATE_OK; fmd_module_unlock(mp); if (rv < 0) { fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for " "fmd_nvl_fmri_service_state\n"); } return (rv); } typedef struct { const char *class; int *rvp; } fmd_has_fault_arg_t; static void fmd_rsrc_has_fault(fmd_asru_link_t *alp, void *arg) { fmd_has_fault_arg_t *fhfp = (fmd_has_fault_arg_t *)arg; char *class; if (fhfp->class == NULL) { if (alp->al_flags & FMD_ASRU_FAULTY) *fhfp->rvp = 1; } else { if ((alp->al_flags & FMD_ASRU_FAULTY) && alp->al_event != NULL && nvlist_lookup_string(alp->al_event, FM_CLASS, &class) == 0 && fmd_strmatch(class, fhfp->class)) *fhfp->rvp = 1; } } int fmd_nvl_fmri_has_fault(fmd_hdl_t *hdl, nvlist_t *nvl, int type, char *class) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_asru_hash_t *ahp = fmd.d_asrus; int rv = 0; char *name; int namelen; fmd_has_fault_arg_t fhf; if (nvl == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist %p\n", (void *)nvl); } if ((namelen = fmd_fmri_nvl2str(nvl, NULL, 0)) == -1) fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist: %p\n", (void *)nvl); name = fmd_alloc(namelen + 1, FMD_SLEEP); if (fmd_fmri_nvl2str(nvl, name, namelen + 1) == -1) { if (name != NULL) fmd_free(name, namelen + 1); fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist: %p\n", (void *)nvl); } fhf.class = class; fhf.rvp = &rv; if (type == FMD_HAS_FAULT_RESOURCE) fmd_asru_hash_apply_by_rsrc(ahp, name, fmd_rsrc_has_fault, &fhf); else if (type == FMD_HAS_FAULT_ASRU) fmd_asru_hash_apply_by_asru(ahp, name, fmd_rsrc_has_fault, &fhf); else if (type == FMD_HAS_FAULT_FRU) fmd_asru_hash_apply_by_fru(ahp, name, fmd_rsrc_has_fault, &fhf); if (name != NULL) fmd_free(name, namelen + 1); fmd_module_unlock(mp); return (rv); } int fmd_nvl_fmri_contains(fmd_hdl_t *hdl, nvlist_t *n1, nvlist_t *n2) { fmd_module_t *mp = fmd_api_module_lock(hdl); int rv; if (n1 == NULL || n2 == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist(s): %p, %p\n", (void *)n1, (void *)n2); } rv = fmd_fmri_contains(n1, n2); fmd_module_unlock(mp); if (rv < 0) { fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for " "fmd_nvl_fmri_contains\n"); } return (rv); } nvlist_t * fmd_nvl_fmri_translate(fmd_hdl_t *hdl, nvlist_t *fmri, nvlist_t *auth) { fmd_module_t *mp = fmd_api_module_lock(hdl); nvlist_t *xfmri; if (fmri == NULL || auth == NULL) { fmd_api_error(mp, EFMD_NVL_INVAL, "invalid nvlist(s): %p, %p\n", (void *)fmri, (void *)auth); } xfmri = fmd_fmri_translate(fmri, auth); fmd_module_unlock(mp); return (xfmri); } static int fmd_nvl_op_init(nv_alloc_t *ops, va_list ap) { fmd_module_t *mp = va_arg(ap, fmd_module_t *); ops->nva_arg = mp; return (0); } static void * fmd_nvl_op_alloc_sleep(nv_alloc_t *ops, size_t size) { fmd_module_t *mp = ops->nva_arg; return (fmd_hdl_alloc_locked(mp, size, FMD_SLEEP)); } static void * fmd_nvl_op_alloc_nosleep(nv_alloc_t *ops, size_t size) { fmd_module_t *mp = ops->nva_arg; return (fmd_hdl_alloc_locked(mp, size, FMD_NOSLEEP)); } static void fmd_nvl_op_free(nv_alloc_t *ops, void *data, size_t size) { fmd_module_t *mp = ops->nva_arg; fmd_hdl_free_locked(mp, data, size); } nv_alloc_ops_t fmd_module_nva_ops_sleep = { fmd_nvl_op_init, NULL, fmd_nvl_op_alloc_sleep, fmd_nvl_op_free, NULL }; nv_alloc_ops_t fmd_module_nva_ops_nosleep = { fmd_nvl_op_init, NULL, fmd_nvl_op_alloc_nosleep, fmd_nvl_op_free, NULL }; nvlist_t * fmd_nvl_alloc(fmd_hdl_t *hdl, int flags) { fmd_module_t *mp = fmd_api_module_lock(hdl); nv_alloc_t *nva; nvlist_t *nvl; int ret; if (flags == FMD_SLEEP) nva = &mp->mod_nva_sleep; else nva = &mp->mod_nva_nosleep; ret = nvlist_xalloc(&nvl, NV_UNIQUE_NAME, nva); fmd_module_unlock(mp); if (ret != 0) return (NULL); else return (nvl); } nvlist_t * fmd_nvl_dup(fmd_hdl_t *hdl, nvlist_t *src, int flags) { fmd_module_t *mp = fmd_api_module_lock(hdl); nv_alloc_t *nva; nvlist_t *nvl; int ret; if (flags == FMD_SLEEP) nva = &mp->mod_nva_sleep; else nva = &mp->mod_nva_nosleep; ret = nvlist_xdup(src, &nvl, nva); fmd_module_unlock(mp); if (ret != 0) return (NULL); else return (nvl); } /*ARGSUSED*/ void fmd_repair_fru(fmd_hdl_t *hdl, const char *fmri) { int err; fmd_asru_rep_arg_t fara; fara.fara_reason = FMD_ASRU_REPAIRED; fara.fara_bywhat = FARA_BY_FRU; fara.fara_rval = &err; fmd_asru_hash_apply_by_fru(fmd.d_asrus, (char *)fmri, fmd_asru_repaired, &fara); } /*ARGSUSED*/ int fmd_repair_asru(fmd_hdl_t *hdl, const char *fmri) { int err = FARA_ERR_RSRCNOTF; fmd_asru_rep_arg_t fara; fara.fara_reason = FMD_ASRU_REPAIRED; fara.fara_rval = &err; fara.fara_uuid = NULL; fara.fara_bywhat = FARA_BY_ASRU; fmd_asru_hash_apply_by_asru(fmd.d_asrus, fmri, fmd_asru_repaired, &fara); return (err); } int fmd_event_local(fmd_hdl_t *hdl, fmd_event_t *ep) { if (hdl == NULL || ep == NULL) { fmd_api_error(fmd_api_module_lock(hdl), EFMD_EVENT_INVAL, "NULL parameter specified to fmd_event_local\n"); } return (((fmd_event_impl_t *)ep)->ev_flags & FMD_EVF_LOCAL); } /*ARGSUSED*/ uint64_t fmd_event_ena_create(fmd_hdl_t *hdl) { return (fmd_ena()); } fmd_xprt_t * fmd_xprt_open(fmd_hdl_t *hdl, uint_t flags, nvlist_t *auth, void *data) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_xprt_t *xp; if (flags & ~FMD_XPRT_CMASK) { fmd_api_error(mp, EFMD_XPRT_INVAL, "invalid transport flags 0x%x\n", flags); } if ((flags & FMD_XPRT_RDWR) != FMD_XPRT_RDWR && (flags & FMD_XPRT_RDWR) != FMD_XPRT_RDONLY) { fmd_api_error(mp, EFMD_XPRT_INVAL, "cannot open write-only transport\n"); } if (mp->mod_stats->ms_xprtopen.fmds_value.ui32 >= mp->mod_stats->ms_xprtlimit.fmds_value.ui32) { fmd_api_error(mp, EFMD_XPRT_LIMIT, "%s request to create a " "transport exceeds module transport limit (%u)\n", mp->mod_name, mp->mod_stats->ms_xprtlimit.fmds_value.ui32); } if ((xp = fmd_xprt_create(mp, flags, auth, data)) == NULL) fmd_api_error(mp, errno, "cannot create transport"); fmd_module_unlock(mp); return (xp); } void fmd_xprt_close(fmd_hdl_t *hdl, fmd_xprt_t *xp) { fmd_module_t *mp = fmd_api_module_lock(hdl); fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp); /* * Although this could be supported, it doesn't seem necessary or worth * the trouble. For now, just detect this and trigger a module abort. * If it is needed, transports should grow reference counts and a new * event type will need to be enqueued for the main thread to reap it. */ if (xip->xi_thread != NULL && xip->xi_thread->thr_tid == pthread_self()) { fmd_api_error(mp, EFMD_XPRT_INVAL, "fmd_xprt_close() cannot be called from fmdo_send()\n"); } fmd_xprt_destroy(xp); fmd_module_unlock(mp); } void fmd_xprt_post(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) { nv_alloc_t *nva = nvlist_lookup_nv_alloc(nvl); fmd_module_t *mp = fmd_api_module(hdl); fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp); nvlist_t *tmp; /* * If this event was allocated using the module-specific nvlist ops, we * need to create a copy using the standard fmd nvlist ops. Otherwise, * the event may persist after the module has been unloaded and we'll * die when attempting to free the nvlist. */ if (nva == &mp->mod_nva_sleep || nva == &mp->mod_nva_nosleep) { (void) nvlist_xdup(nvl, &tmp, &fmd.d_nva); nvlist_free(nvl); nvl = tmp; } /* * fmd_xprt_recv() must block during startup waiting for fmd to globally * clear FMD_XPRT_DSUSPENDED. As such, we can't allow it to be called * from a module's _fmd_init() routine, because that would block * fmd from completing initial module loading, resulting in a deadlock. */ if ((xip->xi_flags & FMD_XPRT_ISUSPENDED) && (pthread_self() == xip->xi_queue->eq_mod->mod_thread->thr_tid)) { fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL, "fmd_xprt_post() cannot be called from _fmd_init()\n"); } fmd_xprt_recv(xp, nvl, hrt, FMD_B_FALSE); } void fmd_xprt_log(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt) { fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp); /* * fmd_xprt_recv() must block during startup waiting for fmd to globally * clear FMD_XPRT_DSUSPENDED. As such, we can't allow it to be called * from a module's _fmd_init() routine, because that would block * fmd from completing initial module loading, resulting in a deadlock. */ if ((xip->xi_flags & FMD_XPRT_ISUSPENDED) && (pthread_self() == xip->xi_queue->eq_mod->mod_thread->thr_tid)) { fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL, "fmd_xprt_log() cannot be called from _fmd_init()\n"); } fmd_xprt_recv(xp, nvl, hrt, FMD_B_TRUE); } void fmd_xprt_suspend(fmd_hdl_t *hdl, fmd_xprt_t *xp) { (void) fmd_api_transport_impl(hdl, xp); /* validate 'xp' */ fmd_xprt_xsuspend(xp, FMD_XPRT_SUSPENDED); } void fmd_xprt_resume(fmd_hdl_t *hdl, fmd_xprt_t *xp) { (void) fmd_api_transport_impl(hdl, xp); /* validate 'xp' */ fmd_xprt_xresume(xp, FMD_XPRT_SUSPENDED); } int fmd_xprt_error(fmd_hdl_t *hdl, fmd_xprt_t *xp) { fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp); return (xip->xi_state == _fmd_xprt_state_err); } /* * Translate all FMRIs in the specified name-value pair list for the specified * FMRI authority, and return a new name-value pair list for the translation. * This function is the recursive engine used by fmd_xprt_translate(), below. */ static nvlist_t * fmd_xprt_xtranslate(nvlist_t *nvl, nvlist_t *auth) { uint_t i, j, n; nvpair_t *nvp, **nvps; uint_t nvpslen = 0; char *name; size_t namelen = 0; nvlist_t **a, **b; nvlist_t *l, *r; data_type_t type; char *s; int err; (void) nvlist_xdup(nvl, &nvl, &fmd.d_nva); /* * Count up the number of name-value pairs in 'nvl' and compute the * maximum length of a name used in this list for use below. */ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp), nvpslen++) { size_t len = strlen(nvpair_name(nvp)); namelen = MAX(namelen, len); } nvps = alloca(sizeof (nvpair_t *) * nvpslen); name = alloca(namelen + 1); /* * Store a snapshot of the name-value pairs in 'nvl' into nvps[] so * that we can iterate over the original pairs in the loop below while * performing arbitrary insert and delete operations on 'nvl' itself. */ for (i = 0, nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) nvps[i++] = nvp; /* * Now iterate over the snapshot of the name-value pairs. If we find a * value that is of type NVLIST or NVLIST_ARRAY, we translate that * object by either calling ourself recursively on it, or calling into * fmd_fmri_translate() if the object is an FMRI. We then rip out the * original name-value pair and replace it with the translated one. */ for (i = 0; i < nvpslen; i++) { nvp = nvps[i]; type = nvpair_type(nvp); switch (type) { case DATA_TYPE_NVLIST_ARRAY: if (nvpair_value_nvlist_array(nvp, &a, &n) != 0 || a == NULL || n == 0) continue; /* array is zero-sized; skip it */ b = fmd_alloc(sizeof (nvlist_t *) * n, FMD_SLEEP); /* * If the first array nvlist element looks like an FMRI * then assume the other elements are FMRIs as well. * If any b[j]'s can't be translated, then EINVAL will * be returned from nvlist_add_nvlist_array() below. */ if (nvlist_lookup_string(*a, FM_FMRI_SCHEME, &s) == 0) { for (j = 0; j < n; j++) b[j] = fmd_fmri_translate(a[j], auth); } else { for (j = 0; j < n; j++) b[j] = fmd_xprt_xtranslate(a[j], auth); } (void) strcpy(name, nvpair_name(nvp)); (void) nvlist_remove(nvl, name, type); err = nvlist_add_nvlist_array(nvl, name, b, n); for (j = 0; j < n; j++) nvlist_free(b[j]); fmd_free(b, sizeof (nvlist_t *) * n); if (err != 0) { nvlist_free(nvl); errno = err; return (NULL); } break; case DATA_TYPE_NVLIST: if (nvpair_value_nvlist(nvp, &l) == 0 && nvlist_lookup_string(l, FM_FMRI_SCHEME, &s) == 0) r = fmd_fmri_translate(l, auth); else r = fmd_xprt_xtranslate(l, auth); if (r == NULL) { nvlist_free(nvl); return (NULL); } (void) strcpy(name, nvpair_name(nvp)); (void) nvlist_remove(nvl, name, type); (void) nvlist_add_nvlist(nvl, name, r); nvlist_free(r); break; } } return (nvl); } nvlist_t * fmd_xprt_translate(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep) { fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp); if (xip->xi_auth == NULL) { fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL, "no authority defined for transport %p\n", (void *)xp); } return (fmd_xprt_xtranslate(FMD_EVENT_NVL(ep), xip->xi_auth)); } /*ARGSUSED*/ void fmd_xprt_add_domain(fmd_hdl_t *hdl, nvlist_t *nvl, char *domain) { nvpair_t *nvp, *nvp2; nvlist_t *nvl2, *nvl3; char *class; if (nvl == NULL || domain == NULL) return; for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) { if (strcmp(nvpair_name(nvp), FM_CLASS) == 0) { (void) nvpair_value_string(nvp, &class); if (strcmp(class, FM_LIST_SUSPECT_CLASS) != 0) return; } } for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) { if (strcmp(nvpair_name(nvp), FM_SUSPECT_DE) == 0) { (void) nvpair_value_nvlist(nvp, &nvl2); for (nvp2 = nvlist_next_nvpair(nvl2, NULL); nvp2 != NULL; nvp2 = nvlist_next_nvpair(nvl2, nvp2)) { if (strcmp(nvpair_name(nvp2), FM_FMRI_AUTHORITY) == 0) { (void) nvpair_value_nvlist(nvp2, &nvl3); (void) nvlist_add_string(nvl3, FM_FMRI_AUTH_DOMAIN, domain); break; } } break; } } } void fmd_xprt_setspecific(fmd_hdl_t *hdl, fmd_xprt_t *xp, void *data) { fmd_api_transport_impl(hdl, xp)->xi_data = data; } void * fmd_xprt_getspecific(fmd_hdl_t *hdl, fmd_xprt_t *xp) { return (fmd_api_transport_impl(hdl, xp)->xi_data); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_API_H #define _FMD_API_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Fault Management Daemon Client Interfaces * * Note: The contents of this file are private to the implementation of the * Solaris system and FMD subsystem and are subject to change at any time * without notice. Applications and drivers using these interfaces will fail * to run on future releases. These interfaces should not be used for any * purpose until they are publicly documented for use outside of Sun. */ #define FMD_API_VERSION_1 1 #define FMD_API_VERSION_2 2 #define FMD_API_VERSION_3 3 #define FMD_API_VERSION_4 4 #define FMD_API_VERSION_5 5 #define FMD_API_VERSION FMD_API_VERSION_5 typedef struct fmd_hdl fmd_hdl_t; typedef struct fmd_event fmd_event_t; typedef struct fmd_case fmd_case_t; typedef struct fmd_xprt fmd_xprt_t; struct topo_hdl; #define FMD_B_FALSE 0 /* false value for booleans as int */ #define FMD_B_TRUE 1 /* true value for booleans as int */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #define FMD_TYPE_BOOL 0 /* int */ #define FMD_TYPE_INT32 1 /* int32_t */ #define FMD_TYPE_UINT32 2 /* uint32_t */ #define FMD_TYPE_INT64 3 /* int64_t */ #define FMD_TYPE_UINT64 4 /* uint64_t */ #define FMD_TYPE_STRING 5 /* const char* */ #define FMD_TYPE_TIME 6 /* uint64_t */ #define FMD_TYPE_SIZE 7 /* uint64_t */ typedef struct fmd_prop { const char *fmdp_name; /* property name */ uint_t fmdp_type; /* property type (see above) */ const char *fmdp_defv; /* default value */ } fmd_prop_t; typedef struct fmd_stat { char fmds_name[32]; /* statistic name */ uint_t fmds_type; /* statistic type (see above) */ char fmds_desc[64]; /* statistic description */ union { int bool; /* FMD_TYPE_BOOL */ int32_t i32; /* FMD_TYPE_INT32 */ uint32_t ui32; /* FMD_TYPE_UINT32 */ int64_t i64; /* FMD_TYPE_INT64 */ uint64_t ui64; /* FMD_TYPE_UINT64, TIME, SIZE */ char *str; /* FMD_TYPE_STRING */ } fmds_value; } fmd_stat_t; typedef struct fmd_hdl_ops { void (*fmdo_recv)(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *); void (*fmdo_timeout)(fmd_hdl_t *, id_t, void *); void (*fmdo_close)(fmd_hdl_t *, fmd_case_t *); void (*fmdo_stats)(fmd_hdl_t *); void (*fmdo_gc)(fmd_hdl_t *); int (*fmdo_send)(fmd_hdl_t *, fmd_xprt_t *, fmd_event_t *, nvlist_t *); void (*fmdo_topo)(fmd_hdl_t *, struct topo_hdl *); } fmd_hdl_ops_t; #define FMD_SEND_SUCCESS 0 /* fmdo_send queued event */ #define FMD_SEND_FAILED 1 /* fmdo_send unrecoverable error */ #define FMD_SEND_RETRY 2 /* fmdo_send requests retry */ typedef struct fmd_hdl_info { const char *fmdi_desc; /* fmd client description string */ const char *fmdi_vers; /* fmd client version string */ const fmd_hdl_ops_t *fmdi_ops; /* ops vector for client */ const fmd_prop_t *fmdi_props; /* array of configuration props */ } fmd_hdl_info_t; extern void _fmd_init(fmd_hdl_t *); extern void _fmd_fini(fmd_hdl_t *); extern int fmd_hdl_register(fmd_hdl_t *, int, const fmd_hdl_info_t *); extern void fmd_hdl_unregister(fmd_hdl_t *); extern void fmd_hdl_subscribe(fmd_hdl_t *, const char *); extern void fmd_hdl_unsubscribe(fmd_hdl_t *, const char *); extern void fmd_hdl_setspecific(fmd_hdl_t *, void *); extern void *fmd_hdl_getspecific(fmd_hdl_t *); extern void fmd_hdl_opendict(fmd_hdl_t *, const char *); extern struct topo_hdl *fmd_hdl_topo_hold(fmd_hdl_t *, int); extern void fmd_hdl_topo_rele(fmd_hdl_t *, struct topo_hdl *); #define FMD_NOSLEEP 0x0 /* do not sleep or retry on failure */ #define FMD_SLEEP 0x1 /* sleep or retry if alloc fails */ extern void *fmd_hdl_alloc(fmd_hdl_t *, size_t, int); extern void *fmd_hdl_zalloc(fmd_hdl_t *, size_t, int); extern void fmd_hdl_free(fmd_hdl_t *, void *, size_t); extern char *fmd_hdl_strdup(fmd_hdl_t *, const char *, int); extern void fmd_hdl_strfree(fmd_hdl_t *, char *); extern void fmd_hdl_vabort(fmd_hdl_t *, const char *, va_list) __NORETURN; extern void fmd_hdl_abort(fmd_hdl_t *, const char *, ...) __NORETURN; extern void fmd_hdl_verror(fmd_hdl_t *, const char *, va_list); extern void fmd_hdl_error(fmd_hdl_t *, const char *, ...); extern void fmd_hdl_vdebug(fmd_hdl_t *, const char *, va_list); extern void fmd_hdl_debug(fmd_hdl_t *, const char *, ...); extern int32_t fmd_prop_get_int32(fmd_hdl_t *, const char *); extern int64_t fmd_prop_get_int64(fmd_hdl_t *, const char *); extern char *fmd_prop_get_string(fmd_hdl_t *, const char *); extern void fmd_prop_free_string(fmd_hdl_t *, char *); #define FMD_STAT_NOALLOC 0x0 /* fmd should use caller's memory */ #define FMD_STAT_ALLOC 0x1 /* fmd should allocate stats memory */ extern fmd_stat_t *fmd_stat_create(fmd_hdl_t *, uint_t, uint_t, fmd_stat_t *); extern void fmd_stat_destroy(fmd_hdl_t *, uint_t, fmd_stat_t *); extern void fmd_stat_setstr(fmd_hdl_t *, fmd_stat_t *, const char *); extern fmd_case_t *fmd_case_open(fmd_hdl_t *, void *); extern fmd_case_t *fmd_case_open_uuid(fmd_hdl_t *, const char *, void *); extern void fmd_case_reset(fmd_hdl_t *, fmd_case_t *); extern void fmd_case_solve(fmd_hdl_t *, fmd_case_t *); extern void fmd_case_close(fmd_hdl_t *, fmd_case_t *); extern const char *fmd_case_uuid(fmd_hdl_t *, fmd_case_t *); extern fmd_case_t *fmd_case_uulookup(fmd_hdl_t *, const char *); extern void fmd_case_uuclose(fmd_hdl_t *, const char *); extern int fmd_case_uuclosed(fmd_hdl_t *, const char *); extern int fmd_case_uuisresolved(fmd_hdl_t *, const char *); extern void fmd_case_uuresolved(fmd_hdl_t *, const char *); extern int fmd_case_solved(fmd_hdl_t *, fmd_case_t *); extern int fmd_case_closed(fmd_hdl_t *, fmd_case_t *); extern void fmd_case_add_ereport(fmd_hdl_t *, fmd_case_t *, fmd_event_t *); extern void fmd_case_add_serd(fmd_hdl_t *, fmd_case_t *, const char *); extern void fmd_case_add_suspect(fmd_hdl_t *, fmd_case_t *, nvlist_t *); extern void fmd_case_setspecific(fmd_hdl_t *, fmd_case_t *, void *); extern void *fmd_case_getspecific(fmd_hdl_t *, fmd_case_t *); extern void fmd_case_setprincipal(fmd_hdl_t *, fmd_case_t *, fmd_event_t *); extern fmd_event_t *fmd_case_getprincipal(fmd_hdl_t *, fmd_case_t *); extern fmd_case_t *fmd_case_next(fmd_hdl_t *, fmd_case_t *); extern fmd_case_t *fmd_case_prev(fmd_hdl_t *, fmd_case_t *); extern void fmd_buf_create(fmd_hdl_t *, fmd_case_t *, const char *, size_t); extern void fmd_buf_destroy(fmd_hdl_t *, fmd_case_t *, const char *); extern void fmd_buf_read(fmd_hdl_t *, fmd_case_t *, const char *, void *, size_t); extern void fmd_buf_write(fmd_hdl_t *, fmd_case_t *, const char *, const void *, size_t); extern size_t fmd_buf_size(fmd_hdl_t *, fmd_case_t *, const char *); extern void fmd_serd_create(fmd_hdl_t *, const char *, uint_t, hrtime_t); extern void fmd_serd_destroy(fmd_hdl_t *, const char *); extern int fmd_serd_exists(fmd_hdl_t *, const char *); extern void fmd_serd_reset(fmd_hdl_t *, const char *); extern int fmd_serd_record(fmd_hdl_t *, const char *, fmd_event_t *); extern int fmd_serd_fired(fmd_hdl_t *, const char *); extern int fmd_serd_empty(fmd_hdl_t *, const char *); extern pthread_t fmd_thr_create(fmd_hdl_t *, void (*)(void *), void *); extern void fmd_thr_destroy(fmd_hdl_t *, pthread_t); extern void fmd_thr_signal(fmd_hdl_t *, pthread_t); extern void fmd_thr_checkpoint(fmd_hdl_t *); extern door_xcreate_server_func_t fmd_doorthr_create; extern door_xcreate_thrsetup_func_t fmd_doorthr_setup; extern id_t fmd_timer_install(fmd_hdl_t *, void *, fmd_event_t *, hrtime_t); extern void fmd_timer_remove(fmd_hdl_t *, id_t); extern nvlist_t *fmd_nvl_create_defect(fmd_hdl_t *, const char *, uint8_t, nvlist_t *, nvlist_t *, nvlist_t *); extern nvlist_t *fmd_nvl_create_fault(fmd_hdl_t *, const char *, uint8_t, nvlist_t *, nvlist_t *, nvlist_t *); extern const nvlist_t *fmd_hdl_fmauth(fmd_hdl_t *); extern const nvlist_t *fmd_hdl_modauth(fmd_hdl_t *); extern int fmd_nvl_class_match(fmd_hdl_t *, nvlist_t *, const char *); extern int fmd_nvl_fmri_expand(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_present(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_unusable(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_retire(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_unretire(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_replaced(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_service_state(fmd_hdl_t *, nvlist_t *); extern int fmd_nvl_fmri_has_fault(fmd_hdl_t *, nvlist_t *, int, char *); #define FMD_HAS_FAULT_FRU 0 #define FMD_HAS_FAULT_ASRU 1 #define FMD_HAS_FAULT_RESOURCE 2 extern void fmd_repair_fru(fmd_hdl_t *, const char *); extern int fmd_repair_asru(fmd_hdl_t *, const char *); extern int fmd_nvl_fmri_contains(fmd_hdl_t *, nvlist_t *, nvlist_t *); extern nvlist_t *fmd_nvl_fmri_translate(fmd_hdl_t *, nvlist_t *, nvlist_t *); extern nvlist_t *fmd_nvl_alloc(fmd_hdl_t *, int); extern nvlist_t *fmd_nvl_dup(fmd_hdl_t *, nvlist_t *, int); extern int fmd_event_local(fmd_hdl_t *, fmd_event_t *); extern uint64_t fmd_event_ena_create(fmd_hdl_t *); #define FMD_XPRT_RDONLY 0x1 /* transport is read-only */ #define FMD_XPRT_RDWR 0x3 /* transport is read-write */ #define FMD_XPRT_ACCEPT 0x4 /* transport is accepting connection */ #define FMD_XPRT_SUSPENDED 0x8 /* transport starts suspended */ #define FMD_XPRT_EXTERNAL 0x80 /* xprt is external to a chassis */ #define FMD_XPRT_NO_REMOTE_REPAIR 0x100 /* xprt does not allow remote repair */ #define FMD_XPRT_CACHE_AS_LOCAL 0x200 /* xprt caches fault as if local */ #define FMD_XPRT_HCONLY 0x400 /* xprt only proxies hc-scheme faults */ #define FMD_XPRT_HC_PRESENT_ONLY 0x800 /* only locally present hc faults */ extern fmd_xprt_t *fmd_xprt_open(fmd_hdl_t *, uint_t, nvlist_t *, void *); extern void fmd_xprt_close(fmd_hdl_t *, fmd_xprt_t *); extern void fmd_xprt_post(fmd_hdl_t *, fmd_xprt_t *, nvlist_t *, hrtime_t); extern void fmd_xprt_log(fmd_hdl_t *, fmd_xprt_t *, nvlist_t *, hrtime_t); extern void fmd_xprt_suspend(fmd_hdl_t *, fmd_xprt_t *); extern void fmd_xprt_resume(fmd_hdl_t *, fmd_xprt_t *); extern int fmd_xprt_error(fmd_hdl_t *, fmd_xprt_t *); extern nvlist_t *fmd_xprt_translate(fmd_hdl_t *, fmd_xprt_t *, fmd_event_t *); extern void fmd_xprt_add_domain(fmd_hdl_t *, nvlist_t *, char *); extern void fmd_xprt_setspecific(fmd_hdl_t *, fmd_xprt_t *, void *); extern void *fmd_xprt_getspecific(fmd_hdl_t *, fmd_xprt_t *); #ifdef __cplusplus } #endif #endif /* _FMD_API_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # $mapfile_version 2 SYMBOL_SCOPE { fmd_buf_create { TYPE = function; FLAGS = extern }; fmd_buf_destroy { TYPE = function; FLAGS = extern }; fmd_buf_read { TYPE = function; FLAGS = extern }; fmd_buf_size { TYPE = function; FLAGS = extern }; fmd_buf_write { TYPE = function; FLAGS = extern }; fmd_case_add_ereport { TYPE = function; FLAGS = extern }; fmd_case_add_serd { TYPE = function; FLAGS = extern }; fmd_case_add_suspect { TYPE = function; FLAGS = extern }; fmd_case_close { TYPE = function; FLAGS = extern }; fmd_case_closed { TYPE = function; FLAGS = extern }; fmd_case_getprincipal { TYPE = function; FLAGS = extern }; fmd_case_getspecific { TYPE = function; FLAGS = extern }; fmd_case_next { TYPE = function; FLAGS = extern }; fmd_case_open { TYPE = function; FLAGS = extern }; fmd_case_open_uuid { TYPE = function; FLAGS = extern }; fmd_case_prev { TYPE = function; FLAGS = extern }; fmd_case_reset { TYPE = function; FLAGS = extern }; fmd_case_setprincipal { TYPE = function; FLAGS = extern }; fmd_case_setspecific { TYPE = function; FLAGS = extern }; fmd_case_solve { TYPE = function; FLAGS = extern }; fmd_case_solved { TYPE = function; FLAGS = extern }; fmd_case_uuclose { TYPE = function; FLAGS = extern }; fmd_case_uuclosed { TYPE = function; FLAGS = extern }; fmd_case_uuid { TYPE = function; FLAGS = extern }; fmd_case_uuisresolved { TYPE = function; FLAGS = extern }; fmd_case_uulookup { TYPE = function; FLAGS = extern }; fmd_case_uuresolved { TYPE = function; FLAGS = extern }; fmd_doorthr_create { TYPE = function; FLAGS = extern }; fmd_doorthr_setup { TYPE = function; FLAGS = extern }; fmd_event_local { TYPE = function; FLAGS = extern }; fmd_event_ena_create { TYPE = function; FLAGS = extern }; fmd_hdl_abort { TYPE = function; FLAGS = extern }; fmd_hdl_alloc { TYPE = function; FLAGS = extern }; fmd_hdl_debug { TYPE = function; FLAGS = extern }; fmd_hdl_error { TYPE = function; FLAGS = extern }; fmd_hdl_free { TYPE = function; FLAGS = extern }; fmd_hdl_fmauth { TYPE = function; FLAGS = extern }; fmd_hdl_getspecific { TYPE = function; FLAGS = extern }; fmd_hdl_modauth { TYPE = function; FLAGS = extern }; fmd_hdl_opendict { TYPE = function; FLAGS = extern }; fmd_hdl_register { TYPE = function; FLAGS = extern }; fmd_hdl_setspecific { TYPE = function; FLAGS = extern }; fmd_hdl_strdup { TYPE = function; FLAGS = extern }; fmd_hdl_strfree { TYPE = function; FLAGS = extern }; fmd_hdl_subscribe { TYPE = function; FLAGS = extern }; fmd_hdl_topo_hold { TYPE = function; FLAGS = extern }; fmd_hdl_topo_rele { TYPE = function; FLAGS = extern }; fmd_hdl_unregister { TYPE = function; FLAGS = extern }; fmd_hdl_unsubscribe { TYPE = function; FLAGS = extern }; fmd_hdl_vabort { TYPE = function; FLAGS = extern }; fmd_hdl_vdebug { TYPE = function; FLAGS = extern }; fmd_hdl_verror { TYPE = function; FLAGS = extern }; fmd_hdl_zalloc { TYPE = function; FLAGS = extern }; fmd_nvl_alloc { TYPE = function; FLAGS = extern }; fmd_nvl_class_match { TYPE = function; FLAGS = extern }; fmd_nvl_create_defect { TYPE = function; FLAGS = extern }; fmd_nvl_create_fault { TYPE = function; FLAGS = extern }; fmd_nvl_dup { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_expand { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_present { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_replaced { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_unusable { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_retire { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_unretire { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_service_state { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_has_fault { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_contains { TYPE = function; FLAGS = extern }; fmd_nvl_fmri_translate { TYPE = function; FLAGS = extern }; fmd_prop_free_string { TYPE = function; FLAGS = extern }; fmd_prop_get_int32 { TYPE = function; FLAGS = extern }; fmd_prop_get_int64 { TYPE = function; FLAGS = extern }; fmd_prop_get_string { TYPE = function; FLAGS = extern }; fmd_repair_fru { TYPE = function; FLAGS = extern }; fmd_repair_asru { TYPE = function; FLAGS = extern }; fmd_serd_create { TYPE = function; FLAGS = extern }; fmd_serd_destroy { TYPE = function; FLAGS = extern }; fmd_serd_exists { TYPE = function; FLAGS = extern }; fmd_serd_fired { TYPE = function; FLAGS = extern }; fmd_serd_reset { TYPE = function; FLAGS = extern }; fmd_serd_record { TYPE = function; FLAGS = extern }; fmd_serd_empty { TYPE = function; FLAGS = extern }; fmd_stat_create { TYPE = function; FLAGS = extern }; fmd_stat_destroy { TYPE = function; FLAGS = extern }; fmd_stat_setstr { TYPE = function; FLAGS = extern }; fmd_thr_create { TYPE = function; FLAGS = extern }; fmd_thr_destroy { TYPE = function; FLAGS = extern }; fmd_thr_signal { TYPE = function; FLAGS = extern }; fmd_thr_checkpoint { TYPE = function; FLAGS = extern }; fmd_timer_install { TYPE = function; FLAGS = extern }; fmd_timer_remove { TYPE = function; FLAGS = extern }; fmd_xprt_close { TYPE = function; FLAGS = extern }; fmd_xprt_error { TYPE = function; FLAGS = extern }; fmd_xprt_getspecific { TYPE = function; FLAGS = extern }; fmd_xprt_log { TYPE = function; FLAGS = extern }; fmd_xprt_open { TYPE = function; FLAGS = extern }; fmd_xprt_post { TYPE = function; FLAGS = extern }; fmd_xprt_resume { TYPE = function; FLAGS = extern }; fmd_xprt_setspecific { TYPE = function; FLAGS = extern }; fmd_xprt_suspend { TYPE = function; FLAGS = extern }; fmd_xprt_translate { TYPE = function; FLAGS = extern }; fmd_xprt_add_domain { TYPE = function; FLAGS = extern }; }; /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *const _fmd_asru_events[] = { FMD_RSRC_CLASS "asru.ok", /* UNUSABLE=0 FAULTED=0 */ FMD_RSRC_CLASS "asru.degraded", /* UNUSABLE=0 FAULTED=1 */ FMD_RSRC_CLASS "asru.unknown", /* UNUSABLE=1 FAULTED=0 */ FMD_RSRC_CLASS "asru.faulted" /* UNUSABLE=1 FAULTED=1 */ }; static const char *const _fmd_asru_snames[] = { "uf", "uF", "Uf", "UF" /* same order as above */ }; volatile uint32_t fmd_asru_fake_not_present = 0; static uint_t fmd_asru_strhash(fmd_asru_hash_t *ahp, const char *val) { return (topo_fmri_strhash(ahp->ah_topo->ft_hdl, val) % ahp->ah_hashlen); } static boolean_t fmd_asru_strcmp(fmd_asru_hash_t *ahp, const char *a, const char *b) { return (topo_fmri_strcmp(ahp->ah_topo->ft_hdl, a, b)); } static fmd_asru_t * fmd_asru_create(fmd_asru_hash_t *ahp, const char *uuid, const char *name, nvlist_t *fmri) { fmd_asru_t *ap = fmd_zalloc(sizeof (fmd_asru_t), FMD_SLEEP); char *s; (void) pthread_mutex_init(&ap->asru_lock, NULL); (void) pthread_cond_init(&ap->asru_cv, NULL); ap->asru_name = fmd_strdup(name, FMD_SLEEP); if (fmri) (void) nvlist_xdup(fmri, &ap->asru_fmri, &fmd.d_nva); ap->asru_root = fmd_strdup(ahp->ah_dirpath, FMD_SLEEP); ap->asru_uuid = fmd_strdup(uuid, FMD_SLEEP); ap->asru_uuidlen = ap->asru_uuid ? strlen(ap->asru_uuid) : 0; ap->asru_refs = 1; if (fmri && nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &s) == 0 && strcmp(s, FM_FMRI_SCHEME_FMD) == 0) ap->asru_flags |= FMD_ASRU_INTERNAL; return (ap); } static void fmd_asru_destroy(fmd_asru_t *ap) { ASSERT(MUTEX_HELD(&ap->asru_lock)); ASSERT(ap->asru_refs == 0); nvlist_free(ap->asru_event); fmd_strfree(ap->asru_name); nvlist_free(ap->asru_fmri); fmd_strfree(ap->asru_root); fmd_free(ap->asru_uuid, ap->asru_uuidlen + 1); fmd_free(ap, sizeof (fmd_asru_t)); } static void fmd_asru_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_t *ap) { uint_t h = fmd_asru_strhash(ahp, ap->asru_name); ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); ap->asru_next = ahp->ah_hash[h]; ahp->ah_hash[h] = ap; ahp->ah_count++; } static fmd_asru_t * fmd_asru_hold(fmd_asru_t *ap) { (void) pthread_mutex_lock(&ap->asru_lock); ap->asru_refs++; ASSERT(ap->asru_refs != 0); (void) pthread_mutex_unlock(&ap->asru_lock); return (ap); } /* * Lookup an asru in the hash by name and place a hold on it. If the asru is * not found, no entry is created and NULL is returned. This internal function * is for callers who have the ah_lock held and is used by lookup_name below. */ fmd_asru_t * fmd_asru_hash_lookup(fmd_asru_hash_t *ahp, const char *name) { fmd_asru_t *ap; uint_t h; ASSERT(RW_LOCK_HELD(&ahp->ah_lock)); h = fmd_asru_strhash(ahp, name); for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) { if (fmd_asru_strcmp(ahp, ap->asru_name, name)) break; } if (ap != NULL) (void) fmd_asru_hold(ap); else (void) fmd_set_errno(EFMD_ASRU_NOENT); return (ap); } #define HC_ONLY_FALSE 0 #define HC_ONLY_TRUE 1 static int fmd_asru_replacement_state(nvlist_t *event, int hc_only) { int ps = -1; nvlist_t *asru, *fru, *rsrc; char *s; /* * Check if there is evidence that this object is no longer present. * In general fmd_fmri_present() should be supported on resources and/or * frus, as those are the things that are physically present or not * present - an asru can be spread over a number of frus some of which * are present and some not, so fmd_fmri_present() is not generally * meaningful. However retain a check for asru first for compatibility. * If we have checked all three and we still get -1 then nothing knows * whether it's present or not, so err on the safe side and treat it * as still present. * * Note that if hc_only is set, then we only check status using fmris * that are in hc-scheme. */ if (fmd_asru_fake_not_present) return (fmd_asru_fake_not_present); if (nvlist_lookup_nvlist(event, FM_FAULT_ASRU, &asru) == 0 && (hc_only == HC_ONLY_FALSE || (nvlist_lookup_string(asru, FM_FMRI_SCHEME, &s) == 0 && strcmp(s, FM_FMRI_SCHEME_HC) == 0))) ps = fmd_fmri_replaced(asru); if (ps == -1 || ps == FMD_OBJ_STATE_UNKNOWN) { if (nvlist_lookup_nvlist(event, FM_FAULT_RESOURCE, &rsrc) == 0 && (hc_only == HC_ONLY_FALSE || (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &s) == 0 && strcmp(s, FM_FMRI_SCHEME_HC) == 0))) { if (ps == -1) { ps = fmd_fmri_replaced(rsrc); } else { /* see if we can improve on UNKNOWN */ int ps2 = fmd_fmri_replaced(rsrc); if (ps2 == FMD_OBJ_STATE_STILL_PRESENT || ps2 == FMD_OBJ_STATE_REPLACED) ps = ps2; } } } if (ps == -1 || ps == FMD_OBJ_STATE_UNKNOWN) { if (nvlist_lookup_nvlist(event, FM_FAULT_FRU, &fru) == 0 && (hc_only == HC_ONLY_FALSE || (nvlist_lookup_string(fru, FM_FMRI_SCHEME, &s) == 0 && strcmp(s, FM_FMRI_SCHEME_HC) == 0))) { if (ps == -1) { ps = fmd_fmri_replaced(fru); } else { /* see if we can improve on UNKNOWN */ int ps2 = fmd_fmri_replaced(fru); if (ps2 == FMD_OBJ_STATE_STILL_PRESENT || ps2 == FMD_OBJ_STATE_REPLACED) ps = ps2; } } } if (ps == -1) ps = FMD_OBJ_STATE_UNKNOWN; return (ps); } static void fmd_asru_asru_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_link_t *alp, char *name) { uint_t h = fmd_asru_strhash(ahp, name); ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); alp->al_asru_next = ahp->ah_asru_hash[h]; ahp->ah_asru_hash[h] = alp; ahp->ah_al_count++; } static void fmd_asru_case_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_link_t *alp, char *name) { uint_t h = fmd_asru_strhash(ahp, name); ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); alp->al_case_next = ahp->ah_case_hash[h]; ahp->ah_case_hash[h] = alp; } static void fmd_asru_fru_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_link_t *alp, char *name) { uint_t h = fmd_asru_strhash(ahp, name); ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); alp->al_fru_next = ahp->ah_fru_hash[h]; ahp->ah_fru_hash[h] = alp; } static void fmd_asru_label_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_link_t *alp, char *name) { uint_t h = fmd_asru_strhash(ahp, name); ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); alp->al_label_next = ahp->ah_label_hash[h]; ahp->ah_label_hash[h] = alp; } static void fmd_asru_rsrc_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_link_t *alp, char *name) { uint_t h = fmd_asru_strhash(ahp, name); ASSERT(RW_WRITE_HELD(&ahp->ah_lock)); alp->al_rsrc_next = ahp->ah_rsrc_hash[h]; ahp->ah_rsrc_hash[h] = alp; } static void fmd_asru_al_destroy(fmd_asru_link_t *alp) { ASSERT(alp->al_refs == 0); ASSERT(MUTEX_HELD(&alp->al_asru->asru_lock)); if (alp->al_log != NULL) fmd_log_rele(alp->al_log); fmd_free(alp->al_uuid, alp->al_uuidlen + 1); nvlist_free(alp->al_event); fmd_strfree(alp->al_rsrc_name); fmd_strfree(alp->al_case_uuid); fmd_strfree(alp->al_fru_name); fmd_strfree(alp->al_asru_name); fmd_strfree(alp->al_label); nvlist_free(alp->al_asru_fmri); fmd_free(alp, sizeof (fmd_asru_link_t)); } static fmd_asru_link_t * fmd_asru_al_hold(fmd_asru_link_t *alp) { fmd_asru_t *ap = alp->al_asru; (void) pthread_mutex_lock(&ap->asru_lock); ap->asru_refs++; alp->al_refs++; ASSERT(alp->al_refs != 0); (void) pthread_mutex_unlock(&ap->asru_lock); return (alp); } static void fmd_asru_destroy(fmd_asru_t *ap); /*ARGSUSED*/ static void fmd_asru_al_hash_release(fmd_asru_hash_t *ahp, fmd_asru_link_t *alp) { fmd_asru_t *ap = alp->al_asru; (void) pthread_mutex_lock(&ap->asru_lock); ASSERT(alp->al_refs != 0); if (--alp->al_refs == 0) fmd_asru_al_destroy(alp); ASSERT(ap->asru_refs != 0); if (--ap->asru_refs == 0) fmd_asru_destroy(ap); else (void) pthread_mutex_unlock(&ap->asru_lock); } static int fmd_asru_get_namestr(nvlist_t *nvl, char **name, ssize_t *namelen) { if ((*namelen = fmd_fmri_nvl2str(nvl, NULL, 0)) == -1) return (EFMD_ASRU_FMRI); *name = fmd_alloc(*namelen + 1, FMD_SLEEP); if (fmd_fmri_nvl2str(nvl, *name, *namelen + 1) == -1) { if (*name != NULL) fmd_free(*name, *namelen + 1); return (EFMD_ASRU_FMRI); } return (0); } static fmd_asru_link_t * fmd_asru_al_create(fmd_asru_hash_t *ahp, nvlist_t *nvl, fmd_case_t *cp, const char *al_uuid) { nvlist_t *asru = NULL, *fru, *rsrc; int got_rsrc = 0, got_asru = 0, got_fru = 0; ssize_t fru_namelen, rsrc_namelen, asru_namelen; char *asru_name, *rsrc_name, *fru_name, *name, *label; fmd_asru_link_t *alp; fmd_asru_t *ap; boolean_t msg; fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; if (nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru) == 0 && fmd_asru_get_namestr(asru, &asru_name, &asru_namelen) == 0) got_asru = 1; if (nvlist_lookup_nvlist(nvl, FM_FAULT_FRU, &fru) == 0 && fmd_asru_get_namestr(fru, &fru_name, &fru_namelen) == 0) got_fru = 1; if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc) == 0 && fmd_asru_get_namestr(rsrc, &rsrc_name, &rsrc_namelen) == 0) got_rsrc = 1; if (nvlist_lookup_string(nvl, FM_FAULT_LOCATION, &label) != 0) label = ""; /* * Grab the rwlock as a writer; Then create and insert the asru with * ahp->ah_lock held and hash it in. We'll then drop the rwlock and * proceed to initializing the asru. */ (void) pthread_rwlock_wrlock(&ahp->ah_lock); /* * Create and initialise the per-fault "link" structure. */ alp = fmd_zalloc(sizeof (fmd_asru_link_t), FMD_SLEEP); if (got_asru) (void) nvlist_xdup(asru, &alp->al_asru_fmri, &fmd.d_nva); alp->al_uuid = fmd_strdup(al_uuid, FMD_SLEEP); alp->al_uuidlen = strlen(alp->al_uuid); alp->al_refs = 1; /* * If this is the first fault for this asru, then create the per-asru * structure and link into the hash. */ name = got_asru ? asru_name : ""; if ((ap = fmd_asru_hash_lookup(ahp, name)) == NULL) { ap = fmd_asru_create(ahp, al_uuid, name, got_asru ? asru : NULL); fmd_asru_hash_insert(ahp, ap); } else nvlist_free(ap->asru_event); (void) nvlist_xdup(nvl, &ap->asru_event, &fmd.d_nva); /* * Put the link structure on the list associated with the per-asru * structure. Then put the link structure on the various hashes. */ fmd_list_append(&ap->asru_list, (fmd_list_t *)alp); alp->al_asru = ap; alp->al_asru_name = got_asru ? asru_name : fmd_strdup("", FMD_SLEEP); fmd_asru_asru_hash_insert(ahp, alp, alp->al_asru_name); alp->al_fru_name = got_fru ? fru_name : fmd_strdup("", FMD_SLEEP); fmd_asru_fru_hash_insert(ahp, alp, alp->al_fru_name); alp->al_rsrc_name = got_rsrc ? rsrc_name : fmd_strdup("", FMD_SLEEP); fmd_asru_rsrc_hash_insert(ahp, alp, alp->al_rsrc_name); alp->al_label = fmd_strdup(label, FMD_SLEEP); fmd_asru_label_hash_insert(ahp, alp, label); alp->al_case_uuid = fmd_strdup(cip->ci_uuid, FMD_SLEEP); fmd_asru_case_hash_insert(ahp, alp, cip->ci_uuid); (void) pthread_mutex_lock(&ap->asru_lock); (void) pthread_rwlock_unlock(&ahp->ah_lock); ap->asru_case = alp->al_case = cp; if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_MESSAGE, &msg) == 0 && msg == B_FALSE) ap->asru_flags |= FMD_ASRU_INVISIBLE; (void) nvlist_xdup(nvl, &alp->al_event, &fmd.d_nva); ap->asru_flags |= FMD_ASRU_VALID; (void) pthread_cond_broadcast(&ap->asru_cv); (void) pthread_mutex_unlock(&ap->asru_lock); return (alp); } static void fmd_asru_hash_recreate(fmd_log_t *lp, fmd_event_t *ep, fmd_asru_hash_t *ahp) { nvlist_t *nvl = FMD_EVENT_NVL(ep); boolean_t faulty = FMD_B_FALSE, unusable = FMD_B_FALSE; int ps; boolean_t repaired = FMD_B_FALSE, replaced = FMD_B_FALSE; boolean_t acquitted = FMD_B_FALSE, resolved = FMD_B_FALSE; nvlist_t *flt, *flt_copy, *asru; char *case_uuid = NULL, *case_code = NULL; fmd_asru_t *ap; fmd_asru_link_t *alp; fmd_case_t *cp; int64_t *diag_time; nvlist_t *de_fmri, *de_fmri_dup; uint_t nelem; topo_hdl_t *thp; char *class; nvlist_t *rsrc; int err; boolean_t injected; /* * Extract the most recent values of 'faulty' from the event log. */ if (nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_FAULTY, &faulty) != 0) { fmd_error(EFMD_ASRU_EVENT, "failed to reload asru %s: " "invalid event log record\n", lp->log_name); ahp->ah_error = EFMD_ASRU_EVENT; return; } if (nvlist_lookup_nvlist(nvl, FM_RSRC_ASRU_EVENT, &flt) != 0) { fmd_error(EFMD_ASRU_EVENT, "failed to reload asru %s: " "invalid event log record\n", lp->log_name); ahp->ah_error = EFMD_ASRU_EVENT; return; } (void) nvlist_lookup_string(nvl, FM_RSRC_ASRU_UUID, &case_uuid); (void) nvlist_lookup_string(nvl, FM_RSRC_ASRU_CODE, &case_code); (void) nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_UNUSABLE, &unusable); (void) nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_REPAIRED, &repaired); (void) nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_REPLACED, &replaced); (void) nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_ACQUITTED, &acquitted); (void) nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_RESOLVED, &resolved); /* * Attempt to recreate the case in CLOSED, REPAIRED or RESOLVED state * (depending on whether the faulty/resolved bits are set). * If the case is already present, fmd_case_recreate() will return it. * If not, we'll create a new orphaned case. Either way, we use the * ASRU event to insert a suspect into the partially-restored case. */ fmd_module_lock(fmd.d_rmod); cp = fmd_case_recreate(fmd.d_rmod, NULL, faulty ? FMD_CASE_CLOSED : resolved ? FMD_CASE_RESOLVED : FMD_CASE_REPAIRED, case_uuid, case_code); fmd_case_hold(cp); fmd_module_unlock(fmd.d_rmod); if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_INJECTED, &injected) == 0 && injected) fmd_case_set_injected(cp); if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME, &diag_time, &nelem) == 0 && nelem >= 2) fmd_case_settime(cp, diag_time[0], diag_time[1]); else fmd_case_settime(cp, lp->log_stat.st_ctime, 0); if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &de_fmri) == 0) { (void) nvlist_xdup(de_fmri, &de_fmri_dup, &fmd.d_nva); fmd_case_set_de_fmri(cp, de_fmri_dup); } (void) nvlist_xdup(flt, &flt_copy, &fmd.d_nva); /* * For faults with a resource, re-evaluate the asru from the resource. */ thp = fmd_fmri_topo_hold(TOPO_VERSION); if (nvlist_lookup_string(flt_copy, FM_CLASS, &class) == 0 && strncmp(class, "fault", 5) == 0 && nvlist_lookup_nvlist(flt_copy, FM_FAULT_RESOURCE, &rsrc) == 0 && rsrc != NULL && (fmd_fmri_replaced(rsrc) != FMD_OBJ_STATE_REPLACED) && topo_fmri_asru(thp, rsrc, &asru, &err) == 0) { (void) nvlist_remove(flt_copy, FM_FAULT_ASRU, DATA_TYPE_NVLIST); (void) nvlist_add_nvlist(flt_copy, FM_FAULT_ASRU, asru); nvlist_free(asru); } fmd_fmri_topo_rele(thp); (void) nvlist_xdup(flt_copy, &flt, &fmd.d_nva); fmd_case_recreate_suspect(cp, flt_copy); /* * Now create the resource cache entries. */ alp = fmd_asru_al_create(ahp, flt, cp, fmd_strbasename(lp->log_name)); ap = alp->al_asru; /* * Check to see if the resource is still present in the system. */ ps = fmd_asru_replacement_state(flt, HC_ONLY_FALSE); if (ps == FMD_OBJ_STATE_REPLACED) { replaced = FMD_B_TRUE; } else if (ps == FMD_OBJ_STATE_STILL_PRESENT || ps == FMD_OBJ_STATE_UNKNOWN) { ap->asru_flags |= FMD_ASRU_PRESENT; if (nvlist_lookup_nvlist(alp->al_event, FM_FAULT_ASRU, &asru) == 0) { int us; switch (fmd_fmri_service_state(asru)) { case FMD_SERVICE_STATE_UNUSABLE: unusable = FMD_B_TRUE; break; case FMD_SERVICE_STATE_OK: case FMD_SERVICE_STATE_ISOLATE_PENDING: case FMD_SERVICE_STATE_DEGRADED: unusable = FMD_B_FALSE; break; case FMD_SERVICE_STATE_UNKNOWN: case -1: /* not supported by scheme */ us = fmd_fmri_unusable(asru); if (us > 0) unusable = FMD_B_TRUE; else if (us == 0) unusable = FMD_B_FALSE; break; } } } nvlist_free(flt); ap->asru_flags |= FMD_ASRU_RECREATED; if (faulty) { alp->al_flags |= FMD_ASRU_FAULTY; ap->asru_flags |= FMD_ASRU_FAULTY; } if (unusable) { alp->al_flags |= FMD_ASRU_UNUSABLE; ap->asru_flags |= FMD_ASRU_UNUSABLE; } if (replaced) alp->al_reason = FMD_ASRU_REPLACED; else if (repaired) alp->al_reason = FMD_ASRU_REPAIRED; else if (acquitted) alp->al_reason = FMD_ASRU_ACQUITTED; else alp->al_reason = FMD_ASRU_REMOVED; TRACE((FMD_DBG_ASRU, "asru %s recreated as %p (%s)", alp->al_uuid, (void *)ap, _fmd_asru_snames[ap->asru_flags & FMD_ASRU_STATE])); } static void fmd_asru_hash_discard(fmd_asru_hash_t *ahp, const char *uuid, int err) { char src[PATH_MAX], dst[PATH_MAX]; (void) snprintf(src, PATH_MAX, "%s/%s", ahp->ah_dirpath, uuid); (void) snprintf(dst, PATH_MAX, "%s/%s-", ahp->ah_dirpath, uuid); if (err != 0) err = rename(src, dst); else err = unlink(src); if (err != 0 && errno != ENOENT) fmd_error(EFMD_ASRU_EVENT, "failed to rename log %s", src); } /* * Open a saved log file and restore it into the ASRU hash. If we can't even * open the log, rename the log file to - to indicate it is corrupt. If * fmd_log_replay() fails, we either delete the file (if it has reached the * upper limit on cache age) or rename it for debugging if it was corrupted. */ static void fmd_asru_hash_logopen(fmd_asru_hash_t *ahp, const char *uuid) { fmd_log_t *lp = fmd_log_tryopen(ahp->ah_dirpath, uuid, FMD_LOG_ASRU); uint_t n; if (lp == NULL) { fmd_asru_hash_discard(ahp, uuid, errno); return; } ahp->ah_error = 0; n = ahp->ah_al_count; fmd_log_replay(lp, (fmd_log_f *)fmd_asru_hash_recreate, ahp); fmd_log_rele(lp); if (ahp->ah_al_count == n) fmd_asru_hash_discard(ahp, uuid, ahp->ah_error); } void fmd_asru_hash_refresh(fmd_asru_hash_t *ahp) { struct dirent *dp; DIR *dirp; int zero; if ((dirp = opendir(ahp->ah_dirpath)) == NULL) { fmd_error(EFMD_ASRU_NODIR, "failed to open asru cache directory %s", ahp->ah_dirpath); return; } (void) fmd_conf_getprop(fmd.d_conf, "rsrc.zero", &zero); (void) pthread_rwlock_wrlock(&ahp->ah_lock); while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.') continue; /* skip "." and ".." */ if (zero) fmd_asru_hash_discard(ahp, dp->d_name, 0); else if (!fmd_strmatch(dp->d_name, "*-")) fmd_asru_hash_logopen(ahp, dp->d_name); } (void) pthread_rwlock_unlock(&ahp->ah_lock); (void) closedir(dirp); } /* * If the resource is present and faulty but not unusable, replay the fault * event that caused it be marked faulty. This will cause the agent * subscribing to this fault class to again disable the resource. */ /*ARGSUSED*/ static void fmd_asru_hash_replay_asru(fmd_asru_t *ap, void *data) { fmd_event_t *e; nvlist_t *nvl; char *class; if (ap->asru_event != NULL && (ap->asru_flags & (FMD_ASRU_STATE | FMD_ASRU_PRESENT)) == (FMD_ASRU_FAULTY | FMD_ASRU_PRESENT)) { fmd_dprintf(FMD_DBG_ASRU, "replaying fault event for %s", ap->asru_name); (void) nvlist_xdup(ap->asru_event, &nvl, &fmd.d_nva); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); (void) nvlist_add_string(nvl, FMD_EVN_UUID, ((fmd_case_impl_t *)ap->asru_case)->ci_uuid); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dispq_dispatch(fmd.d_disp, e, class); } } void fmd_asru_hash_replay(fmd_asru_hash_t *ahp) { fmd_asru_hash_apply(ahp, fmd_asru_hash_replay_asru, NULL); } /* * Check if the resource is still present. If not, and if the rsrc.age time * has expired, then do an implicit repair on the resource. */ /*ARGSUSED*/ static void fmd_asru_repair_if_aged(fmd_asru_link_t *alp, void *arg) { struct timeval tv; fmd_log_t *lp; hrtime_t hrt; int ps; int err; fmd_asru_rep_arg_t fara; if (!(alp->al_flags & FMD_ASRU_FAULTY)) return; /* * Checking for aged resources only happens on the diagnosing side * not on a proxy. */ if (alp->al_flags & FMD_ASRU_PROXY) return; ps = fmd_asru_replacement_state(alp->al_event, HC_ONLY_FALSE); if (ps == FMD_OBJ_STATE_REPLACED) { fara.fara_reason = FMD_ASRU_REPLACED; fara.fara_bywhat = FARA_ALL; fara.fara_rval = &err; fmd_asru_repaired(alp, &fara); } else if (ps == FMD_OBJ_STATE_NOT_PRESENT) { fmd_time_gettimeofday(&tv); lp = fmd_log_open(alp->al_asru->asru_root, alp->al_uuid, FMD_LOG_ASRU); if (lp == NULL) return; hrt = (hrtime_t)(tv.tv_sec - lp->log_stat.st_mtime); fmd_log_rele(lp); if (hrt * NANOSEC >= fmd.d_asrus->ah_lifetime) { fara.fara_reason = FMD_ASRU_REMOVED; fara.fara_bywhat = FARA_ALL; fara.fara_rval = &err; fmd_asru_repaired(alp, &fara); } } } /*ARGSUSED*/ void fmd_asru_check_if_aged(fmd_asru_link_t *alp, void *arg) { struct timeval tv; fmd_log_t *lp; hrtime_t hrt; /* * Case must be in resolved state for this to be called. So modified * time on resource cache entry should be the time the resolve occurred. * Return 0 if not yet hit rsrc.aged. */ fmd_time_gettimeofday(&tv); lp = fmd_log_open(alp->al_asru->asru_root, alp->al_uuid, FMD_LOG_ASRU); if (lp == NULL) return; hrt = (hrtime_t)(tv.tv_sec - lp->log_stat.st_mtime); fmd_log_rele(lp); if (hrt * NANOSEC < fmd.d_asrus->ah_lifetime) *(int *)arg = 0; } /*ARGSUSED*/ void fmd_asru_most_recent(fmd_asru_link_t *alp, void *arg) { fmd_log_t *lp; uint64_t hrt; /* * Find most recent modified time of a set of resource cache entries. */ lp = fmd_log_open(alp->al_asru->asru_root, alp->al_uuid, FMD_LOG_ASRU); if (lp == NULL) return; hrt = lp->log_stat.st_mtime; fmd_log_rele(lp); if (*(uint64_t *)arg < hrt) *(uint64_t *)arg = hrt; } void fmd_asru_clear_aged_rsrcs() { int check_if_aged = 1; fmd_asru_al_hash_apply(fmd.d_asrus, fmd_asru_repair_if_aged, NULL); fmd_case_hash_apply(fmd.d_cases, fmd_case_discard_resolved, &check_if_aged); } fmd_asru_hash_t * fmd_asru_hash_create(const char *root, const char *dir) { fmd_asru_hash_t *ahp; char path[PATH_MAX]; ahp = fmd_alloc(sizeof (fmd_asru_hash_t), FMD_SLEEP); (void) pthread_rwlock_init(&ahp->ah_lock, NULL); ahp->ah_hashlen = fmd.d_str_buckets; ahp->ah_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); ahp->ah_asru_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); ahp->ah_case_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); ahp->ah_fru_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); ahp->ah_label_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); ahp->ah_rsrc_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP); (void) snprintf(path, sizeof (path), "%s/%s", root, dir); ahp->ah_dirpath = fmd_strdup(path, FMD_SLEEP); (void) fmd_conf_getprop(fmd.d_conf, "rsrc.age", &ahp->ah_lifetime); (void) fmd_conf_getprop(fmd.d_conf, "fakenotpresent", (uint32_t *)&fmd_asru_fake_not_present); ahp->ah_al_count = 0; ahp->ah_count = 0; ahp->ah_error = 0; ahp->ah_topo = fmd_topo_hold(); return (ahp); } void fmd_asru_hash_destroy(fmd_asru_hash_t *ahp) { fmd_asru_link_t *alp, *np; uint_t i; for (i = 0; i < ahp->ah_hashlen; i++) { for (alp = ahp->ah_case_hash[i]; alp != NULL; alp = np) { np = alp->al_case_next; alp->al_case_next = NULL; fmd_case_rele(alp->al_case); alp->al_case = NULL; fmd_asru_al_hash_release(ahp, alp); } } fmd_strfree(ahp->ah_dirpath); fmd_free(ahp->ah_hash, sizeof (void *) * ahp->ah_hashlen); fmd_free(ahp->ah_asru_hash, sizeof (void *) * ahp->ah_hashlen); fmd_free(ahp->ah_case_hash, sizeof (void *) * ahp->ah_hashlen); fmd_free(ahp->ah_fru_hash, sizeof (void *) * ahp->ah_hashlen); fmd_free(ahp->ah_label_hash, sizeof (void *) * ahp->ah_hashlen); fmd_free(ahp->ah_rsrc_hash, sizeof (void *) * ahp->ah_hashlen); fmd_topo_rele(ahp->ah_topo); fmd_free(ahp, sizeof (fmd_asru_hash_t)); } /* * Take a snapshot of the ASRU database by placing an additional hold on each * member in an auxiliary array, and then call 'func' for each ASRU. */ void fmd_asru_hash_apply(fmd_asru_hash_t *ahp, void (*func)(fmd_asru_t *, void *), void *arg) { fmd_asru_t *ap, **aps, **app; uint_t apc, i; (void) pthread_rwlock_rdlock(&ahp->ah_lock); aps = app = fmd_alloc(ahp->ah_count * sizeof (fmd_asru_t *), FMD_SLEEP); apc = ahp->ah_count; for (i = 0; i < ahp->ah_hashlen; i++) { for (ap = ahp->ah_hash[i]; ap != NULL; ap = ap->asru_next) *app++ = fmd_asru_hold(ap); } ASSERT(app == aps + apc); (void) pthread_rwlock_unlock(&ahp->ah_lock); for (i = 0; i < apc; i++) { if (aps[i]->asru_fmri != NULL) func(aps[i], arg); fmd_asru_hash_release(ahp, aps[i]); } fmd_free(aps, apc * sizeof (fmd_asru_t *)); } void fmd_asru_al_hash_apply(fmd_asru_hash_t *ahp, void (*func)(fmd_asru_link_t *, void *), void *arg) { fmd_asru_link_t *alp, **alps, **alpp; uint_t alpc, i; (void) pthread_rwlock_rdlock(&ahp->ah_lock); alps = alpp = fmd_alloc(ahp->ah_al_count * sizeof (fmd_asru_link_t *), FMD_SLEEP); alpc = ahp->ah_al_count; for (i = 0; i < ahp->ah_hashlen; i++) { for (alp = ahp->ah_case_hash[i]; alp != NULL; alp = alp->al_case_next) *alpp++ = fmd_asru_al_hold(alp); } ASSERT(alpp == alps + alpc); (void) pthread_rwlock_unlock(&ahp->ah_lock); for (i = 0; i < alpc; i++) { func(alps[i], arg); fmd_asru_al_hash_release(ahp, alps[i]); } fmd_free(alps, alpc * sizeof (fmd_asru_link_t *)); } static void fmd_asru_do_hash_apply(fmd_asru_hash_t *ahp, const char *name, void (*func)(fmd_asru_link_t *, void *), void *arg, fmd_asru_link_t **hash, size_t match_offset, size_t next_offset) { fmd_asru_link_t *alp, **alps, **alpp; uint_t alpc = 0, i; uint_t h; (void) pthread_rwlock_rdlock(&ahp->ah_lock); h = fmd_asru_strhash(ahp, name); for (alp = hash[h]; alp != NULL; alp = /* LINTED pointer alignment */ FMD_ASRU_AL_HASH_NEXT(alp, next_offset)) if (fmd_asru_strcmp(ahp, /* LINTED pointer alignment */ FMD_ASRU_AL_HASH_NAME(alp, match_offset), name)) alpc++; alps = alpp = fmd_alloc(alpc * sizeof (fmd_asru_link_t *), FMD_SLEEP); for (alp = hash[h]; alp != NULL; alp = /* LINTED pointer alignment */ FMD_ASRU_AL_HASH_NEXT(alp, next_offset)) if (fmd_asru_strcmp(ahp, /* LINTED pointer alignment */ FMD_ASRU_AL_HASH_NAME(alp, match_offset), name)) *alpp++ = fmd_asru_al_hold(alp); ASSERT(alpp == alps + alpc); (void) pthread_rwlock_unlock(&ahp->ah_lock); for (i = 0; i < alpc; i++) { func(alps[i], arg); fmd_asru_al_hash_release(ahp, alps[i]); } fmd_free(alps, alpc * sizeof (fmd_asru_link_t *)); } void fmd_asru_hash_apply_by_asru(fmd_asru_hash_t *ahp, const char *name, void (*func)(fmd_asru_link_t *, void *), void *arg) { fmd_asru_do_hash_apply(ahp, name, func, arg, ahp->ah_asru_hash, offsetof(fmd_asru_link_t, al_asru_name), offsetof(fmd_asru_link_t, al_asru_next)); } void fmd_asru_hash_apply_by_case(fmd_asru_hash_t *ahp, fmd_case_t *cp, void (*func)(fmd_asru_link_t *, void *), void *arg) { fmd_asru_do_hash_apply(ahp, ((fmd_case_impl_t *)cp)->ci_uuid, func, arg, ahp->ah_case_hash, offsetof(fmd_asru_link_t, al_case_uuid), offsetof(fmd_asru_link_t, al_case_next)); } void fmd_asru_hash_apply_by_fru(fmd_asru_hash_t *ahp, const char *name, void (*func)(fmd_asru_link_t *, void *), void *arg) { fmd_asru_do_hash_apply(ahp, name, func, arg, ahp->ah_fru_hash, offsetof(fmd_asru_link_t, al_fru_name), offsetof(fmd_asru_link_t, al_fru_next)); } void fmd_asru_hash_apply_by_rsrc(fmd_asru_hash_t *ahp, const char *name, void (*func)(fmd_asru_link_t *, void *), void *arg) { fmd_asru_do_hash_apply(ahp, name, func, arg, ahp->ah_rsrc_hash, offsetof(fmd_asru_link_t, al_rsrc_name), offsetof(fmd_asru_link_t, al_rsrc_next)); } void fmd_asru_hash_apply_by_label(fmd_asru_hash_t *ahp, const char *name, void (*func)(fmd_asru_link_t *, void *), void *arg) { fmd_asru_do_hash_apply(ahp, name, func, arg, ahp->ah_label_hash, offsetof(fmd_asru_link_t, al_label), offsetof(fmd_asru_link_t, al_label_next)); } /* * Lookup an asru in the hash by name and place a hold on it. If the asru is * not found, no entry is created and NULL is returned. */ fmd_asru_t * fmd_asru_hash_lookup_name(fmd_asru_hash_t *ahp, const char *name) { fmd_asru_t *ap; (void) pthread_rwlock_rdlock(&ahp->ah_lock); ap = fmd_asru_hash_lookup(ahp, name); (void) pthread_rwlock_unlock(&ahp->ah_lock); return (ap); } /* * Create a resource cache entry using the fault event "nvl" for one of the * suspects from the case "cp". * * The fault event can have the following components : FM_FAULT_ASRU, * FM_FAULT_FRU, FM_FAULT_RESOURCE. These should be set by the Diagnosis Engine * when calling fmd_nvl_create_fault(). In the general case, these are all * optional and an entry will always be added into the cache even if one or all * of these fields is missing. * * However, for hardware faults the recommended practice is that the fault * event should always have the FM_FAULT_RESOURCE field present and that this * should be represented in hc-scheme. * * Currently the DE should also add the FM_FAULT_ASRU and FM_FAULT_FRU fields * where known, though at some future stage fmd might be able to fill these * in automatically from the topology. */ fmd_asru_link_t * fmd_asru_hash_create_entry(fmd_asru_hash_t *ahp, fmd_case_t *cp, nvlist_t *nvl) { char *parsed_uuid; uuid_t uuid; int uuidlen; fmd_asru_link_t *alp; /* * Generate a UUID for the ASRU. libuuid cleverly gives us no * interface for specifying or learning the buffer size. Sigh. * The spec says 36 bytes but we use a tunable just to be safe. */ (void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &uuidlen); parsed_uuid = fmd_zalloc(uuidlen + 1, FMD_SLEEP); uuid_generate(uuid); uuid_unparse(uuid, parsed_uuid); /* * Now create the resource cache entries. */ fmd_case_hold_locked(cp); alp = fmd_asru_al_create(ahp, nvl, cp, parsed_uuid); TRACE((FMD_DBG_ASRU, "asru %s created as %p", alp->al_uuid, (void *)alp->al_asru)); fmd_free(parsed_uuid, uuidlen + 1); return (alp); } /* * Release the reference count on an asru obtained using fmd_asru_hash_lookup. * We take 'ahp' for symmetry and in case we need to use it in future work. */ /*ARGSUSED*/ void fmd_asru_hash_release(fmd_asru_hash_t *ahp, fmd_asru_t *ap) { (void) pthread_mutex_lock(&ap->asru_lock); ASSERT(ap->asru_refs != 0); if (--ap->asru_refs == 0) fmd_asru_destroy(ap); else (void) pthread_mutex_unlock(&ap->asru_lock); } static void fmd_asru_do_delete_entry(fmd_asru_hash_t *ahp, fmd_case_t *cp, fmd_asru_link_t **hash, size_t next_offset, char *name) { uint_t h; fmd_asru_link_t *alp, **pp, *alpnext, **alpnextp; (void) pthread_rwlock_wrlock(&ahp->ah_lock); h = fmd_asru_strhash(ahp, name); pp = &hash[h]; for (alp = *pp; alp != NULL; alp = alpnext) { /* LINTED pointer alignment */ alpnextp = FMD_ASRU_AL_HASH_NEXTP(alp, next_offset); alpnext = *alpnextp; if (alp->al_case == cp) { *pp = *alpnextp; *alpnextp = NULL; } else pp = alpnextp; } (void) pthread_rwlock_unlock(&ahp->ah_lock); } static void fmd_asru_do_hash_delete(fmd_asru_hash_t *ahp, fmd_case_susp_t *cis, fmd_case_t *cp, fmd_asru_link_t **hash, size_t next_offset, char *nvname) { nvlist_t *nvl; char *name = NULL; ssize_t namelen; if (nvlist_lookup_nvlist(cis->cis_nvl, nvname, &nvl) == 0 && (namelen = fmd_fmri_nvl2str(nvl, NULL, 0)) != -1 && (name = fmd_alloc(namelen + 1, FMD_SLEEP)) != NULL) { if (fmd_fmri_nvl2str(nvl, name, namelen + 1) != -1) fmd_asru_do_delete_entry(ahp, cp, hash, next_offset, name); fmd_free(name, namelen + 1); } else fmd_asru_do_delete_entry(ahp, cp, hash, next_offset, ""); } void fmd_asru_hash_delete_case(fmd_asru_hash_t *ahp, fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_susp_t *cis; fmd_asru_link_t *alp, **plp, *alpnext; fmd_asru_t *ap; char path[PATH_MAX]; char *label; uint_t h; /* * first delete hash entries for each suspect */ for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) { fmd_asru_do_hash_delete(ahp, cis, cp, ahp->ah_fru_hash, offsetof(fmd_asru_link_t, al_fru_next), FM_FAULT_FRU); fmd_asru_do_hash_delete(ahp, cis, cp, ahp->ah_rsrc_hash, offsetof(fmd_asru_link_t, al_rsrc_next), FM_FAULT_RESOURCE); if (nvlist_lookup_string(cis->cis_nvl, FM_FAULT_LOCATION, &label) != 0) label = ""; fmd_asru_do_delete_entry(ahp, cp, ahp->ah_label_hash, offsetof(fmd_asru_link_t, al_label_next), label); fmd_asru_do_hash_delete(ahp, cis, cp, ahp->ah_asru_hash, offsetof(fmd_asru_link_t, al_asru_next), FM_FAULT_ASRU); } /* * then delete associated case hash entries */ (void) pthread_rwlock_wrlock(&ahp->ah_lock); h = fmd_asru_strhash(ahp, cip->ci_uuid); plp = &ahp->ah_case_hash[h]; for (alp = *plp; alp != NULL; alp = alpnext) { alpnext = alp->al_case_next; if (alp->al_case == cp) { *plp = alp->al_case_next; alp->al_case_next = NULL; ASSERT(ahp->ah_al_count != 0); ahp->ah_al_count--; /* * decrement case ref. */ fmd_case_rele_locked(cp); alp->al_case = NULL; /* * If we found a matching ASRU, unlink its log file and * then release the hash entry. Note that it may still * be referenced if another thread is manipulating it; * this is ok because once we unlink, the log file will * not be restored, and the log data will be freed when * all of the referencing threads release their * respective references. */ (void) snprintf(path, sizeof (path), "%s/%s", ahp->ah_dirpath, alp->al_uuid); if (cip->ci_xprt == NULL && unlink(path) != 0) fmd_error(EFMD_ASRU_UNLINK, "failed to unlink asru %s", path); /* * Now unlink from the global per-resource cache * and if this is the last link then remove that from * it's own hash too. */ ap = alp->al_asru; (void) pthread_mutex_lock(&ap->asru_lock); fmd_list_delete(&ap->asru_list, alp); if (ap->asru_list.l_next == NULL) { uint_t h; fmd_asru_t *ap2, **pp; fmd_asru_t *apnext, **apnextp; ASSERT(ahp->ah_count != 0); ahp->ah_count--; h = fmd_asru_strhash(ahp, ap->asru_name); pp = &ahp->ah_hash[h]; for (ap2 = *pp; ap2 != NULL; ap2 = apnext) { apnextp = &ap2->asru_next; apnext = *apnextp; if (ap2 == ap) { *pp = *apnextp; *apnextp = NULL; } else pp = apnextp; } } (void) pthread_mutex_unlock(&ap->asru_lock); fmd_asru_al_hash_release(ahp, alp); } else plp = &alp->al_case_next; } (void) pthread_rwlock_unlock(&ahp->ah_lock); } typedef struct { nvlist_t *farc_parent_fmri; uint8_t farc_reason; } fmd_asru_farc_t; static void fmd_asru_repair_containee(fmd_asru_link_t *alp, void *arg) { fmd_asru_farc_t *farcp = (fmd_asru_farc_t *)arg; if ((alp->al_asru->asru_flags & FMD_ASRU_INVISIBLE) && alp->al_asru_fmri && fmd_fmri_contains(farcp->farc_parent_fmri, alp->al_asru_fmri) > 0) { if (fmd_asru_clrflags(alp, FMD_ASRU_FAULTY, farcp->farc_reason)) { if (alp->al_flags & FMD_ASRU_PROXY) fmd_case_xprt_updated(alp->al_case); else fmd_case_update(alp->al_case); } } } static void fmd_asru_do_repair_containees(fmd_asru_link_t *alp, uint8_t reason) { int flags; /* * Check if all entries associated with this asru are acquitted and * if so acquit containees. Don't try to repair containees on proxy * side unless we have local asru. */ if (alp->al_asru_fmri != NULL && (!(alp->al_flags & FMD_ASRU_PROXY) || (alp->al_flags & FMD_ASRU_PROXY_WITH_ASRU))) { (void) pthread_mutex_lock(&alp->al_asru->asru_lock); flags = alp->al_asru->asru_flags; (void) pthread_mutex_unlock(&alp->al_asru->asru_lock); if (!(flags & (FMD_ASRU_FAULTY | FMD_ASRU_INVISIBLE))) { fmd_asru_farc_t farc; farc.farc_parent_fmri = alp->al_asru_fmri; farc.farc_reason = reason; fmd_asru_al_hash_apply(fmd.d_asrus, fmd_asru_repair_containee, &farc); } } } void fmd_asru_repaired(fmd_asru_link_t *alp, void *arg) { int cleared; fmd_asru_rep_arg_t *farap = (fmd_asru_rep_arg_t *)arg; /* * don't allow remote repair over readonly transport */ if (alp->al_flags & FMD_ASRU_PROXY_RDONLY) return; /* * don't allow repair etc by asru on proxy unless asru is local */ if (farap->fara_bywhat == FARA_BY_ASRU && (alp->al_flags & FMD_ASRU_PROXY) && !(alp->al_flags & FMD_ASRU_PROXY_WITH_ASRU)) return; /* * For acquit, need to check both name and uuid if specified */ if (farap->fara_reason == FMD_ASRU_ACQUITTED && farap->fara_rval != NULL && strcmp(farap->fara_uuid, "") != 0 && strcmp(farap->fara_uuid, alp->al_case_uuid) != 0) return; /* * For replaced, verify it has been replaced if we have serial number. * If not set *farap->fara_rval to FARA_ERR_RSRCNOTR. */ if (farap->fara_reason == FMD_ASRU_REPLACED && !(alp->al_flags & FMD_ASRU_PROXY_EXTERNAL) && fmd_asru_replacement_state(alp->al_event, (alp->al_flags & FMD_ASRU_PROXY) ? HC_ONLY_TRUE : HC_ONLY_FALSE) == FMD_OBJ_STATE_STILL_PRESENT) { if (farap->fara_rval) *farap->fara_rval = FARA_ERR_RSRCNOTR; return; } cleared = fmd_asru_clrflags(alp, FMD_ASRU_FAULTY, farap->fara_reason); fmd_asru_do_repair_containees(alp, farap->fara_reason); /* * if called from fmd_adm_*() and we really did clear the bit then * we need to do a case update to see if the associated case can be * repaired. No need to do this if called from fmd_case_*() (ie * when arg is NULL) as the case will be explicitly repaired anyway. */ if (farap->fara_rval) { /* * *farap->fara_rval defaults to FARA_ERR_RSRCNOTF (not found). * If we find a valid cache entry which we repair then we * set it to FARA_OK. However we don't want to do this if * we have already set it to FARA_ERR_RSRCNOTR (not replaced) * in a previous iteration (see above). So only set it to * FARA_OK if the current value is still FARA_ERR_RSRCNOTF. */ if (*farap->fara_rval == FARA_ERR_RSRCNOTF) *farap->fara_rval = FARA_OK; if (cleared) { if (alp->al_flags & FMD_ASRU_PROXY) fmd_case_xprt_updated(alp->al_case); else fmd_case_update(alp->al_case); } } } /* * Discard the case associated with this alp if it is in resolved state. * Called on "fmadm flush". */ /*ARGSUSED*/ void fmd_asru_flush(fmd_asru_link_t *alp, void *arg) { int check_if_aged = 0; int *rval = (int *)arg; if (alp->al_case) fmd_case_discard_resolved(alp->al_case, &check_if_aged); *rval = 0; } /* * This is only called for proxied faults. Set various flags so we can * find the nature of the transport from the resource cache code. */ /*ARGSUSED*/ void fmd_asru_set_on_proxy(fmd_asru_link_t *alp, void *arg) { fmd_asru_set_on_proxy_t *entryp = (fmd_asru_set_on_proxy_t *)arg; if (*entryp->fasp_countp >= entryp->fasp_maxcount) return; /* * Note that this is a proxy fault and save whetehr transport is * RDONLY or EXTERNAL. */ alp->al_flags |= FMD_ASRU_PROXY; alp->al_asru->asru_flags |= FMD_ASRU_PROXY; if (entryp->fasp_proxy_external) { alp->al_flags |= FMD_ASRU_PROXY_EXTERNAL; alp->al_asru->asru_flags |= FMD_ASRU_PROXY_EXTERNAL; } if (entryp->fasp_proxy_rdonly) alp->al_flags |= FMD_ASRU_PROXY_RDONLY; /* * Save whether asru is accessible in local domain */ if (entryp->fasp_proxy_asru[*entryp->fasp_countp]) { alp->al_flags |= FMD_ASRU_PROXY_WITH_ASRU; alp->al_asru->asru_flags |= FMD_ASRU_PROXY_WITH_ASRU; } (*entryp->fasp_countp)++; } /*ARGSUSED*/ void fmd_asru_update_containees(fmd_asru_link_t *alp, void *arg) { fmd_asru_do_repair_containees(alp, alp->al_reason); } /* * This function is used for fault proxying. It updates the resource status in * the resource cache based on information that has come from the other side of * the transport. This can be called on either the proxy side or the * diagnosing side. */ void fmd_asru_update_status(fmd_asru_link_t *alp, void *arg) { fmd_asru_update_status_t *entryp = (fmd_asru_update_status_t *)arg; uint8_t status; if (*entryp->faus_countp >= entryp->faus_maxcount) return; status = entryp->faus_ba[*entryp->faus_countp]; /* * For proxy, if there is no asru on the proxy side, but there is on * the diag side, then take the diag side asru status. * For diag, if there is an asru on the proxy side, then take the proxy * side asru status. */ if (entryp->faus_is_proxy ? (entryp->faus_diag_asru[*entryp->faus_countp] && !entryp->faus_proxy_asru[*entryp->faus_countp]) : entryp->faus_proxy_asru[*entryp->faus_countp]) { if (status & FM_SUSPECT_DEGRADED) alp->al_flags |= FMD_ASRU_DEGRADED; else alp->al_flags &= ~FMD_ASRU_DEGRADED; if (status & FM_SUSPECT_UNUSABLE) (void) fmd_asru_setflags(alp, FMD_ASRU_UNUSABLE); else (void) fmd_asru_clrflags(alp, FMD_ASRU_UNUSABLE, 0); } /* * Update the faulty status too. */ if (!(status & FM_SUSPECT_FAULTY)) (void) fmd_asru_clrflags(alp, FMD_ASRU_FAULTY, (status & FM_SUSPECT_REPAIRED) ? FMD_ASRU_REPAIRED : (status & FM_SUSPECT_REPLACED) ? FMD_ASRU_REPLACED : (status & FM_SUSPECT_ACQUITTED) ? FMD_ASRU_ACQUITTED : FMD_ASRU_REMOVED); else if (entryp->faus_is_proxy) (void) fmd_asru_setflags(alp, FMD_ASRU_FAULTY); /* * for proxy only, update the present status too. */ if (entryp->faus_is_proxy) { if (!(status & FM_SUSPECT_NOT_PRESENT)) { alp->al_flags |= FMD_ASRU_PRESENT; alp->al_asru->asru_flags |= FMD_ASRU_PRESENT; } else { alp->al_flags &= ~FMD_ASRU_PRESENT; alp->al_asru->asru_flags &= ~FMD_ASRU_PRESENT; } } (*entryp->faus_countp)++; } /* * This function is called on the diagnosing side when fault proxying is * in use and the proxy has sent a uuclose. It updates the status of the * resource cache entries. */ void fmd_asru_close_status(fmd_asru_link_t *alp, void *arg) { fmd_asru_close_status_t *entryp = (fmd_asru_close_status_t *)arg; if (*entryp->facs_countp >= entryp->facs_maxcount) return; alp->al_flags &= ~FMD_ASRU_DEGRADED; (void) fmd_asru_setflags(alp, FMD_ASRU_UNUSABLE); (*entryp->facs_countp)++; } static void fmd_asru_logevent(fmd_asru_link_t *alp) { fmd_asru_t *ap = alp->al_asru; boolean_t faulty = (alp->al_flags & FMD_ASRU_FAULTY) != 0; boolean_t unusable = (alp->al_flags & FMD_ASRU_UNUSABLE) != 0; boolean_t message = (ap->asru_flags & FMD_ASRU_INVISIBLE) == 0; boolean_t repaired = (alp->al_reason == FMD_ASRU_REPAIRED); boolean_t replaced = (alp->al_reason == FMD_ASRU_REPLACED); boolean_t acquitted = (alp->al_reason == FMD_ASRU_ACQUITTED); fmd_case_impl_t *cip; fmd_event_t *e; fmd_log_t *lp; nvlist_t *nvl; char *class; ASSERT(MUTEX_HELD(&ap->asru_lock)); cip = (fmd_case_impl_t *)alp->al_case; ASSERT(cip != NULL); /* * Don't log to disk on proxy side */ if (cip->ci_xprt != NULL) return; if ((lp = alp->al_log) == NULL) lp = fmd_log_open(ap->asru_root, alp->al_uuid, FMD_LOG_ASRU); if (lp == NULL) return; /* can't log events if we can't open the log */ nvl = fmd_protocol_rsrc_asru(_fmd_asru_events[faulty | (unusable << 1)], alp->al_asru_fmri, cip->ci_uuid, cip->ci_code, faulty, unusable, message, alp->al_event, &cip->ci_tv, repaired, replaced, acquitted, cip->ci_state == FMD_CASE_RESOLVED, cip->ci_diag_de == NULL ? cip->ci_mod->mod_fmri : cip->ci_diag_de, cip->ci_injected == 1); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_event_hold(e); fmd_log_append(lp, e, NULL); fmd_event_rele(e); /* * For now, we close the log file after every update to conserve file * descriptors and daemon overhead. If this becomes a performance * issue this code can change to keep a fixed-size LRU cache of logs. */ fmd_log_rele(lp); alp->al_log = NULL; } int fmd_asru_setflags(fmd_asru_link_t *alp, uint_t sflag) { fmd_asru_t *ap = alp->al_asru; uint_t nstate, ostate; ASSERT(!(sflag & ~FMD_ASRU_STATE)); ASSERT(sflag != FMD_ASRU_STATE); (void) pthread_mutex_lock(&ap->asru_lock); ostate = alp->al_flags & FMD_ASRU_STATE; alp->al_flags |= sflag; nstate = alp->al_flags & FMD_ASRU_STATE; if (nstate == ostate) { (void) pthread_mutex_unlock(&ap->asru_lock); return (0); } ap->asru_flags |= sflag; TRACE((FMD_DBG_ASRU, "asru %s %s->%s", alp->al_uuid, _fmd_asru_snames[ostate], _fmd_asru_snames[nstate])); fmd_asru_logevent(alp); (void) pthread_cond_broadcast(&ap->asru_cv); (void) pthread_mutex_unlock(&ap->asru_lock); return (1); } int fmd_asru_clrflags(fmd_asru_link_t *alp, uint_t sflag, uint8_t reason) { fmd_asru_t *ap = alp->al_asru; fmd_asru_link_t *nalp; uint_t nstate, ostate, flags = 0; ASSERT(!(sflag & ~FMD_ASRU_STATE)); ASSERT(sflag != FMD_ASRU_STATE); (void) pthread_mutex_lock(&ap->asru_lock); ostate = alp->al_flags & FMD_ASRU_STATE; alp->al_flags &= ~sflag; nstate = alp->al_flags & FMD_ASRU_STATE; if (nstate == ostate) { if (reason > alp->al_reason && ((fmd_case_impl_t *)alp->al_case)->ci_state < FMD_CASE_REPAIRED) { alp->al_reason = reason; fmd_asru_logevent(alp); (void) pthread_cond_broadcast(&ap->asru_cv); } (void) pthread_mutex_unlock(&ap->asru_lock); return (0); } if (reason > alp->al_reason) alp->al_reason = reason; if (sflag == FMD_ASRU_UNUSABLE) ap->asru_flags &= ~sflag; else if (sflag == FMD_ASRU_FAULTY) { /* * only clear the faulty bit if all links are clear */ for (nalp = fmd_list_next(&ap->asru_list); nalp != NULL; nalp = fmd_list_next(nalp)) flags |= nalp->al_flags; if (!(flags & FMD_ASRU_FAULTY)) ap->asru_flags &= ~sflag; } TRACE((FMD_DBG_ASRU, "asru %s %s->%s", alp->al_uuid, _fmd_asru_snames[ostate], _fmd_asru_snames[nstate])); fmd_asru_logevent(alp); (void) pthread_cond_broadcast(&ap->asru_cv); (void) pthread_mutex_unlock(&ap->asru_lock); return (1); } /*ARGSUSED*/ void fmd_asru_log_resolved(fmd_asru_link_t *alp, void *unused) { fmd_asru_t *ap = alp->al_asru; (void) pthread_mutex_lock(&ap->asru_lock); fmd_asru_logevent(alp); (void) pthread_cond_broadcast(&ap->asru_cv); (void) pthread_mutex_unlock(&ap->asru_lock); } /* * Report the current known state of the link entry (ie this particular fault * affecting this particular ASRU). */ int fmd_asru_al_getstate(fmd_asru_link_t *alp) { int us, st = (alp->al_flags & (FMD_ASRU_FAULTY | FMD_ASRU_UNUSABLE)); nvlist_t *asru; int ps = FMD_OBJ_STATE_UNKNOWN; /* * For fault proxying with an EXTERNAL transport, believe the presence * state as sent by the diagnosing side. Otherwise find the presence * state here. Note that if fault proxying with an INTERNAL transport * we can only trust the presence state where we are using hc-scheme * fmris which should be consistant across domains in the same system - * other schemes can refer to different devices in different domains. */ if (!(alp->al_flags & FMD_ASRU_PROXY_EXTERNAL)) { ps = fmd_asru_replacement_state(alp->al_event, (alp->al_flags & FMD_ASRU_PROXY)? HC_ONLY_TRUE : HC_ONLY_FALSE); if (ps == FMD_OBJ_STATE_NOT_PRESENT) return (st | FMD_ASRU_UNUSABLE); if (ps == FMD_OBJ_STATE_REPLACED) { if (alp->al_reason < FMD_ASRU_REPLACED) alp->al_reason = FMD_ASRU_REPLACED; return (st | FMD_ASRU_UNUSABLE); } } if (ps == FMD_OBJ_STATE_UNKNOWN && (alp->al_flags & FMD_ASRU_PROXY)) st |= (alp->al_flags & (FMD_ASRU_DEGRADED | FMD_ASRU_PRESENT)); else st |= (alp->al_flags & (FMD_ASRU_DEGRADED)) | FMD_ASRU_PRESENT; /* * For fault proxying, unless we have a local ASRU, then believe the * service state sent by the diagnosing side. Otherwise find the service * state here. Try fmd_fmri_service_state() first, but if that's not * supported by the scheme then fall back to fmd_fmri_unusable(). */ if ((!(alp->al_flags & FMD_ASRU_PROXY) || (alp->al_flags & FMD_ASRU_PROXY_WITH_ASRU)) && nvlist_lookup_nvlist(alp->al_event, FM_FAULT_ASRU, &asru) == 0) { us = fmd_fmri_service_state(asru); if (us == -1 || us == FMD_SERVICE_STATE_UNKNOWN) { /* not supported by scheme - try fmd_fmri_unusable */ us = fmd_fmri_unusable(asru); if (us > 0) st |= FMD_ASRU_UNUSABLE; else if (us == 0) st &= ~FMD_ASRU_UNUSABLE; } else { if (us == FMD_SERVICE_STATE_UNUSABLE) { st &= ~FMD_ASRU_DEGRADED; st |= FMD_ASRU_UNUSABLE; } else if (us == FMD_SERVICE_STATE_OK) { st &= ~(FMD_ASRU_DEGRADED | FMD_ASRU_UNUSABLE); } else if (us == FMD_SERVICE_STATE_ISOLATE_PENDING) { st &= ~(FMD_ASRU_DEGRADED | FMD_ASRU_UNUSABLE); } else if (us == FMD_SERVICE_STATE_DEGRADED) { st &= ~FMD_ASRU_UNUSABLE; st |= FMD_ASRU_DEGRADED; } } } return (st); } /* * Report the current known state of the ASRU by refreshing its unusable status * based upon the routines provided by the scheme module. If the unusable bit * is different, we do *not* generate a state change here because that change * may be unrelated to fmd activities and therefore we have no case or event. * The absence of the transition is harmless as this function is only provided * for RPC observability and fmd's clients are only concerned with ASRU_FAULTY. */ int fmd_asru_getstate(fmd_asru_t *ap) { int us, st, p = -1; char *s; /* do not report non-fmd non-present resources */ if (!(ap->asru_flags & FMD_ASRU_INTERNAL)) { /* * As with fmd_asru_al_getstate(), we can only trust the * local presence state on a proxy if the transport is * internal and the scheme is hc. Otherwise we believe the * state as sent by the diagnosing side. */ if (!(ap->asru_flags & FMD_ASRU_PROXY) || (!(ap->asru_flags & FMD_ASRU_PROXY_EXTERNAL) && (nvlist_lookup_string(ap->asru_fmri, FM_FMRI_SCHEME, &s) == 0 && strcmp(s, FM_FMRI_SCHEME_HC) == 0))) { if (fmd_asru_fake_not_present >= FMD_OBJ_STATE_REPLACED) return (0); p = fmd_fmri_present(ap->asru_fmri); } if (p == 0 || (p < 0 && !(ap->asru_flags & FMD_ASRU_PROXY) || !(ap->asru_flags & FMD_ASRU_PRESENT))) return (0); } /* * As with fmd_asru_al_getstate(), we can only trust the local unusable * state on a proxy if there is a local ASRU. */ st = ap->asru_flags & (FMD_ASRU_FAULTY | FMD_ASRU_UNUSABLE); if (!(ap->asru_flags & FMD_ASRU_PROXY) || (ap->asru_flags & FMD_ASRU_PROXY_WITH_ASRU)) { us = fmd_fmri_unusable(ap->asru_fmri); if (us > 0) st |= FMD_ASRU_UNUSABLE; else if (us == 0) st &= ~FMD_ASRU_UNUSABLE; } return (st); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_ASRU_H #define _FMD_ASRU_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include /* * The resource is represented by an fmd_asru_t structure and one or more * fmd_asru_link_t structures. Each of the latter represents a separate * unrepaired case (problem) involving the resource. There are separate * reference counts for both the fmd_asru_t and fmd_asru_link_t structures, * but only one lock is used (asru_lock) which protects both the parent * fmd_asru_t and its associated fmd_asru_link_t structures. The FMD_ASRU_FAULTY * flags in the fmd_asru_t represents the cumulative value of the associated * FMD_ASRU_FAULTY flags in the fmd_asru_link_t structures (and therefore of * all independant unrepaired problems that are affecting that resource). */ typedef struct fmd_asru { struct fmd_asru *asru_next; /* next asru on hash chain */ char *asru_name; /* string form of resource fmri (ro) */ nvlist_t *asru_fmri; /* nvlist form of resource fmri (ro) */ char *asru_root; /* directory for cache entry (ro) */ char *asru_uuid; /* uuid for asru cache entry (ro) */ uint_t asru_uuidlen; /* length of asru_uuid (not incl. \0) */ pthread_mutex_t asru_lock; /* lock protecting remaining members */ pthread_cond_t asru_cv; /* condition variable for asru_flags */ uint_t asru_refs; /* reference count */ uint_t asru_flags; /* flags (see below) */ fmd_case_t *asru_case; /* case associated with last change */ nvlist_t *asru_event; /* event associated with last change */ fmd_list_t asru_list; /* linked list next/prev pointers */ } fmd_asru_t; typedef struct fmd_asru_link { fmd_list_t al_list; /* linked list next/prev pointers */ struct fmd_asru *al_asru; /* pointer back to parent */ struct fmd_asru_link *al_asru_next; /* next link on hash chain */ struct fmd_asru_link *al_case_next; /* next link on hash chain */ struct fmd_asru_link *al_fru_next; /* next link on hash chain */ struct fmd_asru_link *al_label_next; /* next link on hash chain */ struct fmd_asru_link *al_rsrc_next; /* next link on hash chain */ char *al_uuid; /* uuid for asru cache entry (ro) */ uint_t al_uuidlen; /* length of al_uuid (not incl. \0) */ fmd_log_t *al_log; /* persistent event log */ char *al_asru_name; /* string form of asru fmri (ro) */ char *al_fru_name; /* string form of fru fmri (ro) */ char *al_rsrc_name; /* string form of resource fmri (ro) */ char *al_label; /* label */ char *al_case_uuid; /* case uuid */ nvlist_t *al_asru_fmri; /* nvlist form of resource fmri (ro) */ fmd_case_t *al_case; /* case associated with last change */ nvlist_t *al_event; /* event associated with last change */ uint_t al_refs; /* reference count */ uint_t al_flags; /* flags (see below) */ uint8_t al_reason; /* repair reason (see below) */ } fmd_asru_link_t; #define FMD_ASRU_FAULTY 0x01 /* asru has been diagnosed as faulty */ #define FMD_ASRU_UNUSABLE 0x02 /* asru can not be used at present */ #define FMD_ASRU_VALID 0x04 /* asru is initialized and valid */ #define FMD_ASRU_INTERNAL 0x08 /* asru is managed by fmd itself */ #define FMD_ASRU_INVISIBLE 0x10 /* asru is not visibly administered */ #define FMD_ASRU_RECREATED 0x20 /* asru recreated by cache replay */ #define FMD_ASRU_PRESENT 0x40 /* asru present at last R$ update */ #define FMD_ASRU_DEGRADED 0x80 /* asru service is degraded */ #define FMD_ASRU_PROXY 0x100 /* asru on proxy */ #define FMD_ASRU_PROXY_WITH_ASRU 0x200 /* asru accessible locally on proxy */ #define FMD_ASRU_PROXY_EXTERNAL 0x400 /* proxy over external transport */ #define FMD_ASRU_PROXY_RDONLY 0x800 /* proxy over readonly transport */ /* * Note the following are defined in order of increasing precedence and * this should not be changed */ #define FMD_ASRU_REMOVED 0 /* asru removed */ #define FMD_ASRU_ACQUITTED 1 /* asru acquitted */ #define FMD_ASRU_REPAIRED 2 /* asru repaired */ #define FMD_ASRU_REPLACED 3 /* asru replaced */ #define FMD_ASRU_STATE (FMD_ASRU_FAULTY | FMD_ASRU_UNUSABLE) #define FMD_ASRU_AL_HASH_NAME(a, off) \ *(char **)((uint8_t *)a + off) #define FMD_ASRU_AL_HASH_NEXT(a, off) \ *(fmd_asru_link_t **)((uint8_t *)a + off) #define FMD_ASRU_AL_HASH_NEXTP(a, off) \ (fmd_asru_link_t **)((uint8_t *)a + off) typedef struct fmd_asru_hash { pthread_rwlock_t ah_lock; /* r/w lock protecting hash contents */ fmd_asru_t **ah_hash; /* hash bucket array for asrus */ fmd_asru_link_t **ah_asru_hash; /* hash bucket array for asrus */ fmd_asru_link_t **ah_case_hash; /* hash bucket array for frus */ fmd_asru_link_t **ah_fru_hash; /* hash bucket array for cases */ fmd_asru_link_t **ah_label_hash; /* label hash bucket array */ fmd_asru_link_t **ah_rsrc_hash; /* hash bucket array for rsrcs */ uint_t ah_hashlen; /* length of hash bucket array */ char *ah_dirpath; /* path of hash's log file directory */ uint64_t ah_lifetime; /* max lifetime of log if not present */ uint_t ah_al_count; /* count of number of entries in hash */ uint_t ah_count; /* count of separate rsrcs in hash */ int ah_error; /* error from opening asru log */ fmd_topo_t *ah_topo; /* topo handle */ } fmd_asru_hash_t; extern fmd_asru_hash_t *fmd_asru_hash_create(const char *, const char *); extern void fmd_asru_hash_destroy(fmd_asru_hash_t *); extern void fmd_asru_hash_refresh(fmd_asru_hash_t *); extern void fmd_asru_hash_replay(fmd_asru_hash_t *); extern void fmd_asru_hash_apply(fmd_asru_hash_t *, void (*)(fmd_asru_t *, void *), void *); extern void fmd_asru_al_hash_apply(fmd_asru_hash_t *, void (*)(fmd_asru_link_t *, void *), void *); extern void fmd_asru_hash_apply_by_asru(fmd_asru_hash_t *, const char *, void (*)(fmd_asru_link_t *, void *), void *); extern void fmd_asru_hash_apply_by_label(fmd_asru_hash_t *, const char *, void (*)(fmd_asru_link_t *, void *), void *); extern void fmd_asru_hash_apply_by_fru(fmd_asru_hash_t *, const char *, void (*)(fmd_asru_link_t *, void *), void *); extern void fmd_asru_hash_apply_by_rsrc(fmd_asru_hash_t *, const char *, void (*)(fmd_asru_link_t *, void *), void *); extern void fmd_asru_hash_apply_by_case(fmd_asru_hash_t *, fmd_case_t *, void (*)(fmd_asru_link_t *, void *), void *); extern fmd_asru_t *fmd_asru_hash_lookup_name(fmd_asru_hash_t *, const char *); extern fmd_asru_link_t *fmd_asru_hash_create_entry(fmd_asru_hash_t *, fmd_case_t *, nvlist_t *); extern void fmd_asru_hash_release(fmd_asru_hash_t *, fmd_asru_t *); extern void fmd_asru_hash_delete_case(fmd_asru_hash_t *, fmd_case_t *); extern void fmd_asru_clear_aged_rsrcs(); /* * flags used in fara_bywhat field in fmd_asru_rep_arg_t */ #define FARA_ALL 0 #define FARA_BY_CASE 1 #define FARA_BY_ASRU 2 #define FARA_BY_FRU 3 #define FARA_BY_RSRC 4 #define FARA_BY_LABEL 5 /* * Return values for fmd_asru_repaired. May return "ok" or "not replaced". * If no fault is found we will get default value of "not found". */ #define FARA_OK 0 #define FARA_ERR_RSRCNOTF 1 #define FARA_ERR_RSRCNOTR 2 /* * The following structures are used to pass arguments to the corresponding * function when walking the resource cache by case etc. */ typedef struct { uint8_t fara_reason; /* repaired, acquit, replaced, removed */ uint8_t fara_bywhat; /* whether doing a walk by case, asru, etc */ int *fara_rval; /* for return success or failure */ char *fara_uuid; /* uuid can be passed in for comparison */ } fmd_asru_rep_arg_t; extern void fmd_asru_repaired(fmd_asru_link_t *, void *); extern void fmd_asru_flush(fmd_asru_link_t *, void *); typedef struct { int *faus_countp; int faus_maxcount; uint8_t *faus_ba; /* received status for each suspect */ uint8_t *faus_proxy_asru; /* asru on proxy for each suspect? */ uint8_t *faus_diag_asru; /* asru on diag for each suspect? */ boolean_t faus_is_proxy; /* are we on the proxy side? */ } fmd_asru_update_status_t; extern void fmd_asru_update_status(fmd_asru_link_t *alp, void *arg); typedef struct { int *fasp_countp; int fasp_maxcount; uint8_t *fasp_proxy_asru; /* asru on proxy for each suspect? */ int fasp_proxy_external; /* is this an external transport? */ int fasp_proxy_rdonly; /* is this a rdonly transport? */ } fmd_asru_set_on_proxy_t; extern void fmd_asru_set_on_proxy(fmd_asru_link_t *alp, void *arg); extern void fmd_asru_update_containees(fmd_asru_link_t *alp, void *arg); typedef struct { int *facs_countp; int facs_maxcount; } fmd_asru_close_status_t; extern void fmd_asru_close_status(fmd_asru_link_t *alp, void *arg); extern int fmd_asru_setflags(fmd_asru_link_t *, uint_t); extern int fmd_asru_clrflags(fmd_asru_link_t *, uint_t, uint8_t); extern void fmd_asru_log_resolved(fmd_asru_link_t *, void *); extern int fmd_asru_al_getstate(fmd_asru_link_t *); extern int fmd_asru_getstate(fmd_asru_t *); extern void fmd_asru_check_if_aged(fmd_asru_link_t *, void *); void fmd_asru_most_recent(fmd_asru_link_t *, void *); #ifdef __cplusplus } #endif #endif /* _FMD_ASRU_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include static fmd_buf_t * fmd_buf_alloc(const char *name, size_t size) { fmd_buf_t *bp = fmd_alloc(sizeof (fmd_buf_t), FMD_SLEEP); bp->buf_name = fmd_strdup(name, FMD_SLEEP); bp->buf_next = NULL; bp->buf_data = fmd_zalloc(size, FMD_SLEEP); bp->buf_size = size; bp->buf_flags = FMD_BUF_DIRTY; return (bp); } static void fmd_buf_free(fmd_buf_t *bp) { fmd_strfree(bp->buf_name); fmd_free(bp->buf_data, bp->buf_size); fmd_free(bp, sizeof (fmd_buf_t)); } void fmd_buf_hash_create(fmd_buf_hash_t *bhp) { bhp->bh_hashlen = fmd.d_str_buckets; bhp->bh_hash = fmd_zalloc(sizeof (void *) * bhp->bh_hashlen, FMD_SLEEP); bhp->bh_count = 0; } size_t fmd_buf_hash_destroy(fmd_buf_hash_t *bhp) { size_t total = 0; fmd_buf_t *bp, *np; uint_t i; for (i = 0; i < bhp->bh_hashlen; i++) { for (bp = bhp->bh_hash[i]; bp != NULL; bp = np) { np = bp->buf_next; total += bp->buf_size; fmd_buf_free(bp); } } fmd_free(bhp->bh_hash, sizeof (void *) * bhp->bh_hashlen); bzero(bhp, sizeof (fmd_buf_hash_t)); return (total); } void fmd_buf_hash_apply(fmd_buf_hash_t *bhp, fmd_buf_f *func, void *arg) { fmd_buf_t *bp; uint_t i; for (i = 0; i < bhp->bh_hashlen; i++) { for (bp = bhp->bh_hash[i]; bp != NULL; bp = bp->buf_next) func(bp, arg); } } void fmd_buf_hash_commit(fmd_buf_hash_t *bhp) { fmd_buf_t *bp; uint_t i; for (i = 0; i < bhp->bh_hashlen; i++) { for (bp = bhp->bh_hash[i]; bp != NULL; bp = bp->buf_next) bp->buf_flags &= ~FMD_BUF_DIRTY; } } uint_t fmd_buf_hash_count(fmd_buf_hash_t *bhp) { return (bhp->bh_count); } fmd_buf_t * fmd_buf_insert(fmd_buf_hash_t *bhp, const char *name, size_t size) { uint_t h = fmd_strhash(name) % bhp->bh_hashlen; fmd_buf_t *bp = fmd_buf_alloc(name, size); bp->buf_next = bhp->bh_hash[h]; bhp->bh_hash[h] = bp; bhp->bh_count++; return (bp); } fmd_buf_t * fmd_buf_lookup(fmd_buf_hash_t *bhp, const char *name) { uint_t h = fmd_strhash(name) % bhp->bh_hashlen; fmd_buf_t *bp; for (bp = bhp->bh_hash[h]; bp != NULL; bp = bp->buf_next) { if (strcmp(name, bp->buf_name) == 0) return (bp); } return (NULL); } void fmd_buf_delete(fmd_buf_hash_t *bhp, const char *name) { uint_t h = fmd_strhash(name) % bhp->bh_hashlen; fmd_buf_t *bp, **pp = &bhp->bh_hash[h]; for (bp = *pp; bp != NULL; bp = bp->buf_next) { if (strcmp(bp->buf_name, name) != 0) pp = &bp->buf_next; else break; } if (bp != NULL) { *pp = bp->buf_next; fmd_buf_free(bp); ASSERT(bhp->bh_count != 0); bhp->bh_count--; } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_BUF_H #define _FMD_BUF_H #include #ifdef __cplusplus extern "C" { #endif typedef struct fmd_buf { char *buf_name; /* name of this buffer */ struct fmd_buf *buf_next; /* next buffer in hash chain */ void *buf_data; /* buffer data storage */ size_t buf_size; /* buffer size */ uint_t buf_flags; /* buffer flags (see below) */ } fmd_buf_t; #define FMD_BUF_DIRTY 0x1 /* buffer is dirty (needs checkpoint) */ typedef void fmd_buf_f(fmd_buf_t *, void *); typedef struct fmd_buf_hash { fmd_buf_t **bh_hash; /* hash bucket array for buffers */ uint_t bh_hashlen; /* length of hash bucket array */ uint_t bh_count; /* number of buffers in hash */ } fmd_buf_hash_t; extern void fmd_buf_hash_create(fmd_buf_hash_t *); extern size_t fmd_buf_hash_destroy(fmd_buf_hash_t *); extern void fmd_buf_hash_apply(fmd_buf_hash_t *, fmd_buf_f *, void *); extern void fmd_buf_hash_commit(fmd_buf_hash_t *); extern uint_t fmd_buf_hash_count(fmd_buf_hash_t *); extern fmd_buf_t *fmd_buf_insert(fmd_buf_hash_t *, const char *, size_t); extern fmd_buf_t *fmd_buf_lookup(fmd_buf_hash_t *, const char *); extern void fmd_buf_delete(fmd_buf_hash_t *, const char *); #ifdef __cplusplus } #endif #endif /* _FMD_BUF_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include static const struct fmd_builtin _fmd_builtins[] = { { "fmd-self-diagnosis", self_init, self_fini, FMD_BUILTIN_ALLCTXT }, { "sysevent-transport", sysev_init, sysev_fini, FMD_BUILTIN_CTXT_GLOBALZONE }, { NULL, NULL, NULL, 0 } }; static int bltin_init(fmd_module_t *mp) { const fmd_builtin_t *bp; for (bp = _fmd_builtins; bp->bltin_name != NULL; bp++) { if (strcmp(mp->mod_name, bp->bltin_name) == 0) break; } if (bp == NULL) return (fmd_set_errno(EFMD_BLTIN_NAME)); if (bp->bltin_init == NULL) return (fmd_set_errno(EFMD_BLTIN_INIT)); mp->mod_data = (void *)bp; (void) pthread_mutex_unlock(&mp->mod_lock); /* * Call _fmd_init() in the module. If this causes a module abort and * mod_info has been registered, unregister it on behalf of the module. */ if (fmd_module_enter(mp, bp->bltin_init) != 0 && mp->mod_info != NULL) fmd_hdl_unregister((fmd_hdl_t *)mp); fmd_module_exit(mp); (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_info == NULL) return (fmd_set_errno(EFMD_HDL_INIT)); return (0); } static int bltin_fini(fmd_module_t *mp) { fmd_builtin_t *bp = mp->mod_data; if (mp->mod_info != NULL) { (void) fmd_module_enter(mp, bp->bltin_fini); if (mp->mod_info != NULL) { fmd_module_lock(mp); fmd_module_unregister(mp); fmd_module_unlock(mp); } fmd_module_exit(mp); } return (0); } const fmd_modops_t fmd_bltin_ops = { bltin_init, bltin_fini, fmd_module_dispatch, fmd_module_transport, }; int fmd_builtin_loadall(fmd_modhash_t *mhp) { const fmd_builtin_t *bp; uint32_t ctxt = 0; int err = 0; ctxt |= (getzoneid() == GLOBAL_ZONEID) ? FMD_BUILTIN_CTXT_GLOBALZONE : FMD_BUILTIN_CTXT_NONGLOBALZONE; for (bp = _fmd_builtins; bp->bltin_name != NULL; bp++) { if (!(ctxt & bp->bltin_ctxts)) continue; if (fmd_modhash_load(mhp, bp->bltin_name, &fmd_bltin_ops) == NULL) err = -1; } return (err); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_BUILTIN_H #define _FMD_BUILTIN_H #ifdef __cplusplus extern "C" { #endif #include #include /* * fmd_builtin.h * * This header file provides prototypes for any built-in diagnosis engines and * agents that are compiled directly into fmd. Prototypes for their init and * fini routines can be added here and corresponding linkage information to * these functions should be added to the table found in fmd_builtin.c. */ typedef struct fmd_builtin { const char *bltin_name; void (*bltin_init)(fmd_hdl_t *); void (*bltin_fini)(fmd_hdl_t *); int bltin_ctxts; } fmd_builtin_t; #define FMD_BUILTIN_CTXT_GLOBALZONE 0x1 #define FMD_BUILTIN_CTXT_NONGLOBALZONE 0x2 #define FMD_BUILTIN_ALLCTXT \ (FMD_BUILTIN_CTXT_GLOBALZONE | FMD_BUILTIN_CTXT_NONGLOBALZONE) extern int fmd_builtin_loadall(fmd_modhash_t *); extern void self_init(fmd_hdl_t *); /* see fmd_self.c */ extern void self_fini(fmd_hdl_t *); /* see fmd_self.c */ extern void sysev_init(fmd_hdl_t *); /* see fmd_transport.c */ extern void sysev_fini(fmd_hdl_t *); /* see fmd_transport.c */ #ifdef __cplusplus } #endif #endif /* _FMD_BUILTIN_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * FMD Case Subsystem * * Diagnosis engines are expected to group telemetry events related to the * diagnosis of a particular problem on the system into a set of cases. The * diagnosis engine may have any number of cases open at a given point in time. * Some cases may eventually be *solved* by associating a suspect list of one * or more problems with the case, at which point fmd publishes a list.suspect * event for the case and it becomes visible to administrators and agents. * * Every case is named using a UUID, and is globally visible in the case hash. * Cases are reference-counted, except for the reference from the case hash * itself. Consumers of case references include modules, which store active * cases on the mod_cases list, ASRUs in the resource cache, and the RPC code. * * Cases obey the following state machine. In states UNSOLVED, SOLVED, and * CLOSE_WAIT, a case's module refers to the owning module (a diagnosis engine * or transport) and the case is referenced by the mod_cases list. Once the * case reaches the CLOSED or REPAIRED states, a case's module changes to refer * to the root module (fmd.d_rmod) and is deleted from the owner's mod_cases. * * +------------+ * +----------| UNSOLVED | * | +------------+ * | 1 | * | | * | +-------v----+ * 2 | | SOLVED | * | +------------+ * | 3 | 5 | * +------------+ | | * | | | * +-v---v----v-+ * | CLOSE_WAIT | * +------------+ * | | | * +-----------+ | +------------+ * | 4 | | * v +-----v------+ | * discard | CLOSED | 6 | * +------------+ | * | | * | +------------+ * 7 | | * +-----v----v-+ * | REPAIRED | * +------------+ * | * 8 | * +-----v------+ * | RESOLVED | * +------------+ * | * v * discard * * The state machine changes are triggered by calls to fmd_case_transition() * from various locations inside of fmd, as described below: * * [1] Called by: fmd_case_solve() * Actions: FMD_CF_SOLVED flag is set in ci_flags * conviction policy is applied to suspect list * suspects convicted are marked faulty (F) in R$ * list.suspect event logged and dispatched * * [2] Called by: fmd_case_close(), fmd_case_uuclose() * Actions: diagnosis engine fmdo_close() entry point scheduled * case discarded upon exit from CLOSE_WAIT * * [3] Called by: fmd_case_close(), fmd_case_uuclose(), fmd_xprt_event_uuclose() * Actions: FMD_CF_ISOLATED flag is set in ci_flags * suspects convicted (F) are marked unusable (U) in R$ * diagnosis engine fmdo_close() entry point scheduled * case transitions to CLOSED [4] upon exit from CLOSE_WAIT * * [4] Called by: fmd_case_delete() (after fmdo_close() entry point returns) * Actions: list.isolated event dispatched * case deleted from module's list of open cases * * [5] Called by: fmd_case_repair(), fmd_case_update() * Actions: FMD_CF_REPAIR flag is set in ci_flags * diagnosis engine fmdo_close() entry point scheduled * case transitions to REPAIRED [6] upon exit from CLOSE_WAIT * * [6] Called by: fmd_case_delete() (after fmdo_close() entry point returns) * Actions: suspects convicted are marked non faulty (!F) in R$ * list.repaired or list.updated event dispatched * * [7] Called by: fmd_case_repair(), fmd_case_update() * Actions: FMD_CF_REPAIR flag is set in ci_flags * suspects convicted are marked non faulty (!F) in R$ * list.repaired or list.updated event dispatched * * [8] Called by: fmd_case_uuresolve() * Actions: list.resolved event dispatched * case is discarded */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *const _fmd_case_snames[] = { "UNSOLVED", /* FMD_CASE_UNSOLVED */ "SOLVED", /* FMD_CASE_SOLVED */ "CLOSE_WAIT", /* FMD_CASE_CLOSE_WAIT */ "CLOSED", /* FMD_CASE_CLOSED */ "REPAIRED", /* FMD_CASE_REPAIRED */ "RESOLVED" /* FMD_CASE_RESOLVED */ }; static fmd_case_impl_t *fmd_case_tryhold(fmd_case_impl_t *); fmd_case_hash_t * fmd_case_hash_create(void) { fmd_case_hash_t *chp = fmd_alloc(sizeof (fmd_case_hash_t), FMD_SLEEP); (void) pthread_rwlock_init(&chp->ch_lock, NULL); chp->ch_hashlen = fmd.d_str_buckets; chp->ch_hash = fmd_zalloc(sizeof (void *) * chp->ch_hashlen, FMD_SLEEP); chp->ch_code_hash = fmd_zalloc(sizeof (void *) * chp->ch_hashlen, FMD_SLEEP); chp->ch_count = 0; return (chp); } /* * 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->ch_code_hash, sizeof (void *) * chp->ch_hashlen); fmd_free(chp, sizeof (fmd_case_hash_t)); } /* * 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) *cpp++ = fmd_case_tryhold(cp); } ASSERT(cpp == cps + cpc); (void) pthread_rwlock_unlock(&chp->ch_lock); for (i = 0; i < cpc; i++) { if (cps[i] != NULL) { 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_case_code_hash_insert(fmd_case_hash_t *chp, fmd_case_impl_t *cip) { uint_t h = fmd_strhash(cip->ci_code) % chp->ch_hashlen; cip->ci_code_next = chp->ch_code_hash[h]; chp->ch_code_hash[h] = cip; } static void fmd_case_code_hash_delete(fmd_case_hash_t *chp, fmd_case_impl_t *cip) { fmd_case_impl_t **pp, *cp; if (cip->ci_code) { uint_t h = fmd_strhash(cip->ci_code) % chp->ch_hashlen; pp = &chp->ch_code_hash[h]; for (cp = *pp; cp != NULL; cp = cp->ci_code_next) { if (cp != cip) pp = &cp->ci_code_next; else break; } if (cp != NULL) { *pp = cp->ci_code_next; cp->ci_code_next = NULL; } } } /* * 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; fmd_case_hash_t *chp = fmd.d_cases; char **keys, **keyp; const char *s; ASSERT(MUTEX_HELD(&cip->ci_lock)); ASSERT(cip->ci_state >= FMD_CASE_SOLVED); /* * delete any existing entry from code hash if it is on it */ fmd_case_code_hash_delete(chp, cip); 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); } /* * add into hash of solved cases */ fmd_case_code_hash_insert(chp, cip); return (cip->ci_code); } typedef struct { int *fcl_countp; int fcl_maxcount; uint8_t *fcl_ba; nvlist_t **fcl_nva; int *fcl_msgp; } fmd_case_lst_t; static void fmd_case_set_lst(fmd_asru_link_t *alp, void *arg) { fmd_case_lst_t *entryp = (fmd_case_lst_t *)arg; boolean_t b; int state; if (*entryp->fcl_countp >= entryp->fcl_maxcount) return; if (nvlist_lookup_boolean_value(alp->al_event, FM_SUSPECT_MESSAGE, &b) == 0 && b == B_FALSE) *entryp->fcl_msgp = B_FALSE; entryp->fcl_ba[*entryp->fcl_countp] = 0; state = fmd_asru_al_getstate(alp); if (state & FMD_ASRU_DEGRADED) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_DEGRADED; if (state & FMD_ASRU_UNUSABLE) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_UNUSABLE; if (state & FMD_ASRU_FAULTY) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_FAULTY; if (!(state & FMD_ASRU_PRESENT)) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_NOT_PRESENT; if (alp->al_reason == FMD_ASRU_REPAIRED) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_REPAIRED; else if (alp->al_reason == FMD_ASRU_REPLACED) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_REPLACED; else if (alp->al_reason == FMD_ASRU_ACQUITTED) entryp->fcl_ba[*entryp->fcl_countp] |= FM_SUSPECT_ACQUITTED; entryp->fcl_nva[*entryp->fcl_countp] = alp->al_event; (*entryp->fcl_countp)++; } static void fmd_case_faulty(fmd_asru_link_t *alp, void *arg) { int *faultyp = (int *)arg; *faultyp |= (alp->al_flags & FMD_ASRU_FAULTY); } static void fmd_case_usable(fmd_asru_link_t *alp, void *arg) { int *usablep = (int *)arg; *usablep |= !(fmd_asru_al_getstate(alp) & FMD_ASRU_UNUSABLE); } static void fmd_case_not_faulty(fmd_asru_link_t *alp, void *arg) { int *not_faultyp = (int *)arg; *not_faultyp |= !(alp->al_flags & FMD_ASRU_FAULTY); } /* * Have we got any suspects with an asru that are still unusable and present? */ static void fmd_case_unusable_and_present(fmd_asru_link_t *alp, void *arg) { int *rvalp = (int *)arg; int state; nvlist_t *asru; /* * if this a proxy case and this suspect doesn't have an local asru * then state is unknown so we must assume it may still be unusable. */ if ((alp->al_flags & FMD_ASRU_PROXY) && !(alp->al_flags & FMD_ASRU_PROXY_WITH_ASRU)) { *rvalp |= B_TRUE; return; } state = fmd_asru_al_getstate(alp); if (nvlist_lookup_nvlist(alp->al_event, FM_FAULT_ASRU, &asru) != 0) return; *rvalp |= ((state & FMD_ASRU_UNUSABLE) && (state & FMD_ASRU_PRESENT)); } nvlist_t * fmd_case_mkevent(fmd_case_t *cp, const char *class) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; nvlist_t **nva, *nvl; uint8_t *ba; int msg = B_TRUE; const char *code; fmd_case_lst_t fcl; int count = 0; (void) pthread_mutex_lock(&cip->ci_lock); ASSERT(cip->ci_state >= FMD_CASE_SOLVED); nva = alloca(sizeof (nvlist_t *) * cip->ci_nsuspects); ba = alloca(sizeof (uint8_t) * cip->ci_nsuspects); /* * For each suspect associated with the case, store its fault event * nvlist in 'nva'. We also look to see if any of the suspect faults * have asked not to be messaged. If any of them have made such a * request, propagate that attribute to the composite list.* event. * Finally, store each suspect's faulty status into the bitmap 'ba'. */ fcl.fcl_countp = &count; fcl.fcl_maxcount = cip->ci_nsuspects; fcl.fcl_msgp = &msg; fcl.fcl_ba = ba; fcl.fcl_nva = nva; fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_set_lst, &fcl); if (cip->ci_code == NULL) (void) fmd_case_mkcode(cp); /* * For repair and updated event, we lookup diagcode from dict using key * "list.repaired" or "list.updated" or "list.resolved". */ if (strcmp(class, FM_LIST_REPAIRED_CLASS) == 0) (void) fmd_conf_getprop(fmd.d_conf, "repaircode", &code); else if (strcmp(class, FM_LIST_RESOLVED_CLASS) == 0) (void) fmd_conf_getprop(fmd.d_conf, "resolvecode", &code); else if (strcmp(class, FM_LIST_UPDATED_CLASS) == 0) (void) fmd_conf_getprop(fmd.d_conf, "updatecode", &code); else code = cip->ci_code; if (msg == B_FALSE) cip->ci_flags |= FMD_CF_INVISIBLE; /* * Use the ci_diag_de if one has been saved (eg for an injected fault). * Otherwise use the authority for the current module. */ nvl = fmd_protocol_list(class, cip->ci_diag_de == NULL ? cip->ci_mod->mod_fmri : cip->ci_diag_de, cip->ci_uuid, code, count, nva, ba, msg, &cip->ci_tv, cip->ci_injected); (void) pthread_mutex_unlock(&cip->ci_lock); return (nvl); } static int fmd_case_match_on_faulty_overlap = 1; static int fmd_case_match_on_acquit_overlap = 1; static int fmd_case_auto_acquit_isolated = 1; static int fmd_case_auto_acquit_non_acquitted = 1; static int fmd_case_too_recent = 10; /* time in seconds */ static boolean_t fmd_case_compare_elem(nvlist_t *nvl, nvlist_t *xnvl, const char *elem) { nvlist_t *new_rsrc; nvlist_t *rsrc; char *new_name = NULL; char *name = NULL; ssize_t new_namelen; ssize_t namelen; int fmri_present = 1; int new_fmri_present = 1; int match = B_FALSE; fmd_topo_t *ftp = fmd_topo_hold(); if (nvlist_lookup_nvlist(xnvl, elem, &rsrc) != 0) fmri_present = 0; else { if ((namelen = fmd_fmri_nvl2str(rsrc, NULL, 0)) == -1) goto done; name = fmd_alloc(namelen + 1, FMD_SLEEP); if (fmd_fmri_nvl2str(rsrc, name, namelen + 1) == -1) goto done; } if (nvlist_lookup_nvlist(nvl, elem, &new_rsrc) != 0) new_fmri_present = 0; else { if ((new_namelen = fmd_fmri_nvl2str(new_rsrc, NULL, 0)) == -1) goto done; new_name = fmd_alloc(new_namelen + 1, FMD_SLEEP); if (fmd_fmri_nvl2str(new_rsrc, new_name, new_namelen + 1) == -1) goto done; } match = (fmri_present == new_fmri_present && (fmri_present == 0 || topo_fmri_strcmp(ftp->ft_hdl, name, new_name))); done: if (name != NULL) fmd_free(name, namelen + 1); if (new_name != NULL) fmd_free(new_name, new_namelen + 1); fmd_topo_rele(ftp); return (match); } static int fmd_case_match_suspect(nvlist_t *nvl1, nvlist_t *nvl2) { char *class, *new_class; if (!fmd_case_compare_elem(nvl1, nvl2, FM_FAULT_ASRU)) return (0); if (!fmd_case_compare_elem(nvl1, nvl2, FM_FAULT_RESOURCE)) return (0); if (!fmd_case_compare_elem(nvl1, nvl2, FM_FAULT_FRU)) return (0); (void) nvlist_lookup_string(nvl2, FM_CLASS, &class); (void) nvlist_lookup_string(nvl1, FM_CLASS, &new_class); return (strcmp(class, new_class) == 0); } typedef struct { int *fcms_countp; int fcms_maxcount; fmd_case_impl_t *fcms_cip; uint8_t *fcms_new_susp_state; uint8_t *fcms_old_susp_state; uint8_t *fcms_old_match_state; } fcms_t; #define SUSPECT_STATE_FAULTY 0x1 #define SUSPECT_STATE_ISOLATED 0x2 #define SUSPECT_STATE_REMOVED 0x4 #define SUSPECT_STATE_ACQUITED 0x8 #define SUSPECT_STATE_REPAIRED 0x10 #define SUSPECT_STATE_REPLACED 0x20 #define SUSPECT_STATE_NO_MATCH 0x1 /* * This is called for each suspect in the old case. Compare it against each * suspect in the new case, setting fcms_old_susp_state and fcms_new_susp_state * as appropriate. fcms_new_susp_state will left as 0 if the suspect is not * found in the old case. */ static void fmd_case_match_suspects(fmd_asru_link_t *alp, void *arg) { fcms_t *fcmsp = (fcms_t *)arg; fmd_case_impl_t *cip = fcmsp->fcms_cip; fmd_case_susp_t *cis; int i = 0; int state = fmd_asru_al_getstate(alp); if (*fcmsp->fcms_countp >= fcmsp->fcms_maxcount) return; if (!(state & FMD_ASRU_PRESENT) || (!(state & FMD_ASRU_FAULTY) && alp->al_reason == FMD_ASRU_REMOVED)) fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp] = SUSPECT_STATE_REMOVED; else if ((state & FMD_ASRU_UNUSABLE) && (state & FMD_ASRU_FAULTY)) fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp] = SUSPECT_STATE_ISOLATED; else if (state & FMD_ASRU_FAULTY) fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp] = SUSPECT_STATE_FAULTY; else if (alp->al_reason == FMD_ASRU_REPLACED) fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp] = SUSPECT_STATE_REPLACED; else if (alp->al_reason == FMD_ASRU_ACQUITTED) fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp] = SUSPECT_STATE_ACQUITED; else fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp] = SUSPECT_STATE_REPAIRED; for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next, i++) if (fmd_case_match_suspect(cis->cis_nvl, alp->al_event) == 1) break; if (cis != NULL) fcmsp->fcms_new_susp_state[i] = fcmsp->fcms_old_susp_state[*fcmsp->fcms_countp]; else fcmsp->fcms_old_match_state[*fcmsp->fcms_countp] |= SUSPECT_STATE_NO_MATCH; (*fcmsp->fcms_countp)++; } typedef struct { int *fca_do_update; fmd_case_impl_t *fca_cip; } fca_t; /* * Re-fault all acquitted suspects that are still present in the new list. */ static void fmd_case_fault_acquitted_matching(fmd_asru_link_t *alp, void *arg) { fca_t *fcap = (fca_t *)arg; fmd_case_impl_t *cip = fcap->fca_cip; fmd_case_susp_t *cis; int state = fmd_asru_al_getstate(alp); if (!(state & FMD_ASRU_FAULTY) && alp->al_reason == FMD_ASRU_ACQUITTED) { for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) if (fmd_case_match_suspect(cis->cis_nvl, alp->al_event) == 1) break; if (cis != NULL) { (void) fmd_asru_setflags(alp, FMD_ASRU_FAULTY); *fcap->fca_do_update = 1; } } } /* * Re-fault all suspects that are still present in the new list. */ static void fmd_case_fault_all_matching(fmd_asru_link_t *alp, void *arg) { fca_t *fcap = (fca_t *)arg; fmd_case_impl_t *cip = fcap->fca_cip; fmd_case_susp_t *cis; int state = fmd_asru_al_getstate(alp); if (!(state & FMD_ASRU_FAULTY)) { for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) if (fmd_case_match_suspect(cis->cis_nvl, alp->al_event) == 1) break; if (cis != NULL) { (void) fmd_asru_setflags(alp, FMD_ASRU_FAULTY); *fcap->fca_do_update = 1; } } } /* * Acquit all suspects that are no longer present in the new list. */ static void fmd_case_acquit_no_match(fmd_asru_link_t *alp, void *arg) { fca_t *fcap = (fca_t *)arg; fmd_case_impl_t *cip = fcap->fca_cip; fmd_case_susp_t *cis; int state = fmd_asru_al_getstate(alp); if (state & FMD_ASRU_FAULTY) { for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) if (fmd_case_match_suspect(cis->cis_nvl, alp->al_event) == 1) break; if (cis == NULL) { (void) fmd_asru_clrflags(alp, FMD_ASRU_FAULTY, FMD_ASRU_ACQUITTED); *fcap->fca_do_update = 1; } } } /* * Acquit all isolated suspects. */ static void fmd_case_acquit_isolated(fmd_asru_link_t *alp, void *arg) { int *do_update = (int *)arg; int state = fmd_asru_al_getstate(alp); if ((state & FMD_ASRU_PRESENT) && (state & FMD_ASRU_UNUSABLE) && (state & FMD_ASRU_FAULTY)) { (void) fmd_asru_clrflags(alp, FMD_ASRU_FAULTY, FMD_ASRU_ACQUITTED); *do_update = 1; } } /* * Acquit suspect which matches specified nvlist */ static void fmd_case_acquit_suspect(fmd_asru_link_t *alp, void *arg) { nvlist_t *nvl = (nvlist_t *)arg; int state = fmd_asru_al_getstate(alp); if ((state & FMD_ASRU_FAULTY) && fmd_case_match_suspect(nvl, alp->al_event) == 1) (void) fmd_asru_clrflags(alp, FMD_ASRU_FAULTY, FMD_ASRU_ACQUITTED); } typedef struct { fmd_case_impl_t *fccd_cip; uint8_t *fccd_new_susp_state; uint8_t *fccd_new_match_state; int *fccd_discard_new; int *fccd_adjust_new; } fccd_t; /* * see if a matching suspect list already exists in the cache */ static void fmd_case_check_for_dups(fmd_case_t *old_cp, void *arg) { fccd_t *fccdp = (fccd_t *)arg; fmd_case_impl_t *new_cip = fccdp->fccd_cip; fmd_case_impl_t *old_cip = (fmd_case_impl_t *)old_cp; int i, count = 0, do_update = 0, got_isolated_overlap = 0; int got_faulty_overlap = 0; int got_acquit_overlap = 0; boolean_t too_recent; uint64_t most_recent = 0; fcms_t fcms; fca_t fca; uint8_t *new_susp_state; uint8_t *old_susp_state; uint8_t *old_match_state; new_susp_state = alloca(new_cip->ci_nsuspects * sizeof (uint8_t)); for (i = 0; i < new_cip->ci_nsuspects; i++) new_susp_state[i] = 0; old_susp_state = alloca(old_cip->ci_nsuspects * sizeof (uint8_t)); for (i = 0; i < old_cip->ci_nsuspects; i++) old_susp_state[i] = 0; old_match_state = alloca(old_cip->ci_nsuspects * sizeof (uint8_t)); for (i = 0; i < old_cip->ci_nsuspects; i++) old_match_state[i] = 0; /* * Compare with each suspect in the existing case. */ fcms.fcms_countp = &count; fcms.fcms_maxcount = old_cip->ci_nsuspects; fcms.fcms_cip = new_cip; fcms.fcms_new_susp_state = new_susp_state; fcms.fcms_old_susp_state = old_susp_state; fcms.fcms_old_match_state = old_match_state; fmd_asru_hash_apply_by_case(fmd.d_asrus, (fmd_case_t *)old_cip, fmd_case_match_suspects, &fcms); /* * If we have some faulty, non-isolated suspects that overlap, then most * likely it is the suspects that overlap in the suspect lists that are * to blame. So we can consider this to be a match. */ for (i = 0; i < new_cip->ci_nsuspects; i++) if (new_susp_state[i] == SUSPECT_STATE_FAULTY) got_faulty_overlap = 1; if (got_faulty_overlap && fmd_case_match_on_faulty_overlap) goto got_match; /* * If we have no faulty, non-isolated suspects in the old case, but we * do have some acquitted suspects that overlap, then most likely it is * the acquitted suspects that overlap in the suspect lists that are * to blame. So we can consider this to be a match. */ for (i = 0; i < new_cip->ci_nsuspects; i++) if (new_susp_state[i] == SUSPECT_STATE_ACQUITED) got_acquit_overlap = 1; for (i = 0; i < old_cip->ci_nsuspects; i++) if (old_susp_state[i] == SUSPECT_STATE_FAULTY) got_acquit_overlap = 0; if (got_acquit_overlap && fmd_case_match_on_acquit_overlap) goto got_match; /* * Check that all suspects in the new list are present in the old list. * Return if we find one that isn't. */ for (i = 0; i < new_cip->ci_nsuspects; i++) if (new_susp_state[i] == 0) return; /* * Check that all suspects in the old list are present in the new list * *or* they are isolated or removed/replaced (which would explain why * they are not present in the new list). Return if we find one that is * faulty and unisolated or repaired or acquitted, and that is not * present in the new case. */ for (i = 0; i < old_cip->ci_nsuspects; i++) if (old_match_state[i] == SUSPECT_STATE_NO_MATCH && (old_susp_state[i] == SUSPECT_STATE_FAULTY || old_susp_state[i] == SUSPECT_STATE_ACQUITED || old_susp_state[i] == SUSPECT_STATE_REPAIRED)) return; got_match: /* * If the old case is already in repaired/resolved state, we can't * do anything more with it, so keep the new case, but acquit some * of the suspects if appropriate. */ if (old_cip->ci_state >= FMD_CASE_REPAIRED) { if (fmd_case_auto_acquit_non_acquitted) { *fccdp->fccd_adjust_new = 1; for (i = 0; i < new_cip->ci_nsuspects; i++) { fccdp->fccd_new_susp_state[i] |= new_susp_state[i]; if (new_susp_state[i] == 0) fccdp->fccd_new_susp_state[i] = SUSPECT_STATE_NO_MATCH; } } return; } /* * Otherwise discard the new case and keep the old, again updating the * state of the suspects as appropriate */ *fccdp->fccd_discard_new = 1; fca.fca_cip = new_cip; fca.fca_do_update = &do_update; /* * See if new case occurred within fmd_case_too_recent seconds of the * most recent modification to the old case and if so don't do * auto-acquit. This avoids problems if a flood of ereports come in and * they don't all get diagnosed before the first case causes some of * the devices to be isolated making it appear that an isolated device * was in the suspect list. */ fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_asru_most_recent, &most_recent); too_recent = (new_cip->ci_tv.tv_sec - most_recent < fmd_case_too_recent); if (got_faulty_overlap) { /* * Acquit any suspects not present in the new list, plus * any that are are present but are isolated. */ fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_case_acquit_no_match, &fca); if (fmd_case_auto_acquit_isolated && !too_recent) fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_case_acquit_isolated, &do_update); } else if (got_acquit_overlap) { /* * Re-fault the acquitted matching suspects and acquit all * isolated suspects. */ if (fmd_case_auto_acquit_isolated && !too_recent) { fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_case_fault_acquitted_matching, &fca); fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_case_acquit_isolated, &do_update); } } else if (fmd_case_auto_acquit_isolated) { /* * To get here, there must be no faulty or acquitted suspects, * but there must be at least one isolated suspect. Just acquit * non-matching isolated suspects. If there are no matching * isolated suspects, then re-fault all matching suspects. */ for (i = 0; i < new_cip->ci_nsuspects; i++) if (new_susp_state[i] == SUSPECT_STATE_ISOLATED) got_isolated_overlap = 1; if (!got_isolated_overlap) fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_case_fault_all_matching, &fca); fmd_asru_hash_apply_by_case(fmd.d_asrus, old_cp, fmd_case_acquit_no_match, &fca); } /* * If we've updated anything in the old case, call fmd_case_update() */ if (do_update) fmd_case_update(old_cp); } /* * Convict suspects in a case by applying a conviction policy and updating the * resource cache prior to emitting the list.suspect event for the given case. * At present, our policy is very simple: convict every suspect in the case. * In the future, this policy can be extended and made configurable to permit: * * - convicting the suspect with the highest FIT rate * - convicting the suspect with the cheapest FRU * - convicting the suspect with the FRU that is in a depot's inventory * - convicting the suspect with the longest lifetime * * and so forth. A word to the wise: this problem is significantly harder that * it seems at first glance. Future work should heed the following advice: * * Hacking the policy into C code here is a very bad idea. The policy needs to * be decided upon very carefully and fundamentally encodes knowledge of what * suspect list combinations can be emitted by what diagnosis engines. As such * fmd's code is the wrong location, because that would require fmd itself to * be updated for every diagnosis engine change, defeating the entire design. * The FMA Event Registry knows the suspect list combinations: policy inputs * can be derived from it and used to produce per-module policy configuration. * * If the policy needs to be dynamic and not statically fixed at either fmd * startup or module load time, any implementation of dynamic policy retrieval * must employ some kind of caching mechanism or be part of a built-in module. * The fmd_case_convict() function is called with locks held inside of fmd and * is not a place where unbounded blocking on some inter-process or inter- * system communication to another service (e.g. another daemon) can occur. */ static int fmd_case_convict(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_asru_hash_t *ahp = fmd.d_asrus; int discard_new = 0, i; fmd_case_susp_t *cis; fmd_asru_link_t *alp; uint8_t *new_susp_state; uint8_t *new_match_state; int adjust_new = 0; fccd_t fccd; fmd_case_impl_t *ncp, **cps, **cpp; uint_t cpc; fmd_case_hash_t *chp; /* * First we must see if any matching cases already exist. */ new_susp_state = alloca(cip->ci_nsuspects * sizeof (uint8_t)); for (i = 0; i < cip->ci_nsuspects; i++) new_susp_state[i] = 0; new_match_state = alloca(cip->ci_nsuspects * sizeof (uint8_t)); for (i = 0; i < cip->ci_nsuspects; i++) new_match_state[i] = 0; fccd.fccd_cip = cip; fccd.fccd_adjust_new = &adjust_new; fccd.fccd_new_susp_state = new_susp_state; fccd.fccd_new_match_state = new_match_state; fccd.fccd_discard_new = &discard_new; /* * Hold all cases */ chp = fmd.d_cases; (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 (ncp = chp->ch_hash[i]; ncp != NULL; ncp = ncp->ci_next) *cpp++ = fmd_case_tryhold(ncp); ASSERT(cpp == cps + cpc); (void) pthread_rwlock_unlock(&chp->ch_lock); /* * Run fmd_case_check_for_dups() on all cases except the current one. */ for (i = 0; i < cpc; i++) { if (cps[i] != NULL) { if (cps[i] != (fmd_case_impl_t *)cp) fmd_case_check_for_dups((fmd_case_t *)cps[i], &fccd); fmd_case_rele((fmd_case_t *)cps[i]); } } fmd_free(cps, cpc * sizeof (fmd_case_t *)); (void) pthread_mutex_lock(&cip->ci_lock); if (cip->ci_code == NULL) (void) fmd_case_mkcode(cp); else if (cip->ci_precanned) fmd_case_code_hash_insert(fmd.d_cases, cip); if (discard_new) { /* * We've found an existing case that is a match and it is not * already in repaired or resolved state. So we can close this * one as a duplicate. */ (void) pthread_mutex_unlock(&cip->ci_lock); return (1); } /* * Allocate new cache entries */ for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) { if ((alp = fmd_asru_hash_create_entry(ahp, cp, cis->cis_nvl)) == NULL) { fmd_error(EFMD_CASE_EVENT, "cannot convict suspect in " "%s: %s\n", cip->ci_uuid, fmd_strerror(errno)); continue; } alp->al_flags |= FMD_ASRU_PRESENT; alp->al_asru->asru_flags |= FMD_ASRU_PRESENT; (void) fmd_asru_clrflags(alp, FMD_ASRU_UNUSABLE, 0); (void) fmd_asru_setflags(alp, FMD_ASRU_FAULTY); } if (adjust_new) { int some_suspect = 0, some_not_suspect = 0; /* * There is one or more matching case but they are already in * repaired or resolved state. So we need to keep the new * case, but we can adjust it. Repaired/removed/replaced * suspects are unlikely to be to blame (unless there are * actually two separate faults). So if we have a combination of * repaired/replaced/removed suspects and acquitted suspects in * the old lists, then we should acquit in the new list those * that were repaired/replaced/removed in the old. */ for (i = 0; i < cip->ci_nsuspects; i++) { if ((new_susp_state[i] & SUSPECT_STATE_REPLACED) || (new_susp_state[i] & SUSPECT_STATE_REPAIRED) || (new_susp_state[i] & SUSPECT_STATE_REMOVED) || (new_match_state[i] & SUSPECT_STATE_NO_MATCH)) some_not_suspect = 1; else some_suspect = 1; } if (some_suspect && some_not_suspect) { for (cis = cip->ci_suspects, i = 0; cis != NULL; cis = cis->cis_next, i++) if ((new_susp_state[i] & SUSPECT_STATE_REPLACED) || (new_susp_state[i] & SUSPECT_STATE_REPAIRED) || (new_susp_state[i] & SUSPECT_STATE_REMOVED) || (new_match_state[i] & SUSPECT_STATE_NO_MATCH)) fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_acquit_suspect, cis->cis_nvl); } } (void) pthread_mutex_unlock(&cip->ci_lock); return (0); } void fmd_case_publish(fmd_case_t *cp, uint_t state) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_event_t *e; nvlist_t *nvl; char *class; if (state == FMD_CASE_CURRENT) state = cip->ci_state; /* use current state */ switch (state) { case FMD_CASE_SOLVED: (void) pthread_mutex_lock(&cip->ci_lock); /* * If we already have a code, then case is already solved. */ if (cip->ci_precanned == 0 && cip->ci_xprt == NULL && cip->ci_code != NULL) { (void) pthread_mutex_unlock(&cip->ci_lock); break; } if (cip->ci_tv_valid == 0) { fmd_time_gettimeofday(&cip->ci_tv); cip->ci_tv_valid = 1; } (void) pthread_mutex_unlock(&cip->ci_lock); if (fmd_case_convict(cp) == 1) { /* dupclose */ cip->ci_flags &= ~FMD_CF_SOLVED; fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, 0); break; } if (cip->ci_xprt != NULL) { /* * For proxy, save some information about the transport * in the resource cache. */ int count = 0; fmd_asru_set_on_proxy_t fasp; fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)cip->ci_xprt; fasp.fasp_countp = &count; fasp.fasp_maxcount = cip->ci_nsuspects; fasp.fasp_proxy_asru = cip->ci_proxy_asru; fasp.fasp_proxy_external = xip->xi_flags & FMD_XPRT_EXTERNAL; fasp.fasp_proxy_rdonly = ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_set_on_proxy, &fasp); } nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); (void) pthread_rwlock_rdlock(&fmd.d_log_lock); fmd_log_append(fmd.d_fltlog, e, cp); (void) pthread_rwlock_unlock(&fmd.d_log_lock); fmd_dispq_dispatch(fmd.d_disp, e, class); (void) pthread_mutex_lock(&cip->ci_mod->mod_stats_lock); cip->ci_mod->mod_stats->ms_casesolved.fmds_value.ui64++; (void) pthread_mutex_unlock(&cip->ci_mod->mod_stats_lock); break; case FMD_CASE_CLOSE_WAIT: fmd_case_hold(cp); e = fmd_event_create(FMD_EVT_CLOSE, FMD_HRT_NOW, NULL, cp); fmd_eventq_insert_at_head(cip->ci_mod->mod_queue, e); (void) pthread_mutex_lock(&cip->ci_mod->mod_stats_lock); cip->ci_mod->mod_stats->ms_caseclosed.fmds_value.ui64++; (void) pthread_mutex_unlock(&cip->ci_mod->mod_stats_lock); break; case FMD_CASE_CLOSED: nvl = fmd_case_mkevent(cp, FM_LIST_ISOLATED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dispq_dispatch(fmd.d_disp, e, class); break; case FMD_CASE_REPAIRED: nvl = fmd_case_mkevent(cp, FM_LIST_REPAIRED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); (void) pthread_rwlock_rdlock(&fmd.d_log_lock); fmd_log_append(fmd.d_fltlog, e, cp); (void) pthread_rwlock_unlock(&fmd.d_log_lock); fmd_dispq_dispatch(fmd.d_disp, e, class); break; case FMD_CASE_RESOLVED: nvl = fmd_case_mkevent(cp, FM_LIST_RESOLVED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); (void) pthread_rwlock_rdlock(&fmd.d_log_lock); fmd_log_append(fmd.d_fltlog, e, cp); (void) pthread_rwlock_unlock(&fmd.d_log_lock); fmd_dispq_dispatch(fmd.d_disp, e, class); break; } } fmd_case_t * fmd_case_hash_lookup(fmd_case_hash_t *chp, const char *uuid) { fmd_case_impl_t *cip; uint_t h; (void) pthread_rwlock_rdlock(&chp->ch_lock); h = fmd_strhash(uuid) % chp->ch_hashlen; for (cip = chp->ch_hash[h]; cip != NULL; cip = cip->ci_next) { if (strcmp(cip->ci_uuid, uuid) == 0) break; } /* * If deleting bit is set, treat the case as if it doesn't exist. */ if (cip != NULL) cip = fmd_case_tryhold(cip); if (cip == NULL) (void) fmd_set_errno(EFMD_CASE_INVAL); (void) pthread_rwlock_unlock(&chp->ch_lock); return ((fmd_case_t *)cip); } static fmd_case_impl_t * fmd_case_hash_insert(fmd_case_hash_t *chp, fmd_case_impl_t *cip) { fmd_case_impl_t *eip; uint_t h; (void) pthread_rwlock_wrlock(&chp->ch_lock); h = fmd_strhash(cip->ci_uuid) % chp->ch_hashlen; for (eip = chp->ch_hash[h]; eip != NULL; eip = eip->ci_next) { if (strcmp(cip->ci_uuid, eip->ci_uuid) == 0 && fmd_case_tryhold(eip) != NULL) { (void) pthread_rwlock_unlock(&chp->ch_lock); return (eip); /* uuid already present */ } } cip->ci_next = chp->ch_hash[h]; chp->ch_hash[h] = cip; chp->ch_count++; ASSERT(chp->ch_count != 0); (void) pthread_rwlock_unlock(&chp->ch_lock); return (cip); } static void fmd_case_hash_delete(fmd_case_hash_t *chp, fmd_case_impl_t *cip) { fmd_case_impl_t *cp, **pp; uint_t h; ASSERT(MUTEX_HELD(&cip->ci_lock)); cip->ci_flags |= FMD_CF_DELETING; (void) pthread_mutex_unlock(&cip->ci_lock); (void) pthread_rwlock_wrlock(&chp->ch_lock); h = fmd_strhash(cip->ci_uuid) % chp->ch_hashlen; pp = &chp->ch_hash[h]; for (cp = *pp; cp != NULL; cp = cp->ci_next) { if (cp != cip) pp = &cp->ci_next; else break; } if (cp == NULL) { fmd_panic("case %p (%s) not found on hash chain %u\n", (void *)cip, cip->ci_uuid, h); } *pp = cp->ci_next; cp->ci_next = NULL; /* * delete from code hash if it is on it */ fmd_case_code_hash_delete(chp, cip); ASSERT(chp->ch_count != 0); chp->ch_count--; (void) pthread_rwlock_unlock(&chp->ch_lock); (void) pthread_mutex_lock(&cip->ci_lock); ASSERT(cip->ci_flags & FMD_CF_DELETING); } fmd_case_t * fmd_case_create(fmd_module_t *mp, const char *uuidstr, void *data) { fmd_case_impl_t *cip = fmd_zalloc(sizeof (fmd_case_impl_t), FMD_SLEEP); fmd_case_impl_t *eip = NULL; uuid_t uuid; (void) pthread_mutex_init(&cip->ci_lock, NULL); fmd_buf_hash_create(&cip->ci_bufs); fmd_module_hold(mp); cip->ci_mod = mp; cip->ci_refs = 1; cip->ci_state = FMD_CASE_UNSOLVED; cip->ci_flags = FMD_CF_DIRTY; cip->ci_data = data; /* * Calling libuuid: get a clue. The library interfaces cleverly do not * define any constant for the length of an unparse string, and do not * permit the caller to specify a buffer length for safety. The spec * says it will be 36 bytes, but we make it tunable just in case. */ (void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &cip->ci_uuidlen); cip->ci_uuid = fmd_zalloc(cip->ci_uuidlen + 1, FMD_SLEEP); if (uuidstr == NULL) { /* * We expect this loop to execute only once, but code it * defensively against the possibility of libuuid bugs. * Keep generating uuids and attempting to do a hash insert * until we get a unique one. */ do { if (eip != NULL) fmd_case_rele((fmd_case_t *)eip); uuid_generate(uuid); uuid_unparse(uuid, cip->ci_uuid); } while ((eip = fmd_case_hash_insert(fmd.d_cases, cip)) != cip); } else { /* * If a uuid was specified we must succeed with that uuid, * or return NULL indicating a case with that uuid already * exists. */ (void) strncpy(cip->ci_uuid, uuidstr, cip->ci_uuidlen + 1); if (fmd_case_hash_insert(fmd.d_cases, cip) != cip) { fmd_free(cip->ci_uuid, cip->ci_uuidlen + 1); (void) fmd_buf_hash_destroy(&cip->ci_bufs); fmd_module_rele(mp); pthread_mutex_destroy(&cip->ci_lock); fmd_free(cip, sizeof (*cip)); return (NULL); } } ASSERT(fmd_module_locked(mp)); fmd_list_append(&mp->mod_cases, cip); fmd_module_setcdirty(mp); (void) pthread_mutex_lock(&cip->ci_mod->mod_stats_lock); cip->ci_mod->mod_stats->ms_caseopen.fmds_value.ui64++; (void) pthread_mutex_unlock(&cip->ci_mod->mod_stats_lock); return ((fmd_case_t *)cip); } static void fmd_case_destroy_suspects(fmd_case_impl_t *cip) { fmd_case_susp_t *cis, *ncis; ASSERT(MUTEX_HELD(&cip->ci_lock)); if (cip->ci_proxy_asru) fmd_free(cip->ci_proxy_asru, sizeof (uint8_t) * cip->ci_nsuspects); nvlist_free(cip->ci_diag_de); if (cip->ci_diag_asru) fmd_free(cip->ci_diag_asru, sizeof (uint8_t) * cip->ci_nsuspects); for (cis = cip->ci_suspects; cis != NULL; cis = ncis) { ncis = cis->cis_next; nvlist_free(cis->cis_nvl); fmd_free(cis, sizeof (fmd_case_susp_t)); } cip->ci_suspects = NULL; cip->ci_nsuspects = 0; } fmd_case_t * fmd_case_recreate(fmd_module_t *mp, fmd_xprt_t *xp, uint_t state, const char *uuid, const char *code) { fmd_case_impl_t *cip = fmd_zalloc(sizeof (fmd_case_impl_t), FMD_SLEEP); fmd_case_impl_t *eip; (void) pthread_mutex_init(&cip->ci_lock, NULL); fmd_buf_hash_create(&cip->ci_bufs); fmd_module_hold(mp); cip->ci_mod = mp; cip->ci_xprt = xp; cip->ci_refs = 1; cip->ci_state = state; cip->ci_uuid = fmd_strdup(uuid, FMD_SLEEP); cip->ci_uuidlen = strlen(cip->ci_uuid); cip->ci_code = fmd_strdup(code, FMD_SLEEP); cip->ci_codelen = cip->ci_code ? strlen(cip->ci_code) + 1 : 0; if (state > FMD_CASE_CLOSE_WAIT) cip->ci_flags |= FMD_CF_SOLVED; /* * Insert the case into the global case hash. If the specified UUID is * already present, check to see if it is an orphan: if so, reclaim it; * otherwise if it is owned by a different module then return NULL. */ if ((eip = fmd_case_hash_insert(fmd.d_cases, cip)) != cip) { (void) pthread_mutex_lock(&cip->ci_lock); cip->ci_refs--; /* decrement to zero */ fmd_case_destroy((fmd_case_t *)cip, B_FALSE); cip = eip; /* switch 'cip' to the existing case */ (void) pthread_mutex_lock(&cip->ci_lock); /* * If the ASRU cache is trying to recreate an orphan, then just * return the existing case that we found without changing it. */ if (mp == fmd.d_rmod) { /* * In case the case has already been created from * a checkpoint file we need to set up code now. */ if (cip->ci_state < FMD_CASE_CLOSED) { if (code != NULL && cip->ci_code == NULL) { cip->ci_code = fmd_strdup(code, FMD_SLEEP); cip->ci_codelen = cip->ci_code ? strlen(cip->ci_code) + 1 : 0; fmd_case_code_hash_insert(fmd.d_cases, cip); } } /* * When recreating an orphan case, state passed in may * be CLOSED (faulty) or REPAIRED/RESOLVED (!faulty). If * any suspects are still CLOSED (faulty) then the * overall state needs to be CLOSED. */ if ((cip->ci_state == FMD_CASE_REPAIRED || cip->ci_state == FMD_CASE_RESOLVED) && state == FMD_CASE_CLOSED) cip->ci_state = FMD_CASE_CLOSED; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_case_rele((fmd_case_t *)cip); return ((fmd_case_t *)cip); } /* * If the existing case isn't an orphan or is being proxied, * then we have a UUID conflict: return failure to the caller. */ if (cip->ci_mod != fmd.d_rmod || xp != NULL) { (void) pthread_mutex_unlock(&cip->ci_lock); fmd_case_rele((fmd_case_t *)cip); return (NULL); } /* * If the new module is reclaiming an orphaned case, remove * the case from the root module, switch ci_mod, and then fall * through to adding the case to the new owner module 'mp'. */ fmd_module_lock(cip->ci_mod); fmd_list_delete(&cip->ci_mod->mod_cases, cip); fmd_module_unlock(cip->ci_mod); fmd_module_rele(cip->ci_mod); cip->ci_mod = mp; fmd_module_hold(mp); /* * It's possible that fmd crashed or was restarted during a * previous solve operation between the asru cache being created * and the ckpt file being updated to SOLVED. Thus when the DE * recreates the case here from the checkpoint file, the state * will be UNSOLVED and yet we are having to reclaim because * the case was in the asru cache. If this happens, revert the * case back to the UNSOLVED state and let the DE solve it again */ if (state == FMD_CASE_UNSOLVED) { fmd_asru_hash_delete_case(fmd.d_asrus, (fmd_case_t *)cip); fmd_case_destroy_suspects(cip); fmd_case_code_hash_delete(fmd.d_cases, cip); fmd_free(cip->ci_code, cip->ci_codelen); cip->ci_code = NULL; cip->ci_codelen = 0; cip->ci_tv_valid = 0; } cip->ci_state = state; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_case_rele((fmd_case_t *)cip); } else { /* * add into hash of solved cases */ if (cip->ci_code) fmd_case_code_hash_insert(fmd.d_cases, cip); } ASSERT(fmd_module_locked(mp)); fmd_list_append(&mp->mod_cases, cip); (void) pthread_mutex_lock(&cip->ci_mod->mod_stats_lock); cip->ci_mod->mod_stats->ms_caseopen.fmds_value.ui64++; (void) pthread_mutex_unlock(&cip->ci_mod->mod_stats_lock); return ((fmd_case_t *)cip); } void fmd_case_destroy(fmd_case_t *cp, int visible) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_item_t *cit, *ncit; ASSERT(MUTEX_HELD(&cip->ci_lock)); ASSERT(cip->ci_refs == 0); if (visible) { TRACE((FMD_DBG_CASE, "deleting case %s", cip->ci_uuid)); fmd_case_hash_delete(fmd.d_cases, cip); } for (cit = cip->ci_items; cit != NULL; cit = ncit) { ncit = cit->cit_next; fmd_event_rele(cit->cit_event); fmd_free(cit, sizeof (fmd_case_item_t)); } fmd_case_destroy_suspects(cip); if (cip->ci_principal != NULL) fmd_event_rele(cip->ci_principal); fmd_free(cip->ci_uuid, cip->ci_uuidlen + 1); fmd_free(cip->ci_code, cip->ci_codelen); (void) fmd_buf_hash_destroy(&cip->ci_bufs); fmd_module_rele(cip->ci_mod); fmd_free(cip, sizeof (fmd_case_impl_t)); } void fmd_case_hold(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_lock); fmd_case_hold_locked(cp); (void) pthread_mutex_unlock(&cip->ci_lock); } void fmd_case_hold_locked(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; ASSERT(MUTEX_HELD(&cip->ci_lock)); if (cip->ci_flags & FMD_CF_DELETING) fmd_panic("attempt to hold a deleting case %p (%s)\n", (void *)cip, cip->ci_uuid); cip->ci_refs++; ASSERT(cip->ci_refs != 0); } static fmd_case_impl_t * fmd_case_tryhold(fmd_case_impl_t *cip) { /* * If the case's "deleting" bit is unset, hold and return case, * otherwise, return NULL. */ (void) pthread_mutex_lock(&cip->ci_lock); if (cip->ci_flags & FMD_CF_DELETING) { (void) pthread_mutex_unlock(&cip->ci_lock); cip = NULL; } else { fmd_case_hold_locked((fmd_case_t *)cip); (void) pthread_mutex_unlock(&cip->ci_lock); } return (cip); } void fmd_case_rele(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_lock); ASSERT(cip->ci_refs != 0); if (--cip->ci_refs == 0) fmd_case_destroy((fmd_case_t *)cip, B_TRUE); else (void) pthread_mutex_unlock(&cip->ci_lock); } void fmd_case_rele_locked(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; ASSERT(MUTEX_HELD(&cip->ci_lock)); --cip->ci_refs; ASSERT(cip->ci_refs != 0); } int fmd_case_insert_principal(void *cp, fmd_event_t *ep) { fmd_case_impl_t *cip = cp; fmd_case_item_t *cit; fmd_event_t *oep; uint_t state; int new; fmd_event_hold(ep); (void) pthread_mutex_lock(&cip->ci_lock); if (cip->ci_flags & FMD_CF_SOLVED) state = FMD_EVS_DIAGNOSED; else state = FMD_EVS_ACCEPTED; oep = cip->ci_principal; cip->ci_principal = ep; for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) { if (cit->cit_event == ep) break; } cip->ci_flags |= FMD_CF_DIRTY; new = cit == NULL && ep != oep; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_setcdirty(cip->ci_mod); fmd_event_transition(ep, state); if (oep != NULL) fmd_event_rele(oep); return (new); } int fmd_case_insert_event(void *cp, fmd_event_t *ep) { fmd_case_impl_t *cip = cp; fmd_case_item_t *cit; uint_t state; int new; boolean_t injected; (void) pthread_mutex_lock(&cip->ci_lock); if (cip->ci_flags & FMD_CF_SOLVED) state = FMD_EVS_DIAGNOSED; else state = FMD_EVS_ACCEPTED; for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) { if (cit->cit_event == ep) break; } new = cit == NULL && ep != cip->ci_principal; /* * If the event is already in the case or the case is already solved, * there is no reason to save it: just transition it appropriately. */ if (cit != NULL || (cip->ci_flags & FMD_CF_SOLVED)) { (void) pthread_mutex_unlock(&cip->ci_lock); fmd_event_transition(ep, state); return (new); } cit = fmd_alloc(sizeof (fmd_case_item_t), FMD_SLEEP); fmd_event_hold(ep); if (nvlist_lookup_boolean_value(((fmd_event_impl_t *)ep)->ev_nvl, "__injected", &injected) == 0 && injected) fmd_case_set_injected(cp); cit->cit_next = cip->ci_items; cit->cit_event = ep; cip->ci_items = cit; cip->ci_nitems++; cip->ci_flags |= FMD_CF_DIRTY; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_setcdirty(cip->ci_mod); fmd_event_transition(ep, state); return (new); } void fmd_case_insert_suspect(fmd_case_t *cp, nvlist_t *nvl) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_susp_t *cis = fmd_alloc(sizeof (fmd_case_susp_t), FMD_SLEEP); (void) pthread_mutex_lock(&cip->ci_lock); ASSERT(cip->ci_state < FMD_CASE_CLOSE_WAIT); cip->ci_flags |= FMD_CF_DIRTY; cis->cis_next = cip->ci_suspects; cis->cis_nvl = nvl; cip->ci_suspects = cis; cip->ci_nsuspects++; (void) pthread_mutex_unlock(&cip->ci_lock); if (cip->ci_xprt == NULL) fmd_module_setcdirty(cip->ci_mod); } void fmd_case_recreate_suspect(fmd_case_t *cp, nvlist_t *nvl) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_susp_t *cis = fmd_alloc(sizeof (fmd_case_susp_t), FMD_SLEEP); boolean_t b; (void) pthread_mutex_lock(&cip->ci_lock); cis->cis_next = cip->ci_suspects; cis->cis_nvl = nvl; if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_MESSAGE, &b) == 0 && b == B_FALSE) cip->ci_flags |= FMD_CF_INVISIBLE; cip->ci_suspects = cis; cip->ci_nsuspects++; (void) pthread_mutex_unlock(&cip->ci_lock); } void fmd_case_reset_suspects(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_lock); ASSERT(cip->ci_state < FMD_CASE_SOLVED); fmd_case_destroy_suspects(cip); cip->ci_flags |= FMD_CF_DIRTY; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_setcdirty(cip->ci_mod); } /*ARGSUSED*/ static void fmd_case_unusable(fmd_asru_link_t *alp, void *arg) { (void) fmd_asru_setflags(alp, FMD_ASRU_UNUSABLE); } /* * Grab ci_lock and update the case state and set the dirty bit. Then perform * whatever actions and emit whatever events are appropriate for the state. * Refer to the topmost block comment explaining the state machine for details. */ void fmd_case_transition(fmd_case_t *cp, uint_t state, uint_t flags) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_item_t *cit; fmd_event_t *e; int resolved = 0; int any_unusable_and_present = 0; ASSERT(state <= FMD_CASE_RESOLVED); (void) pthread_mutex_lock(&cip->ci_lock); if (!(cip->ci_flags & FMD_CF_SOLVED) && !(flags & FMD_CF_SOLVED)) flags &= ~(FMD_CF_ISOLATED | FMD_CF_REPAIRED | FMD_CF_RESOLVED); cip->ci_flags |= flags; if (cip->ci_state >= state) { (void) pthread_mutex_unlock(&cip->ci_lock); return; /* already in specified state */ } TRACE((FMD_DBG_CASE, "case %s %s->%s", cip->ci_uuid, _fmd_case_snames[cip->ci_state], _fmd_case_snames[state])); cip->ci_state = state; cip->ci_flags |= FMD_CF_DIRTY; if (cip->ci_xprt == NULL && cip->ci_mod != fmd.d_rmod) fmd_module_setcdirty(cip->ci_mod); switch (state) { case FMD_CASE_SOLVED: for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) fmd_event_transition(cit->cit_event, FMD_EVS_DIAGNOSED); if (cip->ci_principal != NULL) { fmd_event_transition(cip->ci_principal, FMD_EVS_DIAGNOSED); } break; case FMD_CASE_CLOSE_WAIT: /* * If the case was never solved, do not change ASRUs. * If the case was never fmd_case_closed, do not change ASRUs. * If the case was repaired, do not change ASRUs. */ if ((cip->ci_flags & (FMD_CF_SOLVED | FMD_CF_ISOLATED | FMD_CF_REPAIRED)) == (FMD_CF_SOLVED | FMD_CF_ISOLATED)) fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_unusable, NULL); /* * If an orphaned case transitions to CLOSE_WAIT, the owning * module is no longer loaded: continue on to CASE_CLOSED or * CASE_REPAIRED as appropriate. */ if (fmd_case_orphaned(cp)) { if (cip->ci_flags & FMD_CF_REPAIRED) { state = cip->ci_state = FMD_CASE_REPAIRED; TRACE((FMD_DBG_CASE, "case %s %s->%s", cip->ci_uuid, _fmd_case_snames[FMD_CASE_CLOSE_WAIT], _fmd_case_snames[FMD_CASE_REPAIRED])); goto do_repair; } else { state = cip->ci_state = FMD_CASE_CLOSED; TRACE((FMD_DBG_CASE, "case %s %s->%s", cip->ci_uuid, _fmd_case_snames[FMD_CASE_CLOSE_WAIT], _fmd_case_snames[FMD_CASE_CLOSED])); } } break; case FMD_CASE_REPAIRED: do_repair: ASSERT(cip->ci_xprt != NULL || fmd_case_orphaned(cp)); /* * If we've been requested to transition straight on to the * RESOLVED state (which can happen with fault proxying where a * list.resolved or a uuresolved is received from the other * side), or if all suspects are already either usable or not * present then transition straight to RESOLVED state, * publishing both the list.repaired and list.resolved. For a * proxy, if we discover here that all suspects are already * either usable or not present, notify the diag side instead * using fmd_xprt_uuresolved(). */ if (flags & FMD_CF_RESOLVED) { if (cip->ci_xprt != NULL) fmd_list_delete(&cip->ci_mod->mod_cases, cip); } else { fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_unusable_and_present, &any_unusable_and_present); if (any_unusable_and_present) break; if (cip->ci_xprt != NULL) { fmd_xprt_uuresolved(cip->ci_xprt, cip->ci_uuid); break; } } cip->ci_state = FMD_CASE_RESOLVED; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_case_publish(cp, state); TRACE((FMD_DBG_CASE, "case %s %s->%s", cip->ci_uuid, _fmd_case_snames[FMD_CASE_REPAIRED], _fmd_case_snames[FMD_CASE_RESOLVED])); state = FMD_CASE_RESOLVED; resolved = 1; (void) pthread_mutex_lock(&cip->ci_lock); break; case FMD_CASE_RESOLVED: /* * For a proxy, no need to check that all suspects are already * either usable or not present - this request has come from * the diagnosing side which makes the final decision on this. */ if (cip->ci_xprt != NULL) { fmd_list_delete(&cip->ci_mod->mod_cases, cip); resolved = 1; break; } ASSERT(fmd_case_orphaned(cp)); /* * If all suspects are already either usable or not present then * carry on, publish list.resolved and discard the case. */ fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_unusable_and_present, &any_unusable_and_present); if (any_unusable_and_present) { (void) pthread_mutex_unlock(&cip->ci_lock); return; } resolved = 1; break; } (void) pthread_mutex_unlock(&cip->ci_lock); /* * If the module has initialized, then publish the appropriate event * for the new case state. If not, we are being called from the * checkpoint code during module load, in which case the module's * _fmd_init() routine hasn't finished yet, and our event dictionaries * may not be open yet, which will prevent us from computing the event * code. Defer the call to fmd_case_publish() by enqueuing a PUBLISH * event in our queue: this won't be processed until _fmd_init is done. */ if (cip->ci_mod->mod_flags & FMD_MOD_INIT) fmd_case_publish(cp, state); else { fmd_case_hold(cp); e = fmd_event_create(FMD_EVT_PUBLISH, FMD_HRT_NOW, NULL, cp); fmd_eventq_insert_at_head(cip->ci_mod->mod_queue, e); } if (resolved) { if (cip->ci_xprt != NULL) { /* * If we transitioned to RESOLVED, adjust the reference * count to reflect our removal from * fmd.d_rmod->mod_cases above. If the caller has not * placed an additional hold on the case, it will now * be freed. */ (void) pthread_mutex_lock(&cip->ci_lock); fmd_asru_hash_delete_case(fmd.d_asrus, cp); (void) pthread_mutex_unlock(&cip->ci_lock); fmd_case_rele(cp); } else { fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_log_resolved, NULL); (void) pthread_mutex_lock(&cip->ci_lock); /* mark as "ready to be discarded */ cip->ci_flags |= FMD_CF_RES_CMPL; (void) pthread_mutex_unlock(&cip->ci_lock); } } } /* * Discard any case if it is in RESOLVED state (and if check_if_aged argument * is set if all suspects have passed the rsrc.aged time). */ void fmd_case_discard_resolved(fmd_case_t *cp, void *arg) { int check_if_aged = *(int *)arg; fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; /* * First check if case has completed transition to resolved. */ (void) pthread_mutex_lock(&cip->ci_lock); if (!(cip->ci_flags & FMD_CF_RES_CMPL)) { (void) pthread_mutex_unlock(&cip->ci_lock); return; } /* * Now if check_is_aged is set, see if all suspects have aged. */ if (check_if_aged) { int aged = 1; fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_check_if_aged, &aged); if (!aged) { (void) pthread_mutex_unlock(&cip->ci_lock); return; } } /* * Finally discard the case, clearing FMD_CF_RES_CMPL so we don't * do it twice. */ fmd_module_lock(cip->ci_mod); fmd_list_delete(&cip->ci_mod->mod_cases, cip); fmd_module_unlock(cip->ci_mod); fmd_asru_hash_delete_case(fmd.d_asrus, cp); cip->ci_flags &= ~FMD_CF_RES_CMPL; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_case_rele(cp); } /* * Transition the specified case to *at least* the specified state by first * re-validating the suspect list using the resource cache. This function is * employed by the checkpoint code when restoring a saved, solved case to see * if the state of the case has effectively changed while fmd was not running * or the module was not loaded. */ void fmd_case_transition_update(fmd_case_t *cp, uint_t state, uint_t flags) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; int usable = 0; /* are any suspects usable? */ ASSERT(state >= FMD_CASE_SOLVED); (void) pthread_mutex_lock(&cip->ci_lock); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_usable, &usable); (void) pthread_mutex_unlock(&cip->ci_lock); if (!usable) { state = MAX(state, FMD_CASE_CLOSE_WAIT); flags |= FMD_CF_ISOLATED; } fmd_case_transition(cp, state, flags); } void fmd_case_setdirty(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_lock); cip->ci_flags |= FMD_CF_DIRTY; (void) pthread_mutex_unlock(&cip->ci_lock); fmd_module_setcdirty(cip->ci_mod); } void fmd_case_clrdirty(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_lock); cip->ci_flags &= ~FMD_CF_DIRTY; (void) pthread_mutex_unlock(&cip->ci_lock); } void fmd_case_commit(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_item_t *cit; (void) pthread_mutex_lock(&cip->ci_lock); if (cip->ci_flags & FMD_CF_DIRTY) { for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) fmd_event_commit(cit->cit_event); if (cip->ci_principal != NULL) fmd_event_commit(cip->ci_principal); fmd_buf_hash_commit(&cip->ci_bufs); cip->ci_flags &= ~FMD_CF_DIRTY; } (void) pthread_mutex_unlock(&cip->ci_lock); } /* * On proxy side, send back repair/acquit/etc request to diagnosing side */ void fmd_case_xprt_updated(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; nvlist_t **nva; uint8_t *ba; int msg = B_TRUE; int count = 0; fmd_case_lst_t fcl; ASSERT(cip->ci_xprt != NULL); (void) pthread_mutex_lock(&cip->ci_lock); ba = alloca(sizeof (uint8_t) * cip->ci_nsuspects); nva = alloca(sizeof (nvlist_t *) * cip->ci_nsuspects); fcl.fcl_countp = &count; fcl.fcl_maxcount = cip->ci_nsuspects; fcl.fcl_msgp = &msg; fcl.fcl_ba = ba; fcl.fcl_nva = nva; fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_set_lst, &fcl); (void) pthread_mutex_unlock(&cip->ci_lock); fmd_xprt_updated(cip->ci_xprt, cip->ci_uuid, ba, cip->ci_proxy_asru, count); } /* * fmd_case_update_status() can be called on either the proxy side when a * list.suspect is received, or on the diagnosing side when an update request * is received from the proxy. It updates the status in the resource cache. */ void fmd_case_update_status(fmd_case_t *cp, uint8_t *statusp, uint8_t *proxy_asrup, uint8_t *diag_asrup) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; int count = 0; fmd_asru_update_status_t faus; /* * update status of resource cache entries */ faus.faus_countp = &count; faus.faus_maxcount = cip->ci_nsuspects; faus.faus_ba = statusp; faus.faus_proxy_asru = proxy_asrup; faus.faus_diag_asru = diag_asrup; faus.faus_is_proxy = (cip->ci_xprt != NULL); (void) pthread_mutex_lock(&cip->ci_lock); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_update_status, &faus); (void) pthread_mutex_unlock(&cip->ci_lock); } /* * Called on either the proxy side or the diag side when a repair has taken * place on the other side but this side may know the asru "contains" * relationships. */ void fmd_case_update_containees(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_lock); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_update_containees, NULL); (void) pthread_mutex_unlock(&cip->ci_lock); } /* * fmd_case_close_status() is called on diagnosing side when proxy side * has had a uuclose. It updates the status in the resource cache. */ void fmd_case_close_status(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; int count = 0; fmd_asru_close_status_t facs; /* * update status of resource cache entries */ facs.facs_countp = &count; facs.facs_maxcount = cip->ci_nsuspects; (void) pthread_mutex_lock(&cip->ci_lock); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_close_status, &facs); (void) pthread_mutex_unlock(&cip->ci_lock); } /* * Indicate that the case may need to change state because one or more of the * ASRUs named as a suspect has changed state. We examine all the suspects * and if none are still faulty, we initiate a case close transition. */ void fmd_case_update(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; uint_t cstate; int faulty = 0; (void) pthread_mutex_lock(&cip->ci_lock); cstate = cip->ci_state; if (cip->ci_state < FMD_CASE_SOLVED) { (void) pthread_mutex_unlock(&cip->ci_lock); return; /* update is not appropriate */ } if (cip->ci_flags & FMD_CF_REPAIRED) { (void) pthread_mutex_unlock(&cip->ci_lock); return; /* already repaired */ } TRACE((FMD_DBG_CASE, "case update %s", cip->ci_uuid)); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_faulty, &faulty); (void) pthread_mutex_unlock(&cip->ci_lock); if (faulty) { nvlist_t *nvl; fmd_event_t *e; char *class; TRACE((FMD_DBG_CASE, "sending list.updated %s", cip->ci_uuid)); nvl = fmd_case_mkevent(cp, FM_LIST_UPDATED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); (void) pthread_rwlock_rdlock(&fmd.d_log_lock); fmd_log_append(fmd.d_fltlog, e, cp); (void) pthread_rwlock_unlock(&fmd.d_log_lock); fmd_dispq_dispatch(fmd.d_disp, e, class); return; /* one or more suspects are still marked faulty */ } if (cstate == FMD_CASE_CLOSED) fmd_case_transition(cp, FMD_CASE_REPAIRED, FMD_CF_REPAIRED); else fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_REPAIRED); } /* * Delete a closed case from the module's case list once the fmdo_close() entry * point has run to completion. If the case is owned by a transport module, * tell the transport to proxy a case close on the other end of the transport. * Transition to the appropriate next state based on ci_flags. This * function represents the end of CLOSE_WAIT and transitions the case to either * CLOSED or REPAIRED or discards it entirely because it was never solved; * refer to the topmost block comment explaining the state machine for details. */ void fmd_case_delete(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_modstat_t *msp; size_t buftotal; TRACE((FMD_DBG_CASE, "case delete %s", cip->ci_uuid)); ASSERT(fmd_module_locked(cip->ci_mod)); fmd_list_delete(&cip->ci_mod->mod_cases, cip); buftotal = fmd_buf_hash_destroy(&cip->ci_bufs); (void) pthread_mutex_lock(&cip->ci_mod->mod_stats_lock); msp = cip->ci_mod->mod_stats; ASSERT(msp->ms_caseopen.fmds_value.ui64 != 0); msp->ms_caseopen.fmds_value.ui64--; ASSERT(msp->ms_buftotal.fmds_value.ui64 >= buftotal); msp->ms_buftotal.fmds_value.ui64 -= buftotal; (void) pthread_mutex_unlock(&cip->ci_mod->mod_stats_lock); if (cip->ci_xprt == NULL) fmd_module_setcdirty(cip->ci_mod); fmd_module_rele(cip->ci_mod); cip->ci_mod = fmd.d_rmod; fmd_module_hold(cip->ci_mod); /* * If the case has been solved, then retain it * on the root module's case list at least until we're transitioned. * Otherwise free the case with our final fmd_case_rele() below. */ if (cip->ci_flags & FMD_CF_SOLVED) { fmd_module_lock(cip->ci_mod); fmd_list_append(&cip->ci_mod->mod_cases, cip); fmd_module_unlock(cip->ci_mod); fmd_case_hold(cp); } /* * Transition onwards to REPAIRED or CLOSED as originally requested. * Note that for proxy case if we're transitioning to CLOSED it means * the case was isolated locally, so call fmd_xprt_uuclose() to notify * the diagnosing side. No need to notify the diagnosing side if we are * transitioning to REPAIRED as we only do this when requested to do * so by the diagnosing side anyway. */ if (cip->ci_flags & FMD_CF_REPAIRED) fmd_case_transition(cp, FMD_CASE_REPAIRED, 0); else if (cip->ci_flags & FMD_CF_ISOLATED) { fmd_case_transition(cp, FMD_CASE_CLOSED, 0); if (cip->ci_xprt != NULL) fmd_xprt_uuclose(cip->ci_xprt, cip->ci_uuid); } fmd_case_rele(cp); } void fmd_case_discard(fmd_case_t *cp, boolean_t delete_from_asru_cache) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; (void) pthread_mutex_lock(&cip->ci_mod->mod_stats_lock); cip->ci_mod->mod_stats->ms_caseopen.fmds_value.ui64--; (void) pthread_mutex_unlock(&cip->ci_mod->mod_stats_lock); ASSERT(fmd_module_locked(cip->ci_mod)); fmd_list_delete(&cip->ci_mod->mod_cases, cip); if (delete_from_asru_cache) { (void) pthread_mutex_lock(&cip->ci_lock); fmd_asru_hash_delete_case(fmd.d_asrus, cp); (void) pthread_mutex_unlock(&cip->ci_lock); } fmd_case_rele(cp); } /* * Indicate that the problem corresponding to a case has been repaired by * clearing the faulty bit on each ASRU named as a suspect. If the case hasn't * already been closed, this function initiates the transition to CLOSE_WAIT. * The caller must have the case held from fmd_case_hash_lookup(), so we can * grab and drop ci_lock without the case being able to be freed in between. */ int fmd_case_repair(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; uint_t cstate; fmd_asru_rep_arg_t fara; (void) pthread_mutex_lock(&cip->ci_lock); cstate = cip->ci_state; if (cstate < FMD_CASE_SOLVED) { (void) pthread_mutex_unlock(&cip->ci_lock); return (fmd_set_errno(EFMD_CASE_STATE)); } if (cip->ci_flags & FMD_CF_REPAIRED) { (void) pthread_mutex_unlock(&cip->ci_lock); return (0); /* already repaired */ } TRACE((FMD_DBG_CASE, "case repair %s", cip->ci_uuid)); fara.fara_reason = FMD_ASRU_REPAIRED; fara.fara_bywhat = FARA_BY_CASE; fara.fara_rval = NULL; fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_repaired, &fara); (void) pthread_mutex_unlock(&cip->ci_lock); /* * if this is a proxied case, send the repair across the transport. * The remote side will then do the repair and send a list.repaired back * again such that we can finally repair the case on this side. */ if (cip->ci_xprt != NULL) { fmd_case_xprt_updated(cp); return (0); } if (cstate == FMD_CASE_CLOSED) fmd_case_transition(cp, FMD_CASE_REPAIRED, FMD_CF_REPAIRED); else fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_REPAIRED); return (0); } int fmd_case_acquit(fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; uint_t cstate; fmd_asru_rep_arg_t fara; (void) pthread_mutex_lock(&cip->ci_lock); cstate = cip->ci_state; if (cstate < FMD_CASE_SOLVED) { (void) pthread_mutex_unlock(&cip->ci_lock); return (fmd_set_errno(EFMD_CASE_STATE)); } if (cip->ci_flags & FMD_CF_REPAIRED) { (void) pthread_mutex_unlock(&cip->ci_lock); return (0); /* already repaired */ } TRACE((FMD_DBG_CASE, "case acquit %s", cip->ci_uuid)); fara.fara_reason = FMD_ASRU_ACQUITTED; fara.fara_bywhat = FARA_BY_CASE; fara.fara_rval = NULL; fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_repaired, &fara); (void) pthread_mutex_unlock(&cip->ci_lock); /* * if this is a proxied case, send the repair across the transport. * The remote side will then do the repair and send a list.repaired back * again such that we can finally repair the case on this side. */ if (cip->ci_xprt != NULL) { fmd_case_xprt_updated(cp); return (0); } if (cstate == FMD_CASE_CLOSED) fmd_case_transition(cp, FMD_CASE_REPAIRED, FMD_CF_REPAIRED); else fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_REPAIRED); return (0); } int fmd_case_contains(fmd_case_t *cp, fmd_event_t *ep) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_item_t *cit; uint_t state; int rv = 0; (void) pthread_mutex_lock(&cip->ci_lock); if (cip->ci_state >= FMD_CASE_SOLVED) state = FMD_EVS_DIAGNOSED; else state = FMD_EVS_ACCEPTED; for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) { if ((rv = fmd_event_equal(ep, cit->cit_event)) != 0) break; } if (rv == 0 && cip->ci_principal != NULL) rv = fmd_event_equal(ep, cip->ci_principal); (void) pthread_mutex_unlock(&cip->ci_lock); if (rv != 0) fmd_event_transition(ep, state); return (rv); } int fmd_case_orphaned(fmd_case_t *cp) { return (((fmd_case_impl_t *)cp)->ci_mod == fmd.d_rmod); } void fmd_case_settime(fmd_case_t *cp, time_t tv_sec, suseconds_t tv_usec) { ((fmd_case_impl_t *)cp)->ci_tv.tv_sec = tv_sec; ((fmd_case_impl_t *)cp)->ci_tv.tv_usec = tv_usec; ((fmd_case_impl_t *)cp)->ci_tv_valid = 1; } void fmd_case_set_injected(fmd_case_t *cp) { ((fmd_case_impl_t *)cp)->ci_injected = 1; } void fmd_case_set_de_fmri(fmd_case_t *cp, nvlist_t *nvl) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; nvlist_free(cip->ci_diag_de); cip->ci_diag_de = nvl; } void fmd_case_setcode(fmd_case_t *cp, char *code) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; cip->ci_code = fmd_strdup(code, FMD_SLEEP); cip->ci_codelen = cip->ci_code ? strlen(cip->ci_code) + 1 : 0; } /*ARGSUSED*/ static void fmd_case_repair_replay_case(fmd_case_t *cp, void *arg) { int not_faulty = 0; int faulty = 0; nvlist_t *nvl; fmd_event_t *e; char *class; int any_unusable_and_present = 0; fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; if (cip->ci_state < FMD_CASE_SOLVED || cip->ci_xprt != NULL) return; if (cip->ci_state == FMD_CASE_RESOLVED) { cip->ci_flags |= FMD_CF_RES_CMPL; return; } fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_faulty, &faulty); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_not_faulty, ¬_faulty); if (cip->ci_state >= FMD_CASE_REPAIRED && !faulty) { /* * If none of the suspects is faulty, replay the list.repaired. * If all suspects are already either usable or not present then * also transition straight to RESOLVED state. */ fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_case_unusable_and_present, &any_unusable_and_present); if (!any_unusable_and_present) { cip->ci_state = FMD_CASE_RESOLVED; TRACE((FMD_DBG_CASE, "replay sending list.repaired %s", cip->ci_uuid)); nvl = fmd_case_mkevent(cp, FM_LIST_REPAIRED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dispq_dispatch(fmd.d_disp, e, class); TRACE((FMD_DBG_CASE, "replay sending list.resolved %s", cip->ci_uuid)); fmd_case_publish(cp, FMD_CASE_RESOLVED); fmd_asru_hash_apply_by_case(fmd.d_asrus, cp, fmd_asru_log_resolved, NULL); cip->ci_flags |= FMD_CF_RES_CMPL; } else { TRACE((FMD_DBG_CASE, "replay sending list.repaired %s", cip->ci_uuid)); nvl = fmd_case_mkevent(cp, FM_LIST_REPAIRED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dispq_dispatch(fmd.d_disp, e, class); } } else if (faulty && not_faulty) { /* * if some but not all of the suspects are not faulty, replay * the list.updated. */ TRACE((FMD_DBG_CASE, "replay sending list.updated %s", cip->ci_uuid)); nvl = fmd_case_mkevent(cp, FM_LIST_UPDATED_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dispq_dispatch(fmd.d_disp, e, class); } } void fmd_case_repair_replay() { fmd_case_hash_apply(fmd.d_cases, fmd_case_repair_replay_case, NULL); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_CASE_H #define _FMD_CASE_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include struct fmd_module; /* see */ typedef struct fmd_case_item { struct fmd_case_item *cit_next; /* pointer to next element in list */ fmd_event_t *cit_event; /* pointer to held event */ } fmd_case_item_t; typedef struct fmd_case_susp { struct fmd_case_susp *cis_next; /* pointer to next element in list */ nvlist_t *cis_nvl; /* nvpair representing fault event */ } fmd_case_susp_t; typedef struct fmd_case_impl { fmd_list_t ci_list; /* linked list next/prev pointers */ struct fmd_case_impl *ci_next; /* next pointer for hash bucket chain */ struct fmd_case_impl *ci_code_next; /* ci_code hash bucket chain */ char *ci_uuid; /* uuid string for this case */ uint_t ci_uuidlen; /* length of ci_uuid (not incl. \0) */ char *ci_code; /* code associated with this case */ size_t ci_codelen; /* size of ci_code buffer in bytes */ struct fmd_module *ci_mod; /* module that owns this case */ fmd_xprt_t *ci_xprt; /* transport for this case (or NULL) */ uint8_t ci_precanned; /* precanned code from injection */ nvlist_t *ci_diag_de; /* diag side de fmri */ uint8_t *ci_diag_asru; /* is asru valid on diag side */ uint8_t *ci_proxy_asru; /* is asru valid on proxy side */ void *ci_data; /* data from fmd_case_setspecific() */ pthread_mutex_t ci_lock; /* lock for remainder of contents */ uint_t ci_refs; /* reference count */ ushort_t ci_state; /* case state (see below) */ ushort_t ci_flags; /* case flags (see below) */ fmd_case_item_t *ci_items; /* list of items in this case */ uint_t ci_nitems; /* number of ci_items */ fmd_event_t *ci_principal; /* principal event (if any) */ fmd_case_susp_t *ci_suspects; /* list of suspects in this case */ uint_t ci_nsuspects; /* number of ci_suspects */ size_t ci_nvsz; /* packed suspect nvlist array size */ fmd_buf_hash_t ci_bufs; /* hash of bufs associated with case */ struct timeval ci_tv; /* time of original diagnosis */ int ci_tv_valid; /* time of original diagnosis valid */ int ci_injected; /* was the fault injected */ } fmd_case_impl_t; #define FMD_CASE_CURRENT -1u /* flag for current state */ #define FMD_CASE_UNSOLVED 0 /* case is not yet solved (waiting) */ #define FMD_CASE_SOLVED 1 /* case is solved (suspects added) */ #define FMD_CASE_CLOSE_WAIT 2 /* case is executing fmdo_close() */ #define FMD_CASE_CLOSED 3 /* case is closed (reconfig done) */ #define FMD_CASE_REPAIRED 4 /* case is repaired */ #define FMD_CASE_RESOLVED 5 /* case is resolved (can be freed) */ #define FMD_CF_DIRTY 0x01 /* case is in need of checkpoint */ #define FMD_CF_SOLVED 0x02 /* case has been solved */ #define FMD_CF_ISOLATED 0x04 /* case has been isolated */ #define FMD_CF_REPAIRED 0x08 /* case has been repaired */ #define FMD_CF_RESOLVED 0x10 /* case has been resolved */ #define FMD_CF_INVISIBLE 0x20 /* case should be invisible */ #define FMD_CF_DELETING 0x40 /* case is about to be deleted */ #define FMD_CF_RES_CMPL 0x80 /* transition to resolved is complete */ /* * ci_proxy_asru flags record if we created a new asru on the proxy side and * if so whether it is derived from the received asru or received resource. */ #define FMD_PROXY_ASRU_NOT_NEEDED 0 #define FMD_PROXY_ASRU_FROM_ASRU 1 #define FMD_PROXY_ASRU_FROM_RSRC 2 typedef struct fmd_case_hash { pthread_rwlock_t ch_lock; /* lock protecting case hash */ fmd_case_impl_t **ch_hash; /* hash bucket array for cases */ fmd_case_impl_t **ch_code_hash; /* ci_code hash bucket array */ uint_t ch_hashlen; /* size of hash bucket array */ uint_t ch_count; /* number of cases in hash */ } fmd_case_hash_t; extern fmd_case_hash_t *fmd_case_hash_create(void); extern void fmd_case_hash_destroy(fmd_case_hash_t *); extern fmd_case_t *fmd_case_hash_lookup(fmd_case_hash_t *, const char *); extern void fmd_case_hash_apply(fmd_case_hash_t *, void (*)(fmd_case_t *, void *), void *); extern fmd_case_t *fmd_case_create(struct fmd_module *, const char *, void *); extern fmd_case_t *fmd_case_recreate(struct fmd_module *, struct fmd_xprt *, uint_t, const char *, const char *); extern void fmd_case_destroy(fmd_case_t *, int); extern void fmd_case_hold(fmd_case_t *); extern void fmd_case_hold_locked(fmd_case_t *); extern void fmd_case_rele(fmd_case_t *); extern void fmd_case_rele_locked(fmd_case_t *); extern void fmd_case_update(fmd_case_t *); extern int fmd_case_insert_principal(void *, fmd_event_t *); extern int fmd_case_insert_event(void *, fmd_event_t *); extern void fmd_case_insert_suspect(fmd_case_t *, nvlist_t *); extern void fmd_case_recreate_suspect(fmd_case_t *, nvlist_t *); extern void fmd_case_reset_suspects(fmd_case_t *); extern nvlist_t *fmd_case_mkevent(fmd_case_t *, const char *); extern void fmd_case_publish(fmd_case_t *, uint_t); extern void fmd_case_transition(fmd_case_t *, uint_t, uint_t); extern void fmd_case_transition_update(fmd_case_t *, uint_t, uint_t); extern void fmd_case_setdirty(fmd_case_t *); extern void fmd_case_clrdirty(fmd_case_t *); extern void fmd_case_commit(fmd_case_t *); extern void fmd_case_update(fmd_case_t *); extern void fmd_case_delete(fmd_case_t *); extern void fmd_case_discard(fmd_case_t *, boolean_t); extern void fmd_case_settime(fmd_case_t *, time_t, suseconds_t); extern void fmd_case_setcode(fmd_case_t *, char *); extern void fmd_case_set_de_fmri(fmd_case_t *, nvlist_t *); extern void fmd_case_set_injected(fmd_case_t *); extern void fmd_case_update_status(fmd_case_t *, uint8_t *, uint8_t *, uint8_t *); extern void fmd_case_update_containees(fmd_case_t *); extern void fmd_case_xprt_updated(fmd_case_t *); extern void fmd_case_close_status(fmd_case_t *); extern int fmd_case_repair(fmd_case_t *); extern int fmd_case_acquit(fmd_case_t *); extern int fmd_case_contains(fmd_case_t *, fmd_event_t *); extern int fmd_case_orphaned(fmd_case_t *); extern void fmd_case_repair_replay(void); extern void fmd_case_discard_resolved(fmd_case_t *, void *); #ifdef __cplusplus } #endif #endif /* _FMD_CASE_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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define P2ROUNDUP(x, align) (-(-(x) & -(align))) #define IS_P2ALIGNED(v, a) ((((uintptr_t)(v)) & ((uintptr_t)(a) - 1)) == 0) /* * The fmd_ckpt_t structure is used to manage all of the state needed by the * various subroutines that save and restore checkpoints. The structure is * initialized using fmd_ckpt_create() or fmd_ckpt_open() and is destroyed * by fmd_ckpt_destroy(). Refer to the subroutines below for more details. */ typedef struct fmd_ckpt { char ckp_src[PATH_MAX]; /* ckpt input or output filename */ char ckp_dst[PATH_MAX]; /* ckpt rename filename */ uchar_t *ckp_buf; /* data buffer base address */ fcf_hdr_t *ckp_hdr; /* file header pointer */ uchar_t *ckp_ptr; /* data buffer pointer */ size_t ckp_size; /* data buffer size */ fcf_sec_t *ckp_secp; /* section header table pointer */ fcf_sec_t *ckp_modp; /* section header for module */ uint_t ckp_secs; /* number of sections */ char *ckp_strs; /* string table base pointer */ char *ckp_strp; /* string table pointer */ size_t ckp_strn; /* string table size */ int ckp_fd; /* output descriptor */ fmd_module_t *ckp_mp; /* checkpoint module */ void *ckp_arg; /* private arg for callbacks */ } fmd_ckpt_t; typedef struct fmd_ckpt_desc { uint64_t secd_size; /* minimum section size */ uint32_t secd_entsize; /* minimum section entry size */ uint32_t secd_align; /* section alignment */ } fmd_ckpt_desc_t; /* * Table of FCF section descriptions. Here we record the minimum size for each * section (for use during restore) and the expected entry size and alignment * for each section (for use during both checkpoint and restore). */ static const fmd_ckpt_desc_t _fmd_ckpt_sections[] = { { 0, 0, sizeof (uint8_t) }, /* NONE */ { 1, 0, sizeof (char) }, /* STRTAB */ { sizeof (fcf_module_t), 0, sizeof (uint32_t) }, /* MODULE */ { sizeof (fcf_case_t), 0, sizeof (uint32_t) }, /* CASE */ { sizeof (fcf_buf_t), sizeof (fcf_buf_t), sizeof (uint32_t) }, /* BUFS */ { 0, 0, _MAX_ALIGNMENT }, /* BUFFER */ { sizeof (fcf_serd_t), sizeof (fcf_serd_t), sizeof (uint64_t) }, /* SERD */ { sizeof (fcf_event_t), sizeof (fcf_event_t), sizeof (uint64_t) }, /* EVENTS */ { sizeof (fcf_nvl_t), sizeof (fcf_nvl_t), sizeof (uint64_t) }, /* NVLISTS */ }; static int fmd_ckpt_create(fmd_ckpt_t *ckp, fmd_module_t *mp) { const char *dir = mp->mod_ckpt; const char *name = mp->mod_name; mode_t mode; bzero(ckp, sizeof (fmd_ckpt_t)); ckp->ckp_mp = mp; ckp->ckp_size = sizeof (fcf_hdr_t); ckp->ckp_strn = 1; /* for \0 */ (void) snprintf(ckp->ckp_src, PATH_MAX, "%s/%s+", dir, name); (void) snprintf(ckp->ckp_dst, PATH_MAX, "%s/%s", dir, name); (void) unlink(ckp->ckp_src); (void) fmd_conf_getprop(fmd.d_conf, "ckpt.mode", &mode); ckp->ckp_fd = open64(ckp->ckp_src, O_WRONLY | O_CREAT | O_EXCL, mode); return (ckp->ckp_fd); } /*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)); } 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); } 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); } /* * fmd_ckpt_error() is used as a wrapper around fmd_error() for ckpt routines. * It calls fmd_module_unlock() on behalf of its caller, logs the error, and * then aborts the API call and the surrounding module entry point by doing an * fmd_module_abort(), which longjmps to the place where we entered the module. * Depending on the type of error and conf settings, we will reset or fail. */ /*PRINTFLIKE3*/ static void fmd_ckpt_error(fmd_ckpt_t *ckp, int err, const char *format, ...) { fmd_module_t *mp = ckp->ckp_mp; va_list ap; va_start(ap, format); fmd_verror(err, format, ap); va_end(ap); if (fmd_module_locked(mp)) fmd_module_unlock(mp); fmd_ckpt_destroy(ckp); fmd_module_abort(mp, err); } static fcf_secidx_t fmd_ckpt_section(fmd_ckpt_t *ckp, const void *data, uint_t type, uint64_t size) { const fmd_ckpt_desc_t *dp; ASSERT(type < sizeof (_fmd_ckpt_sections) / sizeof (fmd_ckpt_desc_t)); dp = &_fmd_ckpt_sections[type]; ckp->ckp_ptr = (uchar_t *) P2ROUNDUP((uintptr_t)ckp->ckp_ptr, dp->secd_align); ckp->ckp_secp->fcfs_type = type; ckp->ckp_secp->fcfs_align = dp->secd_align; ckp->ckp_secp->fcfs_flags = 0; ckp->ckp_secp->fcfs_entsize = dp->secd_entsize; ckp->ckp_secp->fcfs_offset = (size_t)(ckp->ckp_ptr - ckp->ckp_buf); ckp->ckp_secp->fcfs_size = size; /* * If the data pointer is non-NULL, copy the data to our buffer; else * the caller is responsible for doing so and updating ckp->ckp_ptr. */ if (data != NULL) { bcopy(data, ckp->ckp_ptr, size); ckp->ckp_ptr += size; } ckp->ckp_secp++; return (ckp->ckp_secs++); } static fcf_stridx_t fmd_ckpt_string(fmd_ckpt_t *ckp, const char *s) { fcf_stridx_t idx = (fcf_stridx_t)(ckp->ckp_strp - ckp->ckp_strs); (void) strcpy(ckp->ckp_strp, s); ckp->ckp_strp += strlen(s) + 1; return (idx); } static int fmd_ckpt_alloc(fmd_ckpt_t *ckp, uint64_t gen) { /* * We've added up all the sections by now: add two more for SECT_NONE * and SECT_STRTAB, and add the size of the section header table and * string table to the total size. We know that the fcf_hdr_t is * aligned so that that fcf_sec_t's can follow it, and that fcf_sec_t * is aligned so that any section can follow it, so no extra padding * bytes need to be allocated between any of these items. */ ckp->ckp_secs += 2; /* for FCF_SECT_NONE and FCF_SECT_STRTAB */ ckp->ckp_size += sizeof (fcf_sec_t) * ckp->ckp_secs; ckp->ckp_size += ckp->ckp_strn; TRACE((FMD_DBG_CKPT, "alloc fcf buf size %u", ckp->ckp_size)); ckp->ckp_buf = fmd_zalloc(ckp->ckp_size, FMD_NOSLEEP); if (ckp->ckp_buf == NULL) return (-1); /* errno is set for us */ ckp->ckp_hdr = (void *)ckp->ckp_buf; ckp->ckp_hdr->fcfh_ident[FCF_ID_MAG0] = FCF_MAG_MAG0; ckp->ckp_hdr->fcfh_ident[FCF_ID_MAG1] = FCF_MAG_MAG1; ckp->ckp_hdr->fcfh_ident[FCF_ID_MAG2] = FCF_MAG_MAG2; ckp->ckp_hdr->fcfh_ident[FCF_ID_MAG3] = FCF_MAG_MAG3; ckp->ckp_hdr->fcfh_ident[FCF_ID_MODEL] = FCF_MODEL_NATIVE; ckp->ckp_hdr->fcfh_ident[FCF_ID_ENCODING] = FCF_ENCODE_NATIVE; ckp->ckp_hdr->fcfh_ident[FCF_ID_VERSION] = FCF_VERSION; ckp->ckp_hdr->fcfh_hdrsize = sizeof (fcf_hdr_t); ckp->ckp_hdr->fcfh_secsize = sizeof (fcf_sec_t); ckp->ckp_hdr->fcfh_secnum = ckp->ckp_secs; ckp->ckp_hdr->fcfh_secoff = sizeof (fcf_hdr_t); ckp->ckp_hdr->fcfh_filesz = ckp->ckp_size; ckp->ckp_hdr->fcfh_cgen = gen; ckp->ckp_secs = 0; /* reset section counter for second pass */ ckp->ckp_secp = (void *)(ckp->ckp_buf + sizeof (fcf_hdr_t)); ckp->ckp_strs = (char *)ckp->ckp_buf + ckp->ckp_size - ckp->ckp_strn; ckp->ckp_strp = ckp->ckp_strs + 1; /* use first byte as \0 */ ckp->ckp_ptr = (uchar_t *)(ckp->ckp_secp + ckp->ckp_hdr->fcfh_secnum); (void) fmd_ckpt_section(ckp, NULL, FCF_SECT_NONE, 0); return (0); } static int fmd_ckpt_commit(fmd_ckpt_t *ckp) { fcf_sec_t *secbase = (void *)(ckp->ckp_buf + sizeof (fcf_hdr_t)); size_t stroff = ckp->ckp_size - ckp->ckp_strn; /* * Before committing the checkpoint, we assert that fmd_ckpt_t's sizes * and current pointer locations all add up appropriately. Any ASSERTs * which trip here likely indicate an inconsistency in the code for the * reservation pass and the buffer update pass of the FCF subroutines. */ ASSERT((size_t)(ckp->ckp_ptr - ckp->ckp_buf) == stroff); (void) fmd_ckpt_section(ckp, NULL, FCF_SECT_STRTAB, ckp->ckp_strn); ckp->ckp_ptr += ckp->ckp_strn; /* string table is already filled in */ ASSERT(ckp->ckp_secs == ckp->ckp_hdr->fcfh_secnum); ASSERT(ckp->ckp_secp == secbase + ckp->ckp_hdr->fcfh_secnum); ASSERT(ckp->ckp_ptr == ckp->ckp_buf + ckp->ckp_hdr->fcfh_filesz); if (write(ckp->ckp_fd, ckp->ckp_buf, ckp->ckp_size) != ckp->ckp_size || fsync(ckp->ckp_fd) != 0 || close(ckp->ckp_fd) != 0) return (-1); /* errno is set for us */ ckp->ckp_fd = -1; /* fd is now closed */ return (rename(ckp->ckp_src, ckp->ckp_dst) != 0); } static void fmd_ckpt_resv(fmd_ckpt_t *ckp, size_t size, size_t align) { if (size != 0) { ckp->ckp_size = P2ROUNDUP(ckp->ckp_size, align) + size; ckp->ckp_secs++; } } static void fmd_ckpt_resv_buf(fmd_buf_t *bp, fmd_ckpt_t *ckp) { ckp->ckp_size = P2ROUNDUP(ckp->ckp_size, _MAX_ALIGNMENT) + bp->buf_size; ckp->ckp_strn += strlen(bp->buf_name) + 1; ckp->ckp_secs++; } static void fmd_ckpt_save_buf(fmd_buf_t *bp, fmd_ckpt_t *ckp) { fcf_buf_t *fcfb = ckp->ckp_arg; fcfb->fcfb_name = fmd_ckpt_string(ckp, bp->buf_name); fcfb->fcfb_data = fmd_ckpt_section(ckp, bp->buf_data, FCF_SECT_BUFFER, bp->buf_size); ckp->ckp_arg = fcfb + 1; } static void fmd_ckpt_save_event(fmd_ckpt_t *ckp, fmd_event_t *e) { fcf_event_t *fcfe = (void *)ckp->ckp_ptr; fmd_event_impl_t *ep = (fmd_event_impl_t *)e; fmd_log_t *lp = ep->ev_log; fcfe->fcfe_todsec = ep->ev_time.ftv_sec; fcfe->fcfe_todnsec = ep->ev_time.ftv_nsec; fcfe->fcfe_major = lp ? major(lp->log_stat.st_dev) : -1U; fcfe->fcfe_minor = lp ? minor(lp->log_stat.st_dev) : -1U; fcfe->fcfe_inode = lp ? lp->log_stat.st_ino : -1ULL; fcfe->fcfe_offset = ep->ev_off; ckp->ckp_ptr += sizeof (fcf_event_t); } static void fmd_ckpt_save_nvlist(fmd_ckpt_t *ckp, nvlist_t *nvl) { fcf_nvl_t *fcfn = (void *)ckp->ckp_ptr; char *nvbuf = (char *)ckp->ckp_ptr + sizeof (fcf_nvl_t); size_t nvsize = 0; (void) nvlist_size(nvl, &nvsize, NV_ENCODE_NATIVE); fcfn->fcfn_size = (uint64_t)nvsize; (void) nvlist_pack(nvl, &nvbuf, &nvsize, NV_ENCODE_NATIVE, 0); ckp->ckp_ptr += sizeof (fcf_nvl_t) + nvsize; ckp->ckp_ptr = (uchar_t *) P2ROUNDUP((uintptr_t)ckp->ckp_ptr, sizeof (uint64_t)); } static void fmd_ckpt_resv_serd(fmd_serd_eng_t *sgp, fmd_ckpt_t *ckp) { fmd_ckpt_resv(ckp, sizeof (fcf_event_t) * sgp->sg_count, sizeof (uint64_t)); ckp->ckp_strn += strlen(sgp->sg_name) + 1; } static void fmd_ckpt_save_serd(fmd_serd_eng_t *sgp, fmd_ckpt_t *ckp) { fcf_serd_t *fcfd = ckp->ckp_arg; fcf_secidx_t evsec = FCF_SECT_NONE; fmd_serd_elem_t *sep; if (sgp->sg_count != 0) { evsec = fmd_ckpt_section(ckp, NULL, FCF_SECT_EVENTS, sizeof (fcf_event_t) * sgp->sg_count); for (sep = fmd_list_next(&sgp->sg_list); sep != NULL; sep = fmd_list_next(sep)) fmd_ckpt_save_event(ckp, sep->se_event); } fcfd->fcfd_name = fmd_ckpt_string(ckp, sgp->sg_name); fcfd->fcfd_events = evsec; fcfd->fcfd_pad = 0; fcfd->fcfd_n = sgp->sg_n; fcfd->fcfd_t = sgp->sg_t; ckp->ckp_arg = fcfd + 1; } static void fmd_ckpt_resv_case(fmd_ckpt_t *ckp, fmd_case_t *cp) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_susp_t *cis; uint_t n; if (cip->ci_xprt != NULL) return; /* do not checkpoint cases from remote transports */ n = fmd_buf_hash_count(&cip->ci_bufs); fmd_buf_hash_apply(&cip->ci_bufs, (fmd_buf_f *)fmd_ckpt_resv_buf, ckp); fmd_ckpt_resv(ckp, sizeof (fcf_buf_t) * n, sizeof (uint32_t)); if (cip->ci_principal != NULL) fmd_ckpt_resv(ckp, sizeof (fcf_event_t), sizeof (uint64_t)); fmd_ckpt_resv(ckp, sizeof (fcf_event_t) * cip->ci_nitems, sizeof (uint64_t)); if (cip->ci_nsuspects != 0) ckp->ckp_size = P2ROUNDUP(ckp->ckp_size, sizeof (uint64_t)); cip->ci_nvsz = 0; /* compute size of packed suspect nvlist array */ for (cis = cip->ci_suspects; cis != NULL; cis = cis->cis_next) { size_t nvsize = 0; (void) nvlist_size(cis->cis_nvl, &nvsize, NV_ENCODE_NATIVE); cip->ci_nvsz += sizeof (fcf_nvl_t) + nvsize; cip->ci_nvsz = P2ROUNDUP(cip->ci_nvsz, sizeof (uint64_t)); } fmd_ckpt_resv(ckp, cip->ci_nvsz, sizeof (uint64_t)); fmd_ckpt_resv(ckp, sizeof (fcf_case_t), sizeof (uint32_t)); ckp->ckp_strn += strlen(cip->ci_uuid) + 1; } 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 void fmd_ckpt_resv_module(fmd_ckpt_t *ckp, fmd_module_t *mp) { fmd_case_t *cp; uint_t n; for (cp = fmd_list_next(&mp->mod_cases); cp; cp = fmd_list_next(cp)) fmd_ckpt_resv_case(ckp, cp); n = fmd_serd_hash_count(&mp->mod_serds); fmd_serd_hash_apply(&mp->mod_serds, (fmd_serd_eng_f *)fmd_ckpt_resv_serd, ckp); fmd_ckpt_resv(ckp, sizeof (fcf_serd_t) * n, sizeof (uint64_t)); n = fmd_buf_hash_count(&mp->mod_bufs); fmd_buf_hash_apply(&mp->mod_bufs, (fmd_buf_f *)fmd_ckpt_resv_buf, ckp); fmd_ckpt_resv(ckp, sizeof (fcf_buf_t) * n, sizeof (uint32_t)); fmd_ckpt_resv(ckp, sizeof (fcf_module_t), sizeof (uint32_t)); ckp->ckp_strn += strlen(mp->mod_name) + 1; ckp->ckp_strn += strlen(mp->mod_path) + 1; ckp->ckp_strn += strlen(mp->mod_info->fmdi_desc) + 1; ckp->ckp_strn += strlen(mp->mod_info->fmdi_vers) + 1; } 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)); } void fmd_ckpt_save(fmd_module_t *mp) { struct stat64 st; char path[PATH_MAX]; mode_t dirmode; hrtime_t now = gethrtime(); fmd_ckpt_t ckp; int err; ASSERT(fmd_module_locked(mp)); /* * If checkpointing is disabled for the module, just return. We must * commit the module state anyway to transition pending log events. */ if (mp->mod_stats->ms_ckpt_save.fmds_value.bool == FMD_B_FALSE) { fmd_module_commit(mp); return; } if (!(mp->mod_flags & (FMD_MOD_MDIRTY | FMD_MOD_CDIRTY))) return; /* no checkpoint is necessary for this module */ TRACE((FMD_DBG_CKPT, "ckpt save begin %s %llu", mp->mod_name, mp->mod_gen + 1)); /* * If the per-module checkpoint directory isn't found or isn't of type * directory, move aside whatever is there (if anything) and attempt * to mkdir(2) a new module checkpoint directory. If this fails, we * have no choice but to abort the checkpoint and try again later. */ if (stat64(mp->mod_ckpt, &st) != 0 || !S_ISDIR(st.st_mode)) { (void) snprintf(path, sizeof (path), "%s-", mp->mod_ckpt); (void) rename(mp->mod_ckpt, path); (void) fmd_conf_getprop(fmd.d_conf, "ckpt.dirmode", &dirmode); if (mkdir(mp->mod_ckpt, dirmode) != 0) { fmd_error(EFMD_CKPT_MKDIR, "failed to mkdir %s", mp->mod_ckpt); return; /* return without clearing dirty bits */ } } /* * Create a temporary file to write out the checkpoint into, and create * a fmd_ckpt_t structure to manage construction of the checkpoint. We * then figure out how much space will be required, and allocate it. */ if (fmd_ckpt_create(&ckp, mp) == -1) { fmd_error(EFMD_CKPT_CREATE, "failed to create %s", ckp.ckp_src); return; } fmd_ckpt_resv_module(&ckp, mp); if (fmd_ckpt_alloc(&ckp, mp->mod_gen + 1) != 0) { fmd_error(EFMD_CKPT_NOMEM, "failed to build %s", ckp.ckp_src); fmd_ckpt_destroy(&ckp); return; } /* * Fill in the checkpoint content, write it to disk, sync it, and then * atomically rename it to the destination path. If this fails, we * have no choice but to leave all our dirty bits set and return. */ fmd_ckpt_save_module(&ckp, mp); err = fmd_ckpt_commit(&ckp); fmd_ckpt_destroy(&ckp); if (err != 0) { fmd_error(EFMD_CKPT_COMMIT, "failed to commit %s", ckp.ckp_dst); return; /* return without clearing dirty bits */ } fmd_module_commit(mp); TRACE((FMD_DBG_CKPT, "ckpt save end %s", mp->mod_name)); mp->mod_stats->ms_ckpt_cnt.fmds_value.ui64++; mp->mod_stats->ms_ckpt_time.fmds_value.ui64 += gethrtime() - now; fmd_dprintf(FMD_DBG_CKPT, "saved checkpoint of %s (%llu)\n", mp->mod_name, mp->mod_gen); } /* * Utility function to retrieve a pointer to a section's header and verify that * it is of the expected type or it is a FCF_SECT_NONE reference. */ static const fcf_sec_t * fmd_ckpt_secptr(fmd_ckpt_t *ckp, fcf_secidx_t sid, uint_t type) { const fcf_sec_t *sp = (void *)(ckp->ckp_buf + ckp->ckp_hdr->fcfh_secoff + ckp->ckp_hdr->fcfh_secsize * sid); return (sid < ckp->ckp_secs && (sp->fcfs_type == type || sp->fcfs_type == FCF_SECT_NONE) ? sp : NULL); } /* * Utility function to retrieve the data pointer for a particular section. The * validity of the header values has already been checked by fmd_ckpt_open(). */ static const void * fmd_ckpt_dataptr(fmd_ckpt_t *ckp, const fcf_sec_t *sp) { return (ckp->ckp_buf + sp->fcfs_offset); } /* * Utility function to retrieve the end of the data region for a particular * section. The validity of this value has been confirmed by fmd_ckpt_open(). */ static const void * fmd_ckpt_datalim(fmd_ckpt_t *ckp, const fcf_sec_t *sp) { return (ckp->ckp_buf + sp->fcfs_offset + sp->fcfs_size); } /* * Utility function to retrieve a string pointer (fcf_stridx_t). If the string * index is valid, the string data is returned; otherwise 'defstr' is returned. */ static const char * fmd_ckpt_strptr(fmd_ckpt_t *ckp, fcf_stridx_t sid, const char *defstr) { return (sid < ckp->ckp_strn ? ckp->ckp_strs + sid : defstr); } static void fmd_ckpt_restore_events(fmd_ckpt_t *ckp, fcf_secidx_t sid, int (*func)(void *, fmd_event_t *), void *arg) { const fcf_event_t *fcfe; const fcf_sec_t *sp; fmd_timeval_t ftv; fmd_log_t *lp, *errlp; uint_t i, n; uint32_t e_maj, e_min; uint64_t e_ino; if ((sp = fmd_ckpt_secptr(ckp, sid, FCF_SECT_EVENTS)) == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "invalid link to section %u: expected events\n", sid); } if (sp->fcfs_size == 0) return; /* empty events section or type none */ fcfe = fmd_ckpt_dataptr(ckp, sp); n = sp->fcfs_size / sp->fcfs_entsize; /* * Hold the reader lock on log pointers to block log rotation during * the section restore so that we can safely insert refs to d_errlog. */ (void) pthread_rwlock_rdlock(&fmd.d_log_lock); errlp = fmd.d_errlog; e_maj = major(errlp->log_stat.st_dev); e_min = minor(errlp->log_stat.st_dev); e_ino = errlp->log_stat.st_ino; for (i = 0; i < n; i++) { fmd_event_t *ep; ftv.ftv_sec = fcfe->fcfe_todsec; ftv.ftv_nsec = fcfe->fcfe_todnsec; if (e_ino == fcfe->fcfe_inode && e_maj == fcfe->fcfe_major && e_min == fcfe->fcfe_minor) lp = errlp; else lp = NULL; ep = fmd_event_recreate(FMD_EVT_PROTOCOL, &ftv, NULL, NULL, lp, fcfe->fcfe_offset, 0); fmd_event_hold(ep); (void) func(arg, ep); fmd_event_rele(ep); fcfe = (fcf_event_t *)((uintptr_t)fcfe + sp->fcfs_entsize); } (void) pthread_rwlock_unlock(&fmd.d_log_lock); } static int fmd_ckpt_restore_suspects(fmd_ckpt_t *ckp, fmd_case_t *cp, fcf_secidx_t sid) { const fcf_nvl_t *fcfn, *endn; const fcf_sec_t *sp; nvlist_t *nvl; int err, i; if ((sp = fmd_ckpt_secptr(ckp, sid, FCF_SECT_NVLISTS)) == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "invalid link to section %u: expected nvlists\n", sid); } fcfn = fmd_ckpt_dataptr(ckp, sp); endn = fmd_ckpt_datalim(ckp, sp); for (i = 0; fcfn < endn; i++) { char *data = (char *)fcfn + sp->fcfs_entsize; size_t size = (size_t)fcfn->fcfn_size; if (fcfn->fcfn_size > (size_t)((char *)endn - data)) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "nvlist %u [%d] " "size %u exceeds buffer\n", sid, i, size); } if ((err = nvlist_xunpack(data, size, &nvl, &fmd.d_nva)) != 0) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "failed to " "unpack nvlist %u [%d]: %s\n", sid, i, fmd_strerror(err)); } fmd_case_insert_suspect(cp, nvl); size = sp->fcfs_entsize + fcfn->fcfn_size; size = P2ROUNDUP(size, sizeof (uint64_t)); fcfn = (fcf_nvl_t *)((uintptr_t)fcfn + size); } return (i); } static void fmd_ckpt_restore_bufs(fmd_ckpt_t *ckp, fmd_module_t *mp, fmd_case_t *cp, fcf_secidx_t sid) { const fcf_sec_t *sp, *dsp; const fcf_buf_t *fcfb; uint_t i, n; if ((sp = fmd_ckpt_secptr(ckp, sid, FCF_SECT_BUFS)) == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "invalid link to section %u: expected bufs\n", sid); } if (sp->fcfs_size == 0) return; /* empty events section or type none */ fcfb = fmd_ckpt_dataptr(ckp, sp); n = sp->fcfs_size / sp->fcfs_entsize; for (i = 0; i < n; i++) { dsp = fmd_ckpt_secptr(ckp, fcfb->fcfb_data, FCF_SECT_BUFFER); if (dsp == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "invalid %u " "buffer link %u\n", sid, fcfb->fcfb_data); } fmd_buf_write((fmd_hdl_t *)mp, cp, fmd_ckpt_strptr(ckp, fcfb->fcfb_name, ""), ckp->ckp_buf + dsp->fcfs_offset, dsp->fcfs_size); fcfb = (fcf_buf_t *)((uintptr_t)fcfb + sp->fcfs_entsize); } } static void fmd_ckpt_restore_case(fmd_ckpt_t *ckp, fmd_module_t *mp, const fcf_sec_t *sp) { const fcf_case_t *fcfc = fmd_ckpt_dataptr(ckp, sp); const char *uuid = fmd_ckpt_strptr(ckp, fcfc->fcfc_uuid, NULL); fmd_case_t *cp; int n; if (uuid == NULL || fcfc->fcfc_state > FCF_CASE_CLOSE_WAIT) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "corrupt %u case uuid " "and/or state\n", (uint_t)(sp - ckp->ckp_secp)); } fmd_module_lock(mp); if ((cp = fmd_case_recreate(mp, NULL, fcfc->fcfc_state != FCF_CASE_UNSOLVED ? FCF_CASE_SOLVED : FMD_CASE_UNSOLVED, uuid, NULL)) == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "duplicate case uuid: %s\n", uuid); } fmd_ckpt_restore_events(ckp, fcfc->fcfc_principal, fmd_case_insert_principal, cp); fmd_ckpt_restore_events(ckp, fcfc->fcfc_events, fmd_case_insert_event, cp); /* * Once solved, treat suspects from resource cache as master copy. * * If !fmd.d_running, this module must be a builtin, and so we don't * want to restore suspects or call fmd_case_transition_update() at this * stage. The suspects will be added later from the resource cache. * Calling fmd_case_transition("SOLVED") is OK here as the state is * already solved, so all it does is update the case flags. */ if (fmd.d_running && (n = ((fmd_case_impl_t *)cp)->ci_nsuspects) == 0) n = fmd_ckpt_restore_suspects(ckp, cp, fcfc->fcfc_suspects); if (!fmd.d_running) fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED); else if (fcfc->fcfc_state == FCF_CASE_SOLVED) fmd_case_transition_update(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED); else if (fcfc->fcfc_state == FCF_CASE_CLOSE_WAIT && n != 0) fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_SOLVED); else if (fcfc->fcfc_state == FCF_CASE_CLOSE_WAIT && n == 0) fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, 0); fmd_module_unlock(mp); fmd_ckpt_restore_bufs(ckp, mp, cp, fcfc->fcfc_bufs); } static void fmd_ckpt_restore_serd(fmd_ckpt_t *ckp, fmd_module_t *mp, const fcf_sec_t *sp) { const fcf_serd_t *fcfd = fmd_ckpt_dataptr(ckp, sp); uint_t i, n = sp->fcfs_size / sp->fcfs_entsize; const fcf_sec_t *esp; const char *s; for (i = 0; i < n; i++) { esp = fmd_ckpt_secptr(ckp, fcfd->fcfd_events, FCF_SECT_EVENTS); if (esp == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "invalid events link %u\n", fcfd->fcfd_events); } if ((s = fmd_ckpt_strptr(ckp, fcfd->fcfd_name, NULL)) == NULL) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "serd name %u is corrupt\n", fcfd->fcfd_name); } fmd_serd_create((fmd_hdl_t *)mp, s, fcfd->fcfd_n, fcfd->fcfd_t); fmd_module_lock(mp); fmd_ckpt_restore_events(ckp, fcfd->fcfd_events, fmd_serd_eng_record, fmd_serd_eng_lookup(&mp->mod_serds, s)); fmd_module_unlock(mp); fcfd = (fcf_serd_t *)((uintptr_t)fcfd + sp->fcfs_entsize); } } static void fmd_ckpt_restore_module(fmd_ckpt_t *ckp, fmd_module_t *mp) { const fcf_module_t *fcfm = fmd_ckpt_dataptr(ckp, ckp->ckp_modp); const fcf_sec_t *sp; uint_t i; if (strcmp(mp->mod_name, fmd_ckpt_strptr(ckp, fcfm->fcfm_name, "")) || strcmp(mp->mod_path, fmd_ckpt_strptr(ckp, fcfm->fcfm_path, ""))) { fmd_ckpt_error(ckp, EFMD_CKPT_INVAL, "checkpoint is not for module %s\n", mp->mod_name); } for (i = 0; i < ckp->ckp_secs; i++) { sp = (void *)(ckp->ckp_buf + ckp->ckp_hdr->fcfh_secoff + ckp->ckp_hdr->fcfh_secsize * i); switch (sp->fcfs_type) { case FCF_SECT_CASE: fmd_ckpt_restore_case(ckp, mp, sp); break; case FCF_SECT_SERD: fmd_ckpt_restore_serd(ckp, mp, sp); break; } } fmd_ckpt_restore_bufs(ckp, mp, NULL, fcfm->fcfm_bufs); mp->mod_gen = ckp->ckp_hdr->fcfh_cgen; } /* * Restore a checkpoint for the specified module. Any errors which occur * during restore will call fmd_ckpt_error() or trigger an fmd_api_error(), * either of which will automatically unlock the module and trigger an abort. */ void fmd_ckpt_restore(fmd_module_t *mp) { fmd_ckpt_t ckp; if (mp->mod_stats->ms_ckpt_restore.fmds_value.bool == FMD_B_FALSE) return; /* never restore checkpoints for this module */ TRACE((FMD_DBG_CKPT, "ckpt restore begin %s", mp->mod_name)); if (fmd_ckpt_open(&ckp, mp) == -1) { if (errno != ENOENT) fmd_error(EFMD_CKPT_OPEN, "can't open %s", ckp.ckp_src); TRACE((FMD_DBG_CKPT, "ckpt restore end %s", mp->mod_name)); return; } ASSERT(!fmd_module_locked(mp)); fmd_ckpt_restore_module(&ckp, mp); fmd_ckpt_destroy(&ckp); fmd_module_clrdirty(mp); TRACE((FMD_DBG_CKPT, "ckpt restore end %s", mp->mod_name)); fmd_dprintf(FMD_DBG_CKPT, "restored checkpoint of %s\n", mp->mod_name); } /* * Delete the module's checkpoint file. This is used by the ckpt.zero property * code or by the fmadm reset RPC service path to force a checkpoint delete. */ void fmd_ckpt_delete(fmd_module_t *mp) { char path[PATH_MAX]; (void) snprintf(path, sizeof (path), "%s/%s", mp->mod_ckpt, mp->mod_name); TRACE((FMD_DBG_CKPT, "delete %s ckpt", mp->mod_name)); if (unlink(path) != 0 && errno != ENOENT) fmd_error(EFMD_CKPT_DELETE, "failed to delete %s", path); } /* * Move aside the module's checkpoint file if checkpoint restore has failed. * We rename the file rather than deleting it in the hopes that someone might * send it to us for post-mortem analysis of whether we have a checkpoint bug. */ void fmd_ckpt_rename(fmd_module_t *mp) { char src[PATH_MAX], dst[PATH_MAX]; (void) snprintf(src, sizeof (src), "%s/%s", mp->mod_ckpt, mp->mod_name); (void) snprintf(dst, sizeof (dst), "%s-", src); TRACE((FMD_DBG_CKPT, "rename %s ckpt", mp->mod_name)); if (rename(src, dst) != 0 && errno != ENOENT) fmd_error(EFMD_CKPT_DELETE, "failed to rename %s", src); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_CKPT_H #define _FMD_CKPT_H #include #ifdef __cplusplus extern "C" { #endif /* * Fault Manager Checkpoint Format (FCF) * * Fault manager modules can checkpoint state in the FCF format so that they * can survive restarts, module failures, and reboots. The FCF format is * versioned and extensible so that it can be revised and so that internal data * structures can be modified or extended compatibly. It is also specified as * a Project Private interface so that incompatible changes can occur as we see * fit. All FCF structures use fixed-size types so that the 32-bit and 64-bit * forms are identical and consumers can use either data model transparently. * * The file layout is structured as follows: * * +---------------+-------------------+----- ... ----+---- ... ------+ * | fcf_hdr_t | fcf_sec_t[ ... ] | section | section | * | (file header) | (section headers) | #1 data | #N data | * +---------------+-------------------+----- ... ----+---- ... ------+ * |<------------ fcf_hdr.fcfh_filesz ------------------------------->| * * The file header stores meta-data including a magic number, data model for * the checkpointed module, data encoding, and other miscellaneous properties. * The header describes its own size and the size of the section headers. By * convention, an array of section headers follows the file header, and then * the data for all the individual sections listed in the section header table. * * The section headers describe the size, offset, alignment, and section type * for each section. Sections are described using a set of #defines that tell * the consumer what kind of data is expected. Sections can contain links to * other sections by storing a fcf_secidx_t, an index into the section header * array, inside of the section data structures. The section header includes * an entry size so that sections with data arrays can grow their structures. * * Finally, strings are always stored in ELF-style string tables along with a * string table section index and string table offset. Therefore strings in * FCF are always arbitrary-length and not bound to the current implementation. */ #define FCF_ID_SIZE 16 /* total size of fcfh_ident[] in bytes */ typedef struct fcf_hdr { uint8_t fcfh_ident[FCF_ID_SIZE]; /* identification bytes (see below) */ uint32_t fcfh_flags; /* file attribute flags (if any) */ uint32_t fcfh_hdrsize; /* size of file header in bytes */ uint32_t fcfh_secsize; /* size of section header in bytes */ uint32_t fcfh_secnum; /* number of section headers */ uint64_t fcfh_secoff; /* file offset of section headers */ uint64_t fcfh_filesz; /* file size of entire FCF file */ uint64_t fcfh_cgen; /* checkpoint generation number */ uint64_t fcfh_pad; /* reserved for future use */ } fcf_hdr_t; #define FCF_ID_MAG0 0 /* first byte of magic number */ #define FCF_ID_MAG1 1 /* second byte of magic number */ #define FCF_ID_MAG2 2 /* third byte of magic number */ #define FCF_ID_MAG3 3 /* fourth byte of magic number */ #define FCF_ID_MODEL 4 /* FCF data model (see below) */ #define FCF_ID_ENCODING 5 /* FCF data encoding (see below) */ #define FCF_ID_VERSION 6 /* FCF file format major version (see below) */ #define FCF_ID_PAD 7 /* start of padding bytes (all zeroes) */ #define FCF_MAG_MAG0 0x7F /* FCF_ID_MAG[0-3] */ #define FCF_MAG_MAG1 'F' #define FCF_MAG_MAG2 'C' #define FCF_MAG_MAG3 'F' #define FCF_MAG_STRING "\177FCF" #define FCF_MAG_STRLEN 4 #define FCF_MODEL_NONE 0 /* FCF_ID_MODEL */ #define FCF_MODEL_ILP32 1 #define FCF_MODEL_LP64 2 #ifdef _LP64 #define FCF_MODEL_NATIVE FCF_MODEL_LP64 #else #define FCF_MODEL_NATIVE FCF_MODEL_ILP32 #endif #define FCF_ENCODE_NONE 0 /* FCF_ID_ENCODING */ #define FCF_ENCODE_LSB 1 #define FCF_ENCODE_MSB 2 #ifdef _BIG_ENDIAN #define FCF_ENCODE_NATIVE FCF_ENCODE_MSB #else #define FCF_ENCODE_NATIVE FCF_ENCODE_LSB #endif #define FCF_VERSION_1 1 /* FCF_ID_VERSION */ #define FCF_VERSION FCF_VERSION_1 #define FCF_FL_VALID 0 /* mask of all valid fcfh_flags bits */ typedef uint32_t fcf_secidx_t; /* section header table index type */ typedef uint32_t fcf_stridx_t; /* string table index type */ #define FCF_SECIDX_NONE 0 /* null value for section indices */ #define FCF_STRIDX_NONE 0 /* null value for string indices */ typedef struct fcf_sec { uint32_t fcfs_type; /* section type (see below) */ uint32_t fcfs_align; /* section data memory alignment */ uint32_t fcfs_flags; /* section flags (if any) */ uint32_t fcfs_entsize; /* size of section entry (if table) */ uint64_t fcfs_offset; /* offset of section data within file */ uint64_t fcfs_size; /* size of section data in bytes */ } fcf_sec_t; /* * Section types (fcfs_type values). These #defines should be kept in sync * with the decoding table declared in fmd_mdb.c in the fcf_sec() dcmd, and * with the size and alignment table declared at the top of fmd_ckpt.c. */ #define FCF_SECT_NONE 0 /* null section */ #define FCF_SECT_STRTAB 1 /* string table */ #define FCF_SECT_MODULE 2 /* module meta-data (fcf_mod_t) */ #define FCF_SECT_CASE 3 /* case meta-data (fcf_case_t) */ #define FCF_SECT_BUFS 4 /* buffer list (fcf_buf_t) */ #define FCF_SECT_BUFFER 5 /* module data buffer */ #define FCF_SECT_SERD 6 /* serd list (fcf_serd_t) */ #define FCF_SECT_EVENTS 7 /* event list (fcf_event_t) */ #define FCF_SECT_NVLISTS 8 /* nvlist list (fcf_nvl_t) */ typedef struct fcf_module { fcf_stridx_t fcfm_name; /* module basename */ fcf_stridx_t fcfm_path; /* module path */ fcf_stridx_t fcfm_desc; /* description */ fcf_stridx_t fcfm_vers; /* version */ fcf_secidx_t fcfm_bufs; /* FCF_SECT_BUFS containing global buffers */ } fcf_module_t; typedef struct fcf_case { fcf_stridx_t fcfc_uuid; /* case uuid */ uint32_t fcfc_state; /* case state (see below) */ fcf_secidx_t fcfc_bufs; /* FCF_SECT_BUFS containing buffers */ fcf_secidx_t fcfc_principal; /* FCF_SECT_EVENTS containing principal */ fcf_secidx_t fcfc_events; /* FCF_SECT_EVENTS containing events */ fcf_secidx_t fcfc_suspects; /* FCF_SECT_NVLISTS containing suspects */ } fcf_case_t; #define FCF_CASE_UNSOLVED 0 #define FCF_CASE_SOLVED 1 #define FCF_CASE_CLOSE_WAIT 2 typedef struct fcf_buf { fcf_stridx_t fcfb_name; /* buffer name */ fcf_secidx_t fcfb_data; /* FCF_SECT_BUFFER containing data */ } fcf_buf_t; typedef struct fcf_serd { fcf_stridx_t fcfd_name; /* engine name */ fcf_secidx_t fcfd_events; /* FCF_SECT_EVENTS containing events */ uint32_t fcfd_pad; /* reserved for future use */ uint32_t fcfd_n; /* engine N parameter */ uint64_t fcfd_t; /* engine T parameter */ } fcf_serd_t; typedef struct fcf_event { uint64_t fcfe_todsec; /* seconds since gettimeofday(3C) epoch */ uint64_t fcfe_todnsec; /* nanoseconds past value of fcfe_todsec */ uint32_t fcfe_major; /* major number from log file st_dev */ uint32_t fcfe_minor; /* minor number from log file st_rdev */ uint64_t fcfe_inode; /* inode number from log file st_ino */ uint64_t fcfe_offset; /* event offset within log file */ } fcf_event_t; typedef struct fcf_nvlist { uint64_t fcfn_size; /* size of packed nvlist after this header */ } fcf_nvl_t; /* * The checkpoint subsystem provides a very simple set of interfaces to the * reset of fmd: namely, checkpoints can be saved, restored, or deleted by mod. * In the reference implementation, these are implemented to use FCF files. */ struct fmd_module; /* see */ extern void fmd_ckpt_save(struct fmd_module *); extern void fmd_ckpt_restore(struct fmd_module *); extern void fmd_ckpt_delete(struct fmd_module *); extern void fmd_ckpt_rename(struct fmd_module *); #ifdef __cplusplus } #endif #endif /* _FMD_CKPT_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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char FMD_PROP_SUBSCRIPTIONS[] = "_subscriptions"; const char FMD_PROP_DICTIONARIES[] = "_dictionaries"; /* * The property formals defined in _fmd_conf_defv[] are added to every config * dictionary that is created. Here we define several special FMD_PROP_* * properties that are used to implement the config file keyword actions, as * well as properties that should be inherited by fmd_conf_t's from fmd.d_conf. */ static const fmd_conf_formal_t _fmd_conf_defv[] = { { FMD_PROP_SUBSCRIPTIONS, &fmd_conf_list, "" }, { FMD_PROP_DICTIONARIES, &fmd_conf_list, "" }, { "fmd.isaname", &fmd_conf_parent, "isaname" }, { "fmd.machine", &fmd_conf_parent, "machine" }, { "fmd.platform", &fmd_conf_parent, "platform" }, { "fmd.rootdir", &fmd_conf_parent, "rootdir" }, }; static const int _fmd_conf_defc = sizeof (_fmd_conf_defv) / sizeof (_fmd_conf_defv[0]); static int set_bool(fmd_conf_param_t *pp, const char *s) { if (strcasecmp(s, "true") == 0) pp->cp_value.cpv_num = 1; else if (strcasecmp(s, "false") == 0) pp->cp_value.cpv_num = 0; else return (fmd_set_errno(EFMD_CONF_INVAL)); return (0); } static void get_bool(const fmd_conf_param_t *pp, void *ptr) { *((int *)ptr) = (int)pp->cp_value.cpv_num; } static int set_i32x(fmd_conf_param_t *pp, const char *s, int64_t min, int64_t max) { int64_t val; char *end; errno = 0; val = strtoll(s, &end, 0); if (errno == EOVERFLOW || val < min || val > max) return (fmd_set_errno(EFMD_CONF_OVERFLOW)); if (errno != 0 || end == s || *end != '\0') return (fmd_set_errno(EFMD_CONF_INVAL)); pp->cp_value.cpv_num = val; return (0); } static int set_i8(fmd_conf_param_t *pp, const char *s) { return (set_i32x(pp, s, INT8_MIN, INT8_MAX)); } static int set_i16(fmd_conf_param_t *pp, const char *s) { return (set_i32x(pp, s, INT16_MIN, INT16_MAX)); } static int set_i32(fmd_conf_param_t *pp, const char *s) { return (set_i32x(pp, s, INT32_MIN, INT32_MAX)); } static void get_i32(const fmd_conf_param_t *pp, void *ptr) { *((int32_t *)ptr) = (int32_t)pp->cp_value.cpv_num; } static int set_ui32x(fmd_conf_param_t *pp, const char *s, uint64_t max) { uint64_t val; char *end; errno = 0; val = strtoull(s, &end, 0); if (errno == EOVERFLOW || val > max) return (fmd_set_errno(EFMD_CONF_OVERFLOW)); if (errno != 0 || end == s || *end != '\0') return (fmd_set_errno(EFMD_CONF_INVAL)); pp->cp_value.cpv_num = val; return (0); } static int set_ui8(fmd_conf_param_t *pp, const char *s) { return (set_ui32x(pp, s, UINT8_MAX)); } static int set_ui16(fmd_conf_param_t *pp, const char *s) { return (set_ui32x(pp, s, UINT16_MAX)); } static int set_ui32(fmd_conf_param_t *pp, const char *s) { return (set_ui32x(pp, s, UINT32_MAX)); } static void get_ui32(const fmd_conf_param_t *pp, void *ptr) { *((uint32_t *)ptr) = (uint32_t)pp->cp_value.cpv_num; } static int set_i64(fmd_conf_param_t *pp, const char *s) { int64_t val; char *end; errno = 0; val = strtoll(s, &end, 0); if (errno == EOVERFLOW) return (fmd_set_errno(EFMD_CONF_OVERFLOW)); if (errno != 0 || end == s || *end != '\0') return (fmd_set_errno(EFMD_CONF_INVAL)); pp->cp_value.cpv_num = val; return (0); } static void get_i64(const fmd_conf_param_t *pp, void *ptr) { *((int64_t *)ptr) = (int64_t)pp->cp_value.cpv_num; } static int set_ui64(fmd_conf_param_t *pp, const char *s) { uint64_t val; char *end; errno = 0; val = strtoull(s, &end, 0); if (errno == EOVERFLOW) return (fmd_set_errno(EFMD_CONF_OVERFLOW)); if (errno != 0 || end == s || *end != '\0') return (fmd_set_errno(EFMD_CONF_INVAL)); pp->cp_value.cpv_num = val; return (0); } static void get_ui64(const fmd_conf_param_t *pp, void *ptr) { *((uint64_t *)ptr) = pp->cp_value.cpv_num; } static int set_str(fmd_conf_param_t *pp, const char *s) { fmd_strfree(pp->cp_value.cpv_str); pp->cp_value.cpv_str = fmd_strdup(s, FMD_SLEEP); return (0); } static void get_str(const fmd_conf_param_t *pp, void *ptr) { *((const char **)ptr) = pp->cp_value.cpv_str; } static void free_str(fmd_conf_param_t *pp) { fmd_strfree(pp->cp_value.cpv_str); pp->cp_value.cpv_str = NULL; } static int set_path(fmd_conf_param_t *pp, const char *value) { size_t len = strlen(value); char *s = alloca(len + 1); char **patv = alloca(sizeof (char *) * len / 2); int patc = 0; static const char *const percent_sign = "%"; char *p, *q; int c, i; static const struct fmd_conf_token { char tok_tag; const char *const *tok_val; } tokens[] = { { 'i', &fmd.d_platform }, { 'm', &fmd.d_machine }, { 'p', &fmd.d_isaname }, { 'r', &fmd.d_rootdir }, { '%', &percent_sign }, { 0, NULL } }; const struct fmd_conf_token *tok; fmd_conf_path_t *pap; pp->cp_formal->cf_ops->co_free(pp); (void) strcpy(s, value); for (p = strtok_r(s, ":", &q); p != NULL; p = strtok_r(NULL, ":", &q)) patv[patc++] = p; pap = fmd_alloc(sizeof (fmd_conf_path_t), FMD_SLEEP); pap->cpa_argv = fmd_alloc(sizeof (char *) * patc, FMD_SLEEP); pap->cpa_argc = patc; for (i = 0; i < patc; i++) { for (len = 0, p = patv[i]; (c = *p) != '\0'; p++, len++) { if (c != '%' || (c = p[1]) == '\0') continue; for (tok = tokens; tok->tok_tag != 0; tok++) { if (c == tok->tok_tag) { len += strlen(*tok->tok_val) - 1; p++; break; } } } pap->cpa_argv[i] = q = fmd_alloc(len + 1, FMD_SLEEP); q[len] = '\0'; for (p = patv[i]; (c = *p) != '\0'; p++) { if (c != '%' || (c = p[1]) == '\0') { *q++ = c; continue; } for (tok = tokens; tok->tok_tag != 0; tok++) { if (c == tok->tok_tag) { (void) strcpy(q, *tok->tok_val); q += strlen(q); p++; break; } } if (tok->tok_tag == 0) *q++ = c; } } pp->cp_value.cpv_ptr = pap; return (0); } static int set_lst(fmd_conf_param_t *pp, const char *value) { fmd_conf_path_t *old; old = pp->cp_value.cpv_ptr; pp->cp_value.cpv_ptr = NULL; if (set_path(pp, value) != 0) { pp->cp_value.cpv_ptr = old; return (-1); /* errno is set for us */ } if (old != NULL) { fmd_conf_path_t *new = pp->cp_value.cpv_ptr; int i, totc = old->cpa_argc + new->cpa_argc; int new_argc = new->cpa_argc; const char **new_argv = new->cpa_argv; new->cpa_argc = 0; new->cpa_argv = fmd_alloc(sizeof (char *) * totc, FMD_SLEEP); for (i = 0; i < old->cpa_argc; i++) new->cpa_argv[new->cpa_argc++] = old->cpa_argv[i]; for (i = 0; i < new_argc; i++) new->cpa_argv[new->cpa_argc++] = new_argv[i]; ASSERT(new->cpa_argc == totc); fmd_free(new_argv, sizeof (char *) * new_argc); fmd_free(old->cpa_argv, sizeof (char *) * old->cpa_argc); fmd_free(old, sizeof (fmd_conf_path_t)); } return (0); } static int del_lst(fmd_conf_param_t *pp, const char *value) { fmd_conf_path_t *pap = pp->cp_value.cpv_ptr; const char **new_argv; int i, new_argc; for (i = 0; i < pap->cpa_argc; i++) { if (strcmp(pap->cpa_argv[i], value) == 0) break; } if (i == pap->cpa_argc) return (fmd_set_errno(ENOENT)); fmd_strfree((char *)pap->cpa_argv[i]); pap->cpa_argv[i] = NULL; new_argc = 0; new_argv = fmd_alloc(sizeof (char *) * (pap->cpa_argc - 1), FMD_SLEEP); for (i = 0; i < pap->cpa_argc; i++) { if (pap->cpa_argv[i] != NULL) new_argv[new_argc++] = pap->cpa_argv[i]; } fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc); pap->cpa_argv = new_argv; pap->cpa_argc = new_argc; return (0); } static void get_path(const fmd_conf_param_t *pp, void *ptr) { *((fmd_conf_path_t **)ptr) = (fmd_conf_path_t *)pp->cp_value.cpv_ptr; } static void free_path(fmd_conf_param_t *pp) { fmd_conf_path_t *pap = pp->cp_value.cpv_ptr; int i; if (pap == NULL) return; /* no value was ever set */ for (i = 0; i < pap->cpa_argc; i++) fmd_strfree((char *)pap->cpa_argv[i]); fmd_free(pap->cpa_argv, sizeof (char *) * pap->cpa_argc); fmd_free(pap, sizeof (fmd_conf_path_t)); pp->cp_value.cpv_ptr = NULL; } static int set_time(fmd_conf_param_t *pp, const char *s) { static const struct { const char *name; hrtime_t mul; } suffix[] = { { "ns", NANOSEC / NANOSEC }, { "nsec", NANOSEC / NANOSEC }, { "us", NANOSEC / MICROSEC }, { "usec", NANOSEC / MICROSEC }, { "ms", NANOSEC / MILLISEC }, { "msec", NANOSEC / MILLISEC }, { "s", NANOSEC / SEC }, { "sec", NANOSEC / SEC }, { "m", NANOSEC * (hrtime_t)60 }, { "min", NANOSEC * (hrtime_t)60 }, { "h", NANOSEC * (hrtime_t)(60 * 60) }, { "hour", NANOSEC * (hrtime_t)(60 * 60) }, { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "hz", 0 }, { NULL } }; hrtime_t val, mul = 1; char *end; int i; errno = 0; val = strtoull(s, &end, 0); if (errno == EOVERFLOW) return (fmd_set_errno(EFMD_CONF_OVERFLOW)); if (errno != 0 || end == s) return (fmd_set_errno(EFMD_CONF_INVAL)); for (i = 0; suffix[i].name != NULL; i++) { if (strcasecmp(suffix[i].name, end) == 0) { mul = suffix[i].mul; break; } } if (suffix[i].name == NULL && *end != '\0') return (fmd_set_errno(EFMD_CONF_INVAL)); if (mul == 0) { if (val != 0) val = NANOSEC / val; /* compute val as value per sec */ } else val *= mul; pp->cp_value.cpv_num = val; return (0); } static int set_size(fmd_conf_param_t *pp, const char *s) { size_t len = strlen(s); uint64_t val, mul = 1; char *end; switch (s[len - 1]) { case 't': case 'T': mul *= 1024; /*FALLTHRU*/ case 'g': case 'G': mul *= 1024; /*FALLTHRU*/ case 'm': case 'M': mul *= 1024; /*FALLTHRU*/ case 'k': case 'K': mul *= 1024; /*FALLTHRU*/ default: break; } errno = 0; val = strtoull(s, &end, 0) * mul; if (errno == EOVERFLOW) return (fmd_set_errno(EFMD_CONF_OVERFLOW)); if ((mul != 1 && end != &s[len - 1]) || (mul == 1 && *end != '\0') || errno != 0) return (fmd_set_errno(EFMD_CONF_INVAL)); pp->cp_value.cpv_num = val; return (0); } static int set_sig(fmd_conf_param_t *pp, const char *s) { int sig; if (strncasecmp(s, "SIG", 3) == 0) s += 3; /* be friendlier than strsig() and permit the prefix */ if (str2sig(s, &sig) != 0) return (fmd_set_errno(EFMD_CONF_INVAL)); pp->cp_value.cpv_num = sig; return (0); } static void get_par(const fmd_conf_param_t *pp, void *ptr) { if (fmd_conf_getprop(fmd.d_conf, pp->cp_formal->cf_default, ptr) != 0) { fmd_panic("fmd.d_conf does not define '%s' (inherited as %s)\n", (char *)pp->cp_formal->cf_default, pp->cp_formal->cf_name); } } /*ARGSUSED*/ static int set_par(fmd_conf_param_t *pp, const char *s) { return (fmd_set_errno(EFMD_CONF_RDONLY)); } /* * Utility routine for callers who define custom ops where a list of string * tokens are translated into a bitmask. 'cmp' should be set to point to an * array of fmd_conf_mode_t's where the final element has cm_name == NULL. */ int fmd_conf_mode_set(const fmd_conf_mode_t *cma, fmd_conf_param_t *pp, const char *value) { const fmd_conf_mode_t *cmp; char *p, *q, *s = fmd_strdup(value, FMD_SLEEP); size_t len = value ? strlen(value) + 1 : 0; uint_t mode = 0; if (s == NULL) { pp->cp_value.cpv_num = 0; return (0); } for (p = strtok_r(s, ",", &q); p != NULL; p = strtok_r(NULL, ",", &q)) { for (cmp = cma; cmp->cm_name != NULL; cmp++) { if (strcmp(cmp->cm_name, p) == 0) { mode |= cmp->cm_bits; break; } } if (cmp->cm_name == NULL) { fmd_free(s, len); return (fmd_set_errno(EFMD_CONF_INVAL)); } } pp->cp_value.cpv_num = mode; fmd_free(s, len); return (0); } void fmd_conf_mode_get(const fmd_conf_param_t *pp, void *ptr) { *((uint_t *)ptr) = (uint_t)pp->cp_value.cpv_num; } /*ARGSUSED*/ int fmd_conf_notsup(fmd_conf_param_t *pp, const char *value) { return (fmd_set_errno(ENOTSUP)); } /*ARGSUSED*/ void fmd_conf_nop(fmd_conf_param_t *pp) { /* no free required for integer-type parameters */ } #define CONF_DEFINE(name, a, b, c, d) \ const fmd_conf_ops_t name = { a, b, c, d } CONF_DEFINE(fmd_conf_bool, set_bool, get_bool, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_int8, set_i8, get_i32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_uint8, set_ui8, get_ui32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_int16, set_i16, get_i32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_uint16, set_ui16, get_ui32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_int32, set_i32, get_i32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_uint32, set_ui32, get_ui32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_int64, set_i64, get_i64, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_uint64, set_ui64, get_ui64, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_string, set_str, get_str, fmd_conf_notsup, free_str); CONF_DEFINE(fmd_conf_path, set_path, get_path, fmd_conf_notsup, free_path); CONF_DEFINE(fmd_conf_list, set_lst, get_path, del_lst, free_path); CONF_DEFINE(fmd_conf_time, set_time, get_ui64, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_size, set_size, get_ui64, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_signal, set_sig, get_i32, fmd_conf_notsup, fmd_conf_nop); CONF_DEFINE(fmd_conf_parent, set_par, get_par, fmd_conf_notsup, fmd_conf_nop); static char * fmd_conf_skipstr(char *s) { int c; while ((c = *s) != '\0') { if (c == '\\') s++; else if (c == '"') break; s++; } return (s); } static char * fmd_conf_skipnws(char *s) { while (strchr("\f\n\r\t\v ", *s) == NULL) s++; return (s); } static int fmd_conf_tokenize(char *s, char *tokv[]) { int c, tokc = 0; while ((c = *s) != '\0') { switch (c) { case '"': tokv[tokc] = s + 1; s = fmd_conf_skipstr(s + 1); *s++ = '\0'; (void) fmd_stresc2chr(tokv[tokc++]); continue; case '\f': case '\n': case '\r': case '\t': case '\v': case ' ': s++; continue; default: tokv[tokc++] = s; s = fmd_conf_skipnws(s); *s++ = '\0'; } } return (tokc); } static int fmd_conf_exec_setprop(fmd_conf_t *cfp, int argc, char *argv[]) { if (argc != 2) return (fmd_set_errno(EFMD_CONF_USAGE)); return (fmd_conf_setprop(cfp, argv[0], argv[1])); } static int fmd_conf_exec_subscribe(fmd_conf_t *cfp, int argc, char *argv[]) { if (argc != 1) return (fmd_set_errno(EFMD_CONF_USAGE)); return (fmd_conf_setprop(cfp, FMD_PROP_SUBSCRIPTIONS, argv[0])); } static int fmd_conf_exec_dictionary(fmd_conf_t *cfp, int argc, char *argv[]) { if (argc != 1) return (fmd_set_errno(EFMD_CONF_USAGE)); return (fmd_conf_setprop(cfp, FMD_PROP_DICTIONARIES, argv[0])); } static int fmd_conf_parse(fmd_conf_t *cfp, const char *file) { static const fmd_conf_verb_t verbs[] = { { "setprop", fmd_conf_exec_setprop }, { "subscribe", fmd_conf_exec_subscribe }, { "dictionary", fmd_conf_exec_dictionary }, { NULL, NULL } }; int line, errs = 0; char buf[BUFSIZ]; FILE *fp; if ((fp = fopen(file, "r")) == NULL) { if (errno == EMFILE) fmd_error(EFMD_EXIT, "failed to open %s: %s\n", file, fmd_strerror(errno)); else fmd_error(EFMD_CONF_OPEN, "failed to open %s: %s\n", file, fmd_strerror(errno)); return (fmd_set_errno(EFMD_CONF_OPEN)); } for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) { char *tokv[sizeof (buf) / 2 + 1]; int tokc = fmd_conf_tokenize(buf, tokv); const fmd_conf_verb_t *vp; if (tokc == 0 || tokv[0][0] == '#') continue; /* skip blank lines and comment lines */ for (vp = verbs; vp->cv_name != NULL; vp++) { if (strcmp(tokv[0], vp->cv_name) == 0) break; } if (vp->cv_name == NULL) { fmd_error(EFMD_CONF_KEYWORD, "\"%s\", line %d: " "invalid configuration file keyword: %s\n", file, line, tokv[0]); errs++; continue; } if (vp->cv_exec(cfp, tokc - 1, tokv + 1) != 0) { fmd_error(errno, "\"%s\", line %d", file, line); errs++; continue; } } if (ferror(fp) != 0 || fclose(fp) != 0) return (fmd_set_errno(EFMD_CONF_IO)); if (errs != 0) return (fmd_set_errno(EFMD_CONF_ERRS)); return (0); } static void fmd_conf_fill(fmd_conf_t *cfp, fmd_conf_param_t *ppbuf, int argc, const fmd_conf_formal_t *argv, int checkid) { int i; for (i = 0; i < argc; i++, argv++) { fmd_conf_param_t *op, *pp = ppbuf + i; const char *name = argv->cf_name; ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen; if (fmd_strbadid(name, checkid) != NULL) { fmd_error(EFMD_CONF_PROPNAME, "ignoring invalid formal " "property %s\n", name); continue; } for (op = cfp->cf_parhash[h]; op != NULL; op = op->cp_next) { if (strcmp(op->cp_formal->cf_name, name) == 0) { fmd_error(EFMD_CONF_PROPDUP, "ignoring " "duplicate formal property %s\n", name); break; } } if (op != NULL) continue; pp->cp_formal = argv; pp->cp_next = cfp->cf_parhash[h]; cfp->cf_parhash[h] = pp; if (argv->cf_default && argv->cf_ops != &fmd_conf_parent && fmd_conf_setprop(cfp, name, argv->cf_default) != 0) { fmd_error(EFMD_CONF_DEFAULT, "ignoring invalid default " "<%s> for property %s: %s\n", argv->cf_default, name, fmd_strerror(errno)); } } } fmd_conf_t * fmd_conf_open(const char *file, int argc, const fmd_conf_formal_t *argv, uint_t flag) { fmd_conf_t *cfp = fmd_alloc(sizeof (fmd_conf_t), FMD_SLEEP); (void) pthread_rwlock_init(&cfp->cf_lock, NULL); cfp->cf_argv = argv; cfp->cf_argc = argc; cfp->cf_flag = flag; cfp->cf_params = fmd_zalloc( sizeof (fmd_conf_param_t) * (_fmd_conf_defc + argc), FMD_SLEEP); cfp->cf_parhashlen = fmd.d_str_buckets; cfp->cf_parhash = fmd_zalloc( sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen, FMD_SLEEP); cfp->cf_defer = NULL; fmd_conf_fill(cfp, cfp->cf_params, _fmd_conf_defc, _fmd_conf_defv, 0); fmd_conf_fill(cfp, cfp->cf_params + _fmd_conf_defc, argc, argv, 1); if (file != NULL && fmd_conf_parse(cfp, file) != 0) { fmd_conf_close(cfp); return (NULL); } return (cfp); } void fmd_conf_merge(fmd_conf_t *cfp, const char *file) { (void) fmd_conf_parse(cfp, file); } void fmd_conf_propagate(fmd_conf_t *src, fmd_conf_t *dst, const char *scope) { size_t len = strlen(scope); fmd_conf_defer_t *cdp; (void) pthread_rwlock_rdlock(&src->cf_lock); for (cdp = src->cf_defer; cdp != NULL; cdp = cdp->cd_next) { if (len == (size_t)(strchr(cdp->cd_name, ':') - cdp->cd_name) && strncmp(cdp->cd_name, scope, len) == 0 && fmd_conf_setprop( dst, cdp->cd_name + len + 1, cdp->cd_value) != 0) { fmd_error(EFMD_CONF_DEFER, "failed to apply deferred property %s to %s: %s\n", cdp->cd_name, scope, fmd_strerror(errno)); } } (void) pthread_rwlock_unlock(&src->cf_lock); } void fmd_conf_close(fmd_conf_t *cfp) { fmd_conf_param_t *pp = cfp->cf_params; int i, nparams = _fmd_conf_defc + cfp->cf_argc; fmd_conf_defer_t *cdp, *ndp; for (cdp = cfp->cf_defer; cdp != NULL; cdp = ndp) { ndp = cdp->cd_next; fmd_strfree(cdp->cd_name); fmd_strfree(cdp->cd_value); fmd_free(cdp, sizeof (fmd_conf_defer_t)); } fmd_free(cfp->cf_parhash, sizeof (fmd_conf_param_t *) * cfp->cf_parhashlen); for (i = 0; i < nparams; i++, pp++) { if (pp->cp_formal != NULL) pp->cp_formal->cf_ops->co_free(pp); } fmd_free(cfp->cf_params, sizeof (fmd_conf_param_t) * nparams); fmd_free(cfp, sizeof (fmd_conf_t)); } static fmd_conf_param_t * fmd_conf_getparam(fmd_conf_t *cfp, const char *name) { ulong_t h = fmd_strhash(name) % cfp->cf_parhashlen; fmd_conf_param_t *pp = cfp->cf_parhash[h]; ASSERT(RW_LOCK_HELD(&cfp->cf_lock)); for (; pp != NULL; pp = pp->cp_next) { if (strcmp(name, pp->cp_formal->cf_name) == 0) return (pp); } return (NULL); } /* * String-friendly version of fmd_conf_getprop(): return the string as our * return value, and return NULL if the string is the empty string. */ const char * fmd_conf_getnzstr(fmd_conf_t *cfp, const char *name) { const fmd_conf_param_t *pp; char *s = NULL; (void) pthread_rwlock_rdlock(&cfp->cf_lock); if ((pp = fmd_conf_getparam(cfp, name)) != NULL) { ASSERT(pp->cp_formal->cf_ops == &fmd_conf_string); pp->cp_formal->cf_ops->co_get(pp, &s); } else (void) fmd_set_errno(EFMD_CONF_NOPROP); (void) pthread_rwlock_unlock(&cfp->cf_lock); if (s != NULL && s[0] == '\0') { (void) fmd_set_errno(EFMD_CONF_UNDEF); s = NULL; } return (s); } const fmd_conf_ops_t * fmd_conf_gettype(fmd_conf_t *cfp, const char *name) { const fmd_conf_param_t *pp; const fmd_conf_ops_t *ops = NULL; (void) pthread_rwlock_rdlock(&cfp->cf_lock); if ((pp = fmd_conf_getparam(cfp, name)) != NULL) { if ((ops = pp->cp_formal->cf_ops) == &fmd_conf_parent) { ops = fmd_conf_gettype(fmd.d_conf, pp->cp_formal->cf_default); } } else (void) fmd_set_errno(EFMD_CONF_NOPROP); (void) pthread_rwlock_unlock(&cfp->cf_lock); return (ops); } int fmd_conf_getprop(fmd_conf_t *cfp, const char *name, void *data) { const fmd_conf_param_t *pp; int err = 0; (void) pthread_rwlock_rdlock(&cfp->cf_lock); if ((pp = fmd_conf_getparam(cfp, name)) != NULL) pp->cp_formal->cf_ops->co_get(pp, data); else err = fmd_set_errno(EFMD_CONF_NOPROP); (void) pthread_rwlock_unlock(&cfp->cf_lock); return (err); } static int fmd_conf_setdefer(fmd_conf_t *cfp, const char *name, const char *value) { fmd_conf_defer_t *cdp; if (!(cfp->cf_flag & FMD_CONF_DEFER)) return (fmd_set_errno(EFMD_CONF_NODEFER)); (void) pthread_rwlock_wrlock(&cfp->cf_lock); for (cdp = cfp->cf_defer; cdp != NULL; cdp = cdp->cd_next) { if (strcmp(name, cdp->cd_name) == 0) { fmd_strfree(cdp->cd_value); cdp->cd_value = fmd_strdup(value, FMD_SLEEP); goto out; } } cdp = fmd_alloc(sizeof (fmd_conf_defer_t), FMD_SLEEP); cdp->cd_name = fmd_strdup(name, FMD_SLEEP); cdp->cd_value = fmd_strdup(value, FMD_SLEEP); cdp->cd_next = cfp->cf_defer; cfp->cf_defer = cdp; out: (void) pthread_rwlock_unlock(&cfp->cf_lock); return (0); } int fmd_conf_setprop(fmd_conf_t *cfp, const char *name, const char *value) { fmd_conf_param_t *pp; int err; if (strchr(name, ':') != NULL) return (fmd_conf_setdefer(cfp, name, value)); (void) pthread_rwlock_wrlock(&cfp->cf_lock); if ((pp = fmd_conf_getparam(cfp, name)) != NULL) err = pp->cp_formal->cf_ops->co_set(pp, value); else err = fmd_set_errno(EFMD_CONF_NOPROP); (void) pthread_rwlock_unlock(&cfp->cf_lock); return (err); } int fmd_conf_delprop(fmd_conf_t *cfp, const char *name, const char *value) { fmd_conf_param_t *pp; int err; (void) pthread_rwlock_wrlock(&cfp->cf_lock); if ((pp = fmd_conf_getparam(cfp, name)) != NULL) err = pp->cp_formal->cf_ops->co_del(pp, value); else err = fmd_set_errno(EFMD_CONF_NOPROP); (void) pthread_rwlock_unlock(&cfp->cf_lock); return (err); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_CONF_H #define _FMD_CONF_H #include #ifdef __cplusplus extern "C" { #endif struct fmd_conf_param; typedef struct fmd_conf_ops { int (*co_set)(struct fmd_conf_param *, const char *); void (*co_get)(const struct fmd_conf_param *, void *); int (*co_del)(struct fmd_conf_param *, const char *); void (*co_free)(struct fmd_conf_param *); } fmd_conf_ops_t; typedef struct fmd_conf_formal { const char *cf_name; const fmd_conf_ops_t *cf_ops; const char *cf_default; } fmd_conf_formal_t; typedef struct fmd_conf_param { const fmd_conf_formal_t *cp_formal; struct fmd_conf_param *cp_next; union { uint64_t cpv_num; char *cpv_str; void *cpv_ptr; } cp_value; } fmd_conf_param_t; typedef struct fmd_conf_defer { char *cd_name; char *cd_value; struct fmd_conf_defer *cd_next; } fmd_conf_defer_t; typedef struct fmd_conf { pthread_rwlock_t cf_lock; const fmd_conf_formal_t *cf_argv; int cf_argc; uint_t cf_flag; fmd_conf_param_t *cf_params; fmd_conf_param_t **cf_parhash; uint_t cf_parhashlen; fmd_conf_defer_t *cf_defer; } fmd_conf_t; typedef struct fmd_conf_verb { const char *cv_name; int (*cv_exec)(fmd_conf_t *, int, char *[]); } fmd_conf_verb_t; typedef struct fmd_conf_path { const char **cpa_argv; int cpa_argc; } fmd_conf_path_t; typedef struct fmd_conf_mode { const char *cm_name; const char *cm_desc; uint_t cm_bits; } fmd_conf_mode_t; extern int fmd_conf_mode_set(const fmd_conf_mode_t *, fmd_conf_param_t *, const char *); extern void fmd_conf_mode_get(const fmd_conf_param_t *, void *); extern int fmd_conf_notsup(fmd_conf_param_t *, const char *); extern void fmd_conf_nop(fmd_conf_param_t *); extern const fmd_conf_ops_t fmd_conf_bool; /* int */ extern const fmd_conf_ops_t fmd_conf_int8; /* int8_t */ extern const fmd_conf_ops_t fmd_conf_uint8; /* uint8_t */ extern const fmd_conf_ops_t fmd_conf_int16; /* int16_t */ extern const fmd_conf_ops_t fmd_conf_uint16; /* uint16_t */ extern const fmd_conf_ops_t fmd_conf_int32; /* int32_t */ extern const fmd_conf_ops_t fmd_conf_uint32; /* uint32_t */ extern const fmd_conf_ops_t fmd_conf_int64; /* int64_t */ extern const fmd_conf_ops_t fmd_conf_uint64; /* uint64_t */ extern const fmd_conf_ops_t fmd_conf_string; /* const char* */ extern const fmd_conf_ops_t fmd_conf_path; /* fmd_conf_path_t* */ extern const fmd_conf_ops_t fmd_conf_list; /* fmd_conf_path_t* */ extern const fmd_conf_ops_t fmd_conf_time; /* hrtime_t */ extern const fmd_conf_ops_t fmd_conf_size; /* uint64_t */ extern const fmd_conf_ops_t fmd_conf_signal; /* int */ extern const fmd_conf_ops_t fmd_conf_parent; /* any */ extern const char FMD_PROP_SUBSCRIPTIONS[]; /* fmd_conf_list */ extern const char FMD_PROP_DICTIONARIES[]; /* fmd_conf_list */ #define FMD_CONF_DEFER 0x1 /* permit deferred settings */ extern fmd_conf_t *fmd_conf_open(const char *, int, const fmd_conf_formal_t *, uint_t); extern void fmd_conf_merge(fmd_conf_t *, const char *); extern void fmd_conf_propagate(fmd_conf_t *, fmd_conf_t *, const char *); extern void fmd_conf_close(fmd_conf_t *); extern const char *fmd_conf_getnzstr(fmd_conf_t *, const char *); extern const fmd_conf_ops_t *fmd_conf_gettype(fmd_conf_t *, const char *); extern int fmd_conf_getprop(fmd_conf_t *, const char *, void *); extern int fmd_conf_setprop(fmd_conf_t *, const char *, const char *); extern int fmd_conf_delprop(fmd_conf_t *, const char *, const char *); #ifdef __cplusplus } #endif #endif /* _FMD_CONF_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * FMD Control Event Subsystem * * This file provides a simple and extensible subsystem for the processing of * synchronous control events that can be received from the event transport * and used to control the behavior of the fault manager itself. At present * this feature is used for the implementation of simulation controls such as * advancing the simulated clock using events sent by the fminject utility. * Control events are assigned a class of the form "resource.fm.fmd.*" and * are assigned a callback function defined in the _fmd_ctls[] table below. * As control events are received by the event transport, they are assigned a * special event type (ev_type = FMD_EVT_CTL) and the ev_data member is used * to refer to a fmd_ctl_t data structure, managed by the functions below. * * Control events are implemented so that they are synchronous with respect to * the rest of the fault manager event stream, which is usually asynchronous * (that is, the transport dispatch thread and the module receive threads all * execute in parallel). Synchronous processing is required for control events * so that they can affect global state (e.g. the simulated clock) and ensure * that the results of any state changes are seen by *all* subsequent events. * * To achieve synchronization, the event itself implements a thread barrier: * the fmd_ctl_t maintains a reference count that mirrors the fmd_event_t * reference count (which for ctls counts the number of modules the event * was dispatched to). As each module receive thread dequeues the event, it * calls fmd_event_rele() to discard the event, which calls fmd_ctl_rele(). * fmd_ctl_rele() decrements the ctl's reference count but blocks there waiting * for *all* other references to be released. When all threads have reached * the barrier, the final caller of fmd_ctl_rele() executes the control event * callback function and then wakes everyone else up. The transport dispatch * thread, blocked in fmd_modhash_dispatch(), is typically this final caller. */ #include #include #include #include #include #include #include #include #include #include #include #include static void fmd_ctl_addhrt(nvlist_t *nvl) { int64_t delta = 0; (void) nvlist_lookup_int64(nvl, FMD_CTL_ADDHRT_DELTA, &delta); fmd_time_addhrtime(delta); /* * If the non-adjustable clock has reached the apocalypse, fmd(8) * should exit gracefully: queue a SIGTERM for the main thread. */ if (fmd_time_gethrtime() == INT64_MAX) (void) pthread_kill(fmd.d_rmod->mod_thread->thr_tid, SIGTERM); } static void fmd_ctl_inval(nvlist_t *nvl) { char *class = ""; (void) nvlist_lookup_string(nvl, FM_CLASS, &class); fmd_error(EFMD_CTL_INVAL, "ignoring invalid control event %s\n", class); } /*ARGSUSED*/ static void fmd_ctl_pause(nvlist_t *nvl) { fmd_dprintf(FMD_DBG_DISP, "unpausing modules from ctl barrier\n"); } static const fmd_ctl_desc_t _fmd_ctls[] = { { FMD_CTL_ADDHRT, FMD_CTL_ADDHRT_VERS1, fmd_ctl_addhrt }, { NULL, UINT_MAX, fmd_ctl_inval } }; fmd_ctl_t * fmd_ctl_init(nvlist_t *nvl) { fmd_ctl_t *cp = fmd_alloc(sizeof (fmd_ctl_t), FMD_SLEEP); const fmd_ctl_desc_t *dp; uint8_t vers; char *class; (void) pthread_mutex_init(&cp->ctl_lock, NULL); (void) pthread_cond_init(&cp->ctl_cv, NULL); cp->ctl_nvl = nvl; cp->ctl_refs = 0; if (nvl == NULL) { cp->ctl_func = fmd_ctl_pause; return (cp); } if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0 || nvlist_lookup_uint8(nvl, FM_VERSION, &vers) != 0) fmd_panic("ctl_init called with bad nvlist %p", (void *)nvl); for (dp = _fmd_ctls; dp->cde_class != NULL; dp++) { if (strcmp(class, dp->cde_class) == 0) break; } cp->ctl_func = vers > dp->cde_vers ? &fmd_ctl_inval : dp->cde_func; return (cp); } void fmd_ctl_fini(fmd_ctl_t *cp) { fmd_free(cp, sizeof (fmd_ctl_t)); } /* * Increment the ref count on the fmd_ctl_t to correspond to a reference to the * fmd_event_t. This count is used to implement a barrier in fmd_ctl_rele(). */ void fmd_ctl_hold(fmd_ctl_t *cp) { (void) pthread_mutex_lock(&cp->ctl_lock); cp->ctl_refs++; ASSERT(cp->ctl_refs != 0); (void) pthread_mutex_unlock(&cp->ctl_lock); } /* * Decrement the reference count on the fmd_ctl_t. If this rele() is the last * one, then execute the callback function and release all the other callers. * Otherwise enter a loop waiting on ctl_cv for other threads to call rele(). */ void fmd_ctl_rele(fmd_ctl_t *cp) { (void) pthread_mutex_lock(&cp->ctl_lock); ASSERT(cp->ctl_refs != 0); cp->ctl_refs--; if (cp->ctl_refs == 0) { cp->ctl_func(cp->ctl_nvl); (void) pthread_cond_broadcast(&cp->ctl_cv); } else { while (cp->ctl_refs != 0) (void) pthread_cond_wait(&cp->ctl_cv, &cp->ctl_lock); } (void) pthread_mutex_unlock(&cp->ctl_lock); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_CTL_H #define _FMD_CTL_H #include #include #ifdef __cplusplus extern "C" { #endif typedef void fmd_ctl_f(nvlist_t *); typedef struct fmd_ctl_desc { const char *cde_class; /* protocol event class */ uint_t cde_vers; /* protocol event version */ fmd_ctl_f *cde_func; /* callback function */ } fmd_ctl_desc_t; typedef struct fmd_ctl { pthread_mutex_t ctl_lock; /* lock for ctl_exec and ctl_cv */ pthread_cond_t ctl_cv; /* condition variable for ctl_exec */ fmd_ctl_f *ctl_func; /* type-specific callback function */ nvlist_t *ctl_nvl; /* name-value pair list from event */ uint_t ctl_refs; /* reference count for barrier */ } fmd_ctl_t; extern fmd_ctl_t *fmd_ctl_init(nvlist_t *); extern void fmd_ctl_fini(fmd_ctl_t *); extern void fmd_ctl_hold(fmd_ctl_t *); extern void fmd_ctl_rele(fmd_ctl_t *); #ifdef __cplusplus } #endif #endif /* _FMD_CTL_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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include static fmd_dispqelem_t * fmd_dispqelem_create(const char *name) { fmd_dispqelem_t *dep = fmd_alloc(sizeof (fmd_dispqelem_t), FMD_SLEEP); dep->dq_name = fmd_strdup(name, FMD_SLEEP); dep->dq_link = NULL; dep->dq_hashlen = fmd.d_str_buckets; dep->dq_hash = fmd_zalloc(sizeof (void *) * dep->dq_hashlen, FMD_SLEEP); dep->dq_list = NULL; dep->dq_refs = 0; return (dep); } static void fmd_dispqelem_destroy(fmd_dispqelem_t *dep) { fmd_dispqlist_t *dlp, *nlp; fmd_dispqelem_t *p, *q; uint_t i; for (dlp = dep->dq_list; dlp != NULL; dlp = nlp) { nlp = dlp->dq_next; fmd_free(dlp, sizeof (fmd_dispqlist_t)); } for (i = 0; i < dep->dq_hashlen; i++) { for (p = dep->dq_hash[i]; p != NULL; p = q) { q = p->dq_link; fmd_dispqelem_destroy(p); } } fmd_free(dep->dq_hash, sizeof (void *) * dep->dq_hashlen); fmd_strfree(dep->dq_name); fmd_free(dep, sizeof (fmd_dispqelem_t)); } static fmd_dispqelem_t * fmd_dispqelem_lookup(fmd_dispqelem_t *dep, const char *name) { uint_t h = fmd_strhash(name) % dep->dq_hashlen; for (dep = dep->dq_hash[h]; dep != NULL; dep = dep->dq_link) { if (strcmp(dep->dq_name, name) == 0) break; } return (dep); } fmd_dispq_t * fmd_dispq_create(void) { fmd_dispq_t *dqp = fmd_alloc(sizeof (fmd_dispq_t), FMD_SLEEP); (void) pthread_rwlock_init(&dqp->dq_lock, NULL); dqp->dq_root = fmd_dispqelem_create(NULL); dqp->dq_gids = fmd_idspace_create("dispq_gids", 1, INT_MAX); dqp->dq_gmax = 0; return (dqp); } void fmd_dispq_destroy(fmd_dispq_t *dqp) { fmd_dispqelem_destroy(dqp->dq_root); fmd_idspace_destroy(dqp->dq_gids); fmd_free(dqp, sizeof (fmd_dispq_t)); } static fmd_dispqelem_t * fmd_dispq_insert_one(fmd_dispqelem_t *dep, const char *name) { uint_t h = fmd_strhash(name) % dep->dq_hashlen; fmd_dispqelem_t *ep; for (ep = dep->dq_hash[h]; ep != NULL; ep = ep->dq_link) { if (strcmp(ep->dq_name, name) == 0) break; } if (ep == NULL) { ep = fmd_dispqelem_create(name); ep->dq_link = dep->dq_hash[h]; dep->dq_hash[h] = ep; dep->dq_refs++; ASSERT(dep->dq_refs != 0); } return (ep); } void fmd_dispq_insert(fmd_dispq_t *dqp, fmd_eventq_t *eqp, const char *pattern) { char *p, *q, *s = fmd_strdup(pattern, FMD_SLEEP); size_t len = strlen(s); fmd_dispqlist_t *dlp = fmd_alloc(sizeof (fmd_dispqlist_t), FMD_SLEEP); fmd_dispqelem_t *dep; (void) pthread_rwlock_wrlock(&dqp->dq_lock); dep = dqp->dq_root; for (p = strtok_r(s, ".", &q); p != NULL; p = strtok_r(NULL, ".", &q)) dep = fmd_dispq_insert_one(dep, p); dlp->dq_next = dep->dq_list; dlp->dq_eventq = eqp; dep->dq_list = dlp; dep->dq_refs++; ASSERT(dep->dq_refs != 0); (void) pthread_rwlock_unlock(&dqp->dq_lock); fmd_free(s, len + 1); } static void fmd_dispq_delete_one(fmd_dispqelem_t *dep, fmd_eventq_t *eqp, int patc, char *patv[]) { fmd_dispqlist_t *lp, **lpp; fmd_dispqelem_t *ep, **epp; uint_t h = fmd_strhash(patv[0]) % dep->dq_hashlen; epp = &dep->dq_hash[h]; for (ep = *epp; ep != NULL; ep = ep->dq_link) { if (strcmp(ep->dq_name, patv[0]) != 0) epp = &ep->dq_link; else break; } ASSERT(ep != NULL); lpp = &ep->dq_list; if (patc > 1) { fmd_dispq_delete_one(ep, eqp, patc - 1, patv + 1); } else { for (lp = *lpp; lp != NULL; lp = lp->dq_next) { if (lp->dq_eventq != eqp) lpp = &lp->dq_next; else break; } if (lp != NULL) { *lpp = lp->dq_next; fmd_free(lp, sizeof (fmd_dispqlist_t)); ASSERT(ep->dq_refs != 0); ep->dq_refs--; } } if (ep->dq_refs == 0) { *epp = ep->dq_link; fmd_dispqelem_destroy(ep); ASSERT(dep->dq_refs != 0); dep->dq_refs--; } } void fmd_dispq_delete(fmd_dispq_t *dqp, fmd_eventq_t *eqp, const char *pattern) { char *p, *q, *s = fmd_strdup(pattern, FMD_SLEEP); size_t len = strlen(s); char **patv = fmd_zalloc(sizeof (char *) * (len / 2 + 1), FMD_SLEEP); int patc = 0; for (p = strtok_r(s, ".", &q); p != NULL; p = strtok_r(NULL, ".", &q)) patv[patc++] = p; if (patc != 0) { (void) pthread_rwlock_wrlock(&dqp->dq_lock); fmd_dispq_delete_one(dqp->dq_root, eqp, patc, patv); (void) pthread_rwlock_unlock(&dqp->dq_lock); } fmd_free(patv, sizeof (char *) * (len / 2 + 1)); fmd_free(s, len + 1); } static uint_t fmd_dispq_dispatch_one(fmd_dispqelem_t *dep, ulong_t *gids, fmd_event_t *ep, const char *class) { fmd_dispqlist_t *dlp; uint_t n = 0; for (dlp = dep->dq_list; dlp != NULL; dlp = dlp->dq_next, n++) { id_t gid = dlp->dq_eventq->eq_sgid; if (BT_TEST(gids, gid) != 0) continue; /* event already queued for this group ID */ TRACE((FMD_DBG_DISP, "queue %p (%s) for %s (%d)", (void *)ep, class, dlp->dq_eventq->eq_mod->mod_name, (int)gid)); fmd_eventq_insert_at_time(dlp->dq_eventq, ep); BT_SET(gids, gid); } return (n); } /* * This function handles the descent of the dispatch queue hash tree on behalf * of fmd_dispq_dispatch(). We recursively descend the tree along two paths: * one using the next component of the split class string (stored in cv[0]) and * one using the wildcard "*" in place of cv[0]. If we can't find either one, * our descent stops. If we descend far enough to consume cv[] (i.e. cc == 0), * then we have a match and we dispatch the event to all modules at that level. * We also dispatch the event to modules found at any interior "*" element, * allowing a subscription to "a.*" to match "a.b", "a.b.c", and so on. */ static uint_t fmd_dispq_dispatchv(fmd_dispqelem_t *root, ulong_t *gids, fmd_event_t *ep, const char *class, uint_t cc, char *cv[]) { fmd_dispqelem_t *dep; uint_t n = 0; if (cc == 0) return (fmd_dispq_dispatch_one(root, gids, ep, class)); if ((dep = fmd_dispqelem_lookup(root, cv[0])) != NULL) n += fmd_dispq_dispatchv(dep, gids, ep, class, cc - 1, cv + 1); if ((dep = fmd_dispqelem_lookup(root, "*")) != NULL) n += fmd_dispq_dispatchv(dep, gids, ep, class, cc - 1, cv + 1); if (dep != NULL && cc > 1) n += fmd_dispq_dispatch_one(dep, gids, ep, class); return (n); } static uint_t fmd_dispq_tokenize(const char *class, char *buf, size_t buflen, char **cv, uint_t cvlen) { uint_t cc = 0; char *p, *q; (void) strlcpy(buf, class, buflen); for (p = strtok_r(buf, ".", &q); p != NULL; p = strtok_r(NULL, ".", &q)) cv[cc++] = p; if (cc > cvlen) fmd_panic("fmd_dispq_tokenize() cc=%u > cv[%u]\n", cc, cvlen); return (cc); } void fmd_dispq_dispatch_gid(fmd_dispq_t *dqp, fmd_event_t *ep, const char *class, id_t gid) { size_t cvbuflen = strlen(class) + 1; uint_t cc, cvlen, n = 0; char *c, *cvbuf, **cv; ulong_t *gids; uint_t glen, i; nvlist_t **nva; uint_t nvi, nvc = 0; fmd_event_hold(ep); /* * If the event is a protocol list.suspect event with one or more * events contained inside of it, determine the maximum length of all * class strings that will be used in this dispatch operation. */ if (FMD_EVENT_TYPE(ep) == FMD_EVT_PROTOCOL && (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0 || strcmp(class, FM_LIST_REPAIRED_CLASS) == 0 || strcmp(class, FM_LIST_RESOLVED_CLASS) == 0 || strcmp(class, FM_LIST_UPDATED_CLASS) == 0) && nvlist_lookup_nvlist_array(FMD_EVENT_NVL(ep), FM_SUSPECT_FAULT_LIST, &nva, &nvc) == 0) { for (nvi = 0; nvi < nvc; nvi++) { if (nvlist_lookup_string(nva[nvi], FM_CLASS, &c) == 0) { size_t len = strlen(c) + 1; cvbuflen = MAX(cvbuflen, len); } } } cvbuf = alloca(cvbuflen); cvlen = cvbuflen / 2 + 1; cv = alloca(sizeof (char *) * cvlen); /* * With dq_lock held as reader, allocate a bitmap on the stack for * group IDs for this dispatch, zero it, and then do the dispatch. */ (void) pthread_rwlock_rdlock(&dqp->dq_lock); glen = BT_BITOUL(dqp->dq_gmax); gids = alloca(sizeof (ulong_t) * glen); bzero(gids, sizeof (ulong_t) * glen); /* * If we are dispatching to only a single gid, set all bits in the * group IDs mask and then clear only the bit for the specified gid. */ if (gid >= 0) { for (i = 0; i < glen; i++) gids[i] = BT_ULMAXMASK; BT_CLEAR(gids, gid); } for (nvi = 0; nvi < nvc; nvi++) { if (nvlist_lookup_string(nva[nvi], FM_CLASS, &c) == 0) { cc = fmd_dispq_tokenize(c, cvbuf, cvbuflen, cv, cvlen); n += fmd_dispq_dispatchv(dqp->dq_root, gids, ep, c, cc, cv); } } cc = fmd_dispq_tokenize(class, cvbuf, cvbuflen, cv, cvlen); n += fmd_dispq_dispatchv(dqp->dq_root, gids, ep, class, cc, cv); (void) pthread_rwlock_unlock(&dqp->dq_lock); fmd_dprintf(FMD_DBG_DISP, "%s dispatched to %u queues\n", class, n); /* * If the total subscriptions matched (n) was zero and we're not being * called for a single gid, send the event to the self-diagnosis module. */ if (n == 0 && gid < 0 && fmd.d_self != NULL) fmd_eventq_insert_at_time(fmd.d_self->mod_queue, ep); fmd_event_rele(ep); } void fmd_dispq_dispatch(fmd_dispq_t *dqp, fmd_event_t *ep, const char *class) { fmd_dispq_dispatch_gid(dqp, ep, class, -1); } id_t fmd_dispq_getgid(fmd_dispq_t *dqp, void *cookie) { id_t gid; (void) pthread_rwlock_wrlock(&dqp->dq_lock); gid = fmd_idspace_alloc_min(dqp->dq_gids, cookie); dqp->dq_gmax = MAX(dqp->dq_gmax, gid); (void) pthread_rwlock_unlock(&dqp->dq_lock); return (gid); } void fmd_dispq_delgid(fmd_dispq_t *dqp, id_t gid) { (void) pthread_rwlock_wrlock(&dqp->dq_lock); ASSERT(fmd_idspace_contains(dqp->dq_gids, gid)); (void) fmd_idspace_free(dqp->dq_gids, gid); (void) pthread_rwlock_unlock(&dqp->dq_lock); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_DISPQ_H #define _FMD_DISPQ_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include typedef struct fmd_dispqlist { struct fmd_dispqlist *dq_next; /* link to next subscription object */ fmd_eventq_t *dq_eventq; /* pointer to subscribing eventq */ } fmd_dispqlist_t; typedef struct fmd_dispqelem { char *dq_name; /* name associated with this element */ struct fmd_dispqelem *dq_link; /* link to next element in hash chain */ struct fmd_dispqelem **dq_hash; /* hash bucket array for lower levels */ uint_t dq_hashlen; /* length of dq_hash bucket array */ fmd_dispqlist_t *dq_list; /* head of list of subscribers */ uint_t dq_refs; /* ref count of hash and list elems */ } fmd_dispqelem_t; typedef struct fmd_dispq { pthread_rwlock_t dq_lock; /* lock for event dispatch queue */ fmd_dispqelem_t *dq_root; /* root hash table pointer */ fmd_idspace_t *dq_gids; /* id hash for subscriber group ids */ id_t dq_gmax; /* maximum group id allocated */ } fmd_dispq_t; extern fmd_dispq_t *fmd_dispq_create(void); extern void fmd_dispq_destroy(fmd_dispq_t *); extern void fmd_dispq_insert(fmd_dispq_t *, fmd_eventq_t *, const char *); extern void fmd_dispq_delete(fmd_dispq_t *, fmd_eventq_t *, const char *); extern void fmd_dispq_dispatch(fmd_dispq_t *, fmd_event_t *, const char *); extern void fmd_dispq_dispatch_gid(fmd_dispq_t *, fmd_event_t *, const char *, id_t); extern id_t fmd_dispq_getgid(fmd_dispq_t *, void *); extern void fmd_dispq_delgid(fmd_dispq_t *, id_t); #ifdef __cplusplus } #endif #endif /* _FMD_DISPQ_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * FMD Dynamic Reconfiguration (DR) Event Handling * * Fault manager scheme plug-ins must track characteristics of individual * pieces of hardware. As these components can be added or removed by a DR * operation, we need to provide a means by which plug-ins can determine when * they need to re-examine the current configuration. We provide a simple * mechanism whereby this task can be implemented using lazy evaluation: a * simple 64-bit generation counter is maintained and incremented on *any* DR. * Schemes can store the generation number in scheme-specific data structures, * and then revalidate their contents if the current generation number has * changed since the resource information was cached. This method saves time, * avoids the complexity of direct participation in DR, avoids the need for * resource-specific processing of DR events, and is relatively easy to port * to other systems that support dynamic reconfiguration. * * The dr generation is only incremented in response to hardware changes. Since * ASRUs can be in any scheme, including the device scheme, we must also be * aware of software configuration changes which may affect the resource cache. * In addition, we take a snapshot of the topology whenever a reconfiguration * event occurs and notify any modules of the change. */ #include #include #include #include #include #include #include #include #undef MUTEX_HELD #undef RW_READ_HELD #undef RW_WRITE_HELD #include #include #include #include #include #include #include #include void fmd_dr_event(sysevent_t *sep) { uint64_t gen; fmd_event_t *e; const char *class = sysevent_get_class_name(sep); const char *subclass = sysevent_get_subclass_name(sep); hrtime_t evtime; fmd_topo_t *ftp, *prev; if (strcmp(class, EC_DR) == 0) { if (strcmp(subclass, ESC_DR_AP_STATE_CHANGE) != 0 && strcmp(subclass, ESC_DR_TARGET_STATE_CHANGE) != 0) return; /* LINTED: E_NOP_IF_STMT */ } else if (strcmp(class, EC_DEVFS) == 0) { /* * A devfs configuration event can change the topology, * as disk nodes only exist when the device is configured. */ } else if (strcmp(class, EC_PLATFORM) == 0) { /* * Since we rely on the SP to enumerate fans, * power-supplies and sensors/leds, it would be prudent * to take a new snapshot if the SP resets. */ if (strcmp(subclass, ESC_PLATFORM_SP_RESET) != 0) return; } else if (strcmp(class, EC_DEV_ADD) == 0 || strcmp(class, EC_DEV_REMOVE) == 0) { if (strcmp(subclass, ESC_DISK) != 0) return; } else return; /* * Take a topo snapshot and notify modules of the change. Picking an * accurate time here is difficult. On one hand, we have the timestamp * of the underlying sysevent, indicating when the reconfiguration event * occurred. On the other hand, we are taking the topo snapshot * asynchronously, and hence the timestamp of the snapshot is the * current time. Pretending this topo snapshot was valid at the time * the sysevent was posted seems wrong, so we instead opt for the * current time as an upper bound on the snapshot validity. * * Along these lines, we keep track of the last time we dispatched a * topo snapshot. If the sysevent occurred before the last topo * snapshot, then don't bother dispatching another topo change event. * We've already indicated (to the best of our ability) the change in * topology. This prevents endless topo snapshots in response to a * flurry of sysevents. */ sysevent_get_time(sep, &evtime); prev = fmd_topo_hold(); if (evtime <= prev->ft_time_begin && fmd.d_clockops == &fmd_timeops_native) { fmd_topo_rele(prev); return; } fmd_topo_rele(prev); (void) pthread_mutex_lock(&fmd.d_stats_lock); gen = fmd.d_stats->ds_dr_gen.fmds_value.ui64++; (void) pthread_mutex_unlock(&fmd.d_stats_lock); TRACE((FMD_DBG_XPRT, "dr event %p, gen=%llu", (void *)sep, gen)); fmd_topo_update(); ftp = fmd_topo_hold(); e = fmd_event_create(FMD_EVT_TOPO, ftp->ft_time_end, NULL, ftp); fmd_modhash_dispatch(fmd.d_mod_hash, e); } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_DR_H #define _FMD_DR_H #include #ifdef __cplusplus extern "C" { #endif extern void fmd_dr_event(sysevent_t *); #ifdef __cplusplus } #endif #endif /* _FMD_DR_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_ERROR_H #define _FMD_ERROR_H #include #ifdef __cplusplus extern "C" { #endif /* * This enum definition is used to define a set of error tags associated with * the fmd daemon's various error conditions. The shell script mkerror.sh is * used to parse this file and create a corresponding fmd_error.c source file. * If you do something other than add a new error tag here, you may need to * update the mkerror shell script as it is based upon simple regexps. */ typedef enum fmd_errno { EFMD_UNKNOWN = 1000, /* unknown fault management daemon error */ EFMD_PANIC, /* unrecoverable fatal error in daemon occurred */ EFMD_EXIT, /* failed to initialize fault management daemon */ EFMD_MODULE, /* fmd module detected or caused an error */ EFMD_CONF_OPEN, /* failed to open configuration file */ EFMD_CONF_KEYWORD, /* invalid configuration file keyword */ EFMD_CONF_NOPROP, /* invalid configuration file parameter name */ EFMD_CONF_NODEFER, /* deferred properties not permitted in this file */ EFMD_CONF_PROPDUP, /* duplicate configuration file parameter name */ EFMD_CONF_INVAL, /* invalid value for configuration file property */ EFMD_CONF_OVERFLOW, /* configuration value too large for data type */ EFMD_CONF_USAGE, /* syntax error in configuration file directive */ EFMD_CONF_DEFAULT, /* invalid default value for configuration property */ EFMD_CONF_ERRS, /* error(s) detected in configuration file */ EFMD_CONF_IO, /* i/o error prevented configuration file processing */ EFMD_CONF_PROPNAME, /* configuration property name is not an identifier */ EFMD_CONF_RDONLY, /* configuration property is read-only */ EFMD_CONF_DEFER, /* invalid deferred configuration file property */ EFMD_CONF_UNDEF, /* configuration property is not defined */ EFMD_MOD_INIT, /* failed to initialize module */ EFMD_MOD_FINI, /* failed to uninitialize module */ EFMD_MOD_THR, /* failed to create processing thread for module */ EFMD_MOD_JOIN, /* failed to join processing thread for module */ EFMD_MOD_CONF, /* error(s) detected in module configuration file */ EFMD_MOD_DICT, /* failed to open module's event code dictionary */ EFMD_MOD_LOADED, /* specified module is already loaded */ EFMD_MOD_NOMOD, /* specified module is not loaded */ EFMD_MOD_FAIL, /* module failed due to preceding error */ EFMD_MOD_TOPO, /* failed to obtain topology handle */ EFMD_RTLD_OPEN, /* rtld failed to open shared library plug-in */ EFMD_RTLD_INIT, /* shared library plug-in does not define _fmd_init */ EFMD_BLTIN_NAME, /* built-in plug-in name not found in definition list */ EFMD_BLTIN_INIT, /* built-in plug-in does not define init function */ EFMD_EVENT_INVAL, /* event interface programming error */ EFMD_XPRT_INVAL, /* transport interface programming error */ EFMD_XPRT_PAYLOAD, /* transport event has invalid payload */ EFMD_XPRT_OWNER, /* transport can only be manipulated by owner */ EFMD_XPRT_THR, /* failed to create thread for transport */ EFMD_XPRT_LIMIT, /* limit on number of open transports exceeded */ EFMD_TIME_GETTOD, /* failed to get current time-of-day */ EFMD_LOG_OPEN, /* failed to open and initialize log file */ EFMD_LOG_CLOSE, /* failed to close log file */ EFMD_LOG_EXACCT, /* failed to perform log exacct operation */ EFMD_LOG_APPEND, /* failed to append event to log */ EFMD_LOG_MINFREE, /* insufficient min fs space to append event to log */ EFMD_LOG_COMMIT, /* failed to commit event to log */ EFMD_LOG_INVAL, /* invalid log header information */ EFMD_LOG_VERSION, /* invalid log version information */ EFMD_LOG_UNPACK, /* failed to unpack data in log */ EFMD_LOG_REPLAY, /* failed to replay log content */ EFMD_LOG_UPDATE, /* failed to update log toc */ EFMD_LOG_ROTATE, /* failed to rotate log file */ EFMD_LOG_ROTBUSY, /* failed to rotate log file due to pending events */ EFMD_ASRU_NODIR, /* failed to open asru cache directory */ EFMD_ASRU_EVENT, /* failed to process asru event log */ EFMD_ASRU_FMRI, /* failed to convert asru fmri to string */ EFMD_ASRU_NOENT, /* failed to locate specified asru entry */ EFMD_ASRU_UNLINK, /* failed to delete asru cache entry */ EFMD_ASRU_DUP, /* asru log is a duplicate of an existing asru */ EFMD_FMRI_SCHEME, /* fmri scheme module is missing or failed to load */ EFMD_FMRI_OP, /* fmri scheme module operation failed */ EFMD_FMRI_INVAL, /* fmri nvlist is missing required element */ EFMD_FMRI_NOTSUP, /* fmri scheme module does not support operation */ EFMD_VER_OLD, /* plug-in is compiled using an obsolete fmd API */ EFMD_VER_NEW, /* plug-in is compiled using a newer fmd API */ EFMD_HDL_INIT, /* client handle wasn't initialized by _fmd_init */ EFMD_HDL_INFO, /* client info is missing required information */ EFMD_HDL_PROP, /* client info includes invalid property definition */ EFMD_HDL_NOTREG, /* client handle has never been registered */ EFMD_HDL_REG, /* client handle has already been registered */ EFMD_HDL_TID, /* client handle must be registered by owner */ EFMD_HDL_INVAL, /* client handle is corrupt or not owned by caller */ EFMD_HDL_ABORT, /* client requested that module execution abort */ EFMD_HDL_NOMEM, /* client memory limit exceeded */ EFMD_PROP_TYPE, /* property accessed using incompatible type */ EFMD_PROP_DEFN, /* property is not defined */ EFMD_STAT_FLAGS, /* invalid flags passed to fmd_stat_* function */ EFMD_STAT_TYPE, /* invalid operation for statistic type */ EFMD_STAT_BADTYPE, /* invalid type for statistic */ EFMD_STAT_BADNAME, /* invalid name for statistic */ EFMD_STAT_DUPNAME, /* statistic name is already defined in collection */ EFMD_STAT_NOMEM, /* failed to allocate memory for statistics snapshot */ EFMD_CASE_OWNER, /* case can only be manipulated or closed by owner */ EFMD_CASE_STATE, /* case is not in appropriate state for operation */ EFMD_CASE_EVENT, /* case operation failed due to invalid event */ EFMD_CASE_INVAL, /* case uuid does not match any known case */ EFMD_BUF_INVAL, /* buffer specification uses invalid name or size */ EFMD_BUF_LIMIT, /* client exceeded limit on total buffer space */ EFMD_BUF_NOENT, /* no such buffer is currently defined by client */ EFMD_BUF_OFLOW, /* write would overflow the size of this buffer */ EFMD_BUF_EXISTS, /* buffer with the specified name already exists */ EFMD_SERD_NAME, /* no serd engine with the specified name exists */ EFMD_SERD_EXISTS, /* serd engine with the specified name already exists */ EFMD_THR_CREATE, /* failed to create auxiliary module thread */ EFMD_THR_LIMIT, /* limit on module auxiliary threads exceeded */ EFMD_THR_INVAL, /* invalid thread id specified for thread call */ EFMD_THR_JOIN, /* failed to join with auxiliary thread */ EFMD_TIMER_INVAL, /* invalid time delta or id specified for timer call */ EFMD_TIMER_LIMIT, /* client exceeded limit on number of pending timers */ EFMD_CKPT_NOMEM, /* failed to allocate checkpoint buffer */ EFMD_CKPT_MKDIR, /* failed to create checkpoint directory */ EFMD_CKPT_CREATE, /* failed to create checkpoint file */ EFMD_CKPT_COMMIT, /* failed to commit checkpoint file */ EFMD_CKPT_DELETE, /* failed to delete checkpoint file */ EFMD_CKPT_OPEN, /* failed to open checkpoint file */ EFMD_CKPT_SHORT, /* checkpoint file has been truncated or corrupted */ EFMD_CKPT_INVAL, /* checkpoint file has invalid header or content */ EFMD_CKPT_RESTORE, /* failed to restore checkpoint file */ EFMD_RPC_REG, /* failed to register rpc service */ EFMD_RPC_BOUND, /* rpc program/version is already bound */ EFMD_NVL_INVAL, /* invalid nvlist function argument */ EFMD_CTL_INVAL, /* invalid fault manager control event */ EFMD_END /* end of custom errno list (to ease auto-merge) */ } fmd_errno_t; extern const char *fmd_errclass(int); extern const char *fmd_strerror(int); extern int fmd_set_errno(int); #ifdef __cplusplus } #endif #endif /* _FMD_ERROR_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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static void fmd_event_nvwrap(fmd_event_impl_t *ep) { (void) nvlist_remove_all(ep->ev_nvl, FMD_EVN_TTL); (void) nvlist_remove_all(ep->ev_nvl, FMD_EVN_TOD); (void) nvlist_add_uint8(ep->ev_nvl, FMD_EVN_TTL, ep->ev_ttl); (void) nvlist_add_uint64_array(ep->ev_nvl, FMD_EVN_TOD, (uint64_t *)&ep->ev_time, 2); } static void fmd_event_nvunwrap(fmd_event_impl_t *ep, const fmd_timeval_t *tp) { uint64_t *tod; uint_t n; if (nvlist_lookup_uint8(ep->ev_nvl, FMD_EVN_TTL, &ep->ev_ttl) != 0) { ep->ev_flags |= FMD_EVF_LOCAL; ep->ev_ttl = (uint8_t)fmd.d_xprt_ttl; } if (tp != NULL) ep->ev_time = *tp; else if (nvlist_lookup_uint64_array(ep->ev_nvl, FMD_EVN_TOD, &tod, &n) == 0 && n >= 2) ep->ev_time = *(const fmd_timeval_t *)tod; else fmd_time_sync(&ep->ev_time, &ep->ev_hrt, 1); } fmd_event_t * fmd_event_recreate(uint_t type, const fmd_timeval_t *tp, nvlist_t *nvl, void *data, fmd_log_t *lp, off64_t off, size_t len) { fmd_event_impl_t *ep = fmd_alloc(sizeof (fmd_event_impl_t), FMD_SLEEP); fmd_timeval_t tod; hrtime_t hr0; (void) pthread_mutex_init(&ep->ev_lock, NULL); ep->ev_refs = 0; ASSERT(type < FMD_EVT_NTYPES); ep->ev_type = (uint8_t)type; ep->ev_state = FMD_EVS_RECEIVED; ep->ev_flags = FMD_EVF_REPLAY; ep->ev_nvl = nvl; ep->ev_data = data; ep->ev_log = lp; ep->ev_off = off; ep->ev_len = len; fmd_event_nvunwrap(ep, tp); /* * If we're not restoring from a log, the event is marked volatile. If * we are restoring from a log, then hold the log pointer and increment * the pending count. If we're using a log but no offset and data len * are specified, it's a checkpoint event: don't replay or set pending. */ if (lp == NULL) ep->ev_flags |= FMD_EVF_VOLATILE; else if (off != 0 && len != 0) fmd_log_hold_pending(lp); else { ep->ev_flags &= ~FMD_EVF_REPLAY; fmd_log_hold(lp); } /* * Sample a (TOD, hrtime) pair from the current system clocks and then * compute ev_hrt by taking the delta between this TOD and ev_time. */ fmd_time_sync(&tod, &hr0, 1); fmd_time_tod2hrt(hr0, &tod, &ep->ev_time, &ep->ev_hrt); fmd_event_nvwrap(ep); return ((fmd_event_t *)ep); } fmd_event_t * fmd_event_create(uint_t type, hrtime_t hrt, nvlist_t *nvl, void *data) { fmd_event_impl_t *ep = fmd_alloc(sizeof (fmd_event_impl_t), FMD_SLEEP); fmd_timeval_t tod; hrtime_t hr0; const char *p; uint64_t ena; (void) pthread_mutex_init(&ep->ev_lock, NULL); ep->ev_refs = 0; ASSERT(type < FMD_EVT_NTYPES); ep->ev_type = (uint8_t)type; ep->ev_state = FMD_EVS_RECEIVED; ep->ev_flags = FMD_EVF_VOLATILE | FMD_EVF_REPLAY | FMD_EVF_LOCAL; ep->ev_ttl = (uint8_t)fmd.d_xprt_ttl; ep->ev_nvl = nvl; ep->ev_data = data; ep->ev_log = NULL; ep->ev_off = 0; ep->ev_len = 0; /* * Sample TOD and then set ev_time to the earlier TOD corresponding to * the input hrtime value. This needs to be improved later: hrestime * should be sampled by the transport and passed as an input parameter. */ fmd_time_sync(&tod, &hr0, 1); if (hrt == FMD_HRT_NOW) hrt = hr0; /* use hrtime sampled by fmd_time_sync() */ /* * If this is an FMA protocol event of class "ereport.*" that contains * valid ENA, we can compute a more precise bound on the event time. */ if (type == FMD_EVT_PROTOCOL && (p = strchr(data, '.')) != NULL && strncmp(data, FM_EREPORT_CLASS, (size_t)(p - (char *)data)) == 0 && nvlist_lookup_uint64(nvl, FM_EREPORT_ENA, &ena) == 0 && fmd.d_clockops == &fmd_timeops_native) hrt = fmd_time_ena2hrt(hrt, ena); fmd_time_hrt2tod(hr0, &tod, hrt, &ep->ev_time); ep->ev_hrt = hrt; fmd_event_nvwrap(ep); return ((fmd_event_t *)ep); } void fmd_event_destroy(fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; ASSERT(MUTEX_HELD(&ep->ev_lock)); ASSERT(ep->ev_refs == 0); /* * If the current state is RECEIVED (i.e. no module has accepted the * event) and the event was logged, then change the state to DISCARDED. */ if (ep->ev_state == FMD_EVS_RECEIVED) ep->ev_state = FMD_EVS_DISCARDED; /* * If the current state is DISCARDED, ACCEPTED, or DIAGNOSED and the * event has not yet been commited, then attempt to commit it now. */ if (ep->ev_state != FMD_EVS_RECEIVED && (ep->ev_flags & ( FMD_EVF_VOLATILE | FMD_EVF_REPLAY)) == FMD_EVF_REPLAY) fmd_log_commit(ep->ev_log, e); if (ep->ev_log != NULL) { if (ep->ev_flags & FMD_EVF_REPLAY) fmd_log_decommit(ep->ev_log, e); fmd_log_rele(ep->ev_log); } /* * Perform any event type-specific cleanup activities, and then free * the name-value pair list and underlying event data structure. */ switch (ep->ev_type) { case FMD_EVT_TIMEOUT: fmd_free(ep->ev_data, sizeof (fmd_modtimer_t)); break; case FMD_EVT_CLOSE: case FMD_EVT_PUBLISH: fmd_case_rele(ep->ev_data); break; case FMD_EVT_CTL: fmd_ctl_fini(ep->ev_data); break; case FMD_EVT_TOPO: fmd_topo_rele(ep->ev_data); break; } nvlist_free(ep->ev_nvl); fmd_free(ep, sizeof (fmd_event_impl_t)); } void fmd_event_hold(fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; (void) pthread_mutex_lock(&ep->ev_lock); ep->ev_refs++; ASSERT(ep->ev_refs != 0); (void) pthread_mutex_unlock(&ep->ev_lock); if (ep->ev_type == FMD_EVT_CTL) fmd_ctl_hold(ep->ev_data); } void fmd_event_rele(fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; if (ep->ev_type == FMD_EVT_CTL) fmd_ctl_rele(ep->ev_data); (void) pthread_mutex_lock(&ep->ev_lock); ASSERT(ep->ev_refs != 0); if (--ep->ev_refs == 0) fmd_event_destroy(e); else (void) pthread_mutex_unlock(&ep->ev_lock); } /* * Transition event from its current state to the specified state. The states * for events are defined in fmd_event.h and work according to the diagram: * * ------------- ------------- State Description * ( RECEIVED =1 )-->( ACCEPTED =2 ) ---------- --------------------------- * -----+-------\ ------+------ DISCARDED No active references in fmd * | \ | RECEIVED Active refs in fmd, no case * -----v------- \ ------v------ ACCEPTED Active refs, case assigned * ( DISCARDED=0 ) v( DIAGNOSED=3 ) DIAGNOSED Active refs, case solved * ------------- ------------- * * Since events are reference counted on behalf of multiple subscribers, any * attempt to transition an event to an "earlier" or "equal" state (as defined * by the numeric state values shown in the diagram) is silently ignored. * An event begins life in the RECEIVED state, so the RECEIVED -> DISCARDED * transition is handled by fmd_event_destroy() when no references remain. */ void fmd_event_transition(fmd_event_t *e, uint_t state) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; (void) pthread_mutex_lock(&ep->ev_lock); TRACE((FMD_DBG_EVT, "event %p transition %u -> %u", (void *)ep, ep->ev_state, state)); if (state <= ep->ev_state) { (void) pthread_mutex_unlock(&ep->ev_lock); return; /* no state change necessary */ } if (ep->ev_state < FMD_EVS_RECEIVED || ep->ev_state > FMD_EVS_DIAGNOSED) fmd_panic("illegal transition %u -> %u\n", ep->ev_state, state); ep->ev_state = state; (void) pthread_mutex_unlock(&ep->ev_lock); } /* * If the specified event is DISCARDED, ACCEPTED, OR DIAGNOSED and it has been * written to a log but is still marked for replay, attempt to commit it to the * log so that it will not be replayed. If fmd_log_commit() is successful, it * will clear the FMD_EVF_REPLAY flag on the event for us. */ void fmd_event_commit(fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; (void) pthread_mutex_lock(&ep->ev_lock); if (ep->ev_state != FMD_EVS_RECEIVED && (ep->ev_flags & ( FMD_EVF_VOLATILE | FMD_EVF_REPLAY)) == FMD_EVF_REPLAY) fmd_log_commit(ep->ev_log, e); (void) pthread_mutex_unlock(&ep->ev_lock); } /* * Compute the delta between events in nanoseconds. To account for very old * events which are replayed, we must handle the case where ev_hrt is negative. * We convert the hrtime_t's to unsigned 64-bit integers and then handle the * case where 'old' is greater than 'new' (i.e. high-res time has wrapped). */ hrtime_t fmd_event_delta(fmd_event_t *e1, fmd_event_t *e2) { uint64_t old = ((fmd_event_impl_t *)e1)->ev_hrt; uint64_t new = ((fmd_event_impl_t *)e2)->ev_hrt; return (new >= old ? new - old : (UINT64_MAX - old) + new + 1); } hrtime_t fmd_event_hrtime(fmd_event_t *ep) { return (((fmd_event_impl_t *)ep)->ev_hrt); } int fmd_event_match(fmd_event_t *e, uint_t type, const void *data) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; if (ep->ev_type != type) return (0); if (type == FMD_EVT_PROTOCOL) return (fmd_strmatch(ep->ev_data, data)); else if (type == FMD_EVT_TIMEOUT) return ((id_t)data == ((fmd_modtimer_t *)ep->ev_data)->mt_id); else return (ep->ev_data == data); } int fmd_event_equal(fmd_event_t *e1, fmd_event_t *e2) { fmd_event_impl_t *ep1 = (fmd_event_impl_t *)e1; fmd_event_impl_t *ep2 = (fmd_event_impl_t *)e2; return (ep1->ev_log != NULL && ep1->ev_log == ep2->ev_log && ep1->ev_off == ep2->ev_off); } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_EVENT_H #define _FMD_EVENT_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include struct fmd_log; /* see */ typedef struct fmd_event_impl { pthread_mutex_t ev_lock; /* lock protecting structure contents */ uint32_t ev_refs; /* reference count */ uint8_t ev_type; /* event type (see below) */ uint8_t ev_state; /* event state (see below) */ uint8_t ev_flags; /* event flags (see below) */ uint8_t ev_ttl; /* event time-to-live */ nvlist_t *ev_nvl; /* event name/value pair payload */ void *ev_data; /* event type-specific data pointer */ fmd_timeval_t ev_time; /* upper bound on event time-of-day */ hrtime_t ev_hrt; /* upper bound on event hrtime */ struct fmd_log *ev_log; /* event log (or NULL) */ off64_t ev_off; /* event log offset (or zero) */ size_t ev_len; /* event log record length (or zero) */ } fmd_event_impl_t; #define FMD_EVT_PROTOCOL 0 /* protocol event (error/fault/list) */ #define FMD_EVT_TIMEOUT 1 /* timeout expiry notification */ #define FMD_EVT_CLOSE 2 /* case close request */ #define FMD_EVT_STATS 3 /* statistics snapshot request */ #define FMD_EVT_GC 4 /* garbage collection request */ #define FMD_EVT_CTL 5 /* fmd control event (see fmd_ctl.c) */ #define FMD_EVT_PUBLISH 6 /* case publish request */ #define FMD_EVT_TOPO 7 /* topology change notification */ #define FMD_EVT_NTYPES 8 /* number of event types */ #define FMD_EVS_DISCARDED 0 /* discarded by all subscribers */ #define FMD_EVS_RECEIVED 1 /* received but not yet processed */ #define FMD_EVS_ACCEPTED 2 /* accepted and assigned to a case */ #define FMD_EVS_DIAGNOSED 3 /* diagnosed and assigned to a case */ #define FMD_EVF_VOLATILE 0x1 /* event is not yet written to a log */ #define FMD_EVF_REPLAY 0x2 /* event is set for replay on restart */ #define FMD_EVF_LOCAL 0x4 /* event is from fmd or a local xprt */ #define FMD_HRT_NOW 0 /* use current hrtime as event time */ #define FMD_EVENT_TYPE(e) (((fmd_event_impl_t *)e)->ev_type) #define FMD_EVENT_DATA(e) (((fmd_event_impl_t *)e)->ev_data) #define FMD_EVENT_NVL(e) (((fmd_event_impl_t *)e)->ev_nvl) #define FMD_EVENT_TTL(e) (((fmd_event_impl_t *)e)->ev_ttl) #define FMD_EVN_TOD "__tod" /* private name-value pair for ev_tod */ #define FMD_EVN_TTL "__ttl" /* private name-value pair for ev_ttl */ #define FMD_EVN_UUID "__uuid" /* private name-value pair for UUIDs */ extern fmd_event_t *fmd_event_recreate(uint_t, const fmd_timeval_t *, nvlist_t *, void *, struct fmd_log *, off64_t, size_t); extern fmd_event_t *fmd_event_create(uint_t, hrtime_t, nvlist_t *, void *); extern void fmd_event_destroy(fmd_event_t *); extern void fmd_event_hold(fmd_event_t *); extern void fmd_event_rele(fmd_event_t *); extern void fmd_event_transition(fmd_event_t *, uint_t); extern void fmd_event_commit(fmd_event_t *); extern hrtime_t fmd_event_delta(fmd_event_t *, fmd_event_t *); extern hrtime_t fmd_event_hrtime(fmd_event_t *); extern int fmd_event_match(fmd_event_t *, uint_t, const void *); extern int fmd_event_equal(fmd_event_t *, fmd_event_t *); #ifdef __cplusplus } #endif #endif /* _FMD_EVENT_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include fmd_eventq_t * fmd_eventq_create(fmd_module_t *mp, fmd_eventqstat_t *stats, pthread_mutex_t *stats_lock, uint_t limit) { fmd_eventq_t *eq = fmd_zalloc(sizeof (fmd_eventq_t), FMD_SLEEP); (void) pthread_mutex_init(&eq->eq_lock, NULL); (void) pthread_cond_init(&eq->eq_cv, NULL); eq->eq_mod = mp; eq->eq_stats = stats; eq->eq_stats_lock = stats_lock; eq->eq_limit = limit; eq->eq_sgid = fmd_dispq_getgid(fmd.d_disp, eq); return (eq); } void fmd_eventq_destroy(fmd_eventq_t *eq) { fmd_eventqelem_t *eqe; while ((eqe = fmd_list_next(&eq->eq_list)) != NULL) { fmd_list_delete(&eq->eq_list, eqe); fmd_event_rele(eqe->eqe_event); fmd_free(eqe, sizeof (fmd_eventqelem_t)); } fmd_dispq_delgid(fmd.d_disp, eq->eq_sgid); fmd_free(eq, sizeof (fmd_eventq_t)); } static void fmd_eventq_drop(fmd_eventq_t *eq, fmd_eventqelem_t *eqe) { (void) pthread_mutex_lock(eq->eq_stats_lock); eq->eq_stats->eqs_dropped.fmds_value.ui64++; (void) pthread_mutex_unlock(eq->eq_stats_lock); fmd_event_rele(eqe->eqe_event); fmd_free(eqe, sizeof (fmd_eventqelem_t)); } void fmd_eventq_drop_topo(fmd_eventq_t *eq) { fmd_eventqelem_t *eqe, *tmp; boolean_t got_fm_events = B_FALSE; /* * Here we iterate through the per-module event queue in order to remove * redundant FMD_EVT_TOPO events. The trick is to not drop a given * topo event if there are any FM protocol events in the queue after * it, as those events need to be processed with the correct topology. */ (void) pthread_mutex_lock(&eq->eq_lock); eqe = fmd_list_prev(&eq->eq_list); while (eqe) { if (FMD_EVENT_TYPE(eqe->eqe_event) == FMD_EVT_TOPO) { if (!got_fm_events) { tmp = eqe; eqe = fmd_list_prev(eqe); fmd_list_delete(&eq->eq_list, tmp); eq->eq_size--; fmd_eventq_drop(eq, tmp); } else { got_fm_events = B_FALSE; eqe = fmd_list_prev(eqe); } } else if (FMD_EVENT_TYPE(eqe->eqe_event) == FMD_EVT_PROTOCOL) { got_fm_events = B_TRUE; eqe = fmd_list_prev(eqe); } else eqe = fmd_list_prev(eqe); } (void) pthread_mutex_unlock(&eq->eq_lock); } /* * Update statistics when an event is dispatched and placed on a module's event * queue. This is essentially the same code as kstat_waitq_enter(9F). */ static void fmd_eventqstat_dispatch(fmd_eventq_t *eq) { fmd_eventqstat_t *eqs = eq->eq_stats; hrtime_t new, delta; uint32_t wcnt; (void) pthread_mutex_lock(eq->eq_stats_lock); new = gethrtime(); delta = new - eqs->eqs_wlastupdate.fmds_value.ui64; eqs->eqs_wlastupdate.fmds_value.ui64 = new; wcnt = eqs->eqs_wcnt.fmds_value.ui32++; if (wcnt != 0) { eqs->eqs_wlentime.fmds_value.ui64 += delta * wcnt; eqs->eqs_wtime.fmds_value.ui64 += delta; } eqs->eqs_dispatched.fmds_value.ui64++; (void) pthread_mutex_unlock(eq->eq_stats_lock); } void fmd_eventq_insert_at_head(fmd_eventq_t *eq, fmd_event_t *ep) { uint_t evt = FMD_EVENT_TYPE(ep); fmd_eventqelem_t *eqe; int ok; /* * If this event queue is acting as /dev/null, bounce the reference * count to free an unreferenced event and just return immediately. */ if (eq->eq_limit == 0) { fmd_event_hold(ep); fmd_event_rele(ep); return; } eqe = fmd_alloc(sizeof (fmd_eventqelem_t), FMD_SLEEP); fmd_event_hold(ep); eqe->eqe_event = ep; (void) pthread_mutex_lock(&eq->eq_lock); if ((ok = eq->eq_size < eq->eq_limit || evt != FMD_EVT_PROTOCOL) != 0) { if (evt != FMD_EVT_CTL) fmd_eventqstat_dispatch(eq); fmd_list_prepend(&eq->eq_list, eqe); eq->eq_size++; } (void) pthread_cond_broadcast(&eq->eq_cv); (void) pthread_mutex_unlock(&eq->eq_lock); if (!ok) fmd_eventq_drop(eq, eqe); } void fmd_eventq_insert_at_time(fmd_eventq_t *eq, fmd_event_t *ep) { uint_t evt = FMD_EVENT_TYPE(ep); hrtime_t hrt = fmd_event_hrtime(ep); fmd_eventqelem_t *eqe, *oqe; int ok; /* * If this event queue is acting as /dev/null, bounce the reference * count to free an unreferenced event and just return immediately. */ if (eq->eq_limit == 0) { fmd_event_hold(ep); fmd_event_rele(ep); return; } eqe = fmd_alloc(sizeof (fmd_eventqelem_t), FMD_SLEEP); fmd_event_hold(ep); eqe->eqe_event = ep; (void) pthread_mutex_lock(&eq->eq_lock); /* * fmd makes no guarantees that events will be delivered in time order * because its transport can make no such guarantees. Instead we make * a looser guarantee that an enqueued event will be dequeued before * any newer *pending* events according to event time. This permits us * to state, for example, that a timer expiry event will be delivered * prior to any enqueued event whose time is after the timer expired. * We use a simple insertion sort for this task, as queue lengths are * typically short and events do *tend* to be received chronologically. */ for (oqe = fmd_list_prev(&eq->eq_list); oqe; oqe = fmd_list_prev(oqe)) { if (hrt >= fmd_event_hrtime(oqe->eqe_event)) break; /* 'ep' is newer than the event in 'oqe' */ } if ((ok = eq->eq_size < eq->eq_limit || evt != FMD_EVT_PROTOCOL) != 0) { if (evt != FMD_EVT_CTL) fmd_eventqstat_dispatch(eq); if (oqe == NULL) fmd_list_prepend(&eq->eq_list, eqe); else fmd_list_insert_after(&eq->eq_list, oqe, eqe); eq->eq_size++; } (void) pthread_cond_broadcast(&eq->eq_cv); (void) pthread_mutex_unlock(&eq->eq_lock); if (!ok) fmd_eventq_drop(eq, eqe); } fmd_event_t * fmd_eventq_delete(fmd_eventq_t *eq) { fmd_eventqstat_t *eqs = eq->eq_stats; hrtime_t new, delta; uint32_t wcnt; fmd_eventqelem_t *eqe; fmd_event_t *ep; top: (void) pthread_mutex_lock(&eq->eq_lock); while (!(eq->eq_flags & FMD_EVENTQ_ABORT) && (eq->eq_size == 0 || (eq->eq_flags & FMD_EVENTQ_SUSPEND))) (void) pthread_cond_wait(&eq->eq_cv, &eq->eq_lock); if (eq->eq_flags & FMD_EVENTQ_ABORT) { (void) pthread_mutex_unlock(&eq->eq_lock); return (NULL); } eqe = fmd_list_next(&eq->eq_list); fmd_list_delete(&eq->eq_list, eqe); eq->eq_size--; (void) pthread_mutex_unlock(&eq->eq_lock); ep = eqe->eqe_event; fmd_free(eqe, sizeof (fmd_eventqelem_t)); /* * If we dequeued a control event, release it and go back to sleep. * fmd_event_rele() on the event will block as described in fmd_ctl.c. * This effectively renders control events invisible to our callers * as well as to statistics and observability tools (e.g. fmstat(8)). */ if (FMD_EVENT_TYPE(ep) == FMD_EVT_CTL) { fmd_event_rele(ep); goto top; } /* * Before returning, update our statistics. This code is essentially * kstat_waitq_to_runq(9F), except simplified because our queues are * always consumed by a single thread (i.e. runq len == 1). */ (void) pthread_mutex_lock(eq->eq_stats_lock); new = gethrtime(); delta = new - eqs->eqs_wlastupdate.fmds_value.ui64; eqs->eqs_wlastupdate.fmds_value.ui64 = new; eqs->eqs_dlastupdate.fmds_value.ui64 = new; ASSERT(eqs->eqs_wcnt.fmds_value.ui32 != 0); wcnt = eqs->eqs_wcnt.fmds_value.ui32--; eqs->eqs_wlentime.fmds_value.ui64 += delta * wcnt; eqs->eqs_wtime.fmds_value.ui64 += delta; if (FMD_EVENT_TYPE(ep) == FMD_EVT_PROTOCOL) eqs->eqs_prdequeued.fmds_value.ui64++; eqs->eqs_dequeued.fmds_value.ui64++; (void) pthread_mutex_unlock(eq->eq_stats_lock); return (ep); } /* * Update statistics when an event is done being processed by the eventq's * consumer thread. This is essentially kstat_runq_exit(9F) simplified for * our principle that a single thread consumes the queue (i.e. runq len == 1). */ void fmd_eventq_done(fmd_eventq_t *eq) { fmd_eventqstat_t *eqs = eq->eq_stats; hrtime_t new, delta; (void) pthread_mutex_lock(eq->eq_stats_lock); new = gethrtime(); delta = new - eqs->eqs_dlastupdate.fmds_value.ui64; eqs->eqs_dlastupdate.fmds_value.ui64 = new; eqs->eqs_dtime.fmds_value.ui64 += delta; (void) pthread_mutex_unlock(eq->eq_stats_lock); } void fmd_eventq_cancel(fmd_eventq_t *eq, uint_t type, void *data) { fmd_eventqelem_t *eqe, *nqe; (void) pthread_mutex_lock(&eq->eq_lock); for (eqe = fmd_list_next(&eq->eq_list); eqe != NULL; eqe = nqe) { nqe = fmd_list_next(eqe); if (fmd_event_match(eqe->eqe_event, type, data)) { fmd_list_delete(&eq->eq_list, eqe); eq->eq_size--; fmd_event_rele(eqe->eqe_event); fmd_free(eqe, sizeof (fmd_eventqelem_t)); } } (void) pthread_mutex_unlock(&eq->eq_lock); } void fmd_eventq_suspend(fmd_eventq_t *eq) { (void) pthread_mutex_lock(&eq->eq_lock); eq->eq_flags |= FMD_EVENTQ_SUSPEND; (void) pthread_mutex_unlock(&eq->eq_lock); } void fmd_eventq_resume(fmd_eventq_t *eq) { (void) pthread_mutex_lock(&eq->eq_lock); eq->eq_flags &= ~FMD_EVENTQ_SUSPEND; (void) pthread_cond_broadcast(&eq->eq_cv); (void) pthread_mutex_unlock(&eq->eq_lock); } void fmd_eventq_abort(fmd_eventq_t *eq) { fmd_eventqelem_t *eqe; (void) pthread_mutex_lock(&eq->eq_lock); while ((eqe = fmd_list_next(&eq->eq_list)) != NULL) { fmd_list_delete(&eq->eq_list, eqe); fmd_event_rele(eqe->eqe_event); fmd_free(eqe, sizeof (fmd_eventqelem_t)); } eq->eq_flags |= FMD_EVENTQ_ABORT; (void) pthread_cond_broadcast(&eq->eq_cv); (void) pthread_mutex_unlock(&eq->eq_lock); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_EVENTQ_H #define _FMD_EVENTQ_H #include #ifdef __cplusplus extern "C" { #endif #include #include typedef struct fmd_eventqstat { fmd_stat_t eqs_dispatched; /* total events dispatched to queue */ fmd_stat_t eqs_dequeued; /* total events dequeued by consumer */ fmd_stat_t eqs_prdequeued; /* total protocol events dequeued */ fmd_stat_t eqs_dropped; /* total events dropped by queue */ fmd_stat_t eqs_wcnt; /* count of events waiting on queue */ fmd_stat_t eqs_wtime; /* total wait time (pre-dispatch) */ fmd_stat_t eqs_wlentime; /* total wait length * time product */ fmd_stat_t eqs_wlastupdate; /* hrtime of last wait queue update */ fmd_stat_t eqs_dtime; /* total dispatch time */ fmd_stat_t eqs_dlastupdate; /* hrtime of last dispatch */ } fmd_eventqstat_t; typedef struct fmd_eventqelem { fmd_list_t eqe_list; /* linked-list prev/next pointers */ fmd_event_t *eqe_event; /* pointer to event */ } fmd_eventqelem_t; struct fmd_module; /* see */ typedef struct fmd_eventq { pthread_mutex_t eq_lock; /* lock protecting queue contents */ pthread_cond_t eq_cv; /* condition variable for waiters */ fmd_list_t eq_list; /* list head/tail pointers for queue */ struct fmd_module *eq_mod; /* module associated with this queue */ pthread_mutex_t *eq_stats_lock; /* lock that protects eq_stats */ fmd_eventqstat_t *eq_stats; /* statistics associated with queue */ uint_t eq_limit; /* limit on number of queue elements */ uint_t eq_size; /* number of elements on queue */ uint_t eq_flags; /* flags for abort and suspend */ id_t eq_sgid; /* subscription group id for dispq */ } fmd_eventq_t; #define FMD_EVENTQ_ABORT 0x1 /* return NULL from fmd_eventq_delete */ #define FMD_EVENTQ_SUSPEND 0x2 /* suspend in fmd_eventq_delete */ extern fmd_eventq_t *fmd_eventq_create(struct fmd_module *, fmd_eventqstat_t *, pthread_mutex_t *, uint_t); extern void fmd_eventq_destroy(fmd_eventq_t *); extern void fmd_eventq_insert_at_head(fmd_eventq_t *, fmd_event_t *); extern void fmd_eventq_insert_at_time(fmd_eventq_t *, fmd_event_t *); extern fmd_event_t *fmd_eventq_delete(fmd_eventq_t *); extern void fmd_eventq_done(fmd_eventq_t *); extern void fmd_eventq_cancel(fmd_eventq_t *, uint_t, void *); extern void fmd_eventq_suspend(fmd_eventq_t *); extern void fmd_eventq_resume(fmd_eventq_t *); extern void fmd_eventq_abort(fmd_eventq_t *); extern void fmd_eventq_drop_topo(fmd_eventq_t *); #ifdef __cplusplus } #endif #endif /* _FMD_EVENTQ_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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include /* * Interfaces to be used by the plugins */ void * fmd_fmri_alloc(size_t size) { return (fmd_alloc(size, FMD_SLEEP)); } void * fmd_fmri_zalloc(size_t size) { return (fmd_zalloc(size, FMD_SLEEP)); } void fmd_fmri_free(void *data, size_t size) { fmd_free(data, size); } int fmd_fmri_set_errno(int err) { errno = err; return (-1); } void fmd_fmri_warn(const char *format, ...) { va_list ap; va_start(ap, format); fmd_verror(EFMD_FMRI_SCHEME, format, ap); va_end(ap); } /* * Convert an input string to a URI escaped string and return the new string. * RFC2396 Section 2.4 says that data must be escaped if it does not have a * representation using an unreserved character, where an unreserved character * is one that is either alphanumeric or one of the marks defined in S2.3. */ static size_t fmd_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len) { static const char rfc2396_mark[] = "-_.!~*'()"; static const char hex_digits[] = "0123456789ABCDEF"; static const char empty_str[] = ""; const char *p; char c, *q; size_t n = 0; if (s == NULL) s = empty_str; if (xmark == NULL) xmark = empty_str; for (p = s; (c = *p) != '\0'; p++) { if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) n++; /* represent c as itself */ else n += 3; /* represent c as escape */ } if (buf == NULL) return (n); for (p = s, q = buf; (c = *p) != '\0' && q < buf + len; p++) { if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) { *q++ = c; } else { *q++ = '%'; *q++ = hex_digits[((uchar_t)c & 0xf0) >> 4]; *q++ = hex_digits[(uchar_t)c & 0xf]; } } if (q == buf + len) q--; /* len is too small: truncate output string */ *q = '\0'; return (n); } /* * Convert a name-value pair list representing an FMRI authority into the * corresponding RFC2396 string representation and return the new string. */ char * fmd_fmri_auth2str(nvlist_t *nvl) { nvpair_t *nvp; char *s, *p, *v; size_t n = 0; for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) { if (nvpair_type(nvp) != DATA_TYPE_STRING) continue; /* do not format non-string elements */ n += fmd_fmri_uriescape(nvpair_name(nvp), NULL, NULL, 0) + 1; (void) nvpair_value_string(nvp, &v); n += fmd_fmri_uriescape(v, ":", NULL, 0) + 1; } p = s = fmd_alloc(n, FMD_SLEEP); for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) { if (nvpair_type(nvp) != DATA_TYPE_STRING) continue; /* do not format non-string elements */ if (p != s) *p++ = ','; p += fmd_fmri_uriescape(nvpair_name(nvp), NULL, p, n); *p++ = '='; (void) nvpair_value_string(nvp, &v); p += fmd_fmri_uriescape(v, ":", p, n); } return (s); } /* * Convert an input string to a URI escaped string and return the new string. * We amend the unreserved character list to include commas and colons, * as both are needed to make FMRIs readable without escaping. We also permit * "/" to pass through unescaped as any path delimiters used by the event * creator are presumably intended to appear in the final path. */ char * fmd_fmri_strescape(const char *s) { char *s2; size_t n; if (s == NULL) return (NULL); n = fmd_fmri_uriescape(s, ":,/", NULL, 0); s2 = fmd_alloc(n + 1, FMD_SLEEP); (void) fmd_fmri_uriescape(s, ":,/", s2, n + 1); return (s2); } char * fmd_fmri_strdup(const char *s) { return (fmd_strdup(s, FMD_SLEEP)); } void fmd_fmri_strfree(char *s) { fmd_strfree(s); } const char * fmd_fmri_get_rootdir(void) { return (fmd.d_rootdir); } const char * fmd_fmri_get_platform(void) { return (fmd.d_platform); } uint64_t fmd_fmri_get_drgen(void) { uint64_t gen; (void) pthread_mutex_lock(&fmd.d_stats_lock); gen = fmd.d_stats->ds_dr_gen.fmds_value.ui64; (void) pthread_mutex_unlock(&fmd.d_stats_lock); return (gen); } struct topo_hdl * fmd_fmri_topo_hold(int version) { fmd_topo_t *ftp; if (version != TOPO_VERSION) return (NULL); ftp = fmd_topo_hold(); return (ftp->ft_hdl); } void fmd_fmri_topo_rele(struct topo_hdl *thp) { fmd_topo_rele_hdl(thp); } /* * Interfaces for users of the plugins */ static fmd_scheme_t * nvl2scheme(nvlist_t *nvl) { char *name; if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) { (void) fmd_set_errno(EFMD_FMRI_INVAL); return (NULL); } return (fmd_scheme_hash_lookup(fmd.d_schemes, name)); } ssize_t fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) { fmd_scheme_t *sp; char c; ssize_t rv; if (buf == NULL && buflen == 0) { buf = &c; buflen = sizeof (c); } if ((sp = nvl2scheme(nvl)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); ASSERT(buf != NULL || buflen == 0); rv = sp->sch_ops.sop_nvl2str(nvl, buf, buflen); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } int fmd_fmri_expand(nvlist_t *nvl) { fmd_scheme_t *sp; int rv; if ((sp = nvl2scheme(nvl)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); rv = sp->sch_ops.sop_expand(nvl); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } int fmd_fmri_present(nvlist_t *nvl) { fmd_scheme_t *sp; int rv; if ((sp = nvl2scheme(nvl)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); rv = sp->sch_ops.sop_present(nvl); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } int fmd_fmri_replaced(nvlist_t *nvl) { fmd_scheme_t *sp; int rv; if ((sp = nvl2scheme(nvl)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); rv = sp->sch_ops.sop_replaced(nvl); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } int fmd_fmri_service_state(nvlist_t *nvl) { fmd_scheme_t *sp; int rv; if ((sp = nvl2scheme(nvl)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); rv = sp->sch_ops.sop_service_state(nvl); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } int fmd_fmri_unusable(nvlist_t *nvl) { fmd_scheme_t *sp; int rv; if ((sp = nvl2scheme(nvl)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); rv = sp->sch_ops.sop_unusable(nvl); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } /* * Someday we'll retire the scheme plugins. For the * retire/unretire operations, the topo interfaces * are called directly. */ int fmd_fmri_retire(nvlist_t *nvl) { topo_hdl_t *thp; int rv, err; if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (-1); rv = topo_fmri_retire(thp, nvl, &err); fmd_fmri_topo_rele(thp); return (rv); } int fmd_fmri_unretire(nvlist_t *nvl) { topo_hdl_t *thp; int rv, err; if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (-1); rv = topo_fmri_unretire(thp, nvl, &err); fmd_fmri_topo_rele(thp); return (rv); } int fmd_fmri_contains(nvlist_t *er, nvlist_t *ee) { fmd_scheme_t *sp; char *ername, *eename; int rv; if (nvlist_lookup_string(er, FM_FMRI_SCHEME, &ername) != 0 || nvlist_lookup_string(ee, FM_FMRI_SCHEME, &eename) != 0 || strcmp(ername, eename) != 0) return (fmd_set_errno(EFMD_FMRI_INVAL)); if ((sp = fmd_scheme_hash_lookup(fmd.d_schemes, ername)) == NULL) return (-1); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); rv = sp->sch_ops.sop_contains(er, ee); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (rv); } nvlist_t * fmd_fmri_translate(nvlist_t *fmri, nvlist_t *auth) { fmd_scheme_t *sp; nvlist_t *nvl; if ((sp = nvl2scheme(fmri)) == NULL) return (NULL); /* errno is set for us */ (void) pthread_mutex_lock(&sp->sch_opslock); nvl = sp->sch_ops.sop_translate(fmri, auth); (void) pthread_mutex_unlock(&sp->sch_opslock); fmd_scheme_hash_release(fmd.d_schemes, sp); return (nvl); } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_FMRI_H #define _FMD_FMRI_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Fault Management Daemon FMRI Scheme Interfaces * * Note: The contents of this file are private to the implementation of the * Solaris system and FMD subsystem and are subject to change at any time * without notice. Applications and drivers using these interfaces will fail * to run on future releases. These interfaces should not be used for any * purpose until they are publicly documented for use outside of Sun. */ /* * The following utility functions (in addition to libnvpair) are provided by * fmd to facilitate the implementation of each FMRI scheme library. */ struct topo_hdl; #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif extern void *fmd_fmri_alloc(size_t); extern void *fmd_fmri_zalloc(size_t); extern void fmd_fmri_free(void *, size_t); extern int fmd_fmri_set_errno(int); extern void fmd_fmri_warn(const char *, ...); extern char *fmd_fmri_auth2str(nvlist_t *); extern char *fmd_fmri_strescape(const char *); extern char *fmd_fmri_strdup(const char *); extern void fmd_fmri_strfree(char *); extern const char *fmd_fmri_get_rootdir(void); extern const char *fmd_fmri_get_platform(void); extern uint64_t fmd_fmri_get_drgen(void); extern struct topo_hdl *fmd_fmri_topo_hold(int); extern void fmd_fmri_topo_rele(struct topo_hdl *); /* * The following entry points are to be implemented by each scheme: */ extern int fmd_fmri_init(void); extern void fmd_fmri_fini(void); extern ssize_t fmd_fmri_nvl2str(nvlist_t *, char *, size_t); extern int fmd_fmri_expand(nvlist_t *); extern int fmd_fmri_present(nvlist_t *); extern int fmd_fmri_replaced(nvlist_t *); extern int fmd_fmri_service_state(nvlist_t *); extern int fmd_fmri_unusable(nvlist_t *); extern int fmd_fmri_retire(nvlist_t *); extern int fmd_fmri_unretire(nvlist_t *); extern int fmd_fmri_contains(nvlist_t *, nvlist_t *); extern nvlist_t *fmd_fmri_translate(nvlist_t *, nvlist_t *); #define FMD_OBJ_STATE_UNKNOWN 1 #define FMD_OBJ_STATE_STILL_PRESENT 2 #define FMD_OBJ_STATE_REPLACED 3 #define FMD_OBJ_STATE_NOT_PRESENT 4 #define FMD_SERVICE_STATE_UNKNOWN 0 #define FMD_SERVICE_STATE_OK 1 #define FMD_SERVICE_STATE_DEGRADED 2 #define FMD_SERVICE_STATE_UNUSABLE 3 #define FMD_SERVICE_STATE_DEGRADED_PENDING_RESET 4 #define FMD_SERVICE_STATE_UNUSABLE_PENDING_RESET 5 #define FMD_SERVICE_STATE_UNUSABLE_UNTIL_REPLACED 6 #define FMD_SERVICE_STATE_ISOLATE_PENDING 7 #ifdef __cplusplus } #endif #endif /* _FMD_FMRI_H */ # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # # 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 # $mapfile_version 2 SYMBOL_SCOPE { fmd_fmri_alloc { TYPE = function; FLAGS = extern }; fmd_fmri_zalloc { TYPE = function; FLAGS = extern }; fmd_fmri_free { TYPE = function; FLAGS = extern }; fmd_fmri_set_errno { TYPE = function; FLAGS = extern }; fmd_fmri_warn { TYPE = function; FLAGS = extern }; fmd_fmri_auth2str { TYPE = function; FLAGS = extern }; fmd_fmri_strescape { TYPE = function; FLAGS = extern }; fmd_fmri_strdup { TYPE = function; FLAGS = extern }; fmd_fmri_strfree { TYPE = function; FLAGS = extern }; fmd_fmri_get_rootdir { TYPE = function; FLAGS = extern }; fmd_fmri_get_platform { TYPE = function; FLAGS = extern }; fmd_fmri_get_drgen { TYPE = function; FLAGS = extern }; fmd_fmri_topo_hold { TYPE = function; FLAGS = extern }; fmd_fmri_topo_rele { TYPE = function; FLAGS = extern }; }; /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include fmd_idspace_t * fmd_idspace_create(const char *name, id_t min, id_t max) { fmd_idspace_t *ids = fmd_alloc(sizeof (fmd_idspace_t), FMD_SLEEP); uint_t ids_avg, ids_max, hashlen, hashmax; /* * Dynamically size the hash table bucket array based on the desired * chain length. We hash by indexing on the low-order bits. * Do not permit the hash bucket array to exceed a reasonable size. */ ASSERT(min >= 0 && max >= 0); ASSERT(max >= min); (void) fmd_conf_getprop(fmd.d_conf, "ids.avg", &ids_avg); (void) fmd_conf_getprop(fmd.d_conf, "ids.max", &ids_max); hashmax = max - min + 1; hashlen = 1 << fls(hashmax / ids_avg); if (hashlen > ids_max) hashlen = ids_max; (void) strlcpy(ids->ids_name, name, sizeof (ids->ids_name)); (void) pthread_mutex_init(&ids->ids_lock, NULL); (void) pthread_cond_init(&ids->ids_cv, NULL); ids->ids_hash = fmd_zalloc(sizeof (void *) * hashlen, FMD_SLEEP); ids->ids_hashlen = hashlen; ids->ids_refs = 0; ids->ids_nextid = min - 1; ids->ids_minid = min; ids->ids_maxid = max; ids->ids_count = 0; return (ids); } void fmd_idspace_destroy(fmd_idspace_t *ids) { fmd_idelem_t *ide, *nde; uint_t i; (void) pthread_mutex_lock(&ids->ids_lock); while (ids->ids_refs != 0) (void) pthread_cond_wait(&ids->ids_cv, &ids->ids_lock); for (i = 0; i < ids->ids_hashlen; i++) { for (ide = ids->ids_hash[i]; ide != NULL; ide = nde) { nde = ide->ide_next; fmd_free(ide, sizeof (fmd_idelem_t)); } } fmd_free(ids->ids_hash, sizeof (void *) * ids->ids_hashlen); fmd_free(ids, sizeof (fmd_idspace_t)); } void fmd_idspace_apply(fmd_idspace_t *ids, void (*func)(fmd_idspace_t *, id_t, void *), void *arg) { fmd_idelem_t *ide; id_t *ida, *idp; uint_t i, count; (void) pthread_mutex_lock(&ids->ids_lock); count = ids->ids_count; ida = idp = fmd_alloc(sizeof (id_t) * count, FMD_SLEEP); for (i = 0; i < ids->ids_hashlen; i++) { for (ide = ids->ids_hash[i]; ide != NULL; ide = ide->ide_next) *idp++ = ide->ide_id; } ASSERT(idp == ida + count); (void) pthread_mutex_unlock(&ids->ids_lock); for (i = 0; i < count; i++) func(ids, ida[i], arg); fmd_free(ida, sizeof (id_t) * count); } static fmd_idelem_t * fmd_idspace_lookup(fmd_idspace_t *ids, id_t id) { fmd_idelem_t *ide; ASSERT(MUTEX_HELD(&ids->ids_lock)); ide = ids->ids_hash[id & (ids->ids_hashlen - 1)]; for (; ide != NULL; ide = ide->ide_next) { if (ide->ide_id == id) break; } return (ide); } void * fmd_idspace_getspecific(fmd_idspace_t *ids, id_t id) { fmd_idelem_t *ide; void *data; (void) pthread_mutex_lock(&ids->ids_lock); ide = fmd_idspace_lookup(ids, id); data = ide ? ide->ide_data : NULL; (void) pthread_mutex_unlock(&ids->ids_lock); return (data); } void fmd_idspace_setspecific(fmd_idspace_t *ids, id_t id, void *data) { fmd_idelem_t *ide; (void) pthread_mutex_lock(&ids->ids_lock); while (ids->ids_refs != 0) (void) pthread_cond_wait(&ids->ids_cv, &ids->ids_lock); if ((ide = fmd_idspace_lookup(ids, id)) == NULL) { fmd_panic("idspace %p (%s) does not contain id %ld", (void *)ids, ids->ids_name, id); } ide->ide_data = data; (void) pthread_mutex_unlock(&ids->ids_lock); } int fmd_idspace_contains(fmd_idspace_t *ids, id_t id) { fmd_idelem_t *ide; (void) pthread_mutex_lock(&ids->ids_lock); ide = fmd_idspace_lookup(ids, id); (void) pthread_mutex_unlock(&ids->ids_lock); return (ide != NULL); } int fmd_idspace_valid(fmd_idspace_t *ids, id_t id) { return (id >= ids->ids_minid && id <= ids->ids_maxid); } static id_t fmd_idspace_xalloc_locked(fmd_idspace_t *ids, id_t id, void *data) { fmd_idelem_t *ide; uint_t h; if (id < ids->ids_minid || id > ids->ids_maxid) { fmd_panic("%ld out of range [%ld .. %ld] for idspace %p (%s)\n", id, ids->ids_minid, ids->ids_maxid, (void *)ids, ids->ids_name); } if (fmd_idspace_lookup(ids, id) != NULL) return (fmd_set_errno(EALREADY)); ide = fmd_alloc(sizeof (fmd_idelem_t), FMD_SLEEP); h = id & (ids->ids_hashlen - 1); ide->ide_next = ids->ids_hash[h]; ide->ide_data = data; ide->ide_id = id; ids->ids_hash[h] = ide; ids->ids_count++; return (id); } id_t fmd_idspace_xalloc(fmd_idspace_t *ids, id_t id, void *data) { (void) pthread_mutex_lock(&ids->ids_lock); id = fmd_idspace_xalloc_locked(ids, id, data); (void) pthread_mutex_unlock(&ids->ids_lock); return (id); } static id_t fmd_idspace_alloc_locked(fmd_idspace_t *ids, void *data) { id_t id; ASSERT(MUTEX_HELD(&ids->ids_lock)); if (ids->ids_count == ids->ids_maxid - ids->ids_minid + 1) return (fmd_set_errno(ENOSPC)); do { if (++ids->ids_nextid > ids->ids_maxid) ids->ids_nextid = ids->ids_minid; id = ids->ids_nextid; } while (fmd_idspace_xalloc_locked(ids, id, data) != id); return (id); } id_t fmd_idspace_alloc(fmd_idspace_t *ids, void *data) { id_t id; (void) pthread_mutex_lock(&ids->ids_lock); id = fmd_idspace_alloc_locked(ids, data); (void) pthread_mutex_unlock(&ids->ids_lock); return (id); } /* * For the moment, we use a simple but slow implementation: reset ids_nextid to * the minimum id and search in order from there. If this becomes performance * sensitive we can maintain a freelist of the unallocated identifiers, etc. */ id_t fmd_idspace_alloc_min(fmd_idspace_t *ids, void *data) { id_t id; (void) pthread_mutex_lock(&ids->ids_lock); ids->ids_nextid = ids->ids_minid - 1; id = fmd_idspace_alloc_locked(ids, data); (void) pthread_mutex_unlock(&ids->ids_lock); return (id); } void * fmd_idspace_free(fmd_idspace_t *ids, id_t id) { fmd_idelem_t *ide, **pp; void *data; (void) pthread_mutex_lock(&ids->ids_lock); pp = &ids->ids_hash[id & (ids->ids_hashlen - 1)]; for (ide = *pp; ide != NULL; ide = ide->ide_next) { if (ide->ide_id != id) pp = &ide->ide_next; else break; } if (ide == NULL) { (void) pthread_mutex_unlock(&ids->ids_lock); return (NULL); } data = ide->ide_data; *pp = ide->ide_next; fmd_free(ide, sizeof (fmd_idelem_t)); ASSERT(ids->ids_count != 0); ids->ids_count--; (void) pthread_mutex_unlock(&ids->ids_lock); return (data); } /* * Retrieve the id-specific data for the specified id and place a hold on the * id so that it cannot be or deleted until fmd_idspace_rele(ids, id) is * called. For simplicity, we now use a single global reference count for all * holds. If this feature needs to be used in a place where there is high * contention between holders and deleters, the implementation can be modified * to use either a per-hash-bucket or a per-id-element condition variable. */ void * fmd_idspace_hold(fmd_idspace_t *ids, id_t id) { fmd_idelem_t *ide; void *data = NULL; (void) pthread_mutex_lock(&ids->ids_lock); if ((ide = fmd_idspace_lookup(ids, id)) != NULL) { ids->ids_refs++; ASSERT(ids->ids_refs != 0); data = ide->ide_data; ASSERT(data != NULL); } (void) pthread_mutex_unlock(&ids->ids_lock); return (data); } void fmd_idspace_rele(fmd_idspace_t *ids, id_t id) { (void) pthread_mutex_lock(&ids->ids_lock); ASSERT(fmd_idspace_lookup(ids, id) != NULL); ASSERT(ids->ids_refs != 0); ids->ids_refs--; (void) pthread_cond_broadcast(&ids->ids_cv); (void) pthread_mutex_unlock(&ids->ids_lock); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_IDSPACE_H #define _FMD_IDSPACE_H #include #include #ifdef __cplusplus extern "C" { #endif typedef struct fmd_idelem { struct fmd_idelem *ide_next; /* next element in hash bucket chain */ void *ide_data; /* data associated with this element */ id_t ide_id; /* identifier associated w/ element */ } fmd_idelem_t; typedef struct fmd_idspace { char ids_name[32]; /* string name of idspace for debug */ pthread_mutex_t ids_lock; /* lock protecting idspace contents */ pthread_cond_t ids_cv; /* condition variable for waiters */ fmd_idelem_t **ids_hash; /* hash bucket array of fmd_idelems */ uint_t ids_hashlen; /* size of hash bucket array */ uint_t ids_refs; /* reference count for idspace_hold */ id_t ids_nextid; /* next identifier guess for alloc */ id_t ids_minid; /* minimum identifier value */ id_t ids_maxid; /* maximum identifier value */ id_t ids_count; /* number of allocated ids */ } fmd_idspace_t; extern fmd_idspace_t *fmd_idspace_create(const char *, id_t, id_t); extern void fmd_idspace_destroy(fmd_idspace_t *); extern void fmd_idspace_apply(fmd_idspace_t *, void (*)(fmd_idspace_t *, id_t, void *), void *); extern void *fmd_idspace_getspecific(fmd_idspace_t *, id_t); extern void fmd_idspace_setspecific(fmd_idspace_t *, id_t, void *); extern int fmd_idspace_contains(fmd_idspace_t *, id_t); extern int fmd_idspace_valid(fmd_idspace_t *, id_t); extern id_t fmd_idspace_xalloc(fmd_idspace_t *, id_t, void *); extern id_t fmd_idspace_alloc(fmd_idspace_t *, void *); extern id_t fmd_idspace_alloc_min(fmd_idspace_t *, void *); extern void *fmd_idspace_free(fmd_idspace_t *, id_t); extern void *fmd_idspace_hold(fmd_idspace_t *, id_t); extern void fmd_idspace_rele(fmd_idspace_t *, id_t); #ifdef __cplusplus } #endif #endif /* _FMD_IDSPACE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Embedded Linked Lists * * Simple doubly-linked list implementation. This implementation assumes that * each list element contains an embedded fmd_list_t (previous and next * pointers), which is typically the first member of the element struct. * An additional fmd_list_t is used to store the head (l_next) and tail * (l_prev) pointers. The current head and tail list elements have their * previous and next pointers set to NULL, respectively. * * NOTE: The embeddable list code in this file intentionally provides no * locking of any kind. The implementation of any list in fmd must provide * an appropriate locking strategy to protect the list or to protect access * to the embedded fmd_list_t inside of each list element to avoid corruption. * Refer to comments in the source files that use fmd_list_t for lock details. */ #include #include void fmd_list_append(fmd_list_t *lp, void *new) { fmd_list_t *p = lp->l_prev; /* p = tail list element */ fmd_list_t *q = new; /* q = new list element */ lp->l_prev = q; q->l_prev = p; q->l_next = NULL; if (p != NULL) { ASSERT(p->l_next == NULL); p->l_next = q; } else { ASSERT(lp->l_next == NULL); lp->l_next = q; } } void fmd_list_prepend(fmd_list_t *lp, void *new) { fmd_list_t *p = new; /* p = new list element */ fmd_list_t *q = lp->l_next; /* q = head list element */ lp->l_next = p; p->l_prev = NULL; p->l_next = q; if (q != NULL) { ASSERT(q->l_prev == NULL); q->l_prev = p; } else { ASSERT(lp->l_prev == NULL); lp->l_prev = p; } } void fmd_list_insert_before(fmd_list_t *lp, void *before_me, void *new) { fmd_list_t *p = before_me; fmd_list_t *q = new; if (p == NULL || p->l_prev == NULL) { fmd_list_prepend(lp, new); return; } q->l_prev = p->l_prev; q->l_next = p; p->l_prev = q; q->l_prev->l_next = q; } void fmd_list_insert_after(fmd_list_t *lp, void *after_me, void *new) { fmd_list_t *p = after_me; fmd_list_t *q = new; if (p == NULL || p->l_next == NULL) { fmd_list_append(lp, new); return; } q->l_next = p->l_next; q->l_prev = p; p->l_next = q; q->l_next->l_prev = q; } void fmd_list_delete(fmd_list_t *lp, void *existing) { fmd_list_t *p = existing; if (p->l_prev != NULL) p->l_prev->l_next = p->l_next; else lp->l_next = p->l_next; if (p->l_next != NULL) p->l_next->l_prev = p->l_prev; else lp->l_prev = p->l_prev; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_LIST_H #define _FMD_LIST_H #ifdef __cplusplus extern "C" { #endif typedef struct fmd_list { struct fmd_list *l_prev; struct fmd_list *l_next; } fmd_list_t; #define fmd_list_prev(elem) ((void *)(((fmd_list_t *)(elem))->l_prev)) #define fmd_list_next(elem) ((void *)(((fmd_list_t *)(elem))->l_next)) extern void fmd_list_append(fmd_list_t *, void *); extern void fmd_list_prepend(fmd_list_t *, void *); extern void fmd_list_insert_before(fmd_list_t *, void *, void *); extern void fmd_list_insert_after(fmd_list_t *, void *, void *); extern void fmd_list_delete(fmd_list_t *, void *); #ifdef __cplusplus } #endif #endif /* _FMD_LIST_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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * FMD Log File Subsystem * * Events are written to one of two log files as they are received or created; * the error log tracks all ereport.* events received on the inbound event * transport, and the fault log tracks all list.* events generated by fmd or * its client modules. In addition, we use the same log file format to cache * state and events associated with ASRUs that are named in a diagnosis. * * The log files use the exacct format manipulated by libexacct(3LIB) and * originally defined in PSARC 1999/119. However, the exacct library was * designed primarily for read-only clients and without the synchronous i/o * considerations and seeking required for fmd, so we use libexacct here only * to read and write the file headers and to pack data from memory into a file * bytestream. All of the i/o and file offset manipulations are performed by * the fmd code below. Our exacct file management uses the following grammar: * * file := hdr toc event* * hdr := EXD_FMA_LABEL EXD_FMA_VERSION EXD_FMA_OSREL EXD_FMA_OSVER * EXD_FMA_PLAT EXD_FMA_UUID * toc := EXD_FMA_OFFSET * event := EXD_FMA_TODSEC EXD_FMA_TODNSEC EXD_FMA_NVLIST evref* or legacy evref * evref := EXD_FMA_UUID EXD_FMA_OFFSET * legacy evref := EXD_FMA_MAJOR EXD_FMA_MINOR EXD_FMA_INODE EXD_FMA_OFFSET * * Any event can be uniquely identified by the tuple (file, offset) where file * is encoded as (uuid) when we are cross-linking files. For legacy file * formats we still support encoding the reference as (major, minor, inode). * Note that we break out of the file's dev_t into its two 32-bit components to * permit development of either 32-bit or 64-bit log readers and writers; the * LFS APIs do not yet export a 64-bit dev_t to fstat64(), so there is no way * for a 32-bit application to retrieve and store a 64-bit dev_t. * * In order to replay events in the event of an fmd crash, events are initially * written to the error log using the group catalog tag EXD_GROUP_RFMA by the * fmd_log_append() function. Later, once an event transitions from the * received state to one of its other states (see fmd_event.c for details), * fmd_log_commit() is used to overwrite the tag with EXD_GROUP_FMA, indicating * that the event is fully processed and no longer needs to be replayed. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CAT_FMA_RGROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_RFMA) #define CAT_FMA_GROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_FMA) #define CAT_FMA_LABEL (EXT_STRING | EXC_DEFAULT | EXD_FMA_LABEL) #define CAT_FMA_VERSION (EXT_STRING | EXC_DEFAULT | EXD_FMA_VERSION) #define CAT_FMA_OSREL (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSREL) #define CAT_FMA_OSVER (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSVER) #define CAT_FMA_PLAT (EXT_STRING | EXC_DEFAULT | EXD_FMA_PLAT) #define CAT_FMA_UUID (EXT_STRING | EXC_DEFAULT | EXD_FMA_UUID) #define CAT_FMA_TODSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC) #define CAT_FMA_TODNSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC) #define CAT_FMA_NVLIST (EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST) #define CAT_FMA_MAJOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR) #define CAT_FMA_MINOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR) #define CAT_FMA_INODE (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE) #define CAT_FMA_OFFSET (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET) static ssize_t fmd_log_write(fmd_log_t *lp, const void *buf, size_t n) { ssize_t resid = n; ssize_t len; ASSERT(MUTEX_HELD(&lp->log_lock)); while (resid != 0) { if ((len = write(lp->log_fd, buf, resid)) <= 0) break; resid -= len; buf = (char *)buf + len; } if (resid == n && n != 0) return (-1); return (n - resid); } static int fmd_log_write_hdr(fmd_log_t *lp, const char *tag) { ea_object_t hdr, toc, i0, i1, i2, i3, i4, i5, i6; const char *osrel, *osver, *plat; off64_t off = 0; int err = 0; uuid_t uuid; (void) fmd_conf_getprop(fmd.d_conf, "osrelease", &osrel); (void) fmd_conf_getprop(fmd.d_conf, "osversion", &osver); (void) fmd_conf_getprop(fmd.d_conf, "platform", &plat); (void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &lp->log_uuidlen); lp->log_uuid = fmd_zalloc(lp->log_uuidlen + 1, FMD_SLEEP); uuid_generate(uuid); uuid_unparse(uuid, lp->log_uuid); err |= ea_set_group(&hdr, CAT_FMA_GROUP); err |= ea_set_group(&toc, CAT_FMA_GROUP); err |= ea_set_item(&i0, CAT_FMA_LABEL, tag, 0); err |= ea_set_item(&i1, CAT_FMA_VERSION, fmd.d_version, 0); err |= ea_set_item(&i2, CAT_FMA_OSREL, osrel, 0); err |= ea_set_item(&i3, CAT_FMA_OSVER, osver, 0); err |= ea_set_item(&i4, CAT_FMA_PLAT, plat, 0); err |= ea_set_item(&i5, CAT_FMA_UUID, lp->log_uuid, 0); err |= ea_set_item(&i6, CAT_FMA_OFFSET, &off, 0); (void) ea_attach_to_group(&hdr, &i0); (void) ea_attach_to_group(&hdr, &i1); (void) ea_attach_to_group(&hdr, &i2); (void) ea_attach_to_group(&hdr, &i3); (void) ea_attach_to_group(&hdr, &i4); (void) ea_attach_to_group(&hdr, &i5); (void) ea_attach_to_group(&toc, &i6); if (err == 0) { size_t hdr_size = ea_pack_object(&hdr, NULL, 0); size_t toc_size = ea_pack_object(&toc, NULL, 0); size_t size = hdr_size + toc_size; void *buf = fmd_alloc(size, FMD_SLEEP); (void) ea_pack_object(&hdr, buf, hdr_size); (void) ea_pack_object(&toc, (char *)buf + hdr_size, toc_size); if ((lp->log_off = lseek64(lp->log_fd, 0, SEEK_END)) == -1L) fmd_panic("failed to seek log %s", lp->log_name); if (fmd_log_write(lp, buf, size) != size) err = errno; /* save errno for fmd_set_errno() below */ fmd_free(buf, size); lp->log_toc = lp->log_off + hdr_size; lp->log_beg = lp->log_off + hdr_size + toc_size; lp->log_off = lp->log_off + hdr_size + toc_size; if (lp->log_off != lseek64(lp->log_fd, 0, SEEK_END)) fmd_panic("eof off != log_off 0x%llx\n", lp->log_off); } else err = EFMD_LOG_EXACCT; (void) ea_free_item(&i0, EUP_ALLOC); (void) ea_free_item(&i1, EUP_ALLOC); (void) ea_free_item(&i2, EUP_ALLOC); (void) ea_free_item(&i3, EUP_ALLOC); (void) ea_free_item(&i4, EUP_ALLOC); (void) ea_free_item(&i5, EUP_ALLOC); (void) ea_free_item(&i6, EUP_ALLOC); return (err ? fmd_set_errno(err) : 0); } static int fmd_log_check_err(fmd_log_t *lp, int err, const char *msg) { int eaerr = ea_error(); char buf[BUFSIZ]; (void) snprintf(buf, sizeof (buf), "%s: %s: %s\n", lp->log_name, msg, eaerr != EXR_OK ? fmd_ea_strerror(eaerr) : "catalog tag mismatch"); fmd_error(err, buf); return (fmd_set_errno(err)); } static int fmd_log_check_hdr(fmd_log_t *lp, const char *tag) { int got_version = 0, got_label = 0; ea_object_t *grp, *obj; off64_t hdr_off, hdr_size; int dvers, fvers; const char *p; ea_clear(&lp->log_ea); /* resync exacct file */ if ((hdr_off = lseek64(lp->log_fd, 0, SEEK_CUR)) == -1L) fmd_panic("failed to seek log %s", lp->log_name); /* * Read the first group of log meta-data: the write-once read-only * file header. We read all records in this group, ignoring all but * the VERSION and LABEL, which are required and must be verified. */ if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL || grp->eo_catalog != CAT_FMA_GROUP) { ea_free_object(grp, EUP_ALLOC); return (fmd_log_check_err(lp, EFMD_LOG_INVAL, "invalid fma hdr record group")); } for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { switch (obj->eo_catalog) { case CAT_FMA_VERSION: for (dvers = 0, p = fmd.d_version; *p != '\0'; p++) { if (isdigit(*p)) dvers = dvers * 10 + (*p - '0'); else break; } for (fvers = 0, p = obj->eo_item.ei_string; *p != '\0'; p++) { if (isdigit(*p)) fvers = fvers * 10 + (*p - '0'); else break; } if (fvers > dvers) { fmd_error(EFMD_LOG_INVAL, "%s: log version " "%s is not supported by this daemon\n", lp->log_name, obj->eo_item.ei_string); ea_free_object(grp, EUP_ALLOC); return (fmd_set_errno(EFMD_LOG_VERSION)); } got_version++; break; case CAT_FMA_LABEL: if (strcmp(obj->eo_item.ei_string, tag) != 0) { fmd_error(EFMD_LOG_INVAL, "%s: log tag '%s' " "does not matched expected tag '%s'\n", lp->log_name, obj->eo_item.ei_string, tag); ea_free_object(grp, EUP_ALLOC); return (fmd_set_errno(EFMD_LOG_INVAL)); } got_label++; break; case CAT_FMA_UUID: lp->log_uuid = fmd_strdup(obj->eo_item.ei_string, FMD_SLEEP); lp->log_uuidlen = strlen(lp->log_uuid); break; } } hdr_size = ea_pack_object(grp, NULL, 0); ea_free_object(grp, EUP_ALLOC); if (!got_version || !got_label) { fmd_error(EFMD_LOG_INVAL, "%s: fmd hdr record group did not " "include mandatory version and/or label\n", lp->log_name); return (fmd_set_errno(EFMD_LOG_INVAL)); } /* * Read the second group of log meta-data: the table of contents. We * expect this group to contain an OFFSET object indicating the current * value of log_skip. We save this in our fmd_log_t and then return. */ if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL || grp->eo_catalog != CAT_FMA_GROUP || grp->eo_group.eg_nobjs < 1 || grp->eo_group.eg_objs->eo_catalog != CAT_FMA_OFFSET) { ea_free_object(grp, EUP_ALLOC); return (fmd_log_check_err(lp, EFMD_LOG_INVAL, "invalid fma toc record group")); } lp->log_toc = hdr_off + hdr_size; lp->log_beg = hdr_off + hdr_size + ea_pack_object(grp, NULL, 0); lp->log_off = lseek64(lp->log_fd, 0, SEEK_END); lp->log_skip = grp->eo_group.eg_objs->eo_item.ei_uint64; if (lp->log_skip > lp->log_off) { fmd_error(EFMD_LOG_INVAL, "%s: skip %llx exceeds file size; " "resetting to zero\n", lp->log_name, lp->log_skip); lp->log_skip = 0; } ea_free_object(grp, EUP_ALLOC); return (0); } static int fmd_log_open_exacct(fmd_log_t *lp, int aflags, int oflags) { int fd = dup(lp->log_fd); const char *creator; (void) fmd_conf_getprop(fmd.d_conf, "log.creator", &creator); if (ea_fdopen(&lp->log_ea, fd, creator, aflags, oflags) != 0) { fmd_error(EFMD_LOG_EXACCT, "%s: failed to open log file: %s\n", lp->log_name, fmd_ea_strerror(ea_error())); (void) close(fd); return (fmd_set_errno(EFMD_LOG_EXACCT)); } lp->log_flags |= FMD_LF_EAOPEN; return (0); } static fmd_log_t * fmd_log_xopen(const char *root, const char *name, const char *tag, int oflags) { fmd_log_t *lp = fmd_zalloc(sizeof (fmd_log_t), FMD_SLEEP); char buf[PATH_MAX]; char *slash = "/"; size_t len; int err; (void) pthread_mutex_init(&lp->log_lock, NULL); (void) pthread_cond_init(&lp->log_cv, NULL); (void) pthread_mutex_lock(&lp->log_lock); if (strcmp(root, "") == 0) slash = ""; len = strlen(root) + strlen(name) + strlen(slash) + 1; /* for "\0" */ lp->log_name = fmd_alloc(len, FMD_SLEEP); (void) snprintf(lp->log_name, len, "%s%s%s", root, slash, name); lp->log_tag = fmd_strdup(tag, FMD_SLEEP); (void) fmd_conf_getprop(fmd.d_conf, "log.minfree", &lp->log_minfree); if (strcmp(lp->log_tag, FMD_LOG_ERROR) == 0) lp->log_flags |= FMD_LF_REPLAY; if (strcmp(lp->log_tag, FMD_LOG_XPRT) == 0) oflags &= ~O_SYNC; top: if ((lp->log_fd = open64(lp->log_name, oflags, 0644)) == -1 || fstat64(lp->log_fd, &lp->log_stat) == -1) { fmd_error(EFMD_LOG_OPEN, "failed to open log %s", lp->log_name); fmd_log_close(lp); return (NULL); } /* * If our open() created the log file, use libexacct to write a header * and position the file just after the header (EO_TAIL). If the log * file already existed, use libexacct to validate the header and again * position the file just after the header (EO_HEAD). Note that we lie * to libexacct about 'oflags' in order to achieve the desired result. */ if (lp->log_stat.st_size == 0) { err = fmd_log_open_exacct(lp, EO_VALID_HDR | EO_TAIL, O_CREAT | O_WRONLY) || fmd_log_write_hdr(lp, tag); } else { err = fmd_log_open_exacct(lp, EO_VALID_HDR | EO_HEAD, O_RDONLY) || fmd_log_check_hdr(lp, tag); } /* * If ea_fdopen() failed and the log was pre-existing, attempt to move * it aside and start a new one. If we created the log but failed to * initialize it, then we have no choice but to give up (e.g. EROFS). */ if (err) { fmd_error(EFMD_LOG_OPEN, "failed to initialize log %s", lp->log_name); if (lp->log_flags & FMD_LF_EAOPEN) { lp->log_flags &= ~FMD_LF_EAOPEN; (void) ea_close(&lp->log_ea); } (void) close(lp->log_fd); lp->log_fd = -1; if (lp->log_stat.st_size != 0 && snprintf(buf, sizeof (buf), "%s-", lp->log_name) < PATH_MAX && rename(lp->log_name, buf) == 0) { TRACE((FMD_DBG_LOG, "mv %s to %s", lp->log_name, buf)); if (oflags & O_CREAT) goto top; } fmd_log_close(lp); return (NULL); } lp->log_refs++; (void) pthread_mutex_unlock(&lp->log_lock); return (lp); } fmd_log_t * fmd_log_tryopen(const char *root, const char *name, const char *tag) { return (fmd_log_xopen(root, name, tag, O_RDWR | O_SYNC)); } fmd_log_t * fmd_log_open(const char *root, const char *name, const char *tag) { return (fmd_log_xopen(root, name, tag, O_RDWR | O_CREAT | O_SYNC)); } void fmd_log_close(fmd_log_t *lp) { ASSERT(MUTEX_HELD(&lp->log_lock)); ASSERT(lp->log_refs == 0); if ((lp->log_flags & FMD_LF_EAOPEN) && ea_close(&lp->log_ea) != 0) { fmd_error(EFMD_LOG_CLOSE, "failed to close log %s: %s\n", lp->log_name, fmd_ea_strerror(ea_error())); } if (lp->log_fd >= 0 && close(lp->log_fd) != 0) { fmd_error(EFMD_LOG_CLOSE, "failed to close log %s", lp->log_name); } fmd_strfree(lp->log_name); fmd_strfree(lp->log_tag); if (lp->log_uuid != NULL) fmd_free(lp->log_uuid, lp->log_uuidlen + 1); fmd_free(lp, sizeof (fmd_log_t)); } void fmd_log_hold_pending(fmd_log_t *lp) { (void) pthread_mutex_lock(&lp->log_lock); lp->log_refs++; ASSERT(lp->log_refs != 0); if (lp->log_flags & FMD_LF_REPLAY) { lp->log_pending++; ASSERT(lp->log_pending != 0); } (void) pthread_mutex_unlock(&lp->log_lock); } void fmd_log_hold(fmd_log_t *lp) { (void) pthread_mutex_lock(&lp->log_lock); lp->log_refs++; ASSERT(lp->log_refs != 0); (void) pthread_mutex_unlock(&lp->log_lock); } void fmd_log_rele(fmd_log_t *lp) { (void) pthread_mutex_lock(&lp->log_lock); ASSERT(lp->log_refs != 0); if (--lp->log_refs == 0) fmd_log_close(lp); else (void) pthread_mutex_unlock(&lp->log_lock); } void fmd_log_append(fmd_log_t *lp, fmd_event_t *e, fmd_case_t *cp) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; int err = 0; ea_object_t grp0, grp1, i0, i1, i2, *items; ea_object_t **fe = NULL; size_t nvsize, easize, itsize, frsize; char *nvbuf, *eabuf; statvfs64_t stv; (void) pthread_mutex_lock(&ep->ev_lock); ASSERT(ep->ev_flags & FMD_EVF_VOLATILE); ASSERT(ep->ev_log == NULL); (void) nvlist_size(ep->ev_nvl, &nvsize, NV_ENCODE_XDR); nvbuf = fmd_alloc(nvsize, FMD_SLEEP); (void) nvlist_pack(ep->ev_nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0); if (lp->log_flags & FMD_LF_REPLAY) err |= ea_set_group(&grp0, CAT_FMA_RGROUP); else err |= ea_set_group(&grp0, CAT_FMA_GROUP); err |= ea_set_item(&i0, CAT_FMA_TODSEC, &ep->ev_time.ftv_sec, 0); err |= ea_set_item(&i1, CAT_FMA_TODNSEC, &ep->ev_time.ftv_nsec, 0); err |= ea_set_item(&i2, CAT_FMA_NVLIST, nvbuf, nvsize); if (err != 0) { (void) pthread_mutex_unlock(&ep->ev_lock); err = EFMD_LOG_EXACCT; goto exerr; } (void) ea_attach_to_group(&grp0, &i0); (void) ea_attach_to_group(&grp0, &i1); (void) ea_attach_to_group(&grp0, &i2); /* * If this event has a case associated with it (i.e. it is a list), * then allocate a block of ea_object_t's and fill in a group for * each event saved in the case's item list. For each such group, * we attach it to grp1, which in turn will be attached to grp0. */ if (cp != NULL) { ea_object_t *egrp, *ip, **fp; fmd_event_impl_t *eip; fmd_case_item_t *cit; (void) ea_set_group(&grp1, CAT_FMA_GROUP); frsize = sizeof (ea_object_t *) * cip->ci_nitems; itsize = sizeof (ea_object_t) * cip->ci_nitems * 5; items = ip = fmd_alloc(itsize, FMD_SLEEP); for (cit = cip->ci_items; cit != NULL; cit = cit->cit_next) { major_t maj; minor_t min; eip = (fmd_event_impl_t *)cit->cit_event; if (eip->ev_log == NULL) continue; /* event was never logged */ maj = major(eip->ev_log->log_stat.st_dev); min = minor(eip->ev_log->log_stat.st_dev); (void) ea_set_group(ip, CAT_FMA_GROUP); egrp = ip++; /* first obj is group */ /* * If the event log file is in legacy format, * then write the xref to the file in the legacy * maj/min/inode method else write it using the * file uuid. */ if (eip->ev_log->log_uuid == NULL) { (void) ea_set_item(ip, CAT_FMA_MAJOR, &maj, 0); (void) ea_attach_to_group(egrp, ip++); (void) ea_set_item(ip, CAT_FMA_MINOR, &min, 0); (void) ea_attach_to_group(egrp, ip++); (void) ea_set_item(ip, CAT_FMA_INODE, &eip->ev_log->log_stat.st_ino, 0); (void) ea_attach_to_group(egrp, ip++); } else { if (ea_set_item(ip, CAT_FMA_UUID, eip->ev_log->log_uuid, 0) == -1) { err = EFMD_LOG_EXACCT; goto exerrcp; } if (fe == NULL) fe = fp = fmd_zalloc(frsize, FMD_SLEEP); *fp++ = ip; (void) ea_attach_to_group(egrp, ip++); } (void) ea_set_item(ip, CAT_FMA_OFFSET, &eip->ev_off, 0); (void) ea_attach_to_group(egrp, ip++); (void) ea_attach_to_group(&grp1, egrp); } (void) ea_attach_to_group(&grp0, &grp1); } easize = ea_pack_object(&grp0, NULL, 0); eabuf = fmd_alloc(easize, FMD_SLEEP); (void) ea_pack_object(&grp0, eabuf, easize); /* * Before writing the record, check to see if this would cause the free * space in the filesystem to drop below our minfree threshold. If so, * don't bother attempting the write and instead pretend it failed. As * fmd(8) runs as root, it will be able to access the space "reserved" * for root, and therefore can run the system of out of disk space in a * heavy error load situation, violating the basic design principle of * fmd(8) that we don't want to make a bad situation even worse. */ (void) pthread_mutex_lock(&lp->log_lock); if (lp->log_minfree != 0 && fstatvfs64(lp->log_fd, &stv) == 0 && stv.f_bavail * stv.f_frsize < lp->log_minfree + easize) { TRACE((FMD_DBG_LOG, "append %s crosses minfree", lp->log_tag)); err = EFMD_LOG_MINFREE; } else if (fmd_log_write(lp, eabuf, easize) == easize) { TRACE((FMD_DBG_LOG, "append %s %p off=0x%llx", lp->log_tag, (void *)ep, (u_longlong_t)lp->log_off)); ep->ev_flags &= ~FMD_EVF_VOLATILE; ep->ev_log = lp; ep->ev_off = lp->log_off; ep->ev_len = easize; if (lp->log_flags & FMD_LF_REPLAY) { lp->log_pending++; ASSERT(lp->log_pending != 0); } lp->log_refs++; ASSERT(lp->log_refs != 0); lp->log_off += easize; } else { err = errno; /* save errno for fmd_error() call below */ /* * If we can't write append the record, seek the file back to * the original location and truncate it there in order to make * sure the file is always in a sane state w.r.t. libexacct. */ (void) lseek64(lp->log_fd, lp->log_off, SEEK_SET); (void) ftruncate64(lp->log_fd, lp->log_off); } (void) pthread_mutex_unlock(&lp->log_lock); (void) pthread_mutex_unlock(&ep->ev_lock); fmd_free(eabuf, easize); exerrcp: if (cp != NULL) { if (fe != NULL) { ea_object_t **fp = fe; int i = 0; for (; *fp != NULL && i < cip->ci_nitems; i++) (void) ea_free_item(*fp++, EUP_ALLOC); fmd_free(fe, frsize); } fmd_free(items, itsize); } exerr: fmd_free(nvbuf, nvsize); (void) ea_free_item(&i0, EUP_ALLOC); (void) ea_free_item(&i1, EUP_ALLOC); (void) ea_free_item(&i2, EUP_ALLOC); /* * Keep track of out-of-space errors using global statistics. As we're * out of disk space, it's unlikely the EFMD_LOG_APPEND will be logged. */ if (err == ENOSPC || err == EFMD_LOG_MINFREE) { fmd_stat_t *sp; if (lp == fmd.d_errlog) sp = &fmd.d_stats->ds_err_enospc; else if (lp == fmd.d_fltlog) sp = &fmd.d_stats->ds_flt_enospc; else sp = &fmd.d_stats->ds_oth_enospc; (void) pthread_mutex_lock(&fmd.d_stats_lock); sp->fmds_value.ui64++; (void) pthread_mutex_unlock(&fmd.d_stats_lock); } if (err != 0) { fmd_error(EFMD_LOG_APPEND, "failed to log_append %s %p: %s\n", lp->log_tag, (void *)ep, fmd_strerror(err)); } } /* * Commit an event to the log permanently, indicating that it should not be * replayed on restart. This is done by overwriting the event group's catalog * code with EXD_GROUP_FMA (from EXD_GROUP_RFMA used in fmd_log_append()). We * use pwrite64() to update the existing word directly, using somewhat guilty * knowledge that exacct stores the 32-bit catalog word first for each object. * Since we are overwriting an existing log location using pwrite64() and hold * the event lock, we do not need to hold the log_lock during the i/o. */ void fmd_log_commit(fmd_log_t *lp, fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; ea_catalog_t c; int err = 0; if (!(lp->log_flags & FMD_LF_REPLAY)) return; /* log does not require replay tagging */ ASSERT(MUTEX_HELD(&ep->ev_lock)); ASSERT(ep->ev_log == lp && ep->ev_off != 0); c = CAT_FMA_GROUP; exacct_order32(&c); if (pwrite64(lp->log_fd, &c, sizeof (c), ep->ev_off) == sizeof (c)) { TRACE((FMD_DBG_LOG, "commit %s %p", lp->log_tag, (void *)ep)); ep->ev_flags &= ~FMD_EVF_REPLAY; /* * If we have committed the event, check to see if the TOC skip * offset needs to be updated, and decrement the pending count. */ (void) pthread_mutex_lock(&lp->log_lock); if (lp->log_skip == ep->ev_off) { lp->log_flags |= FMD_LF_DIRTY; lp->log_skip += ep->ev_len; } ASSERT(lp->log_pending != 0); lp->log_pending--; (void) pthread_cond_broadcast(&lp->log_cv); (void) pthread_mutex_unlock(&lp->log_lock); } else { fmd_error(EFMD_LOG_COMMIT, "failed to log_commit %s %p: %s\n", lp->log_tag, (void *)ep, fmd_strerror(err)); } } /* * If we need to destroy an event and it wasn't able to be committed, we permit * the owner to decommit from ever trying again. This operation decrements the * pending count on the log and broadcasts to anyone waiting on log_cv. */ void fmd_log_decommit(fmd_log_t *lp, fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; if (!(lp->log_flags & FMD_LF_REPLAY)) return; /* log does not require replay tagging */ ASSERT(MUTEX_HELD(&ep->ev_lock)); ASSERT(ep->ev_log == lp); (void) pthread_mutex_lock(&lp->log_lock); TRACE((FMD_DBG_LOG, "decommit %s %p", lp->log_tag, (void *)ep)); ep->ev_flags &= ~FMD_EVF_REPLAY; ASSERT(lp->log_pending != 0); lp->log_pending--; (void) pthread_cond_broadcast(&lp->log_cv); (void) pthread_mutex_unlock(&lp->log_lock); } static fmd_event_t * fmd_log_unpack(fmd_log_t *lp, ea_object_t *grp, off64_t off) { fmd_timeval_t ftv = { -1ULL, -1ULL }; nvlist_t *nvl = NULL; ea_object_t *obj; char *class; int err; for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) { switch (obj->eo_catalog) { case CAT_FMA_NVLIST: if ((err = nvlist_xunpack(obj->eo_item.ei_raw, obj->eo_item.ei_size, &nvl, &fmd.d_nva)) != 0) { fmd_error(EFMD_LOG_UNPACK, "failed to unpack " "log nvpair: %s\n", fmd_strerror(err)); return (NULL); } break; case CAT_FMA_TODSEC: ftv.ftv_sec = obj->eo_item.ei_uint64; break; case CAT_FMA_TODNSEC: ftv.ftv_nsec = obj->eo_item.ei_uint64; break; } } if (nvl == NULL || ftv.ftv_sec == -1ULL || ftv.ftv_nsec == -1ULL) { fmd_error(EFMD_LOG_UNPACK, "failed to unpack log event: " "required object(s) missing from record group\n"); nvlist_free(nvl); return (NULL); } if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) { fmd_error(EFMD_LOG_UNPACK, "failed to unpack log event: " "record is missing required '%s' nvpair\n", FM_CLASS); nvlist_free(nvl); return (NULL); } return (fmd_event_recreate(FMD_EVT_PROTOCOL, &ftv, nvl, class, lp, off, ea_pack_object(grp, NULL, 0))); } /* * Replay event(s) from the specified log by invoking the specified callback * function 'func' for each event. If the log has the FMD_LF_REPLAY flag set, * we replay all events after log_skip that have the FMA_RGROUP group tag. * This mode is used for the error telemetry log. If the log does not have * this flag set (used for ASRU logs), only the most recent event is replayed. */ void fmd_log_replay(fmd_log_t *lp, fmd_log_f *func, void *data) { ea_object_t obj, *grp; ea_object_type_t type; ea_catalog_t c; fmd_event_t *ep; off64_t off, skp; uint_t n = 0; (void) pthread_mutex_lock(&lp->log_lock); if (lp->log_stat.st_size == 0 && (lp->log_flags & FMD_LF_REPLAY)) { (void) pthread_mutex_unlock(&lp->log_lock); return; /* we just created this log: never replay events */ } while (lp->log_flags & FMD_LF_BUSY) (void) pthread_cond_wait(&lp->log_cv, &lp->log_lock); if (lp->log_off == lp->log_beg) { (void) pthread_mutex_unlock(&lp->log_lock); return; /* no records appended yet */ } lp->log_flags |= FMD_LF_BUSY; skp = lp->log_skip; ea_clear(&lp->log_ea); /* resync exacct file */ /* * If FMD_LF_REPLAY is set, begin our replay at either log_skip (if it * is non-zero) or at log_beg. Otherwise replay from the end (log_off) */ if (lp->log_flags & FMD_LF_REPLAY) { off = MAX(lp->log_beg, lp->log_skip); c = CAT_FMA_RGROUP; } else { off = lp->log_off; c = CAT_FMA_GROUP; } if (lseek64(lp->log_fd, off, SEEK_SET) != off) { fmd_panic("failed to seek %s to 0x%llx\n", lp->log_name, (u_longlong_t)off); } /* * If FMD_LF_REPLAY is not set, back up to the start of the previous * object and make sure this object is an EO_GROUP; otherwise return. */ if (!(lp->log_flags & FMD_LF_REPLAY) && (type = ea_previous_object(&lp->log_ea, &obj)) != EO_GROUP) { fmd_error(EFMD_LOG_REPLAY, "last log object is of unexpected " "type %d (log may be truncated or corrupt)\n", type); goto out; } while ((grp = ea_get_object_tree(&lp->log_ea, 1)) != NULL) { if (!(lp->log_flags & FMD_LF_REPLAY)) off -= ea_pack_object(grp, NULL, 0); else if (n == 0 && grp->eo_catalog == CAT_FMA_GROUP) skp = off; /* update skip */ /* * We temporarily drop log_lock around the call to unpack the * event, hold it, and perform the callback, because these * operations may try to acquire log_lock to bump log_refs. * We cannot lose control because the FMD_LF_BUSY flag is set. */ (void) pthread_mutex_unlock(&lp->log_lock); if (grp->eo_catalog == c && (ep = fmd_log_unpack(lp, grp, off)) != NULL) { TRACE((FMD_DBG_LOG, "replay %s %p off %llx", lp->log_tag, (void *)ep, (u_longlong_t)off)); fmd_event_hold(ep); func(lp, ep, data); fmd_event_rele(ep); n++; } (void) pthread_mutex_lock(&lp->log_lock); off += ea_pack_object(grp, NULL, 0); ea_free_object(grp, EUP_ALLOC); } if (ea_error() != EXR_EOF) { fmd_error(EFMD_LOG_REPLAY, "failed to replay %s event at " "offset 0x%llx: %s\n", lp->log_name, (u_longlong_t)off, fmd_ea_strerror(ea_error())); } if (n == 0) skp = off; /* if no replays, move skip to where we ended up */ out: if (lseek64(lp->log_fd, lp->log_off, SEEK_SET) != lp->log_off) { fmd_panic("failed to seek %s to 0x%llx\n", lp->log_name, (u_longlong_t)lp->log_off); } if (skp != lp->log_skip) { lp->log_flags |= FMD_LF_DIRTY; lp->log_skip = skp; } lp->log_flags &= ~FMD_LF_BUSY; (void) pthread_cond_broadcast(&lp->log_cv); (void) pthread_mutex_unlock(&lp->log_lock); } void fmd_log_update(fmd_log_t *lp) { ea_object_t toc, item; off64_t skip = 0; size_t size; void *buf; (void) pthread_mutex_lock(&lp->log_lock); if (lp->log_flags & FMD_LF_DIRTY) { lp->log_flags &= ~FMD_LF_DIRTY; skip = lp->log_skip; } (void) pthread_mutex_unlock(&lp->log_lock); /* * If the skip needs to be updated, construct a TOC record group * containing the skip offset and overwrite the TOC in-place. */ if (skip != 0 && ea_set_group(&toc, CAT_FMA_GROUP) == 0 && ea_set_item(&item, CAT_FMA_OFFSET, &skip, 0) == 0) { (void) ea_attach_to_group(&toc, &item); size = ea_pack_object(&toc, NULL, 0); buf = fmd_alloc(size, FMD_SLEEP); (void) ea_pack_object(&toc, buf, size); ASSERT(lp->log_toc + size == lp->log_beg); if (pwrite64(lp->log_fd, buf, size, lp->log_toc) == size) { TRACE((FMD_DBG_LOG, "updated skip to %llx", skip)); } else { fmd_error(EFMD_LOG_UPDATE, "failed to log_update %s", lp->log_tag); } fmd_free(buf, size); (void) ea_free_item(&item, EUP_ALLOC); } } /* * Rotate the specified log by renaming its underlying file to a staging file * that can be handed off to logadm(8) or an administrator script. If the * rename succeeds, open a new log file using the old path and return it. * Note that we are relying our caller to use some higher-level mechanism to * ensure that fmd_log_rotate() cannot be called while other threads are * attempting fmd_log_append() using the same log (fmd's d_log_lock is used * for the global errlog and fltlog). */ fmd_log_t * fmd_log_rotate(fmd_log_t *lp) { char npath[PATH_MAX]; fmd_log_t *nlp; (void) snprintf(npath, sizeof (npath), "%s+", lp->log_name); /* * Open new log file. */ if ((nlp = fmd_log_open("", npath, lp->log_tag)) == NULL) { fmd_error(EFMD_LOG_ROTATE, "failed to open %s", npath); (void) fmd_set_errno(EFMD_LOG_ROTATE); return (NULL); } (void) snprintf(npath, sizeof (npath), "%s.0-", lp->log_name); (void) pthread_mutex_lock(&lp->log_lock); /* * Check for any pending commits to drain before proceeding. We can't * rotate the log out if commits are pending because if we die after * the log is moved aside, we won't be able to replay them on restart. */ if (lp->log_pending != 0) { (void) pthread_mutex_unlock(&lp->log_lock); (void) unlink(nlp->log_name); fmd_log_rele(nlp); (void) fmd_set_errno(EFMD_LOG_ROTBUSY); return (NULL); } if (rename(lp->log_name, npath) != 0) { (void) pthread_mutex_unlock(&lp->log_lock); fmd_error(EFMD_LOG_ROTATE, "failed to rename %s", lp->log_name); (void) unlink(nlp->log_name); fmd_log_rele(nlp); (void) fmd_set_errno(EFMD_LOG_ROTATE); return (NULL); } if (rename(nlp->log_name, lp->log_name) != 0) { (void) pthread_mutex_unlock(&lp->log_lock); fmd_error(EFMD_LOG_ROTATE, "failed to rename %s", nlp->log_name); (void) unlink(nlp->log_name); fmd_log_rele(nlp); (void) fmd_set_errno(EFMD_LOG_ROTATE); return (NULL); } /* * Change name of new log file */ fmd_strfree(nlp->log_name); nlp->log_name = fmd_strdup(lp->log_name, FMD_SLEEP); /* * If we've rotated the log, no pending events exist so we don't have * any more commits coming, and our caller should have arranged for * no more calls to append. As such, we can close log_fd for good. */ if (lp->log_flags & FMD_LF_EAOPEN) { (void) ea_close(&lp->log_ea); lp->log_flags &= ~FMD_LF_EAOPEN; } (void) close(lp->log_fd); lp->log_fd = -1; (void) pthread_mutex_unlock(&lp->log_lock); return (nlp); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_LOG_H #define _FMD_LOG_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include typedef struct fmd_log { char *log_name; /* file pathname */ char *log_tag; /* file content tag */ int log_fd; /* file descriptor */ struct stat64 log_stat; /* status of file at log_open() time */ ea_file_t log_ea; /* exacct file structure */ pthread_mutex_t log_lock; /* lock for flags, refs, off, append */ pthread_cond_t log_cv; /* condition variable for waiters */ int log_flags; /* file flags (see below) */ uint_t log_refs; /* file reference count */ uint_t log_pending; /* number of pending log commits */ off64_t log_toc; /* offset of table of contents */ off64_t log_beg; /* offset of first data record */ off64_t log_off; /* offset at which to append */ off64_t log_skip; /* offset to skip to for replay */ uint64_t log_minfree; /* minimum free bytes for filesystem */ char *log_uuid; /* uuid string for this log file */ uint_t log_uuidlen; /* length of log_uuid (not incl. \0) */ } fmd_log_t; #define FMD_LF_EAOPEN 0x1 /* log_ea is open and valid */ #define FMD_LF_REPLAY 0x2 /* log records should use replay tag */ #define FMD_LF_DIRTY 0x4 /* log toc should be updated */ #define FMD_LF_BUSY 0x8 /* log is busy; skip updates */ typedef void fmd_log_f(fmd_log_t *, fmd_event_t *, void *); #define FMD_LOG_ERROR "error" /* tag for error log files */ #define FMD_LOG_FAULT "fault" /* tag for fault log files */ #define FMD_LOG_ASRU "asru" /* tag for asru log files */ #define FMD_LOG_XPRT "xprt" /* tag for transport log files */ #define FMD_LOG_INFO "info" /* tag for info event log files */ extern fmd_log_t *fmd_log_tryopen(const char *, const char *, const char *); extern fmd_log_t *fmd_log_open(const char *, const char *, const char *); extern void fmd_log_close(fmd_log_t *); extern void fmd_log_hold_pending(fmd_log_t *); extern void fmd_log_hold(fmd_log_t *); extern void fmd_log_rele(fmd_log_t *); extern void fmd_log_append(fmd_log_t *, fmd_event_t *, fmd_case_t *); extern void fmd_log_commit(fmd_log_t *, fmd_event_t *); extern void fmd_log_decommit(fmd_log_t *, fmd_event_t *); extern void fmd_log_replay(fmd_log_t *, fmd_log_f *, void *); extern void fmd_log_update(fmd_log_t *); extern fmd_log_t *fmd_log_rotate(fmd_log_t *); #ifdef __cplusplus } #endif #endif /* _FMD_LOG_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include fmd_t fmd; mutex_t _svcstate_lock = ERRORCHECKMUTEX; /* * For DEBUG builds, we define a set of hooks for libumem that provide useful * default settings for the allocator's debugging facilities. */ #ifdef DEBUG const char * _umem_debug_init() { return ("default,verbose"); /* $UMEM_DEBUG setting */ } const char * _umem_logging_init(void) { return ("fail,contents"); /* $UMEM_LOGGING setting */ } #endif /* DEBUG */ /* * We use a two-phase algorithm for becoming a daemon because we want the * daemon process (the child) to do the work of becoming MT-hot and opening our * event transport. Since these operations can fail and need to result in the * daemon failing to start, the parent must wait until fmd_run() completes to * know whether it can return zero or non-zero status to the invoking command. * The parent waits on a pipe inside this function to read the exit status. * The child gets the write-end of the pipe returned by daemonize_init() and * then fmd_run() uses the pipe to set the exit status and detach the parent. */ static int daemonize_init(void) { const char *gzp1, *gzp2, *gzp3, *gzp4, *gzp5; int status, pfds[2]; sigset_t set, oset; struct rlimit rlim; char path[PATH_MAX]; pid_t pid; /* * Set our per-process core file path to leave core files in our * var/fm/fmd directory, named after the PID to aid in debugging, * and make sure that there is no restriction on core file size. */ (void) snprintf(path, sizeof (path), "%s/var/fm/fmd/core.%s.%%p", fmd.d_rootdir, fmd.d_pname); (void) core_set_process_path(path, strlen(path) + 1, fmd.d_pid); rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; (void) setrlimit(RLIMIT_CORE, &rlim); /* * Claim all the file descriptors we can. */ if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; (void) setrlimit(RLIMIT_NOFILE, &rlim); } /* * Reset all of our privilege sets to the minimum set of required * privileges. We continue to run as root so that files we create * such as logs and checkpoints are secured in the /var filesystem. * * In a non-global zone some of the privileges we retain in a * global zone are only optionally assigned to the zone, while others * are prohibited: * * PRIV_PROC_PRIOCNTL (optional in a non-global zone): * There are no calls to priocntl(2) in fmd or plugins. * * PRIV_SYS_CONFIG (prohibited in a non-global zone): * Required, I think, for sysevent_post_event and/or * other legacy sysevent activity. Legacy sysevent is not * supported in a non-global zone. * * PRIV_SYS_DEVICES (prohibited in a non-global zone): * Needed in the global zone for ioctls on various drivers * such as memory-controller drivers. * * PRIV_SYS_RES_CONFIG (prohibited in a non-global zone): * Require for p_online(2) calls to offline cpus. * * PRIV_SYS_NET_CONFIG (prohibited in a non-global zone): * Required for ipsec in etm (which also requires * PRIV_NET_PRIVADDR). * * We do without those privileges in a non-global zone. It's * possible that there are other privs we could drop since * hardware-related plugins are not present. */ if (getzoneid() == GLOBAL_ZONEID) { gzp1 = PRIV_PROC_PRIOCNTL; gzp2 = PRIV_SYS_CONFIG; gzp3 = PRIV_SYS_DEVICES; gzp4 = PRIV_SYS_RES_CONFIG; gzp5 = PRIV_SYS_NET_CONFIG; } else { gzp1 = gzp2 = gzp3 = gzp4 = gzp5 = NULL; } if (__init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS, 0, 0, /* run as uid 0 and gid 0 */ PRIV_FILE_DAC_EXECUTE, PRIV_FILE_DAC_READ, PRIV_FILE_DAC_SEARCH, PRIV_FILE_DAC_WRITE, PRIV_FILE_OWNER, PRIV_PROC_OWNER, PRIV_SYS_ADMIN, PRIV_NET_PRIVADDR, gzp1, gzp2, gzp3, gzp4, gzp5, NULL) != 0) fmd_error(EFMD_EXIT, "additional privileges required to run\n"); /* * Block all signals prior to the fork and leave them blocked in the * parent so we don't get in a situation where the parent gets SIGINT * and returns non-zero exit status and the child is actually running. * In the child, restore the signal mask once we've done our setsid(). */ (void) sigfillset(&set); (void) sigdelset(&set, SIGABRT); (void) sigprocmask(SIG_BLOCK, &set, &oset); if (pipe(pfds) == -1) fmd_error(EFMD_EXIT, "failed to create pipe for daemonize"); if ((pid = fork()) == -1) fmd_error(EFMD_EXIT, "failed to fork into background"); /* * If we're the parent process, wait for either the child to send us * the appropriate exit status over the pipe or for the read to fail * (presumably with 0 for EOF if our child terminated abnormally). * If the read fails, exit with either the child's exit status if it * exited or with FMD_EXIT_ERROR if it died from a fatal signal. */ if (pid != 0) { (void) close(pfds[1]); if (read(pfds[0], &status, sizeof (status)) == sizeof (status)) _exit(status); if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) _exit(WEXITSTATUS(status)); _exit(FMD_EXIT_ERROR); } fmd.d_pid = getpid(); (void) setsid(); (void) sigprocmask(SIG_SETMASK, &oset, NULL); (void) chdir("/"); (void) umask(022); (void) close(pfds[0]); return (pfds[1]); } static void daemonize_fini(int fd) { (void) close(fd); if ((fd = open("/dev/null", O_RDWR)) >= 0) { (void) fcntl(fd, F_DUP2FD, STDIN_FILENO); (void) fcntl(fd, F_DUP2FD, STDOUT_FILENO); (void) fcntl(fd, F_DUP2FD, STDERR_FILENO); (void) close(fd); } } static void handler(int sig) { if (fmd.d_signal == 0) fmd.d_signal = sig; } static int usage(const char *arg0, FILE *fp) { (void) fprintf(fp, "Usage: %s [-V] [-f file] [-o opt=val] [-R dir]\n", arg0); return (FMD_EXIT_USAGE); } int main(int argc, char *argv[]) { const char *opt_f = NULL, *opt_R = NULL; const char optstr[] = "f:o:R:V"; int c, pfd = -1, opt_V = 0; char *p; struct sigaction act; sigset_t set; /* * Parse the command-line once to validate all options and retrieve * any overrides for our configuration file and root directory. */ while ((c = getopt(argc, argv, optstr)) != EOF) { switch (c) { case 'f': opt_f = optarg; break; case 'o': break; /* handle -o below */ case 'R': opt_R = optarg; break; case 'V': opt_V++; break; default: return (usage(argv[0], stderr)); } } if (optind < argc) return (usage(argv[0], stderr)); if (opt_V) { #ifdef DEBUG const char *debug = " (DEBUG)"; #else const char *debug = ""; #endif (void) printf("%s: version %s%s\n", argv[0], _fmd_version, debug); return (FMD_EXIT_SUCCESS); } closefrom(STDERR_FILENO + 1); fmd_create(&fmd, argv[0], opt_R, opt_f); /* * Now that we've initialized our global state, parse the command-line * again for any configuration options specified using -o and set them. */ for (optind = 1; (c = getopt(argc, argv, optstr)) != EOF; ) { if (c == 'o') { if ((p = strchr(optarg, '=')) == NULL) { (void) fprintf(stderr, "%s: failed to set " "option -o %s: option requires value\n", fmd.d_pname, optarg); return (FMD_EXIT_USAGE); } *p++ = '\0'; /* strike out the delimiter */ if (p[0] == '"' && p[strlen(p) - 1] == '"') { p[strlen(p) - 1] = '\0'; (void) fmd_stresc2chr(++p); } if (fmd_conf_setprop(fmd.d_conf, optarg, p) != 0) { (void) fprintf(stderr, "%s: failed to set option -o %s: %s\n", fmd.d_pname, optarg, fmd_strerror(errno)); return (FMD_EXIT_USAGE); } } } if (fmd.d_fmd_debug & FMD_DBG_HELP) { fmd_help(&fmd); fmd_destroy(&fmd); return (FMD_EXIT_SUCCESS); } /* * Update the value of fmd.d_fg based on "fg" in case it changed. We * use this property to decide whether to daemonize below. */ (void) fmd_conf_getprop(fmd.d_conf, "fg", &fmd.d_fg); /* * Once we're done setting our global state up, set up signal handlers * for ensuring orderly termination on SIGTERM. If we are starting in * the foreground, we also use the same handler for SIGINT and SIGHUP. */ (void) sigfillset(&set); (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */ (void) sigfillset(&act.sa_mask); act.sa_handler = handler; act.sa_flags = 0; (void) sigaction(SIGTERM, &act, NULL); (void) sigdelset(&set, SIGTERM); if (fmd.d_fg) { (void) sigaction(SIGHUP, &act, NULL); (void) sigdelset(&set, SIGHUP); (void) sigaction(SIGINT, &act, NULL); (void) sigdelset(&set, SIGINT); (void) sigdelset(&set, SIGTSTP); (void) sigdelset(&set, SIGTTIN); (void) sigdelset(&set, SIGTTOU); (void) printf("%s: [ loading modules ... ", fmd.d_pname); (void) fflush(stdout); } else pfd = daemonize_init(); /* * Prior to this point, we are single-threaded. Once fmd_run() is * called, we will be multi-threaded from this point on. The daemon's * main thread will wait at the end of this function for signals. */ fmd_run(&fmd, pfd); if (fmd.d_fg) { (void) printf("done ]\n"); (void) printf("%s: [ awaiting events ]\n", fmd.d_pname); } else daemonize_fini(pfd); while (!fmd.d_signal) (void) sigsuspend(&set); fmd_destroy(&fmd); return (FMD_EXIT_SUCCESS); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct trwalk_state { struct trwalk_state *trw_next; fmd_tracebuf_t trw_data; pthread_t trw_tid; uintptr_t trw_base; const fmd_tracerec_t *trw_stop; fmd_tracerec_t *trw_xrec; } trwalk_state_t; typedef struct hashwalk_data { uintptr_t *hw_hash; uint_t hw_hashlen; uint_t hw_hashidx; const char *hw_name; void *hw_data; size_t hw_size; size_t hw_next; } hashwalk_data_t; static int fmd_stat(uintptr_t, uint_t, int, const mdb_arg_t *); static int fmd_ustat(uintptr_t, uint_t, int, const mdb_arg_t *); static int trwalk_init(mdb_walk_state_t *wsp) { uintptr_t addr; fmd_thread_t thr; fmd_t F; if (wsp->walk_addr != 0) { mdb_warn("fmd_trace only supports global walks\n"); return (WALK_ERR); } if (mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } for (addr = (uintptr_t)F.d_thr_list.l_next; addr != 0; addr = (uintptr_t)thr.thr_list.l_next) { size_t len, ptr_off, end_off; fmd_tracerec_t *buf; trwalk_state_t *t; if (mdb_vread(&thr, sizeof (thr), addr) != sizeof (thr)) { mdb_warn("failed to read thread at %p " "(some trace data will be unavailable)", addr); break; } t = mdb_zalloc(sizeof (trwalk_state_t), UM_SLEEP); t->trw_next = wsp->walk_data; wsp->walk_data = t; (void) mdb_vread(&t->trw_data, sizeof (t->trw_data), (uintptr_t)thr.thr_trdata); if (t->trw_data.tb_recs == 0) continue; /* no trace buffer allocated for thread */ len = t->trw_data.tb_recs * t->trw_data.tb_size; buf = mdb_alloc(len, UM_SLEEP); t->trw_tid = thr.thr_tid; t->trw_base = (uintptr_t)t->trw_data.tb_buf; if (mdb_vread(buf, len, t->trw_base) == -1) { mdb_warn("failed to read buffer for t%u", t->trw_tid); bzero(buf, len); } end_off = (uintptr_t)t->trw_data.tb_end - t->trw_base; ptr_off = (uintptr_t)t->trw_data.tb_ptr - t->trw_base; t->trw_data.tb_buf = buf; t->trw_data.tb_end = (void *)((uintptr_t)buf + end_off); t->trw_data.tb_ptr = (void *)((uintptr_t)buf + ptr_off); if (t->trw_data.tb_ptr < t->trw_data.tb_buf || t->trw_data.tb_ptr > t->trw_data.tb_end) { mdb_warn("trace record ptr for t%u is corrupt " "(some data may be unavailable)\n", t->trw_tid); t->trw_data.tb_ptr = t->trw_data.tb_buf; } t->trw_stop = t->trw_data.tb_ptr; t->trw_xrec = mdb_alloc( t->trw_data.tb_size + sizeof (uintptr_t), UM_SLEEP); } return (WALK_NEXT); } static fmd_tracerec_t * trwalk_nextrec(trwalk_state_t *t) { if (t->trw_stop == NULL) return (t->trw_data.tb_ptr); if (t->trw_data.tb_ptr == t->trw_data.tb_buf) t->trw_data.tb_ptr = t->trw_data.tb_end; else t->trw_data.tb_ptr = (fmd_tracerec_t *) ((uintptr_t)t->trw_data.tb_ptr - t->trw_data.tb_size); if (t->trw_data.tb_ptr == t->trw_stop) t->trw_stop = NULL; /* mark buffer as empty */ return (t->trw_data.tb_ptr); } static int trwalk_step(mdb_walk_state_t *wsp) { trwalk_state_t *t, *oldest_t; hrtime_t oldest_time = 0; fmd_tracerec_t *trp; int status; for (t = wsp->walk_data; t != NULL; t = t->trw_next) { for (trp = t->trw_data.tb_ptr; t->trw_stop != NULL && trp->tr_time == 0; trp = trwalk_nextrec(t)) continue; if (t->trw_stop == NULL) continue; /* buffer has been emptied */ if (trp->tr_time > oldest_time) { oldest_time = trp->tr_time; oldest_t = t; } } if (oldest_time == 0) return (WALK_DONE); t = oldest_t; trp = t->trw_data.tb_ptr; bcopy(trp, t->trw_xrec, t->trw_data.tb_size); t->trw_xrec->tr_depth = MIN(trp->tr_depth, t->trw_data.tb_frames); t->trw_xrec->tr_stack[t->trw_xrec->tr_depth] = t->trw_tid; status = wsp->walk_callback((uintptr_t)trp - (uintptr_t) t->trw_data.tb_buf + t->trw_base, t->trw_xrec, wsp->walk_cbdata); (void) trwalk_nextrec(t); return (status); } static void trwalk_fini(mdb_walk_state_t *wsp) { trwalk_state_t *t, *u; for (t = wsp->walk_data; t != NULL; t = u) { u = t->trw_next; mdb_free(t->trw_data.tb_buf, t->trw_data.tb_recs * t->trw_data.tb_size); mdb_free(t->trw_xrec, t->trw_data.tb_size + sizeof (uintptr_t)); mdb_free(t, sizeof (trwalk_state_t)); } } /*ARGSUSED*/ static int trprint_msg(uintptr_t addr, const void *arg, void *arg1) { const fmd_tracerec_t *trp = arg; uintptr_t tid = (uintptr_t)arg1; if (tid == 0) mdb_printf("%3lu ", trp->tr_stack[trp->tr_depth]); else if (trp->tr_stack[trp->tr_depth] != tid) return (WALK_NEXT); mdb_printf("%016llx %04x %-5u %s\n", trp->tr_time, 1 << trp->tr_tag, trp->tr_errno, trp->tr_msg); return (WALK_NEXT); } /*ARGSUSED*/ static int trprint_cpp(uintptr_t addr, const void *arg, void *arg1) { const fmd_tracerec_t *trp = arg; uintptr_t tid = (uintptr_t)arg1; char file[64]; if (tid == 0) mdb_printf("%3lu ", trp->tr_stack[trp->tr_depth]); else if (trp->tr_stack[trp->tr_depth] != tid) return (WALK_NEXT); if (mdb_readstr(file, sizeof (file), (uintptr_t)trp->tr_file) <= 0) (void) strcpy(file, "???"); mdb_printf("%016llx %04x %s: %u\n", trp->tr_time, 1 << trp->tr_tag, file, trp->tr_line); return (WALK_NEXT); } static void trprint_stack(const fmd_tracerec_t *trp) { uint8_t i; for (i = 0; i < trp->tr_depth; i++) mdb_printf("\t%a\n", trp->tr_stack[i]); if (trp->tr_depth != 0) mdb_printf("\n"); } static int trprint_msg_stack(uintptr_t addr, const void *arg, void *arg1) { const fmd_tracerec_t *trp = arg; int status = trprint_msg(addr, trp, arg1); trprint_stack(trp); return (status); } static int trprint_cpp_stack(uintptr_t addr, const void *arg, void *arg1) { const fmd_tracerec_t *trp = arg; int status = trprint_cpp(addr, trp, arg1); trprint_stack(trp); return (status); } static int fmd_trace(uintptr_t tid, uint_t flags, int argc, const mdb_arg_t *argv) { int (*func)(uintptr_t, const void *, void *); uint_t opt_c = FALSE, opt_s = FALSE; if (mdb_getopts(argc, argv, 'c', MDB_OPT_SETBITS, TRUE, &opt_c, 's', MDB_OPT_SETBITS, TRUE, &opt_s, NULL) != argc) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) { mdb_printf("TID "); tid = 0; } if (opt_c) { mdb_printf("%-16s %-4s FILE:LINE\n", "TIME", "TAG"); func = opt_s ? trprint_cpp_stack : trprint_cpp; } else { mdb_printf("%-16s %-4s %-5s MSG\n", "TIME", "TAG", "ERRNO"); func = opt_s ? trprint_msg_stack : trprint_msg; } if (mdb_walk("fmd_trace", func, (void *)tid) == -1) { mdb_warn("failed to walk fmd_trace"); return (DCMD_ERR); } return (DCMD_OK); } static int hash_walk_init(mdb_walk_state_t *wsp, uintptr_t addr, uint_t hashlen, const char *name, size_t size, size_t next) { hashwalk_data_t *hwp; size_t len = sizeof (uintptr_t) * hashlen; if (len == 0) { mdb_warn("failed to walk hash: invalid hash length\n"); return (WALK_ERR); } hwp = mdb_alloc(sizeof (hashwalk_data_t), UM_SLEEP); hwp->hw_hash = mdb_zalloc(len, UM_SLEEP); (void) mdb_vread(hwp->hw_hash, len, addr); hwp->hw_hashlen = hashlen; hwp->hw_hashidx = 0; hwp->hw_name = name; hwp->hw_data = mdb_zalloc(size, UM_SLEEP); hwp->hw_size = size; hwp->hw_next = next; wsp->walk_addr = hwp->hw_hash[0]; wsp->walk_data = hwp; return (WALK_NEXT); } static int hash_walk_step(mdb_walk_state_t *wsp) { hashwalk_data_t *hwp = wsp->walk_data; int rv; while (wsp->walk_addr == 0) { if (++hwp->hw_hashidx < hwp->hw_hashlen) wsp->walk_addr = hwp->hw_hash[hwp->hw_hashidx]; else return (WALK_DONE); } if (mdb_vread(hwp->hw_data, hwp->hw_size, wsp->walk_addr) == -1) { mdb_warn("failed to read %s at %p", hwp->hw_name, wsp->walk_addr); return (WALK_ERR); } rv = wsp->walk_callback(wsp->walk_addr, hwp->hw_data, wsp->walk_cbdata); wsp->walk_addr = *(uintptr_t *)((uintptr_t)hwp->hw_data + hwp->hw_next); return (rv); } static void hash_walk_fini(mdb_walk_state_t *wsp) { hashwalk_data_t *hwp = wsp->walk_data; mdb_free(hwp->hw_hash, sizeof (uintptr_t) * hwp->hw_hashlen); mdb_free(hwp->hw_data, hwp->hw_size); mdb_free(hwp, sizeof (hashwalk_data_t)); } static int ustat_walk_init(mdb_walk_state_t *wsp) { fmd_ustat_t us; if (mdb_vread(&us, sizeof (us), wsp->walk_addr) != sizeof (us)) { mdb_warn("failed to read fmd_ustat_t at %p", wsp->walk_addr); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)us.us_hash, us.us_hashlen, NULL, 0, 0)); } static int ustat_walk_step(mdb_walk_state_t *wsp) { hashwalk_data_t *hwp = wsp->walk_data; fmd_ustat_elem_t ue; fmd_stat_t s; while (wsp->walk_addr == 0) { if (++hwp->hw_hashidx < hwp->hw_hashlen) wsp->walk_addr = hwp->hw_hash[hwp->hw_hashidx]; else return (WALK_DONE); } if (mdb_vread(&ue, sizeof (ue), wsp->walk_addr) != sizeof (ue) || mdb_vread(&s, sizeof (s), (uintptr_t)ue.use_stat) != sizeof (s)) { mdb_warn("failed to read stat element at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)ue.use_next; return (wsp->walk_callback( (uintptr_t)ue.use_stat, &s, wsp->walk_cbdata)); } struct fmd_cmd_data { int argc; const mdb_arg_t *argv; }; /* ARGSUSED */ static int module_ustat(uintptr_t addr, const void *data, void *wsp) { fmd_module_t *modp = (fmd_module_t *)data; char name[PATH_MAX]; const struct fmd_cmd_data *udp = wsp; if (mdb_readstr(name, sizeof (name), (uintptr_t)modp->mod_name) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", modp->mod_name); mdb_printf("%s\n", name); (void) fmd_ustat((uintptr_t)modp->mod_ustat, DCMD_ADDRSPEC | DCMD_LOOPFIRST, udp->argc, udp->argv); return (WALK_NEXT); } static int fmd_ustat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { if (!(flags & DCMD_ADDRSPEC)) { struct fmd_cmd_data ud; ud.argc = argc; ud.argv = argv; if (mdb_walk("fmd_module", module_ustat, &ud) == -1) { mdb_warn("failed to walk 'fmd_module'"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_pwalk_dcmd("fmd_ustat", "fmd_stat", argc, argv, addr) != 0) { mdb_warn("failed to walk fmd_ustat at %p", addr); return (DCMD_ERR); } return (DCMD_OK); } /* ARGSUSED */ static int module_stat(uintptr_t addr, const void *data, void *wsp) { fmd_module_t *modp = (fmd_module_t *)data; char name[PATH_MAX]; const struct fmd_cmd_data *udp = wsp; fmd_modstat_t *mod_stats; if (mdb_readstr(name, sizeof (name), (uintptr_t)modp->mod_name) <= 0) { (void) mdb_snprintf(name, sizeof (name), "<%p>", modp->mod_name); } mdb_printf("%s\n", name); mod_stats = modp->mod_stats; (void) fmd_stat((uintptr_t)&mod_stats->ms_loadtime, DCMD_ADDRSPEC | DCMD_LOOPFIRST, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_snaptime, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_accepted, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_debugdrop, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_memtotal, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_memlimit, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_buftotal, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_buflimit, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_thrtotal, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_thrlimit, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_doorthrtotal, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_doorthrlimit, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_caseopen, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_casesolved, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_caseclosed, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_ckpt_save, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_ckpt_restore, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_ckpt_zeroed, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_ckpt_cnt, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_ckpt_time, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_xprtopen, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_xprtlimit, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); (void) fmd_stat((uintptr_t)&mod_stats->ms_xprtqlimit, DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv); return (WALK_NEXT); } /*ARGSUSED*/ static int fmd_stat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char buf[512]; fmd_stat_t s; if (argc != 0) return (DCMD_USAGE); if (DCMD_HDRSPEC(flags)) mdb_printf("%%-11s %-4s %-32s %s%\n", "ADDR", "TYPE", "NAME", "VALUE"); if (!(flags & DCMD_ADDRSPEC)) { struct fmd_cmd_data ud; ud.argc = argc; ud.argv = argv; if (mdb_walk("fmd_module", module_stat, &ud) == -1) { mdb_warn("failed to walk 'fmd_module'"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&s, sizeof (s), addr) != sizeof (s)) { mdb_warn("failed to read statistic at %p", addr); return (DCMD_ERR); } switch (s.fmds_type) { case FMD_TYPE_BOOL: mdb_printf("%-11p %-4s %-32s %s\n", addr, "bool", s.fmds_name, s.fmds_value.bool ? "true" : "false"); break; case FMD_TYPE_INT32: mdb_printf("%-11p %-4s %-32s %d\n", addr, "i32", s.fmds_name, s.fmds_value.i32); break; case FMD_TYPE_UINT32: mdb_printf("%-11p %-4s %-32s %u\n", addr, "ui32", s.fmds_name, s.fmds_value.i32); break; case FMD_TYPE_INT64: mdb_printf("%-11p %-4s %-32s %lld\n", addr, "i64", s.fmds_name, s.fmds_value.i64); break; case FMD_TYPE_UINT64: mdb_printf("%-11p %-4s %-32s %llu\n", addr, "ui64", s.fmds_name, s.fmds_value.ui64); break; case FMD_TYPE_STRING: if (mdb_readstr(buf, sizeof (buf), (uintptr_t)s.fmds_value.str) < 0) { (void) mdb_snprintf(buf, sizeof (buf), "<%p>", s.fmds_value.str); } mdb_printf("%-11p %-4s %-32s %s\n", addr, "str", s.fmds_name, buf); break; case FMD_TYPE_TIME: mdb_printf("%-11p %-4s %-32s %llu\n", addr, "time", s.fmds_name, s.fmds_value.ui64); break; case FMD_TYPE_SIZE: mdb_printf("%-11p %-4s %-32s %llu\n", addr, "size", s.fmds_name, s.fmds_value.ui64); break; default: mdb_printf("%-11p %-4u %-32s ???\n", addr, s.fmds_type, s.fmds_name); break; } return (DCMD_OK); } /*ARGSUSED*/ static int fmd_event(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char type[16], name[16]; fmd_event_impl_t ev; if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&ev, sizeof (ev), addr) != sizeof (ev)) { mdb_warn("failed to read fmd_event at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-11s %-4s %-5s %-3s %-?s%\n", "ADDR", "TYPE", "STATE", "REF", "NVPAIR"); } switch (ev.ev_type) { case FMD_EVT_PROTOCOL: (void) strcpy(type, "PROT"); break; case FMD_EVT_GC: (void) strcpy(type, "GC"); break; case FMD_EVT_CLOSE: (void) strcpy(type, "CLSE"); break; case FMD_EVT_TIMEOUT: (void) strcpy(type, "TIME"); break; case FMD_EVT_STATS: (void) strcpy(type, "STAT"); break; case FMD_EVT_PUBLISH: (void) strcpy(type, "PUBL"); break; case FMD_EVT_TOPO: (void) strcpy(type, "TOPO"); break; default: (void) mdb_snprintf(type, sizeof (type), "%u", ev.ev_type); } switch (ev.ev_state) { case FMD_EVS_RECEIVED: (void) strcpy(name, "RECVD"); break; case FMD_EVS_ACCEPTED: (void) strcpy(name, "ACCPT"); break; case FMD_EVS_DISCARDED: (void) strcpy(name, "DSCRD"); break; case FMD_EVS_DIAGNOSED: (void) strcpy(name, "DIAGN"); break; default: (void) mdb_snprintf(name, sizeof (name), "%u", ev.ev_state); } mdb_printf("%-11p %-4s %-5s %-3u %p\n", addr, type, name, ev.ev_refs, ev.ev_nvl); return (DCMD_OK); } static int thread_walk_init(mdb_walk_state_t *wsp) { fmd_t F; if (mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)F.d_thr_list.l_next; return (WALK_NEXT); } static int thread_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_thread_t t; if (addr == 0) return (WALK_DONE); if (mdb_vread(&t, sizeof (t), addr) != sizeof (t)) { mdb_warn("failed to read fmd_thread at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)t.thr_list.l_next; return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); } static int fmd_thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { fmd_thread_t thr; if (!(flags & DCMD_ADDRSPEC)) return (mdb_walk_dcmd("fmd_thread", "fmd_thread", argc, argv)); if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&thr, sizeof (thr), addr) != sizeof (thr)) { mdb_warn("failed to read fmd_thread at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-11s %-11s %-8s %-16s%\n", "ADDR", "MOD", "TID", "FUNC"); } mdb_printf("%-11p %-11p %-8u %a\n", addr, thr.thr_mod, thr.thr_tid, thr.thr_func); return (DCMD_OK); } static int mod_walk_init(mdb_walk_state_t *wsp) { fmd_t F; if (mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)F.d_mod_list.l_next; return (WALK_NEXT); } static int mod_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_module_t m; if (addr == 0) return (WALK_DONE); if (mdb_vread(&m, sizeof (m), addr) != sizeof (m)) { mdb_warn("failed to read fmd_module at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)m.mod_list.l_next; return (wsp->walk_callback(addr, &m, wsp->walk_cbdata)); } static int fmd_module(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { fmd_module_t mod; char name[PATH_MAX]; if (!(flags & DCMD_ADDRSPEC)) return (mdb_walk_dcmd("fmd_module", "fmd_module", argc, argv)); if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&mod, sizeof (mod), addr) != sizeof (mod)) { mdb_warn("failed to read fmd_module at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-11s %-16s %-11s %-4s %-?s %-16s%\n", "ADDR", "OPS", "DATA", "FLAG", "USTAT", "NAME"); } if (mdb_readstr(name, sizeof (name), (uintptr_t)mod.mod_name) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", mod.mod_name); mdb_printf("%-11p %-16a %-11p 0x%02x %-?p %s\n", addr, mod.mod_ops, mod.mod_data, mod.mod_flags, mod.mod_ustat, name); return (DCMD_OK); } static int case_walk_init(mdb_walk_state_t *wsp) { fmd_module_t mod; fmd_case_hash_t ch; fmd_t F; if (wsp->walk_addr != 0) { if (mdb_vread(&mod, sizeof (mod), wsp->walk_addr) == -1) { mdb_warn("failed to read module at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)mod.mod_cases.l_next; return (WALK_NEXT); } if (mdb_readvar(&F, "fmd") != sizeof (F) || mdb_vread(&ch, sizeof (ch), (uintptr_t)F.d_cases) != sizeof (ch)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)ch.ch_hash, ch.ch_hashlen, "fmd_case", sizeof (fmd_case_impl_t), OFFSETOF(fmd_case_impl_t, ci_next))); } static int case_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_case_impl_t ci; if (wsp->walk_data != NULL) return (hash_walk_step(wsp)); if (addr == 0) return (WALK_DONE); if (mdb_vread(&ci, sizeof (ci), addr) != sizeof (ci)) { mdb_warn("failed to read fmd_case at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)ci.ci_list.l_next; return (wsp->walk_callback(addr, &ci, wsp->walk_cbdata)); } static void case_walk_fini(mdb_walk_state_t *wsp) { if (wsp->walk_data != NULL) hash_walk_fini(wsp); } static int fmd_case(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char uuid[48], name[16]; fmd_case_impl_t ci; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("fmd_case", "fmd_case", argc, argv) != 0) { mdb_warn("failed to walk fmd_case hash"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&ci, sizeof (ci), addr) != sizeof (ci)) { mdb_warn("failed to read fmd_case at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-11s %-5s %-3s %-?s %-36s%\n", "ADDR", "STATE", "REF", "DATA", "UUID"); } if (mdb_readstr(uuid, sizeof (uuid), (uintptr_t)ci.ci_uuid) <= 0) (void) mdb_snprintf(uuid, sizeof (uuid), "<%p>", ci.ci_uuid); switch (ci.ci_state) { case FMD_CASE_UNSOLVED: (void) strcpy(name, "UNSLV"); break; case FMD_CASE_SOLVED: (void) strcpy(name, "SOLVE"); break; case FMD_CASE_CLOSE_WAIT: (void) strcpy(name, "CWAIT"); break; case FMD_CASE_CLOSED: (void) strcpy(name, "CLOSE"); break; case FMD_CASE_REPAIRED: (void) strcpy(name, "RPAIR"); break; case FMD_CASE_RESOLVED: (void) strcpy(name, "RSLVD"); break; default: (void) mdb_snprintf(name, sizeof (name), "%u", ci.ci_state); } mdb_printf("%-11p %-5s %-3u %-?p %s\n", addr, name, ci.ci_refs, ci.ci_data, uuid); return (DCMD_OK); } static int buf_walk_init(mdb_walk_state_t *wsp) { fmd_buf_hash_t bh; if (mdb_vread(&bh, sizeof (bh), wsp->walk_addr) != sizeof (bh)) { mdb_warn("failed to read fmd_buf_hash_t at %p", wsp->walk_addr); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)bh.bh_hash, bh.bh_hashlen, "fmd_buf", sizeof (fmd_buf_t), OFFSETOF(fmd_buf_t, buf_next))); } /*ARGSUSED*/ static int fmd_buf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char name[PATH_MAX]; fmd_buf_t b; if (argc != 0 || !(flags & DCMD_ADDRSPEC)) return (DCMD_USAGE); if (mdb_vread(&b, sizeof (b), addr) != sizeof (b)) { mdb_warn("failed to read fmd_buf at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-11s %-32s %-5s %-?s %s%\n", "ADDR", "NAME", "FLAGS", "DATA", "SIZE"); } if (mdb_readstr(name, sizeof (name), (uintptr_t)b.buf_name) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", b.buf_name); mdb_printf("%-11p %-32s %-#5x %-?p %lu\n", addr, name, b.buf_flags, b.buf_data, b.buf_size); return (DCMD_OK); } static int serd_walk_init(mdb_walk_state_t *wsp) { fmd_serd_hash_t sh; if (mdb_vread(&sh, sizeof (sh), wsp->walk_addr) != sizeof (sh)) { mdb_warn("failed to read fmd_serd_hash at %p", wsp->walk_addr); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)sh.sh_hash, sh.sh_hashlen, "fmd_serd_eng", sizeof (fmd_serd_eng_t), OFFSETOF(fmd_serd_eng_t, sg_next))); } /* ARGSUSED */ static int module_serd(uintptr_t addr, const void *data, void *wsp) { fmd_module_t *modp = (fmd_module_t *)data; if (modp->mod_serds.sh_count != 0) { modp = (fmd_module_t *)addr; (void) mdb_pwalk_dcmd("fmd_serd", "fmd_serd", 0, 0, (uintptr_t)&modp->mod_serds); } return (WALK_NEXT); } /*ARGSUSED*/ static int fmd_serd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char name[PATH_MAX]; fmd_serd_eng_t sg; if (argc != 0) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk("fmd_module", module_serd, 0) == -1) { mdb_warn("failed to walk 'fmd_module'"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&sg, sizeof (sg), addr) != sizeof (sg)) { mdb_warn("failed to read fmd_serd_eng at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-11s %-32s %-3s F >%-2s %-16s%\n", "ADDR", "NAME", "CNT", "N", "T"); } if (mdb_readstr(name, sizeof (name), (uintptr_t)sg.sg_name) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", sg.sg_name); mdb_printf("%-11p %-32s %-3u %c >%-2u %lluns\n", addr, name, sg.sg_count, (sg.sg_flags & FMD_SERD_FIRED) ? 'F' : ' ', sg.sg_n, (u_longlong_t)sg.sg_t); return (DCMD_OK); } static int asru_walk_init(mdb_walk_state_t *wsp) { fmd_asru_hash_t ah; fmd_t F; if (wsp->walk_addr == 0 && mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } if (wsp->walk_addr == 0) wsp->walk_addr = (uintptr_t)F.d_asrus; if (mdb_vread(&ah, sizeof (ah), wsp->walk_addr) != sizeof (ah)) { mdb_warn("failed to read asru_hash at %p", wsp->walk_addr); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)ah.ah_hash, ah.ah_hashlen, "fmd_asru", sizeof (fmd_asru_t), OFFSETOF(fmd_asru_t, asru_next))); } static int fmd_asru(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char uuid[48], name[PATH_MAX]; fmd_asru_t a; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("fmd_asru", "fmd_asru", argc, argv) != 0) { mdb_warn("failed to walk fmd_asru hash"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&a, sizeof (a), addr) != sizeof (a)) { mdb_warn("failed to read fmd_asru at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) mdb_printf("%%-8s %-36s %s%\n", "ADDR", "UUID", "NAME"); if (mdb_readstr(uuid, sizeof (uuid), (uintptr_t)a.asru_uuid) <= 0) (void) mdb_snprintf(uuid, sizeof (uuid), "<%p>", a.asru_uuid); if (mdb_readstr(name, sizeof (name), (uintptr_t)a.asru_name) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", a.asru_name); mdb_printf("%-8p %-36s %s\n", addr, uuid, name); return (DCMD_OK); } static int al_walk_init(mdb_walk_state_t *wsp) { fmd_asru_hash_t ah; fmd_t F; if (wsp->walk_addr == 0 && mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } if (wsp->walk_addr == 0) wsp->walk_addr = (uintptr_t)F.d_asrus; if (mdb_vread(&ah, sizeof (ah), wsp->walk_addr) != sizeof (ah)) { mdb_warn("failed to read asru_hash at %p", wsp->walk_addr); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)ah.ah_rsrc_hash, ah.ah_hashlen, "fmd_asru_link", sizeof (fmd_asru_link_t), OFFSETOF(fmd_asru_link_t, al_rsrc_next))); } static int fmd_asru_link(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char uuid[48], name[PATH_MAX]; fmd_asru_link_t a; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("fmd_asru_link", "fmd_asru_link", argc, argv) != 0) { mdb_warn("failed to walk fmd_asru_link hash"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&a, sizeof (a), addr) != sizeof (a)) { mdb_warn("failed to read fmd_asru_link at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) mdb_printf("%%-8s %-36s %s%\n", "ADDR", "UUID", "NAME"); if (mdb_readstr(uuid, sizeof (uuid), (uintptr_t)a.al_uuid) <= 0) (void) mdb_snprintf(uuid, sizeof (uuid), "<%p>", a.al_uuid); if (mdb_readstr(name, sizeof (name), (uintptr_t)a.al_rsrc_name) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", a.al_rsrc_name); mdb_printf("%-8p %-36s %s\n", addr, uuid, name); return (DCMD_OK); } /*ARGSUSED*/ static int fcf_hdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { fcf_hdr_t h; if (argc != 0) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) addr = 0; /* assume base of file in file target */ if (mdb_vread(&h, sizeof (h), addr) != sizeof (h)) { mdb_warn("failed to read header at %p", addr); return (DCMD_ERR); } mdb_printf("fcfh_ident.id_magic = 0x%x, %c, %c, %c\n", h.fcfh_ident[FCF_ID_MAG0], h.fcfh_ident[FCF_ID_MAG1], h.fcfh_ident[FCF_ID_MAG2], h.fcfh_ident[FCF_ID_MAG3]); switch (h.fcfh_ident[FCF_ID_MODEL]) { case FCF_MODEL_ILP32: mdb_printf("fcfh_ident.id_model = ILP32\n"); break; case FCF_MODEL_LP64: mdb_printf("fcfh_ident.id_model = LP64\n"); break; default: mdb_printf("fcfh_ident.id_model = 0x%x\n", h.fcfh_ident[FCF_ID_MODEL]); } switch (h.fcfh_ident[FCF_ID_ENCODING]) { case FCF_ENCODE_LSB: mdb_printf("fcfh_ident.id_encoding = LSB\n"); break; case FCF_ENCODE_MSB: mdb_printf("fcfh_ident.id_encoding = MSB\n"); break; default: mdb_printf("fcfh_ident.id_encoding = 0x%x\n", h.fcfh_ident[FCF_ID_ENCODING]); } mdb_printf("fcfh_ident.id_version = %u\n", h.fcfh_ident[FCF_ID_VERSION]); mdb_printf("fcfh_flags = 0x%x\n", h.fcfh_flags); mdb_printf("fcfh_hdrsize = %u\n", h.fcfh_hdrsize); mdb_printf("fcfh_secsize = %u\n", h.fcfh_secsize); mdb_printf("fcfh_secnum = %u\n", h.fcfh_secnum); mdb_printf("fcfh_secoff = %llu\n", h.fcfh_secoff); mdb_printf("fcfh_filesz = %llu\n", h.fcfh_filesz); mdb_printf("fcfh_cgen = %llu\n", h.fcfh_cgen); return (DCMD_OK); } static int fcf_sec(uintptr_t, uint_t, int, const mdb_arg_t *); /*ARGSUSED*/ static int fcf_sec_one(uintptr_t addr, void *ignored, uint_t *secp) { mdb_printf("%3d ", (*secp)++); (void) fcf_sec(addr, DCMD_ADDRSPEC | DCMD_LOOP, 0, NULL); return (WALK_NEXT); } /*ARGSUSED*/ static int fcf_sec(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { static const char *const types[] = { "none", /* FCF_SECT_NONE */ "strtab", /* FCF_SECT_STRTAB */ "module", /* FCF_SECT_MODULE */ "case", /* FCF_SECT_CASE */ "bufs", /* FCF_SECT_BUFS */ "buffer", /* FCF_SECT_BUFFER */ "serd", /* FCF_SECT_SERD */ "events", /* FCF_SECT_EVENTS */ "nvlists", /* FCF_SECT_NVLISTS */ }; uint_t sec = 0; fcf_sec_t s; if (!(flags & DCMD_ADDRSPEC)) mdb_printf("%%-3s ", "NDX"); if (!(flags & DCMD_ADDRSPEC) || DCMD_HDRSPEC(flags)) { mdb_printf("%%?s %-10s %-5s %-5s %-5s %-6s %-5s%\n", "ADDR", "TYPE", "ALIGN", "FLAGS", "ENTSZ", "OFF", "SIZE"); } if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk("fcf_sec", (mdb_walk_cb_t)fcf_sec_one, &sec) < 0) { mdb_warn("failed to walk fcf_sec"); return (DCMD_ERR); } return (DCMD_OK); } if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&s, sizeof (s), addr) != sizeof (s)) { mdb_warn("failed to read section header at %p", addr); return (DCMD_ERR); } mdb_printf("%?p ", addr); if (s.fcfs_type < sizeof (types) / sizeof (types[0])) mdb_printf("%-10s ", types[s.fcfs_type]); else mdb_printf("%-10u ", s.fcfs_type); mdb_printf("%-5u %-#5x %-#5x %-6llx %-#5llx\n", s.fcfs_align, s.fcfs_flags, s.fcfs_entsize, s.fcfs_offset, s.fcfs_size); return (DCMD_OK); } static int fcf_sec_walk_init(mdb_walk_state_t *wsp) { fcf_hdr_t h, *hp; size_t size; if (mdb_vread(&h, sizeof (h), wsp->walk_addr) != sizeof (h)) { mdb_warn("failed to read FCF header at %p", wsp->walk_addr); return (WALK_ERR); } size = sizeof (fcf_hdr_t) + sizeof (fcf_sec_t) * h.fcfh_secnum; hp = mdb_alloc(size, UM_SLEEP); if (mdb_vread(hp, size, wsp->walk_addr) != size) { mdb_warn("failed to read FCF sections at %p", wsp->walk_addr); mdb_free(hp, size); return (WALK_ERR); } wsp->walk_data = hp; wsp->walk_arg = 0; return (WALK_NEXT); } static int fcf_sec_walk_step(mdb_walk_state_t *wsp) { uint_t i = (uint_t)wsp->walk_arg; size_t off = sizeof (fcf_hdr_t) + sizeof (fcf_sec_t) * i; fcf_hdr_t *hp = wsp->walk_data; fcf_sec_t *sp = (fcf_sec_t *)((uintptr_t)hp + off); if (i >= hp->fcfh_secnum) return (WALK_DONE); wsp->walk_arg = (void *)(i + 1); return (wsp->walk_callback(wsp->walk_addr + off, sp, wsp->walk_cbdata)); } static void fcf_sec_walk_fini(mdb_walk_state_t *wsp) { fcf_hdr_t *hp = wsp->walk_data; mdb_free(hp, sizeof (fcf_hdr_t) + sizeof (fcf_sec_t) * hp->fcfh_secnum); } /*ARGSUSED*/ static int fcf_case(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { fcf_case_t fcfc; if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&fcfc, sizeof (fcfc), addr) != sizeof (fcfc)) { mdb_warn("failed to read case at %p", addr); return (DCMD_ERR); } mdb_printf("fcfc_uuid = 0x%x\n", fcfc.fcfc_uuid); mdb_printf("fcfc_state = %u\n", fcfc.fcfc_state); mdb_printf("fcfc_bufs = %u\n", fcfc.fcfc_bufs); mdb_printf("fcfc_events = %u\n", fcfc.fcfc_events); mdb_printf("fcfc_suspects = %u\n", fcfc.fcfc_suspects); return (DCMD_OK); } /*ARGSUSED*/ static int fcf_event(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { fcf_event_t fcfe; if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&fcfe, sizeof (fcfe), addr) != sizeof (fcfe)) { mdb_warn("failed to read event at %p", addr); return (DCMD_ERR); } mdb_printf("fcfe_todsec = %llu (%Y)\n", fcfe.fcfe_todsec, (time_t)fcfe.fcfe_todsec); mdb_printf("fcfe_todnsec = %llu\n", fcfe.fcfe_todnsec); mdb_printf("fcfe_major = %u\n", fcfe.fcfe_major); mdb_printf("fcfe_minor = %u\n", fcfe.fcfe_minor); mdb_printf("fcfe_inode = %llu\n", fcfe.fcfe_inode); mdb_printf("fcfe_offset = %llu\n", fcfe.fcfe_offset); return (DCMD_OK); } /*ARGSUSED*/ static int fcf_serd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { fcf_serd_t fcfd; if (argc != 0) return (DCMD_USAGE); if (mdb_vread(&fcfd, sizeof (fcfd), addr) != sizeof (fcfd)) { mdb_warn("failed to read serd at %p", addr); return (DCMD_ERR); } mdb_printf("fcfd_name = 0x%x\n", fcfd.fcfd_name); mdb_printf("fcfd_events = %u\n", fcfd.fcfd_events); mdb_printf("fcfd_n = >%u\n", fcfd.fcfd_n); mdb_printf("fcfd_t = %lluns\n", fcfd.fcfd_t); return (DCMD_OK); } static int tmq_walk_init(mdb_walk_state_t *wsp) { fmd_timerq_t tmq; fmd_t F; if (wsp->walk_addr == 0 && mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } if (wsp->walk_addr == 0) wsp->walk_addr = (uintptr_t)F.d_timers; if (mdb_vread(&tmq, sizeof (tmq), wsp->walk_addr) != sizeof (tmq)) { mdb_warn("failed to read timerq at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)tmq.tmq_list.l_next; return (WALK_NEXT); } static int tmq_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_timer_t tmr; if (addr == 0) return (WALK_DONE); if (mdb_vread(&tmr, sizeof (tmr), addr) != sizeof (tmr)) { mdb_warn("failed to read fmd_timer at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)tmr.tmr_list.l_next; return (wsp->walk_callback(addr, &tmr, wsp->walk_cbdata)); } static int fmd_timer(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { char name[32], func[MDB_SYM_NAMLEN]; fmd_timer_t t; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("fmd_timerq", "fmd_timer", argc, argv) != 0) { mdb_warn("failed to walk fmd_timerq"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&t, sizeof (t), addr) != sizeof (t)) { mdb_warn("failed to read fmd_timer at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-8s %-20s %-4s %-18s %-8s %s%\n", "ADDR", "MODULE", "ID", "HRTIME", "ARG", "FUNC"); } if (mdb_readstr(name, sizeof (name), (uintptr_t) t.tmr_ids + OFFSETOF(fmd_idspace_t, ids_name)) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", t.tmr_ids); if (mdb_lookup_by_addr((uintptr_t)t.tmr_func, MDB_SYM_FUZZY, func, sizeof (func), NULL) != 0) (void) mdb_snprintf(func, sizeof (func), "<%p>", t.tmr_func); mdb_printf("%-8p %-20s %4d 0x%-16llx %-8p %s\n", addr, name, t.tmr_id, t.tmr_hrt, t.tmr_arg, func); return (DCMD_OK); } static int xprt_walk_init(mdb_walk_state_t *wsp) { fmd_module_t m; if (wsp->walk_addr == 0) { mdb_warn("transport walker requires fmd_module_t address\n"); return (WALK_ERR); } if (mdb_vread(&m, sizeof (m), wsp->walk_addr) != sizeof (m)) { mdb_warn("failed to read module at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)m.mod_transports.l_next; return (WALK_NEXT); } static int xprt_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_xprt_impl_t xi; if (addr == 0) return (WALK_DONE); if (mdb_vread(&xi, sizeof (xi), addr) != sizeof (xi)) { mdb_warn("failed to read fmd_xprt at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)xi.xi_list.l_next; return (wsp->walk_callback(addr, &xi, wsp->walk_cbdata)); } static int xpc_walk_init(mdb_walk_state_t *wsp) { fmd_xprt_class_hash_t xch; if (mdb_vread(&xch, sizeof (xch), wsp->walk_addr) != sizeof (xch)) { mdb_warn("failed to read fmd_xprt_class_hash at %p", wsp->walk_addr); return (WALK_ERR); } return (hash_walk_init(wsp, (uintptr_t)xch.xch_hash, xch.xch_hashlen, "fmd_xprt_class", sizeof (fmd_xprt_class_t), OFFSETOF(fmd_xprt_class_t, xc_next))); } /*ARGSUSED*/ static int fmd_xprt_class(uintptr_t addr, const void *data, void *arg) { const fmd_xprt_class_t *xcp = data; char name[1024]; if (mdb_readstr(name, sizeof (name), (uintptr_t)xcp->xc_class) <= 0) (void) mdb_snprintf(name, sizeof (name), "<%p>", xcp->xc_class); mdb_printf("%-8p %-4u %s\n", addr, xcp->xc_refs, name); return (WALK_NEXT); } static int fmd_xprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint_t opt_s = FALSE, opt_l = FALSE, opt_r = FALSE, opt_u = FALSE; fmd_xprt_impl_t xi; if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_l, 'r', MDB_OPT_SETBITS, TRUE, &opt_r, 's', MDB_OPT_SETBITS, TRUE, &opt_s, 'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("fmd_xprt", "fmd_xprt", argc, argv) != 0) { mdb_warn("failed to walk fmd_xprt"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&xi, sizeof (xi), addr) != sizeof (xi)) { mdb_warn("failed to read fmd_xprt at %p", addr); return (DCMD_ERR); } if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-8s %-4s %-4s %-5s %s%\n", "ADDR", "ID", "VERS", "FLAGS", "STATE"); } mdb_printf("%-8p %-4d %-4u %-5x %a\n", addr, xi.xi_id, xi.xi_version, xi.xi_flags, xi.xi_state); if (opt_l | opt_s) { (void) mdb_inc_indent(4); mdb_printf("Local subscriptions requested by peer:\n"); mdb_printf("%%-8s %-4s %s%\n", "ADDR", "REFS", "CLASS"); (void) mdb_pwalk("fmd_xprt_class", fmd_xprt_class, &xi, addr + OFFSETOF(fmd_xprt_impl_t, xi_lsub)); (void) mdb_dec_indent(4); } if (opt_r | opt_s) { (void) mdb_inc_indent(4); mdb_printf("Remote subscriptions requested of peer:\n"); mdb_printf("%%-8s %-4s %s%\n", "ADDR", "REFS", "CLASS"); (void) mdb_pwalk("fmd_xprt_class", fmd_xprt_class, &xi, addr + OFFSETOF(fmd_xprt_impl_t, xi_rsub)); (void) mdb_dec_indent(4); } if (opt_u | opt_s) { (void) mdb_inc_indent(4); mdb_printf("Pending unsubscription acknowledgements:\n"); mdb_printf("%%-8s %-4s %s%\n", "ADDR", "REFS", "CLASS"); (void) mdb_pwalk("fmd_xprt_class", fmd_xprt_class, &xi, addr + OFFSETOF(fmd_xprt_impl_t, xi_usub)); (void) mdb_dec_indent(4); } return (DCMD_OK); } static int tsnap_walk_init(mdb_walk_state_t *wsp) { fmd_t F; if (mdb_readvar(&F, "fmd") != sizeof (F)) { mdb_warn("failed to read fmd meta-data"); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)F.d_topo_list.l_next; return (WALK_NEXT); } static int tsnap_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_topo_t ftp; if (addr == 0) return (WALK_DONE); if (mdb_vread(&ftp, sizeof (ftp), addr) != sizeof (ftp)) { mdb_warn("failed to read fmd_topo_t at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)ftp.ft_list.l_next; return (wsp->walk_callback(addr, &ftp, wsp->walk_cbdata)); } static int mq_walk_init(mdb_walk_state_t *wsp) { fmd_module_t m; struct fmd_eventq eq; if (wsp->walk_addr == 0) { mdb_warn("NULL fmd_module_t passed in"); return (WALK_ERR); } if (mdb_vread(&m, sizeof (m), wsp->walk_addr) != sizeof (m)) { mdb_warn("failed to read fmd_module_t at %p", wsp->walk_addr); return (WALK_ERR); } if (mdb_vread(&eq, sizeof (eq), (uintptr_t)m.mod_queue) != sizeof (eq)) { mdb_warn("failed to read fmd_eventq at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)eq.eq_list.l_next; return (WALK_NEXT); } static int mq_walk_step(mdb_walk_state_t *wsp) { uintptr_t addr = wsp->walk_addr; fmd_eventqelem_t eqe; if (addr == 0) return (WALK_DONE); if (mdb_vread(&eqe, sizeof (eqe), addr) != sizeof (eqe)) { mdb_warn("failed to read fmd_eventqelem_t at %p", addr); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)eqe.eqe_list.l_next; return (wsp->walk_callback(addr, &eqe, wsp->walk_cbdata)); } static const mdb_dcmd_t dcmds[] = { { "fcf_case", "?", "print a FCF case", fcf_case }, { "fcf_event", "?", "print a FCF event", fcf_event }, { "fcf_hdr", "?", "print a FCF header", fcf_hdr }, { "fcf_sec", ":", "print a FCF section header", fcf_sec }, { "fcf_serd", "?", "print a FCF serd engine", fcf_serd }, { "fmd_trace", "?[-cs]", "display thread trace buffer(s)", fmd_trace }, { "fmd_ustat", "[:]", "display statistics collection", fmd_ustat }, { "fmd_stat", "[:]", "display statistic structure", fmd_stat }, { "fmd_event", NULL, "display event structure", fmd_event }, { "fmd_thread", "?", "display thread or list of threads", fmd_thread }, { "fmd_module", "?", "display module or list of modules", fmd_module }, { "fmd_case", ":", "display case file structure", fmd_case }, { "fmd_buf", ":", "display buffer structure", fmd_buf }, { "fmd_serd", "[:]", "display serd engine structure", fmd_serd }, { "fmd_asru", "?", "display asru resource structure", fmd_asru }, { "fmd_asru_link", "?", "display resource structure", fmd_asru_link }, { "fmd_timer", "?", "display pending timer(s)", fmd_timer }, { "fmd_xprt", "?[-lrsu]", "display event transport(s)", fmd_xprt }, { NULL } }; static const mdb_walker_t walkers[] = { { "fcf_sec", "walk FCF section header table given header address", fcf_sec_walk_init, fcf_sec_walk_step, fcf_sec_walk_fini }, { "fmd_trace", "walk per-thread trace buffers", trwalk_init, trwalk_step, trwalk_fini }, { "fmd_ustat", "walk per-collection statistics", ustat_walk_init, ustat_walk_step, hash_walk_fini }, { "fmd_thread", "walk list of all fmd_thread_t's", thread_walk_init, thread_walk_step, NULL }, { "fmd_module", "walk list of all fmd_module_t's", mod_walk_init, mod_walk_step, NULL }, { "fmd_case", "walk per-module case objects", case_walk_init, case_walk_step, case_walk_fini }, { "fmd_buf", "walk per-buf_hash buffers", buf_walk_init, hash_walk_step, hash_walk_fini }, { "fmd_serd", "walk per-serd_hash engines", serd_walk_init, hash_walk_step, hash_walk_fini }, { "fmd_asru", "walk asru resource hash", asru_walk_init, hash_walk_step, hash_walk_fini }, { "fmd_asru_link", "walk resource hash", al_walk_init, hash_walk_step, hash_walk_fini }, { "fmd_timerq", "walk timer queue", tmq_walk_init, tmq_walk_step, NULL }, { "fmd_xprt", "walk per-module list of transports", xprt_walk_init, xprt_walk_step, NULL }, { "fmd_xprt_class", "walk hash table of subscription classes", xpc_walk_init, hash_walk_step, hash_walk_fini }, { "fmd_topo", "walk fmd's list of topo snapshots", tsnap_walk_init, tsnap_walk_step, NULL }, { "fmd_mod_queue", "walk per-module event queue", mq_walk_init, mq_walk_step, NULL }, { NULL, NULL, NULL, NULL, NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Template for per-module statistics installed by fmd on behalf of each active * module. These are used to initialize the per-module mp->mod_stats below. * NOTE: FMD_TYPE_STRING statistics should not be used here. If they are * required in the future, the FMD_ADM_MODDSTAT service routine must change. */ static const fmd_modstat_t _fmd_modstat_tmpl = { { { "fmd.dispatched", FMD_TYPE_UINT64, "total events dispatched to module" }, { "fmd.dequeued", FMD_TYPE_UINT64, "total events dequeued by module" }, { "fmd.prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by module" }, { "fmd.dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" }, { "fmd.wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" }, { "fmd.wtime", FMD_TYPE_TIME, "total wait time on queue" }, { "fmd.wlentime", FMD_TYPE_TIME, "total wait length * time product" }, { "fmd.wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" }, { "fmd.dtime", FMD_TYPE_TIME, "total processing time after dequeue" }, { "fmd.dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" }, }, { "fmd.loadtime", FMD_TYPE_TIME, "hrtime at which module was loaded" }, { "fmd.snaptime", FMD_TYPE_TIME, "hrtime of last statistics snapshot" }, { "fmd.accepted", FMD_TYPE_UINT64, "total events accepted by module" }, { "fmd.debugdrop", FMD_TYPE_UINT64, "dropped debug messages" }, { "fmd.memtotal", FMD_TYPE_SIZE, "total memory allocated by module" }, { "fmd.memlimit", FMD_TYPE_SIZE, "limit on total memory allocated" }, { "fmd.buftotal", FMD_TYPE_SIZE, "total buffer space used by module" }, { "fmd.buflimit", FMD_TYPE_SIZE, "limit on total buffer space" }, { "fmd.thrtotal", FMD_TYPE_UINT32, "total number of auxiliary threads" }, { "fmd.thrlimit", FMD_TYPE_UINT32, "limit on number of auxiliary threads" }, { "fmd.doorthrtotal", FMD_TYPE_UINT32, "total number of door server threads" }, { "fmd.doorthrlimit", FMD_TYPE_UINT32, "limit on door server threads" }, { "fmd.caseopen", FMD_TYPE_UINT64, "cases currently open by module" }, { "fmd.casesolved", FMD_TYPE_UINT64, "total cases solved by module" }, { "fmd.caseclosed", FMD_TYPE_UINT64, "total cases closed by module" }, { "fmd.ckptsave", FMD_TYPE_BOOL, "save checkpoints for module" }, { "fmd.ckptrestore", FMD_TYPE_BOOL, "restore checkpoints for module" }, { "fmd.ckptzero", FMD_TYPE_BOOL, "zeroed checkpoint at startup" }, { "fmd.ckptcnt", FMD_TYPE_UINT64, "number of checkpoints taken" }, { "fmd.ckpttime", FMD_TYPE_TIME, "total checkpoint time" }, { "fmd.xprtopen", FMD_TYPE_UINT32, "total number of open transports" }, { "fmd.xprtlimit", FMD_TYPE_UINT32, "limit on number of open transports" }, { "fmd.xprtqlimit", FMD_TYPE_UINT32, "limit on transport event queue length" }, }; static void fmd_module_start(void *arg) { fmd_module_t *mp = arg; fmd_event_t *ep; fmd_xprt_t *xp; (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_ops->mop_init(mp) != 0 || mp->mod_error != 0) { if (mp->mod_error == 0) mp->mod_error = errno ? errno : EFMD_MOD_INIT; goto out; } if (fmd.d_mod_event != NULL) fmd_eventq_insert_at_head(mp->mod_queue, fmd.d_mod_event); ASSERT(MUTEX_HELD(&mp->mod_lock)); mp->mod_flags |= FMD_MOD_INIT; (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); /* * If the module opened any transports while executing _fmd_init(), * they are suspended. Now that _fmd_init() is done, wake them up. */ for (xp = fmd_list_next(&mp->mod_transports); xp != NULL; xp = fmd_list_next(xp)) fmd_xprt_xresume(xp, FMD_XPRT_ISUSPENDED); /* * Wait for events to arrive by checking mod_error and then sleeping in * fmd_eventq_delete(). If a NULL event is returned, the eventq has * been aborted and we continue on to call fini and exit the thread. */ while ((ep = fmd_eventq_delete(mp->mod_queue)) != NULL) { /* * If the module has failed, discard the event without ever * passing it to the module and go back to sleep. */ if (mp->mod_error != 0) { fmd_eventq_done(mp->mod_queue); fmd_event_rele(ep); continue; } mp->mod_ops->mop_dispatch(mp, ep); fmd_eventq_done(mp->mod_queue); /* * Once mop_dispatch() is complete, grab the lock and perform * any event-specific post-processing. Finally, if necessary, * checkpoint the state of the module after this event. */ fmd_module_lock(mp); if (FMD_EVENT_TYPE(ep) == FMD_EVT_CLOSE) fmd_case_delete(FMD_EVENT_DATA(ep)); fmd_ckpt_save(mp); fmd_module_unlock(mp); fmd_event_rele(ep); } if (mp->mod_ops->mop_fini(mp) != 0 && mp->mod_error == 0) mp->mod_error = errno ? errno : EFMD_MOD_FINI; (void) pthread_mutex_lock(&mp->mod_lock); mp->mod_flags |= FMD_MOD_FINI; out: (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); } fmd_module_t * fmd_module_create(const char *path, const fmd_modops_t *ops) { fmd_module_t *mp = fmd_zalloc(sizeof (fmd_module_t), FMD_SLEEP); char buf[PATH_MAX], *p; const char *dir; uint32_t limit; int err; (void) strlcpy(buf, fmd_strbasename(path), sizeof (buf)); if ((p = strrchr(buf, '.')) != NULL && strcmp(p, ".so") == 0) *p = '\0'; /* strip trailing .so from any module name */ (void) pthread_mutex_init(&mp->mod_lock, NULL); (void) pthread_cond_init(&mp->mod_cv, NULL); (void) pthread_mutex_init(&mp->mod_stats_lock, NULL); mp->mod_name = fmd_strdup(buf, FMD_SLEEP); mp->mod_path = fmd_strdup(path, FMD_SLEEP); mp->mod_ops = ops; mp->mod_ustat = fmd_ustat_create(); (void) fmd_conf_getprop(fmd.d_conf, "ckpt.dir", &dir); (void) snprintf(buf, sizeof (buf), "%s/%s/%s", fmd.d_rootdir, dir, mp->mod_name); mp->mod_ckpt = fmd_strdup(buf, FMD_SLEEP); (void) fmd_conf_getprop(fmd.d_conf, "client.tmrlim", &limit); mp->mod_timerids = fmd_idspace_create(mp->mod_name, 1, limit + 1); mp->mod_threads = fmd_idspace_create(mp->mod_name, 0, INT_MAX); fmd_buf_hash_create(&mp->mod_bufs); fmd_serd_hash_create(&mp->mod_serds); mp->mod_topo_current = fmd_topo_hold(); (void) pthread_mutex_lock(&fmd.d_mod_lock); fmd_list_append(&fmd.d_mod_list, mp); (void) pthread_mutex_unlock(&fmd.d_mod_lock); /* * Initialize the module statistics that are kept on its behalf by fmd. * These are set up using a template defined at the top of this file. */ if ((mp->mod_stats = (fmd_modstat_t *)fmd_ustat_insert(mp->mod_ustat, FMD_USTAT_ALLOC, sizeof (_fmd_modstat_tmpl) / sizeof (fmd_stat_t), (fmd_stat_t *)&_fmd_modstat_tmpl, NULL)) == NULL) { fmd_error(EFMD_MOD_INIT, "failed to initialize per-mod stats"); fmd_module_destroy(mp); return (NULL); } if (nv_alloc_init(&mp->mod_nva_sleep, &fmd_module_nva_ops_sleep, mp) != 0 || nv_alloc_init(&mp->mod_nva_nosleep, &fmd_module_nva_ops_nosleep, mp) != 0) { fmd_error(EFMD_MOD_INIT, "failed to initialize nvlist " "allocation routines"); fmd_module_destroy(mp); return (NULL); } (void) fmd_conf_getprop(fmd.d_conf, "client.evqlim", &limit); mp->mod_queue = fmd_eventq_create(mp, &mp->mod_stats->ms_evqstat, &mp->mod_stats_lock, limit); (void) fmd_conf_getprop(fmd.d_conf, "client.memlim", &mp->mod_stats->ms_memlimit.fmds_value.ui64); (void) fmd_conf_getprop(fmd.d_conf, "client.buflim", &mp->mod_stats->ms_buflimit.fmds_value.ui64); (void) fmd_conf_getprop(fmd.d_conf, "client.thrlim", &mp->mod_stats->ms_thrlimit.fmds_value.ui32); (void) fmd_conf_getprop(fmd.d_conf, "client.doorthrlim", &mp->mod_stats->ms_doorthrlimit.fmds_value.ui32); (void) fmd_conf_getprop(fmd.d_conf, "client.xprtlim", &mp->mod_stats->ms_xprtlimit.fmds_value.ui32); (void) fmd_conf_getprop(fmd.d_conf, "client.xprtqlim", &mp->mod_stats->ms_xprtqlimit.fmds_value.ui32); (void) fmd_conf_getprop(fmd.d_conf, "ckpt.save", &mp->mod_stats->ms_ckpt_save.fmds_value.bool); (void) fmd_conf_getprop(fmd.d_conf, "ckpt.restore", &mp->mod_stats->ms_ckpt_restore.fmds_value.bool); (void) fmd_conf_getprop(fmd.d_conf, "ckpt.zero", &mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool); if (mp->mod_stats->ms_ckpt_zeroed.fmds_value.bool) fmd_ckpt_delete(mp); /* blow away any pre-existing checkpoint */ /* * Place a hold on the module and grab the module lock before creating * the module's thread to ensure that it cannot destroy the module and * that it cannot call ops->mop_init() before we're done setting up. * NOTE: from now on, we must use fmd_module_rele() for error paths. */ fmd_module_hold(mp); (void) pthread_mutex_lock(&mp->mod_lock); mp->mod_stats->ms_loadtime.fmds_value.ui64 = gethrtime(); mp->mod_thread = fmd_thread_create(mp, fmd_module_start, mp); if (mp->mod_thread == NULL) { fmd_error(EFMD_MOD_THR, "failed to create thread for %s", path); (void) pthread_mutex_unlock(&mp->mod_lock); fmd_module_rele(mp); return (NULL); } /* * At this point our module structure is nearly finished and its thread * is starting execution in fmd_module_start() above, which will begin * by blocking for mod_lock. We now drop mod_lock and wait for either * FMD_MOD_INIT or mod_error to be set before proceeding. */ while (!(mp->mod_flags & FMD_MOD_INIT) && mp->mod_error == 0) (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); /* * If the module has failed to initialize, copy its errno to the errno * of the caller, wait for it to unload, and then destroy it. */ if (!(mp->mod_flags & FMD_MOD_INIT)) { err = mp->mod_error; (void) pthread_mutex_unlock(&mp->mod_lock); if (err == EFMD_CKPT_INVAL) fmd_ckpt_rename(mp); /* move aside bad checkpoint */ /* * If we're in the background, keep quiet about failure to * load because a handle wasn't registered: this is a module's * way of telling us it didn't want to be loaded for some * reason related to system configuration. If we're in the * foreground we log this too in order to inform developers. */ if (fmd.d_fg || err != EFMD_HDL_INIT) { fmd_error(EFMD_MOD_INIT, "failed to load %s: %s\n", path, fmd_strerror(err)); } fmd_module_unload(mp); fmd_module_rele(mp); (void) fmd_set_errno(err); return (NULL); } (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); fmd_dprintf(FMD_DBG_MOD, "loaded module %s\n", mp->mod_name); return (mp); } static void fmd_module_untimeout(fmd_idspace_t *ids, id_t id, fmd_module_t *mp) { void *arg = fmd_timerq_remove(fmd.d_timers, ids, id); /* * The root module calls fmd_timerq_install() directly and must take * responsibility for any cleanup of timer arguments that is required. * All other modules use fmd_modtimer_t's as the arg data; free them. */ if (arg != NULL && mp != fmd.d_rmod) fmd_free(arg, sizeof (fmd_modtimer_t)); } void fmd_module_unload(fmd_module_t *mp) { fmd_modtopo_t *mtp; (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_flags & FMD_MOD_QUIT) { (void) pthread_mutex_unlock(&mp->mod_lock); return; /* module is already unloading */ } ASSERT(mp->mod_thread != NULL); mp->mod_flags |= FMD_MOD_QUIT; if (mp->mod_queue != NULL) fmd_eventq_abort(mp->mod_queue); /* * Wait for the module's thread to stop processing events and call * _fmd_fini() and exit. We do this by waiting for FMD_MOD_FINI to be * set if INIT was set, and then attempting to join with the thread. */ while ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI)) == FMD_MOD_INIT) (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); fmd_thread_destroy(mp->mod_thread, FMD_THREAD_JOIN); mp->mod_thread = NULL; /* * Once the module is no longer active, clean up any data structures * that are only required when the module is loaded. */ fmd_module_lock(mp); if (mp->mod_timerids != NULL) { fmd_idspace_apply(mp->mod_timerids, (void (*)())fmd_module_untimeout, mp); fmd_idspace_destroy(mp->mod_timerids); mp->mod_timerids = NULL; } if (mp->mod_threads != NULL) { fmd_idspace_destroy(mp->mod_threads); mp->mod_threads = NULL; } (void) fmd_buf_hash_destroy(&mp->mod_bufs); fmd_serd_hash_destroy(&mp->mod_serds); while ((mtp = fmd_list_next(&mp->mod_topolist)) != NULL) { fmd_list_delete(&mp->mod_topolist, mtp); fmd_topo_rele(mtp->mt_topo); fmd_free(mtp, sizeof (fmd_modtopo_t)); } fmd_module_unlock(mp); fmd_dprintf(FMD_DBG_MOD, "unloaded module %s\n", mp->mod_name); } void fmd_module_destroy(fmd_module_t *mp) { fmd_conf_formal_t *cfp = mp->mod_argv; int i; ASSERT(MUTEX_HELD(&mp->mod_lock)); if (mp->mod_thread != NULL) { (void) pthread_mutex_unlock(&mp->mod_lock); fmd_module_unload(mp); (void) pthread_mutex_lock(&mp->mod_lock); } ASSERT(mp->mod_thread == NULL); ASSERT(mp->mod_refs == 0); /* * Once the module's thread is dead, we can safely remove the module * from global visibility and by removing it from d_mod_list. Any * modhash pointers are already gone by virtue of mod_refs being zero. */ (void) pthread_mutex_lock(&fmd.d_mod_lock); fmd_list_delete(&fmd.d_mod_list, mp); (void) pthread_mutex_unlock(&fmd.d_mod_lock); if (mp->mod_topo_current != NULL) fmd_topo_rele(mp->mod_topo_current); if (mp->mod_nva_sleep.nva_ops != NULL) nv_alloc_fini(&mp->mod_nva_sleep); if (mp->mod_nva_nosleep.nva_ops != NULL) nv_alloc_fini(&mp->mod_nva_nosleep); /* * Once the module is no longer processing events and no longer visible * through any program data structures, we can free all of its content. */ if (mp->mod_queue != NULL) { fmd_eventq_destroy(mp->mod_queue); mp->mod_queue = NULL; } if (mp->mod_ustat != NULL) { (void) pthread_mutex_lock(&mp->mod_stats_lock); fmd_ustat_destroy(mp->mod_ustat); mp->mod_ustat = NULL; mp->mod_stats = NULL; (void) pthread_mutex_unlock(&mp->mod_stats_lock); } for (i = 0; i < mp->mod_dictc; i++) fm_dc_closedict(mp->mod_dictv[i]); fmd_free(mp->mod_dictv, sizeof (struct fm_dc_handle *) * mp->mod_dictc); if (mp->mod_conf != NULL) fmd_conf_close(mp->mod_conf); for (i = 0; i < mp->mod_argc; i++, cfp++) { fmd_strfree((char *)cfp->cf_name); fmd_strfree((char *)cfp->cf_default); } fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc); fmd_strfree(mp->mod_name); fmd_strfree(mp->mod_path); fmd_strfree(mp->mod_ckpt); nvlist_free(mp->mod_fmri); fmd_strfree(mp->mod_vers); fmd_free(mp, sizeof (fmd_module_t)); } /* * fmd_module_error() is called after the stack is unwound from a call to * fmd_module_abort() to indicate that the module has failed. The mod_error * field is used to hold the error code of the first fatal error to the module. * An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis. */ static void fmd_module_error(fmd_module_t *mp, int err) { fmd_event_t *e; nvlist_t *nvl; char *class; ASSERT(MUTEX_HELD(&mp->mod_lock)); ASSERT(err != 0); TRACE((FMD_DBG_MOD, "module aborted: err=%d", err)); if (mp->mod_error == 0) mp->mod_error = err; if (mp == fmd.d_self) return; /* do not post event if fmd.d_self itself fails */ /* * Send an error indicating the module has now failed to fmd.d_self. * Since the error causing the failure has already been logged by * fmd_api_xerror(), we do not need to bother logging this event. * It only exists for the purpose of notifying fmd.d_self that it can * close the case associated with this module because mod_error is set. */ nvl = fmd_protocol_moderror(mp, EFMD_MOD_FAIL, fmd_strerror(err)); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dispq_dispatch(fmd.d_disp, e, class); } void fmd_module_dispatch(fmd_module_t *mp, fmd_event_t *e) { const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops; fmd_event_impl_t *ep = (fmd_event_impl_t *)e; fmd_hdl_t *hdl = (fmd_hdl_t *)mp; fmd_modtimer_t *t; fmd_topo_t *old_topo; volatile int err; /* * Before calling the appropriate module callback, enter the module as * if by fmd_module_enter() and establish mod_jmpbuf for any aborts. */ (void) pthread_mutex_lock(&mp->mod_lock); ASSERT(!(mp->mod_flags & FMD_MOD_BUSY)); mp->mod_flags |= FMD_MOD_BUSY; if ((err = setjmp(mp->mod_jmpbuf)) != 0) { (void) pthread_mutex_lock(&mp->mod_lock); fmd_module_error(mp, err); } (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); /* * If it's the first time through fmd_module_dispatch(), call the * appropriate module callback based on the event type. If the call * triggers an fmd_module_abort(), we'll return to setjmp() above with * err set to a non-zero value and then bypass this before exiting. */ if (err == 0) { switch (ep->ev_type) { case FMD_EVT_PROTOCOL: ops->fmdo_recv(hdl, e, ep->ev_nvl, ep->ev_data); break; case FMD_EVT_TIMEOUT: t = ep->ev_data; ASSERT(t->mt_mod == mp); ops->fmdo_timeout(hdl, t->mt_id, t->mt_arg); break; case FMD_EVT_CLOSE: ops->fmdo_close(hdl, ep->ev_data); break; case FMD_EVT_STATS: ops->fmdo_stats(hdl); fmd_modstat_publish(mp); break; case FMD_EVT_GC: ops->fmdo_gc(hdl); break; case FMD_EVT_PUBLISH: fmd_case_publish(ep->ev_data, FMD_CASE_CURRENT); break; case FMD_EVT_TOPO: /* * Save the pointer to the old topology and update * the pointer with the updated topology. * With this approach, other threads that reference the * topology either * - finishes with old topology since * it is released after updating * mod_topo_current. * - or is blocked while mod_topo_current is updated. */ old_topo = mp->mod_topo_current; fmd_module_lock(mp); mp->mod_topo_current = (fmd_topo_t *)ep->ev_data; fmd_topo_addref(mp->mod_topo_current); fmd_module_unlock(mp); fmd_topo_rele(old_topo); ops->fmdo_topo(hdl, mp->mod_topo_current->ft_hdl); break; } } fmd_module_exit(mp); } int fmd_module_transport(fmd_module_t *mp, fmd_xprt_t *xp, fmd_event_t *e) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; fmd_hdl_t *hdl = (fmd_hdl_t *)mp; ASSERT(ep->ev_type == FMD_EVT_PROTOCOL); return (mp->mod_info->fmdi_ops->fmdo_send(hdl, xp, e, ep->ev_nvl)); } void fmd_module_timeout(fmd_modtimer_t *t, id_t id, hrtime_t hrt) { fmd_event_t *e; t->mt_id = id; /* save id in case we need to delete from eventq */ e = fmd_event_create(FMD_EVT_TIMEOUT, hrt, NULL, t); fmd_eventq_insert_at_time(t->mt_mod->mod_queue, e); } /* * Garbage collection is initiated by a timer callback once per day or at the * request of fmadm. Purge old SERD entries and send the module a GC event. */ void fmd_module_gc(fmd_module_t *mp) { fmd_hdl_info_t *info; fmd_event_t *e; if (mp->mod_error != 0) return; /* do not do anything if the module has failed */ fmd_module_lock(mp); if ((info = mp->mod_info) != NULL) { fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL); } fmd_module_unlock(mp); if (info != NULL) { e = fmd_event_create(FMD_EVT_GC, FMD_HRT_NOW, NULL, NULL); fmd_eventq_insert_at_head(mp->mod_queue, e); } } void fmd_module_trygc(fmd_module_t *mp) { if (fmd_module_trylock(mp)) { fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL); fmd_module_unlock(mp); } } int fmd_module_contains(fmd_module_t *mp, fmd_event_t *ep) { fmd_case_t *cp; int rv = 0; fmd_module_lock(mp); for (cp = fmd_list_next(&mp->mod_cases); cp != NULL; cp = fmd_list_next(cp)) { if ((rv = fmd_case_contains(cp, ep)) != 0) break; } if (rv == 0) rv = fmd_serd_hash_contains(&mp->mod_serds, ep); fmd_module_unlock(mp); return (rv); } void fmd_module_setdirty(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); mp->mod_flags |= FMD_MOD_MDIRTY; (void) pthread_mutex_unlock(&mp->mod_lock); } void fmd_module_setcdirty(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); mp->mod_flags |= FMD_MOD_CDIRTY; (void) pthread_mutex_unlock(&mp->mod_lock); } void fmd_module_clrdirty(fmd_module_t *mp) { fmd_case_t *cp; fmd_module_lock(mp); if (mp->mod_flags & FMD_MOD_CDIRTY) { for (cp = fmd_list_next(&mp->mod_cases); cp != NULL; cp = fmd_list_next(cp)) fmd_case_clrdirty(cp); } if (mp->mod_flags & FMD_MOD_MDIRTY) { fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_clrdirty, NULL); fmd_buf_hash_commit(&mp->mod_bufs); } (void) pthread_mutex_lock(&mp->mod_lock); mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY); (void) pthread_mutex_unlock(&mp->mod_lock); fmd_module_unlock(mp); } void fmd_module_commit(fmd_module_t *mp) { fmd_case_t *cp; ASSERT(fmd_module_locked(mp)); if (mp->mod_flags & FMD_MOD_CDIRTY) { for (cp = fmd_list_next(&mp->mod_cases); cp != NULL; cp = fmd_list_next(cp)) fmd_case_commit(cp); } if (mp->mod_flags & FMD_MOD_MDIRTY) { fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_commit, NULL); fmd_buf_hash_commit(&mp->mod_bufs); } (void) pthread_mutex_lock(&mp->mod_lock); mp->mod_flags &= ~(FMD_MOD_MDIRTY | FMD_MOD_CDIRTY); (void) pthread_mutex_unlock(&mp->mod_lock); mp->mod_gen++; } void fmd_module_lock(fmd_module_t *mp) { pthread_t self = pthread_self(); (void) pthread_mutex_lock(&mp->mod_lock); while (mp->mod_flags & FMD_MOD_LOCK) { if (mp->mod_owner != self) (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); else fmd_panic("recursive module lock of %p\n", (void *)mp); } mp->mod_owner = self; mp->mod_flags |= FMD_MOD_LOCK; (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); } void fmd_module_unlock(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); ASSERT(mp->mod_owner == pthread_self()); ASSERT(mp->mod_flags & FMD_MOD_LOCK); mp->mod_owner = 0; mp->mod_flags &= ~FMD_MOD_LOCK; (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); } int fmd_module_trylock(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_flags & FMD_MOD_LOCK) { (void) pthread_mutex_unlock(&mp->mod_lock); return (0); } mp->mod_owner = pthread_self(); mp->mod_flags |= FMD_MOD_LOCK; (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); return (1); } int fmd_module_locked(fmd_module_t *mp) { return ((mp->mod_flags & FMD_MOD_LOCK) && mp->mod_owner == pthread_self()); } int fmd_module_enter(fmd_module_t *mp, void (*func)(fmd_hdl_t *)) { volatile int err; (void) pthread_mutex_lock(&mp->mod_lock); ASSERT(!(mp->mod_flags & FMD_MOD_BUSY)); mp->mod_flags |= FMD_MOD_BUSY; if ((err = setjmp(mp->mod_jmpbuf)) != 0) { (void) pthread_mutex_lock(&mp->mod_lock); fmd_module_error(mp, err); } (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); /* * If it's the first time through fmd_module_enter(), call the provided * function on the module. If no fmd_module_abort() results, we will * fall through and return zero. Otherwise we'll longjmp with an err, * return to the setjmp() above, and return the error to our caller. */ if (err == 0 && func != NULL) (*func)((fmd_hdl_t *)mp); return (err); } void fmd_module_exit(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); ASSERT(mp->mod_flags & FMD_MOD_BUSY); mp->mod_flags &= ~FMD_MOD_BUSY; (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); } /* * If the client.error policy has been set by a developer, stop or dump core * based on the policy; if we stop and are resumed we'll continue and execute * the default behavior to discard events in fmd_module_start(). If the caller * is the primary module thread, we reach this state by longjmp'ing back to * fmd_module_enter(), above. If the caller is an auxiliary thread, we cancel * ourself and arrange for the primary thread to call fmd_module_abort(). */ void fmd_module_abort(fmd_module_t *mp, int err) { uint_t policy = FMD_CERROR_UNLOAD; pthread_t tid = pthread_self(); (void) fmd_conf_getprop(fmd.d_conf, "client.error", &policy); if (policy == FMD_CERROR_STOP) { fmd_error(err, "stopping after %s in client %s (%p)\n", fmd_errclass(err), mp->mod_name, (void *)mp); (void) raise(SIGSTOP); } else if (policy == FMD_CERROR_ABORT) { fmd_panic("aborting due to %s in client %s (%p)\n", fmd_errclass(err), mp->mod_name, (void *)mp); } /* * If the caller is an auxiliary thread, cancel the current thread. We * prefer to cancel because it affords developers the option of using * the pthread_cleanup* APIs. If cancellations have been disabled, * fall through to forcing the current thread to exit. In either case * we update mod_error (if zero) to enter the failed state. Once that * is set, further events received by the module will be discarded. * * We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error. * When an auxiliary thread fails, the module is left in a delicate * state where it is likely not able to continue execution (even to * execute its _fmd_fini() routine) because our caller may hold locks * that are private to the module and can no longer be released. The * FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other * module threads reach an API call, in an attempt to get them to exit. */ if (tid != mp->mod_thread->thr_tid) { (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_error == 0) mp->mod_error = err; mp->mod_flags |= FMD_MOD_FAIL; (void) pthread_mutex_unlock(&mp->mod_lock); (void) pthread_cancel(tid); pthread_exit(NULL); } ASSERT(mp->mod_flags & FMD_MOD_BUSY); longjmp(mp->mod_jmpbuf, err); } void fmd_module_hold(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); TRACE((FMD_DBG_MOD, "hold %p (%s/%u)\n", (void *)mp, mp->mod_name, mp->mod_refs)); mp->mod_refs++; ASSERT(mp->mod_refs != 0); (void) pthread_mutex_unlock(&mp->mod_lock); } void fmd_module_rele(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); TRACE((FMD_DBG_MOD, "rele %p (%s/%u)\n", (void *)mp, mp->mod_name, mp->mod_refs)); ASSERT(mp->mod_refs != 0); if (--mp->mod_refs == 0) fmd_module_destroy(mp); else (void) pthread_mutex_unlock(&mp->mod_lock); } /* * Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries. * If the dictionary open is successful, the new dictionary is added to the * mod_dictv[] array and mod_codelen is updated with the new maximum length. */ int fmd_module_dc_opendict(fmd_module_t *mp, const char *dict) { struct fm_dc_handle *dcp, **dcv; char *dictdir, *dictnam, *p; size_t len; ASSERT(fmd_module_locked(mp)); dictnam = strdupa(fmd_strbasename(dict)); if ((p = strrchr(dictnam, '.')) != NULL && strcmp(p, ".dict") == 0) *p = '\0'; /* eliminate any trailing .dict suffix */ /* * If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict` * If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict` */ if (dict[0] == '/') { len = strlen(fmd.d_rootdir) + strlen(dict) + 1; dictdir = alloca(len); (void) snprintf(dictdir, len, "%s%s", fmd.d_rootdir, dict); (void) fmd_strdirname(dictdir); } else { (void) fmd_conf_getprop(fmd.d_conf, "dictdir", &p); len = strlen(fmd.d_rootdir) + strlen(p) + strlen(dict) + 3; dictdir = alloca(len); (void) snprintf(dictdir, len, "%s/%s/%s", fmd.d_rootdir, p, dict); (void) fmd_strdirname(dictdir); } fmd_dprintf(FMD_DBG_MOD, "module %s opening %s -> %s/%s.dict\n", mp->mod_name, dict, dictdir, dictnam); if ((dcp = fm_dc_opendict(FM_DC_VERSION, dictdir, dictnam)) == NULL) return (-1); /* errno is set for us */ dcv = fmd_alloc(sizeof (dcp) * (mp->mod_dictc + 1), FMD_SLEEP); bcopy(mp->mod_dictv, dcv, sizeof (dcp) * mp->mod_dictc); fmd_free(mp->mod_dictv, sizeof (dcp) * mp->mod_dictc); mp->mod_dictv = dcv; mp->mod_dictv[mp->mod_dictc++] = dcp; len = fm_dc_codelen(dcp); mp->mod_codelen = MAX(mp->mod_codelen, len); return (0); } /* * Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's * dictionaries. We adhere to the libdiagcode return values and semantics. */ int fmd_module_dc_key2code(fmd_module_t *mp, char *const keys[], char *code, size_t codelen) { int i, err; for (i = 0; i < mp->mod_dictc; i++) { if ((err = fm_dc_key2code(mp->mod_dictv[i], (const char **)keys, code, codelen)) == 0 || errno != ENOMSG) return (err); } return (fmd_set_errno(ENOMSG)); } fmd_modhash_t * fmd_modhash_create(void) { fmd_modhash_t *mhp = fmd_alloc(sizeof (fmd_modhash_t), FMD_SLEEP); (void) pthread_rwlock_init(&mhp->mh_lock, NULL); mhp->mh_hashlen = fmd.d_str_buckets; mhp->mh_hash = fmd_zalloc(sizeof (void *) * mhp->mh_hashlen, FMD_SLEEP); mhp->mh_nelems = 0; return (mhp); } void fmd_modhash_destroy(fmd_modhash_t *mhp) { fmd_module_t *mp, *nmp; uint_t i; for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = nmp) { nmp = mp->mod_next; mp->mod_next = NULL; fmd_module_rele(mp); } } fmd_free(mhp->mh_hash, sizeof (void *) * mhp->mh_hashlen); (void) pthread_rwlock_destroy(&mhp->mh_lock); fmd_free(mhp, sizeof (fmd_modhash_t)); } static void fmd_modhash_loaddir(fmd_modhash_t *mhp, const char *dir, const fmd_modops_t *ops, const char *suffix) { char path[PATH_MAX]; struct dirent *dp; const char *p; DIR *dirp; if ((dirp = opendir(dir)) == NULL) return; /* failed to open directory; just skip it */ while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.') continue; /* skip "." and ".." */ p = strrchr(dp->d_name, '.'); if (p != NULL && strcmp(p, ".conf") == 0) continue; /* skip .conf files */ if (suffix != NULL && (p == NULL || strcmp(p, suffix) != 0)) continue; /* skip files with the wrong suffix */ (void) snprintf(path, sizeof (path), "%s/%s", dir, dp->d_name); (void) fmd_modhash_load(mhp, path, ops); } (void) closedir(dirp); } void fmd_modhash_loadall(fmd_modhash_t *mhp, const fmd_conf_path_t *pap, const fmd_modops_t *ops, const char *suffix) { int i; for (i = 0; i < pap->cpa_argc; i++) fmd_modhash_loaddir(mhp, pap->cpa_argv[i], ops, suffix); } void fmd_modhash_apply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) { fmd_module_t *mp, *np; uint_t i; (void) pthread_rwlock_rdlock(&mhp->mh_lock); for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { np = mp->mod_next; func(mp); } } (void) pthread_rwlock_unlock(&mhp->mh_lock); } void fmd_modhash_tryapply(fmd_modhash_t *mhp, void (*func)(fmd_module_t *)) { fmd_module_t *mp, *np; uint_t i; if (mhp == NULL || pthread_rwlock_tryrdlock(&mhp->mh_lock) != 0) return; /* not initialized or couldn't grab lock */ for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = np) { np = mp->mod_next; func(mp); } } (void) pthread_rwlock_unlock(&mhp->mh_lock); } void fmd_modhash_dispatch(fmd_modhash_t *mhp, fmd_event_t *ep) { fmd_module_t *mp; uint_t i; fmd_event_hold(ep); (void) pthread_rwlock_rdlock(&mhp->mh_lock); for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) { /* * If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and * mod_error are all zero, then the module is active: * enqueue the event in the corresponding event queue. */ (void) pthread_mutex_lock(&mp->mod_lock); if ((mp->mod_flags & (FMD_MOD_INIT | FMD_MOD_FINI | FMD_MOD_QUIT)) == FMD_MOD_INIT && !mp->mod_error) { /* * If the event we're dispatching is of type * FMD_EVT_TOPO and there are already redundant * FMD_EVT_TOPO events in this module's queue, * then drop those before adding the new one. */ if (FMD_EVENT_TYPE(ep) == FMD_EVT_TOPO) fmd_eventq_drop_topo(mp->mod_queue); fmd_eventq_insert_at_time(mp->mod_queue, ep); } (void) pthread_mutex_unlock(&mp->mod_lock); } } (void) pthread_rwlock_unlock(&mhp->mh_lock); fmd_event_rele(ep); } fmd_module_t * fmd_modhash_lookup(fmd_modhash_t *mhp, const char *name) { fmd_module_t *mp; uint_t h; (void) pthread_rwlock_rdlock(&mhp->mh_lock); h = fmd_strhash(name) % mhp->mh_hashlen; for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { if (strcmp(name, mp->mod_name) == 0) break; } if (mp != NULL) fmd_module_hold(mp); else (void) fmd_set_errno(EFMD_MOD_NOMOD); (void) pthread_rwlock_unlock(&mhp->mh_lock); return (mp); } fmd_module_t * fmd_modhash_load(fmd_modhash_t *mhp, const char *path, const fmd_modops_t *ops) { char name[PATH_MAX], *p; fmd_module_t *mp; int tries = 0; uint_t h; (void) strlcpy(name, fmd_strbasename(path), sizeof (name)); if ((p = strrchr(name, '.')) != NULL && strcmp(p, ".so") == 0) *p = '\0'; /* strip trailing .so from any module name */ (void) pthread_rwlock_wrlock(&mhp->mh_lock); h = fmd_strhash(name) % mhp->mh_hashlen; /* * First check to see if a module is already present in the hash table * for this name. If so, the module is already loaded: skip it. */ for (mp = mhp->mh_hash[h]; mp != NULL; mp = mp->mod_next) { if (strcmp(name, mp->mod_name) == 0) break; } if (mp != NULL) { (void) pthread_rwlock_unlock(&mhp->mh_lock); (void) fmd_set_errno(EFMD_MOD_LOADED); return (NULL); } /* * fmd_module_create() will return a held (as if by fmd_module_hold()) * module. We leave this hold in place to correspond to the hash-in. */ while ((mp = fmd_module_create(path, ops)) == NULL) { if (tries++ != 0 || errno != EFMD_CKPT_INVAL) { (void) pthread_rwlock_unlock(&mhp->mh_lock); return (NULL); /* errno is set for us */ } } mp->mod_hash = mhp; mp->mod_next = mhp->mh_hash[h]; mhp->mh_hash[h] = mp; mhp->mh_nelems++; (void) pthread_rwlock_unlock(&mhp->mh_lock); return (mp); } int fmd_modhash_unload(fmd_modhash_t *mhp, const char *name) { fmd_module_t *mp, **pp; uint_t h; (void) pthread_rwlock_wrlock(&mhp->mh_lock); h = fmd_strhash(name) % mhp->mh_hashlen; pp = &mhp->mh_hash[h]; for (mp = *pp; mp != NULL; mp = mp->mod_next) { if (strcmp(name, mp->mod_name) == 0) break; else pp = &mp->mod_next; } if (mp == NULL) { (void) pthread_rwlock_unlock(&mhp->mh_lock); return (fmd_set_errno(EFMD_MOD_NOMOD)); } *pp = mp->mod_next; mp->mod_next = NULL; ASSERT(mhp->mh_nelems != 0); mhp->mh_nelems--; (void) pthread_rwlock_unlock(&mhp->mh_lock); fmd_module_unload(mp); fmd_module_rele(mp); return (0); } void fmd_modstat_publish(fmd_module_t *mp) { (void) pthread_mutex_lock(&mp->mod_lock); ASSERT(mp->mod_flags & FMD_MOD_STSUB); mp->mod_flags |= FMD_MOD_STPUB; (void) pthread_cond_broadcast(&mp->mod_cv); while (mp->mod_flags & FMD_MOD_STPUB) (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); (void) pthread_mutex_unlock(&mp->mod_lock); } int fmd_modstat_snapshot(fmd_module_t *mp, fmd_ustat_snap_t *uss) { fmd_event_t *e; int err; /* * Grab the module lock and wait for the STSUB bit to be clear. Then * set it to indicate we are a subscriber and everyone else must wait. */ (void) pthread_mutex_lock(&mp->mod_lock); while (mp->mod_error == 0 && (mp->mod_flags & FMD_MOD_STSUB)) (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); if (mp->mod_error != 0) { (void) pthread_mutex_unlock(&mp->mod_lock); return (fmd_set_errno(EFMD_HDL_ABORT)); } mp->mod_flags |= FMD_MOD_STSUB; (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); /* * Create a stats pseudo-event and dispatch it to the module, forcing * it to next execute its custom snapshot routine (or the empty one). */ e = fmd_event_create(FMD_EVT_STATS, FMD_HRT_NOW, NULL, NULL); fmd_eventq_insert_at_head(mp->mod_queue, e); /* * Grab the module lock and then wait on mod_cv for STPUB to be set, * indicating the snapshot routine is completed and the module is idle. */ (void) pthread_mutex_lock(&mp->mod_lock); while (mp->mod_error == 0 && !(mp->mod_flags & FMD_MOD_STPUB)) { struct timespec tms; (void) pthread_cond_wait(&mp->mod_cv, &mp->mod_lock); (void) pthread_mutex_unlock(&mp->mod_lock); tms.tv_sec = 0; tms.tv_nsec = 10000000; (void) nanosleep(&tms, NULL); (void) pthread_mutex_lock(&mp->mod_lock); } if (mp->mod_error != 0) { (void) pthread_mutex_unlock(&mp->mod_lock); return (fmd_set_errno(EFMD_HDL_ABORT)); } (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); /* * Update ms_snaptime and take the actual snapshot of the various * statistics while the module is quiescent and waiting for us. */ (void) pthread_mutex_lock(&mp->mod_stats_lock); if (mp->mod_stats != NULL) { mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime(); err = fmd_ustat_snapshot(mp->mod_ustat, uss); } else err = fmd_set_errno(EFMD_HDL_ABORT); (void) pthread_mutex_unlock(&mp->mod_stats_lock); /* * With the snapshot complete, grab the module lock and clear both * STSUB and STPUB, permitting everyone to wake up and continue. */ (void) pthread_mutex_lock(&mp->mod_lock); ASSERT(mp->mod_flags & FMD_MOD_STSUB); ASSERT(mp->mod_flags & FMD_MOD_STPUB); mp->mod_flags &= ~(FMD_MOD_STSUB | FMD_MOD_STPUB); (void) pthread_cond_broadcast(&mp->mod_cv); (void) pthread_mutex_unlock(&mp->mod_lock); return (err); } struct topo_hdl * fmd_module_topo_hold(fmd_module_t *mp) { fmd_modtopo_t *mtp; ASSERT(fmd_module_locked(mp)); mtp = fmd_zalloc(sizeof (fmd_modtopo_t), FMD_SLEEP); mtp->mt_topo = mp->mod_topo_current; fmd_topo_addref(mtp->mt_topo); fmd_list_prepend(&mp->mod_topolist, mtp); return (mtp->mt_topo->ft_hdl); } int fmd_module_topo_rele(fmd_module_t *mp, struct topo_hdl *hdl) { fmd_modtopo_t *mtp; ASSERT(fmd_module_locked(mp)); for (mtp = fmd_list_next(&mp->mod_topolist); mtp != NULL; mtp = fmd_list_next(mtp)) { if (mtp->mt_topo->ft_hdl == hdl) break; } if (mtp == NULL) return (-1); fmd_list_delete(&mp->mod_topolist, mtp); fmd_topo_rele(mtp->mt_topo); fmd_free(mtp, sizeof (fmd_modtopo_t)); return (0); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_MODULE_H #define _FMD_MODULE_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include struct fmd_module; /* see below */ struct fmd_thread; /* see */ struct fmd_idspace; /* see */ struct fmd_ustat; /* see */ struct fmd_ustat_snap; /* see */ typedef struct fmd_modops { int (*mop_init)(struct fmd_module *); int (*mop_fini)(struct fmd_module *); void (*mop_dispatch)(struct fmd_module *, struct fmd_event *); int (*mop_transport)(struct fmd_module *, fmd_xprt_t *, struct fmd_event *); } fmd_modops_t; typedef struct fmd_modhash { pthread_rwlock_t mh_lock; /* r/w lock to protect hash */ struct fmd_module **mh_hash; /* hash bucket array */ uint_t mh_hashlen; /* size of hash bucket array */ uint_t mh_nelems; /* number of modules in hash */ } fmd_modhash_t; /* * Statistics maintained by fmd itself on behalf of all modules for fmstat(8). * NOTE: FMD_TYPE_STRING statistics should not be used here. If they are * required in the future, the FMD_ADM_MODDSTAT service routine must change. */ typedef struct fmd_modstat { fmd_eventqstat_t ms_evqstat; /* stats for main module event queue */ fmd_stat_t ms_loadtime; /* hrtime at which module was loaded */ fmd_stat_t ms_snaptime; /* hrtime of recent stats snapshot */ fmd_stat_t ms_accepted; /* total events accepted by module */ fmd_stat_t ms_debugdrop; /* dropped debug messages */ fmd_stat_t ms_memtotal; /* total space allocated by module */ fmd_stat_t ms_memlimit; /* limit on space allocated by module */ fmd_stat_t ms_buftotal; /* total space consumed by buffers */ fmd_stat_t ms_buflimit; /* limit on space consumed by buffers */ fmd_stat_t ms_thrtotal; /* total number of auxiliary threads */ fmd_stat_t ms_thrlimit; /* limit on auxiliary threads */ fmd_stat_t ms_doorthrtotal; /* total number of doorserver threads */ fmd_stat_t ms_doorthrlimit; /* limit on doorserver threads */ fmd_stat_t ms_caseopen; /* cases currently open */ fmd_stat_t ms_casesolved; /* total cases solved by module */ fmd_stat_t ms_caseclosed; /* total cases closed by module */ fmd_stat_t ms_ckpt_save; /* save checkpoints for module */ fmd_stat_t ms_ckpt_restore; /* restore checkpoints for module */ fmd_stat_t ms_ckpt_zeroed; /* checkpoint was zeroed at startup */ fmd_stat_t ms_ckpt_cnt; /* number of checkpoints taken */ fmd_stat_t ms_ckpt_time; /* total checkpoint time */ fmd_stat_t ms_xprtopen; /* total number of open transports */ fmd_stat_t ms_xprtlimit; /* limit on number of open transports */ fmd_stat_t ms_xprtqlimit; /* limit on transport eventq length */ } fmd_modstat_t; typedef struct fmd_module { fmd_list_t mod_list; /* linked list next/prev pointers */ pthread_mutex_t mod_lock; /* lock for mod_cv/owner/flags/refs */ pthread_cond_t mod_cv; /* condition variable for waiters */ pthread_t mod_owner; /* tid of thread that set MOD_LOCK */ uint_t mod_refs; /* module reference count */ uint_t mod_flags; /* miscellaneous flags (see below) */ uint64_t mod_gen; /* module checkpoint generation */ int mod_error; /* error return from module thread */ jmp_buf mod_jmpbuf; /* setjmp data for fmd_module_enter() */ fmd_modhash_t *mod_hash; /* containing namespace (ro) */ struct fmd_module *mod_next; /* next module in fmd_modhash chain */ char *mod_name; /* basename of module (ro) */ char *mod_path; /* full pathname of module file (ro) */ char *mod_ckpt; /* pathname of checkpoint dir (ro) */ nvlist_t *mod_fmri; /* fmri for this module */ const fmd_modops_t *mod_ops; /* module class ops vector (ro) */ void *mod_data; /* data private to module ops vector */ fmd_hdl_info_t *mod_info; /* module info registered with handle */ void *mod_spec; /* fmd_hdl_get/setspecific data value */ int mod_argc; /* size of mod_argv formals array */ fmd_conf_formal_t *mod_argv; /* array of conf file formals */ fmd_conf_t *mod_conf; /* configuration properties (ro) */ struct fm_dc_handle **mod_dictv; /* libdiagcode dictionaries */ int mod_dictc; /* size of mod_dictv array */ size_t mod_codelen; /* libdiagcode maximum string length */ struct fmd_eventq *mod_queue; /* eventq associated with module (ro) */ struct fmd_ustat *mod_ustat; /* collection of custom statistics */ pthread_mutex_t mod_stats_lock; /* lock protecting mod_stats data */ fmd_modstat_t *mod_stats; /* fmd built-in per-module statistics */ struct fmd_thread *mod_thread; /* thread associated with module (ro) */ struct fmd_idspace *mod_threads; /* idspace for alternate thread ids */ struct fmd_idspace *mod_timerids; /* idspace for timer identifiers */ fmd_list_t mod_cases; /* list of cases owned by this module */ fmd_buf_hash_t mod_bufs; /* hash of bufs owned by this module */ fmd_serd_hash_t mod_serds; /* hash of serd engs owned by module */ fmd_list_t mod_transports; /* list of transports owned by module */ fmd_list_t mod_topolist; /* list of held topo handles */ fmd_topo_t *mod_topo_current; /* current libtopo snapshot */ char *mod_vers; /* a copy of module version string */ nv_alloc_t mod_nva_sleep; /* module nvalloc routines (sleep) */ nv_alloc_t mod_nva_nosleep; /* module nvalloc routines (nosleep) */ } fmd_module_t; #define FMD_MOD_INIT 0x001 /* mod_ops->mop_init() has completed */ #define FMD_MOD_FINI 0x002 /* mod_ops->mop_fini() has completed */ #define FMD_MOD_QUIT 0x004 /* module has been requested to quit */ #define FMD_MOD_FAIL 0x008 /* unrecoverable error has occurred */ #define FMD_MOD_LOCK 0x010 /* lock bit for fmd_module_lock() */ #define FMD_MOD_BUSY 0x020 /* module is busy executing a call */ #define FMD_MOD_MDIRTY 0x040 /* module meta state needs checkpoint */ #define FMD_MOD_CDIRTY 0x080 /* module case state needs checkpoint */ #define FMD_MOD_STSUB 0x100 /* stats subscriber is waiting */ #define FMD_MOD_STPUB 0x200 /* stats publisher is waiting */ typedef struct fmd_modtimer { fmd_module_t *mt_mod; /* module that installed this timer */ void *mt_arg; /* module private timer argument */ id_t mt_id; /* timer ID (or -1 if still pending) */ } fmd_modtimer_t; typedef struct fmd_modtopo { fmd_list_t mt_link; /* link on module topo list */ fmd_topo_t *mt_topo; /* topo handle */ } fmd_modtopo_t; extern const fmd_modops_t fmd_bltin_ops; /* see fmd/common/fmd_builtin.c */ extern const fmd_modops_t fmd_rtld_ops; /* see fmd/common/fmd_rtld.c */ extern const fmd_modops_t fmd_proc_ops; /* see fmd/common/fmd_proc.c */ extern fmd_module_t *fmd_module_create(const char *, const fmd_modops_t *); extern void fmd_module_unload(fmd_module_t *); extern void fmd_module_destroy(fmd_module_t *); extern void fmd_module_dispatch(fmd_module_t *, fmd_event_t *); extern int fmd_module_transport(fmd_module_t *, fmd_xprt_t *, fmd_event_t *); extern void fmd_module_timeout(fmd_modtimer_t *, id_t, hrtime_t); extern void fmd_module_gc(fmd_module_t *); extern void fmd_module_trygc(fmd_module_t *); extern int fmd_module_contains(fmd_module_t *, fmd_event_t *); extern void fmd_module_setdirty(fmd_module_t *); extern void fmd_module_setcdirty(fmd_module_t *); extern void fmd_module_clrdirty(fmd_module_t *); extern void fmd_module_commit(fmd_module_t *); extern void fmd_module_lock(fmd_module_t *); extern void fmd_module_unlock(fmd_module_t *); extern int fmd_module_trylock(fmd_module_t *); extern int fmd_module_locked(fmd_module_t *); extern void fmd_module_unregister(fmd_module_t *); extern int fmd_module_enter(fmd_module_t *, void (*)(fmd_hdl_t *)); extern void fmd_module_exit(fmd_module_t *); extern void fmd_module_abort(fmd_module_t *, int) __NORETURN; extern void fmd_module_hold(fmd_module_t *); extern void fmd_module_rele(fmd_module_t *); extern int fmd_module_dc_opendict(fmd_module_t *, const char *); extern int fmd_module_dc_key2code(fmd_module_t *, char *const [], char *, size_t); extern fmd_modhash_t *fmd_modhash_create(void); extern void fmd_modhash_destroy(fmd_modhash_t *); extern fmd_module_t *fmd_modhash_load(fmd_modhash_t *, const char *, const fmd_modops_t *); extern void fmd_modhash_loadall(fmd_modhash_t *, const fmd_conf_path_t *, const fmd_modops_t *, const char *); extern fmd_module_t *fmd_modhash_lookup(fmd_modhash_t *, const char *); extern int fmd_modhash_unload(fmd_modhash_t *, const char *); extern void fmd_modhash_apply(fmd_modhash_t *, void (*)(fmd_module_t *)); extern void fmd_modhash_tryapply(fmd_modhash_t *, void (*)(fmd_module_t *)); extern void fmd_modhash_dispatch(fmd_modhash_t *, fmd_event_t *); extern void fmd_modstat_publish(fmd_module_t *); extern int fmd_modstat_snapshot(fmd_module_t *, struct fmd_ustat_snap *); extern struct topo_hdl *fmd_module_topo_hold(fmd_module_t *); extern int fmd_module_topo_rele(fmd_module_t *, struct topo_hdl *); extern nv_alloc_ops_t fmd_module_nva_ops_sleep; extern nv_alloc_ops_t fmd_module_nva_ops_nosleep; #ifdef __cplusplus } #endif #endif /* _FMD_MODULE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include /*ARGSUSED*/ static void * fmd_nv_alloc(nv_alloc_t *nva, size_t size) { return (fmd_alloc(size, FMD_SLEEP)); } /*ARGSUSED*/ static void fmd_nv_free(nv_alloc_t *nva, void *buf, size_t size) { fmd_free(buf, size); } const nv_alloc_ops_t fmd_nv_alloc_ops = { NULL, /* nv_ao_init() */ NULL, /* nv_ao_fini() */ fmd_nv_alloc, /* nv_ao_alloc() */ fmd_nv_free, /* nv_ao_free() */ NULL /* nv_ao_reset() */ }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * External Module Support * * The Fault Manager is designed to support external modules running as other * processes but using the same set of APIs. These support routines are a * placeholder for when that feature set is added to fmd in the near future. */ #include #include #include /*ARGSUSED*/ static int proc_init(fmd_module_t *mp) { return (fmd_set_errno(ENOTSUP)); } /*ARGSUSED*/ static int proc_fini(fmd_module_t *mp) { return (fmd_set_errno(ENOTSUP)); } const fmd_modops_t fmd_proc_ops = { proc_init, proc_fini, fmd_module_dispatch, fmd_module_transport, }; /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Create an FMRI authority element for the environment in which this instance * of fmd is deployed. This function is called once and the result is cached. */ nvlist_t * fmd_protocol_authority(void) { const char *str; nvlist_t *nvl; int err = 0; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc authority nvlist"); err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION); if ((str = fmd_conf_getnzstr(fmd.d_conf, "product")) == NULL) str = fmd_conf_getnzstr(fmd.d_conf, "platform"); if (str != NULL) err |= nvlist_add_string(nvl, FM_FMRI_AUTH_PRODUCT, str); if ((str = fmd_conf_getnzstr(fmd.d_conf, "product_sn")) != NULL) err |= nvlist_add_string(nvl, FM_FMRI_AUTH_PRODUCT_SN, str); if ((str = fmd_conf_getnzstr(fmd.d_conf, "chassis")) != NULL) err |= nvlist_add_string(nvl, FM_FMRI_AUTH_CHASSIS, str); if ((str = fmd_conf_getnzstr(fmd.d_conf, "domain")) != NULL) err |= nvlist_add_string(nvl, FM_FMRI_AUTH_DOMAIN, str); if ((str = fmd_conf_getnzstr(fmd.d_conf, "server")) != NULL) err |= nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, str); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } /* * Create an FMRI for the specified module. We use the cached authority * nvlist saved in fmd.d_auth to fill in the authority member. */ nvlist_t * fmd_protocol_fmri_module(fmd_module_t *mp) { nvlist_t *nvl; int err = 0; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc diag-engine fmri nvlist"); err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FMD_SCHEME_VERSION); err |= nvlist_add_string(nvl, FM_FMRI_SCHEME, FM_FMRI_SCHEME_FMD); err |= nvlist_add_nvlist(nvl, FM_FMRI_AUTHORITY, fmd.d_auth); err |= nvlist_add_string(nvl, FM_FMRI_FMD_NAME, mp->mod_name); if (mp->mod_info != NULL) { err |= nvlist_add_string(nvl, FM_FMRI_FMD_VERSION, mp->mod_info->fmdi_vers); } else if (mp == fmd.d_rmod) { err |= nvlist_add_string(nvl, FM_FMRI_FMD_VERSION, fmd.d_version); } if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_fault(const char *class, uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *resource, const char *location) { nvlist_t *nvl; int err = 0; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc fault nvlist"); err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FAULT_VERSION); err |= nvlist_add_string(nvl, FM_CLASS, class); err |= nvlist_add_uint8(nvl, FM_FAULT_CERTAINTY, certainty); if (asru != NULL) err |= nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru); if (fru != NULL) err |= nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru); if (resource != NULL) err |= nvlist_add_nvlist(nvl, FM_FAULT_RESOURCE, resource); if (location != NULL) err |= nvlist_add_string(nvl, FM_FAULT_LOCATION, location); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_list(const char *class, nvlist_t *de_fmri, const char *uuid, const char *code, uint_t argc, nvlist_t **argv, uint8_t *flagv, int domsg, struct timeval *tvp, int injected) { int64_t tod[2]; nvlist_t *nvl; int err = 0; fmd_msg_hdl_t *msghdl; char *severity; tod[0] = tvp->tv_sec; tod[1] = tvp->tv_usec; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc suspect list nvlist"); err |= nvlist_add_uint8(nvl, FM_VERSION, FM_SUSPECT_VERSION); err |= nvlist_add_string(nvl, FM_CLASS, class); err |= nvlist_add_string(nvl, FM_SUSPECT_UUID, uuid); err |= nvlist_add_string(nvl, FM_SUSPECT_DIAG_CODE, code); err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2); err |= nvlist_add_nvlist(nvl, FM_SUSPECT_DE, de_fmri); err |= nvlist_add_uint32(nvl, FM_SUSPECT_FAULT_SZ, argc); if (injected) err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_INJECTED, B_TRUE); if (!domsg) { err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_MESSAGE, B_FALSE); } if (argc != 0) { err |= nvlist_add_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, argv, argc); err |= nvlist_add_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS, flagv, argc); } /* * Attempt to lookup the severity associated with this diagnosis from * the portable object file using the diag code. Failure to init * libfmd_msg or add to the nvlist will be treated as fatal. However, * we won't treat a fmd_msg_getitem_id failure as fatal since during * development it's not uncommon to be working with po/dict files that * haven't yet been updated with newly added diagnoses. */ msghdl = fmd_msg_init(fmd.d_rootdir, FMD_MSG_VERSION); if (msghdl == NULL) fmd_panic("failed to initialize libfmd_msg\n"); if ((severity = fmd_msg_getitem_id(msghdl, NULL, code, FMD_MSG_ITEM_SEVERITY)) != NULL) { err |= nvlist_add_string(nvl, FM_SUSPECT_SEVERITY, severity); free(severity); } fmd_msg_fini(msghdl); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_rsrc_asru(const char *class, nvlist_t *fmri, const char *uuid, const char *code, boolean_t faulty, boolean_t unusable, boolean_t message, nvlist_t *event, struct timeval *tvp, boolean_t repaired, boolean_t replaced, boolean_t acquitted, boolean_t resolved, nvlist_t *diag_de, boolean_t injected) { nvlist_t *nvl; int64_t tod[2]; int err = 0; tod[0] = tvp->tv_sec; tod[1] = tvp->tv_usec; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc resource nvlist"); err |= nvlist_add_uint8(nvl, FM_VERSION, FM_RSRC_VERSION); err |= nvlist_add_string(nvl, FM_CLASS, class); if (fmri != NULL) err |= nvlist_add_nvlist(nvl, FM_RSRC_RESOURCE, fmri); if (uuid != NULL) err |= nvlist_add_string(nvl, FM_RSRC_ASRU_UUID, uuid); if (code != NULL) err |= nvlist_add_string(nvl, FM_RSRC_ASRU_CODE, code); err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_FAULTY, faulty); err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_REPAIRED, repaired); err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_REPLACED, replaced); err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_ACQUITTED, acquitted); err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_RESOLVED, resolved); err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_UNUSABLE, unusable); err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_MESSAGE, message); err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2); if (diag_de != NULL) err |= nvlist_add_nvlist(nvl, FM_SUSPECT_DE, diag_de); if (injected) err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_INJECTED, B_TRUE); if (event != NULL) err |= nvlist_add_nvlist(nvl, FM_RSRC_ASRU_EVENT, event); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_fmderror(int errnum, const char *format, va_list ap) { uint64_t ena = fmd_ena(); nvlist_t *nvl; int err = 0; char c, *msg; size_t len; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) return (NULL); len = vsnprintf(&c, 1, format, ap); msg = alloca(len + 1); (void) vsnprintf(msg, len + 1, format, ap); if (msg[len] == '\n') msg[len] = '\0'; err |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION); err |= nvlist_add_string(nvl, FM_CLASS, fmd_errclass(errnum)); err |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena); err |= nvlist_add_string(nvl, FMD_ERR_MOD_MSG, msg); if (err != 0) { nvlist_free(nvl); return (NULL); } return (nvl); } nvlist_t * fmd_protocol_moderror(fmd_module_t *mp, int oserr, const char *msg) { uint64_t ena = fmd_ena(); nvlist_t *nvl, *fmri; int err = 0; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc module error nvlist"); if (mp->mod_fmri == NULL) fmri = fmd_protocol_fmri_module(mp); else fmri = mp->mod_fmri; err |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION); err |= nvlist_add_string(nvl, FM_CLASS, fmd_errclass(EFMD_MODULE)); err |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, fmri); err |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena); err |= nvlist_add_string(nvl, FMD_ERR_MOD_MSG, msg); if (mp->mod_fmri == NULL) nvlist_free(fmri); if (oserr != 0) { err |= nvlist_add_int32(nvl, FMD_ERR_MOD_ERRNO, oserr); err |= nvlist_add_string(nvl, FMD_ERR_MOD_ERRCLASS, fmd_errclass(oserr)); } if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_xprt_ctl(fmd_module_t *mp, const char *class, uint8_t version) { nvlist_t *nvl; int err = 0; if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0) fmd_panic("failed to xalloc rsrc xprt nvlist"); err |= nvlist_add_uint8(nvl, FM_VERSION, version); err |= nvlist_add_string(nvl, FM_CLASS, class); err |= nvlist_add_nvlist(nvl, FM_RSRC_RESOURCE, mp->mod_fmri); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_xprt_sub(fmd_module_t *mp, const char *class, uint8_t version, const char *subclass) { nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version); int err = nvlist_add_string(nvl, FM_RSRC_XPRT_SUBCLASS, subclass); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_xprt_uuclose(fmd_module_t *mp, const char *class, uint8_t version, const char *uuid) { nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version); int err = nvlist_add_string(nvl, FM_RSRC_XPRT_UUID, uuid); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_xprt_uuresolved(fmd_module_t *mp, const char *class, uint8_t version, const char *uuid) { nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version); int err = nvlist_add_string(nvl, FM_RSRC_XPRT_UUID, uuid); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } nvlist_t * fmd_protocol_xprt_updated(fmd_module_t *mp, const char *class, uint8_t version, const char *uuid, uint8_t *statusp, uint8_t *has_asrup, uint_t nelem) { nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version); int err = nvlist_add_string(nvl, FM_RSRC_XPRT_UUID, uuid); err |= nvlist_add_uint8_array(nvl, FM_RSRC_XPRT_FAULT_STATUS, statusp, nelem); if (has_asrup) err |= nvlist_add_uint8_array(nvl, FM_RSRC_XPRT_FAULT_HAS_ASRU, has_asrup, nelem); if (err != 0) fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err)); return (nvl); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_PROTOCOL_H #define _FMD_PROTOCOL_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define FMD_RSRC_CLASS FM_RSRC_CLASS ".fm." #define FMD_CTL_CLASS FMD_RSRC_CLASS "fmd." #define SYSEVENT_RSRC_CLASS FM_RSRC_CLASS ".sysevent." #define FMD_RSRC_CLASS_LEN (sizeof (FMD_RSRC_CLASS) - 1) #define FMD_CTL_CLASS_LEN (sizeof (FMD_CTL_CLASS) - 1) #define SYSEVENT_RSRC_CLASS_LEN (sizeof (SYSEVENT_RSRC_CLASS) - 1) #define FMD_CTL_ADDHRT FMD_CTL_CLASS "clock.addhrtime" #define FMD_CTL_ADDHRT_VERS1 1 #define FMD_CTL_ADDHRT_DELTA "delta" /* * The FMD_FLT_* events still use defect.sunos.* for now: a future registry * putback should define all fmd events and convert these to defect.fm.fmd.* */ #define FMD_FLT_NOSUB "defect.sunos.fmd.nosub" #define FMD_FLT_NODC "defect.sunos.fmd.nodiagcode" #define FMD_FLT_MOD "defect.sunos.fmd.module" #define FMD_FLT_CONF "defect.sunos.fmd.config" #define FMD_ERR_CLASS "ereport.fm.fmd." #define FMD_ERR_CLASS_LEN (sizeof (FMD_ERR_CLASS) - 1) #define FMD_ERR_MOD_MSG "msg" #define FMD_ERR_MOD_ERRNO "errno" #define FMD_ERR_MOD_ERRCLASS "errclass" struct fmd_module; /* see */ extern nvlist_t *fmd_protocol_authority(void); extern nvlist_t *fmd_protocol_fmri_module(struct fmd_module *); extern nvlist_t *fmd_protocol_fault(const char *, uint8_t, nvlist_t *, nvlist_t *, nvlist_t *, const char *); extern nvlist_t *fmd_protocol_list(const char *, nvlist_t *, const char *, const char *, uint_t, nvlist_t **, uint8_t *, int, struct timeval *, int); extern nvlist_t *fmd_protocol_rsrc_asru(const char *, nvlist_t *, const char *, const char *, boolean_t, boolean_t, boolean_t, nvlist_t *, struct timeval *m, boolean_t, boolean_t, boolean_t, boolean_t, nvlist_t *, boolean_t); extern nvlist_t *fmd_protocol_fmderror(int, const char *, va_list); extern nvlist_t *fmd_protocol_moderror(struct fmd_module *, int, const char *); extern nvlist_t *fmd_protocol_xprt_ctl(struct fmd_module *, const char *, uint8_t); extern nvlist_t *fmd_protocol_xprt_sub(struct fmd_module *, const char *, uint8_t, const char *); extern nvlist_t *fmd_protocol_xprt_uuclose(struct fmd_module *, const char *, uint8_t, const char *); extern nvlist_t *fmd_protocol_xprt_uuresolved(struct fmd_module *, const char *, uint8_t, const char *); extern nvlist_t *fmd_protocol_xprt_updated(struct fmd_module *, const char *, uint8_t, const char *, uint8_t *, uint8_t *, uint_t); #ifdef __cplusplus } #endif #endif /* _FMD_PROTOCOL_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Define range of transient RPC program numbers to use for transient bindings. * These are defined in the Solaris ONC+ Developer's Guide, Appendix B, but * are cleverly not defined in any ONC+ standard system header file. */ #define RPC_TRANS_MIN 0x40000000 #define RPC_TRANS_MAX 0x5fffffff /* * We use our own private version of svc_create() which registers our services * only on loopback transports and enables an option whereby Solaris ucreds * are associated with each connection, permitting us to check privilege bits. */ static int fmd_rpc_svc_create_local(void (*disp)(struct svc_req *, SVCXPRT *), rpcprog_t prog, rpcvers_t vers, uint_t ssz, uint_t rsz, int force) { struct netconfig *ncp; struct netbuf buf; SVCXPRT *xprt; void *hdl; int fd, n = 0; char door[PATH_MAX]; time_t tm; if ((hdl = setnetconfig()) == NULL) { fmd_error(EFMD_RPC_REG, "failed to iterate over " "netconfig database: %s\n", nc_sperror()); return (fmd_set_errno(EFMD_RPC_REG)); } if (force) svc_unreg(prog, vers); /* clear stale rpcbind registrations */ buf.buf = alloca(_SS_MAXSIZE); buf.maxlen = _SS_MAXSIZE; buf.len = 0; while ((ncp = getnetconfig(hdl)) != NULL) { if (strcmp(ncp->nc_protofmly, NC_LOOPBACK) != 0) continue; if (!force && rpcb_getaddr(prog, vers, ncp, &buf, HOST_SELF)) { (void) endnetconfig(hdl); return (fmd_set_errno(EFMD_RPC_BOUND)); } if ((fd = t_open(ncp->nc_device, O_RDWR, NULL)) == -1) { fmd_error(EFMD_RPC_REG, "failed to open %s: %s\n", ncp->nc_device, t_strerror(t_errno)); continue; } svc_fd_negotiate_ucred(fd); /* enable ucred option on xprt */ if ((xprt = svc_tli_create(fd, ncp, NULL, ssz, rsz)) == NULL) { (void) t_close(fd); continue; } if (svc_reg(xprt, prog, vers, disp, ncp) == FALSE) { fmd_error(EFMD_RPC_REG, "failed to register " "rpc service on %s\n", ncp->nc_netid); svc_destroy(xprt); continue; } n++; } (void) endnetconfig(hdl); /* * If we failed to register services (n == 0) because rpcbind is down, * then check to see if the RPC door file exists before attempting an * svc_door_create(), which cleverly destroys any existing door file. * The RPC APIs have no stable errnos, so we use rpcb_gettime() as a * hack to determine if rpcbind itself is down. */ if (!force && n == 0 && rpcb_gettime(HOST_SELF, &tm) == FALSE && snprintf(door, sizeof (door), RPC_DOOR_RENDEZVOUS, prog, vers) > 0 && access(door, F_OK) == 0) return (fmd_set_errno(EFMD_RPC_BOUND)); /* * Attempt to create a door server for the RPC program as well. Limit * the maximum request size for the door transport to the receive size. */ if ((xprt = svc_door_create(disp, prog, vers, ssz)) == NULL) { fmd_error(EFMD_RPC_REG, "failed to create door for " "rpc service 0x%lx/0x%lx\n", prog, vers); } else { (void) svc_control(xprt, SVCSET_CONNMAXREC, &rsz); n++; } return (n); } static int fmd_rpc_svc_init(void (*disp)(struct svc_req *, SVCXPRT *), const char *name, const char *path, const char *prop, rpcprog_t pmin, rpcprog_t pmax, rpcvers_t vers, uint_t sndsize, uint_t rcvsize, int force) { rpcprog_t prog; char buf[16]; FILE *fp; for (prog = pmin; prog <= pmax; prog++) { if (fmd_rpc_svc_create_local(disp, prog, vers, sndsize, rcvsize, force) > 0) { fmd_dprintf(FMD_DBG_RPC, "registered %s rpc service " "as 0x%lx.%lx\n", name, prog, vers); /* * To aid simulator scripts, save our RPC "digits" in * the specified file for rendezvous with libfmd_adm. */ if (path != NULL && (fp = fopen(path, "w")) != NULL) { (void) fprintf(fp, "%ld\n", prog); (void) fclose(fp); } (void) snprintf(buf, sizeof (buf), "%ld", prog); (void) fmd_conf_setprop(fmd.d_conf, prop, buf); return (0); } } return (-1); /* errno is set for us */ } void fmd_rpc_init(void) { int err, prog, mode = RPC_SVC_MT_USER; uint64_t sndsize = 0, rcvsize = 0; const char *s; if (rpc_control(RPC_SVC_MTMODE_SET, &mode) == FALSE) fmd_panic("failed to enable user-MT rpc mode"); (void) fmd_conf_getprop(fmd.d_conf, "rpc.sndsize", &sndsize); (void) fmd_conf_getprop(fmd.d_conf, "rpc.rcvsize", &rcvsize); /* * Infer whether we are the "default" fault manager or an alternate one * based on whether the initial setting of rpc.adm.prog is non-zero. */ (void) fmd_conf_getprop(fmd.d_conf, "rpc.adm.prog", &prog); (void) fmd_conf_getprop(fmd.d_conf, "rpc.adm.path", &s); if (prog != 0) { err = fmd_rpc_svc_init(fmd_adm_1, "FMD_ADM", s, "rpc.adm.prog", FMD_ADM, FMD_ADM, FMD_ADM_VERSION_1, (uint_t)sndsize, (uint_t)rcvsize, TRUE); } else { err = fmd_rpc_svc_init(fmd_adm_1, "FMD_ADM", s, "rpc.adm.prog", RPC_TRANS_MIN, RPC_TRANS_MAX, FMD_ADM_VERSION_1, (uint_t)sndsize, (uint_t)rcvsize, FALSE); } if (err != 0) fmd_error(EFMD_EXIT, "failed to create rpc server bindings"); if (fmd_thread_create(fmd.d_rmod, (fmd_thread_f *)svc_run, 0) == NULL) fmd_error(EFMD_EXIT, "failed to create rpc server thread"); } void fmd_rpc_fini(void) { rpcprog_t prog; svc_exit(); /* force svc_run() threads to exit */ (void) fmd_conf_getprop(fmd.d_conf, "rpc.adm.prog", &prog); svc_unreg(prog, FMD_ADM_VERSION_1); (void) fmd_conf_getprop(fmd.d_conf, "rpc.api.prog", &prog); svc_unreg(prog, FMD_API_VERSION_1); } /* * Utillity function to fetch the XPRT's ucred and determine if we should deny * the request. For now, we implement a simple policy of rejecting any caller * who does not have the PRIV_SYS_ADMIN bit in their Effective privilege set, * unless the caller is loading a module, which requires all privileges. */ int fmd_rpc_deny(struct svc_req *rqp) { ucred_t *ucp = alloca(ucred_size()); const priv_set_t *psp; if (!fmd.d_booted) { (void) pthread_mutex_lock(&fmd.d_fmd_lock); while (!fmd.d_booted) (void) pthread_cond_wait(&fmd.d_fmd_cv, &fmd.d_fmd_lock); (void) pthread_mutex_unlock(&fmd.d_fmd_lock); } if (svc_getcallerucred(rqp->rq_xprt, &ucp) != 0 || (psp = ucred_getprivset(ucp, PRIV_EFFECTIVE)) == NULL) return (1); /* deny access if we can't get credentials */ #ifndef DEBUG /* * For convenience of testing, we only require all privileges for a * module load when running a non-DEBUG fault management daemon. */ if (rqp->rq_proc == FMD_ADM_MODLOAD) return (!priv_isfullset(psp)); #endif return (!priv_ismember(psp, PRIV_SYS_ADMIN)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_RPC_H #define _FMD_RPC_H #ifdef __cplusplus extern "C" { #endif struct svc_req; extern void fmd_rpc_init(void); extern void fmd_rpc_fini(void); extern int fmd_rpc_deny(struct svc_req *); #ifdef __cplusplus } #endif #endif /* _FMD_RPC_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool_t fmd_adm_modinfo_1_svc(struct fmd_rpc_modlist *rvp, struct svc_req *req) { struct fmd_rpc_modinfo *rmi; fmd_module_t *mp; rvp->rml_list = NULL; rvp->rml_err = 0; rvp->rml_len = 0; if (fmd_rpc_deny(req)) { rvp->rml_err = FMD_ADM_ERR_PERM; return (TRUE); } (void) pthread_mutex_lock(&fmd.d_mod_lock); for (mp = fmd_list_next(&fmd.d_mod_list); mp != NULL; mp = fmd_list_next(mp)) { if ((rmi = malloc(sizeof (struct fmd_rpc_modinfo))) == NULL) { rvp->rml_err = FMD_ADM_ERR_NOMEM; break; } fmd_module_lock(mp); /* * If mod_info is NULL, the module is in the middle of loading: * do not report its presence to observability tools yet. */ if (mp->mod_info == NULL) { fmd_module_unlock(mp); free(rmi); continue; } rmi->rmi_name = strdup(mp->mod_name); rmi->rmi_desc = strdup(mp->mod_info->fmdi_desc); rmi->rmi_vers = strdup(mp->mod_info->fmdi_vers); rmi->rmi_faulty = mp->mod_error != 0; rmi->rmi_next = rvp->rml_list; fmd_module_unlock(mp); rvp->rml_list = rmi; rvp->rml_len++; if (rmi->rmi_desc == NULL || rmi->rmi_vers == NULL) { rvp->rml_err = FMD_ADM_ERR_NOMEM; break; } } (void) pthread_mutex_unlock(&fmd.d_mod_lock); return (TRUE); } bool_t fmd_adm_modcstat_1_svc(char *name, struct fmd_rpc_modstat *rms, struct svc_req *req) { fmd_ustat_snap_t snap; fmd_module_t *mp; rms->rms_buf.rms_buf_val = NULL; rms->rms_buf.rms_buf_len = 0; rms->rms_err = 0; if (fmd_rpc_deny(req)) { rms->rms_err = FMD_ADM_ERR_PERM; return (TRUE); } if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) { rms->rms_err = FMD_ADM_ERR_MODSRCH; return (TRUE); } if (fmd_modstat_snapshot(mp, &snap) == 0) { rms->rms_buf.rms_buf_val = snap.uss_buf; rms->rms_buf.rms_buf_len = snap.uss_len; } else if (errno == EFMD_HDL_ABORT) { rms->rms_err = FMD_ADM_ERR_MODFAIL; } else rms->rms_err = FMD_ADM_ERR_NOMEM; fmd_module_rele(mp); return (TRUE); } bool_t fmd_adm_moddstat_1_svc(char *name, struct fmd_rpc_modstat *rms, struct svc_req *req) { fmd_module_t *mp; rms->rms_buf.rms_buf_val = NULL; rms->rms_buf.rms_buf_len = 0; rms->rms_err = 0; if (fmd_rpc_deny(req)) { rms->rms_err = FMD_ADM_ERR_PERM; return (TRUE); } if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) { rms->rms_err = FMD_ADM_ERR_MODSRCH; return (TRUE); } rms->rms_buf.rms_buf_val = malloc(sizeof (fmd_modstat_t)); rms->rms_buf.rms_buf_len = sizeof (fmd_modstat_t) / sizeof (fmd_stat_t); if (rms->rms_buf.rms_buf_val == NULL) { rms->rms_err = FMD_ADM_ERR_NOMEM; rms->rms_buf.rms_buf_len = 0; fmd_module_rele(mp); return (TRUE); } /* * Note: the bcopy() here is valid only if no FMD_TYPE_STRING stats * are present in mp->mod_stats. We don't use any for the daemon- * maintained stats and provide this function in order to reduce the * overhead of the fmstat(8) default view, where these minimal stats * must be retrieved for all of the active modules. */ (void) pthread_mutex_lock(&mp->mod_stats_lock); if (mp->mod_stats != NULL) { mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime(); bcopy(mp->mod_stats, rms->rms_buf.rms_buf_val, sizeof (fmd_modstat_t)); } else { free(rms->rms_buf.rms_buf_val); rms->rms_buf.rms_buf_val = NULL; rms->rms_buf.rms_buf_len = 0; rms->rms_err = FMD_ADM_ERR_MODFAIL; } (void) pthread_mutex_unlock(&mp->mod_stats_lock); fmd_module_rele(mp); return (TRUE); } bool_t fmd_adm_modgstat_1_svc(struct fmd_rpc_modstat *rms, struct svc_req *req) { const size_t size = sizeof (fmd_statistics_t); if (fmd_rpc_deny(req)) { rms->rms_buf.rms_buf_val = NULL; rms->rms_buf.rms_buf_len = 0; rms->rms_err = FMD_ADM_ERR_PERM; } else if ((rms->rms_buf.rms_buf_val = malloc(size)) != NULL) { /* * Note: the bcopy() here is valid only if no FMD_TYPE_STRING * stats are present in fmd.d_stats (see definition in fmd.c). */ (void) pthread_mutex_lock(&fmd.d_stats_lock); bcopy(fmd.d_stats, rms->rms_buf.rms_buf_val, size); (void) pthread_mutex_unlock(&fmd.d_stats_lock); rms->rms_buf.rms_buf_len = size / sizeof (fmd_stat_t); rms->rms_err = 0; } else { rms->rms_buf.rms_buf_len = 0; rms->rms_err = FMD_ADM_ERR_NOMEM; } return (TRUE); } bool_t fmd_adm_modload_1_svc(char *path, int *rvp, struct svc_req *req) { fmd_module_t *mp; const char *p; int err = 0; if (fmd_rpc_deny(req)) { *rvp = FMD_ADM_ERR_PERM; return (TRUE); } /* * Before we endure the expense of constructing a module and attempting * to load it, do a quick check to see if the pathname is valid. */ if (access(path, F_OK) != 0) { *rvp = FMD_ADM_ERR_MODNOENT; return (TRUE); } if ((p = strrchr(path, '.')) != NULL && strcmp(p, ".so") == 0) mp = fmd_modhash_load(fmd.d_mod_hash, path, &fmd_rtld_ops); else mp = fmd_modhash_load(fmd.d_mod_hash, path, &fmd_proc_ops); if (mp == NULL) { switch (errno) { case EFMD_MOD_LOADED: err = FMD_ADM_ERR_MODEXIST; break; case EFMD_MOD_INIT: err = FMD_ADM_ERR_MODINIT; break; default: err = FMD_ADM_ERR_MODLOAD; break; } } *rvp = err; return (TRUE); } bool_t fmd_adm_modunload_1_svc(char *name, int *rvp, struct svc_req *req) { fmd_module_t *mp = NULL; int err = 0; if (fmd_rpc_deny(req)) err = FMD_ADM_ERR_PERM; else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) err = FMD_ADM_ERR_MODSRCH; else if (mp == fmd.d_self) err = FMD_ADM_ERR_MODBUSY; else if (fmd_modhash_unload(fmd.d_mod_hash, name) != 0) err = FMD_ADM_ERR_MODSRCH; if (mp != NULL) fmd_module_rele(mp); *rvp = err; return (TRUE); } bool_t fmd_adm_modreset_1_svc(char *name, int *rvp, struct svc_req *req) { fmd_module_t *mp = NULL; int err = 0; if (fmd_rpc_deny(req)) err = FMD_ADM_ERR_PERM; else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) err = FMD_ADM_ERR_MODSRCH; else if (mp == fmd.d_self) err = FMD_ADM_ERR_MODBUSY; else if (fmd_modhash_unload(fmd.d_mod_hash, name) != 0) err = FMD_ADM_ERR_MODSRCH; if (err == 0) fmd_ckpt_delete(mp); /* erase any saved checkpoints */ if (err == 0 && fmd_modhash_load(fmd.d_mod_hash, mp->mod_path, mp->mod_ops) == NULL) { if (errno == EFMD_MOD_INIT) err = FMD_ADM_ERR_MODINIT; else err = FMD_ADM_ERR_MODLOAD; } if (mp != NULL) fmd_module_rele(mp); *rvp = err; return (TRUE); } bool_t fmd_adm_modgc_1_svc(char *name, int *rvp, struct svc_req *req) { fmd_module_t *mp; int err = 0; if (fmd_rpc_deny(req)) err = FMD_ADM_ERR_PERM; else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) err = FMD_ADM_ERR_MODSRCH; else { fmd_module_gc(mp); fmd_module_rele(mp); } *rvp = err; return (TRUE); } /* * Unlike our other RPC callbacks, fmd_adm_rsrclist_1 can return large amounts * of data that may exceed the underlying RPC transport buffer size if the * resource cache is heavily populated and/or all resources are requested. * To minimize the likelihood of running out of RPC buffer space and having to * fail the client request, fmd_adm_rsrclist_1 returns a snapshot of the * relevant FMRI strings only: the client can use fmd_adm_rsrcinfo_1 on an * individual FMRI if more information is needed. To further reduce the XDR * overhead, the string list is represented as XDR-opaque data where the * entire list is returned as a string table (e.g. "fmriA\0fmriB\0..."). */ static void fmd_adm_rsrclist_asru(fmd_asru_t *ap, void *arg) { struct fmd_rpc_rsrclist *rrl = arg; size_t name_len, buf_len; void *p; /* * Skip the ASRU if this fault is marked as invisible. * If rrl_all is false, we take a quick look at asru_flags with no lock * held to see if the ASRU is not faulty. If so, * we don't want to report it by default and can just skip this ASRU. * This helps keep overhead low in the common case, as the call to * fmd_asru_getstate() can be expensive depending on the scheme. */ if (ap->asru_flags & FMD_ASRU_INVISIBLE) return; if (rrl->rrl_all == B_FALSE && !(ap->asru_flags & FMD_ASRU_FAULTY)) return; if (rrl->rrl_err != 0 || fmd_asru_getstate(ap) == 0) return; /* error has occurred or resource is in 'ok' state */ /* * Lock the ASRU and reallocate rrl_buf[] to be large enough to hold * another string, doubling it as needed. Then copy the new string * on to the end, and increment rrl_len to indicate the used space. */ (void) pthread_mutex_lock(&ap->asru_lock); name_len = strlen(ap->asru_name) + 1; while (rrl->rrl_len + name_len > rrl->rrl_buf.rrl_buf_len) { if (rrl->rrl_buf.rrl_buf_len != 0) buf_len = rrl->rrl_buf.rrl_buf_len * 2; else buf_len = 1024; /* default buffer size */ if ((p = realloc(rrl->rrl_buf.rrl_buf_val, buf_len)) != NULL) { bzero((char *)p + rrl->rrl_buf.rrl_buf_len, buf_len - rrl->rrl_buf.rrl_buf_len); rrl->rrl_buf.rrl_buf_val = p; rrl->rrl_buf.rrl_buf_len = buf_len; } else { rrl->rrl_err = FMD_ADM_ERR_NOMEM; break; } } if (rrl->rrl_err == 0) { bcopy(ap->asru_name, (char *)rrl->rrl_buf.rrl_buf_val + rrl->rrl_len, name_len); rrl->rrl_len += name_len; rrl->rrl_cnt++; } (void) pthread_mutex_unlock(&ap->asru_lock); } bool_t fmd_adm_rsrclist_1_svc(bool_t all, struct fmd_rpc_rsrclist *rvp, struct svc_req *req) { rvp->rrl_buf.rrl_buf_len = 0; rvp->rrl_buf.rrl_buf_val = NULL; rvp->rrl_len = 0; rvp->rrl_cnt = 0; rvp->rrl_err = 0; rvp->rrl_all = all; if (fmd_rpc_deny(req)) rvp->rrl_err = FMD_ADM_ERR_PERM; else fmd_asru_hash_apply(fmd.d_asrus, fmd_adm_rsrclist_asru, rvp); return (TRUE); } bool_t fmd_adm_rsrcinfo_1_svc(char *fmri, struct fmd_rpc_rsrcinfo *rvp, struct svc_req *req) { fmd_asru_t *ap; fmd_case_impl_t *cip; int state; bzero(rvp, sizeof (struct fmd_rpc_rsrcinfo)); if (fmd_rpc_deny(req)) { rvp->rri_err = FMD_ADM_ERR_PERM; return (TRUE); } if ((ap = fmd_asru_hash_lookup_name(fmd.d_asrus, fmri)) == NULL) { rvp->rri_err = FMD_ADM_ERR_RSRCSRCH; return (TRUE); } state = fmd_asru_getstate(ap); (void) pthread_mutex_lock(&ap->asru_lock); cip = (fmd_case_impl_t *)ap->asru_case; rvp->rri_fmri = strdup(ap->asru_name); rvp->rri_uuid = strdup(ap->asru_uuid); rvp->rri_case = cip ? strdup(cip->ci_uuid) : NULL; rvp->rri_faulty = (state & FMD_ASRU_FAULTY) != 0; rvp->rri_unusable = (state & FMD_ASRU_UNUSABLE) != 0; rvp->rri_invisible = (ap->asru_flags & FMD_ASRU_INVISIBLE) != 0; (void) pthread_mutex_unlock(&ap->asru_lock); fmd_asru_hash_release(fmd.d_asrus, ap); if (rvp->rri_fmri == NULL || rvp->rri_uuid == NULL) rvp->rri_err = FMD_ADM_ERR_NOMEM; return (TRUE); } static void fmd_adm_do_repair(char *name, struct svc_req *req, int *errp, uint8_t reason, char *uuid) { if (fmd_rpc_deny(req)) *errp = FMD_ADM_ERR_PERM; else { fmd_asru_rep_arg_t fara; int err = FARA_ERR_RSRCNOTF; fara.fara_reason = reason; fara.fara_rval = &err; fara.fara_uuid = uuid; fara.fara_bywhat = FARA_BY_ASRU; fmd_asru_hash_apply_by_asru(fmd.d_asrus, name, fmd_asru_repaired, &fara); fara.fara_bywhat = FARA_BY_LABEL; fmd_asru_hash_apply_by_label(fmd.d_asrus, name, fmd_asru_repaired, &fara); fara.fara_bywhat = FARA_BY_FRU; fmd_asru_hash_apply_by_fru(fmd.d_asrus, name, fmd_asru_repaired, &fara); fara.fara_bywhat = FARA_BY_RSRC; fmd_asru_hash_apply_by_rsrc(fmd.d_asrus, name, fmd_asru_repaired, &fara); if (err == FARA_ERR_RSRCNOTR) *errp = FMD_ADM_ERR_RSRCNOTR; else if (err == FARA_OK) *errp = 0; } } bool_t fmd_adm_rsrcflush_1_svc(char *name, int *rvp, struct svc_req *req) { int err = FMD_ADM_ERR_RSRCNOTF; /* * If anyone does an fmadm flush command, discard any resolved * cases that were being retained for historic diagnosis. */ if (fmd_rpc_deny(req)) err = FMD_ADM_ERR_PERM; else { fmd_asru_hash_apply_by_asru(fmd.d_asrus, name, fmd_asru_flush, &err); fmd_asru_hash_apply_by_label(fmd.d_asrus, name, fmd_asru_flush, &err); fmd_asru_hash_apply_by_fru(fmd.d_asrus, name, fmd_asru_flush, &err); fmd_asru_hash_apply_by_rsrc(fmd.d_asrus, name, fmd_asru_flush, &err); } *rvp = err; return (TRUE); } bool_t fmd_adm_rsrcrepaired_1_svc(char *name, int *rvp, struct svc_req *req) { int err = FMD_ADM_ERR_RSRCNOTF; fmd_adm_do_repair(name, req, &err, FMD_ASRU_REPAIRED, NULL); *rvp = err; return (TRUE); } bool_t fmd_adm_rsrcreplaced_1_svc(char *name, int *rvp, struct svc_req *req) { int err = FMD_ADM_ERR_RSRCNOTF; fmd_adm_do_repair(name, req, &err, FMD_ASRU_REPLACED, NULL); *rvp = err; return (TRUE); } bool_t fmd_adm_rsrcacquit_1_svc(char *name, char *uuid, int *rvp, struct svc_req *req) { int err = FMD_ADM_ERR_RSRCNOTF; fmd_adm_do_repair(name, req, &err, FMD_ASRU_ACQUITTED, uuid); *rvp = err; return (TRUE); } static void fmd_adm_serdlist_measure(fmd_serd_eng_t *sgp, void *arg) { struct fmd_rpc_serdlist *rsl = arg; rsl->rsl_len += strlen(sgp->sg_name) + 1; rsl->rsl_cnt++; } static void fmd_adm_serdlist_record(fmd_serd_eng_t *sgp, void *arg) { struct fmd_rpc_serdlist *rsl = arg; bcopy(sgp->sg_name, rsl->rsl_buf.rsl_buf_val + rsl->rsl_len, strlen(sgp->sg_name)); rsl->rsl_len += strlen(sgp->sg_name) + 1; } bool_t fmd_adm_serdlist_1_svc(char *name, struct fmd_rpc_serdlist *rvp, struct svc_req *req) { fmd_module_t *mp; void *p; rvp->rsl_buf.rsl_buf_len = 0; rvp->rsl_buf.rsl_buf_val = NULL; rvp->rsl_len = 0; rvp->rsl_cnt = 0; rvp->rsl_err = 0; if (fmd_rpc_deny(req)) { rvp->rsl_err = FMD_ADM_ERR_PERM; return (TRUE); } if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) { rvp->rsl_err = FMD_ADM_ERR_MODSRCH; return (TRUE); } fmd_module_lock(mp); /* In the first pass, collect the overall length of the buffer. */ fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdlist_measure, rvp); if (rvp->rsl_len == 0) { fmd_module_unlock(mp); fmd_module_rele(mp); return (TRUE); } p = malloc(rvp->rsl_len); if (p) { rvp->rsl_buf.rsl_buf_val = p; rvp->rsl_buf.rsl_buf_len = rvp->rsl_len; bzero(rvp->rsl_buf.rsl_buf_val, rvp->rsl_buf.rsl_buf_len); rvp->rsl_len = 0; /* In the second pass, populate the buffer with data. */ fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdlist_record, rvp); } else { rvp->rsl_err = FMD_ADM_ERR_NOMEM; } fmd_module_unlock(mp); fmd_module_rele(mp); return (TRUE); } static void fmd_adm_serdinfo_record(fmd_serd_eng_t *sgp, struct fmd_rpc_serdinfo *rsi) { uint64_t old, now = fmd_time_gethrtime(); const fmd_serd_elem_t *oep; if ((rsi->rsi_name = strdup(sgp->sg_name)) == NULL) { rsi->rsi_err = FMD_ADM_ERR_NOMEM; return; } if ((oep = fmd_list_next(&sgp->sg_list)) != NULL) old = fmd_event_hrtime(oep->se_event); else old = now; rsi->rsi_delta = now >= old ? now - old : (UINT64_MAX - old) + now + 1; rsi->rsi_count = sgp->sg_count; rsi->rsi_fired = fmd_serd_eng_fired(sgp) != 0; rsi->rsi_n = sgp->sg_n; rsi->rsi_t = sgp->sg_t; } bool_t fmd_adm_serdinfo_1_svc(char *mname, char *sname, struct fmd_rpc_serdinfo *rvp, struct svc_req *req) { fmd_module_t *mp; fmd_serd_eng_t *sgp; bzero(rvp, sizeof (struct fmd_rpc_serdinfo)); if (fmd_rpc_deny(req)) { rvp->rsi_err = FMD_ADM_ERR_PERM; return (TRUE); } if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) { rvp->rsi_err = FMD_ADM_ERR_MODSRCH; return (TRUE); } fmd_module_lock(mp); if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) { fmd_adm_serdinfo_record(sgp, rvp); } else rvp->rsi_err = FMD_ADM_ERR_SERDSRCH; fmd_module_unlock(mp); fmd_module_rele(mp); return (TRUE); } /*ARGSUSED*/ bool_t fmd_adm_serdinfo_old_1_svc(char *name, struct fmd_rpc_serdlist *rvp, struct svc_req *req) { return (FALSE); } bool_t fmd_adm_serdreset_1_svc(char *mname, char *sname, int *rvp, struct svc_req *req) { fmd_module_t *mp; fmd_serd_eng_t *sgp; int err = 0; if (fmd_rpc_deny(req)) { *rvp = FMD_ADM_ERR_PERM; return (TRUE); } if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) { *rvp = FMD_ADM_ERR_MODSRCH; return (TRUE); } fmd_module_lock(mp); if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) { if (fmd_serd_eng_fired(sgp)) { err = FMD_ADM_ERR_SERDFIRED; } else { fmd_serd_eng_reset(sgp); fmd_module_setdirty(mp); } } else err = FMD_ADM_ERR_SERDSRCH; fmd_module_unlock(mp); fmd_module_rele(mp); *rvp = err; return (TRUE); } bool_t fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req) { fmd_log_t **lpp, *old, *new; int try = 1, trylimit = 1; pthread_rwlock_t *lockp; hrtime_t nsec = 0; timespec_t tv; if (fmd_rpc_deny(req)) { *rvp = FMD_ADM_ERR_PERM; return (TRUE); } if (strcmp(name, "errlog") == 0) { lpp = &fmd.d_errlog; lockp = &fmd.d_log_lock; } else if (strcmp(name, "fltlog") == 0) { lpp = &fmd.d_fltlog; lockp = &fmd.d_log_lock; } else if (strcmp(name, "infolog") == 0) { lpp = &fmd.d_ilog; lockp = &fmd.d_ilog_lock; } else if (strcmp(name, "infolog_hival") == 0) { lpp = &fmd.d_hvilog; lockp = &fmd.d_hvilog_lock; } else { *rvp = FMD_ADM_ERR_ROTSRCH; return (TRUE); } (void) fmd_conf_getprop(fmd.d_conf, "log.tryrotate", &trylimit); (void) fmd_conf_getprop(fmd.d_conf, "log.waitrotate", &nsec); tv.tv_sec = nsec / NANOSEC; tv.tv_nsec = nsec % NANOSEC; /* * To rotate a log file, grab d_log_lock as writer to make sure no * one else can discover the current log pointer. Then try to rotate * the log. If we're successful, release the old log pointer. */ do { if (try > 1) (void) nanosleep(&tv, NULL); /* wait for checkpoints */ (void) pthread_rwlock_wrlock(lockp); old = *lpp; if ((new = fmd_log_rotate(old)) != NULL) { fmd_log_rele(old); *lpp = new; } (void) pthread_rwlock_unlock(lockp); } while (new == NULL && errno == EFMD_LOG_ROTBUSY && try++ < trylimit); if (new != NULL) *rvp = 0; else if (errno == EFMD_LOG_ROTBUSY) *rvp = FMD_ADM_ERR_ROTBUSY; else *rvp = FMD_ADM_ERR_ROTFAIL; return (TRUE); } bool_t fmd_adm_caserepair_1_svc(char *uuid, int *rvp, struct svc_req *req) { fmd_case_t *cp = NULL; int err = 0; if (fmd_rpc_deny(req)) err = FMD_ADM_ERR_PERM; else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL) err = FMD_ADM_ERR_CASESRCH; else if (fmd_case_repair(cp) != 0) { err = errno == EFMD_CASE_OWNER ? FMD_ADM_ERR_CASEXPRT : FMD_ADM_ERR_CASEOPEN; } if (cp != NULL) fmd_case_rele(cp); *rvp = err; return (TRUE); } bool_t fmd_adm_caseacquit_1_svc(char *uuid, int *rvp, struct svc_req *req) { fmd_case_t *cp = NULL; int err = 0; if (fmd_rpc_deny(req)) err = FMD_ADM_ERR_PERM; else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL) err = FMD_ADM_ERR_CASESRCH; else if (fmd_case_acquit(cp) != 0) { err = errno == EFMD_CASE_OWNER ? FMD_ADM_ERR_CASEXPRT : FMD_ADM_ERR_CASEOPEN; } if (cp != NULL) fmd_case_rele(cp); *rvp = err; return (TRUE); } void fmd_adm_caselist_case(fmd_case_t *cp, void *arg) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; struct fmd_rpc_caselist *rcl = arg; size_t uuid_len, buf_len; void *p; if (rcl->rcl_err != 0) return; /* * skip invisible cases */ if (cip->ci_flags & FMD_CF_INVISIBLE) return; /* * Lock the case and reallocate rcl_buf[] to be large enough to hold * another string, doubling it as needed. Then copy the new string * on to the end, and increment rcl_len to indicate the used space. */ if (!(cip->ci_flags & FMD_CF_SOLVED)) return; (void) pthread_mutex_lock(&cip->ci_lock); uuid_len = cip->ci_uuidlen + 1; while (rcl->rcl_len + uuid_len > rcl->rcl_buf.rcl_buf_len) { if (rcl->rcl_buf.rcl_buf_len != 0) buf_len = rcl->rcl_buf.rcl_buf_len * 2; else buf_len = 1024; /* default buffer size */ if ((p = realloc(rcl->rcl_buf.rcl_buf_val, buf_len)) != NULL) { bzero((char *)p + rcl->rcl_buf.rcl_buf_len, buf_len - rcl->rcl_buf.rcl_buf_len); rcl->rcl_buf.rcl_buf_val = p; rcl->rcl_buf.rcl_buf_len = buf_len; } else { rcl->rcl_err = FMD_ADM_ERR_NOMEM; break; } } if (rcl->rcl_err == 0) { bcopy(cip->ci_uuid, (char *)rcl->rcl_buf.rcl_buf_val + rcl->rcl_len, uuid_len); rcl->rcl_len += uuid_len; rcl->rcl_cnt++; } (void) pthread_mutex_unlock(&cip->ci_lock); } bool_t fmd_adm_caselist_1_svc(struct fmd_rpc_caselist *rvp, struct svc_req *req) { rvp->rcl_buf.rcl_buf_len = 0; rvp->rcl_buf.rcl_buf_val = NULL; rvp->rcl_len = 0; rvp->rcl_cnt = 0; rvp->rcl_err = 0; if (fmd_rpc_deny(req)) rvp->rcl_err = FMD_ADM_ERR_PERM; else fmd_case_hash_apply(fmd.d_cases, fmd_adm_caselist_case, rvp); return (TRUE); } bool_t fmd_adm_caseinfo_1_svc(char *uuid, struct fmd_rpc_caseinfo *rvp, struct svc_req *req) { fmd_case_t *cp; nvlist_t *nvl; int err = 0; bzero(rvp, sizeof (struct fmd_rpc_caseinfo)); if (fmd_rpc_deny(req)) { rvp->rci_err = FMD_ADM_ERR_PERM; return (TRUE); } if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL) { rvp->rci_err = FMD_ADM_ERR_CASESRCH; return (TRUE); } if (!(((fmd_case_impl_t *)cp)->ci_flags & FMD_CF_SOLVED)) { fmd_case_rele(cp); rvp->rci_err = FMD_ADM_ERR_CASESRCH; return (TRUE); } nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS); err = nvlist_pack(nvl, &rvp->rci_evbuf.rci_evbuf_val, &rvp->rci_evbuf.rci_evbuf_len, NV_ENCODE_XDR, 0); nvlist_free(nvl); if (err != 0) rvp->rci_err = FMD_ADM_ERR_NOMEM; fmd_case_rele(cp); return (TRUE); } /*ARGSUSED*/ static void fmd_adm_xprtlist_one(fmd_idspace_t *ids, id_t id, void *arg) { struct fmd_rpc_xprtlist *rvp = arg; if (rvp->rxl_len < rvp->rxl_buf.rxl_buf_len) rvp->rxl_buf.rxl_buf_val[rvp->rxl_len++] = id; } bool_t fmd_adm_xprtlist_1_svc(struct fmd_rpc_xprtlist *rvp, struct svc_req *req) { if (fmd_rpc_deny(req)) { rvp->rxl_buf.rxl_buf_len = 0; rvp->rxl_buf.rxl_buf_val = NULL; rvp->rxl_len = 0; rvp->rxl_err = FMD_ADM_ERR_PERM; return (TRUE); } /* * Since we're taking a snapshot of the transports, and these could * change after we return our result, there's no need to hold any kind * of lock between retrieving ids_count and taking the snapshot. We'll * just capture up to a maximum of whatever ids_count value we sampled. */ rvp->rxl_buf.rxl_buf_len = fmd.d_xprt_ids->ids_count; rvp->rxl_buf.rxl_buf_val = malloc(sizeof (int32_t) * rvp->rxl_buf.rxl_buf_len); rvp->rxl_len = 0; rvp->rxl_err = 0; if (rvp->rxl_buf.rxl_buf_val == NULL) { rvp->rxl_err = FMD_ADM_ERR_NOMEM; return (TRUE); } fmd_idspace_apply(fmd.d_xprt_ids, fmd_adm_xprtlist_one, rvp); return (TRUE); } bool_t fmd_adm_xprtstat_1_svc(int32_t id, struct fmd_rpc_modstat *rms, struct svc_req *req) { fmd_xprt_impl_t *xip; fmd_stat_t *sp, *ep, *cp; if (fmd_rpc_deny(req)) { rms->rms_buf.rms_buf_val = NULL; rms->rms_buf.rms_buf_len = 0; rms->rms_err = FMD_ADM_ERR_PERM; return (TRUE); } rms->rms_buf.rms_buf_val = malloc(sizeof (fmd_xprt_stat_t)); rms->rms_buf.rms_buf_len = sizeof (fmd_xprt_stat_t) / sizeof (fmd_stat_t); rms->rms_err = 0; if (rms->rms_buf.rms_buf_val == NULL) { rms->rms_err = FMD_ADM_ERR_NOMEM; rms->rms_buf.rms_buf_len = 0; return (TRUE); } if ((xip = fmd_idspace_hold(fmd.d_xprt_ids, id)) == NULL) { rms->rms_err = FMD_ADM_ERR_XPRTSRCH; return (TRUE); } /* * Grab the stats lock and bcopy the entire transport stats array in * one shot. Then go back through and duplicate any string values. */ (void) pthread_mutex_lock(&xip->xi_stats_lock); sp = (fmd_stat_t *)xip->xi_stats; ep = sp + rms->rms_buf.rms_buf_len; cp = rms->rms_buf.rms_buf_val; bcopy(sp, cp, sizeof (fmd_xprt_stat_t)); for (; sp < ep; sp++, cp++) { if (sp->fmds_type == FMD_TYPE_STRING && sp->fmds_value.str != NULL) cp->fmds_value.str = strdup(sp->fmds_value.str); } (void) pthread_mutex_unlock(&xip->xi_stats_lock); fmd_idspace_rele(fmd.d_xprt_ids, id); return (TRUE); } int fmd_adm_1_freeresult(SVCXPRT *xprt, xdrproc_t proc, caddr_t data) { xdr_free(proc, data); svc_done(xprt); return (TRUE); } /* * Custom XDR routine for our API structure fmd_stat_t. This function must * match the definition of fmd_stat_t in and must also match * the corresponding routine in usr/src/lib/fm/libfmd_adm/common/fmd_adm.c. */ bool_t xdr_fmd_stat(XDR *xp, fmd_stat_t *sp) { bool_t rv = TRUE; rv &= xdr_opaque(xp, sp->fmds_name, sizeof (sp->fmds_name)); rv &= xdr_u_int(xp, &sp->fmds_type); rv &= xdr_opaque(xp, sp->fmds_desc, sizeof (sp->fmds_desc)); switch (sp->fmds_type) { case FMD_TYPE_BOOL: rv &= xdr_int(xp, &sp->fmds_value.bool); break; case FMD_TYPE_INT32: rv &= xdr_int32_t(xp, &sp->fmds_value.i32); break; case FMD_TYPE_UINT32: rv &= xdr_uint32_t(xp, &sp->fmds_value.ui32); break; case FMD_TYPE_INT64: rv &= xdr_int64_t(xp, &sp->fmds_value.i64); break; case FMD_TYPE_UINT64: case FMD_TYPE_TIME: case FMD_TYPE_SIZE: rv &= xdr_uint64_t(xp, &sp->fmds_value.ui64); break; case FMD_TYPE_STRING: rv &= xdr_string(xp, &sp->fmds_value.str, ~0); break; } return (rv); } /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _FMD_RPC_ADM_H_RPCGEN #define _FMD_RPC_ADM_H_RPCGEN #include #ifndef _KERNEL #include #include #endif /* !_KERNEL */ #ifdef __cplusplus extern "C" { #endif #include enum fmd_adm_error { FMD_ADM_ERR_NOMEM = 1, FMD_ADM_ERR_PERM = 1 + 1, FMD_ADM_ERR_MODSRCH = 1 + 2, FMD_ADM_ERR_MODBUSY = 1 + 3, FMD_ADM_ERR_MODFAIL = 1 + 4, FMD_ADM_ERR_MODNOENT = 1 + 5, FMD_ADM_ERR_MODEXIST = 1 + 6, FMD_ADM_ERR_MODINIT = 1 + 7, FMD_ADM_ERR_MODLOAD = 1 + 8, FMD_ADM_ERR_RSRCSRCH = 1 + 9, FMD_ADM_ERR_RSRCNOTF = 1 + 10, FMD_ADM_ERR_SERDSRCH = 1 + 11, FMD_ADM_ERR_SERDFIRED = 1 + 12, FMD_ADM_ERR_ROTSRCH = 1 + 13, FMD_ADM_ERR_ROTFAIL = 1 + 14, FMD_ADM_ERR_ROTBUSY = 1 + 15, FMD_ADM_ERR_CASESRCH = 1 + 16, FMD_ADM_ERR_CASEOPEN = 1 + 17, FMD_ADM_ERR_XPRTSRCH = 1 + 18, FMD_ADM_ERR_CASEXPRT = 1 + 19, FMD_ADM_ERR_RSRCNOTR = 1 + 20 }; typedef enum fmd_adm_error fmd_adm_error; struct fmd_rpc_modstat { struct { u_int rms_buf_len; struct fmd_stat *rms_buf_val; } rms_buf; enum fmd_adm_error rms_err; }; typedef struct fmd_rpc_modstat fmd_rpc_modstat; struct fmd_rpc_modinfo { char *rmi_name; char *rmi_desc; char *rmi_vers; bool_t rmi_faulty; struct fmd_rpc_modinfo *rmi_next; }; typedef struct fmd_rpc_modinfo fmd_rpc_modinfo; struct fmd_rpc_modlist { enum fmd_adm_error rml_err; struct fmd_rpc_modinfo *rml_list; uint32_t rml_len; }; typedef struct fmd_rpc_modlist fmd_rpc_modlist; struct fmd_rpc_rsrcinfo { char *rri_fmri; char *rri_uuid; char *rri_case; bool_t rri_faulty; bool_t rri_unusable; bool_t rri_invisible; enum fmd_adm_error rri_err; }; typedef struct fmd_rpc_rsrcinfo fmd_rpc_rsrcinfo; struct fmd_rpc_rsrclist { struct { u_int rrl_buf_len; char *rrl_buf_val; } rrl_buf; uint32_t rrl_len; uint32_t rrl_cnt; enum fmd_adm_error rrl_err; bool_t rrl_all; }; typedef struct fmd_rpc_rsrclist fmd_rpc_rsrclist; struct fmd_rpc_serdinfo { char *rsi_name; uint64_t rsi_delta; uint32_t rsi_count; bool_t rsi_fired; uint64_t rsi_n; uint64_t rsi_t; enum fmd_adm_error rsi_err; }; typedef struct fmd_rpc_serdinfo fmd_rpc_serdinfo; struct fmd_rpc_serdlist { struct { u_int rsl_buf_len; char *rsl_buf_val; } rsl_buf; uint32_t rsl_len; uint32_t rsl_cnt; enum fmd_adm_error rsl_err; }; typedef struct fmd_rpc_serdlist fmd_rpc_serdlist; struct fmd_rpc_xprtlist { struct { u_int rxl_buf_len; int32_t *rxl_buf_val; } rxl_buf; uint32_t rxl_len; enum fmd_adm_error rxl_err; }; typedef struct fmd_rpc_xprtlist fmd_rpc_xprtlist; struct fmd_rpc_caseinfo { struct { u_int rci_evbuf_len; char *rci_evbuf_val; } rci_evbuf; enum fmd_adm_error rci_err; }; typedef struct fmd_rpc_caseinfo fmd_rpc_caseinfo; struct fmd_rpc_caselist { struct { u_int rcl_buf_len; char *rcl_buf_val; } rcl_buf; uint32_t rcl_len; uint32_t rcl_cnt; enum fmd_adm_error rcl_err; }; typedef struct fmd_rpc_caselist fmd_rpc_caselist; extern void fmd_adm_1(struct svc_req *, SVCXPRT *); extern bool_t xdr_fmd_stat(XDR *, struct fmd_stat *); #undef RW_READ_HELD #undef RW_WRITE_HELD #undef RW_LOCK_HELD #undef MUTEX_HELD struct fmd_adm_serdreset_1_argument { char *arg1; char *arg2; }; typedef struct fmd_adm_serdreset_1_argument fmd_adm_serdreset_1_argument; struct fmd_adm_rsrcacquit_1_argument { char *arg1; char *arg2; }; typedef struct fmd_adm_rsrcacquit_1_argument fmd_adm_rsrcacquit_1_argument; struct fmd_adm_serdinfo_1_argument { char *arg1; char *arg2; }; typedef struct fmd_adm_serdinfo_1_argument fmd_adm_serdinfo_1_argument; #define FMD_ADM 100169 #define FMD_ADM_VERSION_1 1 #if defined(__STDC__) || defined(__cplusplus) #define FMD_ADM_MODINFO 1 extern enum clnt_stat fmd_adm_modinfo_1(struct fmd_rpc_modlist *, CLIENT *); extern bool_t fmd_adm_modinfo_1_svc(struct fmd_rpc_modlist *, struct svc_req *); #define FMD_ADM_MODCSTAT 2 extern enum clnt_stat fmd_adm_modcstat_1(char *, struct fmd_rpc_modstat *, CLIENT *); extern bool_t fmd_adm_modcstat_1_svc(char *, struct fmd_rpc_modstat *, struct svc_req *); #define FMD_ADM_MODDSTAT 3 extern enum clnt_stat fmd_adm_moddstat_1(char *, struct fmd_rpc_modstat *, CLIENT *); extern bool_t fmd_adm_moddstat_1_svc(char *, struct fmd_rpc_modstat *, struct svc_req *); #define FMD_ADM_MODGSTAT 4 extern enum clnt_stat fmd_adm_modgstat_1(struct fmd_rpc_modstat *, CLIENT *); extern bool_t fmd_adm_modgstat_1_svc(struct fmd_rpc_modstat *, struct svc_req *); #define FMD_ADM_MODLOAD 5 extern enum clnt_stat fmd_adm_modload_1(char *, int *, CLIENT *); extern bool_t fmd_adm_modload_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_MODUNLOAD 6 extern enum clnt_stat fmd_adm_modunload_1(char *, int *, CLIENT *); extern bool_t fmd_adm_modunload_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_MODRESET 7 extern enum clnt_stat fmd_adm_modreset_1(char *, int *, CLIENT *); extern bool_t fmd_adm_modreset_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_MODGC 8 extern enum clnt_stat fmd_adm_modgc_1(char *, int *, CLIENT *); extern bool_t fmd_adm_modgc_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_RSRCLIST 9 extern enum clnt_stat fmd_adm_rsrclist_1(bool_t , struct fmd_rpc_rsrclist *, CLIENT *); extern bool_t fmd_adm_rsrclist_1_svc(bool_t , struct fmd_rpc_rsrclist *, struct svc_req *); #define FMD_ADM_RSRCINFO 10 extern enum clnt_stat fmd_adm_rsrcinfo_1(char *, struct fmd_rpc_rsrcinfo *, CLIENT *); extern bool_t fmd_adm_rsrcinfo_1_svc(char *, struct fmd_rpc_rsrcinfo *, struct svc_req *); #define FMD_ADM_RSRCFLUSH 11 extern enum clnt_stat fmd_adm_rsrcflush_1(char *, int *, CLIENT *); extern bool_t fmd_adm_rsrcflush_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_RSRCREPAIRED 12 extern enum clnt_stat fmd_adm_rsrcrepaired_1(char *, int *, CLIENT *); extern bool_t fmd_adm_rsrcrepaired_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_SERDINFO_OLD 13 extern enum clnt_stat fmd_adm_serdinfo_old_1(char *, struct fmd_rpc_serdlist *, CLIENT *); extern bool_t fmd_adm_serdinfo_old_1_svc(char *, struct fmd_rpc_serdlist *, struct svc_req *); #define FMD_ADM_SERDRESET 14 extern enum clnt_stat fmd_adm_serdreset_1(char *, char *, int *, CLIENT *); extern bool_t fmd_adm_serdreset_1_svc(char *, char *, int *, struct svc_req *); #define FMD_ADM_LOGROTATE 15 extern enum clnt_stat fmd_adm_logrotate_1(char *, int *, CLIENT *); extern bool_t fmd_adm_logrotate_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_CASEREPAIR 16 extern enum clnt_stat fmd_adm_caserepair_1(char *, int *, CLIENT *); extern bool_t fmd_adm_caserepair_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_XPRTLIST 17 extern enum clnt_stat fmd_adm_xprtlist_1(struct fmd_rpc_xprtlist *, CLIENT *); extern bool_t fmd_adm_xprtlist_1_svc(struct fmd_rpc_xprtlist *, struct svc_req *); #define FMD_ADM_XPRTSTAT 18 extern enum clnt_stat fmd_adm_xprtstat_1(int32_t , struct fmd_rpc_modstat *, CLIENT *); extern bool_t fmd_adm_xprtstat_1_svc(int32_t , struct fmd_rpc_modstat *, struct svc_req *); #define FMD_ADM_CASELIST 19 extern enum clnt_stat fmd_adm_caselist_1(struct fmd_rpc_caselist *, CLIENT *); extern bool_t fmd_adm_caselist_1_svc(struct fmd_rpc_caselist *, struct svc_req *); #define FMD_ADM_CASEINFO 20 extern enum clnt_stat fmd_adm_caseinfo_1(char *, struct fmd_rpc_caseinfo *, CLIENT *); extern bool_t fmd_adm_caseinfo_1_svc(char *, struct fmd_rpc_caseinfo *, struct svc_req *); #define FMD_ADM_RSRCREPLACED 21 extern enum clnt_stat fmd_adm_rsrcreplaced_1(char *, int *, CLIENT *); extern bool_t fmd_adm_rsrcreplaced_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_RSRCACQUIT 22 extern enum clnt_stat fmd_adm_rsrcacquit_1(char *, char *, int *, CLIENT *); extern bool_t fmd_adm_rsrcacquit_1_svc(char *, char *, int *, struct svc_req *); #define FMD_ADM_CASEACQUIT 23 extern enum clnt_stat fmd_adm_caseacquit_1(char *, int *, CLIENT *); extern bool_t fmd_adm_caseacquit_1_svc(char *, int *, struct svc_req *); #define FMD_ADM_SERDLIST 24 extern enum clnt_stat fmd_adm_serdlist_1(char *, struct fmd_rpc_serdlist *, CLIENT *); extern bool_t fmd_adm_serdlist_1_svc(char *, struct fmd_rpc_serdlist *, struct svc_req *); #define FMD_ADM_SERDINFO 25 extern enum clnt_stat fmd_adm_serdinfo_1(char *, char *, struct fmd_rpc_serdinfo *, CLIENT *); extern bool_t fmd_adm_serdinfo_1_svc(char *, char *, struct fmd_rpc_serdinfo *, struct svc_req *); extern int fmd_adm_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define FMD_ADM_MODINFO 1 extern enum clnt_stat fmd_adm_modinfo_1(); extern bool_t fmd_adm_modinfo_1_svc(); #define FMD_ADM_MODCSTAT 2 extern enum clnt_stat fmd_adm_modcstat_1(); extern bool_t fmd_adm_modcstat_1_svc(); #define FMD_ADM_MODDSTAT 3 extern enum clnt_stat fmd_adm_moddstat_1(); extern bool_t fmd_adm_moddstat_1_svc(); #define FMD_ADM_MODGSTAT 4 extern enum clnt_stat fmd_adm_modgstat_1(); extern bool_t fmd_adm_modgstat_1_svc(); #define FMD_ADM_MODLOAD 5 extern enum clnt_stat fmd_adm_modload_1(); extern bool_t fmd_adm_modload_1_svc(); #define FMD_ADM_MODUNLOAD 6 extern enum clnt_stat fmd_adm_modunload_1(); extern bool_t fmd_adm_modunload_1_svc(); #define FMD_ADM_MODRESET 7 extern enum clnt_stat fmd_adm_modreset_1(); extern bool_t fmd_adm_modreset_1_svc(); #define FMD_ADM_MODGC 8 extern enum clnt_stat fmd_adm_modgc_1(); extern bool_t fmd_adm_modgc_1_svc(); #define FMD_ADM_RSRCLIST 9 extern enum clnt_stat fmd_adm_rsrclist_1(); extern bool_t fmd_adm_rsrclist_1_svc(); #define FMD_ADM_RSRCINFO 10 extern enum clnt_stat fmd_adm_rsrcinfo_1(); extern bool_t fmd_adm_rsrcinfo_1_svc(); #define FMD_ADM_RSRCFLUSH 11 extern enum clnt_stat fmd_adm_rsrcflush_1(); extern bool_t fmd_adm_rsrcflush_1_svc(); #define FMD_ADM_RSRCREPAIRED 12 extern enum clnt_stat fmd_adm_rsrcrepaired_1(); extern bool_t fmd_adm_rsrcrepaired_1_svc(); #define FMD_ADM_SERDINFO_OLD 13 extern enum clnt_stat fmd_adm_serdinfo_old_1(); extern bool_t fmd_adm_serdinfo_old_1_svc(); #define FMD_ADM_SERDRESET 14 extern enum clnt_stat fmd_adm_serdreset_1(); extern bool_t fmd_adm_serdreset_1_svc(); #define FMD_ADM_LOGROTATE 15 extern enum clnt_stat fmd_adm_logrotate_1(); extern bool_t fmd_adm_logrotate_1_svc(); #define FMD_ADM_CASEREPAIR 16 extern enum clnt_stat fmd_adm_caserepair_1(); extern bool_t fmd_adm_caserepair_1_svc(); #define FMD_ADM_XPRTLIST 17 extern enum clnt_stat fmd_adm_xprtlist_1(); extern bool_t fmd_adm_xprtlist_1_svc(); #define FMD_ADM_XPRTSTAT 18 extern enum clnt_stat fmd_adm_xprtstat_1(); extern bool_t fmd_adm_xprtstat_1_svc(); #define FMD_ADM_CASELIST 19 extern enum clnt_stat fmd_adm_caselist_1(); extern bool_t fmd_adm_caselist_1_svc(); #define FMD_ADM_CASEINFO 20 extern enum clnt_stat fmd_adm_caseinfo_1(); extern bool_t fmd_adm_caseinfo_1_svc(); #define FMD_ADM_RSRCREPLACED 21 extern enum clnt_stat fmd_adm_rsrcreplaced_1(); extern bool_t fmd_adm_rsrcreplaced_1_svc(); #define FMD_ADM_RSRCACQUIT 22 extern enum clnt_stat fmd_adm_rsrcacquit_1(); extern bool_t fmd_adm_rsrcacquit_1_svc(); #define FMD_ADM_CASEACQUIT 23 extern enum clnt_stat fmd_adm_caseacquit_1(); extern bool_t fmd_adm_caseacquit_1_svc(); #define FMD_ADM_SERDLIST 24 extern enum clnt_stat fmd_adm_serdlist_1(); extern bool_t fmd_adm_serdlist_1_svc(); #define FMD_ADM_SERDINFO 25 extern enum clnt_stat fmd_adm_serdinfo_1(); extern bool_t fmd_adm_serdinfo_1_svc(); extern int fmd_adm_1_freeresult(); #endif /* K&R C */ /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool_t xdr_fmd_adm_error(XDR *, fmd_adm_error*); extern bool_t xdr_fmd_rpc_modstat(XDR *, fmd_rpc_modstat*); extern bool_t xdr_fmd_rpc_modinfo(XDR *, fmd_rpc_modinfo*); extern bool_t xdr_fmd_rpc_modlist(XDR *, fmd_rpc_modlist*); extern bool_t xdr_fmd_rpc_rsrcinfo(XDR *, fmd_rpc_rsrcinfo*); extern bool_t xdr_fmd_rpc_rsrclist(XDR *, fmd_rpc_rsrclist*); extern bool_t xdr_fmd_rpc_serdinfo(XDR *, fmd_rpc_serdinfo*); extern bool_t xdr_fmd_rpc_serdlist(XDR *, fmd_rpc_serdlist*); extern bool_t xdr_fmd_rpc_xprtlist(XDR *, fmd_rpc_xprtlist*); extern bool_t xdr_fmd_rpc_caseinfo(XDR *, fmd_rpc_caseinfo*); extern bool_t xdr_fmd_rpc_caselist(XDR *, fmd_rpc_caselist*); extern bool_t xdr_fmd_adm_serdreset_1_argument(XDR *, fmd_adm_serdreset_1_argument*); extern bool_t xdr_fmd_adm_rsrcacquit_1_argument(XDR *, fmd_adm_rsrcacquit_1_argument*); extern bool_t xdr_fmd_adm_serdinfo_1_argument(XDR *, fmd_adm_serdinfo_1_argument*); #else /* K&R C */ extern bool_t xdr_fmd_adm_error(); extern bool_t xdr_fmd_rpc_modstat(); extern bool_t xdr_fmd_rpc_modinfo(); extern bool_t xdr_fmd_rpc_modlist(); extern bool_t xdr_fmd_rpc_rsrcinfo(); extern bool_t xdr_fmd_rpc_rsrclist(); extern bool_t xdr_fmd_rpc_serdinfo(); extern bool_t xdr_fmd_rpc_serdlist(); extern bool_t xdr_fmd_rpc_xprtlist(); extern bool_t xdr_fmd_rpc_caseinfo(); extern bool_t xdr_fmd_rpc_caselist(); extern bool_t xdr_fmd_adm_serdreset_1_argument(); extern bool_t xdr_fmd_adm_rsrcacquit_1_argument(); extern bool_t xdr_fmd_adm_serdinfo_1_argument(); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_FMD_RPC_ADM_H_RPCGEN */ /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ %#include enum fmd_adm_error { FMD_ADM_ERR_NOMEM = 1, FMD_ADM_ERR_PERM, FMD_ADM_ERR_MODSRCH, FMD_ADM_ERR_MODBUSY, FMD_ADM_ERR_MODFAIL, FMD_ADM_ERR_MODNOENT, FMD_ADM_ERR_MODEXIST, FMD_ADM_ERR_MODINIT, FMD_ADM_ERR_MODLOAD, FMD_ADM_ERR_RSRCSRCH, FMD_ADM_ERR_RSRCNOTF, FMD_ADM_ERR_SERDSRCH, FMD_ADM_ERR_SERDFIRED, FMD_ADM_ERR_ROTSRCH, FMD_ADM_ERR_ROTFAIL, FMD_ADM_ERR_ROTBUSY, FMD_ADM_ERR_CASESRCH, FMD_ADM_ERR_CASEOPEN, FMD_ADM_ERR_XPRTSRCH, FMD_ADM_ERR_CASEXPRT, FMD_ADM_ERR_RSRCNOTR }; struct fmd_rpc_modstat { struct fmd_stat rms_buf<>; enum fmd_adm_error rms_err; }; struct fmd_rpc_modinfo { string rmi_name<>; string rmi_desc<>; string rmi_vers<>; bool rmi_faulty; struct fmd_rpc_modinfo *rmi_next; }; struct fmd_rpc_modlist { enum fmd_adm_error rml_err; struct fmd_rpc_modinfo *rml_list; uint32_t rml_len; }; struct fmd_rpc_rsrcinfo { string rri_fmri<>; string rri_uuid<>; string rri_case<>; bool rri_faulty; bool rri_unusable; bool rri_invisible; enum fmd_adm_error rri_err; }; struct fmd_rpc_rsrclist { opaque rrl_buf<>; uint32_t rrl_len; uint32_t rrl_cnt; enum fmd_adm_error rrl_err; bool rrl_all; }; struct fmd_rpc_serdinfo { string rsi_name<>; uint64_t rsi_delta; uint32_t rsi_count; bool rsi_fired; uint64_t rsi_n; uint64_t rsi_t; enum fmd_adm_error rsi_err; }; struct fmd_rpc_serdlist { opaque rsl_buf<>; uint32_t rsl_len; uint32_t rsl_cnt; enum fmd_adm_error rsl_err; }; struct fmd_rpc_xprtlist { int32_t rxl_buf<>; uint32_t rxl_len; enum fmd_adm_error rxl_err; }; struct fmd_rpc_caseinfo { opaque rci_evbuf<>; enum fmd_adm_error rci_err; }; struct fmd_rpc_caselist { opaque rcl_buf<>; uint32_t rcl_len; uint32_t rcl_cnt; enum fmd_adm_error rcl_err; }; program FMD_ADM { version FMD_ADM_VERSION_1 { struct fmd_rpc_modlist FMD_ADM_MODINFO(void) = 1; struct fmd_rpc_modstat FMD_ADM_MODCSTAT(string) = 2; struct fmd_rpc_modstat FMD_ADM_MODDSTAT(string) = 3; struct fmd_rpc_modstat FMD_ADM_MODGSTAT(void) = 4; int FMD_ADM_MODLOAD(string) = 5; int FMD_ADM_MODUNLOAD(string) = 6; int FMD_ADM_MODRESET(string) = 7; int FMD_ADM_MODGC(string) = 8; struct fmd_rpc_rsrclist FMD_ADM_RSRCLIST(bool) = 9; struct fmd_rpc_rsrcinfo FMD_ADM_RSRCINFO(string) = 10; int FMD_ADM_RSRCFLUSH(string) = 11; int FMD_ADM_RSRCREPAIRED(string) = 12; struct fmd_rpc_serdlist FMD_ADM_SERDINFO_OLD(string) = 13; int FMD_ADM_SERDRESET(string, string) = 14; int FMD_ADM_LOGROTATE(string) = 15; int FMD_ADM_CASEREPAIR(string) = 16; struct fmd_rpc_xprtlist FMD_ADM_XPRTLIST(void) = 17; struct fmd_rpc_modstat FMD_ADM_XPRTSTAT(int32_t) = 18; struct fmd_rpc_caselist FMD_ADM_CASELIST(void) = 19; struct fmd_rpc_caseinfo FMD_ADM_CASEINFO(string) = 20; int FMD_ADM_RSRCREPLACED(string) = 21; int FMD_ADM_RSRCACQUIT(string, string) = 22; int FMD_ADM_CASEACQUIT(string) = 23; struct fmd_rpc_serdlist FMD_ADM_SERDLIST(string) = 24; struct fmd_rpc_serdinfo FMD_ADM_SERDINFO(string, string) = 25; } = 1; } = 100169; %extern void fmd_adm_1(struct svc_req *, SVCXPRT *); %extern bool_t xdr_fmd_stat(XDR *, struct fmd_stat *); %#undef RW_READ_HELD %#undef RW_WRITE_HELD %#undef RW_LOCK_HELD %#undef MUTEX_HELD /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include /*ARGSUSED*/ bool_t fmd_hdl_create_1_svc(int version, void *rvp, struct svc_req *req) { return (FALSE); } int fmd_api_1_freeresult(SVCXPRT *xprt, xdrproc_t proc, caddr_t data) { xdr_free(proc, data); svc_done(xprt); return (TRUE); } /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _FMD_RPC_API_H_RPCGEN #define _FMD_RPC_API_H_RPCGEN #include #ifndef _KERNEL #include #include #endif /* !_KERNEL */ #ifdef __cplusplus extern "C" { #endif extern void fmd_api_1(struct svc_req *, SVCXPRT *); #undef RW_READ_HELD #undef RW_WRITE_HELD #undef RW_LOCK_HELD #undef MUTEX_HELD #define FMD_API 100170 #define FMD_API_VERSION_1 1 #if defined(__STDC__) || defined(__cplusplus) #define FMD_HDL_CREATE 1 extern enum clnt_stat fmd_hdl_create_1(int , void *, CLIENT *); extern bool_t fmd_hdl_create_1_svc(int , void *, struct svc_req *); extern int fmd_api_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define FMD_HDL_CREATE 1 extern enum clnt_stat fmd_hdl_create_1(); extern bool_t fmd_hdl_create_1_svc(); extern int fmd_api_1_freeresult(); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_FMD_RPC_API_H_RPCGEN */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ program FMD_API { version FMD_API_VERSION_1 { void FMD_HDL_CREATE(int) = 1; } = 1; } = 100170; %extern void fmd_api_1(struct svc_req *, SVCXPRT *); %#undef RW_READ_HELD %#undef RW_WRITE_HELD %#undef RW_LOCK_HELD %#undef MUTEX_HELD /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include typedef struct fmd_rtld { void *rtld_dlp; /* libdl handle for shared library */ void (*rtld_init)(fmd_hdl_t *); /* shared library's _fmd_init() */ void (*rtld_fini)(fmd_hdl_t *); /* shared library's _fmd_fini() */ } fmd_rtld_t; static int rtld_init(fmd_module_t *mp) { fmd_rtld_t *rp; void *dlp; if ((dlp = dlopen(mp->mod_path, RTLD_LOCAL | RTLD_NOW)) == NULL) { fmd_error(EFMD_RTLD_OPEN, "%s\n", dlerror()); return (fmd_set_errno(EFMD_RTLD_OPEN)); } rp = mp->mod_data = fmd_alloc(sizeof (fmd_rtld_t), FMD_SLEEP); rp->rtld_dlp = dlp; rp->rtld_init = (void (*)())dlsym(dlp, "_fmd_init"); rp->rtld_fini = (void (*)())dlsym(dlp, "_fmd_fini"); if (rp->rtld_init == NULL) { (void) dlclose(dlp); fmd_free(rp, sizeof (fmd_rtld_t)); return (fmd_set_errno(EFMD_RTLD_INIT)); } (void) pthread_mutex_unlock(&mp->mod_lock); /* * Call _fmd_init() in the module. If this causes a module abort and * mod_info has been registered, unregister it on behalf of the module. */ if (fmd_module_enter(mp, rp->rtld_init) != 0 && mp->mod_info != NULL) fmd_hdl_unregister((fmd_hdl_t *)mp); fmd_module_exit(mp); (void) pthread_mutex_lock(&mp->mod_lock); if (mp->mod_info == NULL) { (void) dlclose(dlp); fmd_free(rp, sizeof (fmd_rtld_t)); return (fmd_set_errno(EFMD_HDL_INIT)); } return (0); } static int rtld_fini(fmd_module_t *mp) { fmd_rtld_t *rp = mp->mod_data; int doclose = 1, err = 0; if (mp->mod_info != NULL) { (void) fmd_module_enter(mp, rp->rtld_fini); if (mp->mod_info != NULL) { fmd_module_lock(mp); fmd_module_unregister(mp); fmd_module_unlock(mp); } fmd_module_exit(mp); } (void) fmd_conf_getprop(fmd.d_conf, "plugin.close", &doclose); if (doclose) err = dlclose(rp->rtld_dlp); fmd_free(rp, sizeof (fmd_rtld_t)); return (err); } const fmd_modops_t fmd_rtld_ops = { rtld_init, rtld_fini, fmd_module_dispatch, fmd_module_transport, }; /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * The fmd resource scheme, used for fmd modules, must be implemented here for * the benefit of fmd-self-diagnosis and also in schemes/fmd for fmdump(8). */ ssize_t fmd_scheme_fmd_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) { char *name; if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0) return (fmd_fmri_set_errno(EINVAL)); return (snprintf(buf, buflen, "%s:///module/%s", FM_FMRI_SCHEME_FMD, name)); } static int fmd_scheme_fmd_present(nvlist_t *nvl) { char *name, *version; fmd_module_t *mp; int rv = 1; if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0 || nvlist_lookup_string(nvl, FM_FMRI_FMD_VERSION, &version) != 0) return (fmd_fmri_set_errno(EINVAL)); if (!fmd.d_loaded) return (1); if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) { rv = mp->mod_vers != NULL && strcmp(mp->mod_vers, version) == 0; fmd_module_rele(mp); } return (rv); } static int fmd_scheme_fmd_replaced(nvlist_t *nvl) { char *name, *version; fmd_module_t *mp; int rv = 1; if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0 || nvlist_lookup_string(nvl, FM_FMRI_FMD_VERSION, &version) != 0) return (fmd_fmri_set_errno(EINVAL)); if (!fmd.d_loaded) return (FMD_OBJ_STATE_UNKNOWN); if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) { rv = mp->mod_vers != NULL && strcmp(mp->mod_vers, version) == 0; fmd_module_rele(mp); } return (rv ? FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED); } static int fmd_scheme_fmd_service_state(nvlist_t *nvl) { char *name; fmd_module_t *mp; int rv = 1; if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0) return (fmd_fmri_set_errno(EINVAL)); if (!fmd.d_loaded) return (FMD_SERVICE_STATE_UNKNOWN); if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) { rv = mp->mod_error != 0; fmd_module_rele(mp); } return (rv ? FMD_SERVICE_STATE_UNUSABLE : FMD_SERVICE_STATE_OK); } static int fmd_scheme_fmd_unusable(nvlist_t *nvl) { char *name; fmd_module_t *mp; int rv = 1; if (nvlist_lookup_string(nvl, FM_FMRI_FMD_NAME, &name) != 0) return (fmd_fmri_set_errno(EINVAL)); if (!fmd.d_loaded) return (0); if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) != NULL) { rv = mp->mod_error != 0; fmd_module_rele(mp); } return (rv); } static nvlist_t * fmd_scheme_notranslate(nvlist_t *fmri, nvlist_t *auth __unused) { (void) nvlist_xdup(fmri, &fmri, &fmd.d_nva); return (fmri); } static ssize_t fmd_scheme_notsup_nvl2str(nvlist_t *fmri __unused, char *arg1 __unused, size_t arg2 __unused) { return (fmd_set_errno(EFMD_FMRI_NOTSUP)); } static int fmd_scheme_notsup(nvlist_t *fmri __unused) { return (fmd_set_errno(EFMD_FMRI_NOTSUP)); } static int fmd_scheme_notsup2(nvlist_t *fmri1 __unused, nvlist_t *fmri2 __unused) { return (fmd_set_errno(EFMD_FMRI_NOTSUP)); } static void fmd_scheme_vnop(void) { } static int fmd_scheme_nop(void) { return (0); } /* * Default values for the scheme ops. If a scheme function is not defined in * the module, then this operation is implemented using the default function. */ static const fmd_scheme_ops_t _fmd_scheme_default_ops = { .sop_init = fmd_scheme_nop, .sop_fini = fmd_scheme_vnop, .sop_nvl2str = fmd_scheme_notsup_nvl2str, .sop_expand = fmd_scheme_notsup, .sop_present = fmd_scheme_notsup, .sop_replaced = fmd_scheme_notsup, .sop_service_state = fmd_scheme_notsup, .sop_unusable = fmd_scheme_notsup, .sop_contains = fmd_scheme_notsup2, .sop_translate = fmd_scheme_notranslate }; static const fmd_scheme_ops_t _fmd_scheme_builtin_ops = { .sop_init = fmd_scheme_nop, .sop_fini = fmd_scheme_vnop, .sop_nvl2str = fmd_scheme_fmd_nvl2str, .sop_expand = fmd_scheme_notsup, .sop_present = fmd_scheme_fmd_present, .sop_replaced = fmd_scheme_fmd_replaced, .sop_service_state = fmd_scheme_fmd_service_state, .sop_unusable = fmd_scheme_fmd_unusable, .sop_contains = fmd_scheme_notsup2, .sop_translate = fmd_scheme_notranslate }; /* * Scheme ops descriptions. These names and offsets are used by the function * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t. */ static const fmd_scheme_opd_t _fmd_scheme_ops[] = { { "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) }, { "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) }, { "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) }, { "fmd_fmri_expand", offsetof(fmd_scheme_ops_t, sop_expand) }, { "fmd_fmri_present", offsetof(fmd_scheme_ops_t, sop_present) }, { "fmd_fmri_replaced", offsetof(fmd_scheme_ops_t, sop_replaced) }, { "fmd_fmri_service_state", offsetof(fmd_scheme_ops_t, sop_service_state) }, { "fmd_fmri_unusable", offsetof(fmd_scheme_ops_t, sop_unusable) }, { "fmd_fmri_contains", offsetof(fmd_scheme_ops_t, sop_contains) }, { "fmd_fmri_translate", offsetof(fmd_scheme_ops_t, sop_translate) }, { NULL, 0 } }; static fmd_scheme_t * fmd_scheme_create(const char *name) { fmd_scheme_t *sp = fmd_alloc(sizeof (fmd_scheme_t), FMD_SLEEP); (void) pthread_mutex_init(&sp->sch_lock, NULL); (void) pthread_cond_init(&sp->sch_cv, NULL); (void) pthread_mutex_init(&sp->sch_opslock, NULL); sp->sch_next = NULL; sp->sch_name = fmd_strdup(name, FMD_SLEEP); sp->sch_dlp = NULL; sp->sch_refs = 1; sp->sch_loaded = 0; sp->sch_ops = _fmd_scheme_default_ops; return (sp); } static void fmd_scheme_destroy(fmd_scheme_t *sp) { ASSERT(MUTEX_HELD(&sp->sch_lock)); ASSERT(sp->sch_refs == 0); if (sp->sch_dlp != NULL) { TRACE((FMD_DBG_FMRI, "dlclose scheme %s", sp->sch_name)); if (sp->sch_ops.sop_fini != NULL) sp->sch_ops.sop_fini(); (void) dlclose(sp->sch_dlp); } fmd_strfree(sp->sch_name); fmd_free(sp, sizeof (fmd_scheme_t)); } fmd_scheme_hash_t * fmd_scheme_hash_create(const char *rootdir, const char *dirpath) { fmd_scheme_hash_t *shp; char path[PATH_MAX]; fmd_scheme_t *sp; shp = fmd_alloc(sizeof (fmd_scheme_hash_t), FMD_SLEEP); (void) snprintf(path, sizeof (path), "%s/%s", rootdir, dirpath); shp->sch_dirpath = fmd_strdup(path, FMD_SLEEP); (void) pthread_rwlock_init(&shp->sch_rwlock, NULL); shp->sch_hashlen = fmd.d_str_buckets; shp->sch_hash = fmd_zalloc(sizeof (fmd_scheme_t *) * shp->sch_hashlen, FMD_SLEEP); sp = fmd_scheme_create(FM_FMRI_SCHEME_FMD); sp->sch_ops = _fmd_scheme_builtin_ops; sp->sch_loaded = FMD_B_TRUE; shp->sch_hash[fmd_strhash(sp->sch_name) % shp->sch_hashlen] = sp; return (shp); } void fmd_scheme_hash_destroy(fmd_scheme_hash_t *shp) { fmd_scheme_t *sp, *np; uint_t i; for (i = 0; i < shp->sch_hashlen; i++) { for (sp = shp->sch_hash[i]; sp != NULL; sp = np) { np = sp->sch_next; sp->sch_next = NULL; fmd_scheme_hash_release(shp, sp); } } fmd_free(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen); fmd_strfree(shp->sch_dirpath); fmd_free(shp, sizeof (fmd_scheme_hash_t)); } void fmd_scheme_hash_trygc(fmd_scheme_hash_t *shp) { fmd_scheme_t *sp, *np; uint_t i; if (shp == NULL || pthread_rwlock_trywrlock(&shp->sch_rwlock) != 0) return; /* failed to acquire lock: just skip garbage collect */ for (i = 0; i < shp->sch_hashlen; i++) { for (sp = shp->sch_hash[i]; sp != NULL; sp = np) { np = sp->sch_next; sp->sch_next = NULL; fmd_scheme_hash_release(shp, sp); } } bzero(shp->sch_hash, sizeof (fmd_scheme_t *) * shp->sch_hashlen); (void) pthread_rwlock_unlock(&shp->sch_rwlock); } static int fmd_scheme_rtld_init(fmd_scheme_t *sp) { const fmd_scheme_opd_t *opd; void *p; for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) { if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL) *(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p; } return (0); } fmd_scheme_t * fmd_scheme_hash_xlookup(fmd_scheme_hash_t *shp, const char *name, uint_t h) { fmd_scheme_t *sp; ASSERT(RW_LOCK_HELD(&shp->sch_rwlock)); for (sp = shp->sch_hash[h]; sp != NULL; sp = sp->sch_next) { if (strcmp(sp->sch_name, name) == 0) break; } return (sp); } /* * Lookup a scheme module by name and return with a reference placed on it. We * use the scheme hash to cache "negative" entries (e.g. missing modules) as * well so this function always returns successfully with a non-NULL scheme. * The caller is responsible for applying fmd_scheme_hash_release() afterward. */ fmd_scheme_t * fmd_scheme_hash_lookup(fmd_scheme_hash_t *shp, const char *name) { fmd_scheme_t *sp, *nsp = NULL; uint_t h; /* * Grab the hash lock as reader and look for the appropriate scheme. * If the scheme isn't yet loaded, allocate a new scheme and grab the * hash lock as writer to insert it (after checking again for it). */ (void) pthread_rwlock_rdlock(&shp->sch_rwlock); h = fmd_strhash(name) % shp->sch_hashlen; if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) { (void) pthread_rwlock_unlock(&shp->sch_rwlock); nsp = fmd_scheme_create(name); (void) pthread_rwlock_wrlock(&shp->sch_rwlock); if ((sp = fmd_scheme_hash_xlookup(shp, name, h)) == NULL) { nsp->sch_next = shp->sch_hash[h]; shp->sch_hash[h] = sp = nsp; } else { fmd_scheme_hash_release(shp, nsp); nsp = NULL; } } /* * Grab the scheme lock so it can't disappear and then drop the hash * lock so that other lookups in the scheme hash can proceed. */ (void) pthread_mutex_lock(&sp->sch_lock); (void) pthread_rwlock_unlock(&shp->sch_rwlock); /* * If we created the scheme, compute its path and try to load it. If * we found an existing scheme, wait until its loaded bit is set. Once * we're done with either operation, increment sch_refs and return. */ if (nsp != NULL) { char path[PATH_MAX]; (void) snprintf(path, sizeof (path), "%s/%s.so", shp->sch_dirpath, sp->sch_name); TRACE((FMD_DBG_FMRI, "dlopen scheme %s", sp->sch_name)); sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW); if (sp->sch_dlp == NULL) { fmd_error(EFMD_FMRI_SCHEME, "failed to load fmri scheme %s: %s\n", path, dlerror()); } else if (fmd_scheme_rtld_init(sp) != 0 || sp->sch_ops.sop_init() != 0) { fmd_error(EFMD_FMRI_SCHEME, "failed to initialize fmri scheme %s", path); (void) dlclose(sp->sch_dlp); sp->sch_dlp = NULL; sp->sch_ops = _fmd_scheme_default_ops; } sp->sch_loaded = FMD_B_TRUE; /* set regardless of success */ sp->sch_refs++; ASSERT(sp->sch_refs != 0); (void) pthread_cond_broadcast(&sp->sch_cv); (void) pthread_mutex_unlock(&sp->sch_lock); } else { while (!sp->sch_loaded) (void) pthread_cond_wait(&sp->sch_cv, &sp->sch_lock); sp->sch_refs++; ASSERT(sp->sch_refs != 0); (void) pthread_mutex_unlock(&sp->sch_lock); } return (sp); } /* * Release the hold on a scheme obtained using fmd_scheme_hash_lookup(). * We take 'shp' for symmetry and in case we need to use it in future work. */ /*ARGSUSED*/ void fmd_scheme_hash_release(fmd_scheme_hash_t *shp, fmd_scheme_t *sp) { (void) pthread_mutex_lock(&sp->sch_lock); ASSERT(sp->sch_refs != 0); if (--sp->sch_refs == 0) fmd_scheme_destroy(sp); else (void) pthread_mutex_unlock(&sp->sch_lock); } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_SCHEME_H #define _FMD_SCHEME_H #include #include #ifdef __cplusplus extern "C" { #endif /* * Scheme operations. These function pointers are filled in when the scheme * is loaded. New operations must be added here as well as to the default * operations declaration and initialization table in fmd_scheme.c. */ typedef struct fmd_scheme_ops { int (*sop_init)(void); void (*sop_fini)(void); ssize_t (*sop_nvl2str)(nvlist_t *, char *, size_t); int (*sop_expand)(nvlist_t *); int (*sop_present)(nvlist_t *); int (*sop_replaced)(nvlist_t *); int (*sop_service_state)(nvlist_t *); int (*sop_unusable)(nvlist_t *); int (*sop_contains)(nvlist_t *, nvlist_t *); nvlist_t *(*sop_translate)(nvlist_t *, nvlist_t *); } fmd_scheme_ops_t; typedef struct fmd_scheme_opd { const char *opd_name; /* symbol name of scheme function */ size_t opd_off; /* offset within fmd_scheme_ops_t */ } fmd_scheme_opd_t; typedef struct fmd_scheme { struct fmd_scheme *sch_next; /* next scheme on hash bucket chain */ char *sch_name; /* name of this scheme (fmri prefix) */ void *sch_dlp; /* libdl shared library handle */ pthread_mutex_t sch_lock; /* lock protecting cv, refs, loaded */ pthread_cond_t sch_cv; /* condition variable for sch_loaded */ uint_t sch_refs; /* scheme reference count */ uint_t sch_loaded; /* scheme has been loaded */ pthread_mutex_t sch_opslock; /* lock protecting non-init/fini ops */ fmd_scheme_ops_t sch_ops; /* scheme function pointers */ } fmd_scheme_t; typedef struct fmd_scheme_hash { pthread_rwlock_t sch_rwlock; /* rwlock protecting scheme hash */ fmd_scheme_t **sch_hash; /* hash bucket array of schemes */ uint_t sch_hashlen; /* length of hash bucket array */ char *sch_dirpath; /* directory path for schemes */ } fmd_scheme_hash_t; extern fmd_scheme_hash_t *fmd_scheme_hash_create(const char *, const char *); extern void fmd_scheme_hash_destroy(fmd_scheme_hash_t *); extern void fmd_scheme_hash_trygc(fmd_scheme_hash_t *); extern fmd_scheme_t *fmd_scheme_hash_lookup(fmd_scheme_hash_t *, const char *); extern void fmd_scheme_hash_release(fmd_scheme_hash_t *, fmd_scheme_t *); #ifdef __cplusplus } #endif #endif /* _FMD_SCHEME_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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include static struct { fmd_stat_t nosub; fmd_stat_t module; } self_stats = { { "nosub", FMD_TYPE_UINT64, "event classes with no subscribers seen" }, { "module", FMD_TYPE_UINT64, "error events received from fmd modules" }, }; typedef struct self_case { enum { SC_CLASS, SC_MODULE } sc_kind; char *sc_name; } self_case_t; static self_case_t * self_case_create(fmd_hdl_t *hdl, int kind, const char *name) { self_case_t *scp = fmd_hdl_alloc(hdl, sizeof (self_case_t), FMD_SLEEP); scp->sc_kind = kind; scp->sc_name = fmd_hdl_strdup(hdl, name, FMD_SLEEP); return (scp); } static void self_case_destroy(fmd_hdl_t *hdl, self_case_t *scp) { fmd_hdl_strfree(hdl, scp->sc_name); fmd_hdl_free(hdl, scp, sizeof (self_case_t)); } static fmd_case_t * self_case_lookup(fmd_hdl_t *hdl, int kind, const char *name) { fmd_case_t *cp = NULL; while ((cp = fmd_case_next(hdl, cp)) != NULL) { self_case_t *scp = fmd_case_getspecific(hdl, cp); if (scp->sc_kind == kind && strcmp(scp->sc_name, name) == 0) break; } return (cp); } /*ARGSUSED*/ static void self_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) { fmd_case_t *cp; nvlist_t *flt, *mod; char *name; int err = 0; /* * If we get an error report from another fmd module, then create a * case for the module and add the ereport to it. The error is either * from fmd_hdl_error() or from fmd_api_error(). If it is the latter, * fmd_module_error() will send another event of class EFMD_MOD_FAIL * when the module has failed, at which point we can solve the case. * We can also close the case on EFMD_MOD_CONF (bad config file). */ if (strcmp(class, fmd_errclass(EFMD_MODULE)) == 0 && nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &mod) == 0 && nvlist_lookup_string(mod, FM_FMRI_FMD_NAME, &name) == 0) { if ((cp = self_case_lookup(hdl, SC_MODULE, name)) == NULL) { cp = fmd_case_open(hdl, self_case_create(hdl, SC_MODULE, name)); } fmd_case_add_ereport(hdl, cp, ep); self_stats.module.fmds_value.ui64++; (void) nvlist_lookup_int32(nvl, FMD_ERR_MOD_ERRNO, &err); if (err != EFMD_MOD_FAIL && err != EFMD_MOD_CONF) return; /* module is still active, so keep case open */ if (fmd_case_solved(hdl, cp)) return; /* case is already closed but error in _fini */ class = err == EFMD_MOD_FAIL ? FMD_FLT_MOD : FMD_FLT_CONF; flt = fmd_protocol_fault(class, 100, mod, NULL, NULL, NULL); fmd_case_add_suspect(hdl, cp, flt); fmd_case_solve(hdl, cp); return; } /* * If we get an I/O DDI ereport, drop it for now until the I/O DE is * implemented and integrated. Existing drivers in O/N have bugs that * will trigger these and we don't want this producing FMD_FLT_NOSUB. */ if (strncmp(class, "ereport.io.ddi.", strlen("ereport.io.ddi.")) == 0) return; /* if we got a DDI ereport, drop it for now */ /* * If we get any other type of event then it is of a class for which * there are no subscribers. Some of these correspond to internal fmd * errors, which we ignore. Otherwise we keep one case per class and * use it to produce a message indicating that something is awry. */ if (strcmp(class, FM_LIST_SUSPECT_CLASS) == 0 || strcmp(class, FM_LIST_ISOLATED_CLASS) == 0 || strcmp(class, FM_LIST_UPDATED_CLASS) == 0 || strcmp(class, FM_LIST_RESOLVED_CLASS) == 0 || strcmp(class, FM_LIST_REPAIRED_CLASS) == 0 || strncmp(class, FM_FAULT_CLASS, strlen(FM_FAULT_CLASS)) == 0 || strncmp(class, FM_DEFECT_CLASS, strlen(FM_DEFECT_CLASS)) == 0) return; /* if no agents are present just drop list.* */ if (strncmp(class, FMD_ERR_CLASS, FMD_ERR_CLASS_LEN) == 0) return; /* if fmd itself produced the error just drop it */ if (strncmp(class, FMD_RSRC_CLASS, FMD_RSRC_CLASS_LEN) == 0) return; /* if fmd itself produced the event just drop it */ if (strncmp(class, SYSEVENT_RSRC_CLASS, SYSEVENT_RSRC_CLASS_LEN) == 0) return; /* sysvent resources are auto generated by fmd */ if (self_case_lookup(hdl, SC_CLASS, class) != NULL) return; /* case is already open against this class */ if (strncmp(class, FM_IREPORT_CLASS ".", sizeof (FM_IREPORT_CLASS)) == 0) return; /* no subscriber required for ireport.* */ cp = fmd_case_open(hdl, self_case_create(hdl, SC_CLASS, class)); fmd_case_add_ereport(hdl, cp, ep); self_stats.nosub.fmds_value.ui64++; flt = fmd_protocol_fault(FMD_FLT_NOSUB, 100, NULL, NULL, NULL, NULL); (void) nvlist_add_string(flt, "nosub_class", class); fmd_case_add_suspect(hdl, cp, flt); fmd_case_solve(hdl, cp); } static void self_close(fmd_hdl_t *hdl, fmd_case_t *cp) { self_case_destroy(hdl, fmd_case_getspecific(hdl, cp)); } static const fmd_hdl_ops_t self_ops = { self_recv, /* fmdo_recv */ NULL, /* fmdo_timeout */ self_close, /* fmdo_close */ NULL, /* fmdo_stats */ NULL, /* fmdo_gc */ }; void self_init(fmd_hdl_t *hdl) { fmd_module_t *mp = (fmd_module_t *)hdl; /* see below */ fmd_hdl_info_t info = { "Fault Manager Self-Diagnosis", "1.0", &self_ops, NULL }; /* * Unlike other modules, fmd-self-diagnosis has some special needs that * fall outside of what we want in the module API. Manually disable * checkpointing for this module by tweaking the mod_stats values. * The self-diagnosis world relates to fmd's running state and modules * which all change when it restarts, so don't bother w/ checkpointing. */ (void) pthread_mutex_lock(&mp->mod_stats_lock); mp->mod_stats->ms_ckpt_save.fmds_value.bool = FMD_B_FALSE; mp->mod_stats->ms_ckpt_restore.fmds_value.bool = FMD_B_FALSE; (void) pthread_mutex_unlock(&mp->mod_stats_lock); if (fmd_hdl_register(hdl, FMD_API_VERSION, &info) != 0) return; /* failed to register with fmd */ (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (self_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&self_stats); } void self_fini(fmd_hdl_t *hdl) { fmd_case_t *cp = NULL; while ((cp = fmd_case_next(hdl, cp)) != NULL) self_case_destroy(hdl, fmd_case_getspecific(hdl, cp)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include static fmd_serd_eng_t * fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t) { fmd_serd_eng_t *sgp = fmd_zalloc(sizeof (fmd_serd_eng_t), FMD_SLEEP); sgp->sg_name = fmd_strdup(name, FMD_SLEEP); sgp->sg_flags = FMD_SERD_DIRTY; sgp->sg_n = n; sgp->sg_t = t; return (sgp); } static void fmd_serd_eng_free(fmd_serd_eng_t *sgp) { fmd_serd_eng_reset(sgp); fmd_strfree(sgp->sg_name); fmd_free(sgp, sizeof (fmd_serd_eng_t)); } void fmd_serd_hash_create(fmd_serd_hash_t *shp) { shp->sh_hashlen = fmd.d_str_buckets; shp->sh_hash = fmd_zalloc(sizeof (void *) * shp->sh_hashlen, FMD_SLEEP); shp->sh_count = 0; } void fmd_serd_hash_destroy(fmd_serd_hash_t *shp) { fmd_serd_eng_t *sgp, *ngp; uint_t i; for (i = 0; i < shp->sh_hashlen; i++) { for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = ngp) { ngp = sgp->sg_next; fmd_serd_eng_free(sgp); } } fmd_free(shp->sh_hash, sizeof (void *) * shp->sh_hashlen); bzero(shp, sizeof (fmd_serd_hash_t)); } void fmd_serd_hash_apply(fmd_serd_hash_t *shp, fmd_serd_eng_f *func, void *arg) { fmd_serd_eng_t *sgp; uint_t i; for (i = 0; i < shp->sh_hashlen; i++) { for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next) func(sgp, arg); } } uint_t fmd_serd_hash_count(fmd_serd_hash_t *shp) { return (shp->sh_count); } int fmd_serd_hash_contains(fmd_serd_hash_t *shp, fmd_event_t *ep) { fmd_serd_eng_t *sgp; uint_t i; for (i = 0; i < shp->sh_hashlen; i++) { for (sgp = shp->sh_hash[i]; sgp != NULL; sgp = sgp->sg_next) { if (fmd_serd_eng_contains(sgp, ep)) { fmd_event_transition(ep, FMD_EVS_ACCEPTED); return (1); } } } return (0); } fmd_serd_eng_t * fmd_serd_eng_insert(fmd_serd_hash_t *shp, const char *name, uint_t n, hrtime_t t) { uint_t h = fmd_strhash(name) % shp->sh_hashlen; fmd_serd_eng_t *sgp = fmd_serd_eng_alloc(name, n, t); sgp->sg_next = shp->sh_hash[h]; shp->sh_hash[h] = sgp; shp->sh_count++; return (sgp); } fmd_serd_eng_t * fmd_serd_eng_lookup(fmd_serd_hash_t *shp, const char *name) { uint_t h = fmd_strhash(name) % shp->sh_hashlen; fmd_serd_eng_t *sgp; for (sgp = shp->sh_hash[h]; sgp != NULL; sgp = sgp->sg_next) { if (strcmp(name, sgp->sg_name) == 0) return (sgp); } return (NULL); } void fmd_serd_eng_delete(fmd_serd_hash_t *shp, const char *name) { uint_t h = fmd_strhash(name) % shp->sh_hashlen; fmd_serd_eng_t *sgp, **pp = &shp->sh_hash[h]; for (sgp = *pp; sgp != NULL; sgp = sgp->sg_next) { if (strcmp(sgp->sg_name, name) != 0) pp = &sgp->sg_next; else break; } if (sgp != NULL) { *pp = sgp->sg_next; fmd_serd_eng_free(sgp); ASSERT(shp->sh_count != 0); shp->sh_count--; } } static void fmd_serd_eng_discard(fmd_serd_eng_t *sgp, fmd_serd_elem_t *sep) { fmd_list_delete(&sgp->sg_list, sep); sgp->sg_count--; fmd_event_rele(sep->se_event); fmd_free(sep, sizeof (fmd_serd_elem_t)); } int fmd_serd_eng_contains(fmd_serd_eng_t *sgp, fmd_event_t *ep) { fmd_serd_elem_t *sep; for (sep = fmd_list_next(&sgp->sg_list); sep != NULL; sep = fmd_list_next(sep)) { if (fmd_event_equal(sep->se_event, ep)) return (1); } return (0); } int fmd_serd_eng_record(void *ptr, fmd_event_t *ep) { fmd_serd_eng_t *sgp = ptr; fmd_serd_elem_t *sep, *oep; /* * If the fired flag is already set, return false and discard the * event. This means that the caller will only see the engine "fire" * once until fmd_serd_eng_reset() is called. The fmd_serd_eng_fired() * function can also be used in combination with fmd_serd_eng_record(). */ if (sgp->sg_flags & FMD_SERD_FIRED) return (FMD_B_FALSE); while (sgp->sg_count > sgp->sg_n) fmd_serd_eng_discard(sgp, fmd_list_next(&sgp->sg_list)); fmd_event_hold(ep); fmd_event_transition(ep, FMD_EVS_ACCEPTED); sep = fmd_alloc(sizeof (fmd_serd_elem_t), FMD_SLEEP); sep->se_event = ep; fmd_list_append(&sgp->sg_list, sep); sgp->sg_count++; /* * Pick up the oldest element pointer for comparison to 'sep'. We must * do this after adding 'sep' because 'oep' and 'sep' can be the same. */ oep = fmd_list_next(&sgp->sg_list); if (sgp->sg_count > sgp->sg_n && fmd_event_delta(oep->se_event, sep->se_event) <= sgp->sg_t) { sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY; return (FMD_B_TRUE); } sgp->sg_flags |= FMD_SERD_DIRTY; return (FMD_B_FALSE); } int fmd_serd_eng_fired(fmd_serd_eng_t *sgp) { return (sgp->sg_flags & FMD_SERD_FIRED); } int fmd_serd_eng_empty(fmd_serd_eng_t *sgp) { return (sgp->sg_count == 0); } void fmd_serd_eng_reset(fmd_serd_eng_t *sgp) { while (sgp->sg_count != 0) fmd_serd_eng_discard(sgp, fmd_list_next(&sgp->sg_list)); sgp->sg_flags &= ~FMD_SERD_FIRED; sgp->sg_flags |= FMD_SERD_DIRTY; } void fmd_serd_eng_gc(fmd_serd_eng_t *sgp, void *arg __unused) { fmd_serd_elem_t *sep, *nep; hrtime_t hrt; if (sgp->sg_count == 0 || (sgp->sg_flags & FMD_SERD_FIRED)) return; /* no garbage collection needed if empty or fired */ sep = fmd_list_prev(&sgp->sg_list); hrt = fmd_event_hrtime(sep->se_event) - sgp->sg_t; for (sep = fmd_list_next(&sgp->sg_list); sep != NULL; sep = nep) { if (fmd_event_hrtime(sep->se_event) >= hrt) break; /* sep and subsequent events are all within T */ nep = fmd_list_next(sep); fmd_serd_eng_discard(sgp, sep); sgp->sg_flags |= FMD_SERD_DIRTY; } } void fmd_serd_eng_commit(fmd_serd_eng_t *sgp, void *arg __unused) { fmd_serd_elem_t *sep; if (!(sgp->sg_flags & FMD_SERD_DIRTY)) return; /* engine has not changed since last commit */ for (sep = fmd_list_next(&sgp->sg_list); sep != NULL; sep = fmd_list_next(sep)) fmd_event_commit(sep->se_event); sgp->sg_flags &= ~FMD_SERD_DIRTY; } void fmd_serd_eng_clrdirty(fmd_serd_eng_t *sgp, void *arg __unused) { sgp->sg_flags &= ~FMD_SERD_DIRTY; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_SERD_H #define _FMD_SERD_H #ifdef __cplusplus extern "C" { #endif #include #include typedef struct fmd_serd_elem { fmd_list_t se_list; /* linked list forward/back pointers */ fmd_event_t *se_event; /* pointer to corresponding event */ } fmd_serd_elem_t; typedef struct fmd_serd_eng { char *sg_name; /* string name for this engine */ struct fmd_serd_eng *sg_next; /* next engine on hash chain */ fmd_list_t sg_list; /* list of fmd_serd_elem_t's */ uint_t sg_count; /* count of events in sg_list */ uint_t sg_flags; /* engine flags (see below) */ uint_t sg_n; /* engine N parameter (event count) */ hrtime_t sg_t; /* engine T parameter (nanoseconds) */ } fmd_serd_eng_t; #define FMD_SERD_FIRED 0x1 /* error rate has exceeded threshold */ #define FMD_SERD_DIRTY 0x2 /* engine needs to be checkpointed */ typedef void fmd_serd_eng_f(fmd_serd_eng_t *, void *); typedef struct fmd_serd_hash { fmd_serd_eng_t **sh_hash; /* hash bucket array for buffers */ uint_t sh_hashlen; /* length of hash bucket array */ uint_t sh_count; /* count of engines in hash */ } fmd_serd_hash_t; extern void fmd_serd_hash_create(fmd_serd_hash_t *); extern void fmd_serd_hash_destroy(fmd_serd_hash_t *); extern void fmd_serd_hash_apply(fmd_serd_hash_t *, fmd_serd_eng_f *, void *); extern uint_t fmd_serd_hash_count(fmd_serd_hash_t *); extern int fmd_serd_hash_contains(fmd_serd_hash_t *, fmd_event_t *); extern fmd_serd_eng_t *fmd_serd_eng_insert(fmd_serd_hash_t *, const char *, uint32_t, hrtime_t); extern fmd_serd_eng_t *fmd_serd_eng_lookup(fmd_serd_hash_t *, const char *); extern void fmd_serd_eng_delete(fmd_serd_hash_t *, const char *); extern int fmd_serd_eng_contains(fmd_serd_eng_t *, fmd_event_t *); extern int fmd_serd_eng_record(void *, fmd_event_t *); extern int fmd_serd_eng_fired(fmd_serd_eng_t *); extern int fmd_serd_eng_empty(fmd_serd_eng_t *); extern void fmd_serd_eng_reset(fmd_serd_eng_t *); extern void fmd_serd_eng_gc(fmd_serd_eng_t *, void *); extern void fmd_serd_eng_commit(fmd_serd_eng_t *, void*); extern void fmd_serd_eng_clrdirty(fmd_serd_eng_t *, void*); #ifdef __cplusplus } #endif #endif /* _FMD_SERD_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include char * fmd_strdup(const char *s, int flags) { char *p; if (s != NULL) p = fmd_alloc(strlen(s) + 1, flags); else p = NULL; if (p != NULL) (void) strcpy(p, s); return (p); } void fmd_strfree(char *s) { if (s != NULL) fmd_free(s, strlen(s) + 1); } const char * fmd_strbasename(const char *s) { const char *p = strrchr(s, '/'); if (p == NULL) return (s); return (++p); } char * fmd_strdirname(char *s) { static char slash[] = "/"; static char dot[] = "."; char *p; if (s == NULL || *s == '\0') return (dot); for (p = s + strlen(s); p != s && *--p == '/'; ) continue; if (p == s && *p == '/') return (slash); while (p != s) { if (*--p == '/') { while (*p == '/' && p != s) p--; *++p = '\0'; return (s); } } return (dot); } ulong_t fmd_strhash(const char *key) { ulong_t g, h = 0; const char *p; for (p = key; *p != '\0'; p++) { h = (h << 4) + *p; if ((g = (h & 0xf0000000)) != 0) { h ^= (g >> 24); h ^= g; } } return (h); } /* * Transform string s inline, converting each embedded C escape sequence string * to the corresponding character. For example, the substring "\n" is replaced * by an inline '\n' character. The length of the resulting string is returned. */ size_t fmd_stresc2chr(char *s) { char *p, *q, c; int esc = 0; int x; for (p = q = s; (c = *p) != '\0'; p++) { if (esc) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c -= '0'; p++; if (*p >= '0' && *p <= '7') { c = c * 8 + *p++ - '0'; if (*p >= '0' && *p <= '7') c = c * 8 + *p - '0'; else p--; } else p--; *q++ = c; break; case 'a': *q++ = '\a'; break; case 'b': *q++ = '\b'; break; case 'f': *q++ = '\f'; break; case 'n': *q++ = '\n'; break; case 'r': *q++ = '\r'; break; case 't': *q++ = '\t'; break; case 'v': *q++ = '\v'; break; case 'x': for (x = 0; (c = *++p) != '\0'; ) { if (c >= '0' && c <= '9') x = x * 16 + c - '0'; else if (c >= 'a' && c <= 'f') x = x * 16 + c - 'a' + 10; else if (c >= 'A' && c <= 'F') x = x * 16 + c - 'A' + 10; else break; } *q++ = (char)x; p--; break; case '"': case '\\': *q++ = c; break; default: *q++ = '\\'; *q++ = c; } esc = 0; } else { if ((esc = c == '\\') == 0) *q++ = c; } } *q = '\0'; return ((size_t)(q - s)); } /* * We require that identifiers for buffers, statistics, and properties conform * to the regular expression [a-zA-Z0-9\-_.]. If check_prefixes is set, we * also flag strings that begin with a set of prefixes reserved for use by fmd. */ const char * fmd_strbadid(const char *s, int check_prefixes) { const char *s0 = s; int c = *s++; while ((c = *s++) != '\0') { if (!isupper(c) && !islower(c) && !isdigit(c) && c != '-' && c != '_' && c != '.') return (s - 1); } if (check_prefixes && (s0[0] == '_' || s0[0] == '.' || strncmp(s0, "fmd_", 4) == 0 || strncmp(s0, "FMD_", 4) == 0 || strncmp(s0, "fmd.", 4) == 0 || strncmp(s0, "FMD.", 4) == 0)) return (s0); return (NULL); } int fmd_strmatch(const char *s, const char *p) { char c; if (p == NULL) return (0); if (s == NULL) s = ""; /* treat NULL string as the empty string */ do { if ((c = *p++) == '\0') return (*s == '\0'); if (c == '*') { while (*p == '*') p++; /* consecutive *'s can be collapsed */ if (*p == '\0') return (1); while (*s != '\0') { if (fmd_strmatch(s++, p) != 0) return (1); } return (0); } } while (c == *s++); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_STRING_H #define _FMD_STRING_H #include #include #ifdef __cplusplus extern "C" { #endif #include extern char *fmd_strdup(const char *, int); extern void fmd_strfree(char *); extern const char *fmd_strbasename(const char *); extern char *fmd_strdirname(char *); extern ulong_t fmd_strhash(const char *); extern size_t fmd_stresc2chr(char *); extern const char *fmd_strbadid(const char *, int); extern int fmd_strmatch(const char *, const char *); #ifdef __cplusplus } #endif #endif /* _FMD_STRING_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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int fmd_assert(const char *expr, const char *file, int line) { fmd_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr); /*NOTREACHED*/ return (0); } /* * To implement a reasonable panic() equivalent for fmd, we atomically bump a * global counter of calls to fmd_vpanic() and attempt to print a panic message * to stderr and dump core as a result of raising SIGABRT. This function must * not attempt to grab any locks so that it can be called from any fmd code. */ void fmd_vpanic(const char *format, va_list ap) { int oserr = errno; pthread_t tid = pthread_self(); fmd_thread_t *tp; char msg[BUFSIZ]; size_t len; /* * If this is not the first call to fmd_vpanic(), then check d_panictid * to see if we are the panic thread. If so, then proceed directly to * abort() because we have recursively panicked. If not, then pause() * indefinitely waiting for the panic thread to terminate the daemon. */ if (atomic_add_32_nv(&fmd.d_panicrefs, 1) != 1) { while (fmd.d_panictid != tid) (void) pause(); goto abort; } /* * Use fmd.d_pid != 0 as a cheap test to see if fmd.d_key is valid * (i.e. we're after fmd_create() and before fmd_destroy()). */ if (fmd.d_pid != 0 && (tp = pthread_getspecific(fmd.d_key)) != NULL) (void) tp->thr_trfunc(tp->thr_trdata, FMD_DBG_ERR, format, ap); fmd.d_panicstr = msg; fmd.d_panictid = tid; (void) snprintf(msg, sizeof (msg), "%s: ABORT: ", fmd.d_pname ? fmd.d_pname : "fmd"); len = strlen(msg); (void) vsnprintf(msg + len, sizeof (msg) - len, format, ap); if (strchr(format, '\n') == NULL) { len = strlen(msg); (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n", fmd_strerror(oserr)); } (void) write(STDERR_FILENO, msg, strlen(msg)); abort: abort(); _exit(FMD_EXIT_ERROR); } /*PRINTFLIKE1*/ void fmd_panic(const char *format, ...) { va_list ap; va_start(ap, format); fmd_vpanic(format, ap); va_end(ap); } void fmd_verror(int err, const char *format, va_list ap) { int oserr = errno; fmd_thread_t *tp; nvlist_t *nvl; fmd_event_t *e; char *class; if ((tp = pthread_getspecific(fmd.d_key)) != NULL) { (void) tp->thr_trfunc(tp->thr_trdata, FMD_DBG_ERR, format, ap); tp->thr_errdepth++; } (void) pthread_mutex_lock(&fmd.d_err_lock); if (fmd.d_errstats != NULL && err >= EFMD_UNKNOWN && err < EFMD_END) fmd.d_errstats[err - EFMD_UNKNOWN].fmds_value.ui64++; if (fmd.d_fg || !fmd.d_running) { (void) fprintf(stderr, "%s: ", fmd.d_pname); (void) vfprintf(stderr, format, ap); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, ": %s\n", fmd_strerror(oserr)); } (void) pthread_mutex_unlock(&fmd.d_err_lock); /* * If we are at error nesting level one and running in the background, * log the error as an ereport to our own log and dispatch it. If the * FMD_LF_BUSY flag is set, we can't attempt to log the event because * a replay is running and we will deadlock on ourself in log_append. */ if (!fmd.d_fg && fmd.d_running && tp != NULL && tp->thr_errdepth == 1 && (nvl = fmd_protocol_fmderror(err, format, ap)) != NULL) { (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); (void) pthread_rwlock_rdlock(&fmd.d_log_lock); if (!(fmd.d_errlog->log_flags & FMD_LF_BUSY)) fmd_log_append(fmd.d_errlog, e, NULL); (void) pthread_rwlock_unlock(&fmd.d_log_lock); fmd_dispq_dispatch(fmd.d_disp, e, class); } if (tp != NULL) tp->thr_errdepth--; if (err == EFMD_EXIT) { int core = 0; (void) fmd_conf_getprop(fmd.d_conf, "core", &core); if (core) fmd_panic("forcing core dump at user request\n"); exit(FMD_EXIT_ERROR); } } /*PRINTFLIKE2*/ void fmd_error(int err, const char *format, ...) { va_list ap; va_start(ap, format); fmd_verror(err, format, ap); va_end(ap); } void fmd_vdprintf(int mask, const char *format, va_list ap) { fmd_thread_t *tp; char *msg; size_t len; char c; if (!(fmd.d_fmd_debug & mask)) return; /* none of the specified modes are enabled */ if ((tp = pthread_getspecific(fmd.d_key)) != NULL) (void) tp->thr_trfunc(tp->thr_trdata, mask, format, ap); if (fmd.d_fmd_dbout == 0) return; /* no debugging output sinks are enabled */ len = vsnprintf(&c, 1, format, ap); msg = alloca(len + 2); (void) vsnprintf(msg, len + 1, format, ap); if (msg[len - 1] != '\n') (void) strcpy(&msg[len], "\n"); if (fmd.d_fmd_dbout & FMD_DBOUT_STDERR) { (void) pthread_mutex_lock(&fmd.d_err_lock); (void) fprintf(stderr, "%s DEBUG: %s", fmd.d_pname, msg); (void) pthread_mutex_unlock(&fmd.d_err_lock); } if (fmd.d_fmd_dbout & FMD_DBOUT_SYSLOG) { syslog(LOG_DEBUG | LOG_DAEMON, "%s DEBUG: %s", fmd.d_pname, msg); } } /*PRINTFLIKE2*/ void fmd_dprintf(int mask, const char *format, ...) { va_list ap; va_start(ap, format); fmd_vdprintf(mask, format, ap); va_end(ap); } /* * The fmd_trace.c routines set tr_file and tr_line to NULL and 0 respectively. * If they are invoked from a macro (see ) this tail function is * called as part of the TRACE() macro to fill in these fields from the cpp * macro values for __FILE__ and __LINE__. No locking is needed because all * trace buffers are allocated separately for each fmd thread. */ void fmd_trace_cpp(void *ptr, const char *file, int line) { fmd_tracerec_t *trp = ptr; if (trp != NULL) { trp->tr_file = file; trp->tr_line = line; } } /* * The fmd_trace() function is the wrapper for the tracing routines provided in * fmd_trace.c. It is invoked by the TRACE() macro in , and uses * the per-thread trace buffer set up in fmd_thread.c to trace debugging info. */ /*PRINTFLIKE2*/ void * fmd_trace(uint_t tag, const char *format, ...) { fmd_thread_t *tp = pthread_getspecific(fmd.d_key); va_list ap; void *trp; if (tp == NULL) return (NULL); /* drop trace record if not ready yet */ va_start(ap, format); trp = tp->thr_trfunc(tp->thr_trdata, tag, format, ap); va_end(ap); return (trp); } const char * fmd_ea_strerror(int err) { switch (err) { case EXR_OK: return ("no exacct error"); case EXR_SYSCALL_FAIL: return (fmd_strerror(errno)); case EXR_CORRUPT_FILE: return ("file corruption detected"); case EXR_EOF: return ("end-of-file reached"); case EXR_NO_CREATOR: return ("creator tag mismatch"); case EXR_INVALID_BUF: return ("invalid unpack buffer"); case EXR_NOTSUPP: return ("exacct operation not supported"); case EXR_UNKN_VERSION: return ("unsupported exacct file version"); case EXR_INVALID_OBJ: return ("invalid exacct object"); default: return ("unknown exacct error"); } } /* * Create a local ENA value for fmd-generated ereports. We use ENA Format 1 * with the low bits of gethrtime() and pthread_self() as the processor ID. */ uint64_t fmd_ena(void) { hrtime_t hrt = fmd_time_gethrtime(); return ((uint64_t)((FM_ENA_FMT1 & ENA_FORMAT_MASK) | ((pthread_self() << ENA_FMT1_CPUID_SHFT) & ENA_FMT1_CPUID_MASK) | ((hrt << ENA_FMT1_TIME_SHFT) & ENA_FMT1_TIME_MASK))); } /* * fmd_ntz32() computes the number of trailing zeroes. The algorithm here is * from "Hacker's Delight" by Henry Warren, Jr. */ uint32_t fmd_ntz32(uint32_t x) { uint_t n = 1; if (x == 0) return (32); if ((x & 0xFFFF) == 0) { n += 16; x >>= 16; } if ((x & 0xFF) == 0) { n += 8; x >>= 8; } if ((x & 0xF) == 0) { n += 4; x >>= 4; } if ((x & 0x3) == 0) { n += 2; x >>= 2; } return (n - (x & 1)); } /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_SUBR_H #define _FMD_SUBR_H #include #include #include #ifdef __cplusplus extern "C" { #endif #ifdef DEBUG extern int fmd_assert(const char *, const char *, int); #define ASSERT(x) ((void)((x) || fmd_assert(#x, __FILE__, __LINE__))) #else #define ASSERT(x) #endif extern void fmd_vpanic(const char *, va_list); extern void fmd_panic(const char *, ...); extern void fmd_verror(int, const char *, va_list); extern void fmd_error(int, const char *, ...); #define FMD_DBG_HELP 0x0001 /* display list of debugging modes and exit */ #define FMD_DBG_ERR 0x0002 /* enable error handling debug messages */ #define FMD_DBG_MOD 0x0004 /* enable module subsystem debug messages */ #define FMD_DBG_DISP 0x0008 /* enable dispq subsystem debug messages */ #define FMD_DBG_XPRT 0x0010 /* enable transport subsystem debug messages */ #define FMD_DBG_EVT 0x0020 /* enable event subsystem debug messages */ #define FMD_DBG_LOG 0x0040 /* enable log subsystem debug messages */ #define FMD_DBG_TMR 0x0080 /* enable timer subsystem debug messages */ #define FMD_DBG_FMRI 0x0100 /* enable fmri subsystem debug messages */ #define FMD_DBG_ASRU 0x0200 /* enable asru subsystem debug messages */ #define FMD_DBG_CASE 0x0400 /* enable case subsystem debug messages */ #define FMD_DBG_CKPT 0x0800 /* enable checkpoint debug messages */ #define FMD_DBG_RPC 0x1000 /* enable rpc service debug messages */ #define FMD_DBG_TRACE 0x2000 /* display matching TRACE() calls */ #define FMD_DBG_ALL 0x1ffe /* enable all modes except for HELP, TRACE */ extern void fmd_vdprintf(int, const char *, va_list); extern void fmd_dprintf(int, const char *, ...); extern void fmd_trace_cpp(void *, const char *, int); extern void *fmd_trace(uint_t, const char *, ...); #ifdef DEBUG #define TRACE(args) { fmd_trace_cpp(fmd_trace args, __FILE__, __LINE__); } #else #define TRACE(args) #endif extern const char *fmd_ea_strerror(int); extern uint64_t fmd_ena(void); extern uint32_t fmd_ntz32(uint32_t); #ifdef __cplusplus } #endif #endif /* _FMD_SUBR_H */ /* * Please do not edit this file. * It was generated using rpcgen. */ #include "fmd_rpc_adm.h" #include #include /* getenv, exit */ #include #include /* for pmap_unset */ #include /* strcmp */ #include /* setsid */ #include #include #include #include /* rlimit */ #include #ifndef SIG_PF #define SIG_PF void(*)(int) #endif #ifdef DEBUG #define RPC_SVC_FG #endif #define _RPCSVC_CLOSEDOWN 120 #include extern void fmd_adm_1(struct svc_req *, SVCXPRT *); extern bool_t xdr_fmd_stat(XDR *, struct fmd_stat *); #undef RW_READ_HELD #undef RW_WRITE_HELD #undef RW_LOCK_HELD #undef MUTEX_HELD extern int _rpcpmstart; /* Started by a port monitor ? */ /* States a server can be in wrt request */ #define _IDLE 0 #define _SERVED 1 /* LINTED static unused if no main */ static int _rpcsvcstate = _IDLE; /* Set when a request is serviced */ static int _rpcsvccount = 0; /* Number of requests being serviced */ extern mutex_t _svcstate_lock; /* lock for _rpcsvcstate, _rpcsvccount */ #if defined(RPC_MSGOUT) extern void RPC_MSGOUT(const char *, ...); #else /* defined(RPC_MSGOUT) */ static void RPC_MSGOUT(const char *fmt, char *msg) { #ifdef RPC_SVC_FG if (_rpcpmstart) syslog(LOG_ERR, fmt, msg); else { (void) fprintf(stderr, fmt, msg); (void) putc('\n', stderr); } #else syslog(LOG_ERR, fmt, msg); #endif } #endif /* defined(RPC_MSGOUT) */ /* ARGSUSED */ int _fmd_adm_modinfo_1( void *argp, struct fmd_rpc_modlist *result, struct svc_req *rqstp) { return (fmd_adm_modinfo_1_svc(result, rqstp)); } int _fmd_adm_modcstat_1( char * *argp, struct fmd_rpc_modstat *result, struct svc_req *rqstp) { return (fmd_adm_modcstat_1_svc(*argp, result, rqstp)); } int _fmd_adm_moddstat_1( char * *argp, struct fmd_rpc_modstat *result, struct svc_req *rqstp) { return (fmd_adm_moddstat_1_svc(*argp, result, rqstp)); } /* ARGSUSED */ int _fmd_adm_modgstat_1( void *argp, struct fmd_rpc_modstat *result, struct svc_req *rqstp) { return (fmd_adm_modgstat_1_svc(result, rqstp)); } int _fmd_adm_modload_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_modload_1_svc(*argp, result, rqstp)); } int _fmd_adm_modunload_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_modunload_1_svc(*argp, result, rqstp)); } int _fmd_adm_modreset_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_modreset_1_svc(*argp, result, rqstp)); } int _fmd_adm_modgc_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_modgc_1_svc(*argp, result, rqstp)); } int _fmd_adm_rsrclist_1( bool_t *argp, struct fmd_rpc_rsrclist *result, struct svc_req *rqstp) { return (fmd_adm_rsrclist_1_svc(*argp, result, rqstp)); } int _fmd_adm_rsrcinfo_1( char * *argp, struct fmd_rpc_rsrcinfo *result, struct svc_req *rqstp) { return (fmd_adm_rsrcinfo_1_svc(*argp, result, rqstp)); } int _fmd_adm_rsrcflush_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_rsrcflush_1_svc(*argp, result, rqstp)); } int _fmd_adm_rsrcrepaired_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_rsrcrepaired_1_svc(*argp, result, rqstp)); } int _fmd_adm_serdinfo_old_1( char * *argp, struct fmd_rpc_serdlist *result, struct svc_req *rqstp) { return (fmd_adm_serdinfo_old_1_svc(*argp, result, rqstp)); } int _fmd_adm_serdreset_1( fmd_adm_serdreset_1_argument *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_serdreset_1_svc( argp->arg1, argp->arg2, result, rqstp)); } int _fmd_adm_logrotate_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_logrotate_1_svc(*argp, result, rqstp)); } int _fmd_adm_caserepair_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_caserepair_1_svc(*argp, result, rqstp)); } /* ARGSUSED */ int _fmd_adm_xprtlist_1( void *argp, struct fmd_rpc_xprtlist *result, struct svc_req *rqstp) { return (fmd_adm_xprtlist_1_svc(result, rqstp)); } int _fmd_adm_xprtstat_1( int32_t *argp, struct fmd_rpc_modstat *result, struct svc_req *rqstp) { return (fmd_adm_xprtstat_1_svc(*argp, result, rqstp)); } /* ARGSUSED */ int _fmd_adm_caselist_1( void *argp, struct fmd_rpc_caselist *result, struct svc_req *rqstp) { return (fmd_adm_caselist_1_svc(result, rqstp)); } int _fmd_adm_caseinfo_1( char * *argp, struct fmd_rpc_caseinfo *result, struct svc_req *rqstp) { return (fmd_adm_caseinfo_1_svc(*argp, result, rqstp)); } int _fmd_adm_rsrcreplaced_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_rsrcreplaced_1_svc(*argp, result, rqstp)); } int _fmd_adm_rsrcacquit_1( fmd_adm_rsrcacquit_1_argument *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_rsrcacquit_1_svc( argp->arg1, argp->arg2, result, rqstp)); } int _fmd_adm_caseacquit_1( char * *argp, int *result, struct svc_req *rqstp) { return (fmd_adm_caseacquit_1_svc(*argp, result, rqstp)); } int _fmd_adm_serdlist_1( char * *argp, struct fmd_rpc_serdlist *result, struct svc_req *rqstp) { return (fmd_adm_serdlist_1_svc(*argp, result, rqstp)); } int _fmd_adm_serdinfo_1( fmd_adm_serdinfo_1_argument *argp, struct fmd_rpc_serdinfo *result, struct svc_req *rqstp) { return (fmd_adm_serdinfo_1_svc( argp->arg1, argp->arg2, result, rqstp)); } void fmd_adm_1(struct svc_req *rqstp, register SVCXPRT *transp) { union { char *fmd_adm_modcstat_1_arg; char *fmd_adm_moddstat_1_arg; char *fmd_adm_modload_1_arg; char *fmd_adm_modunload_1_arg; char *fmd_adm_modreset_1_arg; char *fmd_adm_modgc_1_arg; bool_t fmd_adm_rsrclist_1_arg; char *fmd_adm_rsrcinfo_1_arg; char *fmd_adm_rsrcflush_1_arg; char *fmd_adm_rsrcrepaired_1_arg; char *fmd_adm_serdinfo_old_1_arg; fmd_adm_serdreset_1_argument fmd_adm_serdreset_1_arg; char *fmd_adm_logrotate_1_arg; char *fmd_adm_caserepair_1_arg; int32_t fmd_adm_xprtstat_1_arg; char *fmd_adm_caseinfo_1_arg; char *fmd_adm_rsrcreplaced_1_arg; fmd_adm_rsrcacquit_1_argument fmd_adm_rsrcacquit_1_arg; char *fmd_adm_caseacquit_1_arg; char *fmd_adm_serdlist_1_arg; fmd_adm_serdinfo_1_argument fmd_adm_serdinfo_1_arg; } argument; union { struct fmd_rpc_modlist fmd_adm_modinfo_1_res; struct fmd_rpc_modstat fmd_adm_modcstat_1_res; struct fmd_rpc_modstat fmd_adm_moddstat_1_res; struct fmd_rpc_modstat fmd_adm_modgstat_1_res; int fmd_adm_modload_1_res; int fmd_adm_modunload_1_res; int fmd_adm_modreset_1_res; int fmd_adm_modgc_1_res; struct fmd_rpc_rsrclist fmd_adm_rsrclist_1_res; struct fmd_rpc_rsrcinfo fmd_adm_rsrcinfo_1_res; int fmd_adm_rsrcflush_1_res; int fmd_adm_rsrcrepaired_1_res; struct fmd_rpc_serdlist fmd_adm_serdinfo_old_1_res; int fmd_adm_serdreset_1_res; int fmd_adm_logrotate_1_res; int fmd_adm_caserepair_1_res; struct fmd_rpc_xprtlist fmd_adm_xprtlist_1_res; struct fmd_rpc_modstat fmd_adm_xprtstat_1_res; struct fmd_rpc_caselist fmd_adm_caselist_1_res; struct fmd_rpc_caseinfo fmd_adm_caseinfo_1_res; int fmd_adm_rsrcreplaced_1_res; int fmd_adm_rsrcacquit_1_res; int fmd_adm_caseacquit_1_res; struct fmd_rpc_serdlist fmd_adm_serdlist_1_res; struct fmd_rpc_serdinfo fmd_adm_serdinfo_1_res; } result; bool_t retval; xdrproc_t _xdr_argument, _xdr_result; bool_t (*local)(char *, void *, struct svc_req *); (void) mutex_lock(&_svcstate_lock); _rpcsvccount++; (void) mutex_unlock(&_svcstate_lock); switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ case FMD_ADM_MODINFO: _xdr_argument = (xdrproc_t) xdr_void; _xdr_result = (xdrproc_t) xdr_fmd_rpc_modlist; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modinfo_1; break; case FMD_ADM_MODCSTAT: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_fmd_rpc_modstat; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modcstat_1; break; case FMD_ADM_MODDSTAT: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_fmd_rpc_modstat; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_moddstat_1; break; case FMD_ADM_MODGSTAT: _xdr_argument = (xdrproc_t) xdr_void; _xdr_result = (xdrproc_t) xdr_fmd_rpc_modstat; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modgstat_1; break; case FMD_ADM_MODLOAD: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modload_1; break; case FMD_ADM_MODUNLOAD: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modunload_1; break; case FMD_ADM_MODRESET: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modreset_1; break; case FMD_ADM_MODGC: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_modgc_1; break; case FMD_ADM_RSRCLIST: _xdr_argument = (xdrproc_t) xdr_bool; _xdr_result = (xdrproc_t) xdr_fmd_rpc_rsrclist; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_rsrclist_1; break; case FMD_ADM_RSRCINFO: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_fmd_rpc_rsrcinfo; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_rsrcinfo_1; break; case FMD_ADM_RSRCFLUSH: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_rsrcflush_1; break; case FMD_ADM_RSRCREPAIRED: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_rsrcrepaired_1; break; case FMD_ADM_SERDINFO_OLD: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_fmd_rpc_serdlist; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_serdinfo_old_1; break; case FMD_ADM_SERDRESET: _xdr_argument = (xdrproc_t) xdr_fmd_adm_serdreset_1_argument; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_serdreset_1; break; case FMD_ADM_LOGROTATE: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_logrotate_1; break; case FMD_ADM_CASEREPAIR: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_caserepair_1; break; case FMD_ADM_XPRTLIST: _xdr_argument = (xdrproc_t) xdr_void; _xdr_result = (xdrproc_t) xdr_fmd_rpc_xprtlist; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_xprtlist_1; break; case FMD_ADM_XPRTSTAT: _xdr_argument = (xdrproc_t) xdr_int32_t; _xdr_result = (xdrproc_t) xdr_fmd_rpc_modstat; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_xprtstat_1; break; case FMD_ADM_CASELIST: _xdr_argument = (xdrproc_t) xdr_void; _xdr_result = (xdrproc_t) xdr_fmd_rpc_caselist; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_caselist_1; break; case FMD_ADM_CASEINFO: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_fmd_rpc_caseinfo; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_caseinfo_1; break; case FMD_ADM_RSRCREPLACED: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_rsrcreplaced_1; break; case FMD_ADM_RSRCACQUIT: _xdr_argument = (xdrproc_t) xdr_fmd_adm_rsrcacquit_1_argument; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_rsrcacquit_1; break; case FMD_ADM_CASEACQUIT: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_caseacquit_1; break; case FMD_ADM_SERDLIST: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_fmd_rpc_serdlist; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_serdlist_1; break; case FMD_ADM_SERDINFO: _xdr_argument = (xdrproc_t) xdr_fmd_adm_serdinfo_1_argument; _xdr_result = (xdrproc_t) xdr_fmd_rpc_serdinfo; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_adm_serdinfo_1; break; default: svcerr_noproc(transp); (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ } (void) memset((char *)&argument, 0, sizeof (argument)); if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) { svcerr_decode(transp); (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ } retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp); if (_xdr_result && retval > 0 && !svc_sendreply(transp, _xdr_result, (char *)&result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) { RPC_MSGOUT("%s", "unable to free arguments"); exit(1); } if (_xdr_result != NULL) { if (!fmd_adm_1_freeresult(transp, _xdr_result, (caddr_t)&result)) RPC_MSGOUT("%s", "unable to free results"); } (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ } /* * Please do not edit this file. * It was generated using rpcgen. */ #include "fmd_rpc_api.h" #include #include /* getenv, exit */ #include #include /* for pmap_unset */ #include /* strcmp */ #include /* setsid */ #include #include #include #include /* rlimit */ #include #ifndef SIG_PF #define SIG_PF void(*)(int) #endif #ifdef DEBUG #define RPC_SVC_FG #endif #define _RPCSVC_CLOSEDOWN 120 extern void fmd_api_1(struct svc_req *, SVCXPRT *); #undef RW_READ_HELD #undef RW_WRITE_HELD #undef RW_LOCK_HELD #undef MUTEX_HELD extern int _rpcpmstart; /* Started by a port monitor ? */ /* States a server can be in wrt request */ #define _IDLE 0 #define _SERVED 1 /* LINTED static unused if no main */ static int _rpcsvcstate = _IDLE; /* Set when a request is serviced */ static int _rpcsvccount = 0; /* Number of requests being serviced */ extern mutex_t _svcstate_lock; /* lock for _rpcsvcstate, _rpcsvccount */ #if defined(RPC_MSGOUT) extern void RPC_MSGOUT(const char *, ...); #else /* defined(RPC_MSGOUT) */ static void RPC_MSGOUT(const char *fmt, char *msg) { #ifdef RPC_SVC_FG if (_rpcpmstart) syslog(LOG_ERR, fmt, msg); else { (void) fprintf(stderr, fmt, msg); (void) putc('\n', stderr); } #else syslog(LOG_ERR, fmt, msg); #endif } #endif /* defined(RPC_MSGOUT) */ int _fmd_hdl_create_1( int *argp, void *result, struct svc_req *rqstp) { return (fmd_hdl_create_1_svc(*argp, result, rqstp)); } void fmd_api_1(struct svc_req *rqstp, register SVCXPRT *transp) { union { int fmd_hdl_create_1_arg; } argument; union { int fill; } result; bool_t retval; xdrproc_t _xdr_argument, _xdr_result; bool_t (*local)(char *, void *, struct svc_req *); (void) mutex_lock(&_svcstate_lock); _rpcsvccount++; (void) mutex_unlock(&_svcstate_lock); switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ case FMD_HDL_CREATE: _xdr_argument = (xdrproc_t) xdr_int; _xdr_result = (xdrproc_t) xdr_void; local = (bool_t (*) (char *, void *, struct svc_req *)) _fmd_hdl_create_1; break; default: svcerr_noproc(transp); (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ } (void) memset((char *)&argument, 0, sizeof (argument)); if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) { svcerr_decode(transp); (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ } retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp); if (_xdr_result && retval > 0 && !svc_sendreply(transp, _xdr_result, (char *)&result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) { RPC_MSGOUT("%s", "unable to free arguments"); exit(1); } if (_xdr_result != NULL) { if (!fmd_api_1_freeresult(transp, _xdr_result, (caddr_t)&result)) RPC_MSGOUT("%s", "unable to free results"); } (void) mutex_lock(&_svcstate_lock); _rpcsvccount--; _rpcsvcstate = _SERVED; (void) mutex_unlock(&_svcstate_lock); return; /* CSTYLED */ } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef MUTEX_HELD #undef RW_READ_HELD #undef RW_WRITE_HELD #include #include #include #include #include #include #include #include #include #include static char *sysev_channel; /* event channel to which we are subscribed */ static char *sysev_class; /* event class to which we are subscribed */ static char *sysev_device; /* device path to use for replaying events */ static char *sysev_sid; /* event channel subscriber identifier */ static void *sysev_evc; /* event channel cookie from evc_bind */ static fmd_xprt_t *sysev_xprt; static int sysev_xprt_refcnt; static fmd_hdl_t *sysev_hdl; static struct sysev_stats { fmd_stat_t dump_replay; fmd_stat_t dump_lost; fmd_stat_t bad_class; fmd_stat_t bad_attr; fmd_stat_t eagain; } sysev_stats = { { "dump_replay", FMD_TYPE_UINT64, "events replayed from dump device" }, { "dump_lost", FMD_TYPE_UINT64, "events lost from dump device" }, { "bad_class", FMD_TYPE_UINT64, "events dropped due to invalid class" }, { "bad_attr", FMD_TYPE_UINT64, "events dropped due to invalid nvlist" }, { "eagain", FMD_TYPE_UINT64, "events retried due to low memory" }, }; static pthread_cond_t sysev_cv = PTHREAD_COND_INITIALIZER; static pthread_mutex_t sysev_mutex = PTHREAD_MUTEX_INITIALIZER; static int sysev_replay_wait = 1; static int sysev_exiting; static sysevent_subattr_t *subattr; /* * Entry point for legacy sysevents. This function is responsible for two * things: passing off interesting events to the DR handler, and converting * sysevents into resource events that modules can then subscribe to. */ static void sysev_legacy(sysevent_t *sep) { const char *class = sysevent_get_class_name(sep); const char *subclass = sysevent_get_subclass_name(sep); char *fullclass; size_t len; nvlist_t *attr, *nvl; hrtime_t hrt; /* notify the DR subsystem of the event */ fmd_dr_event(sep); /* get the matching sysevent name */ len = snprintf(NULL, 0, "%s%s.%s", SYSEVENT_RSRC_CLASS, class, subclass); fullclass = alloca(len + 1); (void) snprintf(fullclass, len + 1, "%s%s.%s", SYSEVENT_RSRC_CLASS, class, subclass); /* construct the event payload */ (void) nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva); if (sysevent_get_attr_list(sep, &attr) == 0) { (void) nvlist_merge(nvl, attr, 0); nvlist_free(attr); } /* * Add class and version after the nvlist_merge() just in case * the sysevent has an attribute called class or version. */ (void) nvlist_add_string(nvl, FM_CLASS, fullclass); (void) nvlist_add_uint8(nvl, FM_VERSION, FM_RSRC_VERSION); /* * Dispatch the event. Because we have used sysevent_bind_xhandle * the delivery thread is blessed as a proper fmd thread so * we may use regular fmd api calls. */ sysevent_get_time(sep, &hrt); fmd_xprt_post(sysev_hdl, sysev_xprt, nvl, hrt); } /* * Receive an event from the SysEvent channel and post it to our transport. * Under extreme low-memory situations where we cannot event unpack the event, * we can request that SysEvent redeliver the event later by returning EAGAIN. * If we do this too many times, the kernel will drop the event. Rather than * keeping state per-event, we simply attempt a garbage-collect, hoping that * enough free memory will be available by the time the event is redelivered. */ static int sysev_recv(sysevent_t *sep, void *arg) { uint64_t seq = sysevent_get_seq(sep); fmd_xprt_t *xp = arg; nvlist_t *nvl; hrtime_t hrt; int rc = 0; (void) pthread_mutex_lock(&sysev_mutex); if (sysev_exiting == 1) { while (sysev_xprt_refcnt > 0) (void) pthread_cond_wait(&sysev_cv, &sysev_mutex); (void) pthread_mutex_unlock(&sysev_mutex); return (EAGAIN); } sysev_xprt_refcnt++; while (sysev_replay_wait) (void) pthread_cond_wait(&sysev_cv, &sysev_mutex); (void) pthread_mutex_unlock(&sysev_mutex); if (strcmp(sysevent_get_class_name(sep), EC_FM) != 0) { fmd_hdl_error(sysev_hdl, "discarding event 0x%llx: unexpected" " transport class %s\n", seq, sysevent_get_class_name(sep)); sysev_stats.bad_class.fmds_value.ui64++; } else if (sysevent_get_attr_list(sep, &nvl) != 0) { if (errno == EAGAIN || errno == ENOMEM) { fmd_modhash_tryapply(fmd.d_mod_hash, fmd_module_trygc); fmd_scheme_hash_trygc(fmd.d_schemes); sysev_stats.eagain.fmds_value.ui64++; rc = EAGAIN; } else { fmd_hdl_error(sysev_hdl, "discarding event 0x%llx: " "missing or invalid payload", seq); sysev_stats.bad_attr.fmds_value.ui64++; } } else { sysevent_get_time(sep, &hrt); fmd_xprt_post(sysev_hdl, xp, nvl, hrt); } (void) pthread_mutex_lock(&sysev_mutex); if (--sysev_xprt_refcnt == 0 && sysev_exiting == 1) (void) pthread_cond_broadcast(&sysev_cv); (void) pthread_mutex_unlock(&sysev_mutex); return (rc); } /* * Checksum algorithm used by the dump transport for verifying the content of * error reports saved on the dump device (copy of the kernel's checksum32()). */ static uint32_t sysev_checksum(void *cp_arg, size_t length) { uchar_t *cp, *ep; uint32_t sum = 0; for (cp = cp_arg, ep = cp + length; cp < ep; cp++) sum = ((sum >> 1) | (sum << 31)) + *cp; return (sum); } /* * Replay saved events from the dump transport. This function is installed as * the timer callback and is called only once during the module's lifetime. */ /*ARGSUSED*/ static void sysev_replay(fmd_hdl_t *hdl, id_t id, void *arg) { char *dumpdev; off64_t off, off0; int fd, err; /* * Determine the appropriate dump device to use for replaying pending * error reports. If the device property is NULL (default), we * open and query /dev/dump to determine the current dump device. */ if ((dumpdev = sysev_device) == NULL) { if ((fd = open("/dev/dump", O_RDONLY)) == -1) { fmd_hdl_error(hdl, "failed to open /dev/dump " "to locate dump device for event replay"); goto done; } dumpdev = alloca(PATH_MAX); err = ioctl(fd, DIOCGETDEV, dumpdev); (void) close(fd); if (err == -1) { if (errno != ENODEV) { fmd_hdl_error(hdl, "failed to obtain " "path to dump device for event replay"); } goto done; } } if (strcmp(dumpdev, "/dev/null") == 0) goto done; /* return silently and skip replay for /dev/null */ /* * Open the appropriate device and then determine the offset of the * start of the ereport dump region located at the end of the device. */ if ((fd = open64(dumpdev, O_RDWR | O_DSYNC)) == -1) { fmd_hdl_error(hdl, "failed to open dump transport %s " "(pending events will not be replayed)", dumpdev); goto done; } off = DUMP_OFFSET + DUMP_LOGSIZE + DUMP_ERPTSIZE; off = off0 = lseek64(fd, -off, SEEK_END) & -DUMP_OFFSET; if (off == (off64_t)-1LL) { fmd_hdl_error(hdl, "failed to seek dump transport %s " "(pending events will not be replayed)", dumpdev); (void) close(fd); goto done; } /* * The ereport dump region is a sequence of erpt_dump_t headers each of * which is followed by packed nvlist data. We iterate over them in * order, unpacking and dispatching each one to our dispatch queue. */ for (;;) { char nvbuf[ERPT_DATA_SZ]; uint32_t chksum; erpt_dump_t ed; nvlist_t *nvl; fmd_timeval_t ftv, tod; hrtime_t hrt; uint64_t ena; if (pread64(fd, &ed, sizeof (ed), off) != sizeof (ed)) { fmd_hdl_error(hdl, "failed to read from dump " "transport %s (pending events lost)", dumpdev); break; } if (ed.ed_magic == 0 && ed.ed_size == 0) break; /* end of list: all zero */ if (ed.ed_magic == 0) { off += sizeof (ed) + ed.ed_size; continue; /* continue searching */ } if (ed.ed_magic != ERPT_MAGIC) { /* * Stop reading silently if the first record has the * wrong magic number; this likely indicates that we * rebooted from non-FMA bits or paged over the dump. */ if (off == off0) break; fmd_hdl_error(hdl, "invalid dump transport " "record at %llx (magic number %x, expected %x)\n", (u_longlong_t)off, ed.ed_magic, ERPT_MAGIC); break; } if (ed.ed_size > ERPT_DATA_SZ) { fmd_hdl_error(hdl, "invalid dump transport " "record at %llx size (%u exceeds limit)\n", (u_longlong_t)off, ed.ed_size); break; } if (pread64(fd, nvbuf, ed.ed_size, off + sizeof (ed)) != ed.ed_size) { fmd_hdl_error(hdl, "failed to read dump " "transport event (offset %llx)", (u_longlong_t)off); sysev_stats.dump_lost.fmds_value.ui64++; goto next; } if ((chksum = sysev_checksum(nvbuf, ed.ed_size)) != ed.ed_chksum) { fmd_hdl_error(hdl, "dump transport event at " "offset %llx is corrupt (checksum %x != %x)\n", (u_longlong_t)off, chksum, ed.ed_chksum); sysev_stats.dump_lost.fmds_value.ui64++; goto next; } if ((err = nvlist_xunpack(nvbuf, ed.ed_size, &nvl, &fmd.d_nva)) != 0) { fmd_hdl_error(hdl, "failed to unpack dump " "transport event at offset %llx: %s\n", (u_longlong_t)off, fmd_strerror(err)); sysev_stats.dump_lost.fmds_value.ui64++; goto next; } /* * If ed_hrt_nsec is set it contains the gethrtime() value from * when the event was originally enqueued for the transport. * If it is zero, we use the weaker bound ed_hrt_base instead. */ if (ed.ed_hrt_nsec != 0) hrt = ed.ed_hrt_nsec; else hrt = ed.ed_hrt_base; /* * If this is an FMA protocol event of class "ereport.*" that * contains valid ENA, we can improve the precision of 'hrt'. */ if (nvlist_lookup_uint64(nvl, FM_EREPORT_ENA, &ena) == 0) hrt = fmd_time_ena2hrt(hrt, ena); /* * Now convert 'hrt' to an adjustable TOD based on the values * in ed_tod_base which correspond to one another and are * sampled before reboot using the old gethrtime() clock. * fmd_event_recreate() will use this TOD value to re-assign * the event an updated gethrtime() value based on the current * value of the non-adjustable gethrtime() clock. Phew. */ tod.ftv_sec = ed.ed_tod_base.sec; tod.ftv_nsec = ed.ed_tod_base.nsec; fmd_time_hrt2tod(ed.ed_hrt_base, &tod, hrt, &ftv); (void) nvlist_remove_all(nvl, FMD_EVN_TOD); (void) nvlist_add_uint64_array(nvl, FMD_EVN_TOD, (uint64_t *)&ftv, 2); fmd_xprt_post(hdl, sysev_xprt, nvl, 0); sysev_stats.dump_replay.fmds_value.ui64++; next: /* * Reset the magic number for the event record to zero so that * we do not replay the same event multiple times. */ ed.ed_magic = 0; if (pwrite64(fd, &ed, sizeof (ed), off) != sizeof (ed)) { fmd_hdl_error(hdl, "failed to mark dump " "transport event (offset %llx)", (u_longlong_t)off); } off += sizeof (ed) + ed.ed_size; } (void) close(fd); done: (void) pthread_mutex_lock(&sysev_mutex); sysev_replay_wait = 0; (void) pthread_cond_broadcast(&sysev_cv); (void) pthread_mutex_unlock(&sysev_mutex); } static const fmd_prop_t sysev_props[] = { { "class", FMD_TYPE_STRING, EC_ALL }, /* event class */ { "device", FMD_TYPE_STRING, NULL }, /* replay device */ { "channel", FMD_TYPE_STRING, FM_ERROR_CHAN }, /* channel name */ { "sid", FMD_TYPE_STRING, "fmd" }, /* subscriber id */ { NULL, 0, NULL } }; static const fmd_hdl_ops_t sysev_ops = { NULL, /* fmdo_recv */ sysev_replay, /* fmdo_timeout */ NULL, /* fmdo_close */ NULL, /* fmdo_stats */ NULL, /* fmdo_gc */ NULL, /* fmdo_send */ }; static const fmd_hdl_info_t sysev_info = { "SysEvent Transport Agent", "1.0", &sysev_ops, sysev_props }; /* * Bind to the sysevent channel we use for listening for error events and then * subscribe to appropriate events received over this channel. Setup the * legacy sysevent handler for creating sysevent resources and forwarding DR * events. */ void sysev_init(fmd_hdl_t *hdl) { uint_t flags; const char *subclasses[] = { EC_SUB_ALL }; /* This builtin is for the global zone only */ if (getzoneid() != GLOBAL_ZONEID) return; if (fmd_hdl_register(hdl, FMD_API_VERSION, &sysev_info) != 0) return; /* invalid property settings */ (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (sysev_stats) / sizeof (fmd_stat_t), (fmd_stat_t *)&sysev_stats); sysev_channel = fmd_prop_get_string(hdl, "channel"); sysev_class = fmd_prop_get_string(hdl, "class"); sysev_device = fmd_prop_get_string(hdl, "device"); sysev_sid = fmd_prop_get_string(hdl, "sid"); if (sysev_channel == NULL) fmd_hdl_abort(hdl, "channel property must be defined\n"); if (sysev_sid == NULL) fmd_hdl_abort(hdl, "sid property must be defined\n"); if ((errno = sysevent_evc_bind(sysev_channel, &sysev_evc, EVCH_CREAT | EVCH_HOLD_PEND)) != 0) { fmd_hdl_abort(hdl, "failed to bind to event transport " "channel %s", sysev_channel); } sysev_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY | FMD_XPRT_CACHE_AS_LOCAL, NULL, NULL); sysev_hdl = hdl; /* * If we're subscribing to the default channel, keep our subscription * active even if we die unexpectedly so we continue queuing events. * If we're not (e.g. running under fmsim), do not specify SUB_KEEP so * that our event channel will be destroyed if we die unpleasantly. */ if (strcmp(sysev_channel, FM_ERROR_CHAN) == 0) flags = EVCH_SUB_KEEP | EVCH_SUB_DUMP; else flags = EVCH_SUB_DUMP; if ((subattr = sysevent_subattr_alloc()) == NULL) fmd_hdl_abort(hdl, "failed to allocate subscription " "attributes"); sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL); sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL); errno = sysevent_evc_xsubscribe(sysev_evc, sysev_sid, sysev_class, sysev_recv, sysev_xprt, flags, subattr); if (errno != 0) { if (errno == EEXIST) { fmd_hdl_abort(hdl, "another fault management daemon is " "active on transport channel %s\n", sysev_channel); } else { fmd_hdl_abort(hdl, "failed to xsubscribe to %s on " "transport channel %s", sysev_class, sysev_channel); } } /* * Once the transport is open, install a single timer to fire at once * in the context of the module's thread to run sysev_replay(). This * thread will block in its first fmd_xprt_post() until fmd is ready. */ fmd_hdl_debug(hdl, "transport '%s' open\n", sysev_channel); (void) fmd_timer_install(hdl, NULL, NULL, 0); /* * Open the legacy sysevent handle and subscribe to all events. These * are automatically converted to "resource.sysevent.*" events so that * modules can manage these events without additional infrastructure. */ if (geteuid() != 0) return; if ((fmd.d_sysev_hdl = sysevent_bind_xhandle(sysev_legacy, subattr)) == NULL) fmd_hdl_abort(hdl, "failed to bind to legacy sysevent channel"); if (sysevent_subscribe_event(fmd.d_sysev_hdl, EC_ALL, subclasses, 1) != 0) fmd_hdl_abort(hdl, "failed to subscribe to legacy sysevents"); } /* * Close the channel by unsubscribing and unbinding. We only do this when a * a non-default channel has been selected. If we're using FM_ERROR_CHAN, * the system default, we do *not* want to unsubscribe because the kernel will * remove the subscriber queue and any events published in our absence will * therefore be lost. This scenario may occur when, for example, fmd is sent * a SIGTERM by init(8) during reboot but an error is detected and makes it * into the sysevent channel queue before init(8) manages to call uadmin(2). */ void sysev_fini(fmd_hdl_t *hdl) { if (strcmp(sysev_channel, FM_ERROR_CHAN) != 0) { (void) sysevent_evc_unsubscribe(sysev_evc, sysev_sid); (void) sysevent_evc_unbind(sysev_evc); } if (fmd.d_sysev_hdl != NULL) sysevent_unbind_handle(fmd.d_sysev_hdl); if (subattr != NULL) { sysevent_subattr_free(subattr); subattr = NULL; } if (sysev_xprt != NULL) { /* * Wait callback returns before destroy the transport. */ (void) pthread_mutex_lock(&sysev_mutex); sysev_exiting = 1; while (sysev_xprt_refcnt > 0) (void) pthread_cond_wait(&sysev_cv, &sysev_mutex); (void) pthread_mutex_unlock(&sysev_mutex); fmd_xprt_close(hdl, sysev_xprt); } fmd_prop_free_string(hdl, sysev_class); fmd_prop_free_string(hdl, sysev_channel); fmd_prop_free_string(hdl, sysev_device); fmd_prop_free_string(hdl, sysev_sid); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include fmd_thread_t * fmd_thread_xcreate(fmd_module_t *mp, pthread_t tid) { fmd_thread_t *tp = fmd_alloc(sizeof (fmd_thread_t), FMD_SLEEP); tp->thr_mod = mp; tp->thr_tid = tid; tp->thr_func = NULL; tp->thr_arg = NULL; tp->thr_trdata = fmd_trace_create(); tp->thr_trfunc = fmd.d_thr_trace; tp->thr_errdepth = 0; tp->thr_isdoor = 0; (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); } static void * fmd_thread_start(void *arg) { fmd_thread_t *tp = arg; if (pthread_setspecific(fmd.d_key, tp) != 0) fmd_panic("failed to initialize thread key to %p", arg); if (!tp->thr_isdoor) { (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); (void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } tp->thr_func(tp->thr_arg); return (NULL); } 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.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); } fmd_thread_t * fmd_thread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg) { return (fmd_thread_create_cmn(mp, func, arg, 0)); } fmd_thread_t * fmd_doorthread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg) { return (fmd_thread_create_cmn(mp, func, arg, 1)); } 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)); } /* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_THREAD_H #define _FMD_THREAD_H #ifdef __cplusplus extern "C" { #endif #include #include struct fmd_module; /* see */ typedef void fmd_thread_f(void *); /* signature of thread startup func */ typedef struct fmd_thread { fmd_list_t thr_list; /* linked-list next/prev pointers */ struct fmd_module *thr_mod; /* module associated with this thread */ pthread_t thr_tid; /* thread identifier */ fmd_thread_f *thr_func; /* thread startup function */ void *thr_arg; /* argument for startup function */ fmd_tracebuf_t *thr_trdata; /* thread trace buffer */ fmd_tracebuf_f *thr_trfunc; /* thread trace function */ uint_t thr_errdepth; /* fmd_verror() nesting depth */ int thr_isdoor; /* a private door server thread */ } fmd_thread_t; extern fmd_thread_t *fmd_thread_xcreate(struct fmd_module *, pthread_t); extern fmd_thread_t *fmd_thread_create(struct fmd_module *, fmd_thread_f *, void *); extern fmd_thread_t *fmd_doorthread_create(struct fmd_module *, fmd_thread_f *, void *); #define FMD_THREAD_NOJOIN 0 /* do not attempt to join with thread */ #define FMD_THREAD_JOIN 1 /* wait for and join with thread */ extern void fmd_thread_destroy(fmd_thread_t *, int); #ifdef __cplusplus } #endif #endif /* _FMD_THREAD_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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include void fmd_time_gettimeofday(struct timeval *tvp) { if (fmd.d_clockops->fto_gettimeofday(tvp, NULL) != 0) fmd_panic("failed to read time-of-day clock"); } hrtime_t fmd_time_gethrtime(void) { return (fmd.d_clockops->fto_gethrtime()); } void fmd_time_addhrtime(hrtime_t delta) { fmd.d_clockops->fto_addhrtime(delta); } void fmd_time_waithrtime(hrtime_t delta) { fmd.d_clockops->fto_waithrtime(delta); } void fmd_time_waitcancel(pthread_t tid) { fmd.d_clockops->fto_waitcancel(tid); } /* * To synchronize TOD with a gethrtime() source, we repeatedly sample TOD in * between two calls to gethrtime(), which places a reasonably tight bound on * the high-resolution time that matches the TOD value we sampled. We repeat * this process several times and ultimately select the sample where the two * values of gethrtime() were closest. We then assign the average of those * two high-resolution times to be the gethrtime() associated with that TOD. */ void fmd_time_sync(fmd_timeval_t *ftv, hrtime_t *hrp, uint_t samples) { const fmd_timeops_t *ftop = fmd.d_clockops; hrtime_t hrtbase, hrtmin = INT64_MAX; struct timeval todbase; uint_t i; for (i = 0; i < samples; i++) { hrtime_t t0, t1, delta; struct timeval tod; t0 = ftop->fto_gethrtime(); (void) ftop->fto_gettimeofday(&tod, NULL); t1 = ftop->fto_gethrtime(); delta = t1 - t0; if (delta < hrtmin) { hrtmin = delta; hrtbase = t0 + delta / 2; todbase = tod; } } if (ftv != NULL) { ftv->ftv_sec = todbase.tv_sec; ftv->ftv_nsec = todbase.tv_usec * (NANOSEC / MICROSEC); } if (hrp != NULL) *hrp = hrtbase; } /* * Convert a high-resolution timestamp into 64-bit seconds and nanoseconds. * For efficiency, the multiplication and division are expanded using the * clever algorithm originally designed for the kernel in hrt2ts(). Refer to * the comments in uts/common/os/timers.c for an explanation of how it works. */ static void fmd_time_hrt2ftv(hrtime_t hrt, fmd_timeval_t *ftv) { uint32_t sec, nsec, tmp; tmp = (uint32_t)(hrt >> 30); sec = tmp - (tmp >> 2); sec = tmp - (sec >> 5); sec = tmp + (sec >> 1); sec = tmp - (sec >> 6) + 7; sec = tmp - (sec >> 3); sec = tmp + (sec >> 1); sec = tmp + (sec >> 3); sec = tmp + (sec >> 4); tmp = (sec << 7) - sec - sec - sec; tmp = (tmp << 7) - tmp - tmp - tmp; tmp = (tmp << 7) - tmp - tmp - tmp; nsec = (uint32_t)hrt - (tmp << 9); while (nsec >= NANOSEC) { nsec -= NANOSEC; sec++; } ftv->ftv_sec = sec; ftv->ftv_nsec = nsec; } /* * Convert a high-resolution time from gethrtime() to a TOD (fmd_timeval_t). * We convert 'tod_base' to nanoseconds, adjust it based on the difference * between the corresponding 'hrt_base' and the event high-res time 'hrt', * and then repack the result into ftv_sec and ftv_nsec for our output. */ void fmd_time_hrt2tod(hrtime_t hrt_base, const fmd_timeval_t *tod_base, hrtime_t hrt, fmd_timeval_t *ftv) { fmd_time_hrt2ftv(tod_base->ftv_sec * NANOSEC + tod_base->ftv_nsec + (hrt - hrt_base), ftv); } /* * Convert a TOD (fmd_timeval_t) to a high-resolution time from gethrtime(). * Note that since TOD occurred in the past, the resulting value may be a * negative number according the current gethrtime() clock value. */ void fmd_time_tod2hrt(hrtime_t hrt_base, const fmd_timeval_t *tod_base, const fmd_timeval_t *ftv, hrtime_t *hrtp) { hrtime_t tod_hrt = tod_base->ftv_sec * NANOSEC + tod_base->ftv_nsec; hrtime_t ftv_hrt = ftv->ftv_sec * NANOSEC + ftv->ftv_nsec; *hrtp = hrt_base - (tod_hrt - ftv_hrt); } /* * Adjust a high-resolution time based on the low bits of time stored in ENA. * The assumption here in that ENA won't wrap between the time it is computed * and the time the error is queued (when we capture a full 64-bits of hrtime). * We extract the relevant ENA time bits as 't0' and subtract the difference * between these bits and the corresponding low bits of 'hrt' from 'hrt'. * * Under xVM dom0, the UE ereport is prepared after panic, therefore * the full 64-bit hrtime of 't0' can be bigger than 'hrt'. In such case, * we should just return 'hrt'. * * 't0' contains only the low bits of 64bit hrtime. It is tricky to tell * whether 'hrt' or 't0' happened first. We assume there should be short * period between 'hrt' and 't0', therefore to check which one came first, we * test their subtraction against the highest bit of mask, if the bit is not * set, then 't0' is earlier. This is equivalent to * ((hrt - t0) & mask) < ((mask + 1) / 2) */ hrtime_t fmd_time_ena2hrt(hrtime_t hrt, uint64_t ena) { hrtime_t t0, mask; switch (ENA_FORMAT(ena)) { case FM_ENA_FMT1: t0 = (ena & ENA_FMT1_TIME_MASK) >> ENA_FMT1_TIME_SHFT; mask = ENA_FMT1_TIME_MASK >> ENA_FMT1_TIME_SHFT; if (((hrt - t0) & ((mask + 1) >> 1)) == 0) hrt -= (hrt - t0) & mask; break; case FM_ENA_FMT2: t0 = (ena & ENA_FMT2_TIME_MASK) >> ENA_FMT2_TIME_SHFT; mask = ENA_FMT2_TIME_MASK >> ENA_FMT2_TIME_SHFT; if (((hrt - t0) & ((mask + 1) >> 1)) == 0) hrt -= (hrt - t0) & mask; break; } return (hrt); } /* * To implement a simulated clock, we keep track of an hrtime_t value which * starts at zero and is incremented only by fmd_time_addhrtime() (i.e. when * the driver of the simulation requests that the clock advance). We sample * the native time-of-day clock once at the start of the simulation and then * return subsequent time-of-day values by adjusting TOD using the hrtime_t * clock setting. Simulated nanosleep (fmd_time_waithrtime() entry point) is * implemented by waiting on fts->fts_cv for the hrtime_t to increment. */ static void * fmd_simulator_init(void) { fmd_timesim_t *fts = fmd_alloc(sizeof (fmd_timesim_t), FMD_SLEEP); struct timeval tv; (void) pthread_mutex_init(&fts->fts_lock, NULL); (void) pthread_cond_init(&fts->fts_cv, NULL); (void) gettimeofday(&tv, NULL); fts->fts_tod = (hrtime_t)tv.tv_sec * NANOSEC + (hrtime_t)tv.tv_usec * (NANOSEC / MICROSEC); fts->fts_hrt = 0; fts->fts_cancel = 0; fmd_dprintf(FMD_DBG_TMR, "simulator tod base tv_sec=%lx hrt=%llx\n", tv.tv_sec, fts->fts_tod); return (fts); } static void fmd_simulator_fini(void *fts) { if (fts != NULL) fmd_free(fts, sizeof (fmd_timesim_t)); } /*ARGSUSED*/ static int fmd_simulator_tod(struct timeval *tvp, void *tzp) { fmd_timesim_t *fts = fmd.d_clockptr; hrtime_t tod, hrt, sec, rem; (void) pthread_mutex_lock(&fts->fts_lock); tod = fts->fts_tod; hrt = fts->fts_hrt; (void) pthread_mutex_unlock(&fts->fts_lock); sec = tod / NANOSEC + hrt / NANOSEC; rem = tod % NANOSEC + hrt % NANOSEC; tvp->tv_sec = sec + rem / NANOSEC; tvp->tv_usec = (rem % NANOSEC) / (NANOSEC / MICROSEC); return (0); } static hrtime_t fmd_simulator_hrt(void) { fmd_timesim_t *fts = fmd.d_clockptr; hrtime_t hrt; (void) pthread_mutex_lock(&fts->fts_lock); hrt = fts->fts_hrt; (void) pthread_mutex_unlock(&fts->fts_lock); return (hrt); } static void fmd_simulator_add(hrtime_t delta) { fmd_timesim_t *fts = fmd.d_clockptr; (void) pthread_mutex_lock(&fts->fts_lock); if (fts->fts_hrt + delta < fts->fts_hrt) fts->fts_hrt = INT64_MAX; /* do not increment past apocalypse */ else fts->fts_hrt += delta; TRACE((FMD_DBG_TMR, "hrt clock set %llx", fts->fts_hrt)); fmd_dprintf(FMD_DBG_TMR, "hrt clock set %llx\n", fts->fts_hrt); (void) pthread_cond_broadcast(&fts->fts_cv); (void) pthread_mutex_unlock(&fts->fts_lock); } static void fmd_simulator_wait(hrtime_t delta) { fmd_timesim_t *fts = fmd.d_clockptr; uint64_t hrt; (void) pthread_mutex_lock(&fts->fts_lock); /* * If the delta causes time to wrap because we've reached the simulated * apocalypse, then wait forever. We make 'hrt' unsigned so that the * while-loop comparison fts_hrt < UINT64_MAX will always return true. */ if (fts->fts_hrt + delta < fts->fts_hrt) hrt = UINT64_MAX; else hrt = fts->fts_hrt + delta; while (fts->fts_hrt < hrt && fts->fts_cancel == 0) (void) pthread_cond_wait(&fts->fts_cv, &fts->fts_lock); if (fts->fts_cancel != 0) fts->fts_cancel--; /* cancel has been processed */ (void) pthread_mutex_unlock(&fts->fts_lock); } /*ARGSUSED*/ static void fmd_simulator_cancel(pthread_t tid) { fmd_timesim_t *fts = fmd.d_clockptr; (void) pthread_mutex_lock(&fts->fts_lock); fts->fts_cancel++; (void) pthread_cond_signal(&fts->fts_cv); (void) pthread_mutex_unlock(&fts->fts_lock); } /* * Native time is implemented by calls to gethrtime() and gettimeofday(), which * are stored directly in the native time ops-vector defined below. To wait on * the native clock we use nanosleep(), which we can abort using a signal. The * implementation assumes that callers will have a SIGALRM handler installed. */ static void fmd_native_wait(hrtime_t delta) { timespec_t tv; tv.tv_sec = delta / NANOSEC; tv.tv_nsec = delta % NANOSEC; (void) nanosleep(&tv, NULL); } static void fmd_native_cancel(pthread_t tid) { (void) pthread_kill(tid, SIGALRM); } static void fmd_time_vnop(void) { } static void * fmd_time_nop(void) { return (NULL); } const fmd_timeops_t fmd_timeops_native = { (void *(*)())fmd_time_nop, /* fto_init */ (void (*)())fmd_time_vnop, /* fto_fini */ gettimeofday, /* fto_gettimeofday */ gethrtime, /* fto_gethrtime */ (void (*)())fmd_time_vnop, /* fto_addhrtime */ fmd_native_wait, /* fto_waithrtime */ fmd_native_cancel, /* fto_waitcancel */ }; const fmd_timeops_t fmd_timeops_simulated = { fmd_simulator_init, /* fto_init */ fmd_simulator_fini, /* fto_fini */ fmd_simulator_tod, /* fto_gettimeofday */ fmd_simulator_hrt, /* fto_gethrtime */ fmd_simulator_add, /* fto_addhrtime */ fmd_simulator_wait, /* fto_waithrtime */ fmd_simulator_cancel, /* fto_waitcancel */ }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_TIME_H #define _FMD_TIME_H #include #ifdef __cplusplus extern "C" { #endif typedef struct fmd_timeval { uint64_t ftv_sec; /* seconds since gettimeofday(3C) Epoch */ uint64_t ftv_nsec; /* nanoseconds past value of ftv_sec */ } fmd_timeval_t; typedef struct fmd_timeops { void *(*fto_init)(void); void (*fto_fini)(void *); int (*fto_gettimeofday)(struct timeval *, void *); hrtime_t (*fto_gethrtime)(void); void (*fto_addhrtime)(hrtime_t); void (*fto_waithrtime)(hrtime_t); void (*fto_waitcancel)(pthread_t); } fmd_timeops_t; typedef struct fmd_timesim { pthread_mutex_t fts_lock; /* lock protecting contents of fmd_timesim */ pthread_cond_t fts_cv; /* condition variable for timerq wait */ hrtime_t fts_tod; /* time-of-day nsec corresponding to hrt=0 */ hrtime_t fts_hrt; /* hrtime clock in simulated universe */ uint_t fts_cancel; /* count of pending fto_waitcancel()s */ } fmd_timesim_t; extern const fmd_timeops_t fmd_timeops_native; extern const fmd_timeops_t fmd_timeops_simulated; extern void fmd_time_gettimeofday(struct timeval *); extern hrtime_t fmd_time_gethrtime(void); extern void fmd_time_addhrtime(hrtime_t); extern void fmd_time_waithrtime(hrtime_t); extern void fmd_time_waitcancel(pthread_t); extern void fmd_time_sync(fmd_timeval_t *, hrtime_t *, uint_t); extern void fmd_time_hrt2tod(hrtime_t, const fmd_timeval_t *, hrtime_t, fmd_timeval_t *); extern void fmd_time_tod2hrt(hrtime_t, const fmd_timeval_t *, const fmd_timeval_t *, hrtime_t *); extern hrtime_t fmd_time_ena2hrt(hrtime_t, uint64_t); #ifdef __cplusplus } #endif #endif /* _FMD_TIME_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include /* * Install a new timer to fire after at least 'delta' nanoseconds have elapsed. * Timers are associated with persistent integer identifiers in some idspace. * We allocate a new timer structure or re-use one from our freelist, and then * place it on the queue's list in sorted order by expiration time. If the new * timer is now the earliest to expire, we awaken the fmd_timerq_exec() thread. */ id_t fmd_timerq_install(fmd_timerq_t *tmq, fmd_idspace_t *ids, fmd_timer_f *func, void *arg, fmd_event_t *ep, hrtime_t delta) { hrtime_t now = fmd_time_gethrtime(); hrtime_t base = ep ? fmd_event_hrtime(ep) : now; fmd_timer_t *tp, *up; hrtime_t hrt; id_t id; (void) pthread_mutex_lock(&tmq->tmq_lock); if ((tp = fmd_list_next(&tmq->tmq_free)) == NULL) { tp = fmd_zalloc(sizeof (fmd_timer_t), FMD_SLEEP); (void) pthread_cond_init(&tp->tmr_cv, NULL); } else fmd_list_delete(&tmq->tmq_free, tp); if ((id = fmd_idspace_alloc(ids, tp)) == -1) { fmd_list_prepend(&tmq->tmq_free, tp); (void) pthread_mutex_unlock(&tmq->tmq_lock); return (id); } if (delta < 0) delta = 0; /* ensure delta is at least 0ns from now */ if (base + delta < base) hrt = INT64_MAX; /* if wrap-around, set timer for apocalypse */ else hrt = base + delta; tp->tmr_hrt = hrt; tp->tmr_ids = ids; tp->tmr_id = id; tp->tmr_func = func; tp->tmr_arg = arg; /* * For now we use a simple insertion sort for tmq_list. If we have * scaling problems here due to heavy use of our timer subsystem, * then tmq_list can and should be replaced with a O(logN) heap. */ for (up = fmd_list_next(&tmq->tmq_list); up; up = fmd_list_next(up)) { if (tp->tmr_hrt < up->tmr_hrt) break; } if (up != NULL) fmd_list_insert_before(&tmq->tmq_list, up, tp); else fmd_list_insert_after(&tmq->tmq_list, up, tp); if (up != NULL && fmd_list_next(&tmq->tmq_list) == tp) fmd_time_waitcancel(tmq->tmq_thread->thr_tid); else if (up == NULL && fmd_list_next(&tmq->tmq_list) == tp) (void) pthread_cond_signal(&tmq->tmq_cv); (void) pthread_mutex_unlock(&tmq->tmq_lock); TRACE((FMD_DBG_TMR, "timer %s:%ld insert +%lldns", ids->ids_name, id, delta)); return (id); } /* * Remove the specified timer. If the 'id' is invalid, we'll panic inside of * fmd_idspace_free(). If the timer is still set, we move it to the freelist * and update the timer thread as needed. If the timer 'id' is valid but * tmr_id is not equal to id, then the timer callback is running: we wait for * tmr_id to change to zero (indicating tmr_func is done) before returning. */ void * fmd_timerq_remove(fmd_timerq_t *tmq, fmd_idspace_t *ids, id_t id) { hrtime_t delta = 0; void *arg = NULL; fmd_timer_t *tp; (void) pthread_mutex_lock(&tmq->tmq_lock); tp = fmd_idspace_free(ids, id); ASSERT(tp == NULL || tp->tmr_ids == ids); if (tp == NULL) { (void) pthread_mutex_unlock(&tmq->tmq_lock); return (NULL); /* timer is no longer active */ } if (tp->tmr_id == id) { fmd_list_delete(&tmq->tmq_list, tp); delta = tp->tmr_hrt - fmd_time_gethrtime(); arg = tp->tmr_arg; tp->tmr_id = 0; fmd_list_append(&tmq->tmq_free, tp); /* * If tmq_list is now empty, we must awaken the exec thread so * it will sleep on tmq_cv waiting for the list to change. We * could also awaken the exec thread if we removed the head of * tmq_list, but an early wakeup is harmless so we do nothing. */ if (fmd_list_next(&tmq->tmq_list) == NULL) fmd_time_waitcancel(tmq->tmq_thread->thr_tid); } else { /* * Wait until tmr_id is zero, indicating that tmr_func is done. * This relies on expired fmd_timer_t's being returned to our * free list rather than having the data structure deallocated. */ while (tp->tmr_id != 0) (void) pthread_cond_wait(&tp->tmr_cv, &tmq->tmq_lock); } (void) pthread_mutex_unlock(&tmq->tmq_lock); TRACE((FMD_DBG_TMR, "timer %s:%ld remove -%lldns", ids->ids_name, id, delta > 0 ? delta : 0LL)); return (arg); } /* * fmd_timerq_exec() is the main loop of the thread that runs the timer queue. * We sleep on tmq_cv waiting for timers to show up on tmq_list. When the list * is non-empty, we execute the callback function for each expired timer. If * timers remain that are not yet expired, we nanosleep() until the next expiry * time. We awaken whenever nanosleep() expires or we are interrupted by a * SIGALRM from fmd_timerq_install indicating that we need to rescan our list. */ static void fmd_timerq_exec(fmd_timerq_t *tmq) { fmd_timer_t *tp; sigset_t set; hrtime_t now; /* * fmd_thread_create() initializes threads with all signals blocked. * We must unblock SIGALRM (whose disposition has been to set to call * an empty function by fmd_timerq_init()) in order to permit directed * signals to interrupt our nanosleep() and make it return EINTR. * This SIGALRM mechanism is used by the native clock (see fmd_time.c). */ (void) sigemptyset(&set); (void) sigaddset(&set, SIGALRM); (void) pthread_sigmask(SIG_UNBLOCK, &set, NULL); (void) pthread_mutex_lock(&tmq->tmq_lock); for (;;) { while (!tmq->tmq_abort && fmd_list_next(&tmq->tmq_list) == NULL) (void) pthread_cond_wait(&tmq->tmq_cv, &tmq->tmq_lock); if (tmq->tmq_abort) { (void) pthread_mutex_unlock(&tmq->tmq_lock); return; /* abort timerq thread */ } for (now = fmd_time_gethrtime(); (tp = fmd_list_next( &tmq->tmq_list)) != NULL; now = fmd_time_gethrtime()) { if (now == INT64_MAX || tp->tmr_hrt > now) break; /* no more timers left to expire */ tp->tmr_id = -tp->tmr_id; fmd_list_delete(&tmq->tmq_list, tp); (void) pthread_mutex_unlock(&tmq->tmq_lock); TRACE((FMD_DBG_TMR, "tmr %s:%ld exec start (hrt=%llx)", tp->tmr_ids->ids_name, -tp->tmr_id, tp->tmr_hrt)); tp->tmr_func(tp->tmr_arg, -tp->tmr_id, tp->tmr_hrt); TRACE((FMD_DBG_TMR, "tmr %s:%ld exec end", tp->tmr_ids->ids_name, -tp->tmr_id)); (void) pthread_mutex_lock(&tmq->tmq_lock); (void) fmd_idspace_free(tp->tmr_ids, -tp->tmr_id); fmd_list_append(&tmq->tmq_free, tp); tp->tmr_id = 0; /* for fmd_timer_remove() */ (void) pthread_cond_broadcast(&tp->tmr_cv); } if (tp != NULL) { (void) pthread_mutex_unlock(&tmq->tmq_lock); fmd_time_waithrtime(tp->tmr_hrt - now); (void) pthread_mutex_lock(&tmq->tmq_lock); } } } static void fmd_timerq_alrm(int sig) { TRACE((FMD_DBG_TMR, "timer thread received alarm sig#%d", sig)); } fmd_timerq_t * fmd_timerq_create(void) { fmd_timerq_t *tmq = fmd_zalloc(sizeof (fmd_timerq_t), FMD_SLEEP); struct sigaction act; (void) pthread_mutex_init(&tmq->tmq_lock, NULL); (void) pthread_cond_init(&tmq->tmq_cv, NULL); act.sa_handler = fmd_timerq_alrm; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); (void) sigaction(SIGALRM, &act, NULL); if ((tmq->tmq_thread = fmd_thread_create(fmd.d_rmod, (fmd_thread_f *)fmd_timerq_exec, tmq)) == NULL) fmd_panic("failed to create timer thread"); return (tmq); } void fmd_timerq_destroy(fmd_timerq_t *tmq) { struct sigaction act; fmd_timer_t *tmr; (void) pthread_mutex_lock(&tmq->tmq_lock); tmq->tmq_abort++; if (fmd_list_next(&tmq->tmq_list) != NULL) fmd_time_waitcancel(tmq->tmq_thread->thr_tid); else (void) pthread_cond_signal(&tmq->tmq_cv); (void) pthread_mutex_unlock(&tmq->tmq_lock); fmd_thread_destroy(tmq->tmq_thread, FMD_THREAD_JOIN); (void) pthread_mutex_lock(&tmq->tmq_lock); while ((tmr = fmd_list_next(&tmq->tmq_list)) != NULL) { fmd_list_delete(&tmq->tmq_list, tmr); (void) fmd_idspace_free(tmr->tmr_ids, tmr->tmr_id); fmd_free(tmr, sizeof (fmd_timer_t)); } while ((tmr = fmd_list_next(&tmq->tmq_free)) != NULL) { fmd_list_delete(&tmq->tmq_free, tmr); ASSERT(tmr->tmr_id == 0); fmd_free(tmr, sizeof (fmd_timer_t)); } act.sa_handler = SIG_DFL; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); (void) sigaction(SIGALRM, &act, NULL); fmd_free(tmq, sizeof (fmd_timerq_t)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_TIMERQ_H #define _FMD_TIMERQ_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include typedef void fmd_timer_f(void *, id_t, hrtime_t); typedef struct fmd_timer { fmd_list_t tmr_list; /* expiry or free list next/prev pointers */ hrtime_t tmr_hrt; /* high-res time at which timer should fire */ fmd_idspace_t *tmr_ids; /* idspace that contains the timer id */ id_t tmr_id; /* client identifier for this timer */ fmd_timer_f *tmr_func; /* function that should be called on expiry */ void *tmr_arg; /* argument to pass back to tmr_func */ pthread_cond_t tmr_cv; /* condition variable for waiting on tmr_func */ } fmd_timer_t; typedef struct fmd_timerq { fmd_thread_t *tmq_thread; /* thread handling timer expiry for queue */ uint_t tmq_abort; /* flag indicating tmq_thread should abort */ pthread_mutex_t tmq_lock; /* lock protecting timer queue contents */ pthread_cond_t tmq_cv; /* condition variable for tmq_list, abort */ fmd_list_t tmq_list; /* list of active timers, sorted by tmr_hrt */ fmd_list_t tmq_free; /* list of free timers */ } fmd_timerq_t; extern id_t fmd_timerq_install(fmd_timerq_t *, fmd_idspace_t *, fmd_timer_f *, void *, fmd_event_t *, hrtime_t); extern void *fmd_timerq_remove(fmd_timerq_t *, fmd_idspace_t *, id_t); extern fmd_timerq_t *fmd_timerq_create(void); extern void fmd_timerq_destroy(fmd_timerq_t *); #ifdef __cplusplus } #endif #endif /* _FMD_TIMERQ_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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * FMD Topology Handling * * Fault manager scheme and module plug-ins may need access to the latest * libtopo snapshot. Upon fmd initialization, a snapshot is taken and * made available via fmd_fmri_topology() and fmd_hdl_topology(). Each * of these routines returns a libtopo snapshot handle back to the caller. * New snapshots are taken if and when a DR event causes the DR generation * number to increase. The current snapshot is retained to assure consistency * for modules still using older snapshots and the latest snapshot handle is * returned to the caller. */ #include #include #include #include #include #include #include #include #include #include static void fmd_topo_rele_locked(fmd_topo_t *ftp) { ASSERT(MUTEX_HELD(&fmd.d_topo_lock)); if (--ftp->ft_refcount == 0) { fmd_list_delete(&fmd.d_topo_list, ftp); topo_close(ftp->ft_hdl); fmd_free(ftp, sizeof (fmd_topo_t)); } } void fmd_topo_update(void) { int err; topo_hdl_t *tp; fmd_topo_t *ftp, *prev; char *id; const char *name; (void) pthread_mutex_lock(&fmd.d_topo_lock); fmd.d_stats->ds_topo_drgen.fmds_value.ui64 = fmd_fmri_get_drgen(); name = fmd.d_rootdir != NULL && *fmd.d_rootdir != '\0' ? fmd.d_rootdir : NULL; /* * Update the topology snapshot. */ if ((tp = topo_open(TOPO_VERSION, name, &err)) == NULL) fmd_panic("failed to open topology library: %s", topo_strerror(err)); ftp = fmd_alloc(sizeof (fmd_topo_t), FMD_SLEEP); ftp->ft_hdl = tp; ftp->ft_time_begin = fmd_time_gethrtime(); if ((id = topo_snap_hold(tp, NULL, &err)) == NULL) fmd_panic("failed to get topology snapshot: %s", topo_strerror(err)); topo_hdl_strfree(tp, id); ftp->ft_time_end = fmd_time_gethrtime(); fmd.d_stats->ds_topo_gen.fmds_value.ui64++; /* * We always keep a reference count on the last topo snapshot taken. * Release the previous snapshot (if present), and set the current * reference count to 1. */ if ((prev = fmd_list_next(&fmd.d_topo_list)) != NULL) fmd_topo_rele_locked(prev); ftp->ft_refcount = 1; fmd_list_prepend(&fmd.d_topo_list, ftp); (void) pthread_mutex_unlock(&fmd.d_topo_lock); } fmd_topo_t * fmd_topo_hold(void) { fmd_topo_t *ftp; (void) pthread_mutex_lock(&fmd.d_topo_lock); ftp = fmd_list_next(&fmd.d_topo_list); ftp->ft_refcount++; (void) pthread_mutex_unlock(&fmd.d_topo_lock); return (ftp); } void fmd_topo_addref(fmd_topo_t *ftp) { (void) pthread_mutex_lock(&fmd.d_topo_lock); ftp->ft_refcount++; (void) pthread_mutex_unlock(&fmd.d_topo_lock); } void fmd_topo_rele(fmd_topo_t *ftp) { (void) pthread_mutex_lock(&fmd.d_topo_lock); fmd_topo_rele_locked(ftp); (void) pthread_mutex_unlock(&fmd.d_topo_lock); } void fmd_topo_rele_hdl(topo_hdl_t *thp) { fmd_topo_t *ftp; (void) pthread_mutex_lock(&fmd.d_topo_lock); for (ftp = fmd_list_next(&fmd.d_topo_list); ftp != NULL; ftp = fmd_list_next(ftp)) { if (ftp->ft_hdl == thp) break; } ASSERT(ftp != NULL); fmd_topo_rele_locked(ftp); (void) pthread_mutex_unlock(&fmd.d_topo_lock); } void fmd_topo_init(void) { fmd_topo_update(); } void fmd_topo_fini(void) { fmd_topo_t *ftp; (void) pthread_mutex_lock(&fmd.d_topo_lock); while ((ftp = fmd_list_next(&fmd.d_topo_list)) != NULL) { fmd_list_delete(&fmd.d_topo_list, ftp); topo_close(ftp->ft_hdl); fmd_free(ftp, sizeof (fmd_topo_t)); } (void) pthread_mutex_unlock(&fmd.d_topo_lock); } /* * 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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _FMD_TOPO_H #define _FMD_TOPO_H #ifdef __cplusplus extern "C" { #endif #include #include extern void fmd_topo_init(void); extern void fmd_topo_fini(void); typedef struct fmd_topo { fmd_list_t ft_list; topo_hdl_t *ft_hdl; uint32_t ft_refcount; hrtime_t ft_time_begin; hrtime_t ft_time_end; } fmd_topo_t; extern void fmd_topo_update(void); extern fmd_topo_t *fmd_topo_hold(void); extern void fmd_topo_addref(fmd_topo_t *); extern void fmd_topo_rele(fmd_topo_t *); extern void fmd_topo_rele_hdl(topo_hdl_t *); #ifdef __cplusplus } #endif #endif /* _FMD_TOPO_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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include fmd_tracebuf_t * fmd_trace_create(void) { fmd_tracebuf_t *tbp = fmd_zalloc(sizeof (fmd_tracebuf_t), FMD_SLEEP); size_t bufsize; (void) fmd_conf_getprop(fmd.d_conf, "trace.frames", &tbp->tb_frames); (void) fmd_conf_getprop(fmd.d_conf, "trace.recs", &tbp->tb_recs); /* * We require 8-byte alignment of fmd_tracerec_t to store hrtime_t's. * Since the trailing flexible array member is of type uintptr_t, we * may need to allocate an additional element if we are compiling * 32-bit; otherwise uintptr_t is 8 bytes so any value of tb_frames is * acceptable. * * tb_frames includes the first element, whose size is reflected in * sizeof (fmd_tracerec_t). Therefore, if fmd_tracerec_t's size is * 0 mod 8, we must be sure the total number of frames is odd. * Otherwise, we need at least one extra frame, so the total count * must be even. This will continue to work even if the sizes or * types of other fmd_tracerec_t members are changed. */ #ifdef _ILP32 /*CONSTCOND*/ if (sizeof (fmd_tracerec_t) % sizeof (hrtime_t) == 0) tbp->tb_frames = (tbp->tb_frames & ~1UL) + 1; else tbp->tb_frames = P2ROUNDUP(tbp->tb_frames, 2); #endif tbp->tb_size = sizeof (fmd_tracerec_t) + sizeof (uintptr_t) * (MAX(tbp->tb_frames, 1) - 1); bufsize = tbp->tb_size * tbp->tb_recs; tbp->tb_buf = fmd_zalloc(bufsize, FMD_SLEEP); tbp->tb_end = (void *)((uintptr_t)tbp->tb_buf + bufsize - tbp->tb_size); tbp->tb_ptr = tbp->tb_buf; return (tbp); } 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)); } /* * Callback for walkcontext(3C) to store the stack trace. We use tr_tag below * to store the maximum value of depth that is permitted so we can use it here. */ /*ARGSUSED*/ static int fmd_trace_frame(uintptr_t pc, int sig, fmd_tracerec_t *trp) { trp->tr_stack[trp->tr_depth++] = pc; return (trp->tr_depth >= trp->tr_tag); } /*ARGSUSED*/ fmd_tracerec_t * fmd_trace_none(fmd_tracebuf_t *tbp, uint_t tag, const char *format, va_list ap) { return (NULL); } fmd_tracerec_t * fmd_trace_lite(fmd_tracebuf_t *tbp, uint_t tag, const char *format, va_list ap) { hrtime_t time = gethrtime(); fmd_tracerec_t *trp = tbp->tb_ptr; char *p; if (tbp->tb_depth++ != 0) { tbp->tb_depth--; return (NULL); } trp->tr_time = time; trp->tr_file = NULL; trp->tr_line = 0; trp->tr_errno = (tag == FMD_DBG_ERR) ? errno : 0; trp->tr_tag = fmd_ntz32(tag); (void) vsnprintf(trp->tr_msg, sizeof (trp->tr_msg), format, ap); p = &trp->tr_msg[strlen(trp->tr_msg)]; if (p > trp->tr_msg && p[-1] == '\n') p[-1] = '\0'; if (tbp->tb_ptr != tbp->tb_end) tbp->tb_ptr = (void *)((uintptr_t)tbp->tb_ptr + tbp->tb_size); else tbp->tb_ptr = tbp->tb_buf; tbp->tb_depth--; return (trp); } fmd_tracerec_t * fmd_trace_full(fmd_tracebuf_t *tbp, uint_t tag, const char *format, va_list ap) { hrtime_t time = gethrtime(); fmd_tracerec_t *trp = tbp->tb_ptr; ucontext_t uc; char *p; if (tbp->tb_depth++ != 0) { tbp->tb_depth--; return (NULL); } (void) getcontext(&uc); trp->tr_depth = 0; trp->tr_tag = tbp->tb_frames; /* for use by fmd_trace_frame() */ (void) walkcontext(&uc, (int (*)())fmd_trace_frame, trp); trp->tr_time = time; trp->tr_file = NULL; trp->tr_line = 0; trp->tr_errno = (tag == FMD_DBG_ERR) ? errno : 0; trp->tr_tag = fmd_ntz32(tag); if (fmd.d_fmd_debug & FMD_DBG_TRACE) fmd_vdprintf(tag, format, ap); (void) vsnprintf(trp->tr_msg, sizeof (trp->tr_msg), format, ap); p = &trp->tr_msg[strlen(trp->tr_msg)]; if (p > trp->tr_msg && p[-1] == '\n') p[-1] = '\0'; if (tbp->tb_ptr != tbp->tb_end) tbp->tb_ptr = (void *)((uintptr_t)tbp->tb_ptr + tbp->tb_size); else tbp->tb_ptr = tbp->tb_buf; tbp->tb_depth--; return (trp); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_TRACE_H #define _FMD_TRACE_H #include #include #ifdef __cplusplus extern "C" { #endif typedef struct fmd_tracerec { hrtime_t tr_time; /* high-resolution timestamp */ const char *tr_file; /* source file name */ uint32_t tr_line; /* source file line */ uint16_t tr_errno; /* errno value if error */ uint8_t tr_tag; /* tag (see ) */ uint8_t tr_depth; /* depth of tr_stack[] */ char tr_msg[64]; /* formatted message */ uintptr_t tr_stack[1]; /* stack trace (optional) */ } fmd_tracerec_t; typedef struct fmd_tracebuf { fmd_tracerec_t *tb_buf; /* pointer to first trace record */ fmd_tracerec_t *tb_end; /* pointer to last trace record */ fmd_tracerec_t *tb_ptr; /* next trace record to use */ uint_t tb_frames; /* maximum captured frames */ uint_t tb_recs; /* number of trace records */ uint_t tb_size; /* size of each record */ uint_t tb_depth; /* recursion depth of trace function */ } fmd_tracebuf_t; typedef fmd_tracerec_t *fmd_tracebuf_f(fmd_tracebuf_t *, uint_t, const char *, va_list); extern fmd_tracebuf_t *fmd_trace_create(void); extern void fmd_trace_destroy(fmd_tracebuf_t *); extern fmd_tracebuf_f fmd_trace_none; extern fmd_tracebuf_f fmd_trace_lite; extern fmd_tracebuf_f fmd_trace_full; #ifdef __cplusplus } #endif #endif /* _FMD_TRACE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include static fmd_ustat_chunk_t * fmd_ustat_chunk_init(fmd_ustat_t *usp, fmd_stat_t *base, uint_t len) { fmd_ustat_chunk_t *cp; cp = fmd_zalloc(sizeof (fmd_ustat_chunk_t), FMD_SLEEP); cp->usc_base = base; cp->usc_len = len; cp->usc_refs = 1; ASSERT(RW_WRITE_HELD(&usp->us_lock)); fmd_list_append(&usp->us_chunks, cp); return (cp); } static void fmd_ustat_chunk_hold(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp) { ASSERT(RW_WRITE_HELD(&usp->us_lock)); cp->usc_refs++; ASSERT(cp->usc_refs != 0); } static void fmd_ustat_chunk_rele(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp) { ASSERT(RW_WRITE_HELD(&usp->us_lock)); ASSERT(cp->usc_refs != 0); if (--cp->usc_refs == 0) { /* * Note that any strings pointed to by FMD_TYPE_STRING stats * are freed one-by-one before releasing the chunk. So here * we can just free the chunk and not worry about its content. */ fmd_free(cp->usc_base, sizeof (fmd_stat_t) * cp->usc_len); fmd_list_delete(&usp->us_chunks, cp); fmd_free(cp, sizeof (fmd_ustat_chunk_t)); } } fmd_ustat_t * fmd_ustat_create(void) { fmd_ustat_t *usp = fmd_zalloc(sizeof (fmd_ustat_t), FMD_SLEEP); (void) pthread_rwlock_init(&usp->us_lock, NULL); usp->us_hashlen = fmd.d_str_buckets; usp->us_hash = fmd_zalloc(sizeof (void *) * usp->us_hashlen, FMD_SLEEP); return (usp); } void fmd_ustat_destroy(fmd_ustat_t *usp) { fmd_ustat_elem_t *ep, *np; uint_t i; (void) pthread_rwlock_wrlock(&usp->us_lock); for (i = 0; i < usp->us_hashlen; i++) { for (ep = usp->us_hash[i]; ep != NULL; ep = np) { if (ep->use_stat->fmds_type == FMD_TYPE_STRING) fmd_strfree(ep->use_stat->fmds_value.str); if (ep->use_chunk != NULL) fmd_ustat_chunk_rele(usp, ep->use_chunk); np = ep->use_next; fmd_free(ep, sizeof (fmd_ustat_elem_t)); } } ASSERT(usp->us_chunks.l_next == NULL); ASSERT(usp->us_chunks.l_prev == NULL); fmd_free(usp->us_hash, sizeof (void *) * usp->us_hashlen); fmd_free(usp, sizeof (fmd_ustat_t)); } int fmd_ustat_snapshot(fmd_ustat_t *usp, fmd_ustat_snap_t *uss) { const fmd_ustat_elem_t *ep; fmd_stat_t *sp; uint_t i; (void) pthread_rwlock_wrlock(&usp->us_lock); uss->uss_buf = sp = malloc(sizeof (fmd_stat_t) * usp->us_nelems); uss->uss_len = usp->us_nelems; if (uss->uss_buf == NULL) { (void) pthread_rwlock_unlock(&usp->us_lock); return (fmd_set_errno(EFMD_STAT_NOMEM)); } for (i = 0; i < usp->us_hashlen; i++) { for (ep = usp->us_hash[i]; ep != NULL; ep = ep->use_next) { bcopy(ep->use_stat, sp, sizeof (fmd_stat_t)); if (sp->fmds_type == FMD_TYPE_STRING && sp->fmds_value.str != NULL) sp->fmds_value.str = strdup(sp->fmds_value.str); sp++; } } ASSERT(sp == uss->uss_buf + uss->uss_len); (void) pthread_rwlock_unlock(&usp->us_lock); return (0); } static void fmd_ustat_delete_locked(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp, int strfree) { ASSERT(RW_WRITE_HELD(&usp->us_lock)); for (; n-- != 0; sp++) { uint_t h = fmd_strhash(sp->fmds_name) % usp->us_hashlen; fmd_ustat_elem_t *ep, **pp = &usp->us_hash[h]; for (ep = *pp; ep != NULL; ep = ep->use_next) { if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) != 0) pp = &ep->use_next; else break; } if (ep == NULL) continue; /* silently ignore unregistered entries */ if (strfree && ep->use_stat->fmds_type == FMD_TYPE_STRING) fmd_strfree(ep->use_stat->fmds_value.str); if (ep->use_chunk != NULL) fmd_ustat_chunk_rele(usp, ep->use_chunk); *pp = ep->use_next; fmd_free(ep, sizeof (fmd_ustat_elem_t)); usp->us_nelems--; } } fmd_stat_t * fmd_ustat_insert(fmd_ustat_t *usp, uint_t flags, uint_t n, fmd_stat_t *template, fmd_stat_t **epp) { fmd_stat_t *stats, *sp; fmd_ustat_chunk_t *cp; uint_t i; int checkid = flags & FMD_USTAT_VALIDATE; int has_str = 0; int err = 0; if (flags & FMD_USTAT_ALLOC) { sp = stats = fmd_alloc(sizeof (fmd_stat_t) * n, FMD_SLEEP); bcopy(template, stats, sizeof (fmd_stat_t) * n); } else sp = stats = template; (void) pthread_rwlock_wrlock(&usp->us_lock); if (flags & FMD_USTAT_ALLOC) cp = fmd_ustat_chunk_init(usp, stats, n); else cp = NULL; for (i = 0; i < n; i++, sp++) { char *p, *q = sp->fmds_name + sizeof (sp->fmds_name); fmd_ustat_elem_t *ep; uint_t h; /* * Since a module may be passing in this statistic and our * names are represented by a fixed-size array, scan fmds_name * to ensure it has a \0 somewhere before we attempt strcmps. */ for (p = sp->fmds_name; p < q; p++) { if (*p == '\0') break; } if (p == q) q[-1] = '\0'; /* nul-terminate for subsequent message */ if (p == q || fmd_strbadid(sp->fmds_name, checkid) != NULL) { fmd_error(EFMD_STAT_BADNAME, "'%s' does not conform to " "statistic naming rules\n", sp->fmds_name); err = fmd_set_errno(EFMD_STAT_BADNAME); break; } if (sp->fmds_type > FMD_TYPE_SIZE) { fmd_error(EFMD_STAT_BADTYPE, "'%s' statistic type %u " "is not valid\n", sp->fmds_name, sp->fmds_type); err = fmd_set_errno(EFMD_STAT_BADTYPE); break; } if (sp->fmds_type == FMD_TYPE_STRING) has_str++; /* flag for second pass; see below */ h = fmd_strhash(sp->fmds_name) % usp->us_hashlen; for (ep = usp->us_hash[h]; ep != NULL; ep = ep->use_next) { if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) == 0) break; } if (ep != NULL) { fmd_error(EFMD_STAT_DUPNAME, "'%s' is already defined " "as a statistic name\n", sp->fmds_name); err = fmd_set_errno(EFMD_STAT_DUPNAME); break; } ep = fmd_alloc(sizeof (fmd_ustat_elem_t), FMD_SLEEP); ep->use_next = usp->us_hash[h]; usp->us_hash[h] = ep; ep->use_stat = sp; ep->use_chunk = cp; if (cp != NULL) fmd_ustat_chunk_hold(usp, cp); usp->us_nelems++; } /* * If an error occurred, delete all the stats inserted by successful * iterations of the loop [0 .. i-1]. If 'epp' is non-NULL, store a * copy of the input stat pointer that caused the error there. When * the delete is done, if we allocated a chunk, there should be only * one reference remaining (from the initial fmd_ustat_chunk_init()). */ if (err != 0) { fmd_ustat_delete_locked(usp, i, stats, FMD_B_FALSE); ASSERT(cp == NULL || cp->usc_refs == 1); if (epp != NULL) *epp = template + i; } else if (has_str) { /* * If no error occurred and one or more string stats are being * inserted, make a second pass through 'stats' duplicating any * initial strings so that fmd_stat_setstr() can alloc/free. */ for (sp = stats, i = 0; i < n; i++, sp++) { if (sp->fmds_type == FMD_TYPE_STRING && sp->fmds_value.str != NULL) { sp->fmds_value.str = fmd_strdup( sp->fmds_value.str, FMD_SLEEP); } } } if (cp != NULL) fmd_ustat_chunk_rele(usp, cp); (void) pthread_rwlock_unlock(&usp->us_lock); return (err ? NULL : stats); } void fmd_ustat_delete(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp) { (void) pthread_rwlock_wrlock(&usp->us_lock); fmd_ustat_delete_locked(usp, n, sp, FMD_B_TRUE); (void) pthread_rwlock_unlock(&usp->us_lock); } /* * Delete all statistics that are references to external memory (that is, all * statistics inserted with FMD_STAT_NOALLOC), i.e. a NULL ep->use_chunk. */ void fmd_ustat_delete_references(fmd_ustat_t *usp) { fmd_ustat_elem_t *ep, **pp; uint_t i; (void) pthread_rwlock_wrlock(&usp->us_lock); for (i = 0; i < usp->us_hashlen; i++) { for (pp = &usp->us_hash[i], ep = *pp; ep != NULL; ep = *pp) { if (ep->use_chunk != NULL) { pp = &ep->use_next; continue; } if (ep->use_stat->fmds_type == FMD_TYPE_STRING) fmd_strfree(ep->use_stat->fmds_value.str); *pp = ep->use_next; fmd_free(ep, sizeof (fmd_ustat_elem_t)); usp->us_nelems--; } } (void) pthread_rwlock_unlock(&usp->us_lock); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_USTAT_H #define _FMD_USTAT_H #include #ifdef __cplusplus extern "C" { #endif #include #include typedef struct fmd_ustat_snap { fmd_stat_t *uss_buf; /* array of statistic data */ uint_t uss_len; /* length of uss_buf array */ } fmd_ustat_snap_t; typedef struct fmd_ustat_chunk { fmd_list_t usc_list; /* linked list next/prev pointers */ fmd_stat_t *usc_base; /* base of chunk allocation */ uint_t usc_len; /* number of stat structs in chunk */ uint_t usc_refs; /* reference count on chunk */ } fmd_ustat_chunk_t; typedef struct fmd_ustat_elem { struct fmd_ustat_elem *use_next; /* pointer to next statistic in hash */ const fmd_stat_t *use_stat; /* pointer to statistic data storage */ fmd_ustat_chunk_t *use_chunk; /* pointer to alloc chunk (or NULL) */ } fmd_ustat_elem_t; typedef struct fmd_ustat { pthread_rwlock_t us_lock; /* lock protecting ustat collection */ fmd_list_t us_chunks; /* linked list of allocation chunks */ fmd_ustat_elem_t **us_hash; /* hash bucket array of stat elements */ uint_t us_hashlen; /* length of us_hash bucket array */ uint_t us_nelems; /* number of elements in collection */ } fmd_ustat_t; extern fmd_ustat_t *fmd_ustat_create(void); extern void fmd_ustat_destroy(fmd_ustat_t *); extern int fmd_ustat_snapshot(fmd_ustat_t *, fmd_ustat_snap_t *); #define FMD_USTAT_NOALLOC 0x0 /* fmd should use caller's memory */ #define FMD_USTAT_ALLOC 0x1 /* fmd should allocate stats memory */ #define FMD_USTAT_VALIDATE 0x2 /* fmd should validate stat names */ #if FMD_STAT_NOALLOC != FMD_USTAT_NOALLOC #error "FMD_STAT_NOALLOC must match FMD_USTAT_NOALLOC" #endif #if FMD_STAT_ALLOC != FMD_USTAT_ALLOC #error "FMD_STAT_ALLOC must match FMD_USTAT_ALLOC" #endif extern fmd_stat_t *fmd_ustat_insert(fmd_ustat_t *, uint_t, uint_t, fmd_stat_t *, fmd_stat_t **); extern void fmd_ustat_delete(fmd_ustat_t *, uint_t, fmd_stat_t *); extern void fmd_ustat_delete_references(fmd_ustat_t *); #ifdef __cplusplus } #endif #endif /* _FMD_USTAT_H */ /* * Please do not edit this file. * It was generated using rpcgen. */ #include "fmd_rpc_adm.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ #include bool_t xdr_fmd_adm_error(XDR *xdrs, fmd_adm_error *objp) { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_modstat(XDR *xdrs, fmd_rpc_modstat *objp) { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->rms_buf.rms_buf_val, (u_int *) &objp->rms_buf.rms_buf_len, ~0, sizeof (struct fmd_stat), (xdrproc_t)xdr_fmd_stat)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rms_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_modinfo(XDR *xdrs, fmd_rpc_modinfo *objp) { rpc_inline_t *buf __unused; fmd_rpc_modinfo *tmp_fmd_rpc_modinfo; bool_t more_data = TRUE; bool_t first_objp = TRUE; if (xdrs->x_op == XDR_DECODE) { while (more_data) { void bzero(); if (!xdr_string(xdrs, &objp->rmi_name, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rmi_desc, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rmi_vers, ~0)) return (FALSE); if (!xdr_bool(xdrs, &objp->rmi_faulty)) return (FALSE); if (!xdr_bool(xdrs, &more_data)) return (FALSE); if (!more_data) { objp->rmi_next = NULL; break; } if (objp->rmi_next == NULL) { objp->rmi_next = (fmd_rpc_modinfo *) mem_alloc(sizeof (fmd_rpc_modinfo)); if (objp->rmi_next == NULL) return (FALSE); bzero(objp->rmi_next, sizeof (fmd_rpc_modinfo)); } objp = objp->rmi_next; } } else if (xdrs->x_op == XDR_ENCODE) { while (more_data) { if (!xdr_string(xdrs, &objp->rmi_name, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rmi_desc, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rmi_vers, ~0)) return (FALSE); if (!xdr_bool(xdrs, &objp->rmi_faulty)) return (FALSE); objp = objp->rmi_next; if (objp == NULL) more_data = FALSE; if (!xdr_bool(xdrs, &more_data)) return (FALSE); } } else { while (more_data) { if (!xdr_string(xdrs, &objp->rmi_name, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rmi_desc, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rmi_vers, ~0)) return (FALSE); if (!xdr_bool(xdrs, &objp->rmi_faulty)) return (FALSE); tmp_fmd_rpc_modinfo = objp; objp = objp->rmi_next; if (objp == NULL) more_data = FALSE; if (!first_objp) mem_free(tmp_fmd_rpc_modinfo, sizeof (fmd_rpc_modinfo)); else first_objp = FALSE; } } return (TRUE); } bool_t xdr_fmd_rpc_modlist(XDR *xdrs, fmd_rpc_modlist *objp) { rpc_inline_t *buf __unused; if (!xdr_fmd_adm_error(xdrs, &objp->rml_err)) return (FALSE); if (!xdr_pointer(xdrs, (char **)&objp->rml_list, sizeof (fmd_rpc_modinfo), (xdrproc_t)xdr_fmd_rpc_modinfo)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rml_len)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_rsrcinfo(XDR *xdrs, fmd_rpc_rsrcinfo *objp) { rpc_inline_t *buf __unused; if (!xdr_string(xdrs, &objp->rri_fmri, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rri_uuid, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->rri_case, ~0)) return (FALSE); if (!xdr_bool(xdrs, &objp->rri_faulty)) return (FALSE); if (!xdr_bool(xdrs, &objp->rri_unusable)) return (FALSE); if (!xdr_bool(xdrs, &objp->rri_invisible)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rri_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_rsrclist(XDR *xdrs, fmd_rpc_rsrclist *objp) { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->rrl_buf.rrl_buf_val, (u_int *) &objp->rrl_buf.rrl_buf_len, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rrl_len)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rrl_cnt)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rrl_err)) return (FALSE); if (!xdr_bool(xdrs, &objp->rrl_all)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_serdinfo(XDR *xdrs, fmd_rpc_serdinfo *objp) { rpc_inline_t *buf __unused; if (!xdr_string(xdrs, &objp->rsi_name, ~0)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->rsi_delta)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rsi_count)) return (FALSE); if (!xdr_bool(xdrs, &objp->rsi_fired)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->rsi_n)) return (FALSE); if (!xdr_uint64_t(xdrs, &objp->rsi_t)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rsi_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_serdlist(XDR *xdrs, fmd_rpc_serdlist *objp) { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->rsl_buf.rsl_buf_val, (u_int *) &objp->rsl_buf.rsl_buf_len, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rsl_len)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rsl_cnt)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rsl_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_xprtlist(XDR *xdrs, fmd_rpc_xprtlist *objp) { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->rxl_buf.rxl_buf_val, (u_int *) &objp->rxl_buf.rxl_buf_len, ~0, sizeof (int32_t), (xdrproc_t)xdr_int32_t)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rxl_len)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rxl_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_caseinfo(XDR *xdrs, fmd_rpc_caseinfo *objp) { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->rci_evbuf.rci_evbuf_val, (u_int *) &objp->rci_evbuf.rci_evbuf_len, ~0)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rci_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_rpc_caselist(XDR *xdrs, fmd_rpc_caselist *objp) { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->rcl_buf.rcl_buf_val, (u_int *) &objp->rcl_buf.rcl_buf_len, ~0)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rcl_len)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->rcl_cnt)) return (FALSE); if (!xdr_fmd_adm_error(xdrs, &objp->rcl_err)) return (FALSE); return (TRUE); } bool_t xdr_fmd_adm_serdreset_1_argument(XDR *xdrs, fmd_adm_serdreset_1_argument *objp) { if (!xdr_string(xdrs, &objp->arg1, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->arg2, ~0)) return (FALSE); return (TRUE); } bool_t xdr_fmd_adm_rsrcacquit_1_argument(XDR *xdrs, fmd_adm_rsrcacquit_1_argument *objp) { if (!xdr_string(xdrs, &objp->arg1, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->arg2, ~0)) return (FALSE); return (TRUE); } bool_t xdr_fmd_adm_serdinfo_1_argument(XDR *xdrs, fmd_adm_serdinfo_1_argument *objp) { if (!xdr_string(xdrs, &objp->arg1, ~0)) return (FALSE); if (!xdr_string(xdrs, &objp->arg2, ~0)) return (FALSE); return (TRUE); } extern void fmd_adm_1(struct svc_req *, SVCXPRT *); extern bool_t xdr_fmd_stat(XDR *, struct fmd_stat *); #undef RW_READ_HELD #undef RW_WRITE_HELD #undef RW_LOCK_HELD #undef MUTEX_HELD /* * Please do not edit this file. * It was generated using rpcgen. */ #include "fmd_rpc_api.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ extern void fmd_api_1(struct svc_req *, SVCXPRT *); #undef RW_READ_HELD #undef RW_WRITE_HELD #undef RW_LOCK_HELD #undef MUTEX_HELD /* * 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) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * FMD Transport Subsystem * * A transport module uses some underlying mechanism to transport events. * This mechanism may use any underlying link-layer protocol and may support * additional link-layer packets unrelated to FMA. Some appropriate link- * layer mechanism to create the underlying connection is expected to be * called prior to calling fmd_xprt_open() itself. Alternatively, a transport * may be created in the suspended state by specifying the FMD_XPRT_SUSPENDED * flag as part of the call to fmd_xprt_open(), and then may be resumed later. * The underlying transport mechanism is *required* to provide ordering: that * is, the sequences of bytes written across the transport must be read by * the remote peer in the order that they are written, even across separate * calls to fmdo_send(). As an example, the Internet TCP protocol would be * a valid transport as it guarantees ordering, whereas the Internet UDP * protocol would not because UDP datagrams may be delivered in any order * as a result of delays introduced when datagrams pass through routers. * * Similar to sending events, a transport module receives events that are from * its peer remote endpoint using some transport-specific mechanism that is * unknown to FMD. As each event is received, the transport module is * responsible for constructing a valid nvlist_t object from the data and then * calling fmd_xprt_post() to post the event to the containing FMD's dispatch * queue, making it available to all local modules that are not transport * modules that have subscribed to the event. * * The following state machine is used for each transport. The initial state * is either SYN, ACK, or RUN, depending on the flags specified to xprt_create. * * FMD_XPRT_ACCEPT !FMD_XPRT_ACCEPT * | | * waiting +--v--+ +--v--+ waiting * for syn | SYN |--+ --+| ACK | for ack * event +-----+ \ / +-----+ event * | \ / | * drop all +--v--+ X +--v--+ send subscriptions, * events | ERR |<---+ +--->| SUB | recv subscriptions, * +-----+ +-----+ wait for run event * ^ | * | +-----+ | * +-----| RUN |<----+ * +--^--+ * | * FMD_XPRT_RDONLY * * When fmd_xprt_open() is called without FMD_XPRT_ACCEPT, the Common Transport * Layer enqueues a "syn" event for the module in its event queue and sets the * state to ACK. In state ACK, we are waiting for the transport to get an * "ack" event and call fmd_xprt_post() on this event. Other events will be * discarded. If an "ack" is received, we transition to state SUB. If a * configurable timeout occurs or if the "ack" is invalid (e.g. invalid version * exchange), we transition to state ERR. Once in state ERR, no further * operations are valid except fmd_xprt_close() and fmd_xprt_error() will * return a non-zero value to the caller indicating the transport has failed. * * When fmd_xprt_open() is called with FMD_XPRT_ACCEPT, the Common Transport * Layer assumes this transport is being used to accept a virtual connection * from a remote peer that is sending a "syn", and sets the initial state to * SYN. In this state, the transport waits for a "syn" event, validates it, * and then transitions to state SUB if it is valid or state ERR if it is not. * * Once in state SUB, the transport module is expected to receive a sequence of * zero or more "subscribe" events from the remote peer, followed by a "run" * event. Once in state RUN, the transport is active and any events can be * sent or received. The transport module is free to call fmd_xprt_close() * from any state. The fmd_xprt_error() function will return zero if the * transport is not in the ERR state, or non-zero if it is in the ERR state. * * Once the state machine reaches RUN, other FMA protocol events can be sent * and received across the transport in addition to the various control events. * * Table of Common Transport Layer Control Events * ============================================== * * FMA Class Payload * --------- ------- * resource.fm.xprt.uuclose string (uuid of case) * resource.fm.xprt.uuresolved string (uuid of case) * resource.fm.xprt.updated string (uuid of case) * resource.fm.xprt.subscribe string (class pattern) * resource.fm.xprt.unsubscribe string (class pattern) * resource.fm.xprt.unsuback string (class pattern) * resource.fm.xprt.syn version information * resource.fm.xprt.ack version information * resource.fm.xprt.run version information * * Control events are used to add and delete proxy subscriptions on the remote * transport peer module, and to set up connections. When a "syn" event is * sent, FMD will include in the payload the highest version of the FMA event * protocol that is supported by the sender. When a "syn" event is received, * the receiving FMD will use the minimum of this version and its version of * the protocol, and reply with this new minimum version in the "ack" event. * The receiver will then use this new minimum for subsequent event semantics. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The states shown above in the transport state machine diagram are encoded * using arrays of class patterns and a corresponding action function. These * arrays are then passed to fmd_xprt_transition() to change transport states. */ const fmd_xprt_rule_t _fmd_xprt_state_syn[] = { { "resource.fm.xprt.syn", fmd_xprt_event_syn }, { "*", fmd_xprt_event_error }, { NULL, NULL } }; const fmd_xprt_rule_t _fmd_xprt_state_ack[] = { { "resource.fm.xprt.ack", fmd_xprt_event_ack }, { "*", fmd_xprt_event_error }, }; const fmd_xprt_rule_t _fmd_xprt_state_err[] = { { "*", fmd_xprt_event_drop }, { NULL, NULL } }; const fmd_xprt_rule_t _fmd_xprt_state_sub[] = { { "resource.fm.xprt.subscribe", fmd_xprt_event_sub }, { "resource.fm.xprt.run", fmd_xprt_event_run }, { "resource.fm.xprt.*", fmd_xprt_event_error }, { "*", fmd_xprt_event_drop }, { NULL, NULL } }; const fmd_xprt_rule_t _fmd_xprt_state_run[] = { { "resource.fm.xprt.subscribe", fmd_xprt_event_sub }, { "resource.fm.xprt.unsubscribe", fmd_xprt_event_unsub }, { "resource.fm.xprt.unsuback", fmd_xprt_event_unsuback }, { "resource.fm.xprt.uuclose", fmd_xprt_event_uuclose }, { "resource.fm.xprt.uuresolved", fmd_xprt_event_uuresolved }, { "resource.fm.xprt.updated", fmd_xprt_event_updated }, { "resource.fm.xprt.*", fmd_xprt_event_error }, { NULL, NULL } }; /* * Template for per-transport statistics installed by fmd on behalf of each * transport. These are used to initialize the per-transport xi_stats. For * each statistic, the name is prepended with "fmd.xprt.%u", where %u is the * transport ID (xi_id) and then are inserted into the per-module stats hash. * The values in this array must match fmd_xprt_stat_t from . */ static const fmd_xprt_stat_t _fmd_xprt_stat_tmpl = { { { "dispatched", FMD_TYPE_UINT64, "total events dispatched to transport" }, { "dequeued", FMD_TYPE_UINT64, "total events dequeued by transport" }, { "prdequeued", FMD_TYPE_UINT64, "protocol events dequeued by transport" }, { "dropped", FMD_TYPE_UINT64, "total events dropped on queue overflow" }, { "wcnt", FMD_TYPE_UINT32, "count of events waiting on queue" }, { "wtime", FMD_TYPE_TIME, "total wait time on queue" }, { "wlentime", FMD_TYPE_TIME, "total wait length * time product" }, { "wlastupdate", FMD_TYPE_TIME, "hrtime of last wait queue update" }, { "dtime", FMD_TYPE_TIME, "total processing time after dequeue" }, { "dlastupdate", FMD_TYPE_TIME, "hrtime of last event dequeue completion" }, }, { "module", FMD_TYPE_STRING, "module that owns this transport" }, { "authority", FMD_TYPE_STRING, "authority associated with this transport" }, { "state", FMD_TYPE_STRING, "current transport state" }, { "received", FMD_TYPE_UINT64, "events received by transport" }, { "discarded", FMD_TYPE_UINT64, "bad events discarded by transport" }, { "retried", FMD_TYPE_UINT64, "retries requested of transport" }, { "replayed", FMD_TYPE_UINT64, "events replayed by transport" }, { "lost", FMD_TYPE_UINT64, "events lost by transport" }, { "timeouts", FMD_TYPE_UINT64, "events received by transport with ttl=0" }, { "subscriptions", FMD_TYPE_UINT64, "subscriptions registered to transport" }, }; static void fmd_xprt_class_hash_create(fmd_xprt_class_hash_t *xch, fmd_eventq_t *eq) { uint_t hashlen = fmd.d_str_buckets; xch->xch_queue = eq; xch->xch_hashlen = hashlen; xch->xch_hash = fmd_zalloc(sizeof (void *) * hashlen, FMD_SLEEP); } static void fmd_xprt_class_hash_destroy(fmd_xprt_class_hash_t *xch) { fmd_eventq_t *eq = xch->xch_queue; fmd_xprt_class_t *xcp, *ncp; uint_t i; for (i = 0; i < xch->xch_hashlen; i++) { for (xcp = xch->xch_hash[i]; xcp != NULL; xcp = ncp) { ncp = xcp->xc_next; if (eq != NULL) fmd_dispq_delete(fmd.d_disp, eq, xcp->xc_class); fmd_strfree(xcp->xc_class); fmd_free(xcp, sizeof (fmd_xprt_class_t)); } } fmd_free(xch->xch_hash, sizeof (void *) * xch->xch_hashlen); } /* * Insert the specified class into the specified class hash, and return the * reference count. A return value of one indicates this is the first insert. * If an eventq is associated with the hash, insert a dispq subscription for it. */ static uint_t fmd_xprt_class_hash_insert(fmd_xprt_impl_t *xip, fmd_xprt_class_hash_t *xch, const char *class) { uint_t h = fmd_strhash(class) % xch->xch_hashlen; fmd_xprt_class_t *xcp; ASSERT(MUTEX_HELD(&xip->xi_lock)); for (xcp = xch->xch_hash[h]; xcp != NULL; xcp = xcp->xc_next) { if (strcmp(class, xcp->xc_class) == 0) return (++xcp->xc_refs); } xcp = fmd_alloc(sizeof (fmd_xprt_class_t), FMD_SLEEP); xcp->xc_class = fmd_strdup(class, FMD_SLEEP); xcp->xc_next = xch->xch_hash[h]; xcp->xc_refs = 1; xch->xch_hash[h] = xcp; if (xch->xch_queue != NULL) fmd_dispq_insert(fmd.d_disp, xch->xch_queue, class); return (xcp->xc_refs); } /* * Delete the specified class from the specified class hash, and return the * reference count. A return value of zero indicates the class was deleted. * If an eventq is associated with the hash, delete the dispq subscription. */ static uint_t fmd_xprt_class_hash_delete(fmd_xprt_impl_t *xip, fmd_xprt_class_hash_t *xch, const char *class) { uint_t h = fmd_strhash(class) % xch->xch_hashlen; fmd_xprt_class_t *xcp, **pp; ASSERT(MUTEX_HELD(&xip->xi_lock)); pp = &xch->xch_hash[h]; for (xcp = *pp; xcp != NULL; xcp = xcp->xc_next) { if (strcmp(class, xcp->xc_class) == 0) break; else pp = &xcp->xc_next; } if (xcp == NULL) return (-1U); /* explicitly permit an invalid delete */ if (--xcp->xc_refs != 0) return (xcp->xc_refs); ASSERT(xcp->xc_refs == 0); *pp = xcp->xc_next; fmd_strfree(xcp->xc_class); fmd_free(xcp, sizeof (fmd_xprt_class_t)); if (xch->xch_queue != NULL) fmd_dispq_delete(fmd.d_disp, xch->xch_queue, class); return (0); } /* * Queue subscribe events for the specified transport corresponding to all of * the active module subscriptions. This is an extremely heavyweight operation * that we expect to take place rarely (i.e. when loading a transport module * or when it establishes a connection). We lock all of the known modules to * prevent them from adding or deleting subscriptions, then snapshot their * subscriptions, and then unlock all of the modules. We hold the modhash * lock for the duration of this operation to prevent new modules from loading. */ static void fmd_xprt_subscribe_modhash(fmd_xprt_impl_t *xip, fmd_modhash_t *mhp) { fmd_xprt_t *xp = (fmd_xprt_t *)xip; const fmd_conf_path_t *pap; fmd_module_t *mp; uint_t i, j; (void) pthread_rwlock_rdlock(&mhp->mh_lock); for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) fmd_module_lock(mp); } (void) pthread_mutex_lock(&xip->xi_lock); ASSERT(!(xip->xi_flags & FMD_XPRT_SUBSCRIBER)); xip->xi_flags |= FMD_XPRT_SUBSCRIBER; (void) pthread_mutex_unlock(&xip->xi_lock); for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) { (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap); for (j = 0; j < pap->cpa_argc; j++) fmd_xprt_subscribe(xp, pap->cpa_argv[j]); } } for (i = 0; i < mhp->mh_hashlen; i++) { for (mp = mhp->mh_hash[i]; mp != NULL; mp = mp->mod_next) fmd_module_unlock(mp); } (void) pthread_rwlock_unlock(&mhp->mh_lock); } static void fmd_xprt_transition(fmd_xprt_impl_t *xip, const fmd_xprt_rule_t *state, const char *tag) { fmd_xprt_t *xp = (fmd_xprt_t *)xip; fmd_event_t *e; nvlist_t *nvl; char *s; TRACE((FMD_DBG_XPRT, "xprt %u -> %s\n", xip->xi_id, tag)); xip->xi_state = state; s = fmd_strdup(tag, FMD_SLEEP); (void) pthread_mutex_lock(&xip->xi_stats_lock); fmd_strfree(xip->xi_stats->xs_state.fmds_value.str); xip->xi_stats->xs_state.fmds_value.str = s; (void) pthread_mutex_unlock(&xip->xi_stats_lock); /* * If we've reached the SUB state, take out the big hammer and snapshot * all of the subscriptions of all of the loaded modules. Then queue a * run event for our remote peer indicating that it can enter RUN. */ if (state == _fmd_xprt_state_sub) { fmd_xprt_subscribe_modhash(xip, fmd.d_mod_hash); /* * For read-write transports, we always want to set up remote * subscriptions to the bultin list.* events, regardless of * whether any agents have subscribed to them. */ if (xip->xi_flags & FMD_XPRT_RDWR) { fmd_xprt_subscribe(xp, FM_LIST_SUSPECT_CLASS); fmd_xprt_subscribe(xp, FM_LIST_ISOLATED_CLASS); fmd_xprt_subscribe(xp, FM_LIST_UPDATED_CLASS); fmd_xprt_subscribe(xp, FM_LIST_RESOLVED_CLASS); fmd_xprt_subscribe(xp, FM_LIST_REPAIRED_CLASS); } nvl = fmd_protocol_xprt_ctl(xip->xi_queue->eq_mod, "resource.fm.xprt.run", xip->xi_version); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } } static void fmd_xprt_authupdate(fmd_xprt_impl_t *xip) { char *s = fmd_fmri_auth2str(xip->xi_auth); (void) pthread_mutex_lock(&xip->xi_stats_lock); fmd_strfree(xip->xi_stats->xs_authority.fmds_value.str); xip->xi_stats->xs_authority.fmds_value.str = s; (void) pthread_mutex_unlock(&xip->xi_stats_lock); } static int fmd_xprt_vmismatch(fmd_xprt_impl_t *xip, nvlist_t *nvl, uint_t *rversionp) { uint8_t rversion; if (nvlist_lookup_uint8(nvl, FM_VERSION, &rversion) != 0) { (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_discarded.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); fmd_xprt_transition(xip, _fmd_xprt_state_err, "ERR"); return (1); } if (rversion > xip->xi_version) { fmd_dprintf(FMD_DBG_XPRT, "xprt %u protocol mismatch: %u>%u\n", xip->xi_id, rversion, xip->xi_version); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_discarded.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); fmd_xprt_transition(xip, _fmd_xprt_state_err, "ERR"); return (1); } if (rversionp != NULL) *rversionp = rversion; return (0); } void fmd_xprt_event_syn(fmd_xprt_impl_t *xip, nvlist_t *nvl) { fmd_event_t *e; uint_t vers; char *class; if (fmd_xprt_vmismatch(xip, nvl, &vers)) return; /* transitioned to error state */ /* * If the transport module didn't specify an authority, extract the * one that is passed along with the xprt.syn event and use that. */ if (xip->xi_auth == NULL && nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &nvl) == 0 && nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &nvl) == 0) { (void) nvlist_xdup(nvl, &xip->xi_auth, &fmd.d_nva); fmd_xprt_authupdate(xip); } nvl = fmd_protocol_xprt_ctl(xip->xi_queue->eq_mod, "resource.fm.xprt.ack", xip->xi_version); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_eventq_insert_at_time(xip->xi_queue, e); xip->xi_version = MIN(FM_RSRC_XPRT_VERSION, vers); fmd_xprt_transition(xip, _fmd_xprt_state_sub, "SUB"); } void fmd_xprt_event_ack(fmd_xprt_impl_t *xip, nvlist_t *nvl) { uint_t vers; if (fmd_xprt_vmismatch(xip, nvl, &vers)) return; /* transitioned to error state */ /* * If the transport module didn't specify an authority, extract the * one that is passed along with the xprt.syn event and use that. */ if (xip->xi_auth == NULL && nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &nvl) == 0 && nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &nvl) == 0) { (void) nvlist_xdup(nvl, &xip->xi_auth, &fmd.d_nva); fmd_xprt_authupdate(xip); } xip->xi_version = MIN(FM_RSRC_XPRT_VERSION, vers); fmd_xprt_transition(xip, _fmd_xprt_state_sub, "SUB"); } /* * Upon transition to RUN, we take every solved case and resend a list.suspect * event for it to our remote peer. If a case transitions from solved to a * future state (CLOSE_WAIT, CLOSED, or REPAIRED) while we are iterating over * the case hash, we will get it as part of examining the resource cache, next. */ static void fmd_xprt_send_case(fmd_case_t *cp, void *arg) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_xprt_impl_t *xip = arg; fmd_event_t *e; nvlist_t *nvl; char *class; if (cip->ci_state != FMD_CASE_SOLVED) return; nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dprintf(FMD_DBG_XPRT, "re-send %s for %s to transport %u\n", FM_LIST_SUSPECT_CLASS, cip->ci_uuid, xip->xi_id); fmd_dispq_dispatch_gid(fmd.d_disp, e, class, xip->xi_queue->eq_sgid); } /* * Similar to the above function, but for use with readonly transport. Puts * the event on the module's queue such that it's fmdo_recv function can pick * it up and send it if appropriate. */ static void fmd_xprt_send_case_ro(fmd_case_t *cp, void *arg) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_module_t *mp = arg; fmd_event_t *e; nvlist_t *nvl; char *class; if (cip->ci_state != FMD_CASE_SOLVED) return; nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_dprintf(FMD_DBG_XPRT, "re-send %s for %s to rdonly transport %s\n", FM_LIST_SUSPECT_CLASS, cip->ci_uuid, mp->mod_name); fmd_dispq_dispatch_gid(fmd.d_disp, e, class, mp->mod_queue->eq_sgid); } void fmd_xprt_event_run(fmd_xprt_impl_t *xip, nvlist_t *nvl) { if (!fmd_xprt_vmismatch(xip, nvl, NULL)) { fmd_xprt_transition(xip, _fmd_xprt_state_run, "RUN"); fmd_case_hash_apply(fmd.d_cases, fmd_xprt_send_case, xip); } } void fmd_xprt_event_sub(fmd_xprt_impl_t *xip, nvlist_t *nvl) { char *class; if (fmd_xprt_vmismatch(xip, nvl, NULL)) return; /* transitioned to error state */ if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_SUBCLASS, &class) != 0) return; /* malformed protocol event */ (void) pthread_mutex_lock(&xip->xi_lock); (void) fmd_xprt_class_hash_insert(xip, &xip->xi_lsub, class); (void) pthread_mutex_unlock(&xip->xi_lock); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_subscriptions.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); } void fmd_xprt_event_unsub(fmd_xprt_impl_t *xip, nvlist_t *nvl) { fmd_event_t *e; char *class; if (fmd_xprt_vmismatch(xip, nvl, NULL)) return; /* transitioned to error state */ if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_SUBCLASS, &class) != 0) return; /* malformed protocol event */ (void) pthread_mutex_lock(&xip->xi_lock); (void) fmd_xprt_class_hash_delete(xip, &xip->xi_lsub, class); (void) pthread_mutex_unlock(&xip->xi_lock); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_subscriptions.fmds_value.ui64--; (void) pthread_mutex_unlock(&xip->xi_stats_lock); nvl = fmd_protocol_xprt_sub(xip->xi_queue->eq_mod, "resource.fm.xprt.unsuback", xip->xi_version, class); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class); fmd_eventq_insert_at_time(xip->xi_queue, e); } void fmd_xprt_event_unsuback(fmd_xprt_impl_t *xip, nvlist_t *nvl) { char *class; if (fmd_xprt_vmismatch(xip, nvl, NULL)) return; /* transitioned to error state */ if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_SUBCLASS, &class) != 0) return; /* malformed protocol event */ (void) pthread_mutex_lock(&xip->xi_lock); (void) fmd_xprt_class_hash_delete(xip, &xip->xi_usub, class); (void) pthread_mutex_unlock(&xip->xi_lock); } /* * on diagnosing side, receive a uuclose from the proxy. */ void fmd_xprt_event_uuclose(fmd_xprt_impl_t *xip, nvlist_t *nvl) { fmd_case_t *cp; char *uuid; if (fmd_xprt_vmismatch(xip, nvl, NULL)) return; /* transitioned to error state */ if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_UUID, &uuid) == 0 && (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) { /* * update resource cache status and transition case */ fmd_case_close_status(cp); fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED); fmd_case_rele(cp); } } /* * on diagnosing side, receive a uuresolved from the proxy. */ void fmd_xprt_event_uuresolved(fmd_xprt_impl_t *xip, nvlist_t *nvl) { fmd_case_t *cp; char *uuid; if (fmd_xprt_vmismatch(xip, nvl, NULL)) return; /* transitioned to error state */ if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_UUID, &uuid) == 0 && (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; fmd_case_transition(cp, (cip->ci_state == FMD_CASE_REPAIRED) ? FMD_CASE_RESOLVED : (cip->ci_state == FMD_CASE_CLOSED) ? FMD_CASE_REPAIRED : FMD_CASE_CLOSE_WAIT, FMD_CF_RESOLVED); fmd_case_rele(cp); } } /* * on diagnosing side, receive a repair/acquit from the proxy. */ void fmd_xprt_event_updated(fmd_xprt_impl_t *xip, nvlist_t *nvl) { fmd_case_t *cp; char *uuid; if (fmd_xprt_vmismatch(xip, nvl, NULL)) return; /* transitioned to error state */ if (nvlist_lookup_string(nvl, FM_RSRC_XPRT_UUID, &uuid) == 0 && (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) { uint8_t *statusp, *proxy_asrup = NULL; uint_t nelem = 0; /* * Only update status with new repairs if "no remote repair" * is not set. Do the case_update anyway though (as this will * refresh the status on the proxy side). */ if (!(xip->xi_flags & FMD_XPRT_NO_REMOTE_REPAIR)) { if (nvlist_lookup_uint8_array(nvl, FM_RSRC_XPRT_FAULT_STATUS, &statusp, &nelem) == 0 && nelem != 0) { (void) nvlist_lookup_uint8_array(nvl, FM_RSRC_XPRT_FAULT_HAS_ASRU, &proxy_asrup, &nelem); fmd_case_update_status(cp, statusp, proxy_asrup, NULL); } fmd_case_update_containees(cp); } fmd_case_update(cp); fmd_case_rele(cp); } } void fmd_xprt_event_error(fmd_xprt_impl_t *xip, nvlist_t *nvl) { char *class = ""; (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_discarded.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); TRACE((FMD_DBG_XPRT, "xprt %u bad event %s\n", xip->xi_id, class)); fmd_xprt_transition(xip, _fmd_xprt_state_err, "ERR"); } void fmd_xprt_event_drop(fmd_xprt_impl_t *xip, nvlist_t *nvl) { char *class = ""; (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_discarded.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); (void) nvlist_lookup_string(nvl, FM_CLASS, &class); TRACE((FMD_DBG_XPRT, "xprt %u drop event %s\n", xip->xi_id, class)); } fmd_xprt_t * fmd_xprt_create(fmd_module_t *mp, uint_t flags, nvlist_t *auth, void *data) { fmd_xprt_impl_t *xip = fmd_zalloc(sizeof (fmd_xprt_impl_t), FMD_SLEEP); fmd_stat_t *statv; uint_t i, statc; char buf[PATH_MAX]; fmd_event_t *e; nvlist_t *nvl; char *s; (void) pthread_mutex_init(&xip->xi_lock, NULL); (void) pthread_cond_init(&xip->xi_cv, NULL); (void) pthread_mutex_init(&xip->xi_stats_lock, NULL); xip->xi_auth = auth; xip->xi_data = data; xip->xi_version = FM_RSRC_XPRT_VERSION; xip->xi_flags = flags; /* * Grab fmd.d_xprt_lock to block fmd_xprt_suspend_all() and then create * a transport ID and make it visible in fmd.d_xprt_ids. If transports * were previously suspended, set the FMD_XPRT_DSUSPENDED flag on us to * ensure that this transport will not run until fmd_xprt_resume_all(). */ (void) pthread_mutex_lock(&fmd.d_xprt_lock); xip->xi_id = fmd_idspace_alloc(fmd.d_xprt_ids, xip); if (fmd.d_xprt_suspend != 0) xip->xi_flags |= FMD_XPRT_DSUSPENDED; (void) pthread_mutex_unlock(&fmd.d_xprt_lock); /* * If the module has not yet finished _fmd_init(), set the ISUSPENDED * bit so that fmdo_send() is not called until _fmd_init() completes. */ if (!(mp->mod_flags & FMD_MOD_INIT)) xip->xi_flags |= FMD_XPRT_ISUSPENDED; /* * Initialize the transport statistics that we keep on behalf of fmd. * These are set up using a template defined at the top of this file. * We rename each statistic with a prefix ensuring its uniqueness. */ statc = sizeof (_fmd_xprt_stat_tmpl) / sizeof (fmd_stat_t); statv = fmd_alloc(sizeof (_fmd_xprt_stat_tmpl), FMD_SLEEP); bcopy(&_fmd_xprt_stat_tmpl, statv, sizeof (_fmd_xprt_stat_tmpl)); for (i = 0; i < statc; i++) { (void) snprintf(statv[i].fmds_name, sizeof (statv[i].fmds_name), "fmd.xprt.%u.%s", xip->xi_id, ((fmd_stat_t *)&_fmd_xprt_stat_tmpl + i)->fmds_name); } xip->xi_stats = (fmd_xprt_stat_t *)fmd_ustat_insert( mp->mod_ustat, FMD_USTAT_NOALLOC, statc, statv, NULL); if (xip->xi_stats == NULL) fmd_panic("failed to create xi_stats (%p)\n", (void *)statv); xip->xi_stats->xs_module.fmds_value.str = fmd_strdup(mp->mod_name, FMD_SLEEP); if (xip->xi_auth != NULL) fmd_xprt_authupdate(xip); /* * Create the outbound eventq for this transport and link to its stats. * If any suspend bits were set above, suspend the eventq immediately. */ xip->xi_queue = fmd_eventq_create(mp, &xip->xi_stats->xs_evqstat, &xip->xi_stats_lock, mp->mod_stats->ms_xprtqlimit.fmds_value.ui32); if (xip->xi_flags & FMD_XPRT_SMASK) fmd_eventq_suspend(xip->xi_queue); /* * Create our subscription hashes: local subscriptions go to xi_queue, * remote subscriptions are tracked only for protocol requests, and * pending unsubscriptions are associated with the /dev/null eventq. */ fmd_xprt_class_hash_create(&xip->xi_lsub, xip->xi_queue); fmd_xprt_class_hash_create(&xip->xi_rsub, NULL); fmd_xprt_class_hash_create(&xip->xi_usub, fmd.d_rmod->mod_queue); /* * Determine our initial state based upon the creation flags. If we're * read-only, go directly to RUN. If we're accepting a new connection, * wait for a SYN. Otherwise send a SYN and wait for an ACK. */ if ((flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) { /* * Send the list.suspects across here for readonly transports. * For read-write transport they will be sent on transition to * RUN state in fmd_xprt_event_run(). */ fmd_case_hash_apply(fmd.d_cases, fmd_xprt_send_case_ro, mp); fmd_xprt_transition(xip, _fmd_xprt_state_run, "RUN"); } else if (flags & FMD_XPRT_ACCEPT) fmd_xprt_transition(xip, _fmd_xprt_state_syn, "SYN"); else fmd_xprt_transition(xip, _fmd_xprt_state_ack, "ACK"); /* * If client.xprtlog is set to TRUE, create a debugging log for the * events received by the transport in var/fm/fmd/xprt/. */ (void) fmd_conf_getprop(fmd.d_conf, "client.xprtlog", &i); (void) fmd_conf_getprop(fmd.d_conf, "log.xprt", &s); if (i) { (void) snprintf(buf, sizeof (buf), "%s/%u.log", s, xip->xi_id); xip->xi_log = fmd_log_open(fmd.d_rootdir, buf, FMD_LOG_XPRT); } ASSERT(fmd_module_locked(mp)); fmd_list_append(&mp->mod_transports, xip); (void) pthread_mutex_lock(&mp->mod_stats_lock); mp->mod_stats->ms_xprtopen.fmds_value.ui32++; (void) pthread_mutex_unlock(&mp->mod_stats_lock); /* * If this is a read-only transport, return without creating a send * queue thread and setting up any connection events in our queue. */ if ((flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) goto out; /* * Once the transport is fully initialized, create a send queue thread * and start any connect events flowing to complete our initialization. */ if ((xip->xi_thread = fmd_thread_create(mp, (fmd_thread_f *)fmd_xprt_send, xip)) == NULL) { fmd_error(EFMD_XPRT_THR, "failed to create thread for transport %u", xip->xi_id); fmd_xprt_destroy((fmd_xprt_t *)xip); (void) fmd_set_errno(EFMD_XPRT_THR); return (NULL); } /* * If the transport is not being opened to accept an inbound connect, * start an outbound connection by enqueuing a SYN event for our peer. */ if (!(flags & FMD_XPRT_ACCEPT)) { nvl = fmd_protocol_xprt_ctl(mp, "resource.fm.xprt.syn", FM_RSRC_XPRT_VERSION); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } out: fmd_dprintf(FMD_DBG_XPRT, "opened transport %u\n", xip->xi_id); return ((fmd_xprt_t *)xip); } void fmd_xprt_destroy(fmd_xprt_t *xp) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; fmd_module_t *mp = xip->xi_queue->eq_mod; uint_t id = xip->xi_id; fmd_case_impl_t *cip, *nip; fmd_stat_t *sp; uint_t i, n; ASSERT(fmd_module_locked(mp)); fmd_list_delete(&mp->mod_transports, xip); (void) pthread_mutex_lock(&mp->mod_stats_lock); mp->mod_stats->ms_xprtopen.fmds_value.ui32--; (void) pthread_mutex_unlock(&mp->mod_stats_lock); (void) pthread_mutex_lock(&xip->xi_lock); while (xip->xi_busy != 0) (void) pthread_cond_wait(&xip->xi_cv, &xip->xi_lock); /* * Remove the transport from global visibility, cancel its send-side * thread, join with it, and then remove the transport from module * visibility. Once all this is done, destroy and free the transport. */ (void) fmd_idspace_free(fmd.d_xprt_ids, xip->xi_id); if (xip->xi_thread != NULL) { fmd_eventq_abort(xip->xi_queue); fmd_module_unlock(mp); fmd_thread_destroy(xip->xi_thread, FMD_THREAD_JOIN); fmd_module_lock(mp); } if (xip->xi_log != NULL) fmd_log_rele(xip->xi_log); /* * Release every case handle in the module that was cached by this * transport. This will result in these cases disappearing from the * local case hash so that fmd_case_uuclose() and fmd_case_repaired() * etc can no longer be used. */ for (cip = fmd_list_next(&mp->mod_cases); cip != NULL; cip = nip) { nip = fmd_list_next(cip); if (cip->ci_xprt == xp) fmd_case_discard((fmd_case_t *)cip, B_TRUE); } /* * Destroy every class in the various subscription hashes and remove * any corresponding subscriptions from the event dispatch queue. */ fmd_xprt_class_hash_destroy(&xip->xi_lsub); fmd_xprt_class_hash_destroy(&xip->xi_rsub); fmd_xprt_class_hash_destroy(&xip->xi_usub); /* * Uniquify the stat names exactly as was done in fmd_xprt_create() * before calling fmd_ustat_insert(), otherwise fmd_ustat_delete() * won't find the entries in the hash table. */ n = sizeof (_fmd_xprt_stat_tmpl) / sizeof (fmd_stat_t); sp = fmd_alloc(sizeof (_fmd_xprt_stat_tmpl), FMD_SLEEP); bcopy(&_fmd_xprt_stat_tmpl, sp, sizeof (_fmd_xprt_stat_tmpl)); for (i = 0; i < n; i++) { (void) snprintf(sp[i].fmds_name, sizeof (sp[i].fmds_name), "fmd.xprt.%u.%s", xip->xi_id, ((fmd_stat_t *)&_fmd_xprt_stat_tmpl + i)->fmds_name); } fmd_ustat_delete(mp->mod_ustat, n, sp); fmd_free(sp, sizeof (_fmd_xprt_stat_tmpl)); fmd_free(xip->xi_stats, sizeof (fmd_xprt_stat_t)); fmd_eventq_destroy(xip->xi_queue); nvlist_free(xip->xi_auth); fmd_free(xip, sizeof (fmd_xprt_impl_t)); fmd_dprintf(FMD_DBG_XPRT, "closed transport %u\n", id); } void fmd_xprt_xsuspend(fmd_xprt_t *xp, uint_t flags) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; uint_t oflags; ASSERT((flags & ~FMD_XPRT_SMASK) == 0); (void) pthread_mutex_lock(&xip->xi_lock); oflags = xip->xi_flags; xip->xi_flags |= flags; if (!(oflags & FMD_XPRT_SMASK) && (xip->xi_flags & FMD_XPRT_SMASK) != 0) fmd_eventq_suspend(xip->xi_queue); (void) pthread_cond_broadcast(&xip->xi_cv); while (xip->xi_busy != 0) (void) pthread_cond_wait(&xip->xi_cv, &xip->xi_lock); (void) pthread_mutex_unlock(&xip->xi_lock); } void fmd_xprt_xresume(fmd_xprt_t *xp, uint_t flags) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; uint_t oflags; ASSERT((flags & ~FMD_XPRT_SMASK) == 0); (void) pthread_mutex_lock(&xip->xi_lock); oflags = xip->xi_flags; xip->xi_flags &= ~flags; if ((oflags & FMD_XPRT_SMASK) != 0 && !(xip->xi_flags & FMD_XPRT_SMASK)) fmd_eventq_resume(xip->xi_queue); (void) pthread_cond_broadcast(&xip->xi_cv); (void) pthread_mutex_unlock(&xip->xi_lock); } void fmd_xprt_send(fmd_xprt_t *xp) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; fmd_module_t *mp = xip->xi_queue->eq_mod; fmd_event_t *ep; int err; while ((ep = fmd_eventq_delete(xip->xi_queue)) != NULL) { if (FMD_EVENT_TTL(ep) == 0) { fmd_event_rele(ep); continue; } fmd_dprintf(FMD_DBG_XPRT, "xprt %u sending %s\n", xip->xi_id, (char *)FMD_EVENT_DATA(ep)); err = mp->mod_ops->mop_transport(mp, xp, ep); fmd_eventq_done(xip->xi_queue); if (err == FMD_SEND_RETRY) { fmd_eventq_insert_at_time(xip->xi_queue, ep); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_retried.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); } if (err != FMD_SEND_SUCCESS && err != FMD_SEND_RETRY) { (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_lost.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); } fmd_event_rele(ep); } } /* * This function creates a local suspect list. This is used when a suspect list * is created directly by an external source like fminject. */ static void fmd_xprt_list_suspect_local(fmd_xprt_t *xp, nvlist_t *nvl) { nvlist_t **nvlp; nvlist_t *de_fmri, *de_fmri_dup = NULL; int64_t *diag_time; char *code = NULL; fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; fmd_case_t *cp; uint_t nelem = 0, nelem2 = 0, i; boolean_t injected; fmd_module_lock(xip->xi_queue->eq_mod); cp = fmd_case_create(xip->xi_queue->eq_mod, NULL, NULL); if (cp == NULL) { fmd_module_unlock(xip->xi_queue->eq_mod); return; } /* * copy diag_code if present */ (void) nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code); if (code != NULL) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; cip->ci_precanned = 1; fmd_case_setcode(cp, code); } /* * copy suspects */ (void) nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &nvlp, &nelem); for (i = 0; i < nelem; i++) { nvlist_t *flt_copy, *asru = NULL, *fru = NULL, *rsrc = NULL; topo_hdl_t *thp; char *loc = NULL; int err; thp = fmd_fmri_topo_hold(TOPO_VERSION); (void) nvlist_xdup(nvlp[i], &flt_copy, &fmd.d_nva); (void) nvlist_lookup_nvlist(nvlp[i], FM_FAULT_RESOURCE, &rsrc); /* * If no fru specified, get it from topo */ if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_FRU, &fru) != 0 && rsrc && topo_fmri_fru(thp, rsrc, &fru, &err) == 0) (void) nvlist_add_nvlist(flt_copy, FM_FAULT_FRU, fru); /* * If no asru specified, get it from topo */ if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_ASRU, &asru) != 0 && rsrc && topo_fmri_asru(thp, rsrc, &asru, &err) == 0) (void) nvlist_add_nvlist(flt_copy, FM_FAULT_ASRU, asru); /* * If no location specified, get it from topo */ if (nvlist_lookup_string(nvlp[i], FM_FAULT_LOCATION, &loc) != 0) { if (fru && topo_fmri_label(thp, fru, &loc, &err) == 0) (void) nvlist_add_string(flt_copy, FM_FAULT_LOCATION, loc); else if (rsrc && topo_fmri_label(thp, rsrc, &loc, &err) == 0) (void) nvlist_add_string(flt_copy, FM_FAULT_LOCATION, loc); if (loc) topo_hdl_strfree(thp, loc); } nvlist_free(fru); nvlist_free(asru); nvlist_free(rsrc); fmd_fmri_topo_rele(thp); fmd_case_insert_suspect(cp, flt_copy); } /* * copy diag_time if present */ if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME, &diag_time, &nelem2) == 0 && nelem2 >= 2) fmd_case_settime(cp, diag_time[0], diag_time[1]); /* * copy DE fmri if present */ if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &de_fmri) == 0) { (void) nvlist_xdup(de_fmri, &de_fmri_dup, &fmd.d_nva); fmd_case_set_de_fmri(cp, de_fmri_dup); } /* * copy injected if present */ if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_INJECTED, &injected) == 0 && injected) fmd_case_set_injected(cp); fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED); fmd_module_unlock(xip->xi_queue->eq_mod); } /* * This function is called to create a proxy case on receipt of a list.suspect * from the diagnosing side of the transport. */ static void fmd_xprt_list_suspect(fmd_xprt_t *xp, nvlist_t *nvl) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; nvlist_t **nvlp; uint_t nelem = 0, nelem2 = 0, i; int64_t *diag_time; topo_hdl_t *thp; char *class; nvlist_t *rsrc, *asru, *de_fmri, *de_fmri_dup = NULL; nvlist_t *flt_copy; int err; nvlist_t **asrua; uint8_t *proxy_asru = NULL; int got_proxy_asru = 0; int got_hc_rsrc = 0; int got_hc_asru = 0; int got_present_rsrc = 0; uint8_t *diag_asru = NULL; char *scheme; uint8_t *statusp; char *uuid, *code; fmd_case_t *cp; fmd_case_impl_t *cip; int need_update = 0; boolean_t injected; if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0) return; if (nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code) != 0) return; (void) nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &nvlp, &nelem); /* * In order to implement FMD_XPRT_HCONLY and FMD_XPRT_HC_PRESENT_ONLY * etc we first scan the suspects to see if * - there was an asru in the received fault * - there was an hc-scheme resource in the received fault * - any hc-scheme resource in the received fault is present in the * local topology * - any hc-scheme resource in the received fault has an asru in the * local topology */ if (nelem > 0) { asrua = fmd_zalloc(sizeof (nvlist_t *) * nelem, FMD_SLEEP); proxy_asru = fmd_zalloc(sizeof (uint8_t) * nelem, FMD_SLEEP); diag_asru = fmd_zalloc(sizeof (uint8_t) * nelem, FMD_SLEEP); thp = fmd_fmri_topo_hold(TOPO_VERSION); for (i = 0; i < nelem; i++) { if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_ASRU, &asru) == 0 && asru != NULL) diag_asru[i] = 1; if (nvlist_lookup_string(nvlp[i], FM_CLASS, &class) != 0 || strncmp(class, "fault", 5) != 0) continue; /* * If there is an hc-scheme asru, use that to find the * real asru. Otherwise if there is an hc-scheme * resource, work out the old asru from that. * This order is to allow a two stage evaluation * of the asru where a fault in the diagnosing side * is in a component not visible to the proxy side, * but prevents a component that is visible from * working. So the diagnosing side sets the asru to * the latter component (in hc-scheme as the diagnosing * side doesn't know about the proxy side's virtual * schemes), and then the proxy side can convert that * to a suitable virtual scheme asru. */ if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_ASRU, &asru) == 0 && asru != NULL && nvlist_lookup_string(asru, FM_FMRI_SCHEME, &scheme) == 0 && strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { got_hc_asru = 1; if (xip->xi_flags & FMD_XPRT_EXTERNAL) continue; if (topo_fmri_present(thp, asru, &err) != 0) got_present_rsrc = 1; if (topo_fmri_asru(thp, asru, &asrua[i], &err) == 0) { proxy_asru[i] = FMD_PROXY_ASRU_FROM_ASRU; got_proxy_asru = 1; } } else if (nvlist_lookup_nvlist(nvlp[i], FM_FAULT_RESOURCE, &rsrc) == 0 && rsrc != NULL && nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) == 0 && strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { got_hc_rsrc = 1; if (xip->xi_flags & FMD_XPRT_EXTERNAL) continue; if (topo_fmri_present(thp, rsrc, &err) != 0) got_present_rsrc = 1; if (topo_fmri_asru(thp, rsrc, &asrua[i], &err) == 0) { proxy_asru[i] = FMD_PROXY_ASRU_FROM_RSRC; got_proxy_asru = 1; } } } fmd_fmri_topo_rele(thp); } /* * If we're set up only to report hc-scheme faults, and * there aren't any, then just drop the event. */ if (got_hc_rsrc == 0 && got_hc_asru == 0 && (xip->xi_flags & FMD_XPRT_HCONLY)) { if (nelem > 0) { fmd_free(proxy_asru, sizeof (uint8_t) * nelem); fmd_free(diag_asru, sizeof (uint8_t) * nelem); fmd_free(asrua, sizeof (nvlist_t *) * nelem); } return; } /* * If we're set up only to report locally present hc-scheme * faults, and there aren't any, then just drop the event. */ if (got_present_rsrc == 0 && (xip->xi_flags & FMD_XPRT_HC_PRESENT_ONLY)) { if (nelem > 0) { for (i = 0; i < nelem; i++) nvlist_free(asrua[i]); fmd_free(proxy_asru, sizeof (uint8_t) * nelem); fmd_free(diag_asru, sizeof (uint8_t) * nelem); fmd_free(asrua, sizeof (nvlist_t *) * nelem); } return; } /* * If fmd_case_recreate() returns NULL, UUID is already known. */ fmd_module_lock(xip->xi_queue->eq_mod); if ((cp = fmd_case_recreate(xip->xi_queue->eq_mod, xp, FMD_CASE_UNSOLVED, uuid, code)) == NULL) { if (nelem > 0) { for (i = 0; i < nelem; i++) nvlist_free(asrua[i]); fmd_free(proxy_asru, sizeof (uint8_t) * nelem); fmd_free(diag_asru, sizeof (uint8_t) * nelem); fmd_free(asrua, sizeof (nvlist_t *) * nelem); } fmd_module_unlock(xip->xi_queue->eq_mod); return; } cip = (fmd_case_impl_t *)cp; cip->ci_diag_asru = diag_asru; cip->ci_proxy_asru = proxy_asru; for (i = 0; i < nelem; i++) { (void) nvlist_xdup(nvlp[i], &flt_copy, &fmd.d_nva); if (proxy_asru[i] != FMD_PROXY_ASRU_NOT_NEEDED) { /* * Copy suspects, but remove/replace asru first. Also if * the original asru was hc-scheme use that as resource. */ if (proxy_asru[i] == FMD_PROXY_ASRU_FROM_ASRU) { (void) nvlist_remove(flt_copy, FM_FAULT_RESOURCE, DATA_TYPE_NVLIST); (void) nvlist_lookup_nvlist(flt_copy, FM_FAULT_ASRU, &asru); (void) nvlist_add_nvlist(flt_copy, FM_FAULT_RESOURCE, asru); } (void) nvlist_remove(flt_copy, FM_FAULT_ASRU, DATA_TYPE_NVLIST); (void) nvlist_add_nvlist(flt_copy, FM_FAULT_ASRU, asrua[i]); nvlist_free(asrua[i]); } else if (got_hc_asru == 0 && nvlist_lookup_nvlist(flt_copy, FM_FAULT_ASRU, &asru) == 0 && asru != NULL) { /* * If we have an asru from diag side, but it's not * in hc scheme, then we can't be sure what it * represents, so mark as no retire. */ (void) nvlist_add_boolean_value(flt_copy, FM_SUSPECT_RETIRE, B_FALSE); } fmd_case_insert_suspect(cp, flt_copy); } /* * copy diag_time */ if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME, &diag_time, &nelem2) == 0 && nelem2 >= 2) fmd_case_settime(cp, diag_time[0], diag_time[1]); /* * copy DE fmri */ if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &de_fmri) == 0) { (void) nvlist_xdup(de_fmri, &de_fmri_dup, &fmd.d_nva); fmd_case_set_de_fmri(cp, de_fmri_dup); } /* * copy injected if present */ if (nvlist_lookup_boolean_value(nvl, FM_SUSPECT_INJECTED, &injected) == 0 && injected) fmd_case_set_injected(cp); /* * Transition to solved. This will log the suspect list and create * the resource cache entries. */ fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED); /* * Update status if it is not simply "all faulty" (can happen if * list.suspects are being re-sent when the transport has reconnected). */ (void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS, &statusp, &nelem); for (i = 0; i < nelem; i++) { if ((statusp[i] & (FM_SUSPECT_FAULTY | FM_SUSPECT_UNUSABLE | FM_SUSPECT_NOT_PRESENT | FM_SUSPECT_DEGRADED)) != FM_SUSPECT_FAULTY) need_update = 1; } if (need_update) { fmd_case_update_status(cp, statusp, cip->ci_proxy_asru, cip->ci_diag_asru); fmd_case_update_containees(cp); fmd_case_update(cp); } /* * if asru on proxy side, send an update back to the diagnosing side to * update UNUSABLE/DEGRADED. */ if (got_proxy_asru) fmd_case_xprt_updated(cp); if (nelem > 0) fmd_free(asrua, sizeof (nvlist_t *) * nelem); fmd_module_unlock(xip->xi_queue->eq_mod); } void fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt, boolean_t logonly) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; const fmd_xprt_rule_t *xrp; fmd_t *dp = &fmd; fmd_event_t *e; char *class, *uuid; boolean_t isproto, isereport, isireport, ishvireport, issysevent; uint64_t *tod; uint8_t ttl; uint_t n; fmd_case_t *cp; /* * Grab the transport lock and set the busy flag to indicate we are * busy receiving an event. If [DI]SUSPEND is pending, wait until fmd * resumes the transport before continuing on with the receive. */ (void) pthread_mutex_lock(&xip->xi_lock); while (xip->xi_flags & (FMD_XPRT_DSUSPENDED | FMD_XPRT_ISUSPENDED)) { if (fmd.d_signal != 0) { (void) pthread_mutex_unlock(&xip->xi_lock); return; /* fmd_destroy() is in progress */ } (void) pthread_cond_wait(&xip->xi_cv, &xip->xi_lock); } xip->xi_busy++; ASSERT(xip->xi_busy != 0); (void) pthread_mutex_unlock(&xip->xi_lock); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_received.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0) { fmd_error(EFMD_XPRT_PAYLOAD, "discarding nvlist %p: missing " "required \"%s\" payload element", (void *)nvl, FM_CLASS); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_discarded.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); nvlist_free(nvl); goto done; } fmd_dprintf(FMD_DBG_XPRT, "xprt %u %s %s\n", xip->xi_id, ((logonly == FMD_B_TRUE) ? "logging" : "posting"), class); isereport = (strncmp(class, FM_EREPORT_CLASS ".", sizeof (FM_EREPORT_CLASS)) == 0) ? FMD_B_TRUE : FMD_B_FALSE; isireport = (strncmp(class, FM_IREPORT_CLASS ".", sizeof (FM_IREPORT_CLASS)) == 0) ? FMD_B_TRUE : FMD_B_FALSE; issysevent = (strncmp(class, SYSEVENT_RSRC_CLASS, sizeof (SYSEVENT_RSRC_CLASS) - 1)) == 0 ? FMD_B_TRUE : FMD_B_FALSE; if (isireport) { char *pri; if (nvlist_lookup_string(nvl, FM_IREPORT_PRIORITY, &pri) == 0 && strncmp(pri, "high", 5) == 0) { ishvireport = 1; } else { ishvireport = 0; } } /* * The logonly flag should only be set for ereports. */ if (logonly == FMD_B_TRUE && isereport == FMD_B_FALSE) { fmd_error(EFMD_XPRT_INVAL, "discarding nvlist %p: " "logonly flag is not valid for class %s", (void *)nvl, class); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_discarded.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); nvlist_free(nvl); goto done; } /* * If a time-to-live value is present in the event and is zero, drop * the event and bump xs_timeouts. Otherwise decrement the TTL value. */ if (nvlist_lookup_uint8(nvl, FMD_EVN_TTL, &ttl) == 0) { if (ttl == 0) { fmd_dprintf(FMD_DBG_XPRT, "xprt %u nvlist %p (%s) " "timeout: event received with ttl=0\n", xip->xi_id, (void *)nvl, class); (void) pthread_mutex_lock(&xip->xi_stats_lock); xip->xi_stats->xs_timeouts.fmds_value.ui64++; (void) pthread_mutex_unlock(&xip->xi_stats_lock); nvlist_free(nvl); goto done; } (void) nvlist_remove(nvl, FMD_EVN_TTL, DATA_TYPE_UINT8); (void) nvlist_add_uint8(nvl, FMD_EVN_TTL, ttl - 1); } /* * If we are using the native system clock, the underlying transport * code can provide a tighter event time bound by telling us when the * event was enqueued. If we're using simulated clocks, this time * has no meaning to us, so just reset the value to use HRT_NOW. */ if (dp->d_clockops != &fmd_timeops_native) hrt = FMD_HRT_NOW; /* * If an event's class is in the FMD_CTL_CLASS family, then create a * control event. If a FMD_EVN_TOD member is found, create a protocol * event using this time. Otherwise create a protocol event using hrt. */ isproto = (strncmp(class, FMD_CTL_CLASS, FMD_CTL_CLASS_LEN) == 0) ? FMD_B_FALSE : FMD_B_TRUE; if (isproto == FMD_B_FALSE) e = fmd_event_create(FMD_EVT_CTL, hrt, nvl, fmd_ctl_init(nvl)); else if (nvlist_lookup_uint64_array(nvl, FMD_EVN_TOD, &tod, &n) != 0) e = fmd_event_create(FMD_EVT_PROTOCOL, hrt, nvl, class); else { e = fmd_event_recreate(FMD_EVT_PROTOCOL, NULL, nvl, class, NULL, 0, 0); } /* * If the debug log is enabled, create a temporary event, log it to the * debug log, and then reset the underlying state of the event. */ if (xip->xi_log != NULL) { fmd_event_impl_t *ep = (fmd_event_impl_t *)e; fmd_log_append(xip->xi_log, e, NULL); ep->ev_flags |= FMD_EVF_VOLATILE; ep->ev_off = 0; ep->ev_len = 0; if (ep->ev_log != NULL) { fmd_log_rele(ep->ev_log); ep->ev_log = NULL; } } /* * Iterate over the rules for the current state trying to match the * event class to one of our special rules. If a rule is matched, the * event is consumed and not dispatched to other modules. If the rule * set ends without matching an event, we fall through to dispatching. */ for (xrp = xip->xi_state; xrp->xr_class != NULL; xrp++) { if (fmd_event_match(e, FMD_EVT_PROTOCOL, xrp->xr_class)) { fmd_event_hold(e); xrp->xr_func(xip, nvl); fmd_event_rele(e); goto done; } } /* * Record ereports and ireports in the log. This code will * be replaced later with a per-transport intent log instead. */ if (isereport == FMD_B_TRUE || isireport == FMD_B_TRUE || issysevent == B_TRUE) { pthread_rwlock_t *lockp; fmd_log_t *lp; if (isereport == FMD_B_TRUE) { lp = fmd.d_errlog; lockp = &fmd.d_log_lock; } else { if (ishvireport || issysevent) { lp = fmd.d_hvilog; lockp = &fmd.d_hvilog_lock; } else { lp = fmd.d_ilog; lockp = &fmd.d_ilog_lock; } } (void) pthread_rwlock_rdlock(lockp); fmd_log_append(lp, e, NULL); (void) pthread_rwlock_unlock(lockp); } /* * If a list.suspect event is received, create a case for the specified * UUID in the case hash, with the transport module as its owner. */ if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_SUSPECT_CLASS)) { if (xip->xi_flags & FMD_XPRT_CACHE_AS_LOCAL) fmd_xprt_list_suspect_local(xp, nvl); else fmd_xprt_list_suspect(xp, nvl); fmd_event_hold(e); fmd_event_rele(e); goto done; } /* * If a list.updated or list.repaired event is received, update the * resource cache status and the local case. */ if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_REPAIRED_CLASS) || fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_UPDATED_CLASS)) { uint8_t *statusp; uint_t nelem = 0; (void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS, &statusp, &nelem); fmd_module_lock(xip->xi_queue->eq_mod); if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0 && (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; if (cip->ci_xprt != NULL) { fmd_case_update_status(cp, statusp, cip->ci_proxy_asru, cip->ci_diag_asru); fmd_case_update_containees(cp); fmd_case_update(cp); } fmd_case_rele(cp); } fmd_module_unlock(xip->xi_queue->eq_mod); fmd_event_hold(e); fmd_event_rele(e); goto done; } /* * If a list.isolated event is received, update resource cache status */ if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_ISOLATED_CLASS)) { uint8_t *statusp; uint_t nelem = 0; (void) nvlist_lookup_uint8_array(nvl, FM_SUSPECT_FAULT_STATUS, &statusp, &nelem); fmd_module_lock(xip->xi_queue->eq_mod); if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0 && (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; if (cip->ci_xprt != NULL) fmd_case_update_status(cp, statusp, cip->ci_proxy_asru, cip->ci_diag_asru); fmd_case_rele(cp); } fmd_module_unlock(xip->xi_queue->eq_mod); fmd_event_hold(e); fmd_event_rele(e); goto done; } /* * If a list.resolved event is received, resolve the local case. */ if (fmd_event_match(e, FMD_EVT_PROTOCOL, FM_LIST_RESOLVED_CLASS)) { fmd_module_lock(xip->xi_queue->eq_mod); if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) == 0 && (cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) != NULL) { fmd_case_impl_t *cip = (fmd_case_impl_t *)cp; if (cip->ci_xprt != NULL) fmd_case_transition(cp, (cip->ci_state == FMD_CASE_REPAIRED) ? FMD_CASE_RESOLVED : (cip->ci_state == FMD_CASE_CLOSED) ? FMD_CASE_REPAIRED : FMD_CASE_CLOSE_WAIT, FMD_CF_RESOLVED); fmd_case_rele(cp); } fmd_module_unlock(xip->xi_queue->eq_mod); fmd_event_hold(e); fmd_event_rele(e); goto done; } if (logonly == FMD_B_TRUE || (xip->xi_flags & FMD_XPRT_EXTERNAL)) { /* * Don't proxy ereports on an EXTERNAL transport - we won't * know how to diagnose them with the wrong topology. Note * that here (and above) we have to hold/release the event in * order for it to be freed. */ fmd_event_hold(e); fmd_event_rele(e); } else if (isproto == FMD_B_TRUE) fmd_dispq_dispatch(dp->d_disp, e, class); else fmd_modhash_dispatch(dp->d_mod_hash, e); done: (void) pthread_mutex_lock(&xip->xi_lock); ASSERT(xip->xi_busy != 0); xip->xi_busy--; (void) pthread_cond_broadcast(&xip->xi_cv); (void) pthread_mutex_unlock(&xip->xi_lock); } void fmd_xprt_uuclose(fmd_xprt_t *xp, const char *uuid) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; fmd_event_t *e; nvlist_t *nvl; char *s; if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) return; /* read-only transports do not proxy uuclose */ TRACE((FMD_DBG_XPRT, "xprt %u closing case %s\n", xip->xi_id, uuid)); nvl = fmd_protocol_xprt_uuclose(xip->xi_queue->eq_mod, "resource.fm.xprt.uuclose", xip->xi_version, uuid); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } /* * On proxy side, send back uuresolved request to diagnosing side */ void fmd_xprt_uuresolved(fmd_xprt_t *xp, const char *uuid) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; fmd_event_t *e; nvlist_t *nvl; char *s; if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) return; /* read-only transports do not proxy uuresolved */ TRACE((FMD_DBG_XPRT, "xprt %u resolving case %s\n", xip->xi_id, uuid)); nvl = fmd_protocol_xprt_uuresolved(xip->xi_queue->eq_mod, "resource.fm.xprt.uuresolved", xip->xi_version, uuid); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } /* * On proxy side, send back repair/acquit/etc request to diagnosing side */ void fmd_xprt_updated(fmd_xprt_t *xp, const char *uuid, uint8_t *statusp, uint8_t *has_asrup, uint_t nelem) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; fmd_event_t *e; nvlist_t *nvl; char *s; if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) return; /* read-only transports do not support remote repairs */ TRACE((FMD_DBG_XPRT, "xprt %u updating case %s\n", xip->xi_id, uuid)); nvl = fmd_protocol_xprt_updated(xip->xi_queue->eq_mod, "resource.fm.xprt.updated", xip->xi_version, uuid, statusp, has_asrup, nelem); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } /* * Insert the specified class into our remote subscription hash. If the class * is already present, bump the reference count; otherwise add it to the hash * and then enqueue an event for our remote peer to proxy our subscription. */ void fmd_xprt_subscribe(fmd_xprt_t *xp, const char *class) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; uint_t refs; nvlist_t *nvl; fmd_event_t *e; char *s; if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) return; /* read-only transports do not proxy subscriptions */ if (!(xip->xi_flags & FMD_XPRT_SUBSCRIBER)) return; /* transport is not yet an active subscriber */ (void) pthread_mutex_lock(&xip->xi_lock); refs = fmd_xprt_class_hash_insert(xip, &xip->xi_rsub, class); (void) pthread_mutex_unlock(&xip->xi_lock); if (refs > 1) return; /* we've already asked our peer for this subscription */ fmd_dprintf(FMD_DBG_XPRT, "xprt %u subscribing to %s\n", xip->xi_id, class); nvl = fmd_protocol_xprt_sub(xip->xi_queue->eq_mod, "resource.fm.xprt.subscribe", xip->xi_version, class); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } /* * Delete the specified class from the remote subscription hash. If the * reference count drops to zero, ask our remote peer to unsubscribe by proxy. */ void fmd_xprt_unsubscribe(fmd_xprt_t *xp, const char *class) { fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp; uint_t refs; nvlist_t *nvl; fmd_event_t *e; char *s; if ((xip->xi_flags & FMD_XPRT_RDWR) == FMD_XPRT_RDONLY) return; /* read-only transports do not proxy subscriptions */ if (!(xip->xi_flags & FMD_XPRT_SUBSCRIBER)) return; /* transport is not yet an active subscriber */ /* * If the subscription reference count drops to zero in xi_rsub, insert * an entry into the xi_usub hash indicating we await an unsuback event. */ (void) pthread_mutex_lock(&xip->xi_lock); if ((refs = fmd_xprt_class_hash_delete(xip, &xip->xi_rsub, class)) == 0) (void) fmd_xprt_class_hash_insert(xip, &xip->xi_usub, class); (void) pthread_mutex_unlock(&xip->xi_lock); if (refs != 0) return; /* other subscriptions for this class still active */ fmd_dprintf(FMD_DBG_XPRT, "xprt %u unsubscribing from %s\n", xip->xi_id, class); nvl = fmd_protocol_xprt_sub(xip->xi_queue->eq_mod, "resource.fm.xprt.unsubscribe", xip->xi_version, class); (void) nvlist_lookup_string(nvl, FM_CLASS, &s); e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, s); fmd_eventq_insert_at_time(xip->xi_queue, e); } static void fmd_xprt_subscribe_xid(fmd_idspace_t *ids, id_t id, void *class) { fmd_xprt_t *xp; if ((xp = fmd_idspace_hold(ids, id)) != NULL) { fmd_xprt_subscribe(xp, class); fmd_idspace_rele(ids, id); } } void fmd_xprt_subscribe_all(const char *class) { fmd_idspace_t *ids = fmd.d_xprt_ids; if (ids->ids_count != 0) fmd_idspace_apply(ids, fmd_xprt_subscribe_xid, (void *)class); } static void fmd_xprt_unsubscribe_xid(fmd_idspace_t *ids, id_t id, void *class) { fmd_xprt_t *xp; if ((xp = fmd_idspace_hold(ids, id)) != NULL) { fmd_xprt_unsubscribe(xp, class); fmd_idspace_rele(ids, id); } } void fmd_xprt_unsubscribe_all(const char *class) { fmd_idspace_t *ids = fmd.d_xprt_ids; if (ids->ids_count != 0) fmd_idspace_apply(ids, fmd_xprt_unsubscribe_xid, (void *)class); } /*ARGSUSED*/ static void fmd_xprt_suspend_xid(fmd_idspace_t *ids, id_t id, void *arg) { fmd_xprt_t *xp; if ((xp = fmd_idspace_hold(ids, id)) != NULL) { fmd_xprt_xsuspend(xp, FMD_XPRT_DSUSPENDED); fmd_idspace_rele(ids, id); } } void fmd_xprt_suspend_all(void) { fmd_idspace_t *ids = fmd.d_xprt_ids; (void) pthread_mutex_lock(&fmd.d_xprt_lock); if (fmd.d_xprt_suspend++ != 0) { (void) pthread_mutex_unlock(&fmd.d_xprt_lock); return; /* already suspended */ } if (ids->ids_count != 0) fmd_idspace_apply(ids, fmd_xprt_suspend_xid, NULL); (void) pthread_mutex_unlock(&fmd.d_xprt_lock); } /*ARGSUSED*/ static void fmd_xprt_resume_xid(fmd_idspace_t *ids, id_t id, void *arg) { fmd_xprt_t *xp; if ((xp = fmd_idspace_hold(ids, id)) != NULL) { fmd_xprt_xresume(xp, FMD_XPRT_DSUSPENDED); fmd_idspace_rele(ids, id); } } void fmd_xprt_resume_all(void) { fmd_idspace_t *ids = fmd.d_xprt_ids; (void) pthread_mutex_lock(&fmd.d_xprt_lock); if (fmd.d_xprt_suspend == 0) fmd_panic("fmd_xprt_suspend/resume_all mismatch\n"); if (--fmd.d_xprt_suspend != 0) { (void) pthread_mutex_unlock(&fmd.d_xprt_lock); return; /* not ready to be resumed */ } if (ids->ids_count != 0) fmd_idspace_apply(ids, fmd_xprt_resume_xid, NULL); (void) pthread_mutex_unlock(&fmd.d_xprt_lock); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_XPRT_H #define _FMD_XPRT_H #include #include #ifdef __cplusplus extern "C" { #endif #include #include struct fmd_eventq; /* see */ struct fmd_thread; /* see */ struct fmd_idspace; /* see */ struct fmd_log; /* see */ struct fmd_xprt_impl; /* see below */ typedef void fmd_xprt_rule_f(struct fmd_xprt_impl *, nvlist_t *); extern fmd_xprt_rule_f fmd_xprt_event_syn; extern fmd_xprt_rule_f fmd_xprt_event_ack; extern fmd_xprt_rule_f fmd_xprt_event_run; extern fmd_xprt_rule_f fmd_xprt_event_sub; extern fmd_xprt_rule_f fmd_xprt_event_unsub; extern fmd_xprt_rule_f fmd_xprt_event_unsuback; extern fmd_xprt_rule_f fmd_xprt_event_uuclose; extern fmd_xprt_rule_f fmd_xprt_event_error; extern fmd_xprt_rule_f fmd_xprt_event_drop; extern fmd_xprt_rule_f fmd_xprt_event_uuresolved; extern fmd_xprt_rule_f fmd_xprt_event_updated; typedef struct fmd_xprt_rule { const char *xr_class; /* pattern to match */ fmd_xprt_rule_f *xr_func; /* action to invoke */ } fmd_xprt_rule_t; extern const fmd_xprt_rule_t _fmd_xprt_state_syn[]; extern const fmd_xprt_rule_t _fmd_xprt_state_ack[]; extern const fmd_xprt_rule_t _fmd_xprt_state_err[]; extern const fmd_xprt_rule_t _fmd_xprt_state_sub[]; extern const fmd_xprt_rule_t _fmd_xprt_state_run[]; typedef struct fmd_xprt_stat { fmd_eventqstat_t xs_evqstat; /* statistics for xprt event queue */ fmd_stat_t xs_module; /* module name associated with xprt */ fmd_stat_t xs_authority; /* authority associated with xprt */ fmd_stat_t xs_state; /* state name associated with xprt */ fmd_stat_t xs_received; /* number of events received by xprt */ fmd_stat_t xs_discarded; /* number of events discarded by xprt */ fmd_stat_t xs_retried; /* number of events retried by xprt */ fmd_stat_t xs_replayed; /* number of events replayed by xprt */ fmd_stat_t xs_lost; /* number of events lost by xprt */ fmd_stat_t xs_timeouts; /* number of events recv'd with ttl=0 */ fmd_stat_t xs_subscriptions; /* number of active subscriptions */ } fmd_xprt_stat_t; typedef struct fmd_xprt_class { char *xc_class; /* class string for subscription */ uint_t xc_refs; /* reference count for subscription */ struct fmd_xprt_class *xc_next; /* next class on xi_subhash chain */ } fmd_xprt_class_t; typedef struct fmd_xprt_class_hash { fmd_eventq_t *xch_queue; /* associated event queue (or NULL) */ fmd_xprt_class_t **xch_hash; /* subscription hash bucket array */ uint_t xch_hashlen; /* size of xch_hash bucket array */ } fmd_xprt_class_hash_t; typedef struct fmd_xprt_impl { fmd_list_t xi_list; /* linked list next/prev pointers */ uint_t xi_version; /* transport protocol version */ uint_t xi_id; /* transport identifier */ struct fmd_eventq *xi_queue; /* event queue for outbound events */ struct fmd_thread *xi_thread; /* thread associated with transport */ const fmd_xprt_rule_t *xi_state; /* rules for the current state */ nvlist_t *xi_auth; /* authority for peer endpoint */ void *xi_data; /* data for xprt_get/setspecific */ struct fmd_log *xi_log; /* log for received events (optional) */ pthread_mutex_t xi_stats_lock; /* lock protecting xi_stats data */ fmd_xprt_stat_t *xi_stats; /* built-in per-transport statistics */ pthread_mutex_t xi_lock; /* lock for modifying members below */ pthread_cond_t xi_cv; /* condition variable for xi_flags */ uint_t xi_flags; /* flags (see below) */ uint_t xi_busy; /* active threads in xprt_recv() */ fmd_xprt_class_hash_t xi_lsub; /* subscriptions in local dispq */ fmd_xprt_class_hash_t xi_rsub; /* subscriptions in remote peer */ fmd_xprt_class_hash_t xi_usub; /* pending remote unsubscriptions */ } fmd_xprt_impl_t; /* * Flags for fmd_xprt_create() and xi_flags. NOTE: Any public API flags must * exactly match the corresponding definitions in . */ #define FMD_XPRT_RDONLY 0x1 /* xprt is read-only */ #define FMD_XPRT_RDWR 0x3 /* xprt is read-write */ #define FMD_XPRT_ACCEPT 0x4 /* xprt is accepting connection */ #define FMD_XPRT_SUSPENDED 0x8 /* xprt is suspended by user */ #define FMD_XPRT_SUBSCRIBER 0x10 /* xprt is actively subscribing */ #define FMD_XPRT_ISUSPENDED 0x20 /* xprt is waiting for _fmd_init */ #define FMD_XPRT_DSUSPENDED 0x40 /* xprt is suspended by fmd mechanism */ #define FMD_XPRT_EXTERNAL 0x80 /* xprt is external to a chassis */ #define FMD_XPRT_NO_REMOTE_REPAIR 0x100 /* xprt allows remote repair */ #define FMD_XPRT_CACHE_AS_LOCAL 0x200 /* xprt caches fault as if local */ #define FMD_XPRT_HCONLY 0x400 /* xprt only proxies hc-scheme faults */ #define FMD_XPRT_HC_PRESENT_ONLY 0x800 /* only locally present hc faults */ #define FMD_XPRT_CMASK /* xprt create flag mask */ \ (FMD_XPRT_RDWR | FMD_XPRT_ACCEPT | FMD_XPRT_SUSPENDED | \ FMD_XPRT_EXTERNAL | FMD_XPRT_NO_REMOTE_REPAIR | \ FMD_XPRT_CACHE_AS_LOCAL | FMD_XPRT_HCONLY | FMD_XPRT_HC_PRESENT_ONLY) #define FMD_XPRT_SMASK \ (FMD_XPRT_SUSPENDED | FMD_XPRT_ISUSPENDED | FMD_XPRT_DSUSPENDED) extern fmd_xprt_t *fmd_xprt_create(fmd_module_t *, uint_t, nvlist_t *, void *); extern void fmd_xprt_destroy(fmd_xprt_t *); extern void fmd_xprt_xsuspend(fmd_xprt_t *, uint_t); extern void fmd_xprt_xresume(fmd_xprt_t *, uint_t); extern void fmd_xprt_send(fmd_xprt_t *); extern void fmd_xprt_recv(fmd_xprt_t *, nvlist_t *, hrtime_t, boolean_t); extern void fmd_xprt_uuclose(fmd_xprt_t *, const char *); extern void fmd_xprt_uuresolved(fmd_xprt_t *, const char *); extern void fmd_xprt_updated(fmd_xprt_t *, const char *, uint8_t *, uint8_t *, uint_t); extern void fmd_xprt_subscribe(fmd_xprt_t *, const char *); extern void fmd_xprt_unsubscribe(fmd_xprt_t *, const char *); extern void fmd_xprt_subscribe_all(const char *); extern void fmd_xprt_unsubscribe_all(const char *); extern void fmd_xprt_suspend_all(void); extern void fmd_xprt_resume_all(void); #ifdef __cplusplus } #endif #endif /* _FMD_XPRT_H */ #!/bin/ksh93 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # input="`cat`" [ -z "$input" ] && exit 1 echo "\ /*\n\ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.\n\ * Use is subject to license terms.\n\ */\n\ \n\ #include #include \n\ static const char *const _fmd_ereports[] = {" pattern='^[ ]*EFMD_\([A-Z0-9_]*\).*,*' replace=' "ereport.fm.fmd.\1",' echo "$input" | sed -n "s/$pattern/$replace/p" | tr '[A-Z]' '[a-z]' || exit 1 echo "\ };\n\ \n\ static const char *const _fmd_errstrs[] = {" pattern='^[ ]*EFMD_[A-Z0-9_]*.*\* \(.*\) \*.*' replace=' "\1",' echo "$input" | sed -n "s/$pattern/$replace/p" || exit 1 echo "\ };\n\ \n\ static const int _fmd_nereports =\n\ sizeof (_fmd_ereports) / sizeof (_fmd_ereports[0]);\n\ \n\ static const int _fmd_nerrstrs =\n\ sizeof (_fmd_errstrs) / sizeof (_fmd_errstrs[0]);\n\ \n\ const char * fmd_errclass(int err) { const char *c; if (err >= EFMD_UNKNOWN && (err - EFMD_UNKNOWN) < _fmd_nereports) c = _fmd_ereports[err - EFMD_UNKNOWN]; else c = _fmd_ereports[0]; return (c); } const char * fmd_strerror(int err) { const char *s; if (err >= EFMD_UNKNOWN && (err - EFMD_UNKNOWN) < _fmd_nerrstrs) s = _fmd_errstrs[err - EFMD_UNKNOWN]; else if (err < 0 || (s = strerror(err)) == NULL) s = _fmd_errstrs[0]; return (s); } int fmd_set_errno(int err) { errno = err; return (-1); }" exit 0