#include <sys/fm/protocol.h>
#include <libintl.h>
#include <locale.h>
#include <wchar.h>
#include <alloca.h>
#include <assert.h>
#include <netdb.h>
#include <pthread.h>
#include <synch.h>
#include <strings.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include <fmd_msg.h>
#define FMD_MSGBUF_SZ 256
struct fmd_msg_hdl {
int fmh_version;
char *fmh_urlbase;
char *fmh_binding;
char *fmh_locale;
const char *fmh_template;
};
typedef struct fmd_msg_buf {
wchar_t *fmb_data;
size_t fmb_size;
size_t fmb_used;
int fmb_error;
} fmd_msg_buf_t;
static const char *const fmd_msg_items[] = {
"type",
"severity",
"description",
"response",
"impact",
"action",
"url",
};
static pthread_rwlock_t fmd_msg_rwlock = PTHREAD_RWLOCK_INITIALIZER;
static const char FMD_MSG_DOMAIN[] = "FMD";
static const char FMD_MSG_TEMPLATE[] = "syslog-msgs-message-template";
static const char FMD_MSG_URLKEY[] = "syslog-url";
static const char FMD_MSG_URLBASE[] = "http://illumos.org/msg/";
static const char FMD_MSG_NLSPATH[] = "NLSPATH=/usr/lib/fm/fmd/fmd.cat";
static const char FMD_MSG_MISSING[] = "-";
typedef enum {
T_EOF,
T_ERR,
T_IDENT,
T_INT,
T_DOT,
T_LBRAC,
T_RBRAC
} fmd_msg_nv_tkind_t;
typedef struct fmd_msg_nv_token {
fmd_msg_nv_tkind_t t_kind;
union {
char tu_str[256];
uint_t tu_int;
} t_data;
} fmd_msg_nv_token_t;
static const struct fmd_msg_nv_type {
data_type_t nvt_type;
data_type_t nvt_base;
size_t nvt_size;
int (*nvt_value)();
int (*nvt_array)();
} fmd_msg_nv_types[] = {
{ DATA_TYPE_INT8, DATA_TYPE_INT8,
sizeof (int8_t), nvpair_value_int8, NULL },
{ DATA_TYPE_INT16, DATA_TYPE_INT16,
sizeof (int16_t), nvpair_value_int16, NULL },
{ DATA_TYPE_INT32, DATA_TYPE_INT32,
sizeof (int32_t), nvpair_value_int32, NULL },
{ DATA_TYPE_INT64, DATA_TYPE_INT64,
sizeof (int64_t), nvpair_value_int64, NULL },
{ DATA_TYPE_UINT8, DATA_TYPE_UINT8,
sizeof (uint8_t), nvpair_value_uint8, NULL },
{ DATA_TYPE_UINT16, DATA_TYPE_UINT16,
sizeof (uint16_t), nvpair_value_uint16, NULL },
{ DATA_TYPE_UINT32, DATA_TYPE_UINT32,
sizeof (uint32_t), nvpair_value_uint32, NULL },
{ DATA_TYPE_UINT64, DATA_TYPE_UINT64,
sizeof (uint64_t), nvpair_value_uint64, NULL },
{ DATA_TYPE_BYTE, DATA_TYPE_BYTE,
sizeof (uchar_t), nvpair_value_byte, NULL },
{ DATA_TYPE_BOOLEAN, DATA_TYPE_BOOLEAN,
0, NULL, NULL },
{ DATA_TYPE_BOOLEAN_VALUE, DATA_TYPE_BOOLEAN_VALUE,
sizeof (boolean_t), nvpair_value_boolean_value, NULL },
{ DATA_TYPE_HRTIME, DATA_TYPE_HRTIME,
sizeof (hrtime_t), nvpair_value_hrtime, NULL },
{ DATA_TYPE_STRING, DATA_TYPE_STRING,
sizeof (char *), nvpair_value_string, NULL },
{ DATA_TYPE_NVLIST, DATA_TYPE_NVLIST,
sizeof (nvlist_t *), nvpair_value_nvlist, NULL },
{ DATA_TYPE_INT8_ARRAY, DATA_TYPE_INT8,
sizeof (int8_t), NULL, nvpair_value_int8_array },
{ DATA_TYPE_INT16_ARRAY, DATA_TYPE_INT16,
sizeof (int16_t), NULL, nvpair_value_int16_array },
{ DATA_TYPE_INT32_ARRAY, DATA_TYPE_INT32,
sizeof (int32_t), NULL, nvpair_value_int32_array },
{ DATA_TYPE_INT64_ARRAY, DATA_TYPE_INT64,
sizeof (int64_t), NULL, nvpair_value_int64_array },
{ DATA_TYPE_UINT8_ARRAY, DATA_TYPE_UINT8,
sizeof (uint8_t), NULL, nvpair_value_uint8_array },
{ DATA_TYPE_UINT16_ARRAY, DATA_TYPE_UINT16,
sizeof (uint16_t), NULL, nvpair_value_uint16_array },
{ DATA_TYPE_UINT32_ARRAY, DATA_TYPE_UINT32,
sizeof (uint32_t), NULL, nvpair_value_uint32_array },
{ DATA_TYPE_UINT64_ARRAY, DATA_TYPE_UINT64,
sizeof (uint64_t), NULL, nvpair_value_uint64_array },
{ DATA_TYPE_BYTE_ARRAY, DATA_TYPE_BYTE,
sizeof (uchar_t), NULL, nvpair_value_byte_array },
{ DATA_TYPE_BOOLEAN_ARRAY, DATA_TYPE_BOOLEAN_VALUE,
sizeof (boolean_t), NULL, nvpair_value_boolean_array },
{ DATA_TYPE_STRING_ARRAY, DATA_TYPE_STRING,
sizeof (char *), NULL, nvpair_value_string_array },
{ DATA_TYPE_NVLIST_ARRAY, DATA_TYPE_NVLIST,
sizeof (nvlist_t *), NULL, nvpair_value_nvlist_array },
{ DATA_TYPE_UNKNOWN, DATA_TYPE_UNKNOWN, 0, NULL, NULL }
};
static int fmd_msg_nv_parse_nvpair(fmd_msg_buf_t *, nvpair_t *, char *);
static int fmd_msg_nv_parse_nvname(fmd_msg_buf_t *, nvlist_t *, char *);
static int fmd_msg_nv_parse_nvlist(fmd_msg_buf_t *, nvlist_t *, char *);
static int
fmd_msg_lock_held(fmd_msg_hdl_t *h)
{
return (RW_WRITE_HELD(&fmd_msg_rwlock));
}
void
fmd_msg_lock(void)
{
if (pthread_rwlock_wrlock(&fmd_msg_rwlock) != 0)
abort();
}
void
fmd_msg_unlock(void)
{
if (pthread_rwlock_unlock(&fmd_msg_rwlock) != 0)
abort();
}
static fmd_msg_hdl_t *
fmd_msg_init_err(fmd_msg_hdl_t *h, int err)
{
fmd_msg_fini(h);
errno = err;
return (NULL);
}
fmd_msg_hdl_t *
fmd_msg_init(const char *root, int version)
{
fmd_msg_hdl_t *h = NULL;
const char *s;
size_t len;
if (version != FMD_MSG_VERSION)
return (fmd_msg_init_err(h, EINVAL));
if ((h = malloc(sizeof (fmd_msg_hdl_t))) == NULL)
return (fmd_msg_init_err(h, ENOMEM));
bzero(h, sizeof (fmd_msg_hdl_t));
h->fmh_version = version;
if ((h->fmh_urlbase = strdup(FMD_MSG_URLBASE)) == NULL)
return (fmd_msg_init_err(h, ENOMEM));
(void) setlocale(LC_ALL, "");
s = setlocale(LC_ALL, NULL);
h->fmh_locale = strdup(s ? s : "C");
if (h->fmh_locale == NULL)
return (fmd_msg_init_err(h, ENOMEM));
if (root != NULL && root[0] != '\0' && strcmp(root, "/") != 0) {
if (root[0] != '/')
return (fmd_msg_init_err(h, EINVAL));
if ((s = bindtextdomain(FMD_MSG_DOMAIN, NULL)) == NULL)
s = "/usr/lib/locale";
len = strlen(root) + strlen(s) + 1;
if ((h->fmh_binding = malloc(len)) == NULL)
return (fmd_msg_init_err(h, ENOMEM));
(void) snprintf(h->fmh_binding, len, "%s%s", root, s);
}
if (getenv("NLSPATH") == NULL &&
((s = strdup(FMD_MSG_NLSPATH)) == NULL || putenv((char *)s) != 0))
return (fmd_msg_init_err(h, errno));
if ((h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE))
== FMD_MSG_TEMPLATE && strcmp(h->fmh_locale, "C") != 0) {
(void) setlocale(LC_ALL, "C");
h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE);
(void) setlocale(LC_ALL, h->fmh_locale);
}
return (h);
}
void
fmd_msg_fini(fmd_msg_hdl_t *h)
{
if (h == NULL)
return;
free(h->fmh_binding);
free(h->fmh_urlbase);
free(h->fmh_locale);
free(h);
}
int
fmd_msg_locale_set(fmd_msg_hdl_t *h, const char *locale)
{
char *l;
if (locale == NULL) {
errno = EINVAL;
return (-1);
}
if ((l = strdup(locale)) == NULL) {
errno = ENOMEM;
return (-1);
}
fmd_msg_lock();
if (setlocale(LC_ALL, l) == NULL) {
free(l);
errno = EINVAL;
fmd_msg_unlock();
return (-1);
}
h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE);
free(h->fmh_locale);
h->fmh_locale = l;
fmd_msg_unlock();
return (0);
}
const char *
fmd_msg_locale_get(fmd_msg_hdl_t *h)
{
return (h->fmh_locale);
}
int
fmd_msg_url_set(fmd_msg_hdl_t *h, const char *url)
{
char *u;
if (url == NULL) {
errno = EINVAL;
return (-1);
}
if ((u = strdup(url)) == NULL) {
errno = ENOMEM;
return (-1);
}
fmd_msg_lock();
free(h->fmh_urlbase);
h->fmh_urlbase = u;
fmd_msg_unlock();
return (0);
}
const char *
fmd_msg_url_get(fmd_msg_hdl_t *h)
{
return (h->fmh_urlbase);
}
static wchar_t *
fmd_msg_mbstowcs(const char *s)
{
size_t n = strlen(s) + 1;
wchar_t *w = malloc(n * sizeof (wchar_t));
if (w == NULL) {
errno = ENOMEM;
return (NULL);
}
if (mbstowcs(w, s, n) == (size_t)-1) {
free(w);
return (NULL);
}
return (w);
}
static void
fmd_msg_buf_init(fmd_msg_buf_t *b)
{
bzero(b, sizeof (fmd_msg_buf_t));
b->fmb_data = malloc(sizeof (wchar_t) * FMD_MSGBUF_SZ);
if (b->fmb_data == NULL)
b->fmb_error = ENOMEM;
else
b->fmb_size = FMD_MSGBUF_SZ;
}
static void
fmd_msg_buf_fini(fmd_msg_buf_t *b)
{
free(b->fmb_data);
bzero(b, sizeof (fmd_msg_buf_t));
}
static char *
fmd_msg_buf_read(fmd_msg_buf_t *b)
{
char *s;
if (b->fmb_error != 0) {
errno = b->fmb_error;
return (NULL);
}
if ((s = malloc(b->fmb_used * MB_CUR_MAX)) == NULL) {
errno = ENOMEM;
return (NULL);
}
if (wcstombs(s, b->fmb_data, b->fmb_used) == (size_t)-1) {
free(s);
return (NULL);
}
return (s);
}
static void
fmd_msg_buf_write(fmd_msg_buf_t *b, const wchar_t *w, size_t n)
{
if (b->fmb_used + n > b->fmb_size) {
size_t size = MAX(b->fmb_size * 2, b->fmb_used + n);
wchar_t *data = malloc(sizeof (wchar_t) * size);
if (data == NULL) {
if (b->fmb_error == 0)
b->fmb_error = ENOMEM;
return;
}
bcopy(b->fmb_data, data, b->fmb_used * sizeof (wchar_t));
free(b->fmb_data);
b->fmb_data = data;
b->fmb_size = size;
}
bcopy(w, &b->fmb_data[b->fmb_used], sizeof (wchar_t) * n);
b->fmb_used += n;
}
static void
fmd_msg_buf_printf(fmd_msg_buf_t *b, const char *format, ...)
{
ssize_t len;
va_list ap;
char *buf;
wchar_t *w;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
buf = alloca(len + 1);
(void) vsnprintf(buf, len + 1, format, ap);
va_end(ap);
if ((w = fmd_msg_mbstowcs(buf)) == NULL) {
if (b->fmb_error != 0)
b->fmb_error = errno;
} else {
fmd_msg_buf_write(b, w, wcslen(w));
free(w);
}
}
static int
fmd_msg_nv_error(const char *format, ...)
{
int err = errno;
va_list ap;
if (getenv("FMD_MSG_DEBUG") == NULL)
return (1);
(void) fprintf(stderr, "libfmd_msg DEBUG: ");
va_start(ap, format);
(void) vfprintf(stderr, format, ap);
va_end(ap);
if (strchr(format, '\n') == NULL)
(void) fprintf(stderr, ": %s\n", strerror(err));
return (1);
}
static const struct fmd_msg_nv_type *
fmd_msg_nv_type_lookup(data_type_t type)
{
const struct fmd_msg_nv_type *t;
for (t = fmd_msg_nv_types; t->nvt_type != DATA_TYPE_UNKNOWN; t++) {
if (t->nvt_type == type)
break;
}
return (t);
}
static void
fmd_msg_nv_print_string(fmd_msg_buf_t *b, const char *s)
{
char c;
while ((c = *s++) != '\0') {
if (c >= ' ' && c <= '~' && c != '\'') {
fmd_msg_buf_printf(b, "%c", c);
continue;
}
switch (c) {
case '\0':
fmd_msg_buf_printf(b, "\\0");
break;
case '\a':
fmd_msg_buf_printf(b, "\\a");
break;
case '\b':
fmd_msg_buf_printf(b, "\\b");
break;
case '\f':
fmd_msg_buf_printf(b, "\\f");
break;
case '\n':
fmd_msg_buf_printf(b, "\\n");
break;
case '\r':
fmd_msg_buf_printf(b, "\\r");
break;
case '\t':
fmd_msg_buf_printf(b, "\\t");
break;
case '\v':
fmd_msg_buf_printf(b, "\\v");
break;
case '\'':
fmd_msg_buf_printf(b, "\\'");
break;
case '"':
fmd_msg_buf_printf(b, "\\\"");
break;
case '\\':
fmd_msg_buf_printf(b, "\\\\");
break;
default:
fmd_msg_buf_printf(b, "\\x%02x", (uchar_t)c);
}
}
}
static int
fmd_msg_nv_print_items(fmd_msg_buf_t *b, nvpair_t *nvp,
data_type_t type, void *p, uint_t n, uint_t idx)
{
const struct fmd_msg_nv_type *nvt = fmd_msg_nv_type_lookup(type);
uint_t i;
if (idx != -1u) {
if (idx >= n) {
return (fmd_msg_nv_error("index %u out-of-range for "
"array %s: valid range is [0 .. %u]\n",
idx, nvpair_name(nvp), n ? n - 1 : 0));
}
p = (uchar_t *)p + nvt->nvt_size * idx;
n = 1;
}
for (i = 0; i < n; i++, p = (uchar_t *)p + nvt->nvt_size) {
if (i > 0)
fmd_msg_buf_printf(b, " ");
switch (type) {
case DATA_TYPE_INT8:
fmd_msg_buf_printf(b, "%d", *(int8_t *)p);
break;
case DATA_TYPE_INT16:
fmd_msg_buf_printf(b, "%d", *(int16_t *)p);
break;
case DATA_TYPE_INT32:
fmd_msg_buf_printf(b, "%d", *(int32_t *)p);
break;
case DATA_TYPE_INT64:
fmd_msg_buf_printf(b, "%lld", *(longlong_t *)p);
break;
case DATA_TYPE_UINT8:
fmd_msg_buf_printf(b, "%u", *(uint8_t *)p);
break;
case DATA_TYPE_UINT16:
fmd_msg_buf_printf(b, "%u", *(uint16_t *)p);
break;
case DATA_TYPE_UINT32:
fmd_msg_buf_printf(b, "%u", *(uint32_t *)p);
break;
case DATA_TYPE_UINT64:
fmd_msg_buf_printf(b, "%llu", *(u_longlong_t *)p);
break;
case DATA_TYPE_BYTE:
fmd_msg_buf_printf(b, "0x%x", *(uchar_t *)p);
break;
case DATA_TYPE_BOOLEAN_VALUE:
fmd_msg_buf_printf(b,
*(boolean_t *)p ? "true" : "false");
break;
case DATA_TYPE_HRTIME:
fmd_msg_buf_printf(b, "%lld", *(longlong_t *)p);
break;
case DATA_TYPE_STRING:
fmd_msg_nv_print_string(b, *(char **)p);
break;
}
}
return (0);
}
static int
fmd_msg_nv_print_nvpair(fmd_msg_buf_t *b, nvpair_t *nvp, uint_t idx)
{
data_type_t type = nvpair_type(nvp);
const struct fmd_msg_nv_type *nvt = fmd_msg_nv_type_lookup(type);
uint64_t v;
void *a;
uint_t n;
int err;
if (nvt->nvt_type == DATA_TYPE_BOOLEAN) {
fmd_msg_buf_printf(b, "true");
err = 0;
} else if (nvt->nvt_array != NULL) {
(void) nvt->nvt_array(nvp, &a, &n);
err = fmd_msg_nv_print_items(b, nvp, nvt->nvt_base, a, n, idx);
} else if (nvt->nvt_value != NULL) {
(void) nvt->nvt_value(nvp, &v);
err = fmd_msg_nv_print_items(b, nvp, nvt->nvt_base, &v, 1, idx);
} else {
err = fmd_msg_nv_error("unknown data type %u", type);
}
return (err);
}
static char *
fmd_msg_nv_parse_token(char *s, fmd_msg_nv_token_t *tp)
{
char *p = s, *q, c = *s;
while (c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r')
c = *++p;
if (c >= '0' && c <= '9') {
errno = 0;
tp->t_data.tu_int = strtoul(p, &q, 0);
if (errno != 0 || p == q) {
tp->t_kind = T_ERR;
return (p);
}
tp->t_kind = T_INT;
return (q);
}
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
for (q = p + 1; (c = *q) != '\0'; q++) {
if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') &&
(c < '0' || c > '9') && (c != '_' && c != '-'))
break;
}
if (sizeof (tp->t_data.tu_str) <= (size_t)(q - p)) {
tp->t_kind = T_ERR;
return (p);
}
bcopy(p, tp->t_data.tu_str, (size_t)(q - p));
tp->t_data.tu_str[(size_t)(q - p)] = '\0';
tp->t_kind = T_IDENT;
return (q);
}
switch (c) {
case '\0':
tp->t_kind = T_EOF;
return (p);
case '.':
tp->t_kind = T_DOT;
return (p + 1);
case '[':
tp->t_kind = T_LBRAC;
return (p + 1);
case ']':
tp->t_kind = T_RBRAC;
return (p + 1);
default:
tp->t_kind = T_ERR;
return (p);
}
}
static int
fmd_msg_nv_parse_error(const char *s, fmd_msg_nv_token_t *tp)
{
if (tp->t_kind == T_ERR)
return (fmd_msg_nv_error("illegal character at \"%s\"\n", s));
else
return (fmd_msg_nv_error("syntax error near \"%s\"\n", s));
}
static int
fmd_msg_nv_parse_array(fmd_msg_buf_t *b, nvpair_t *nvp, char *s1)
{
fmd_msg_nv_token_t t;
nvlist_t **nva;
uint_t i, n;
char *s2;
if (fmd_msg_nv_type_lookup(nvpair_type(nvp))->nvt_array == NULL) {
return (fmd_msg_nv_error("inappropriate use of operator [ ]: "
"element '%s' is not an array\n", nvpair_name(nvp)));
}
s2 = fmd_msg_nv_parse_token(s1, &t);
i = t.t_data.tu_int;
if (t.t_kind != T_INT)
return (fmd_msg_nv_error("expected integer index after [\n"));
s2 = fmd_msg_nv_parse_token(s2, &t);
if (t.t_kind != T_RBRAC)
return (fmd_msg_nv_error("expected ] after [ %u\n", i));
if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) {
(void) nvpair_value_nvlist_array(nvp, &nva, &n);
if (i >= n) {
return (fmd_msg_nv_error("index %u out-of-range for "
"array %s: valid range is [0 .. %u]\n",
i, nvpair_name(nvp), n ? n - 1 : 0));
}
return (fmd_msg_nv_parse_nvlist(b, nva[i], s2));
}
(void) fmd_msg_nv_parse_token(s2, &t);
if (t.t_kind != T_EOF) {
return (fmd_msg_nv_error("expected end-of-string "
"in expression instead of \"%s\"\n", s2));
}
return (fmd_msg_nv_print_nvpair(b, nvp, i));
}
static int
fmd_msg_nv_parse_nvpair(fmd_msg_buf_t *b, nvpair_t *nvp, char *s1)
{
fmd_msg_nv_token_t t;
nvlist_t *nvl;
char *s2;
s2 = fmd_msg_nv_parse_token(s1, &t);
if (t.t_kind == T_EOF)
return (fmd_msg_nv_print_nvpair(b, nvp, -1));
if (t.t_kind == T_LBRAC)
return (fmd_msg_nv_parse_array(b, nvp, s2));
if (t.t_kind != T_DOT)
return (fmd_msg_nv_parse_error(s1, &t));
if (nvpair_type(nvp) != DATA_TYPE_NVLIST) {
return (fmd_msg_nv_error("inappropriate use of operator '.': "
"element '%s' is not of type nvlist\n", nvpair_name(nvp)));
}
(void) nvpair_value_nvlist(nvp, &nvl);
return (fmd_msg_nv_parse_nvname(b, nvl, s2));
}
static int
fmd_msg_nv_parse_nvname(fmd_msg_buf_t *b, nvlist_t *nvl, char *s1)
{
nvpair_t *nvp = NULL;
fmd_msg_nv_token_t t;
char *s2;
s2 = fmd_msg_nv_parse_token(s1, &t);
if (t.t_kind != T_IDENT)
return (fmd_msg_nv_parse_error(s1, &t));
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
if (strcmp(nvpair_name(nvp), t.t_data.tu_str) == 0)
break;
}
if (nvp == NULL) {
return (fmd_msg_nv_error("no such name-value pair "
"member: %s\n", t.t_data.tu_str));
}
return (fmd_msg_nv_parse_nvpair(b, nvp, s2));
}
static int
fmd_msg_nv_parse_nvlist(fmd_msg_buf_t *b, nvlist_t *nvl, char *s1)
{
fmd_msg_nv_token_t t;
char *s2;
s2 = fmd_msg_nv_parse_token(s1, &t);
if (t.t_kind == T_EOF)
return (0);
if (t.t_kind == T_DOT)
return (fmd_msg_nv_parse_nvname(b, nvl, s2));
return (fmd_msg_nv_parse_error(s1, &t));
}
static char *
fmd_msg_getitem_locked(fmd_msg_hdl_t *h,
nvlist_t *nvl, const char *dict, const char *code, fmd_msg_item_t item)
{
const char *istr = fmd_msg_items[item];
size_t len = strlen(code) + 1 + strlen(istr) + 1;
char *key = alloca(len);
fmd_msg_buf_t buf;
wchar_t *c, *u, *w, *p, *q;
const char *url, *txt;
char *s, *expr;
size_t elen;
int i;
assert(fmd_msg_lock_held(h));
if ((url = dgettext(dict, FMD_MSG_URLKEY)) == FMD_MSG_URLKEY)
url = h->fmh_urlbase;
if (item == FMD_MSG_ITEM_URL) {
len = strlen(url) + strlen(code) + 1;
key = alloca(len);
(void) snprintf(key, len, "%s%s", url, code);
txt = key;
} else {
len = strlen(code) + 1 + strlen(istr) + 1;
key = alloca(len);
(void) snprintf(key, len, "%s.%s", code, istr);
txt = dgettext(dict, key);
}
c = fmd_msg_mbstowcs(code);
u = fmd_msg_mbstowcs(url);
w = fmd_msg_mbstowcs(txt);
if (c == NULL || u == NULL || w == NULL) {
free(c);
free(u);
free(w);
return (NULL);
}
fmd_msg_buf_init(&buf);
for (q = w, p = w; (p = wcschr(p, L'%')) != NULL; q = p) {
if (p > q)
fmd_msg_buf_write(&buf, q, (size_t)(p - q));
switch (p[1]) {
case L'%':
fmd_msg_buf_write(&buf, p, 1);
p += 2;
break;
case L's':
fmd_msg_buf_write(&buf, u, wcslen(u));
fmd_msg_buf_write(&buf, c, wcslen(c));
p += 2;
break;
case L'<':
q = p + 2;
p = wcschr(p + 2, L'>');
if (p == NULL)
goto eos;
elen = (size_t)(p - q);
expr = malloc(elen + MB_CUR_MAX + 1);
if (expr == NULL) {
buf.fmb_error = ENOMEM;
goto eos;
}
for (i = 0; i < elen; i++)
(void) wctomb(&expr[i], q[i]);
expr[i] = '\0';
if (nvl != NULL)
(void) fmd_msg_nv_parse_nvname(&buf, nvl, expr);
else
fmd_msg_buf_printf(&buf, "%%<%s>", expr);
free(expr);
p++;
break;
case L'\0':
goto eos;
default:
p += 2;
break;
}
}
eos:
fmd_msg_buf_write(&buf, q, wcslen(q) + 1);
free(c);
free(u);
free(w);
s = fmd_msg_buf_read(&buf);
fmd_msg_buf_fini(&buf);
return (s);
}
char *
fmd_msg_decode_tokens(nvlist_t *nvl, const char *msg, const char *url)
{
fmd_msg_buf_t buf;
wchar_t *h, *u, *w, *p, *q;
char *s, *expr, host[MAXHOSTNAMELEN + 1];
size_t elen;
int i;
u = fmd_msg_mbstowcs(url);
(void) gethostname(host, MAXHOSTNAMELEN + 1);
h = fmd_msg_mbstowcs(host);
if ((w = fmd_msg_mbstowcs(msg)) == NULL)
return (NULL);
fmd_msg_buf_init(&buf);
for (q = w, p = w; (p = wcschr(p, L'%')) != NULL; q = p) {
if (p > q)
fmd_msg_buf_write(&buf, q, (size_t)(p - q));
switch (p[1]) {
case L'%':
fmd_msg_buf_write(&buf, p, 1);
p += 2;
break;
case L'h':
if (h != NULL)
fmd_msg_buf_write(&buf, h, wcslen(h));
p += 2;
break;
case L's':
if (u != NULL)
fmd_msg_buf_write(&buf, u, wcslen(u));
p += 2;
break;
case L'<':
q = p + 2;
p = wcschr(p + 2, L'>');
if (p == NULL)
goto eos;
elen = (size_t)(p - q);
expr = malloc(elen + MB_CUR_MAX + 1);
if (expr == NULL) {
buf.fmb_error = ENOMEM;
goto eos;
}
for (i = 0; i < elen; i++)
(void) wctomb(&expr[i], q[i]);
expr[i] = '\0';
if (nvl != NULL)
(void) fmd_msg_nv_parse_nvname(&buf, nvl, expr);
else
fmd_msg_buf_printf(&buf, "%%<%s>", expr);
free(expr);
p++;
break;
case L'\0':
goto eos;
default:
p += 2;
break;
}
}
eos:
fmd_msg_buf_write(&buf, q, wcslen(q) + 1);
free(h);
free(u);
free(w);
s = fmd_msg_buf_read(&buf);
fmd_msg_buf_fini(&buf);
return (s);
}
static char *
fmd_msg_gettext_locked(fmd_msg_hdl_t *h,
nvlist_t *nvl, const char *dict, const char *code)
{
char *items[FMD_MSG_ITEM_MAX];
const char *format;
char *buf = NULL;
size_t len;
int i;
nvlist_t *fmri, *auth;
struct tm tm, *tmp;
int64_t *tv;
uint_t tn = 0;
time_t sec;
char date[64];
char *uuid, *src_name, *src_vers;
char *platform, *server, *csn;
assert(fmd_msg_lock_held(h));
bzero(items, sizeof (items));
for (i = 0; i < FMD_MSG_ITEM_MAX; i++) {
items[i] = fmd_msg_getitem_locked(h, nvl, dict, code, i);
if (items[i] == NULL)
goto out;
}
if ((format = dgettext(dict, FMD_MSG_TEMPLATE)) == FMD_MSG_TEMPLATE)
format = h->fmh_template;
if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0)
uuid = (char *)FMD_MSG_MISSING;
if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME,
&tv, &tn) == 0 && tn == 2 && (sec = (time_t)tv[0]) != (time_t)-1 &&
(tmp = localtime_r(&sec, &tm)) != NULL)
(void) strftime(date, sizeof (date), "%a %b %e %H:%M:%S %Z %Y",
tmp);
else
(void) strlcpy(date, FMD_MSG_MISSING, sizeof (date));
if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &fmri) != 0)
fmri = NULL;
if (nvlist_lookup_nvlist(fmri, FM_FMRI_AUTHORITY, &auth) != 0)
auth = NULL;
if (nvlist_lookup_string(fmri, FM_FMRI_FMD_NAME, &src_name) != 0)
src_name = (char *)FMD_MSG_MISSING;
if (nvlist_lookup_string(fmri, FM_FMRI_FMD_VERSION, &src_vers) != 0)
src_vers = (char *)FMD_MSG_MISSING;
if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &platform) != 0)
platform = (char *)FMD_MSG_MISSING;
if (nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server) != 0)
server = (char *)FMD_MSG_MISSING;
if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN, &csn) != 0 &&
nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &csn) != 0)
csn = (char *)FMD_MSG_MISSING;
len = snprintf(NULL, 0, format, code,
items[FMD_MSG_ITEM_TYPE], items[FMD_MSG_ITEM_SEVERITY],
date, platform, csn, server, src_name, src_vers, uuid,
items[FMD_MSG_ITEM_DESC], items[FMD_MSG_ITEM_RESPONSE],
items[FMD_MSG_ITEM_IMPACT], items[FMD_MSG_ITEM_ACTION]);
if ((buf = malloc(len + 1)) == NULL) {
errno = ENOMEM;
goto out;
}
(void) snprintf(buf, len + 1, format, code,
items[FMD_MSG_ITEM_TYPE], items[FMD_MSG_ITEM_SEVERITY],
date, platform, csn, server, src_name, src_vers, uuid,
items[FMD_MSG_ITEM_DESC], items[FMD_MSG_ITEM_RESPONSE],
items[FMD_MSG_ITEM_IMPACT], items[FMD_MSG_ITEM_ACTION]);
out:
for (i = 0; i < FMD_MSG_ITEM_MAX; i++)
free(items[i]);
return (buf);
}
static char *
fmd_msg_getitem(fmd_msg_hdl_t *h,
const char *locale, nvlist_t *nvl, const char *code, fmd_msg_item_t item)
{
char *old_b, *old_c;
char *dict, *key, *p, *s;
size_t len;
int err;
if ((p = strchr(code, '-')) == NULL || p == code) {
errno = EINVAL;
return (NULL);
}
if (locale != NULL && strcmp(h->fmh_locale, locale) == 0)
locale = NULL;
dict = strndupa(code, p - code);
fmd_msg_lock();
if (h->fmh_binding != NULL) {
p = bindtextdomain(dict, NULL);
old_b = strdupa(p);
(void) bindtextdomain(dict, h->fmh_binding);
}
len = strlen(code) + 1 + strlen(fmd_msg_items[FMD_MSG_ITEM_TYPE]) + 1;
key = alloca(len);
(void) snprintf(key, len, "%s.%s",
code, fmd_msg_items[FMD_MSG_ITEM_TYPE]);
p = setlocale(LC_ALL, NULL);
old_c = strdupa(p);
if (locale != NULL)
(void) setlocale(LC_ALL, locale);
if (dgettext(dict, key) == key &&
(locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
(void) setlocale(LC_ALL, "C");
locale = "C";
}
if (dgettext(dict, key) == key) {
s = NULL;
err = ENOENT;
} else {
s = fmd_msg_getitem_locked(h, nvl, dict, code, item);
err = errno;
}
if (locale != NULL)
(void) setlocale(LC_ALL, old_c);
if (h->fmh_binding != NULL)
(void) bindtextdomain(dict, old_b);
fmd_msg_unlock();
if (s == NULL)
errno = err;
return (s);
}
char *
fmd_msg_getitem_nv(fmd_msg_hdl_t *h,
const char *locale, nvlist_t *nvl, fmd_msg_item_t item)
{
char *code;
if (item >= FMD_MSG_ITEM_MAX) {
errno = EINVAL;
return (NULL);
}
if (nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code) != 0) {
errno = EINVAL;
return (NULL);
}
return (fmd_msg_getitem(h, locale, nvl, code, item));
}
char *
fmd_msg_getitem_id(fmd_msg_hdl_t *h,
const char *locale, const char *code, fmd_msg_item_t item)
{
if (item >= FMD_MSG_ITEM_MAX) {
errno = EINVAL;
return (NULL);
}
return (fmd_msg_getitem(h, locale, NULL, code, item));
}
char *
fmd_msg_gettext_key(fmd_msg_hdl_t *h,
const char *locale, const char *dict, const char *key)
{
char *old_b, *old_c, *p, *s;
fmd_msg_lock();
if (h->fmh_binding != NULL) {
p = bindtextdomain(dict, NULL);
old_b = alloca(strlen(p) + 1);
(void) strcpy(old_b, p);
(void) bindtextdomain(dict, h->fmh_binding);
}
p = setlocale(LC_ALL, NULL);
old_c = alloca(strlen(p) + 1);
(void) strcpy(old_c, p);
if (locale != NULL)
(void) setlocale(LC_ALL, locale);
if ((s = dgettext(dict, key)) == key &&
(locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
(void) setlocale(LC_ALL, "C");
locale = "C";
if ((s = dgettext(dict, key)) == key) {
s = NULL;
errno = ENOENT;
}
}
if (locale != NULL)
(void) setlocale(LC_ALL, old_c);
if (h->fmh_binding != NULL)
(void) bindtextdomain(dict, old_b);
fmd_msg_unlock();
return (s);
}
static char *
fmd_msg_gettext(fmd_msg_hdl_t *h,
const char *locale, nvlist_t *nvl, const char *code)
{
char *old_b, *old_c;
char *dict, *key, *p, *s;
size_t len;
int err;
if ((p = strchr(code, '-')) == NULL || p == code) {
errno = EINVAL;
return (NULL);
}
if (locale != NULL && strcmp(h->fmh_locale, locale) == 0)
locale = NULL;
dict = strndupa(code, p - code);
fmd_msg_lock();
if (h->fmh_binding != NULL) {
p = bindtextdomain(dict, NULL);
old_b = strdupa(p);
(void) bindtextdomain(dict, h->fmh_binding);
}
len = strlen(code) + 1 + strlen(fmd_msg_items[FMD_MSG_ITEM_TYPE]) + 1;
key = alloca(len);
(void) snprintf(key, len, "%s.%s",
code, fmd_msg_items[FMD_MSG_ITEM_TYPE]);
p = setlocale(LC_ALL, NULL);
old_c = strdupa(p);
if (locale != NULL)
(void) setlocale(LC_ALL, locale);
if (dgettext(dict, key) == key &&
(locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
(void) setlocale(LC_ALL, "C");
locale = "C";
}
if (dgettext(dict, key) == key) {
s = NULL;
err = ENOENT;
} else {
s = fmd_msg_gettext_locked(h, nvl, dict, code);
err = errno;
}
if (locale != NULL)
(void) setlocale(LC_ALL, old_c);
if (h->fmh_binding != NULL)
(void) bindtextdomain(dict, old_b);
fmd_msg_unlock();
if (s == NULL)
errno = err;
return (s);
}
char *
fmd_msg_gettext_nv(fmd_msg_hdl_t *h, const char *locale, nvlist_t *nvl)
{
char *code;
if (nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code) != 0) {
errno = EINVAL;
return (NULL);
}
return (fmd_msg_gettext(h, locale, nvl, code));
}
char *
fmd_msg_gettext_id(fmd_msg_hdl_t *h, const char *locale, const char *code)
{
return (fmd_msg_gettext(h, locale, NULL, code));
}