Mercurial > illumos > illumos-gate
changeset 9501:f84d3cc28d24
PSARC 2009/265 fmdump -m
6810965 port fmdump -m to ON
6802474 Port libfmd_msg to ON
6805723 libtopo: port fmtopo -m to ON
line wrap: on
line diff
--- a/usr/src/cmd/fm/dicts/Makefile Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/dicts/Makefile Wed Apr 29 08:32:53 2009 -0700 @@ -35,7 +35,8 @@ ZFS \ SCA500 \ SCA1000 \ - SENSOR + SENSOR \ + TEST i386_DCNAMES = \ AMD \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/fm/dicts/TEST.dict Wed Apr 29 08:32:53 2009 -0700 @@ -0,0 +1,28 @@ +# +# +# 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. +# + +FMDICT: name=TEST version=1 maxkey=1 + +fault.test01=0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/fm/dicts/TEST.po Wed Apr 29 08:32:53 2009 -0700 @@ -0,0 +1,41 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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 +# +# +# This is portable object file is used for testing libfmd_msg. It +# is not delivered in any package. +# + +msgid "TEST-8000-08.type" +msgstr "Defect" +msgid "TEST-8000-08.severity" +msgstr "Minor" +msgid "TEST-8000-08.description" +msgstr "This entry tests URL variable expansion - url = %s" +msgid "TEST-8000-08.response" +msgstr "This entry tests the percent character escape sequence: %%" +msgid "TEST-8000-08.impact" +msgstr "This entry tests variable expansion for event payload members: uuid = %<uuid>, de scheme = %<de.scheme>" +msgid "TEST-8000-08.action" +msgstr "Variable expansion for arrays: index = %<test_arr[1].index>" +
--- a/usr/src/cmd/fm/fmadm/Makefile.com Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmadm/Makefile.com Wed Apr 29 08:32:53 2009 -0700 @@ -19,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" .KEEP_STATE: .SUFFIXES: @@ -43,7 +42,7 @@ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -I. -I../common CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) $(XSTRCONST) -LDLIBS += -L$(ROOT)/usr/lib/fm -lfmd_adm +LDLIBS += -L$(ROOT)/usr/lib/fm -lfmd_adm -lfmd_msg LDLIBS += -lnvpair -ltopo LDFLAGS += -R/usr/lib/fm LINTFLAGS += -mnu
--- a/usr/src/cmd/fm/fmadm/common/faulty.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmadm/common/faulty.c Wed Apr 29 08:32:53 2009 -0700 @@ -37,6 +37,7 @@ #include <sys/fm/protocol.h> #include <fm/libtopo.h> #include <fm/fmd_adm.h> +#include <fm/fmd_msg.h> #include <dlfcn.h> #include <sys/systeminfo.h> #include <sys/utsname.h> @@ -49,7 +50,6 @@ #define offsetof(s, m) ((size_t)(&(((s*)0)->m))) /* - * catalog_setup() must be called to setup support functions. * Fault records are added to catalog by calling add_fault_record_to_catalog() * records are stored in order of importance to the system. * If -g flag is set or not_suppressed is not set and the class fru, fault, @@ -212,7 +212,6 @@ name_list_t *asru; name_list_t *fru; name_list_t *serial; - char *url; uint8_t not_suppressed; } status_record_t; @@ -240,8 +239,6 @@ resource_list_t *status_fru_list; resource_list_t *status_asru_list; -static char *locale; -static char *nlspath; static int max_display; static int max_fault = 0; static topo_hdl_t *topo_handle; @@ -249,6 +246,7 @@ static host_id_list_t *host_list; static int n_server; static int opt_g; +static fmd_msg_hdl_t *fmadm_msghdl = NULL; /* handle for libfmd_msg calls */ static char * format_date(char *buf, size_t len, uint64_t sec) @@ -324,95 +322,6 @@ return (rt); } -static void -catalog_setup(void) -{ - char *tp; - int pl; - - /* - * All FMA event dictionaries use msgfmt(1) message objects to produce - * messages, even for the C locale. We therefore want to use dgettext - * for all message lookups, but its defined behavior in the C locale is - * to return the input string. Since our input strings are event codes - * and not format strings, this doesn't help us. We resolve this nit - * by setting NLSPATH to a non-existent file: the presence of NLSPATH - * is defined to force dgettext(3C) to do a full lookup even for C. - */ - nlspath = getenv("NLSPATH"); - if (nlspath == NULL) - putenv("NLSPATH=/usr/lib/fm/fmd/fmd.cat"); - else { - pl = strlen(nlspath) + sizeof ("NLSPATH=") + 1; - tp = malloc(pl); - (void) snprintf(tp, pl, "NLSPATH=%s", nlspath); - nlspath = tp; - } - - locale = setlocale(LC_MESSAGES, ""); -} - -static char * -get_dict_url(char *id) -{ - char *url = "http://sun.com/msg/"; - int msz = sizeof (url) + strlen(id) + 1; - char *cp; - - cp = malloc(msz); - (void) snprintf(cp, msz, "%s%s", url, id); - return (cp); -} - -static char * -get_dict_msg(char *id, char *idx, int unknown, int translate) -{ - char mbuf[128]; - char *msg; - char dbuf[32]; - char *p; - int restore_env = 0; - int restore_locale = 0; - - p = strchr(id, '-'); - if (p == NULL || p == id || (p - id) >= 32) { - msg = mbuf; - } else { - strncpy(dbuf, id, (size_t)(p - id)); - dbuf[(size_t)(p - id)] = 0; - - (void) snprintf(mbuf, sizeof (mbuf), "%s.%s", id, idx); - if (translate == 0 || nlspath == NULL) { - (void) setlocale(LC_MESSAGES, "C"); - restore_locale = 1; - } - bindtextdomain("FMD", "/usr/lib/locale"); - msg = dgettext(dbuf, mbuf); - if (msg == mbuf) { - (void) setlocale(LC_MESSAGES, "C"); - restore_locale = 1; - msg = dgettext(dbuf, mbuf); - } - if (msg == mbuf) { - putenv("NLSPATH=/usr/lib/fm/fmd/fmd.cat"); - restore_env = 1; - (void) setlocale(LC_MESSAGES, "C"); - msg = dgettext(dbuf, mbuf); - } - if (restore_locale) - (void) setlocale(LC_MESSAGES, locale); - if (restore_env && nlspath) - putenv(nlspath); - } - if (msg == mbuf) { - if (unknown) - msg = "unknown"; - else - msg = NULL; - } - return (msg); -} - /* * compare two fru strings which are made up of substrings seperated by '/' * return true if every substring is the same in the two strings, or if a @@ -751,7 +660,7 @@ static status_record_t * new_record_init(uurec_t *uurec_p, char *msgid, name_list_t *class, name_list_t *fru, name_list_t *asru, name_list_t *resource, - name_list_t *serial, const char *url, boolean_t not_suppressed, + name_list_t *serial, boolean_t not_suppressed, hostid_t *hostid) { status_record_t *status_rec_p; @@ -763,13 +672,14 @@ uurec_p->next = NULL; uurec_p->prev = NULL; uurec_p->asru = asru; - status_rec_p->severity = get_dict_msg(msgid, "severity", 1, 0); + if ((status_rec_p->severity = fmd_msg_getitem_id(fmadm_msghdl, NULL, + msgid, FMD_MSG_ITEM_SEVERITY)) == NULL) + status_rec_p->severity = strdup("unknown"); status_rec_p->class = class; status_rec_p->fru = fru; status_rec_p->asru = asru; status_rec_p->resource = resource; status_rec_p->serial = serial; - status_rec_p->url = url ? strdup(url) : NULL; status_rec_p->msgid = strdup(msgid); status_rec_p->not_suppressed = not_suppressed; return (status_rec_p); @@ -928,13 +838,13 @@ static void catalog_new_record(uurec_t *uurec_p, char *msgid, name_list_t *class, name_list_t *fru, name_list_t *asru, name_list_t *resource, - name_list_t *serial, const char *url, boolean_t not_suppressed, + name_list_t *serial, boolean_t not_suppressed, hostid_t *hostid) { status_record_t *status_rec_p; status_rec_p = new_record_init(uurec_p, msgid, class, fru, asru, - resource, serial, url, not_suppressed, hostid); + resource, serial, not_suppressed, hostid); add_rec_list(status_rec_p, &status_rec_list); if (status_rec_p->fru) add_list(status_rec_p, status_rec_p->fru, &status_fru_list); @@ -949,7 +859,7 @@ static void catalog_merge_record(status_record_t *status_rec_p, uurec_t *uurec_p, name_list_t *asru, name_list_t *resource, name_list_t *serial, - const char *url, boolean_t not_suppressed) + boolean_t not_suppressed) { uurec_t *uurec1_p; @@ -969,8 +879,6 @@ uurec_p->prev = uurec1_p; uurec1_p->next = uurec_p; } - if (status_rec_p->url == NULL && url != NULL) - status_rec_p->url = strdup(url); status_rec_p->not_suppressed |= not_suppressed; uurec_p->asru = merge_name_list(&status_rec_p->asru, asru, 0); (void) merge_name_list(&status_rec_p->resource, resource, 0); @@ -1102,8 +1010,7 @@ } static void -add_fault_record_to_catalog(nvlist_t *nvl, uint64_t sec, char *uuid, - const char *url) +add_fault_record_to_catalog(nvlist_t *nvl, uint64_t sec, char *uuid) { char *msgid = "-"; uint_t i, size = 0; @@ -1152,12 +1059,12 @@ status_rec_p = record_in_catalog(class, fru, msgid, host); if (status_rec_p) { catalog_merge_record(status_rec_p, uurec_p, asru, resource, - serial, url, not_suppressed); + serial, not_suppressed); free_name_list(class); free_name_list(fru); } else { catalog_new_record(uurec_p, msgid, class, fru, asru, - resource, serial, url, not_suppressed, host); + resource, serial, not_suppressed, host); } } @@ -1238,45 +1145,23 @@ } static void -print_dict_info(char *msgid, char *url) +print_dict_info_line(char *msgid, fmd_msg_item_t what, const char *linehdr) { - const char *cp; - char *l_url; - char *buf; - int bufsz; + char *cp = fmd_msg_getitem_id(fmadm_msghdl, NULL, msgid, what); - cp = get_dict_msg(msgid, "description", 0, 1); if (cp) { - if (url) - l_url = url; - else - l_url = get_dict_url(msgid); - bufsz = strlen(cp) + strlen(l_url) + 1; - buf = malloc(bufsz); - (void) snprintf(buf, bufsz, cp, l_url); - print_line(dgettext("FMD", "Description : "), buf); - free(buf); - if (!url) - free(l_url); + print_line(dgettext("FMD", linehdr), cp); + free(cp); } - cp = get_dict_msg(msgid, "response", 0, 1); - if (cp) { - buf = strdup(cp); - print_line(dgettext("FMD", "Response : "), buf); - free(buf); - } - cp = get_dict_msg(msgid, "impact", 0, 1); - if (cp) { - buf = strdup(cp); - print_line(dgettext("FMD", "Impact : "), buf); - free(buf); - } - cp = get_dict_msg(msgid, "action", 0, 1); - if (cp) { - buf = strdup(cp); - print_line(dgettext("FMD", "Action : "), buf); - free(buf); - } +} + +static void +print_dict_info(char *msgid) +{ + print_dict_info_line(msgid, FMD_MSG_ITEM_DESC, "Description : "); + print_dict_info_line(msgid, FMD_MSG_ITEM_RESPONSE, "Response : "); + print_dict_info_line(msgid, FMD_MSG_ITEM_IMPACT, "Impact : "); + print_dict_info_line(msgid, FMD_MSG_ITEM_ACTION, "Action : "); } static void @@ -1598,7 +1483,7 @@ print_name_list(srp->serial, dgettext("FMD", "Serial ID. :"), NULL, 0, 0, NULL, full); } - print_dict_info(srp->msgid, srp->url); + print_dict_info(srp->msgid); (void) printf("\n"); } @@ -1607,16 +1492,10 @@ { char buf[32]; uurec_t *uurp = srp->uurec; - char *severity; static int header = 0; char *head; ari_list_t *ari_list; - if (nlspath) - severity = get_dict_msg(srp->msgid, "severity", 1, 1); - else - severity = srp->severity; - if (!summary || !header) { if (opt_i) { head = "--------------- " @@ -1645,13 +1524,13 @@ while (ari_list) { (void) printf("%-15s %-37s %-14s %-9s\n", format_date(buf, sizeof (buf), uurp->sec), - ari_list->ari_uuid, srp->msgid, severity); + ari_list->ari_uuid, srp->msgid, srp->severity); ari_list = ari_list->next; } } else { (void) printf("%-15s %-37s %-14s %-9s\n", format_date(buf, sizeof (buf), uurp->sec), - uurp->uuid, srp->msgid, severity); + uurp->uuid, srp->msgid, srp->severity); } if (!summary) @@ -1820,8 +1699,7 @@ if (msgid == NULL || strcmp(msgid, srp->msgid) != 0) { msgid = srp->msgid; - print_dict_info(srp->msgid, - srp->url); + print_dict_info(srp->msgid); } slp = slp->next; } while (slp != end); @@ -1918,7 +1796,7 @@ &uuid); if (uurecp == NULL || uuid_in_list(uuid, uurecp)) add_fault_record_to_catalog(acp->aci_event, *diag_time, - uuid, acp->aci_url); + uuid); } else { rt = -1; } @@ -1973,7 +1851,6 @@ uurec_select_t *tp; uurec_select_t *uurecp = NULL; - catalog_setup(); while ((c = getopt(argc, argv, "afgin:prsu:v")) != EOF) { switch (c) { case 'a': @@ -2017,6 +1894,8 @@ if (optind < argc) return (FMADM_EXIT_USAGE); + if ((fmadm_msghdl = fmd_msg_init(NULL, FMD_MSG_VERSION)) == NULL) + return (FMADM_EXIT_ERROR); rt = get_cases_from_fmd(adm, uurecp, opt_i); if (opt_p) { if ((pager = getenv("PAGER")) == NULL) @@ -2038,6 +1917,7 @@ print_asru(opt_a); if (opt_f == 0 && opt_r == 0) print_catalog(opt_s, opt_a, opt_v, opt_i, opt_p && !opt_s); + fmd_msg_fini(fmadm_msghdl); label_release_topo(); if (opt_p) { (void) fclose(stdout);
--- a/usr/src/cmd/fm/fmd/common/fmd_xprt.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmd/common/fmd_xprt.c Wed Apr 29 08:32:53 2009 -0700 @@ -369,6 +369,7 @@ 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; @@ -391,6 +392,19 @@ 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);
--- a/usr/src/cmd/fm/fmdump/Makefile.com Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmdump/Makefile.com Wed Apr 29 08:32:53 2009 -0700 @@ -2,9 +2,8 @@ # 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. +# 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. @@ -20,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" .KEEP_STATE: .SUFFIXES: @@ -38,7 +36,7 @@ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -I. -I../common -I../../include CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) $(XSTRCONST) -LDLIBS += -L$(ROOT)/usr/lib/fm -lfmd_log -lnvpair -ltopo +LDLIBS += -L$(ROOT)/usr/lib/fm -lfmd_log -lnvpair -ltopo -lfmd_msg LDFLAGS += -R/usr/lib/fm LINTFLAGS += -mnu
--- a/usr/src/cmd/fm/fmdump/common/asru.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmdump/common/asru.c Wed Apr 29 08:32:53 2009 -0700 @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <fmdump.h> #include <strings.h> #include <stdio.h> @@ -93,5 +91,7 @@ }, { "TIME UUID STATE", (fmd_log_rec_f *)asru_verb2 +}, { +NULL, NULL } } };
--- a/usr/src/cmd/fm/fmdump/common/error.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmdump/common/error.c Wed Apr 29 08:32:53 2009 -0700 @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <fmdump.h> #include <stdio.h> #include <time.h> @@ -81,5 +79,7 @@ }, { "TIME CLASS", (fmd_log_rec_f *)err_verb2 +}, { +NULL, NULL } } };
--- a/usr/src/cmd/fm/fmdump/common/fault.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmdump/common/fault.c Wed Apr 29 08:32:53 2009 -0700 @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <fmdump.h> #include <stdio.h> #include <strings.h> @@ -164,6 +162,51 @@ return (0); } +/* + * There is a lack of uniformity in how the various entries in our diagnosis + * are terminated. Some end with one newline, others with two. This makes the + * output of fmdump -m look a bit ugly. Therefore we postprocess the message + * before printing it, removing consecutive occurences of newlines. + */ +static void +postprocess_msg(char *msg) +{ + int i = 0, j = 0; + char *buf; + + if ((buf = malloc(strlen(msg) + 1)) == NULL) + return; + + buf[j++] = msg[i++]; + for (i = 1; i < strlen(msg); i++) { + if (!(msg[i] == '\n' && msg[i - 1] == '\n')) + buf[j++] = msg[i]; + } + buf[j] = '\0'; + (void) strncpy(msg, buf, j+1); + free(buf); +} + +/*ARGSUSED*/ +static int +flt_msg(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp) +{ + char *msg; + + if ((msg = fmd_msg_gettext_nv(g_msg, NULL, rp->rec_nvl)) == NULL) { + (void) fprintf(stderr, "%s: failed to format message: %s\n", + g_pname, strerror(errno)); + g_errs++; + return (-1); + } else { + postprocess_msg(msg); + fmdump_printf(fp, "%s\n", msg); + free(msg); + } + + return (0); +} + const fmdump_ops_t fmdump_flt_ops = { "fault", { { @@ -175,5 +218,8 @@ }, { NULL, (fmd_log_rec_f *)flt_verb2 +}, { +NULL, +(fmd_log_rec_f *)flt_msg } } };
--- a/usr/src/cmd/fm/fmdump/common/fmdump.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmdump/common/fmdump.c Wed Apr 29 08:32:53 2009 -0700 @@ -47,7 +47,9 @@ ulong_t g_errs; ulong_t g_recs; char *g_root; + struct topo_hdl *g_thp; +fmd_msg_hdl_t *g_msg; /*PRINTFLIKE2*/ void @@ -135,7 +137,7 @@ static int usage(FILE *fp) { - (void) fprintf(fp, "Usage: %s [-efvV] [-c class] [-R root] [-t time] " + (void) fprintf(fp, "Usage: %s [-efmvV] [-c class] [-R root] [-t time] " "[-T time] [-u uuid]\n\t\t[-n name[.name]*[=value]] [file]\n", g_pname); @@ -143,6 +145,7 @@ "\t-c select events that match the specified class\n" "\t-e display error log content instead of fault log content\n" "\t-f follow growth of log file by waiting for additional data\n" + "\t-m display human-readable messages for the fault log\n" "\t-R set root directory for pathname expansions\n" "\t-t select events that occurred after the specified time\n" "\t-T select events that occurred before the specified time\n" @@ -533,7 +536,7 @@ int main(int argc, char *argv[]) { - int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0; + int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0; int opt_u = 0, opt_v = 0, opt_V = 0; char ifile[PATH_MAX] = ""; @@ -565,7 +568,7 @@ while (optind < argc) { while ((c = - getopt(argc, argv, "ac:efHn:O:R:t:T:u:vV")) != EOF) { + getopt(argc, argv, "ac:efHmn:O:R:t:T:u:vV")) != EOF) { switch (c) { case 'a': opt_a++; @@ -584,6 +587,9 @@ case 'H': opt_H++; break; + case 'm': + opt_m++; + break; case 'O': off = strtoull(optarg, NULL, 16); iflags |= FMD_LOG_XITER_OFFS; @@ -653,6 +659,12 @@ return (FMDUMP_EXIT_USAGE); } + if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL) { + (void) fprintf(stderr, "%s: failed to initialize " + "libfmd_msg: %s\n", g_pname, strerror(errno)); + return (FMDUMP_EXIT_FATAL); + } + if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) { (void) fprintf(stderr, "%s: failed to open %s: %s\n", g_pname, ifile, fmd_log_errmsg(NULL, err)); @@ -700,9 +712,17 @@ iflags |= FMD_LOG_XITER_REFS; } else if (opt_v) { arg.da_fmt = &ops->do_formats[FMDUMP_VERB1]; + } else if (opt_m) { + arg.da_fmt = &ops->do_formats[FMDUMP_MSG]; } else arg.da_fmt = &ops->do_formats[FMDUMP_SHORT]; + if (opt_m && arg.da_fmt->do_func == NULL) { + (void) fprintf(stderr, "%s: -m mode is not supported for " + "log of type %s: %s\n", g_pname, fmd_log_label(lp), ifile); + return (FMDUMP_EXIT_USAGE); + } + arg.da_fv = errfv; arg.da_fc = errfc; arg.da_fp = stdout; @@ -777,6 +797,11 @@ if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO)) (void) fprintf(stderr, "%s: %s is empty\n", g_pname, ifile); + if (g_thp != NULL) + topo_close(g_thp); + fmd_log_close(lp); + fmd_msg_fini(g_msg); + return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS); }
--- a/usr/src/cmd/fm/fmdump/common/fmdump.h Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmdump/common/fmdump.h Wed Apr 29 08:32:53 2009 -0700 @@ -2,9 +2,8 @@ * 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. + * 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. @@ -20,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMDUMP_H #define _FMDUMP_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -38,13 +35,16 @@ #include <sys/types.h> #include <sys/fm/protocol.h> + #include <fm/fmd_log.h> +#include <fm/fmd_msg.h> #include <fm/libtopo.h> enum { FMDUMP_SHORT, FMDUMP_VERB1, FMDUMP_VERB2, + FMDUMP_MSG, FMDUMP_NFMTS }; @@ -77,7 +77,9 @@ extern ulong_t g_errs; extern ulong_t g_recs; extern char *g_root; + extern struct topo_hdl *g_thp; +extern fmd_msg_hdl_t *g_msg; extern void fmdump_printf(FILE *, const char *, ...); extern void fmdump_warn(const char *, ...);
--- a/usr/src/cmd/fm/fmtopo/common/fmtopo.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/fmtopo/common/fmtopo.c Wed Apr 29 08:32:53 2009 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -49,7 +49,8 @@ static const char *opt_R = "/"; static const char *opt_s = FM_FMRI_SCHEME_HC; -static const char optstr[] = "bCdeP:pR:s:StVx"; +static const char optstr[] = "bCdem:P:pR:s:StVx"; +static const char *opt_m; static int opt_b = 0; static int opt_d = 0; @@ -76,13 +77,14 @@ { (void) fprintf(fp, "Usage: %s [-bCedpSVx] [-P group.property[=type:value]] " - "[-R root] [-s scheme] [fmri]\n", g_pname); + "[-R root] [-m method] [-s scheme] [fmri]\n", g_pname); (void) fprintf(fp, "\t-b walk in sibling-first order (default is child-first)\n" "\t-C dump core after completing execution\n" "\t-d set debug mode for libtopo modules\n" "\t-e display FMRIs as paths using esc/eft notation\n" + "\t-m execute given method\n" "\t-P get/set specified properties\n" "\t-p display of FMRI protocol properties\n" "\t-R set root directory for libtopo plug-ins and other files\n" @@ -796,7 +798,7 @@ { int err; nvlist_t *nvl; - nvlist_t *rsrc; + nvlist_t *rsrc, *out; char *s; if (opt_e && strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0) { @@ -818,15 +820,25 @@ } if (g_fmri != NULL && fnmatch(g_fmri, s, 0) != 0) { - nvlist_free(rsrc); - topo_hdl_strfree(thp, s); - return (TOPO_WALK_NEXT); + nvlist_free(rsrc); + topo_hdl_strfree(thp, s); + return (TOPO_WALK_NEXT); } print_node(thp, node, rsrc, s); topo_hdl_strfree(thp, s); nvlist_free(rsrc); + if (opt_m != NULL) { + if (topo_method_invoke(node, opt_m, 0, NULL, &out, &err) == 0) { + nvlist_print(stdout, out); + nvlist_free(out); + } else if (err != ETOPO_METHOD_NOTSUP) + (void) fprintf(stderr, "%s: method failed unexpectedly " + "on %s=%d (%s)\n", g_pname, topo_node_name(node), + topo_node_instance(node), topo_strerror(err)); + } + if (opt_V || opt_all) { if ((nvl = topo_prop_getprops(node, &err)) == NULL) { (void) fprintf(stderr, "%s: failed to get " @@ -1175,6 +1187,9 @@ case 'e': opt_e++; break; + case 'm': + opt_m = optarg; + break; case 'P': pcnt++; break;
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile Wed Apr 29 08:32:53 2009 -0700 @@ -20,7 +20,7 @@ # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # @@ -35,7 +35,7 @@ include ../../Makefile.plugin -SNMPLIBS = -lnetsnmp -lnetsnmpagent +SNMPLIBS = -lnetsnmp -lnetsnmpagent -lfmd_msg lint := SNMPLIBS = LDFLAGS += -L$(ROOT)/usr/lib/fm -R/usr/lib/fm
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c Wed Apr 29 08:32:53 2009 -0700 @@ -20,12 +20,10 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/fm/protocol.h> #include <fm/fmd_api.h> #include <fm/fmd_snmp.h> @@ -40,30 +38,6 @@ #include <limits.h> #include <alloca.h> -/* - * SNMP_DOMAIN defines the dgettext() parameters the agent - * can use to retrieve the localized format string for diagnosis messages. - * The format string retrieved from SNMP_DOMAIN is the default format - * string, but when processing each suspect list, dgettext() is also called - * for the domain that matches the diagcode dictname. - * - * Similarly, SNMP_URL is also checked to see if snmp_url - * should be overridden for each suspect list. - * - * The net effect of all this is that for a given diagcode DICT-1234-56: - * - * - If DICT.mo defines snmp-url, it is used when filling - * in the sunFmProblemURL variable. - * - * - Otherwise, if snmp-trapgen.conf defines a "url" property, that - * value is used. - * - * - Otherwise, the default "http://sun.com/msg/" is used (via the - * fmd_props[] table defined in this file). - */ -static const char SNMP_DOMAIN[] = "FMD"; -static const char SNMP_URL[] = SNMP_URL_MSG; - static struct stats { fmd_stat_t bad_vers; fmd_stat_t bad_code; @@ -71,14 +45,13 @@ fmd_stat_t no_trap; } snmp_stats = { { "bad_vers", FMD_TYPE_UINT64, "event version is missing or invalid" }, - { "bad_code", FMD_TYPE_UINT64, "event code has no dictionary name" }, + { "bad_code", FMD_TYPE_UINT64, "failed to compute url for code" }, { "bad_uuid", FMD_TYPE_UINT64, "event uuid is too long to send" }, { "no_trap", FMD_TYPE_UINT64, "trap generation suppressed" } }; -static char *snmp_locdir; /* l10n messages directory (if alternate) */ -static char *snmp_url; /* current value of "url" property */ -static int snmp_trapall; /* set to trap on all faults */ +static fmd_msg_hdl_t *snmp_msghdl; /* handle for libfmd_msg */ +static int snmp_trapall; /* set to trap on all faults */ static const char SNMP_SUPPCONF[] = "fmd-trapgen"; @@ -167,13 +140,9 @@ static void snmp_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) { - char *uuid, *code, *dict, *url, *urlcode, *locdir, *p; + char *uuid, *code, *url; boolean_t domsg; - uint8_t version; - char *olang = NULL; - int locale_c = 0; - size_t len; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_SUSPECT_VERSION) { @@ -189,79 +158,17 @@ return; } - /* - * Extract the uuid and diagcode dictionary from the event code. The - * dictionary name is the text preceding the first "-" in the code. - */ (void) nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid); (void) nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code); - if ((p = strchr(code, '-')) == NULL || p == code) { - fmd_hdl_debug(hdl, "invalid diagnosis code: %s\n", code); - snmp_stats.bad_code.fmds_value.ui64++; - return; - } - - dict = alloca((size_t)(p - code) + 1); - (void) strncpy(dict, code, (size_t)(p - code)); - dict[(size_t)(p - code)] = '\0'; - - fmd_msg_lock(); - - if (snmp_locdir != NULL) - locdir = bindtextdomain(dict, snmp_locdir); - - if ((url = dgettext(dict, SNMP_URL)) == SNMP_URL) { - /* - * We didn't find a translation in the dictionary for the - * current language. Fall back to C and try again. - */ - olang = setlocale(LC_MESSAGES, NULL); - if (olang) { - p = alloca(strlen(olang) + 1); - olang = strcpy(p, olang); - } - locale_c = 1; - (void) setlocale(LC_MESSAGES, "C"); - if ((url = dgettext(dict, SNMP_URL)) == SNMP_URL) - url = snmp_url; - } + url = fmd_msg_getitem_nv(snmp_msghdl, NULL, nvl, FMD_MSG_ITEM_URL); - /* - * If the URL ends with a slash, that indicates the code should be - * appended to it. After formatting the URL, reformat the DESC - * text using the URL as an snprintf argument. - */ - len = strlen(url); - if (url[len - 1] == '/') { - urlcode = alloca(len + strlen(code) + 1); - (void) snprintf(urlcode, INT_MAX, "%s%s", url, code); + if (url != NULL) { + send_trap(hdl, uuid, code, url); + free(url); } else { - urlcode = url; - } - - /* - * We have what we need; now send the trap. - */ - send_trap(hdl, uuid, code, urlcode); - - /* - * Switch back to our original language if we had to fall back to C. - */ - if (olang != NULL) - (void) setlocale(LC_MESSAGES, olang); - - if (snmp_locdir != NULL) - (void) bindtextdomain(dict, locdir); - - fmd_msg_unlock(); - - if (locale_c) { - fmd_hdl_debug(hdl, - url == snmp_url ? - "dgettext(%s, %s) in %s and C failed\n" : - "dgettext(%s, %s) in %s failed; C used\n", - dict, SNMP_URL, olang ? olang : "<null>"); + fmd_hdl_debug(hdl, "failed to format url for %s", uuid); + snmp_stats.bad_code.fmds_value.ui64++; } } @@ -330,7 +237,7 @@ void _fmd_init(fmd_hdl_t *hdl) { - char *rootdir, *locdir, *locale, *p; + char *rootdir, *urlbase; if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) return; /* invalid data in configuration file */ @@ -341,72 +248,28 @@ if (init_sma() != SNMPERR_SUCCESS) fmd_hdl_abort(hdl, "snmp-trapgen agent initialization failed"); - /* - * All FMA event dictionaries use msgfmt(1) message objects to produce - * messages, even for the C locale. We therefore want to use dgettext - * for all message lookups, but its defined behavior in the C locale is - * to return the input string. Since our input strings are event codes - * and not format strings, this doesn't help us. We resolve this nit - * by setting NLSPATH to a non-existent file: the presence of NLSPATH - * is defined to force dgettext(3C) to do a full lookup even for C. - */ - if (getenv("NLSPATH") == NULL && putenv(fmd_hdl_strdup(hdl, - "NLSPATH=/usr/lib/fm/fmd/fmd.cat", FMD_SLEEP)) != 0) - fmd_hdl_abort(hdl, "snmp-trapgen failed to set NLSPATH"); - - fmd_msg_lock(); - (void) setlocale(LC_MESSAGES, ""); - locale = setlocale(LC_MESSAGES, NULL); - if (locale) { - p = alloca(strlen(locale) + 1); - locale = strcpy(p, locale); - } else { - locale = "<null>"; - } - fmd_msg_unlock(); - fmd_hdl_debug(hdl, "locale=%s\n", locale); + rootdir = fmd_prop_get_string(hdl, "fmd.rootdir"); + snmp_msghdl = fmd_msg_init(rootdir, FMD_MSG_VERSION); + fmd_prop_free_string(hdl, rootdir); - /* - * Cache any properties we use every time we receive an event and - * subscribe to list.suspect events regardless of the .conf file. - */ - snmp_url = fmd_prop_get_string(hdl, "url"); - snmp_trapall = fmd_prop_get_int32(hdl, "trap_all"); - - /* - * If fmd's rootdir property is set to a non-default root, then we are - * going to need to rebind the text domains we use for dgettext() as - * we go. Look up the default l10n messages directory and make - * snmp_locdir be this path with fmd.rootdir prepended to it. - */ - rootdir = fmd_prop_get_string(hdl, "fmd.rootdir"); + if (snmp_msghdl == NULL) + fmd_hdl_abort(hdl, "failed to initialize libfmd_msg"); - if (*rootdir != '\0' && strcmp(rootdir, "/") != 0) { - fmd_msg_lock(); - locdir = bindtextdomain(SNMP_DOMAIN, NULL); - fmd_msg_unlock(); - if (locdir != NULL) { - size_t len = strlen(rootdir) + strlen(locdir) + 1; - snmp_locdir = fmd_hdl_alloc(hdl, len, FMD_SLEEP); - (void) snprintf(snmp_locdir, len, "%s%s", rootdir, - locdir); - fmd_hdl_debug(hdl, - "binding textdomain to %s for snmp\n", - snmp_locdir); - } - } + urlbase = fmd_prop_get_string(hdl, "url"); + (void) fmd_msg_url_set(snmp_msghdl, urlbase); + fmd_prop_free_string(hdl, urlbase); - fmd_prop_free_string(hdl, rootdir); + snmp_trapall = fmd_prop_get_int32(hdl, "trap_all"); fmd_hdl_subscribe(hdl, FM_LIST_SUSPECT_CLASS); fmd_hdl_subscribe(hdl, FM_LIST_REPAIRED_CLASS); fmd_hdl_subscribe(hdl, FM_LIST_RESOLVED_CLASS); } +/*ARGSUSED*/ void _fmd_fini(fmd_hdl_t *hdl) { - fmd_hdl_strfree(hdl, snmp_locdir); - fmd_prop_free_string(hdl, snmp_url); + fmd_msg_fini(snmp_msghdl); /* * snmp_shutdown, which we would normally use here, calls free_slots,
--- a/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c Wed Apr 29 08:32:53 2009 -0700 @@ -18,6 +18,7 @@ * * CDDL HEADER END */ + /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -26,66 +27,28 @@ #include <sys/fm/protocol.h> #include <sys/strlog.h> #include <sys/log.h> + #include <fm/fmd_api.h> #include <fm/fmd_msg.h> #include <stropts.h> +#include <strings.h> #include <syslog.h> -#include <locale.h> -#include <strings.h> +#include <alloca.h> +#include <unistd.h> #include <stdlib.h> -#include <unistd.h> -#include <limits.h> -#include <alloca.h> #include <errno.h> #include <fcntl.h> -#include <time.h> - -/* - * SYSLOG_DOMAIN and SYSLOG_TEMPLATE define the dgettext() parameters the agent - * can use to retrieve the localized format string for diagnosis messages. - * The format string retrieved from SYSLOG_DOMAIN is the default format - * string, but when processing each suspect list, dgettext() is also called - * for the domain that matches the diagcode dictname and if SYSLOG_TEMPLATE - * is defined, it overrides the default for that suspect list only. - * - * Similarly, SYSLOG_URL is also checked to see if syslog_url - * should be overridden for each suspect list. - * - * The net effect of all this is that for a given diagcode DICT-1234-56: - * - * - If DICT.mo defines syslog-msgs-message-template, it is used - * as the format string for the diagnosis message. - * - * - Otherwise, syslog-msgs-message-template from FMD.mo is used. - * - * - If DICT.mo defines syslog-url, it is used when filling - * in the %s in the "description" message. - * - * - Otherwise, if syslog-msgs.conf defines a "url" property, that - * value is used. - * - * - Otherwise, the default "http://sun.com/msg/" is used (via the - * fmd_props[] table defined in this file). - */ -static const char SYSLOG_DOMAIN[] = "FMD"; -static const char SYSLOG_TEMPLATE[] = "syslog-msgs-message-template"; -static const char SYSLOG_URL[] = "syslog-url"; -static const char SYSLOG_POINTER[] = "syslog-msgs-pointer"; static struct stats { fmd_stat_t bad_vers; - fmd_stat_t bad_fmri; fmd_stat_t bad_code; - fmd_stat_t bad_time; fmd_stat_t log_err; fmd_stat_t msg_err; fmd_stat_t no_msg; } syslog_stats = { { "bad_vers", FMD_TYPE_UINT64, "event version is missing or invalid" }, - { "bad_fmri", FMD_TYPE_UINT64, "event fmri is missing or invalid" }, { "bad_code", FMD_TYPE_UINT64, "event code has no dictionary name" }, - { "bad_time", FMD_TYPE_UINT64, "event time is not properly encoded" }, { "log_err", FMD_TYPE_UINT64, "failed to log message to log(7D)" }, { "msg_err", FMD_TYPE_UINT64, "failed to log message to sysmsg(7D)" }, { "no_msg", FMD_TYPE_UINT64, "message logging suppressed" } @@ -107,15 +70,14 @@ { NULL, 0 } }; -static char *syslog_locdir; /* l10n messages directory (if alternate) */ -static char *syslog_url; /* current value of "url" property */ -static char *syslog_pointer; /* info to point user to the full message */ +static fmd_msg_hdl_t *syslog_msghdl; /* handle for libfmd_msg calls */ static int syslog_msgall; /* set to message all faults */ static log_ctl_t syslog_ctl; /* log(7D) meta-data for each msg */ static int syslog_logfd = -1; /* log(7D) file descriptor */ static int syslog_msgfd = -1; /* sysmsg(7D) file descriptor */ static int syslog_file; /* log to syslog_logfd */ static int syslog_cons; /* log to syslog_msgfd */ +static const char SYSLOG_POINTER[] = "syslog-msgs-pointer"; /* * Ideally we would just use syslog(3C) for outputting our messages, but our @@ -139,77 +101,70 @@ * set in the log_ctl_t. The log driver allows us to set SL_LOGONLY when we * construct messages ourself, indicating that syslogd should only emit the * message to /var/adm/messages and any remote hosts, and skip the console. - * Note: the log driver packet size limit for output via putmsg is LOGMAX_PS. * Then we emit the message a second time, without the special prefix, to the * sysmsg(7D) device, which handles console redirection and also permits us * to output any characters we like to the console, including \n and \r. */ -/*PRINTFLIKE2*/ static void -syslog_emit(fmd_hdl_t *hdl, const char *msgformat, ...) +syslog_emit(fmd_hdl_t *hdl, const char *msg) { struct strbuf ctl, dat; uint32_t msgid; - char *format, *p, c; - char *buf = NULL; - size_t formatlen, logmsglen; - int len, plen; - va_list ap; - - formatlen = strlen(msgformat) + 64; /* +64 for prefix and \0 */ - format = alloca(formatlen); - - STRLOG_MAKE_MSGID(msgformat, msgid); - (void) snprintf(format, formatlen, - "fmd: [ID %u FACILITY_AND_PRIORITY] %s", msgid, msgformat); + char *buf; + size_t buflen; - /* - * Figure out the length of the message then allocate a buffer - * of adequate size. - */ - va_start(ap, msgformat); - if ((len = vsnprintf(&c, 1, format, ap)) >= 0 && - (buf = fmd_hdl_alloc(hdl, len + 1, FMD_SLEEP)) != NULL) - (void) vsnprintf(buf, len + 1, format, ap); - va_end(ap); + const char *format = "fmd: [ID %u FACILITY_AND_PRIORITY] %s"; + STRLOG_MAKE_MSGID(format, msgid); - if (buf == NULL) - return; + buflen = snprintf(NULL, 0, format, msgid, msg); + buf = alloca(buflen + 1); + (void) snprintf(buf, buflen + 1, format, msgid, msg); ctl.buf = (void *)&syslog_ctl; ctl.len = sizeof (syslog_ctl); dat.buf = buf; - logmsglen = strlen(buf) + 1; + dat.len = buflen + 1; /* - * The underlying log driver won't accept (ERANGE) messages - * longer than LOG_MAXPS bytes. The long message will be truncated - * and appended with a pointer to the full message. + * The underlying log driver won't accept messages longer than + * LOG_MAXPS bytes. Therefore, messages which exceed this limit will + * be truncated and appended with a pointer to the full message. */ - if (logmsglen <= LOG_MAXPS) { - dat.len = logmsglen; - } else { - plen = strlen(syslog_pointer) + 1; - buf[LOG_MAXPS - plen] = '\0'; - /* - * If possible, the pointer is appended after a newline - */ - if ((p = strrchr(buf, '\n')) == NULL) - p = &buf[LOG_MAXPS - plen]; + if (dat.len > LOG_MAXPS) { + char *syslog_pointer, *p; + size_t plen; - (void) strcpy(p, syslog_pointer); - dat.len = strlen(buf) + 1; + if ((syslog_pointer = fmd_msg_gettext_id(syslog_msghdl, NULL, + SYSLOG_POINTER)) == NULL) { + /* + * This shouldn't happen, but if it does we'll just + * truncate the message. + */ + buf[LOG_MAXPS - 1] = '\0'; + dat.len = LOG_MAXPS; + } else { + plen = strlen(syslog_pointer) + 1; + buf[LOG_MAXPS - plen] = '\0'; + /* + * If possible, the pointer is appended after a newline + */ + if ((p = strrchr(buf, '\n')) == NULL) + p = &buf[LOG_MAXPS - plen]; + + (void) strcpy(p, syslog_pointer); + free(syslog_pointer); + dat.len = strlen(buf) + 1; + } } - if (syslog_file && putmsg(syslog_logfd, &ctl, &dat, 0) != 0) { fmd_hdl_debug(hdl, "putmsg failed: %s\n", strerror(errno)); syslog_stats.log_err.fmds_value.ui64++; } dat.buf = strchr(buf, ']'); - dat.len = (size_t)(logmsglen - (dat.buf - buf)); + dat.len -= (size_t)(dat.buf - buf); dat.buf[0] = '\r'; /* overwrite ']' with carriage return */ dat.buf[1] = '\n'; /* overwrite ' ' with newline */ @@ -218,36 +173,15 @@ fmd_hdl_debug(hdl, "write failed: %s\n", strerror(errno)); syslog_stats.msg_err.fmds_value.ui64++; } - - fmd_hdl_free(hdl, buf, len + 1); } /*ARGSUSED*/ static void syslog_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) { - char *uuid, *code, *dict, *url, *urlcode, *template, *p; - char *src_name, *src_vers, *platform, *chassis, *server; - char *typ, *sev, *fmt, *trfmt, *rsp, *imp, *act, *locdir; - char desc[1024], date[64]; + uint8_t version; boolean_t domsg; - - nvlist_t *fmri, *auth; - uint8_t version; - struct tm tm, *tmp; - int64_t *tv; - time_t sec; - uint_t tn = 0; - char *olang = NULL; - int locale_c = 0; - size_t len; - - /* - * don't log updated and isolated events (for now) - */ - if (strcmp(class, FM_LIST_ISOLATED_CLASS) == 0 || - strcmp(class, FM_LIST_UPDATED_CLASS) == 0) - return; + char *msg; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_SUSPECT_VERSION) { @@ -263,166 +197,15 @@ return; /* event is not to be messaged */ } - /* - * Extract the DE element, which is an FMRI for the diagnosis engine - * that made this event, and validate its meta-data before continuing. - */ - if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &fmri) != 0 || - nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &p) != 0 || - strcmp(p, FM_FMRI_SCHEME_FMD) != 0 || - nvlist_lookup_uint8(fmri, FM_VERSION, &version) != 0 || - version > FM_FMD_SCHEME_VERSION || - nvlist_lookup_nvlist(fmri, FM_FMRI_AUTHORITY, &auth) != 0 || - nvlist_lookup_uint8(auth, FM_VERSION, &version) != 0 || - version > FM_FMRI_AUTH_VERSION) { - syslog_stats.bad_fmri.fmds_value.ui64++; - return; /* invalid de fmri */ - } - /* - * Extract the relevant identifying elements of the FMRI and authority. - * Note: for now, we ignore FM_FMRI_AUTH_DOMAIN (only for SPs). - */ - (void) nvlist_lookup_string(fmri, FM_FMRI_FMD_NAME, &src_name); - (void) nvlist_lookup_string(fmri, FM_FMRI_FMD_VERSION, &src_vers); - (void) nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &platform); - (void) nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server); - - if (nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &chassis) != 0) - chassis = "-"; /* chassis serial number may not be present */ - - /* - * Extract the uuid and diagcode dictionary from the event code. The - * dictionary name is the text preceding the first "-" in the code. - */ - (void) nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid); - (void) nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code); - - if ((p = strchr(code, '-')) == NULL || p == code) { - fmd_hdl_debug(hdl, "invalid diagnosis code: %s\n", code); + if ((msg = fmd_msg_gettext_nv(syslog_msghdl, NULL, nvl)) == NULL) { + fmd_hdl_debug(hdl, "failed to format message"); syslog_stats.bad_code.fmds_value.ui64++; - return; /* invalid diagnosis code */ + return; /* libfmd_msg error */ } - dict = alloca((size_t)(p - code) + 1); - (void) strncpy(dict, code, (size_t)(p - code)); - dict[(size_t)(p - code)] = '\0'; - - /* - * Alloca a hunk of memory and use it to create the msgid strings - * <code>.type, <code>.severity, <code>.description, and so forth. - * These form the msgids we will use to look up the localized text. - * Since we've allocated things to be of the right size, we know - * than snprintf() can't overflow: INT_MAX is used shut lint up and - * avoid code to needlessly recompute the remaining buffer space. - */ - typ = alloca(6 * (strlen(code) + 16)); - sev = typ + snprintf(typ, INT_MAX, "%s.type", code) + 1; - fmt = sev + snprintf(sev, INT_MAX, "%s.severity", code) + 1; - rsp = fmt + snprintf(fmt, INT_MAX, "%s.description", code) + 1; - imp = rsp + snprintf(rsp, INT_MAX, "%s.response", code) + 1; - act = imp + snprintf(imp, INT_MAX, "%s.impact", code) + 1; - (void) snprintf(act, INT_MAX, "%s.action", code); - - fmd_msg_lock(); - - if (syslog_locdir != NULL) - locdir = bindtextdomain(dict, syslog_locdir); - - if ((trfmt = dgettext(dict, fmt)) == fmt) { - /* - * We didn't find a translation in the dictionary for the - * current language. The string we passed to gettext is merely - * an index - it isn't sufficient, on its own, to be used as the - * message. Fall back to C and try again. - */ - olang = setlocale(LC_MESSAGES, NULL); - if (olang) { - p = alloca(strlen(olang) + 1); - olang = strcpy(p, olang); - } - locale_c = 1; - (void) setlocale(LC_MESSAGES, "C"); - trfmt = dgettext(dict, fmt); - } - - if ((url = dgettext(dict, SYSLOG_URL)) == SYSLOG_URL) - url = syslog_url; - - /* - * If the URL ends with a slash, that indicates the code should be - * appended to it. After formatting the URL, reformat the DESC - * text using the URL as an snprintf argument. - */ - len = strlen(url); - if (url[len - 1] == '/') { - urlcode = alloca(len + strlen(code) + 1); - (void) snprintf(urlcode, INT_MAX, "%s%s", url, code); - } else { - urlcode = url; - } - /* LINTED - variable format specifier to snprintf() */ - (void) snprintf(desc, sizeof (desc), trfmt, urlcode); - - /* - * Extract the diagnosis time and format it using the locale's default. - * strftime() will use GMT or local time based on our "gmt" setting. - */ - 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), "%C", tmp); - else { - syslog_stats.bad_time.fmds_value.ui64++; - (void) strcpy(date, "-"); - } - - /* - * Create and log the final string by filling in the template with the - * strings we've created and the strings from the message dictionary. - * If a template is provided for this dictionary, use it, otherwise - * fall back to the default template. - */ - if ((template = dgettext(dict, SYSLOG_TEMPLATE)) == SYSLOG_TEMPLATE) - template = dgettext(SYSLOG_DOMAIN, SYSLOG_TEMPLATE); - /* - * Do samely for the message pointer. If the message is too long - * to be handled by the underlying log drvier, the message will be - * truncated and the pointer will be added to point user to the - * full message. - */ - if ((syslog_pointer = dgettext(dict, SYSLOG_POINTER)) == SYSLOG_POINTER) - syslog_pointer = dgettext(SYSLOG_DOMAIN, SYSLOG_POINTER); - - syslog_ctl.pri &= LOG_FACMASK; - if (strcmp(class, FM_LIST_RESOLVED_CLASS) == 0 || - strcmp(class, FM_LIST_REPAIRED_CLASS) == 0) - syslog_ctl.pri |= LOG_NOTICE; - else - syslog_ctl.pri |= LOG_ERR; - syslog_emit(hdl, template, code, dgettext(dict, typ), - dgettext(dict, sev), date, platform, chassis, server, src_name, - src_vers, uuid, desc, dgettext(dict, rsp), dgettext(dict, imp), - dgettext(dict, act)); - - /* - * Switch back to our original language if we had to fall back to C. - */ - if (olang != NULL) - (void) setlocale(LC_MESSAGES, olang); - - if (syslog_locdir != NULL) - (void) bindtextdomain(dict, locdir); - - fmd_msg_unlock(); - - if (locale_c) { - fmd_hdl_debug(hdl, - trfmt == fmt ? - "dgettext(%s, %s) in %s and C failed\n" : - "dgettext(%s, %s) in %s failed; C used\n", - dict, fmt, olang ? olang : "<null>"); - } + syslog_emit(hdl, msg); + free(msg); } static const fmd_prop_t fmd_props[] = { @@ -451,7 +234,7 @@ _fmd_init(fmd_hdl_t *hdl) { const struct facility *fp; - char *facname, *tz, *rootdir, *locdir, *locale, *p; + char *facname, *tz, *rootdir, *urlbase; if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) return; /* invalid data in configuration file */ @@ -466,31 +249,6 @@ fmd_hdl_abort(hdl, "syslog-msgs failed to open /dev/sysmsg"); /* - * All FMA event dictionaries use msgfmt(1) message objects to produce - * messages, even for the C locale. We therefore want to use dgettext - * for all message lookups, but its defined behavior in the C locale is - * to return the input string. Since our input strings are event codes - * and not format strings, this doesn't help us. We resolve this nit - * by setting NLSPATH to a non-existent file: the presence of NLSPATH - * is defined to force dgettext(3C) to do a full lookup even for C. - */ - if (getenv("NLSPATH") == NULL && putenv(fmd_hdl_strdup(hdl, - "NLSPATH=/usr/lib/fm/fmd/fmd.cat", FMD_SLEEP)) != 0) - fmd_hdl_abort(hdl, "syslog-msgs failed to set NLSPATH"); - - fmd_msg_lock(); - (void) setlocale(LC_MESSAGES, ""); - locale = setlocale(LC_MESSAGES, NULL); - if (locale) { - p = alloca(strlen(locale) + 1); - locale = strcpy(p, locale); - } else { - locale = "<null>"; - } - fmd_msg_unlock(); - fmd_hdl_debug(hdl, "locale=%s\n", locale); - - /* * If the "gmt" property is set to true, force our EVENT-TIME to be * reported in GMT time; otherwise we use localtime. tzset() affects * the results of subsequent calls to strftime(3C) above. @@ -526,46 +284,29 @@ */ syslog_file = fmd_prop_get_int32(hdl, "syslogd"); syslog_cons = fmd_prop_get_int32(hdl, "console"); - syslog_url = fmd_prop_get_string(hdl, "url"); syslog_msgall = fmd_prop_get_int32(hdl, "message_all"); - /* - * If fmd's rootdir property is set to a non-default root, then we are - * going to need to rebind the text domains we use for dgettext() as - * we go. Look up the default l10n messages directory and make - * syslog_locdir be this path with fmd.rootdir prepended to it. - */ rootdir = fmd_prop_get_string(hdl, "fmd.rootdir"); + syslog_msghdl = fmd_msg_init(rootdir, FMD_MSG_VERSION); + fmd_prop_free_string(hdl, rootdir); - if (*rootdir != '\0' && strcmp(rootdir, "/") != 0) { - fmd_msg_lock(); - locdir = bindtextdomain(SYSLOG_DOMAIN, NULL); - fmd_msg_unlock(); - if (locdir != NULL) { - size_t len = strlen(rootdir) + strlen(locdir) + 1; - syslog_locdir = fmd_hdl_alloc(hdl, len, FMD_SLEEP); - (void) snprintf(syslog_locdir, len, "%s%s", rootdir, - locdir); - fmd_hdl_debug(hdl, - "binding textdomain to %s for syslog\n", - syslog_locdir); - } - } + if (syslog_msghdl == NULL) + fmd_hdl_abort(hdl, "failed to initialize libfmd_msg"); - fmd_prop_free_string(hdl, rootdir); + urlbase = fmd_prop_get_string(hdl, "url"); + (void) fmd_msg_url_set(syslog_msghdl, urlbase); + fmd_prop_free_string(hdl, urlbase); + fmd_hdl_subscribe(hdl, FM_LIST_SUSPECT_CLASS); - fmd_hdl_subscribe(hdl, FM_LIST_UPDATED_CLASS); - fmd_hdl_subscribe(hdl, FM_LIST_ISOLATED_CLASS); fmd_hdl_subscribe(hdl, FM_LIST_REPAIRED_CLASS); fmd_hdl_subscribe(hdl, FM_LIST_RESOLVED_CLASS); } +/*ARGSUSED*/ void _fmd_fini(fmd_hdl_t *hdl) { - fmd_hdl_strfree(hdl, syslog_locdir); - fmd_prop_free_string(hdl, syslog_url); - + fmd_msg_fini(syslog_msghdl); (void) close(syslog_logfd); (void) close(syslog_msgfd); }
--- a/usr/src/lib/fm/libfmd_msg/Makefile Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/lib/fm/libfmd_msg/Makefile Wed Apr 29 08:32:53 2009 -0700 @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# include ../../Makefile.lib include ../Makefile.lib @@ -39,10 +37,11 @@ clobber := TARGET = clobber install := TARGET = install lint := TARGET = lint +test := TARGET = test .KEEP_STATE: -all clean clobber lint: $(SUBDIRS) +all clean clobber lint test: $(SUBDIRS) install: install_h .WAIT $(SUBDIRS)
--- a/usr/src/lib/fm/libfmd_msg/Makefile.com Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/lib/fm/libfmd_msg/Makefile.com Wed Apr 29 08:32:53 2009 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -40,11 +40,13 @@ CPPFLAGS += -I../common -I. CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS) CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS) -LDLIBS += -lc +LDLIBS += -lnvpair -lc LINTFLAGS = -msux LINTFLAGS64 = -msux -m64 +CLOBBERFILES += fmd_msg_test fmd_msg_test.core fmd_msg_test.out + $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) $(LINTLIB) := LINTFLAGS = -nsvx $(LINTLIB) := LINTFLAGS64 = -nsvx -m64 @@ -57,3 +59,38 @@ include ../../../Makefile.targ include ../../Makefile.targ + +LDLIBS_$(MACH) = -L$(ROOT)/usr/lib/fm -R$(ROOT)/usr/lib/fm +LDLIBS_$(MACH64) = -L$(ROOT)/usr/lib/fm/$(MACH64) -R$(ROOT)/usr/lib/fm/$(MACH64) + +# +# To ease the development and maintenance of libfmd_msg, a test suite is built +# directly into the library. The test program fmd_msg_test.c includes a set of +# tests for all the code paths in the library. The test program executes the +# calls, and then forks into the background and dumps core. After the test +# runs, we diff the output against the master hand-verified output, and confirm +# no leaks or corruption exist. To run the entire suite, type "make test" and +# inspect the results (the make target will fail on an error). +# +# The cmp skips the first 900 bytes of $(SRCDIR)/fmd_msg_test.out to get us +# passed the CDDL header and copyright in that file. +# +test: install fmd_msg_test + @echo; echo "Running `pwd | sed 's/.*\///'` fmd_msg test suite ... \c" + @coreadm -p core $$$$ + @UMEM_DEBUG=default,verbose UMEM_LOGGING=fail,contents LANG=C \ + LC_ALL=C ./fmd_msg_test | grep -v EVENT-TIME > fmd_msg_test.out + @chmod 0444 core; mv -f core fmd_msg_test.core + @echo; echo "Checking test output ... \c" + @cmp -s $(SRCDIR)/fmd_msg_test.out fmd_msg_test.out 900 0 && echo pass \ + || ( echo FAIL && diff $(SRCDIR)/fmd_msg_test.out fmd_msg_test.out ) + @echo; echo Checking for memory leaks: + @echo ::findleaks | mdb fmd_msg_test.core + @echo; echo Checking for latent corruption: + @echo ::umem_verify | mdb fmd_msg_test.core | grep -v clean + @echo + +fmd_msg_test: $(SRCDIR)/fmd_msg_test.c + $(LINT.c) $(SRCDIR)/fmd_msg_test.c + $(LINK.c) -o fmd_msg_test $(SRCDIR)/fmd_msg_test.c \ + $(LDLIBS_$(TARGETMACH)) -lfmd_msg -lnvpair -lumem
--- a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c Wed Apr 29 08:32:53 2009 -0700 @@ -18,43 +18,1446 @@ * * CDDL HEADER END */ + /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * FMD Message Library + * + * This library supports a simple set of routines for use in converting FMA + * events and message codes to localized human-readable message strings. + * + * 1. Library API + * + * The APIs are as follows: + * + * fmd_msg_init - set up the library and return a handle + * fmd_msg_fini - destroy the handle from fmd_msg_init + * + * fmd_msg_locale_set - set the default locale (initially based on environ(5)) + * fmd_msg_locale_get - get the default locale + * + * fmd_msg_url_set - set the default URL for knowledge articles + * fmd_msg_url_get - get the default URL for knowledge articles + * + * fmd_msg_gettext_nv - format the entire message for the given event + * fmd_msg_gettext_id - format the entire message for the given event code + * + * fmd_msg_getitem_nv - format a single message item for the given event + * fmd_msg_getitem_id - format a single message item for the given event code + * + * Upon success, fmd_msg_gettext_* and fmd_msg_getitem_* return newly-allocated + * localized strings in multi-byte format. The caller must call free() on the + * resulting buffer to deallocate the string after making use of it. Upon + * failure, these functions return NULL and set errno as follows: + * + * ENOMEM - Memory allocation failure while formatting message + * ENOENT - No message was found for the specified message identifier + * EINVAL - Invalid argument (e.g. bad event code, illegal fmd_msg_item_t) + * EILSEQ - Illegal multi-byte sequence detected in message + * + * 2. Variable Expansion + * + * The human-readable messages are stored in msgfmt(1) message object files in + * the corresponding locale directories. The values for the message items are + * permitted to contain variable expansions, currently defined as follows: + * + * %% - literal % character + * %s - knowledge article URL (e.g. http://sun.com/msg/<MSG-ID>) + * %< x > - value x from the current event, using the expression syntax below: + * + * foo.bar => print nvlist_t member "bar" contained within nvlist_t "foo" + * foo[123] => print array element 123 of nvlist_t member "foo" + * foo[123].bar => print member "bar" of nvlist_t element 123 in array "foo" + * + * For example, the msgstr value for FMD-8000-2K might be defined as: + * + * msgid "FMD-8000-2K.action" + * msgstr "Use fmdump -v -u %<uuid> to locate the module. Use fmadm \ + * reset %<fault-list[0].asru.mod-name> to reset the module." + * + * 3. Locking + * + * In order to format a human-readable message, libfmd_msg must get and set + * the process locale and potentially alter text domain bindings. At present, + * these facilities in libc are not fully MT-safe. As such, a library-wide + * lock is provided: fmd_msg_lock() and fmd_msg_unlock(). These locking calls + * are made internally as part of the top-level library entry points, but they + * can also be used by applications that themselves call setlocale() and wish + * to appropriately synchronize with other threads that are calling libfmd_msg. + */ -#include <stdio.h> + +#include <sys/fm/protocol.h> + +#include <libintl.h> +#include <locale.h> +#include <wchar.h> + +#include <alloca.h> +#include <assert.h> +#include <pthread.h> +#include <strings.h> +#include <stdarg.h> #include <stdlib.h> -#include <synch.h> +#include <stdio.h> +#include <errno.h> +#include <sys/sysmacros.h> + +#include <fmd_msg.h> + +#define FMD_MSGBUF_SZ 256 + +struct fmd_msg_hdl { + int fmh_version; /* libfmd_msg client abi version number */ + char *fmh_urlbase; /* base url for all knowledge articles */ + char *fmh_binding; /* base directory for bindtextdomain() */ + char *fmh_locale; /* default program locale from environment */ + const char *fmh_template; /* FMD_MSG_TEMPLATE value for fmh_locale */ +}; -static rwlock_t fmd_msg_rwlock; +typedef struct fmd_msg_buf { + wchar_t *fmb_data; /* wide-character data buffer */ + size_t fmb_size; /* size of fmb_data in wchar_t units */ + size_t fmb_used; /* used portion of fmb_data in wchar_t units */ + int fmb_error; /* error if any has occurred */ +} fmd_msg_buf_t; + +static const char *const fmd_msg_items[] = { + "type", /* key for FMD_MSG_ITEM_TYPE */ + "severity", /* key for FMD_MSG_ITEM_SEVERITY */ + "description", /* key for FMD_MSG_ITEM_DESC */ + "response", /* key for FMD_MSG_ITEM_RESPONSE */ + "impact", /* key for FMD_MSG_ITEM_IMPACT */ + "action", /* key for FMD_MSG_ITEM_ACTION */ + "url", /* key for FMD_MSG_ITEM_URL */ +}; -#pragma init(fmd_msg_init) -static void -fmd_msg_init(void) +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://sun.com/msg/"; +static const char FMD_MSG_NLSPATH[] = "NLSPATH=/usr/lib/fm/fmd/fmd.cat"; +static const char FMD_MSG_MISSING[] = "-"; + +/* + * An enumeration of token types. The following are valid tokens that can be + * embedded into the message content: + * + * T_INT - integer tokens (for array indices) + * T_IDENT - nvpair identifiers + * T_DOT - "." + * T_LBRAC - "[" + * T_RBRAC - "]" + * + * A NULL character (T_EOF) is used to terminate messages. + * Invalid tokens are assigned the type T_ERR. + */ +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 *); + +struct _rwlock; + +/*ARGSUSED*/ +static int +fmd_msg_lock_held(fmd_msg_hdl_t *h) { - (void) rwlock_init(&fmd_msg_rwlock, USYNC_THREAD, NULL); -} - -#pragma fini(fmd_msg_fini) -static void -fmd_msg_fini(void) -{ - (void) rwlock_destroy(&fmd_msg_rwlock); + extern int _rw_write_held(struct _rwlock *); + return (_rw_write_held((struct _rwlock *)&fmd_msg_rwlock)); } void fmd_msg_lock(void) { - if (rw_wrlock(&fmd_msg_rwlock) != 0) + if (pthread_rwlock_wrlock(&fmd_msg_rwlock) != 0) abort(); } void fmd_msg_unlock(void) { - if (rw_unlock(&fmd_msg_rwlock) != 0) + 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)); + + /* + * Initialize the program's locale from the environment if it hasn't + * already been initialized, and then retrieve the default setting. + */ + (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 a non-default root directory is specified, then look up the base + * directory for our default catalog, and set fmh_binding as the same + * directory prefixed with the new root directory. This simply turns + * usr/lib/locale into <rootdir>/usr/lib/locale, but handles all of the + * environ(5) settings that can change the default messages binding. + */ + 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"; /* substitute default */ + + 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); + } + + /* + * All FMA event dictionaries use msgfmt(1) message objects to produce + * messages, even for the C locale. We therefore want to use dgettext + * for all message lookups, but its defined behavior in the C locale is + * to return the input string. Since our input strings are event codes + * and not format strings, this doesn't help us. We resolve this nit + * by setting NLSPATH to a non-existent file: the presence of NLSPATH + * is defined to force dgettext(3C) to do a full lookup even for C. + */ + if (getenv("NLSPATH") == NULL && + ((s = strdup(FMD_MSG_NLSPATH)) == NULL || putenv((char *)s) != 0)) + return (fmd_msg_init_err(h, errno)); + + /* + * Cache the message template for the current locale. This is the + * snprintf(3C) format string for the final human-readable message. + */ + h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE); + + return (h); +} + +void +fmd_msg_fini(fmd_msg_hdl_t *h) +{ + if (h == NULL) + return; /* simplify caller code */ + + 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); +} + +/* + * Buffer utility function to write a wide-character string into the buffer, + * appending it at the end, and growing the buffer as needed as we go. Any + * allocation errors are stored in fmb_error and deferred until later. + */ +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; +} + +/* + * Buffer utility function to printf a multi-byte string, convert to wide- + * character form, and then write the result into an fmd_msg_buf_t. + */ +/*PRINTFLIKE2*/ +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); + } +} + +/*PRINTFLIKE1*/ +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); +} + +/* + * Print the specified string, escaping any unprintable character sequences + * using the ISO C character escape sequences. + */ +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); + } + } +} + +/* + * Print the value of the specified nvpair into the supplied buffer. + * + * For nvpairs that are arrays types, passing -1 as the idx param indicates + * that we want to print all of the elements in the array. + * + * Returns 0 on success, 1 otherwise. + */ +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, " "); /* array item delimiter */ + + 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); +} + +/* + * Writes the value of the specified nvpair to the supplied buffer. + * + * Returns 0 on success, 1 otherwise. + */ +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); +} + +/* + * Consume a token from the specified string, fill in the specified token + * struct, and return the new string position from which to continue parsing. + */ +static char * +fmd_msg_nv_parse_token(char *s, fmd_msg_nv_token_t *tp) +{ + char *p = s, *q, c = *s; + + /* + * Skip whitespace and then look for an integer token first. We can't + * use isspace() or isdigit() because we're in setlocale() context now. + */ + 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); + } + + /* + * Look for a name-value pair identifier, which we define to be the + * regular expression [a-zA-Z_][a-zA-Z0-9_-]* (NOTE: Ideally "-" would + * not be allowed here and we would require ISO C identifiers, but many + * FMA event members use hyphens.) This code specifically cannot use + * the isspace(), isalnum() etc. macros because we are currently in the + * context of an earlier call to setlocale() that may have installed a + * non-C locale, but this code needs to always operate on C characters. + */ + 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)); +} + +/* + * Parse an array expression for referencing an element of the specified + * nvpair_t, which is expected to be of an array type. If it's an array of + * intrinsics, print the specified value. If it's an array of nvlist_t's, + * call fmd_msg_nv_parse_nvlist() recursively to continue parsing. + */ +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)); + + /* + * An array of nvlist is different from other array types in that it + * permits us to continue parsing instead of printing a terminal node. + */ + 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)); +} + +/* + * Parse an expression rooted at an nvpair_t. If we see EOF, print the entire + * nvpair. If we see LBRAC, parse an array expression. If we see DOT, call + * fmd_msg_nv_parse_nvname() recursively to dereference an embedded member. + */ +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)); +} + +/* + * Parse an expression for a name-value pair name (IDENT). If we find a match + * continue parsing with the corresponding nvpair_t. + */ +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)); +} + +/* + * Parse an expression rooted at an nvlist: if we see EOF, print nothing. + * If we see DOT, continue parsing to retrieve a name-value pair name. + */ +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)); +} + +/* + * This function is the main engine for formatting an event message item, such + * as the Description field. It loads the item text from a message object, + * expands any variables defined in the item text, and then returns a newly- + * allocated multi-byte string with the localized message text, or NULL with + * errno set if an error occurred. + */ +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 <dict>.mo defines an item with the key <FMD_MSG_URLKEY> then it + * is used as the URL; otherwise the default from our handle is used. + * Once we have the multi-byte URL, convert it to wide-character form. + */ + if ((url = dgettext(dict, FMD_MSG_URLKEY)) == FMD_MSG_URLKEY) + url = h->fmh_urlbase; + + /* + * If the item is FMD_MSG_ITEM_URL, then its value is directly computed + * as the URL base concatenated with the code. Otherwise the item text + * is derived by looking up the key <code>.<istr> in the dict object. + * Once we're done, convert the 'txt' multi-byte to wide-character. + */ + 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); + } + + /* + * Now expand any escape sequences in the string, storing the final + * text in 'buf' in wide-character format, and then convert it back + * to multi-byte for return. We expand the following sequences: + * + * %% - literal % character + * %s - base URL for knowledge articles + * %<x> - expression x in the current event, if any + * + * If an invalid sequence is present, it is elided so we can safely + * reserve any future characters for other types of expansions. + */ + 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; + + /* + * The expression in %< > must be an ASCII string: as + * such allocate its length in bytes plus an extra + * MB_CUR_MAX for slop if a multi-byte character is in + * there, plus another byte for \0. Since we move a + * byte at a time, any multi-byte chars will just be + * silently overwritten and fail to parse, which is ok. + */ + 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); +} + +/* + * This function is the main engine for formatting an entire event message. + * It retrieves the master format string for an event, formats the individual + * items, and then produces the final string composing all of the items. The + * result is a newly-allocated multi-byte string of the localized message + * text, or NULL with errno set if an error occurred. + */ +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, *chassis; + + 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 <dict>.mo defines an item with the key <FMD_MSG_TEMPLATE> then it + * is used as the format; otherwise the default from FMD.mo is used. + */ + 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), "%C", tmp); + else + (void) strlcpy(date, FMD_MSG_MISSING, sizeof (date)); + + /* + * Extract the relevant identifying elements of the FMRI and authority. + * Note: for now, we ignore FM_FMRI_AUTH_DOMAIN (only for SPs). + */ + 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_CHASSIS, &chassis) != 0) + chassis = (char *)FMD_MSG_MISSING; + + /* + * Format the message once to get its length, allocate a buffer, and + * then format the message again into the buffer to return it. + */ + len = snprintf(NULL, 0, format, code, + items[FMD_MSG_ITEM_TYPE], items[FMD_MSG_ITEM_SEVERITY], + date, platform, chassis, 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, chassis, 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); +} + +/* + * Common code for fmd_msg_getitem_nv() and fmd_msg_getitem_id(): this function + * handles locking, changing locales and domains, and restoring i18n state. + */ +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; /* simplify later tests */ + + dict = alloca((size_t)(p - code) + 1); + (void) strncpy(dict, code, (size_t)(p - code)); + dict[(size_t)(p - code)] = '\0'; + + fmd_msg_lock(); + + /* + * If a non-default text domain binding was requested, save the old + * binding perform the re-bind now that fmd_msg_lock() is held. + */ + 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); + } + + /* + * Compute the lookup code for FMD_MSG_ITEM_TYPE: we'll use this to + * determine if the dictionary contains any data for this code at all. + */ + 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]); + + /* + * Save the current locale string, and if we've been asked to fetch + * the text for a different locale, switch locales now under the lock. + */ + p = setlocale(LC_ALL, NULL); + old_c = alloca(strlen(p) + 1); + (void) strcpy(old_c, p); + + if (locale != NULL) + (void) setlocale(LC_ALL, locale); + + /* + * Prefetch the first item: if this isn't found, and we're in a non- + * default locale, attempt to fall back to the C locale for this code. + */ + if (dgettext(dict, key) == key && + (locale != NULL || strcmp(h->fmh_locale, "C") != 0)) { + (void) setlocale(LC_ALL, "C"); + locale = "C"; /* restore locale */ + } + + 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)); +} + +/* + * Common code for fmd_msg_gettext_nv() and fmd_msg_gettext_id(): this function + * handles locking, changing locales and domains, and restoring i18n state. + */ +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; /* simplify later tests */ + + dict = alloca((size_t)(p - code) + 1); + (void) strncpy(dict, code, (size_t)(p - code)); + dict[(size_t)(p - code)] = '\0'; + + fmd_msg_lock(); + + /* + * If a non-default text domain binding was requested, save the old + * binding perform the re-bind now that fmd_msg_lock() is held. + */ + 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); + } + + /* + * Compute the lookup code for FMD_MSG_ITEM_TYPE: we'll use this to + * determine if the dictionary contains any data for this code at all. + */ + 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]); + + /* + * Save the current locale string, and if we've been asked to fetch + * the text for a different locale, switch locales now under the lock. + */ + p = setlocale(LC_ALL, NULL); + old_c = alloca(strlen(p) + 1); + (void) strcpy(old_c, p); + + if (locale != NULL) + (void) setlocale(LC_ALL, locale); + + /* + * Prefetch the first item: if this isn't found, and we're in a non- + * default locale, attempt to fall back to the C locale for this code. + */ + if (dgettext(dict, key) == key && + (locale != NULL || strcmp(h->fmh_locale, "C") != 0)) { + (void) setlocale(LC_ALL, "C"); + locale = "C"; /* restore locale */ + } + + 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)); +}
--- a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h Wed Apr 29 08:32:53 2009 -0700 @@ -18,15 +18,17 @@ * * CDDL HEADER END */ + /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FMD_MSG_H #define _FMD_MSG_H -#pragma ident "%Z%%M% %I% %E% SMI" +#include <sys/types.h> +#include <sys/nvpair.h> #ifdef __cplusplus extern "C" { @@ -42,9 +44,42 @@ * purpose until they are publicly documented for use outside of Sun. */ +#define FMD_MSG_VERSION 1 /* libary ABI interface version */ + +typedef struct fmd_msg_hdl fmd_msg_hdl_t; + +typedef enum { + FMD_MSG_ITEM_TYPE, + FMD_MSG_ITEM_SEVERITY, + FMD_MSG_ITEM_DESC, + FMD_MSG_ITEM_RESPONSE, + FMD_MSG_ITEM_IMPACT, + FMD_MSG_ITEM_ACTION, + FMD_MSG_ITEM_URL, + FMD_MSG_ITEM_MAX +} fmd_msg_item_t; + extern void fmd_msg_lock(void); extern void fmd_msg_unlock(void); +fmd_msg_hdl_t *fmd_msg_init(const char *, int); +void fmd_msg_fini(fmd_msg_hdl_t *); + +extern int fmd_msg_locale_set(fmd_msg_hdl_t *, const char *); +extern const char *fmd_msg_locale_get(fmd_msg_hdl_t *); + +extern int fmd_msg_url_set(fmd_msg_hdl_t *, const char *); +extern const char *fmd_msg_url_get(fmd_msg_hdl_t *); + +extern char *fmd_msg_gettext_nv(fmd_msg_hdl_t *, const char *, nvlist_t *); +extern char *fmd_msg_gettext_id(fmd_msg_hdl_t *, const char *, const char *); + +extern char *fmd_msg_getitem_nv(fmd_msg_hdl_t *, + const char *, nvlist_t *, fmd_msg_item_t); + +extern char *fmd_msg_getitem_id(fmd_msg_hdl_t *, + const char *, const char *, fmd_msg_item_t); + #ifdef __cplusplus } #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg_test.c Wed Apr 29 08:32:53 2009 -0700 @@ -0,0 +1,279 @@ +/* + * 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 <sys/types.h> +#include <sys/wait.h> + +#include <sys/fm/protocol.h> +#include <fm/fmd_msg.h> + +#include <unistd.h> +#include <signal.h> +#include <strings.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#define TEST_ARR_SZ 2 + +int +main(int argc, char *argv[]) +{ + fmd_msg_hdl_t *h; + pid_t pid; + int i, err = 0; + char *s; + + nvlist_t *auth, *fmri, *list, *test_arr[TEST_ARR_SZ]; + const char *code = "TEST-8000-08"; + int64_t tod[] = { 0x9400000, 0 }; + + if (argc > 1) { + (void) fprintf(stderr, "Usage: %s\n", argv[0]); + return (2); + } + + /* + * Build up a valid list.suspect event for a fictional diagnosis + * using a diagnosis code from our test dictionary so we can format + * messages. + */ + if (nvlist_alloc(&auth, NV_UNIQUE_NAME, 0) != 0 || + nvlist_alloc(&fmri, NV_UNIQUE_NAME, 0) != 0 || + nvlist_alloc(&list, NV_UNIQUE_NAME, 0) != 0) { + (void) fprintf(stderr, "%s: nvlist_alloc failed\n", argv[0]); + return (1); + } + + err |= nvlist_add_uint8(auth, FM_VERSION, FM_FMRI_AUTH_VERSION); + err |= nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, "product"); + err |= nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, "chassis"); + err |= nvlist_add_string(auth, FM_FMRI_AUTH_DOMAIN, "domain"); + err |= nvlist_add_string(auth, FM_FMRI_AUTH_SERVER, "server"); + + if (err != 0) { + (void) fprintf(stderr, "%s: failed to build auth nvlist: %s\n", + argv[0], strerror(err)); + return (1); + } + + err |= nvlist_add_uint8(fmri, FM_VERSION, FM_FMD_SCHEME_VERSION); + err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_FMD); + err |= nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, auth); + err |= nvlist_add_string(fmri, FM_FMRI_FMD_NAME, "fmd_msg_test"); + err |= nvlist_add_string(fmri, FM_FMRI_FMD_VERSION, "1.0"); + + if (err != 0) { + (void) fprintf(stderr, "%s: failed to build fmri nvlist: %s\n", + argv[0], strerror(err)); + return (1); + } + + err |= nvlist_add_uint8(list, FM_VERSION, FM_SUSPECT_VERSION); + err |= nvlist_add_string(list, FM_CLASS, FM_LIST_SUSPECT_CLASS); + err |= nvlist_add_string(list, FM_SUSPECT_UUID, "12345678"); + err |= nvlist_add_string(list, FM_SUSPECT_DIAG_CODE, code); + err |= nvlist_add_int64_array(list, FM_SUSPECT_DIAG_TIME, tod, 2); + err |= nvlist_add_nvlist(list, FM_SUSPECT_DE, fmri); + err |= nvlist_add_uint32(list, FM_SUSPECT_FAULT_SZ, 0); + + /* + * Add a contrived nvlist array to our list.suspect so that we can + * exercise the expansion syntax for dereferencing nvlist array members + */ + for (i = 0; i < TEST_ARR_SZ; i++) { + if (nvlist_alloc(&test_arr[i], NV_UNIQUE_NAME, 0) != 0) { + (void) fprintf(stderr, "%s: failed to alloc nvlist " + "array: %s\n", argv[0], strerror(err)); + return (1); + } + err |= nvlist_add_uint8(test_arr[i], "index", i); + } + err |= nvlist_add_nvlist_array(list, "test_arr", test_arr, TEST_ARR_SZ); + + if (err != 0) { + (void) fprintf(stderr, "%s: failed to build list nvlist: %s\n", + argv[0], strerror(err)); + return (1); + } + + /* + * Now initialize the libfmd_msg library for testing, using the message + * catalogs found in the proto area of the current workspace. + */ + if ((h = fmd_msg_init(getenv("ROOT"), FMD_MSG_VERSION)) == NULL) { + (void) fprintf(stderr, "%s: fmd_msg_init failed: %s\n", + argv[0], strerror(errno)); + return (1); + } + + /* + * Test 0: Verify that both fmd_msg_getitem_id and fmd_msg_gettext_id + * return NULL and EINVAL for an illegal message code, and NULL + * and ENOENT for a valid but not defined message code. + */ + s = fmd_msg_getitem_id(h, NULL, "I_AM_NOT_VALID", 0); + if (s != NULL || errno != EINVAL) { + (void) fprintf(stderr, "%s: test0 FAIL: illegal code returned " + "s = %p, errno = %d\n", argv[0], (void *)s, errno); + return (1); + } + + s = fmd_msg_gettext_id(h, NULL, "I_AM_NOT_VALID"); + if (s != NULL || errno != EINVAL) { + (void) fprintf(stderr, "%s: test0 FAIL: illegal code returned " + "s = %p, errno = %d\n", argv[0], (void *)s, errno); + return (1); + } + + s = fmd_msg_getitem_id(h, NULL, "I_AM_NOT_HERE-0000-0000", 0); + if (s != NULL || errno != ENOENT) { + (void) fprintf(stderr, "%s: test0 FAIL: missing code returned " + "s = %p, errno = %d\n", argv[0], (void *)s, errno); + return (1); + } + + s = fmd_msg_gettext_id(h, NULL, "I_AM_NOT_HERE-0000-0000"); + if (s != NULL || errno != ENOENT) { + (void) fprintf(stderr, "%s: test0 FAIL: missing code returned " + "s = %p, errno = %d\n", argv[0], (void *)s, errno); + return (1); + } + + /* + * Test 1: Use fmd_msg_getitem_id to retrieve the item strings for + * a known message code without having any actual event handle. + */ + for (i = 0; i < FMD_MSG_ITEM_MAX; i++) { + if ((s = fmd_msg_getitem_id(h, NULL, code, i)) == NULL) { + (void) fprintf(stderr, "%s: fmd_msg_getitem_id failed " + "for %s, item %d: %s\n", + argv[0], code, i, strerror(errno)); + } + + (void) printf("code %s item %d = <<%s>>\n", code, i, s); + free(s); + } + + /* + * Test 2: Use fmd_msg_gettext_id to retrieve the complete message for + * a known message code without having any actual event handle. + */ + if ((s = fmd_msg_gettext_id(h, NULL, code)) == NULL) { + (void) fprintf(stderr, "%s: fmd_msg_gettext_id failed for %s: " + "%s\n", argv[0], code, strerror(errno)); + return (1); + } + + (void) printf("%s\n", s); + free(s); + + /* + * Test 3: Use fmd_msg_getitem_nv to retrieve the item strings for + * our list.suspect event handle. + */ + for (i = 0; i < FMD_MSG_ITEM_MAX; i++) { + if ((s = fmd_msg_getitem_nv(h, NULL, list, i)) == NULL) { + (void) fprintf(stderr, "%s: fmd_msg_getitem_nv failed " + "for %s, item %d: %s\n", + argv[0], code, i, strerror(errno)); + } + + (void) printf("code %s item %d = <<%s>>\n", code, i, s); + free(s); + } + + /* + * Test 4: Use fmd_msg_getitem_nv to retrieve the complete message for + * a known message code using our list.suspect event handle. + */ + if ((s = fmd_msg_gettext_nv(h, NULL, list)) == NULL) { + (void) fprintf(stderr, "%s: fmd_msg_gettext_nv failed for %s: " + "%s\n", argv[0], code, strerror(errno)); + return (1); + } + + (void) printf("%s\n", s); + free(s); + + /* + * Test 5: Use fmd_msg_getitem_nv to retrieve the complete message for + * a known message code using our list.suspect event handle, but this + * time set the URL to our own customized URL. Our contrived message + * has been designed to exercise the key aspects of the variable + * expansion syntax. + */ + if (fmd_msg_url_set(h, "http://foo.bar.com/") != 0) { + (void) fprintf(stderr, "%s: fmd_msg_url_set failed: %s\n", + argv[0], strerror(errno)); + } + + if ((s = fmd_msg_gettext_nv(h, NULL, list)) == NULL) { + (void) fprintf(stderr, "%s: fmd_msg_gettext_nv failed for %s: " + "%s\n", argv[0], code, strerror(errno)); + return (1); + } + + (void) printf("%s\n", s); + free(s); + + for (i = 0; i < TEST_ARR_SZ; i++) + nvlist_free(test_arr[i]); + nvlist_free(fmri); + nvlist_free(auth); + nvlist_free(list); + + fmd_msg_fini(h); /* free library state before dumping core */ + pid = fork(); /* fork into background to not bother make(1) */ + + switch (pid) { + case -1: + (void) fprintf(stderr, "FAIL (failed to fork)\n"); + return (1); + case 0: + abort(); + return (1); + } + + if (waitpid(pid, &err, 0) == -1) { + (void) fprintf(stderr, "FAIL (failed to wait for %d: %s)\n", + (int)pid, strerror(errno)); + return (1); + } + + if (WIFSIGNALED(err) == 0 || WTERMSIG(err) != SIGABRT) { + (void) fprintf(stderr, "FAIL (child did not SIGABRT)\n"); + return (1); + } + + if (!WCOREDUMP(err)) { + (void) fprintf(stderr, "FAIL (no core generated)\n"); + return (1); + } + + (void) fprintf(stderr, "done\n"); + return (0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg_test.out Wed Apr 29 08:32:53 2009 -0700 @@ -0,0 +1,106 @@ +# +# 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. +# +code TEST-8000-08 item 0 = <<Defect>> +code TEST-8000-08 item 1 = <<Minor>> +code TEST-8000-08 item 2 = <<This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08>> +code TEST-8000-08 item 3 = <<This entry tests the percent character escape sequence: %>> +code TEST-8000-08 item 4 = <<This entry tests variable expansion for event payload members: uuid = %<uuid>, de scheme = %<de.scheme>>> +code TEST-8000-08 item 5 = <<Variable expansion for arrays: index = %<test_arr[1].index>>> +code TEST-8000-08 item 6 = <<http://sun.com/msg/TEST-8000-08>> +SUNW-MSG-ID: TEST-8000-08, TYPE: Defect, VER: 1, SEVERITY: Minor +PLATFORM: -, CSN: -, HOSTNAME: - +SOURCE: -, REV: - +EVENT-ID: - +DESC: This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08 +AUTO-RESPONSE: This entry tests the percent character escape sequence: % +IMPACT: This entry tests variable expansion for event payload members: uuid = %<uuid>, de scheme = %<de.scheme> +REC-ACTION: Variable expansion for arrays: index = %<test_arr[1].index> + +code TEST-8000-08 item 0 = <<Defect>> +code TEST-8000-08 item 1 = <<Minor>> +code TEST-8000-08 item 2 = <<This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08>> +code TEST-8000-08 item 3 = <<This entry tests the percent character escape sequence: %>> +code TEST-8000-08 item 4 = <<This entry tests variable expansion for event payload members: uuid = 12345678, de scheme = fmd>> +code TEST-8000-08 item 5 = <<Variable expansion for arrays: index = 1>> +code TEST-8000-08 item 6 = <<http://sun.com/msg/TEST-8000-08>> +SUNW-MSG-ID: TEST-8000-08, TYPE: Defect, VER: 1, SEVERITY: Minor +PLATFORM: product, CSN: chassis, HOSTNAME: server +SOURCE: fmd_msg_test, REV: 1.0 +EVENT-ID: 12345678 +DESC: This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08 +AUTO-RESPONSE: This entry tests the percent character escape sequence: % +IMPACT: This entry tests variable expansion for event payload members: uuid = 12345678, de scheme = fmd +REC-ACTION: Variable expansion for arrays: index = 1 + +SUNW-MSG-ID: TEST-8000-08, TYPE: Defect, VER: 1, SEVERITY: Minor +PLATFORM: product, CSN: chassis, HOSTNAME: server +SOURCE: fmd_msg_test, REV: 1.0 +EVENT-ID: 12345678 +DESC: This entry tests URL variable expansion - url = http://foo.bar.com/TEST-8000-08 +AUTO-RESPONSE: This entry tests the percent character escape sequence: % +IMPACT: This entry tests variable expansion for event payload members: uuid = 12345678, de scheme = fmd +REC-ACTION: Variable expansion for arrays: index = 1 + +code TEST-8000-08 item 0 = <<Defect>> +code TEST-8000-08 item 1 = <<Minor>> +code TEST-8000-08 item 2 = <<This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08>> +code TEST-8000-08 item 3 = <<This entry tests the percent character escape sequence: %>> +code TEST-8000-08 item 4 = <<This entry tests variable expansion for event payload members: uuid = %<uuid>, de scheme = %<de.scheme>>> +code TEST-8000-08 item 5 = <<Variable expansion for arrays: index = %<test_arr[1].index>>> +code TEST-8000-08 item 6 = <<http://sun.com/msg/TEST-8000-08>> +SUNW-MSG-ID: TEST-8000-08, TYPE: Defect, VER: 1, SEVERITY: Minor +PLATFORM: -, CSN: -, HOSTNAME: - +SOURCE: -, REV: - +EVENT-ID: - +DESC: This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08 +AUTO-RESPONSE: This entry tests the percent character escape sequence: % +IMPACT: This entry tests variable expansion for event payload members: uuid = %<uuid>, de scheme = %<de.scheme> +REC-ACTION: Variable expansion for arrays: index = %<test_arr[1].index> + +code TEST-8000-08 item 0 = <<Defect>> +code TEST-8000-08 item 1 = <<Minor>> +code TEST-8000-08 item 2 = <<This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08>> +code TEST-8000-08 item 3 = <<This entry tests the percent character escape sequence: %>> +code TEST-8000-08 item 4 = <<This entry tests variable expansion for event payload members: uuid = 12345678, de scheme = fmd>> +code TEST-8000-08 item 5 = <<Variable expansion for arrays: index = 1>> +code TEST-8000-08 item 6 = <<http://sun.com/msg/TEST-8000-08>> +SUNW-MSG-ID: TEST-8000-08, TYPE: Defect, VER: 1, SEVERITY: Minor +PLATFORM: product, CSN: chassis, HOSTNAME: server +SOURCE: fmd_msg_test, REV: 1.0 +EVENT-ID: 12345678 +DESC: This entry tests URL variable expansion - url = http://sun.com/msg/TEST-8000-08 +AUTO-RESPONSE: This entry tests the percent character escape sequence: % +IMPACT: This entry tests variable expansion for event payload members: uuid = 12345678, de scheme = fmd +REC-ACTION: Variable expansion for arrays: index = 1 + +SUNW-MSG-ID: TEST-8000-08, TYPE: Defect, VER: 1, SEVERITY: Minor +PLATFORM: product, CSN: chassis, HOSTNAME: server +SOURCE: fmd_msg_test, REV: 1.0 +EVENT-ID: 12345678 +DESC: This entry tests URL variable expansion - url = http://foo.bar.com/TEST-8000-08 +AUTO-RESPONSE: This entry tests the percent character escape sequence: % +IMPACT: This entry tests variable expansion for event payload members: uuid = 12345678, de scheme = fmd +REC-ACTION: Variable expansion for arrays: index = 1 +
--- a/usr/src/lib/fm/libfmd_msg/common/mapfile-vers Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/lib/fm/libfmd_msg/common/mapfile-vers Wed Apr 29 08:32:53 2009 -0700 @@ -22,8 +22,6 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # - -# # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. @@ -39,8 +37,18 @@ SUNWprivate { global: + fmd_msg_fini; + fmd_msg_getitem_id; + fmd_msg_getitem_nv; + fmd_msg_gettext_id; + fmd_msg_gettext_nv; + fmd_msg_init; + fmd_msg_locale_get; + fmd_msg_locale_set; fmd_msg_lock; fmd_msg_unlock; + fmd_msg_url_get; + fmd_msg_url_set; local: *; };
--- a/usr/src/pkgdefs/etc/exception_list_i386 Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/pkgdefs/etc/exception_list_i386 Wed Apr 29 08:32:53 2009 -0700 @@ -1239,3 +1239,10 @@ # sbd ioctl hdr usr/include/sys/stmf_sbd_ioctl.h i386 + +# +# portable object file and dictionary used by libfmd_msg test +# +usr/lib/fm/dict/TEST.dict i386 +usr/lib/locale/C/LC_MESSAGES/TEST.mo i386 +usr/lib/locale/C/LC_MESSAGES/TEST.po i386
--- a/usr/src/pkgdefs/etc/exception_list_sparc Wed Apr 29 03:44:12 2009 -0700 +++ b/usr/src/pkgdefs/etc/exception_list_sparc Wed Apr 29 08:32:53 2009 -0700 @@ -1338,3 +1338,10 @@ # sbd ioctl hdr usr/include/sys/stmf_sbd_ioctl.h sparc + +# +# portable object file and dictionary used by libfmd_msg test +# +usr/lib/fm/dict/TEST.dict sparc +usr/lib/locale/C/LC_MESSAGES/TEST.mo sparc +usr/lib/locale/C/LC_MESSAGES/TEST.po sparc