4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
29 * This library supports a simple set of routines for use in converting FMA
30 * events and message codes to localized human-readable message strings.
34 * The APIs are as follows:
36 * fmd_msg_init - set up the library and return a handle
37 * fmd_msg_fini - destroy the handle from fmd_msg_init
39 * fmd_msg_locale_set - set the default locale (initially based on environ(5))
40 * fmd_msg_locale_get - get the default locale
42 * fmd_msg_url_set - set the default URL for knowledge articles
43 * fmd_msg_url_get - get the default URL for knowledge articles
45 * fmd_msg_gettext_nv - format the entire message for the given event
46 * fmd_msg_gettext_id - format the entire message for the given event code
47 * fmd_msg_gettext_key - format the entire message for the given dict for the
48 * given explicit message key
50 * fmd_msg_getitem_nv - format a single message item for the given event
51 * fmd_msg_getitem_id - format a single message item for the given event code
53 * Upon success, fmd_msg_gettext_* and fmd_msg_getitem_* return newly-allocated
54 * localized strings in multi-byte format. The caller must call free() on the
55 * resulting buffer to deallocate the string after making use of it. Upon
56 * failure, these functions return NULL and set errno as follows:
58 * ENOMEM - Memory allocation failure while formatting message
59 * ENOENT - No message was found for the specified message identifier
60 * EINVAL - Invalid argument (e.g. bad event code, illegal fmd_msg_item_t)
61 * EILSEQ - Illegal multi-byte sequence detected in message
63 * 2. Variable Expansion
65 * The human-readable messages are stored in msgfmt(1) message object files in
66 * the corresponding locale directories. The values for the message items are
67 * permitted to contain variable expansions, currently defined as follows:
69 * %% - literal % character
70 * %s - knowledge article URL (e.g. http://illumos.org/msg/<MSG-ID>)
71 * %< x > - value x from the current event, using the expression syntax below:
73 * foo.bar => print nvlist_t member "bar" contained within nvlist_t "foo"
74 * foo[123] => print array element 123 of nvlist_t member "foo"
75 * foo[123].bar => print member "bar" of nvlist_t element 123 in array "foo"
77 * For example, the msgstr value for FMD-8000-2K might be defined as:
79 * msgid "FMD-8000-2K.action"
80 * msgstr "Use fmdump -v -u %<uuid> to locate the module. Use fmadm \
81 * reset %<fault-list[0].asru.mod-name> to reset the module."
85 * In order to format a human-readable message, libfmd_msg must get and set
86 * the process locale and potentially alter text domain bindings. At present,
87 * these facilities in libc are not fully MT-safe. As such, a library-wide
88 * lock is provided: fmd_msg_lock() and fmd_msg_unlock(). These locking calls
89 * are made internally as part of the top-level library entry points, but they
90 * can also be used by applications that themselves call setlocale() and wish
91 * to appropriately synchronize with other threads that are calling libfmd_msg.
95 #include <sys/fm/protocol.h>
112 #include <sys/sysmacros.h>
116 #define FMD_MSGBUF_SZ 256
119 int fmh_version
; /* libfmd_msg client abi version number */
120 char *fmh_urlbase
; /* base url for all knowledge articles */
121 char *fmh_binding
; /* base directory for bindtextdomain() */
122 char *fmh_locale
; /* default program locale from environment */
123 const char *fmh_template
; /* FMD_MSG_TEMPLATE value for fmh_locale */
126 typedef struct fmd_msg_buf
{
127 wchar_t *fmb_data
; /* wide-character data buffer */
128 size_t fmb_size
; /* size of fmb_data in wchar_t units */
129 size_t fmb_used
; /* used portion of fmb_data in wchar_t units */
130 int fmb_error
; /* error if any has occurred */
133 static const char *const fmd_msg_items
[] = {
134 "type", /* key for FMD_MSG_ITEM_TYPE */
135 "severity", /* key for FMD_MSG_ITEM_SEVERITY */
136 "description", /* key for FMD_MSG_ITEM_DESC */
137 "response", /* key for FMD_MSG_ITEM_RESPONSE */
138 "impact", /* key for FMD_MSG_ITEM_IMPACT */
139 "action", /* key for FMD_MSG_ITEM_ACTION */
140 "url", /* key for FMD_MSG_ITEM_URL */
143 static pthread_rwlock_t fmd_msg_rwlock
= PTHREAD_RWLOCK_INITIALIZER
;
145 static const char FMD_MSG_DOMAIN
[] = "FMD";
146 static const char FMD_MSG_TEMPLATE
[] = "syslog-msgs-message-template";
147 static const char FMD_MSG_URLKEY
[] = "syslog-url";
148 static const char FMD_MSG_URLBASE
[] = "http://illumos.org/msg/";
149 static const char FMD_MSG_NLSPATH
[] = "NLSPATH=/usr/lib/fm/fmd/fmd.cat";
150 static const char FMD_MSG_MISSING
[] = "-";
153 * An enumeration of token types. The following are valid tokens that can be
154 * embedded into the message content:
156 * T_INT - integer tokens (for array indices)
157 * T_IDENT - nvpair identifiers
162 * A NULL character (T_EOF) is used to terminate messages.
163 * Invalid tokens are assigned the type T_ERR.
173 } fmd_msg_nv_tkind_t
;
175 typedef struct fmd_msg_nv_token
{
176 fmd_msg_nv_tkind_t t_kind
;
181 } fmd_msg_nv_token_t
;
183 static const struct fmd_msg_nv_type
{
184 data_type_t nvt_type
;
185 data_type_t nvt_base
;
189 } fmd_msg_nv_types
[] = {
190 { DATA_TYPE_INT8
, DATA_TYPE_INT8
,
191 sizeof (int8_t), nvpair_value_int8
, NULL
},
192 { DATA_TYPE_INT16
, DATA_TYPE_INT16
,
193 sizeof (int16_t), nvpair_value_int16
, NULL
},
194 { DATA_TYPE_INT32
, DATA_TYPE_INT32
,
195 sizeof (int32_t), nvpair_value_int32
, NULL
},
196 { DATA_TYPE_INT64
, DATA_TYPE_INT64
,
197 sizeof (int64_t), nvpair_value_int64
, NULL
},
198 { DATA_TYPE_UINT8
, DATA_TYPE_UINT8
,
199 sizeof (uint8_t), nvpair_value_uint8
, NULL
},
200 { DATA_TYPE_UINT16
, DATA_TYPE_UINT16
,
201 sizeof (uint16_t), nvpair_value_uint16
, NULL
},
202 { DATA_TYPE_UINT32
, DATA_TYPE_UINT32
,
203 sizeof (uint32_t), nvpair_value_uint32
, NULL
},
204 { DATA_TYPE_UINT64
, DATA_TYPE_UINT64
,
205 sizeof (uint64_t), nvpair_value_uint64
, NULL
},
206 { DATA_TYPE_BYTE
, DATA_TYPE_BYTE
,
207 sizeof (uchar_t
), nvpair_value_byte
, NULL
},
208 { DATA_TYPE_BOOLEAN
, DATA_TYPE_BOOLEAN
,
210 { DATA_TYPE_BOOLEAN_VALUE
, DATA_TYPE_BOOLEAN_VALUE
,
211 sizeof (boolean_t
), nvpair_value_boolean_value
, NULL
},
212 { DATA_TYPE_HRTIME
, DATA_TYPE_HRTIME
,
213 sizeof (hrtime_t
), nvpair_value_hrtime
, NULL
},
214 { DATA_TYPE_STRING
, DATA_TYPE_STRING
,
215 sizeof (char *), nvpair_value_string
, NULL
},
216 { DATA_TYPE_NVLIST
, DATA_TYPE_NVLIST
,
217 sizeof (nvlist_t
*), nvpair_value_nvlist
, NULL
},
218 { DATA_TYPE_INT8_ARRAY
, DATA_TYPE_INT8
,
219 sizeof (int8_t), NULL
, nvpair_value_int8_array
},
220 { DATA_TYPE_INT16_ARRAY
, DATA_TYPE_INT16
,
221 sizeof (int16_t), NULL
, nvpair_value_int16_array
},
222 { DATA_TYPE_INT32_ARRAY
, DATA_TYPE_INT32
,
223 sizeof (int32_t), NULL
, nvpair_value_int32_array
},
224 { DATA_TYPE_INT64_ARRAY
, DATA_TYPE_INT64
,
225 sizeof (int64_t), NULL
, nvpair_value_int64_array
},
226 { DATA_TYPE_UINT8_ARRAY
, DATA_TYPE_UINT8
,
227 sizeof (uint8_t), NULL
, nvpair_value_uint8_array
},
228 { DATA_TYPE_UINT16_ARRAY
, DATA_TYPE_UINT16
,
229 sizeof (uint16_t), NULL
, nvpair_value_uint16_array
},
230 { DATA_TYPE_UINT32_ARRAY
, DATA_TYPE_UINT32
,
231 sizeof (uint32_t), NULL
, nvpair_value_uint32_array
},
232 { DATA_TYPE_UINT64_ARRAY
, DATA_TYPE_UINT64
,
233 sizeof (uint64_t), NULL
, nvpair_value_uint64_array
},
234 { DATA_TYPE_BYTE_ARRAY
, DATA_TYPE_BYTE
,
235 sizeof (uchar_t
), NULL
, nvpair_value_byte_array
},
236 { DATA_TYPE_BOOLEAN_ARRAY
, DATA_TYPE_BOOLEAN_VALUE
,
237 sizeof (boolean_t
), NULL
, nvpair_value_boolean_array
},
238 { DATA_TYPE_STRING_ARRAY
, DATA_TYPE_STRING
,
239 sizeof (char *), NULL
, nvpair_value_string_array
},
240 { DATA_TYPE_NVLIST_ARRAY
, DATA_TYPE_NVLIST
,
241 sizeof (nvlist_t
*), NULL
, nvpair_value_nvlist_array
},
242 { DATA_TYPE_UNKNOWN
, DATA_TYPE_UNKNOWN
, 0, NULL
, NULL
}
245 static int fmd_msg_nv_parse_nvpair(fmd_msg_buf_t
*, nvpair_t
*, char *);
246 static int fmd_msg_nv_parse_nvname(fmd_msg_buf_t
*, nvlist_t
*, char *);
247 static int fmd_msg_nv_parse_nvlist(fmd_msg_buf_t
*, nvlist_t
*, char *);
251 fmd_msg_lock_held(fmd_msg_hdl_t
*h
)
253 return (RW_WRITE_HELD(&fmd_msg_rwlock
));
259 if (pthread_rwlock_wrlock(&fmd_msg_rwlock
) != 0)
266 if (pthread_rwlock_unlock(&fmd_msg_rwlock
) != 0)
270 static fmd_msg_hdl_t
*
271 fmd_msg_init_err(fmd_msg_hdl_t
*h
, int err
)
279 fmd_msg_init(const char *root
, int version
)
281 fmd_msg_hdl_t
*h
= NULL
;
285 if (version
!= FMD_MSG_VERSION
)
286 return (fmd_msg_init_err(h
, EINVAL
));
288 if ((h
= malloc(sizeof (fmd_msg_hdl_t
))) == NULL
)
289 return (fmd_msg_init_err(h
, ENOMEM
));
291 bzero(h
, sizeof (fmd_msg_hdl_t
));
292 h
->fmh_version
= version
;
294 if ((h
->fmh_urlbase
= strdup(FMD_MSG_URLBASE
)) == NULL
)
295 return (fmd_msg_init_err(h
, ENOMEM
));
298 * Initialize the program's locale from the environment if it hasn't
299 * already been initialized, and then retrieve the default setting.
301 (void) setlocale(LC_ALL
, "");
302 s
= setlocale(LC_ALL
, NULL
);
303 h
->fmh_locale
= strdup(s
? s
: "C");
305 if (h
->fmh_locale
== NULL
)
306 return (fmd_msg_init_err(h
, ENOMEM
));
309 * If a non-default root directory is specified, then look up the base
310 * directory for our default catalog, and set fmh_binding as the same
311 * directory prefixed with the new root directory. This simply turns
312 * usr/lib/locale into <rootdir>/usr/lib/locale, but handles all of the
313 * environ(5) settings that can change the default messages binding.
315 if (root
!= NULL
&& root
[0] != '\0' && strcmp(root
, "/") != 0) {
317 return (fmd_msg_init_err(h
, EINVAL
));
319 if ((s
= bindtextdomain(FMD_MSG_DOMAIN
, NULL
)) == NULL
)
320 s
= "/usr/lib/locale"; /* substitute default */
322 len
= strlen(root
) + strlen(s
) + 1;
324 if ((h
->fmh_binding
= malloc(len
)) == NULL
)
325 return (fmd_msg_init_err(h
, ENOMEM
));
327 (void) snprintf(h
->fmh_binding
, len
, "%s%s", root
, s
);
331 * All FMA event dictionaries use msgfmt(1) message objects to produce
332 * messages, even for the C locale. We therefore want to use dgettext
333 * for all message lookups, but its defined behavior in the C locale is
334 * to return the input string. Since our input strings are event codes
335 * and not format strings, this doesn't help us. We resolve this nit
336 * by setting NLSPATH to a non-existent file: the presence of NLSPATH
337 * is defined to force dgettext(3C) to do a full lookup even for C.
339 if (getenv("NLSPATH") == NULL
&&
340 ((s
= strdup(FMD_MSG_NLSPATH
)) == NULL
|| putenv((char *)s
) != 0))
341 return (fmd_msg_init_err(h
, errno
));
344 * Cache the message template for the current locale. This is the
345 * snprintf(3C) format string for the final human-readable message.
346 * If the lookup fails for the current locale, fall back to the C locale
347 * and try again. Then restore the original locale.
349 if ((h
->fmh_template
= dgettext(FMD_MSG_DOMAIN
, FMD_MSG_TEMPLATE
))
350 == FMD_MSG_TEMPLATE
&& strcmp(h
->fmh_locale
, "C") != 0) {
351 (void) setlocale(LC_ALL
, "C");
352 h
->fmh_template
= dgettext(FMD_MSG_DOMAIN
, FMD_MSG_TEMPLATE
);
353 (void) setlocale(LC_ALL
, h
->fmh_locale
);
360 fmd_msg_fini(fmd_msg_hdl_t
*h
)
363 return; /* simplify caller code */
365 free(h
->fmh_binding
);
366 free(h
->fmh_urlbase
);
372 fmd_msg_locale_set(fmd_msg_hdl_t
*h
, const char *locale
)
376 if (locale
== NULL
) {
381 if ((l
= strdup(locale
)) == NULL
) {
388 if (setlocale(LC_ALL
, l
) == NULL
) {
395 h
->fmh_template
= dgettext(FMD_MSG_DOMAIN
, FMD_MSG_TEMPLATE
);
404 fmd_msg_locale_get(fmd_msg_hdl_t
*h
)
406 return (h
->fmh_locale
);
410 fmd_msg_url_set(fmd_msg_hdl_t
*h
, const char *url
)
419 if ((u
= strdup(url
)) == NULL
) {
426 free(h
->fmh_urlbase
);
434 fmd_msg_url_get(fmd_msg_hdl_t
*h
)
436 return (h
->fmh_urlbase
);
440 fmd_msg_mbstowcs(const char *s
)
442 size_t n
= strlen(s
) + 1;
443 wchar_t *w
= malloc(n
* sizeof (wchar_t));
450 if (mbstowcs(w
, s
, n
) == (size_t)-1) {
459 fmd_msg_buf_init(fmd_msg_buf_t
*b
)
461 bzero(b
, sizeof (fmd_msg_buf_t
));
462 b
->fmb_data
= malloc(sizeof (wchar_t) * FMD_MSGBUF_SZ
);
464 if (b
->fmb_data
== NULL
)
465 b
->fmb_error
= ENOMEM
;
467 b
->fmb_size
= FMD_MSGBUF_SZ
;
471 fmd_msg_buf_fini(fmd_msg_buf_t
*b
)
474 bzero(b
, sizeof (fmd_msg_buf_t
));
478 fmd_msg_buf_read(fmd_msg_buf_t
*b
)
482 if (b
->fmb_error
!= 0) {
483 errno
= b
->fmb_error
;
487 if ((s
= malloc(b
->fmb_used
* MB_CUR_MAX
)) == NULL
) {
492 if (wcstombs(s
, b
->fmb_data
, b
->fmb_used
) == (size_t)-1) {
501 * Buffer utility function to write a wide-character string into the buffer,
502 * appending it at the end, and growing the buffer as needed as we go. Any
503 * allocation errors are stored in fmb_error and deferred until later.
506 fmd_msg_buf_write(fmd_msg_buf_t
*b
, const wchar_t *w
, size_t n
)
508 if (b
->fmb_used
+ n
> b
->fmb_size
) {
509 size_t size
= MAX(b
->fmb_size
* 2, b
->fmb_used
+ n
);
510 wchar_t *data
= malloc(sizeof (wchar_t) * size
);
513 if (b
->fmb_error
== 0)
514 b
->fmb_error
= ENOMEM
;
518 bcopy(b
->fmb_data
, data
, b
->fmb_used
* sizeof (wchar_t));
525 bcopy(w
, &b
->fmb_data
[b
->fmb_used
], sizeof (wchar_t) * n
);
530 * Buffer utility function to printf a multi-byte string, convert to wide-
531 * character form, and then write the result into an fmd_msg_buf_t.
535 fmd_msg_buf_printf(fmd_msg_buf_t
*b
, const char *format
, ...)
542 va_start(ap
, format
);
543 len
= vsnprintf(NULL
, 0, format
, ap
);
544 buf
= alloca(len
+ 1);
545 (void) vsnprintf(buf
, len
+ 1, format
, ap
);
548 if ((w
= fmd_msg_mbstowcs(buf
)) == NULL
) {
549 if (b
->fmb_error
!= 0)
550 b
->fmb_error
= errno
;
552 fmd_msg_buf_write(b
, w
, wcslen(w
));
559 fmd_msg_nv_error(const char *format
, ...)
564 if (getenv("FMD_MSG_DEBUG") == NULL
)
567 (void) fprintf(stderr
, "libfmd_msg DEBUG: ");
568 va_start(ap
, format
);
569 (void) vfprintf(stderr
, format
, ap
);
572 if (strchr(format
, '\n') == NULL
)
573 (void) fprintf(stderr
, ": %s\n", strerror(err
));
578 static const struct fmd_msg_nv_type
*
579 fmd_msg_nv_type_lookup(data_type_t type
)
581 const struct fmd_msg_nv_type
*t
;
583 for (t
= fmd_msg_nv_types
; t
->nvt_type
!= DATA_TYPE_UNKNOWN
; t
++) {
584 if (t
->nvt_type
== type
)
592 * Print the specified string, escaping any unprintable character sequences
593 * using the ISO C character escape sequences.
596 fmd_msg_nv_print_string(fmd_msg_buf_t
*b
, const char *s
)
600 while ((c
= *s
++) != '\0') {
601 if (c
>= ' ' && c
<= '~' && c
!= '\'') {
602 fmd_msg_buf_printf(b
, "%c", c
);
608 fmd_msg_buf_printf(b
, "\\0");
611 fmd_msg_buf_printf(b
, "\\a");
614 fmd_msg_buf_printf(b
, "\\b");
617 fmd_msg_buf_printf(b
, "\\f");
620 fmd_msg_buf_printf(b
, "\\n");
623 fmd_msg_buf_printf(b
, "\\r");
626 fmd_msg_buf_printf(b
, "\\t");
629 fmd_msg_buf_printf(b
, "\\v");
632 fmd_msg_buf_printf(b
, "\\'");
635 fmd_msg_buf_printf(b
, "\\\"");
638 fmd_msg_buf_printf(b
, "\\\\");
641 fmd_msg_buf_printf(b
, "\\x%02x", (uchar_t
)c
);
647 * Print the value of the specified nvpair into the supplied buffer.
649 * For nvpairs that are arrays types, passing -1 as the idx param indicates
650 * that we want to print all of the elements in the array.
652 * Returns 0 on success, 1 otherwise.
655 fmd_msg_nv_print_items(fmd_msg_buf_t
*b
, nvpair_t
*nvp
,
656 data_type_t type
, void *p
, uint_t n
, uint_t idx
)
658 const struct fmd_msg_nv_type
*nvt
= fmd_msg_nv_type_lookup(type
);
663 return (fmd_msg_nv_error("index %u out-of-range for "
664 "array %s: valid range is [0 .. %u]\n",
665 idx
, nvpair_name(nvp
), n
? n
- 1 : 0));
667 p
= (uchar_t
*)p
+ nvt
->nvt_size
* idx
;
671 for (i
= 0; i
< n
; i
++, p
= (uchar_t
*)p
+ nvt
->nvt_size
) {
673 fmd_msg_buf_printf(b
, " "); /* array item delimiter */
677 fmd_msg_buf_printf(b
, "%d", *(int8_t *)p
);
680 case DATA_TYPE_INT16
:
681 fmd_msg_buf_printf(b
, "%d", *(int16_t *)p
);
684 case DATA_TYPE_INT32
:
685 fmd_msg_buf_printf(b
, "%d", *(int32_t *)p
);
688 case DATA_TYPE_INT64
:
689 fmd_msg_buf_printf(b
, "%lld", *(longlong_t
*)p
);
692 case DATA_TYPE_UINT8
:
693 fmd_msg_buf_printf(b
, "%u", *(uint8_t *)p
);
696 case DATA_TYPE_UINT16
:
697 fmd_msg_buf_printf(b
, "%u", *(uint16_t *)p
);
700 case DATA_TYPE_UINT32
:
701 fmd_msg_buf_printf(b
, "%u", *(uint32_t *)p
);
704 case DATA_TYPE_UINT64
:
705 fmd_msg_buf_printf(b
, "%llu", *(u_longlong_t
*)p
);
709 fmd_msg_buf_printf(b
, "0x%x", *(uchar_t
*)p
);
712 case DATA_TYPE_BOOLEAN_VALUE
:
713 fmd_msg_buf_printf(b
,
714 *(boolean_t
*)p
? "true" : "false");
717 case DATA_TYPE_HRTIME
:
718 fmd_msg_buf_printf(b
, "%lld", *(longlong_t
*)p
);
721 case DATA_TYPE_STRING
:
722 fmd_msg_nv_print_string(b
, *(char **)p
);
731 * Writes the value of the specified nvpair to the supplied buffer.
733 * Returns 0 on success, 1 otherwise.
736 fmd_msg_nv_print_nvpair(fmd_msg_buf_t
*b
, nvpair_t
*nvp
, uint_t idx
)
738 data_type_t type
= nvpair_type(nvp
);
739 const struct fmd_msg_nv_type
*nvt
= fmd_msg_nv_type_lookup(type
);
746 if (nvt
->nvt_type
== DATA_TYPE_BOOLEAN
) {
747 fmd_msg_buf_printf(b
, "true");
749 } else if (nvt
->nvt_array
!= NULL
) {
750 (void) nvt
->nvt_array(nvp
, &a
, &n
);
751 err
= fmd_msg_nv_print_items(b
, nvp
, nvt
->nvt_base
, a
, n
, idx
);
752 } else if (nvt
->nvt_value
!= NULL
) {
753 (void) nvt
->nvt_value(nvp
, &v
);
754 err
= fmd_msg_nv_print_items(b
, nvp
, nvt
->nvt_base
, &v
, 1, idx
);
756 err
= fmd_msg_nv_error("unknown data type %u", type
);
763 * Consume a token from the specified string, fill in the specified token
764 * struct, and return the new string position from which to continue parsing.
767 fmd_msg_nv_parse_token(char *s
, fmd_msg_nv_token_t
*tp
)
769 char *p
= s
, *q
, c
= *s
;
772 * Skip whitespace and then look for an integer token first. We can't
773 * use isspace() or isdigit() because we're in setlocale() context now.
775 while (c
== ' ' || c
== '\t' || c
== '\v' || c
== '\n' || c
== '\r')
778 if (c
>= '0' && c
<= '9') {
780 tp
->t_data
.tu_int
= strtoul(p
, &q
, 0);
782 if (errno
!= 0 || p
== q
) {
792 * Look for a name-value pair identifier, which we define to be the
793 * regular expression [a-zA-Z_][a-zA-Z0-9_-]* (NOTE: Ideally "-" would
794 * not be allowed here and we would require ISO C identifiers, but many
795 * FMA event members use hyphens.) This code specifically cannot use
796 * the isspace(), isalnum() etc. macros because we are currently in the
797 * context of an earlier call to setlocale() that may have installed a
798 * non-C locale, but this code needs to always operate on C characters.
800 if ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') || c
== '_') {
801 for (q
= p
+ 1; (c
= *q
) != '\0'; q
++) {
802 if ((c
< 'a' || c
> 'z') && (c
< 'A' || c
> 'Z') &&
803 (c
< '0' || c
> '9') && (c
!= '_' && c
!= '-'))
807 if (sizeof (tp
->t_data
.tu_str
) <= (size_t)(q
- p
)) {
812 bcopy(p
, tp
->t_data
.tu_str
, (size_t)(q
- p
));
813 tp
->t_data
.tu_str
[(size_t)(q
- p
)] = '\0';
814 tp
->t_kind
= T_IDENT
;
826 tp
->t_kind
= T_LBRAC
;
829 tp
->t_kind
= T_RBRAC
;
838 fmd_msg_nv_parse_error(const char *s
, fmd_msg_nv_token_t
*tp
)
840 if (tp
->t_kind
== T_ERR
)
841 return (fmd_msg_nv_error("illegal character at \"%s\"\n", s
));
843 return (fmd_msg_nv_error("syntax error near \"%s\"\n", s
));
847 * Parse an array expression for referencing an element of the specified
848 * nvpair_t, which is expected to be of an array type. If it's an array of
849 * intrinsics, print the specified value. If it's an array of nvlist_t's,
850 * call fmd_msg_nv_parse_nvlist() recursively to continue parsing.
853 fmd_msg_nv_parse_array(fmd_msg_buf_t
*b
, nvpair_t
*nvp
, char *s1
)
855 fmd_msg_nv_token_t t
;
860 if (fmd_msg_nv_type_lookup(nvpair_type(nvp
))->nvt_array
== NULL
) {
861 return (fmd_msg_nv_error("inappropriate use of operator [ ]: "
862 "element '%s' is not an array\n", nvpair_name(nvp
)));
865 s2
= fmd_msg_nv_parse_token(s1
, &t
);
868 if (t
.t_kind
!= T_INT
)
869 return (fmd_msg_nv_error("expected integer index after [\n"));
871 s2
= fmd_msg_nv_parse_token(s2
, &t
);
873 if (t
.t_kind
!= T_RBRAC
)
874 return (fmd_msg_nv_error("expected ] after [ %u\n", i
));
877 * An array of nvlist is different from other array types in that it
878 * permits us to continue parsing instead of printing a terminal node.
880 if (nvpair_type(nvp
) == DATA_TYPE_NVLIST_ARRAY
) {
881 (void) nvpair_value_nvlist_array(nvp
, &nva
, &n
);
884 return (fmd_msg_nv_error("index %u out-of-range for "
885 "array %s: valid range is [0 .. %u]\n",
886 i
, nvpair_name(nvp
), n
? n
- 1 : 0));
889 return (fmd_msg_nv_parse_nvlist(b
, nva
[i
], s2
));
892 (void) fmd_msg_nv_parse_token(s2
, &t
);
894 if (t
.t_kind
!= T_EOF
) {
895 return (fmd_msg_nv_error("expected end-of-string "
896 "in expression instead of \"%s\"\n", s2
));
899 return (fmd_msg_nv_print_nvpair(b
, nvp
, i
));
903 * Parse an expression rooted at an nvpair_t. If we see EOF, print the entire
904 * nvpair. If we see LBRAC, parse an array expression. If we see DOT, call
905 * fmd_msg_nv_parse_nvname() recursively to dereference an embedded member.
908 fmd_msg_nv_parse_nvpair(fmd_msg_buf_t
*b
, nvpair_t
*nvp
, char *s1
)
910 fmd_msg_nv_token_t t
;
914 s2
= fmd_msg_nv_parse_token(s1
, &t
);
916 if (t
.t_kind
== T_EOF
)
917 return (fmd_msg_nv_print_nvpair(b
, nvp
, -1));
919 if (t
.t_kind
== T_LBRAC
)
920 return (fmd_msg_nv_parse_array(b
, nvp
, s2
));
922 if (t
.t_kind
!= T_DOT
)
923 return (fmd_msg_nv_parse_error(s1
, &t
));
925 if (nvpair_type(nvp
) != DATA_TYPE_NVLIST
) {
926 return (fmd_msg_nv_error("inappropriate use of operator '.': "
927 "element '%s' is not of type nvlist\n", nvpair_name(nvp
)));
930 (void) nvpair_value_nvlist(nvp
, &nvl
);
931 return (fmd_msg_nv_parse_nvname(b
, nvl
, s2
));
935 * Parse an expression for a name-value pair name (IDENT). If we find a match
936 * continue parsing with the corresponding nvpair_t.
939 fmd_msg_nv_parse_nvname(fmd_msg_buf_t
*b
, nvlist_t
*nvl
, char *s1
)
941 nvpair_t
*nvp
= NULL
;
942 fmd_msg_nv_token_t t
;
945 s2
= fmd_msg_nv_parse_token(s1
, &t
);
947 if (t
.t_kind
!= T_IDENT
)
948 return (fmd_msg_nv_parse_error(s1
, &t
));
950 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
951 if (strcmp(nvpair_name(nvp
), t
.t_data
.tu_str
) == 0)
956 return (fmd_msg_nv_error("no such name-value pair "
957 "member: %s\n", t
.t_data
.tu_str
));
960 return (fmd_msg_nv_parse_nvpair(b
, nvp
, s2
));
964 * Parse an expression rooted at an nvlist: if we see EOF, print nothing.
965 * If we see DOT, continue parsing to retrieve a name-value pair name.
968 fmd_msg_nv_parse_nvlist(fmd_msg_buf_t
*b
, nvlist_t
*nvl
, char *s1
)
970 fmd_msg_nv_token_t t
;
973 s2
= fmd_msg_nv_parse_token(s1
, &t
);
975 if (t
.t_kind
== T_EOF
)
978 if (t
.t_kind
== T_DOT
)
979 return (fmd_msg_nv_parse_nvname(b
, nvl
, s2
));
981 return (fmd_msg_nv_parse_error(s1
, &t
));
985 * This function is the main engine for formatting an event message item, such
986 * as the Description field. It loads the item text from a message object,
987 * expands any variables defined in the item text, and then returns a newly-
988 * allocated multi-byte string with the localized message text, or NULL with
989 * errno set if an error occurred.
992 fmd_msg_getitem_locked(fmd_msg_hdl_t
*h
,
993 nvlist_t
*nvl
, const char *dict
, const char *code
, fmd_msg_item_t item
)
995 const char *istr
= fmd_msg_items
[item
];
996 size_t len
= strlen(code
) + 1 + strlen(istr
) + 1;
997 char *key
= alloca(len
);
1000 wchar_t *c
, *u
, *w
, *p
, *q
;
1002 const char *url
, *txt
;
1007 assert(fmd_msg_lock_held(h
));
1010 * If <dict>.mo defines an item with the key <FMD_MSG_URLKEY> then it
1011 * is used as the URL; otherwise the default from our handle is used.
1012 * Once we have the multi-byte URL, convert it to wide-character form.
1014 if ((url
= dgettext(dict
, FMD_MSG_URLKEY
)) == FMD_MSG_URLKEY
)
1015 url
= h
->fmh_urlbase
;
1018 * If the item is FMD_MSG_ITEM_URL, then its value is directly computed
1019 * as the URL base concatenated with the code. Otherwise the item text
1020 * is derived by looking up the key <code>.<istr> in the dict object.
1021 * Once we're done, convert the 'txt' multi-byte to wide-character.
1023 if (item
== FMD_MSG_ITEM_URL
) {
1024 len
= strlen(url
) + strlen(code
) + 1;
1026 (void) snprintf(key
, len
, "%s%s", url
, code
);
1029 len
= strlen(code
) + 1 + strlen(istr
) + 1;
1031 (void) snprintf(key
, len
, "%s.%s", code
, istr
);
1032 txt
= dgettext(dict
, key
);
1035 c
= fmd_msg_mbstowcs(code
);
1036 u
= fmd_msg_mbstowcs(url
);
1037 w
= fmd_msg_mbstowcs(txt
);
1039 if (c
== NULL
|| u
== NULL
|| w
== NULL
) {
1047 * Now expand any escape sequences in the string, storing the final
1048 * text in 'buf' in wide-character format, and then convert it back
1049 * to multi-byte for return. We expand the following sequences:
1051 * %% - literal % character
1052 * %s - base URL for knowledge articles
1053 * %<x> - expression x in the current event, if any
1055 * If an invalid sequence is present, it is elided so we can safely
1056 * reserve any future characters for other types of expansions.
1058 fmd_msg_buf_init(&buf
);
1060 for (q
= w
, p
= w
; (p
= wcschr(p
, L
'%')) != NULL
; q
= p
) {
1062 fmd_msg_buf_write(&buf
, q
, (size_t)(p
- q
));
1066 fmd_msg_buf_write(&buf
, p
, 1);
1071 fmd_msg_buf_write(&buf
, u
, wcslen(u
));
1072 fmd_msg_buf_write(&buf
, c
, wcslen(c
));
1079 p
= wcschr(p
+ 2, L
'>');
1085 * The expression in %< > must be an ASCII string: as
1086 * such allocate its length in bytes plus an extra
1087 * MB_CUR_MAX for slop if a multi-byte character is in
1088 * there, plus another byte for \0. Since we move a
1089 * byte at a time, any multi-byte chars will just be
1090 * silently overwritten and fail to parse, which is ok.
1092 elen
= (size_t)(p
- q
);
1093 expr
= malloc(elen
+ MB_CUR_MAX
+ 1);
1096 buf
.fmb_error
= ENOMEM
;
1100 for (i
= 0; i
< elen
; i
++)
1101 (void) wctomb(&expr
[i
], q
[i
]);
1106 (void) fmd_msg_nv_parse_nvname(&buf
, nvl
, expr
);
1108 fmd_msg_buf_printf(&buf
, "%%<%s>", expr
);
1123 fmd_msg_buf_write(&buf
, q
, wcslen(q
) + 1);
1129 s
= fmd_msg_buf_read(&buf
);
1130 fmd_msg_buf_fini(&buf
);
1136 * This is a private interface used by the notification daemons to parse tokens
1137 * in user-supplied message templates.
1140 fmd_msg_decode_tokens(nvlist_t
*nvl
, const char *msg
, const char *url
)
1143 wchar_t *h
, *u
, *w
, *p
, *q
;
1145 char *s
, *expr
, host
[MAXHOSTNAMELEN
+ 1];
1149 u
= fmd_msg_mbstowcs(url
);
1151 (void) gethostname(host
, MAXHOSTNAMELEN
+ 1);
1152 h
= fmd_msg_mbstowcs(host
);
1154 if ((w
= fmd_msg_mbstowcs(msg
)) == NULL
)
1158 * Now expand any escape sequences in the string, storing the final
1159 * text in 'buf' in wide-character format, and then convert it back
1160 * to multi-byte for return. We expand the following sequences:
1162 * %% - literal % character
1164 * %s - base URL for knowledge articles
1165 * %<x> - expression x in the current event, if any
1167 * If an invalid sequence is present, it is elided so we can safely
1168 * reserve any future characters for other types of expansions.
1170 fmd_msg_buf_init(&buf
);
1172 for (q
= w
, p
= w
; (p
= wcschr(p
, L
'%')) != NULL
; q
= p
) {
1174 fmd_msg_buf_write(&buf
, q
, (size_t)(p
- q
));
1178 fmd_msg_buf_write(&buf
, p
, 1);
1184 fmd_msg_buf_write(&buf
, h
, wcslen(h
));
1191 fmd_msg_buf_write(&buf
, u
, wcslen(u
));
1198 p
= wcschr(p
+ 2, L
'>');
1204 * The expression in %< > must be an ASCII string: as
1205 * such allocate its length in bytes plus an extra
1206 * MB_CUR_MAX for slop if a multi-byte character is in
1207 * there, plus another byte for \0. Since we move a
1208 * byte at a time, any multi-byte chars will just be
1209 * silently overwritten and fail to parse, which is ok.
1211 elen
= (size_t)(p
- q
);
1212 expr
= malloc(elen
+ MB_CUR_MAX
+ 1);
1215 buf
.fmb_error
= ENOMEM
;
1219 for (i
= 0; i
< elen
; i
++)
1220 (void) wctomb(&expr
[i
], q
[i
]);
1225 (void) fmd_msg_nv_parse_nvname(&buf
, nvl
, expr
);
1227 fmd_msg_buf_printf(&buf
, "%%<%s>", expr
);
1242 fmd_msg_buf_write(&buf
, q
, wcslen(q
) + 1);
1248 s
= fmd_msg_buf_read(&buf
);
1249 fmd_msg_buf_fini(&buf
);
1255 * This function is the main engine for formatting an entire event message.
1256 * It retrieves the master format string for an event, formats the individual
1257 * items, and then produces the final string composing all of the items. The
1258 * result is a newly-allocated multi-byte string of the localized message
1259 * text, or NULL with errno set if an error occurred.
1262 fmd_msg_gettext_locked(fmd_msg_hdl_t
*h
,
1263 nvlist_t
*nvl
, const char *dict
, const char *code
)
1265 char *items
[FMD_MSG_ITEM_MAX
];
1271 nvlist_t
*fmri
, *auth
;
1279 char *uuid
, *src_name
, *src_vers
;
1280 char *platform
, *server
, *csn
;
1282 assert(fmd_msg_lock_held(h
));
1283 bzero(items
, sizeof (items
));
1285 for (i
= 0; i
< FMD_MSG_ITEM_MAX
; i
++) {
1286 items
[i
] = fmd_msg_getitem_locked(h
, nvl
, dict
, code
, i
);
1287 if (items
[i
] == NULL
)
1292 * If <dict>.mo defines an item with the key <FMD_MSG_TEMPLATE> then it
1293 * is used as the format; otherwise the default from FMD.mo is used.
1295 if ((format
= dgettext(dict
, FMD_MSG_TEMPLATE
)) == FMD_MSG_TEMPLATE
)
1296 format
= h
->fmh_template
;
1298 if (nvlist_lookup_string(nvl
, FM_SUSPECT_UUID
, &uuid
) != 0)
1299 uuid
= (char *)FMD_MSG_MISSING
;
1301 if (nvlist_lookup_int64_array(nvl
, FM_SUSPECT_DIAG_TIME
,
1302 &tv
, &tn
) == 0 && tn
== 2 && (sec
= (time_t)tv
[0]) != (time_t)-1 &&
1303 (tmp
= localtime_r(&sec
, &tm
)) != NULL
)
1304 (void) strftime(date
, sizeof (date
), "%a %b %e %H:%M:%S %Z %Y",
1307 (void) strlcpy(date
, FMD_MSG_MISSING
, sizeof (date
));
1310 * Extract the relevant identifying elements of the FMRI and authority.
1311 * Note: for now, we ignore FM_FMRI_AUTH_DOMAIN (only for SPs).
1313 if (nvlist_lookup_nvlist(nvl
, FM_SUSPECT_DE
, &fmri
) != 0)
1316 if (nvlist_lookup_nvlist(fmri
, FM_FMRI_AUTHORITY
, &auth
) != 0)
1319 if (nvlist_lookup_string(fmri
, FM_FMRI_FMD_NAME
, &src_name
) != 0)
1320 src_name
= (char *)FMD_MSG_MISSING
;
1322 if (nvlist_lookup_string(fmri
, FM_FMRI_FMD_VERSION
, &src_vers
) != 0)
1323 src_vers
= (char *)FMD_MSG_MISSING
;
1325 if (nvlist_lookup_string(auth
, FM_FMRI_AUTH_PRODUCT
, &platform
) != 0)
1326 platform
= (char *)FMD_MSG_MISSING
;
1328 if (nvlist_lookup_string(auth
, FM_FMRI_AUTH_SERVER
, &server
) != 0)
1329 server
= (char *)FMD_MSG_MISSING
;
1331 if (nvlist_lookup_string(auth
, FM_FMRI_AUTH_PRODUCT_SN
, &csn
) != 0 &&
1332 nvlist_lookup_string(auth
, FM_FMRI_AUTH_CHASSIS
, &csn
) != 0)
1333 csn
= (char *)FMD_MSG_MISSING
;
1336 * Format the message once to get its length, allocate a buffer, and
1337 * then format the message again into the buffer to return it.
1339 len
= snprintf(NULL
, 0, format
, code
,
1340 items
[FMD_MSG_ITEM_TYPE
], items
[FMD_MSG_ITEM_SEVERITY
],
1341 date
, platform
, csn
, server
, src_name
, src_vers
, uuid
,
1342 items
[FMD_MSG_ITEM_DESC
], items
[FMD_MSG_ITEM_RESPONSE
],
1343 items
[FMD_MSG_ITEM_IMPACT
], items
[FMD_MSG_ITEM_ACTION
]);
1345 if ((buf
= malloc(len
+ 1)) == NULL
) {
1350 (void) snprintf(buf
, len
+ 1, format
, code
,
1351 items
[FMD_MSG_ITEM_TYPE
], items
[FMD_MSG_ITEM_SEVERITY
],
1352 date
, platform
, csn
, server
, src_name
, src_vers
, uuid
,
1353 items
[FMD_MSG_ITEM_DESC
], items
[FMD_MSG_ITEM_RESPONSE
],
1354 items
[FMD_MSG_ITEM_IMPACT
], items
[FMD_MSG_ITEM_ACTION
]);
1356 for (i
= 0; i
< FMD_MSG_ITEM_MAX
; i
++)
1363 * Common code for fmd_msg_getitem_nv() and fmd_msg_getitem_id(): this function
1364 * handles locking, changing locales and domains, and restoring i18n state.
1367 fmd_msg_getitem(fmd_msg_hdl_t
*h
,
1368 const char *locale
, nvlist_t
*nvl
, const char *code
, fmd_msg_item_t item
)
1370 char *old_b
, *old_c
;
1371 char *dict
, *key
, *p
, *s
;
1375 if ((p
= strchr(code
, '-')) == NULL
|| p
== code
) {
1380 if (locale
!= NULL
&& strcmp(h
->fmh_locale
, locale
) == 0)
1381 locale
= NULL
; /* simplify later tests */
1383 dict
= strndupa(code
, p
- code
);
1388 * If a non-default text domain binding was requested, save the old
1389 * binding perform the re-bind now that fmd_msg_lock() is held.
1391 if (h
->fmh_binding
!= NULL
) {
1392 p
= bindtextdomain(dict
, NULL
);
1394 (void) bindtextdomain(dict
, h
->fmh_binding
);
1398 * Compute the lookup code for FMD_MSG_ITEM_TYPE: we'll use this to
1399 * determine if the dictionary contains any data for this code at all.
1401 len
= strlen(code
) + 1 + strlen(fmd_msg_items
[FMD_MSG_ITEM_TYPE
]) + 1;
1404 (void) snprintf(key
, len
, "%s.%s",
1405 code
, fmd_msg_items
[FMD_MSG_ITEM_TYPE
]);
1408 * Save the current locale string, and if we've been asked to fetch
1409 * the text for a different locale, switch locales now under the lock.
1411 p
= setlocale(LC_ALL
, NULL
);
1415 (void) setlocale(LC_ALL
, locale
);
1418 * Prefetch the first item: if this isn't found, and we're in a non-
1419 * default locale, attempt to fall back to the C locale for this code.
1421 if (dgettext(dict
, key
) == key
&&
1422 (locale
!= NULL
|| strcmp(h
->fmh_locale
, "C") != 0)) {
1423 (void) setlocale(LC_ALL
, "C");
1424 locale
= "C"; /* restore locale */
1427 if (dgettext(dict
, key
) == key
) {
1431 s
= fmd_msg_getitem_locked(h
, nvl
, dict
, code
, item
);
1436 (void) setlocale(LC_ALL
, old_c
);
1438 if (h
->fmh_binding
!= NULL
)
1439 (void) bindtextdomain(dict
, old_b
);
1450 fmd_msg_getitem_nv(fmd_msg_hdl_t
*h
,
1451 const char *locale
, nvlist_t
*nvl
, fmd_msg_item_t item
)
1455 if (item
>= FMD_MSG_ITEM_MAX
) {
1460 if (nvlist_lookup_string(nvl
, FM_SUSPECT_DIAG_CODE
, &code
) != 0) {
1465 return (fmd_msg_getitem(h
, locale
, nvl
, code
, item
));
1469 fmd_msg_getitem_id(fmd_msg_hdl_t
*h
,
1470 const char *locale
, const char *code
, fmd_msg_item_t item
)
1472 if (item
>= FMD_MSG_ITEM_MAX
) {
1477 return (fmd_msg_getitem(h
, locale
, NULL
, code
, item
));
1481 fmd_msg_gettext_key(fmd_msg_hdl_t
*h
,
1482 const char *locale
, const char *dict
, const char *key
)
1484 char *old_b
, *old_c
, *p
, *s
;
1489 * If a non-default text domain binding was requested, save the old
1490 * binding perform the re-bind now that fmd_msg_lock() is held.
1492 if (h
->fmh_binding
!= NULL
) {
1493 p
= bindtextdomain(dict
, NULL
);
1494 old_b
= alloca(strlen(p
) + 1);
1495 (void) strcpy(old_b
, p
);
1496 (void) bindtextdomain(dict
, h
->fmh_binding
);
1500 * Save the current locale string, and if we've been asked to fetch
1501 * the text for a different locale, switch locales now under the lock.
1503 p
= setlocale(LC_ALL
, NULL
);
1504 old_c
= alloca(strlen(p
) + 1);
1505 (void) strcpy(old_c
, p
);
1508 (void) setlocale(LC_ALL
, locale
);
1511 * First attempt to fetch the string in the current locale. If this
1512 * fails and we're in a non-default locale, attempt to fall back to the
1513 * C locale and try again. If it still fails then we return NULL and
1516 if ((s
= dgettext(dict
, key
)) == key
&&
1517 (locale
!= NULL
|| strcmp(h
->fmh_locale
, "C") != 0)) {
1518 (void) setlocale(LC_ALL
, "C");
1519 locale
= "C"; /* restore locale */
1521 if ((s
= dgettext(dict
, key
)) == key
) {
1527 (void) setlocale(LC_ALL
, old_c
);
1529 if (h
->fmh_binding
!= NULL
)
1530 (void) bindtextdomain(dict
, old_b
);
1538 * Common code for fmd_msg_gettext_nv() and fmd_msg_gettext_id(): this function
1539 * handles locking, changing locales and domains, and restoring i18n state.
1542 fmd_msg_gettext(fmd_msg_hdl_t
*h
,
1543 const char *locale
, nvlist_t
*nvl
, const char *code
)
1545 char *old_b
, *old_c
;
1546 char *dict
, *key
, *p
, *s
;
1550 if ((p
= strchr(code
, '-')) == NULL
|| p
== code
) {
1555 if (locale
!= NULL
&& strcmp(h
->fmh_locale
, locale
) == 0)
1556 locale
= NULL
; /* simplify later tests */
1558 dict
= strndupa(code
, p
- code
);
1563 * If a non-default text domain binding was requested, save the old
1564 * binding perform the re-bind now that fmd_msg_lock() is held.
1566 if (h
->fmh_binding
!= NULL
) {
1567 p
= bindtextdomain(dict
, NULL
);
1569 (void) bindtextdomain(dict
, h
->fmh_binding
);
1573 * Compute the lookup code for FMD_MSG_ITEM_TYPE: we'll use this to
1574 * determine if the dictionary contains any data for this code at all.
1576 len
= strlen(code
) + 1 + strlen(fmd_msg_items
[FMD_MSG_ITEM_TYPE
]) + 1;
1579 (void) snprintf(key
, len
, "%s.%s",
1580 code
, fmd_msg_items
[FMD_MSG_ITEM_TYPE
]);
1583 * Save the current locale string, and if we've been asked to fetch
1584 * the text for a different locale, switch locales now under the lock.
1586 p
= setlocale(LC_ALL
, NULL
);
1590 (void) setlocale(LC_ALL
, locale
);
1593 * Prefetch the first item: if this isn't found, and we're in a non-
1594 * default locale, attempt to fall back to the C locale for this code.
1596 if (dgettext(dict
, key
) == key
&&
1597 (locale
!= NULL
|| strcmp(h
->fmh_locale
, "C") != 0)) {
1598 (void) setlocale(LC_ALL
, "C");
1599 locale
= "C"; /* restore locale */
1602 if (dgettext(dict
, key
) == key
) {
1606 s
= fmd_msg_gettext_locked(h
, nvl
, dict
, code
);
1611 (void) setlocale(LC_ALL
, old_c
);
1613 if (h
->fmh_binding
!= NULL
)
1614 (void) bindtextdomain(dict
, old_b
);
1625 fmd_msg_gettext_nv(fmd_msg_hdl_t
*h
, const char *locale
, nvlist_t
*nvl
)
1629 if (nvlist_lookup_string(nvl
, FM_SUSPECT_DIAG_CODE
, &code
) != 0) {
1634 return (fmd_msg_gettext(h
, locale
, nvl
, code
));
1638 fmd_msg_gettext_id(fmd_msg_hdl_t
*h
, const char *locale
, const char *code
)
1640 return (fmd_msg_gettext(h
, locale
, NULL
, code
));