#include <atomic.h>
#include <alloca.h>
#include <syslog.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <exacct.h>
#include <fmd_subr.h>
#include <fmd_conf.h>
#include <fmd_error.h>
#include <fmd_thread.h>
#include <fmd_protocol.h>
#include <fmd_event.h>
#include <fmd_dispq.h>
#include <fmd_log.h>
#include <fmd.h>
int
fmd_assert(const char *expr, const char *file, int line)
{
fmd_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr);
return (0);
}
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 (atomic_add_32_nv(&fmd.d_panicrefs, 1) != 1) {
while (fmd.d_panictid != tid)
(void) pause();
goto abort;
}
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);
}
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 (!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);
}
}
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;
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;
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);
}
}
void
fmd_dprintf(int mask, const char *format, ...)
{
va_list ap;
va_start(ap, format);
fmd_vdprintf(mask, format, ap);
va_end(ap);
}
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;
}
}
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);
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");
}
}
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)));
}
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));
}