/* * 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)); }