1e1c679faSaf /*
2e1c679faSaf  * CDDL HEADER START
3e1c679faSaf  *
4e1c679faSaf  * The contents of this file are subject to the terms of the
5e1c679faSaf  * Common Development and Distribution License (the "License").
6e1c679faSaf  * You may not use this file except in compliance with the License.
7e1c679faSaf  *
8e1c679faSaf  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e1c679faSaf  * or http://www.opensolaris.org/os/licensing.
10e1c679faSaf  * See the License for the specific language governing permissions
11e1c679faSaf  * and limitations under the License.
12e1c679faSaf  *
13e1c679faSaf  * When distributing Covered Code, include this CDDL HEADER in each
14e1c679faSaf  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e1c679faSaf  * If applicable, add the following below this CDDL HEADER, with the
16e1c679faSaf  * fields enclosed by brackets "[]" replaced with your own identifying
17e1c679faSaf  * information: Portions Copyright [yyyy] [name of copyright owner]
18e1c679faSaf  *
19e1c679faSaf  * CDDL HEADER END
20e1c679faSaf  */
21b6955755SRobert Johnston 
22e1c679faSaf /*
23f6e214c7SGavin Maltby  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24e1c679faSaf  */
25e1c679faSaf 
26b6955755SRobert Johnston /*
27b6955755SRobert Johnston  * FMD Message Library
28b6955755SRobert Johnston  *
29b6955755SRobert Johnston  * This library supports a simple set of routines for use in converting FMA
30b6955755SRobert Johnston  * events and message codes to localized human-readable message strings.
31b6955755SRobert Johnston  *
32b6955755SRobert Johnston  * 1. Library API
33b6955755SRobert Johnston  *
34b6955755SRobert Johnston  * The APIs are as follows:
35b6955755SRobert Johnston  *
36b6955755SRobert Johnston  * fmd_msg_init - set up the library and return a handle
37b6955755SRobert Johnston  * fmd_msg_fini - destroy the handle from fmd_msg_init
38b6955755SRobert Johnston  *
39*bbf21555SRichard Lowe  * fmd_msg_locale_set - set the default locale (initially based on environ(7))
40b6955755SRobert Johnston  * fmd_msg_locale_get - get the default locale
41b6955755SRobert Johnston  *
42b6955755SRobert Johnston  * fmd_msg_url_set - set the default URL for knowledge articles
43b6955755SRobert Johnston  * fmd_msg_url_get - get the default URL for knowledge articles
44b6955755SRobert Johnston  *
45b6955755SRobert Johnston  * fmd_msg_gettext_nv - format the entire message for the given event
46b6955755SRobert Johnston  * fmd_msg_gettext_id - format the entire message for the given event code
47f6e214c7SGavin Maltby  * fmd_msg_gettext_key - format the entire message for the given dict for the
48f6e214c7SGavin Maltby  *                       given explicit message key
49b6955755SRobert Johnston  *
50b6955755SRobert Johnston  * fmd_msg_getitem_nv - format a single message item for the given event
51b6955755SRobert Johnston  * fmd_msg_getitem_id - format a single message item for the given event code
52b6955755SRobert Johnston  *
53b6955755SRobert Johnston  * Upon success, fmd_msg_gettext_* and fmd_msg_getitem_* return newly-allocated
54b6955755SRobert Johnston  * localized strings in multi-byte format.  The caller must call free() on the
55b6955755SRobert Johnston  * resulting buffer to deallocate the string after making use of it.  Upon
56b6955755SRobert Johnston  * failure, these functions return NULL and set errno as follows:
57b6955755SRobert Johnston  *
58b6955755SRobert Johnston  * ENOMEM - Memory allocation failure while formatting message
59b6955755SRobert Johnston  * ENOENT - No message was found for the specified message identifier
60b6955755SRobert Johnston  * EINVAL - Invalid argument (e.g. bad event code, illegal fmd_msg_item_t)
61b6955755SRobert Johnston  * EILSEQ - Illegal multi-byte sequence detected in message
62b6955755SRobert Johnston  *
63b6955755SRobert Johnston  * 2. Variable Expansion
64b6955755SRobert Johnston  *
65b6955755SRobert Johnston  * The human-readable messages are stored in msgfmt(1) message object files in
66b6955755SRobert Johnston  * the corresponding locale directories.  The values for the message items are
67b6955755SRobert Johnston  * permitted to contain variable expansions, currently defined as follows:
68b6955755SRobert Johnston  *
69b6955755SRobert Johnston  * %%     - literal % character
70654b400cSJoshua M. Clulow  * %s     - knowledge article URL (e.g. http://illumos.org/msg/<MSG-ID>)
71b6955755SRobert Johnston  * %< x > - value x from the current event, using the expression syntax below:
72b6955755SRobert Johnston  *
73b6955755SRobert Johnston  * foo.bar  => print nvlist_t member "bar" contained within nvlist_t "foo"
74b6955755SRobert Johnston  * foo[123] => print array element 123 of nvlist_t member "foo"
75b6955755SRobert Johnston  * foo[123].bar => print member "bar" of nvlist_t element 123 in array "foo"
76b6955755SRobert Johnston  *
77b6955755SRobert Johnston  * For example, the msgstr value for FMD-8000-2K might be defined as:
78b6955755SRobert Johnston  *
79b6955755SRobert Johnston  * msgid "FMD-8000-2K.action"
80b6955755SRobert Johnston  * msgstr "Use fmdump -v -u %<uuid> to locate the module.  Use fmadm \
81b6955755SRobert Johnston  *     reset %<fault-list[0].asru.mod-name> to reset the module."
82b6955755SRobert Johnston  *
83b6955755SRobert Johnston  * 3. Locking
84b6955755SRobert Johnston  *
85b6955755SRobert Johnston  * In order to format a human-readable message, libfmd_msg must get and set
86b6955755SRobert Johnston  * the process locale and potentially alter text domain bindings.  At present,
87b6955755SRobert Johnston  * these facilities in libc are not fully MT-safe.  As such, a library-wide
88b6955755SRobert Johnston  * lock is provided: fmd_msg_lock() and fmd_msg_unlock().  These locking calls
89b6955755SRobert Johnston  * are made internally as part of the top-level library entry points, but they
90b6955755SRobert Johnston  * can also be used by applications that themselves call setlocale() and wish
91b6955755SRobert Johnston  * to appropriately synchronize with other threads that are calling libfmd_msg.
92b6955755SRobert Johnston  */
93e1c679faSaf 
94b6955755SRobert Johnston 
95b6955755SRobert Johnston #include <sys/fm/protocol.h>
96b6955755SRobert Johnston 
97b6955755SRobert Johnston #include <libintl.h>
98b6955755SRobert Johnston #include <locale.h>
99b6955755SRobert Johnston #include <wchar.h>
100b6955755SRobert Johnston 
101b6955755SRobert Johnston #include <alloca.h>
102b6955755SRobert Johnston #include <assert.h>
103f6e214c7SGavin Maltby #include <netdb.h>
104b6955755SRobert Johnston #include <pthread.h>
10553f3aea0SRoger A. Faulkner #include <synch.h>
106b6955755SRobert Johnston #include <strings.h>
107b6955755SRobert Johnston #include <stdarg.h>
108e1c679faSaf #include <stdlib.h>
109b6955755SRobert Johnston #include <stdio.h>
110b6955755SRobert Johnston #include <errno.h>
111f6e214c7SGavin Maltby #include <unistd.h>
112b6955755SRobert Johnston #include <sys/sysmacros.h>
113e1c679faSaf 
114b6955755SRobert Johnston #include <fmd_msg.h>
115e1c679faSaf 
116b6955755SRobert Johnston #define	FMD_MSGBUF_SZ	256
117e1c679faSaf 
118b6955755SRobert Johnston struct fmd_msg_hdl {
119b6955755SRobert Johnston 	int fmh_version;	/* libfmd_msg client abi version number */
120b6955755SRobert Johnston 	char *fmh_urlbase;	/* base url for all knowledge articles */
121b6955755SRobert Johnston 	char *fmh_binding;	/* base directory for bindtextdomain() */
122b6955755SRobert Johnston 	char *fmh_locale;	/* default program locale from environment */
123b6955755SRobert Johnston 	const char *fmh_template; /* FMD_MSG_TEMPLATE value for fmh_locale */
124b6955755SRobert Johnston };
125b6955755SRobert Johnston 
126b6955755SRobert Johnston typedef struct fmd_msg_buf {
127b6955755SRobert Johnston 	wchar_t *fmb_data;	/* wide-character data buffer */
128b6955755SRobert Johnston 	size_t fmb_size;	/* size of fmb_data in wchar_t units */
129b6955755SRobert Johnston 	size_t fmb_used;	/* used portion of fmb_data in wchar_t units */
130b6955755SRobert Johnston 	int fmb_error;		/* error if any has occurred */
131b6955755SRobert Johnston } fmd_msg_buf_t;
132b6955755SRobert Johnston 
133b6955755SRobert Johnston static const char *const fmd_msg_items[] = {
134b6955755SRobert Johnston 	"type",			/* key for FMD_MSG_ITEM_TYPE */
135b6955755SRobert Johnston 	"severity",		/* key for FMD_MSG_ITEM_SEVERITY */
136b6955755SRobert Johnston 	"description",		/* key for FMD_MSG_ITEM_DESC */
137b6955755SRobert Johnston 	"response",		/* key for FMD_MSG_ITEM_RESPONSE */
138b6955755SRobert Johnston 	"impact", 		/* key for FMD_MSG_ITEM_IMPACT */
139b6955755SRobert Johnston 	"action", 		/* key for FMD_MSG_ITEM_ACTION */
140b6955755SRobert Johnston 	"url",			/* key for FMD_MSG_ITEM_URL */
141b6955755SRobert Johnston };
142b6955755SRobert Johnston 
143b6955755SRobert Johnston static pthread_rwlock_t fmd_msg_rwlock = PTHREAD_RWLOCK_INITIALIZER;
144b6955755SRobert Johnston 
145b6955755SRobert Johnston static const char FMD_MSG_DOMAIN[] = "FMD";
146b6955755SRobert Johnston static const char FMD_MSG_TEMPLATE[] = "syslog-msgs-message-template";
147b6955755SRobert Johnston static const char FMD_MSG_URLKEY[] = "syslog-url";
148654b400cSJoshua M. Clulow static const char FMD_MSG_URLBASE[] = "http://illumos.org/msg/";
149b6955755SRobert Johnston static const char FMD_MSG_NLSPATH[] = "NLSPATH=/usr/lib/fm/fmd/fmd.cat";
150b6955755SRobert Johnston static const char FMD_MSG_MISSING[] = "-";
151b6955755SRobert Johnston 
152b6955755SRobert Johnston /*
153b6955755SRobert Johnston  * An enumeration of token types.  The following are valid tokens that can be
154b6955755SRobert Johnston  * embedded into the message content:
155b6955755SRobert Johnston  *
156b6955755SRobert Johnston  * T_INT - integer tokens (for array indices)
157b6955755SRobert Johnston  * T_IDENT - nvpair identifiers
158b6955755SRobert Johnston  * T_DOT - "."
159b6955755SRobert Johnston  * T_LBRAC - "["
160b6955755SRobert Johnston  * T_RBRAC - "]"
161b6955755SRobert Johnston  *
162b6955755SRobert Johnston  * A NULL character (T_EOF) is used to terminate messages.
163b6955755SRobert Johnston  * Invalid tokens are assigned the type T_ERR.
164b6955755SRobert Johnston  */
165b6955755SRobert Johnston typedef enum {
166b6955755SRobert Johnston 	T_EOF,
167b6955755SRobert Johnston 	T_ERR,
168b6955755SRobert Johnston 	T_IDENT,
169b6955755SRobert Johnston 	T_INT,
170b6955755SRobert Johnston 	T_DOT,
171b6955755SRobert Johnston 	T_LBRAC,
172b6955755SRobert Johnston 	T_RBRAC
173b6955755SRobert Johnston } fmd_msg_nv_tkind_t;
174b6955755SRobert Johnston 
175b6955755SRobert Johnston typedef struct fmd_msg_nv_token {
176b6955755SRobert Johnston 	fmd_msg_nv_tkind_t t_kind;
177b6955755SRobert Johnston 	union {
178b6955755SRobert Johnston 		char tu_str[256];
179b6955755SRobert Johnston 		uint_t tu_int;
180b6955755SRobert Johnston 	} t_data;
181b6955755SRobert Johnston } fmd_msg_nv_token_t;
182b6955755SRobert Johnston 
183b6955755SRobert Johnston static const struct fmd_msg_nv_type {
184b6955755SRobert Johnston 	data_type_t nvt_type;
185b6955755SRobert Johnston 	data_type_t nvt_base;
186b6955755SRobert Johnston 	size_t nvt_size;
187b6955755SRobert Johnston 	int (*nvt_value)();
188b6955755SRobert Johnston 	int (*nvt_array)();
189b6955755SRobert Johnston } fmd_msg_nv_types[] = {
190b6955755SRobert Johnston 	{ DATA_TYPE_INT8, DATA_TYPE_INT8,
191b6955755SRobert Johnston 	    sizeof (int8_t), nvpair_value_int8, NULL },
192b6955755SRobert Johnston 	{ DATA_TYPE_INT16, DATA_TYPE_INT16,
193b6955755SRobert Johnston 	    sizeof (int16_t), nvpair_value_int16, NULL },
194b6955755SRobert Johnston 	{ DATA_TYPE_INT32, DATA_TYPE_INT32,
195b6955755SRobert Johnston 	    sizeof (int32_t), nvpair_value_int32, NULL },
196b6955755SRobert Johnston 	{ DATA_TYPE_INT64, DATA_TYPE_INT64,
197b6955755SRobert Johnston 	    sizeof (int64_t), nvpair_value_int64, NULL },
198b6955755SRobert Johnston 	{ DATA_TYPE_UINT8, DATA_TYPE_UINT8,
199b6955755SRobert Johnston 	    sizeof (uint8_t), nvpair_value_uint8, NULL },
200b6955755SRobert Johnston 	{ DATA_TYPE_UINT16, DATA_TYPE_UINT16,
201b6955755SRobert Johnston 	    sizeof (uint16_t), nvpair_value_uint16, NULL },
202b6955755SRobert Johnston 	{ DATA_TYPE_UINT32, DATA_TYPE_UINT32,
203b6955755SRobert Johnston 	    sizeof (uint32_t), nvpair_value_uint32, NULL },
204b6955755SRobert Johnston 	{ DATA_TYPE_UINT64, DATA_TYPE_UINT64,
205b6955755SRobert Johnston 	    sizeof (uint64_t), nvpair_value_uint64, NULL },
206b6955755SRobert Johnston 	{ DATA_TYPE_BYTE, DATA_TYPE_BYTE,
207b6955755SRobert Johnston 	    sizeof (uchar_t), nvpair_value_byte, NULL },
208b6955755SRobert Johnston 	{ DATA_TYPE_BOOLEAN, DATA_TYPE_BOOLEAN,
209b6955755SRobert Johnston 	    0, NULL, NULL },
210b6955755SRobert Johnston 	{ DATA_TYPE_BOOLEAN_VALUE, DATA_TYPE_BOOLEAN_VALUE,
211b6955755SRobert Johnston 	    sizeof (boolean_t), nvpair_value_boolean_value, NULL },
212b6955755SRobert Johnston 	{ DATA_TYPE_HRTIME, DATA_TYPE_HRTIME,
213b6955755SRobert Johnston 	    sizeof (hrtime_t), nvpair_value_hrtime, NULL },
214b6955755SRobert Johnston 	{ DATA_TYPE_STRING, DATA_TYPE_STRING,
215b6955755SRobert Johnston 	    sizeof (char *), nvpair_value_string, NULL },
216b6955755SRobert Johnston 	{ DATA_TYPE_NVLIST, DATA_TYPE_NVLIST,
217b6955755SRobert Johnston 	    sizeof (nvlist_t *), nvpair_value_nvlist, NULL },
218b6955755SRobert Johnston 	{ DATA_TYPE_INT8_ARRAY, DATA_TYPE_INT8,
219b6955755SRobert Johnston 	    sizeof (int8_t), NULL, nvpair_value_int8_array },
220b6955755SRobert Johnston 	{ DATA_TYPE_INT16_ARRAY, DATA_TYPE_INT16,
221b6955755SRobert Johnston 	    sizeof (int16_t), NULL, nvpair_value_int16_array },
222b6955755SRobert Johnston 	{ DATA_TYPE_INT32_ARRAY, DATA_TYPE_INT32,
223b6955755SRobert Johnston 	    sizeof (int32_t), NULL, nvpair_value_int32_array },
224b6955755SRobert Johnston 	{ DATA_TYPE_INT64_ARRAY, DATA_TYPE_INT64,
225b6955755SRobert Johnston 	    sizeof (int64_t), NULL, nvpair_value_int64_array },
226b6955755SRobert Johnston 	{ DATA_TYPE_UINT8_ARRAY, DATA_TYPE_UINT8,
227b6955755SRobert Johnston 	    sizeof (uint8_t), NULL, nvpair_value_uint8_array },
228b6955755SRobert Johnston 	{ DATA_TYPE_UINT16_ARRAY, DATA_TYPE_UINT16,
229b6955755SRobert Johnston 	    sizeof (uint16_t), NULL, nvpair_value_uint16_array },
230b6955755SRobert Johnston 	{ DATA_TYPE_UINT32_ARRAY, DATA_TYPE_UINT32,
231b6955755SRobert Johnston 	    sizeof (uint32_t), NULL, nvpair_value_uint32_array },
232b6955755SRobert Johnston 	{ DATA_TYPE_UINT64_ARRAY, DATA_TYPE_UINT64,
233b6955755SRobert Johnston 	    sizeof (uint64_t), NULL, nvpair_value_uint64_array },
234b6955755SRobert Johnston 	{ DATA_TYPE_BYTE_ARRAY, DATA_TYPE_BYTE,
235b6955755SRobert Johnston 	    sizeof (uchar_t), NULL, nvpair_value_byte_array },
236b6955755SRobert Johnston 	{ DATA_TYPE_BOOLEAN_ARRAY, DATA_TYPE_BOOLEAN_VALUE,
237b6955755SRobert Johnston 	    sizeof (boolean_t), NULL, nvpair_value_boolean_array },
238b6955755SRobert Johnston 	{ DATA_TYPE_STRING_ARRAY, DATA_TYPE_STRING,
239b6955755SRobert Johnston 	    sizeof (char *), NULL, nvpair_value_string_array },
240b6955755SRobert Johnston 	{ DATA_TYPE_NVLIST_ARRAY, DATA_TYPE_NVLIST,
241b6955755SRobert Johnston 	    sizeof (nvlist_t *), NULL, nvpair_value_nvlist_array },
242b6955755SRobert Johnston 	{ DATA_TYPE_UNKNOWN, DATA_TYPE_UNKNOWN, 0, NULL, NULL }
243b6955755SRobert Johnston };
244b6955755SRobert Johnston 
245b6955755SRobert Johnston static int fmd_msg_nv_parse_nvpair(fmd_msg_buf_t *, nvpair_t *, char *);
246b6955755SRobert Johnston static int fmd_msg_nv_parse_nvname(fmd_msg_buf_t *, nvlist_t *, char *);
247b6955755SRobert Johnston static int fmd_msg_nv_parse_nvlist(fmd_msg_buf_t *, nvlist_t *, char *);
248b6955755SRobert Johnston 
249b6955755SRobert Johnston /*ARGSUSED*/
250b6955755SRobert Johnston static int
fmd_msg_lock_held(fmd_msg_hdl_t * h)251b6955755SRobert Johnston fmd_msg_lock_held(fmd_msg_hdl_t *h)
252e1c679faSaf {
25353f3aea0SRoger A. Faulkner 	return (RW_WRITE_HELD(&fmd_msg_rwlock));
254e1c679faSaf }
255e1c679faSaf 
256e1c679faSaf void
fmd_msg_lock(void)257e1c679faSaf fmd_msg_lock(void)
258e1c679faSaf {
259b6955755SRobert Johnston 	if (pthread_rwlock_wrlock(&fmd_msg_rwlock) != 0)
260e1c679faSaf 		abort();
261e1c679faSaf }
262e1c679faSaf 
263e1c679faSaf void
fmd_msg_unlock(void)264e1c679faSaf fmd_msg_unlock(void)
265e1c679faSaf {
266b6955755SRobert Johnston 	if (pthread_rwlock_unlock(&fmd_msg_rwlock) != 0)
267e1c679faSaf 		abort();
268e1c679faSaf }
269b6955755SRobert Johnston 
270b6955755SRobert Johnston static fmd_msg_hdl_t *
fmd_msg_init_err(fmd_msg_hdl_t * h,int err)271b6955755SRobert Johnston fmd_msg_init_err(fmd_msg_hdl_t *h, int err)
272b6955755SRobert Johnston {
273b6955755SRobert Johnston 	fmd_msg_fini(h);
274b6955755SRobert Johnston 	errno = err;
275b6955755SRobert Johnston 	return (NULL);
276b6955755SRobert Johnston }
277b6955755SRobert Johnston 
278b6955755SRobert Johnston fmd_msg_hdl_t *
fmd_msg_init(const char * root,int version)279b6955755SRobert Johnston fmd_msg_init(const char *root, int version)
280b6955755SRobert Johnston {
281b6955755SRobert Johnston 	fmd_msg_hdl_t *h = NULL;
282b6955755SRobert Johnston 	const char *s;
283b6955755SRobert Johnston 	size_t len;
284b6955755SRobert Johnston 
285b6955755SRobert Johnston 	if (version != FMD_MSG_VERSION)
286b6955755SRobert Johnston 		return (fmd_msg_init_err(h, EINVAL));
287b6955755SRobert Johnston 
288b6955755SRobert Johnston 	if ((h = malloc(sizeof (fmd_msg_hdl_t))) == NULL)
289b6955755SRobert Johnston 		return (fmd_msg_init_err(h, ENOMEM));
290b6955755SRobert Johnston 
291b6955755SRobert Johnston 	bzero(h, sizeof (fmd_msg_hdl_t));
292b6955755SRobert Johnston 	h->fmh_version = version;
293b6955755SRobert Johnston 
294b6955755SRobert Johnston 	if ((h->fmh_urlbase = strdup(FMD_MSG_URLBASE)) == NULL)
295b6955755SRobert Johnston 		return (fmd_msg_init_err(h, ENOMEM));
296b6955755SRobert Johnston 
297b6955755SRobert Johnston 	/*
298b6955755SRobert Johnston 	 * Initialize the program's locale from the environment if it hasn't
299b6955755SRobert Johnston 	 * already been initialized, and then retrieve the default setting.
300b6955755SRobert Johnston 	 */
301b6955755SRobert Johnston 	(void) setlocale(LC_ALL, "");
302b6955755SRobert Johnston 	s = setlocale(LC_ALL, NULL);
303b6955755SRobert Johnston 	h->fmh_locale = strdup(s ? s : "C");
304b6955755SRobert Johnston 
305b6955755SRobert Johnston 	if (h->fmh_locale == NULL)
306b6955755SRobert Johnston 		return (fmd_msg_init_err(h, ENOMEM));
307b6955755SRobert Johnston 
308b6955755SRobert Johnston 	/*
309b6955755SRobert Johnston 	 * If a non-default root directory is specified, then look up the base
310b6955755SRobert Johnston 	 * directory for our default catalog, and set fmh_binding as the same
311b6955755SRobert Johnston 	 * directory prefixed with the new root directory.  This simply turns
312b6955755SRobert Johnston 	 * usr/lib/locale into <rootdir>/usr/lib/locale, but handles all of the
313*bbf21555SRichard Lowe 	 * environ(7) settings that can change the default messages binding.
314b6955755SRobert Johnston 	 */
315b6955755SRobert Johnston 	if (root != NULL && root[0] != '\0' && strcmp(root, "/") != 0) {
316b6955755SRobert Johnston 		if (root[0] != '/')
317b6955755SRobert Johnston 			return (fmd_msg_init_err(h, EINVAL));
318b6955755SRobert Johnston 
319b6955755SRobert Johnston 		if ((s = bindtextdomain(FMD_MSG_DOMAIN, NULL)) == NULL)
320b6955755SRobert Johnston 			s = "/usr/lib/locale"; /* substitute default */
321b6955755SRobert Johnston 
322b6955755SRobert Johnston 		len = strlen(root) + strlen(s) + 1;
323b6955755SRobert Johnston 
324b6955755SRobert Johnston 		if ((h->fmh_binding = malloc(len)) == NULL)
325b6955755SRobert Johnston 			return (fmd_msg_init_err(h, ENOMEM));
326b6955755SRobert Johnston 
327b6955755SRobert Johnston 		(void) snprintf(h->fmh_binding, len, "%s%s", root, s);
328b6955755SRobert Johnston 	}
329b6955755SRobert Johnston 
330b6955755SRobert Johnston 	/*
331b6955755SRobert Johnston 	 * All FMA event dictionaries use msgfmt(1) message objects to produce
332b6955755SRobert Johnston 	 * messages, even for the C locale.  We therefore want to use dgettext
333b6955755SRobert Johnston 	 * for all message lookups, but its defined behavior in the C locale is
334b6955755SRobert Johnston 	 * to return the input string.  Since our input strings are event codes
335b6955755SRobert Johnston 	 * and not format strings, this doesn't help us.  We resolve this nit
336b6955755SRobert Johnston 	 * by setting NLSPATH to a non-existent file: the presence of NLSPATH
337b6955755SRobert Johnston 	 * is defined to force dgettext(3C) to do a full lookup even for C.
338b6955755SRobert Johnston 	 */
339b6955755SRobert Johnston 	if (getenv("NLSPATH") == NULL &&
340b6955755SRobert Johnston 	    ((s = strdup(FMD_MSG_NLSPATH)) == NULL || putenv((char *)s) != 0))
341b6955755SRobert Johnston 		return (fmd_msg_init_err(h, errno));
342b6955755SRobert Johnston 
343b6955755SRobert Johnston 	/*
344b6955755SRobert Johnston 	 * Cache the message template for the current locale.  This is the
345b6955755SRobert Johnston 	 * snprintf(3C) format string for the final human-readable message.
34624e578f7SRobert Johnston 	 * If the lookup fails for the current locale, fall back to the C locale
34724e578f7SRobert Johnston 	 * and try again.  Then restore the original locale.
348b6955755SRobert Johnston 	 */
34924e578f7SRobert Johnston 	if ((h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE))
35024e578f7SRobert Johnston 	    == FMD_MSG_TEMPLATE && strcmp(h->fmh_locale, "C") != 0) {
35124e578f7SRobert Johnston 		(void) setlocale(LC_ALL, "C");
35224e578f7SRobert Johnston 		h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE);
35324e578f7SRobert Johnston 		(void) setlocale(LC_ALL, h->fmh_locale);
35424e578f7SRobert Johnston 	}
355b6955755SRobert Johnston 
356b6955755SRobert Johnston 	return (h);
357b6955755SRobert Johnston }
358b6955755SRobert Johnston 
359b6955755SRobert Johnston void
fmd_msg_fini(fmd_msg_hdl_t * h)360b6955755SRobert Johnston fmd_msg_fini(fmd_msg_hdl_t *h)
361b6955755SRobert Johnston {
362b6955755SRobert Johnston 	if (h == NULL)
363b6955755SRobert Johnston 		return; /* simplify caller code */
364b6955755SRobert Johnston 
365b6955755SRobert Johnston 	free(h->fmh_binding);
366b6955755SRobert Johnston 	free(h->fmh_urlbase);
367b6955755SRobert Johnston 	free(h->fmh_locale);
368b6955755SRobert Johnston 	free(h);
369b6955755SRobert Johnston }
370b6955755SRobert Johnston 
371b6955755SRobert Johnston int
fmd_msg_locale_set(fmd_msg_hdl_t * h,const char * locale)372b6955755SRobert Johnston fmd_msg_locale_set(fmd_msg_hdl_t *h, const char *locale)
373b6955755SRobert Johnston {
374b6955755SRobert Johnston 	char *l;
375b6955755SRobert Johnston 
376b6955755SRobert Johnston 	if (locale == NULL) {
377b6955755SRobert Johnston 		errno = EINVAL;
378b6955755SRobert Johnston 		return (-1);
379b6955755SRobert Johnston 	}
380b6955755SRobert Johnston 
381b6955755SRobert Johnston 	if ((l = strdup(locale)) == NULL) {
382b6955755SRobert Johnston 		errno = ENOMEM;
383b6955755SRobert Johnston 		return (-1);
384b6955755SRobert Johnston 	}
385b6955755SRobert Johnston 
386b6955755SRobert Johnston 	fmd_msg_lock();
387b6955755SRobert Johnston 
388b6955755SRobert Johnston 	if (setlocale(LC_ALL, l) == NULL) {
389b6955755SRobert Johnston 		free(l);
390b6955755SRobert Johnston 		errno = EINVAL;
391b6955755SRobert Johnston 		fmd_msg_unlock();
392b6955755SRobert Johnston 		return (-1);
393b6955755SRobert Johnston 	}
394b6955755SRobert Johnston 
395b6955755SRobert Johnston 	h->fmh_template = dgettext(FMD_MSG_DOMAIN, FMD_MSG_TEMPLATE);
396b6955755SRobert Johnston 	free(h->fmh_locale);
397b6955755SRobert Johnston 	h->fmh_locale = l;
398b6955755SRobert Johnston 
399b6955755SRobert Johnston 	fmd_msg_unlock();
400b6955755SRobert Johnston 	return (0);
401b6955755SRobert Johnston }
402b6955755SRobert Johnston 
403b6955755SRobert Johnston const char *
fmd_msg_locale_get(fmd_msg_hdl_t * h)404b6955755SRobert Johnston fmd_msg_locale_get(fmd_msg_hdl_t *h)
405b6955755SRobert Johnston {
406b6955755SRobert Johnston 	return (h->fmh_locale);
407b6955755SRobert Johnston }
408b6955755SRobert Johnston 
409b6955755SRobert Johnston int
fmd_msg_url_set(fmd_msg_hdl_t * h,const char * url)410b6955755SRobert Johnston fmd_msg_url_set(fmd_msg_hdl_t *h, const char *url)
411b6955755SRobert Johnston {
412b6955755SRobert Johnston 	char *u;
413b6955755SRobert Johnston 
414b6955755SRobert Johnston 	if (url == NULL) {
415b6955755SRobert Johnston 		errno = EINVAL;
416b6955755SRobert Johnston 		return (-1);
417b6955755SRobert Johnston 	}
418b6955755SRobert Johnston 
419b6955755SRobert Johnston 	if ((u = strdup(url)) == NULL) {
420b6955755SRobert Johnston 		errno = ENOMEM;
421b6955755SRobert Johnston 		return (-1);
422b6955755SRobert Johnston 	}
423b6955755SRobert Johnston 
424b6955755SRobert Johnston 	fmd_msg_lock();
425b6955755SRobert Johnston 
426b6955755SRobert Johnston 	free(h->fmh_urlbase);
427b6955755SRobert Johnston 	h->fmh_urlbase = u;
428b6955755SRobert Johnston 
429b6955755SRobert Johnston 	fmd_msg_unlock();
430b6955755SRobert Johnston 	return (0);
431b6955755SRobert Johnston }
432b6955755SRobert Johnston 
433b6955755SRobert Johnston const char *
fmd_msg_url_get(fmd_msg_hdl_t * h)434b6955755SRobert Johnston fmd_msg_url_get(fmd_msg_hdl_t *h)
435b6955755SRobert Johnston {
436b6955755SRobert Johnston 	return (h->fmh_urlbase);
437b6955755SRobert Johnston }
438b6955755SRobert Johnston 
439b6955755SRobert Johnston static wchar_t *
fmd_msg_mbstowcs(const char * s)440b6955755SRobert Johnston fmd_msg_mbstowcs(const char *s)
441b6955755SRobert Johnston {
442b6955755SRobert Johnston 	size_t n = strlen(s) + 1;
443b6955755SRobert Johnston 	wchar_t *w = malloc(n * sizeof (wchar_t));
444b6955755SRobert Johnston 
445b6955755SRobert Johnston 	if (w == NULL) {
446b6955755SRobert Johnston 		errno = ENOMEM;
447b6955755SRobert Johnston 		return (NULL);
448b6955755SRobert Johnston 	}
449b6955755SRobert Johnston 
450b6955755SRobert Johnston 	if (mbstowcs(w, s, n) == (size_t)-1) {
451b6955755SRobert Johnston 		free(w);
452b6955755SRobert Johnston 		return (NULL);
453b6955755SRobert Johnston 	}
454b6955755SRobert Johnston 
455b6955755SRobert Johnston 	return (w);
456b6955755SRobert Johnston }
457b6955755SRobert Johnston 
458b6955755SRobert Johnston static void
fmd_msg_buf_init(fmd_msg_buf_t * b)459b6955755SRobert Johnston fmd_msg_buf_init(fmd_msg_buf_t *b)
460b6955755SRobert Johnston {
461b6955755SRobert Johnston 	bzero(b, sizeof (fmd_msg_buf_t));
462b6955755SRobert Johnston 	b->fmb_data = malloc(sizeof (wchar_t) * FMD_MSGBUF_SZ);
463b6955755SRobert Johnston 
464b6955755SRobert Johnston 	if (b->fmb_data == NULL)
465b6955755SRobert Johnston 		b->fmb_error = ENOMEM;
466b6955755SRobert Johnston 	else
467b6955755SRobert Johnston 		b->fmb_size = FMD_MSGBUF_SZ;
468b6955755SRobert Johnston }
469b6955755SRobert Johnston 
470b6955755SRobert Johnston static void
fmd_msg_buf_fini(fmd_msg_buf_t * b)471b6955755SRobert Johnston fmd_msg_buf_fini(fmd_msg_buf_t *b)
472b6955755SRobert Johnston {
473b6955755SRobert Johnston 	free(b->fmb_data);
474b6955755SRobert Johnston 	bzero(b, sizeof (fmd_msg_buf_t));
475b6955755SRobert Johnston }
476b6955755SRobert Johnston 
477b6955755SRobert Johnston static char *
fmd_msg_buf_read(fmd_msg_buf_t * b)478b6955755SRobert Johnston fmd_msg_buf_read(fmd_msg_buf_t *b)
479b6955755SRobert Johnston {
480b6955755SRobert Johnston 	char *s;
481b6955755SRobert Johnston 
482b6955755SRobert Johnston 	if (b->fmb_error != 0) {
483b6955755SRobert Johnston 		errno = b->fmb_error;
484b6955755SRobert Johnston 		return (NULL);
485b6955755SRobert Johnston 	}
486b6955755SRobert Johnston 
487b6955755SRobert Johnston 	if ((s = malloc(b->fmb_used * MB_CUR_MAX)) == NULL) {
488b6955755SRobert Johnston 		errno = ENOMEM;
489b6955755SRobert Johnston 		return (NULL);
490b6955755SRobert Johnston 	}
491b6955755SRobert Johnston 
492b6955755SRobert Johnston 	if (wcstombs(s, b->fmb_data, b->fmb_used) == (size_t)-1) {
493b6955755SRobert Johnston 		free(s);
494b6955755SRobert Johnston 		return (NULL);
495b6955755SRobert Johnston 	}
496b6955755SRobert Johnston 
497b6955755SRobert Johnston 	return (s);
498b6955755SRobert Johnston }
499b6955755SRobert Johnston 
500b6955755SRobert Johnston /*
501b6955755SRobert Johnston  * Buffer utility function to write a wide-character string into the buffer,
502b6955755SRobert Johnston  * appending it at the end, and growing the buffer as needed as we go.  Any
503b6955755SRobert Johnston  * allocation errors are stored in fmb_error and deferred until later.
504b6955755SRobert Johnston  */
505b6955755SRobert Johnston static void
fmd_msg_buf_write(fmd_msg_buf_t * b,const wchar_t * w,size_t n)506b6955755SRobert Johnston fmd_msg_buf_write(fmd_msg_buf_t *b, const wchar_t *w, size_t n)
507b6955755SRobert Johnston {
508b6955755SRobert Johnston 	if (b->fmb_used + n > b->fmb_size) {
509b6955755SRobert Johnston 		size_t size = MAX(b->fmb_size * 2, b->fmb_used + n);
510b6955755SRobert Johnston 		wchar_t *data = malloc(sizeof (wchar_t) * size);
511b6955755SRobert Johnston 
512b6955755SRobert Johnston 		if (data == NULL) {
513b6955755SRobert Johnston 			if (b->fmb_error == 0)
514b6955755SRobert Johnston 				b->fmb_error = ENOMEM;
515b6955755SRobert Johnston 			return;
516b6955755SRobert Johnston 		}
517b6955755SRobert Johnston 
518b6955755SRobert Johnston 		bcopy(b->fmb_data, data, b->fmb_used * sizeof (wchar_t));
519b6955755SRobert Johnston 		free(b->fmb_data);
520b6955755SRobert Johnston 
521b6955755SRobert Johnston 		b->fmb_data = data;
522b6955755SRobert Johnston 		b->fmb_size = size;
523b6955755SRobert Johnston 	}
524b6955755SRobert Johnston 
525b6955755SRobert Johnston 	bcopy(w, &b->fmb_data[b->fmb_used], sizeof (wchar_t) * n);
526b6955755SRobert Johnston 	b->fmb_used += n;
527b6955755SRobert Johnston }
528b6955755SRobert Johnston 
529b6955755SRobert Johnston /*
530b6955755SRobert Johnston  * Buffer utility function to printf a multi-byte string, convert to wide-
531b6955755SRobert Johnston  * character form, and then write the result into an fmd_msg_buf_t.
532b6955755SRobert Johnston  */
533b6955755SRobert Johnston /*PRINTFLIKE2*/
534b6955755SRobert Johnston static void
fmd_msg_buf_printf(fmd_msg_buf_t * b,const char * format,...)535b6955755SRobert Johnston fmd_msg_buf_printf(fmd_msg_buf_t *b, const char *format, ...)
536b6955755SRobert Johnston {
537b6955755SRobert Johnston 	ssize_t len;
538b6955755SRobert Johnston 	va_list ap;
539b6955755SRobert Johnston 	char *buf;
540b6955755SRobert Johnston 	wchar_t *w;
541b6955755SRobert Johnston 
542b6955755SRobert Johnston 	va_start(ap, format);
543b6955755SRobert Johnston 	len = vsnprintf(NULL, 0, format, ap);
544b6955755SRobert Johnston 	buf = alloca(len + 1);
545b6955755SRobert Johnston 	(void) vsnprintf(buf, len + 1, format, ap);
546b6955755SRobert Johnston 	va_end(ap);
547b6955755SRobert Johnston 
548b6955755SRobert Johnston 	if ((w = fmd_msg_mbstowcs(buf)) == NULL) {
549b6955755SRobert Johnston 		if (b->fmb_error != 0)
550b6955755SRobert Johnston 			b->fmb_error = errno;
551b6955755SRobert Johnston 	} else {
552b6955755SRobert Johnston 		fmd_msg_buf_write(b, w, wcslen(w));
553b6955755SRobert Johnston 		free(w);
554b6955755SRobert Johnston 	}
555b6955755SRobert Johnston }
556b6955755SRobert Johnston 
557b6955755SRobert Johnston /*PRINTFLIKE1*/
558b6955755SRobert Johnston static int
fmd_msg_nv_error(const char * format,...)559b6955755SRobert Johnston fmd_msg_nv_error(const char *format, ...)
560b6955755SRobert Johnston {
561b6955755SRobert Johnston 	int err = errno;
562b6955755SRobert Johnston 	va_list ap;
563b6955755SRobert Johnston 
564b6955755SRobert Johnston 	if (getenv("FMD_MSG_DEBUG") == NULL)
565b6955755SRobert Johnston 		return (1);
566b6955755SRobert Johnston 
567b6955755SRobert Johnston 	(void) fprintf(stderr, "libfmd_msg DEBUG: ");
568b6955755SRobert Johnston 	va_start(ap, format);
569b6955755SRobert Johnston 	(void) vfprintf(stderr, format, ap);
570b6955755SRobert Johnston 	va_end(ap);
571b6955755SRobert Johnston 
572b6955755SRobert Johnston 	if (strchr(format, '\n') == NULL)
573b6955755SRobert Johnston 		(void) fprintf(stderr, ": %s\n", strerror(err));
574b6955755SRobert Johnston 
575b6955755SRobert Johnston 	return (1);
576b6955755SRobert Johnston }
577b6955755SRobert Johnston 
578b6955755SRobert Johnston static const struct fmd_msg_nv_type *
fmd_msg_nv_type_lookup(data_type_t type)579b6955755SRobert Johnston fmd_msg_nv_type_lookup(data_type_t type)
580b6955755SRobert Johnston {
581b6955755SRobert Johnston 	const struct fmd_msg_nv_type *t;
582b6955755SRobert Johnston 
583b6955755SRobert Johnston 	for (t = fmd_msg_nv_types; t->nvt_type != DATA_TYPE_UNKNOWN; t++) {
584b6955755SRobert Johnston 		if (t->nvt_type == type)
585b6955755SRobert Johnston 			break;
586b6955755SRobert Johnston 	}
587b6955755SRobert Johnston 
588b6955755SRobert Johnston 	return (t);
589b6955755SRobert Johnston }
590b6955755SRobert Johnston 
591b6955755SRobert Johnston /*
592b6955755SRobert Johnston  * Print the specified string, escaping any unprintable character sequences
593b6955755SRobert Johnston  * using the ISO C character escape sequences.
594b6955755SRobert Johnston  */
595b6955755SRobert Johnston static void
fmd_msg_nv_print_string(fmd_msg_buf_t * b,const char * s)596b6955755SRobert Johnston fmd_msg_nv_print_string(fmd_msg_buf_t *b, const char *s)
597b6955755SRobert Johnston {
598b6955755SRobert Johnston 	char c;
599b6955755SRobert Johnston 
600b6955755SRobert Johnston 	while ((c = *s++) != '\0') {
601b6955755SRobert Johnston 		if (c >= ' ' && c <= '~' && c != '\'') {
602b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%c", c);
603b6955755SRobert Johnston 			continue;
604b6955755SRobert Johnston 		}
605b6955755SRobert Johnston 
606b6955755SRobert Johnston 		switch (c) {
607b6955755SRobert Johnston 		case '\0':
608b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\0");
609b6955755SRobert Johnston 			break;
610b6955755SRobert Johnston 		case '\a':
611b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\a");
612b6955755SRobert Johnston 			break;
613b6955755SRobert Johnston 		case '\b':
614b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\b");
615b6955755SRobert Johnston 			break;
616b6955755SRobert Johnston 		case '\f':
617b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\f");
618b6955755SRobert Johnston 			break;
619b6955755SRobert Johnston 		case '\n':
620b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\n");
621b6955755SRobert Johnston 			break;
622b6955755SRobert Johnston 		case '\r':
623b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\r");
624b6955755SRobert Johnston 			break;
625b6955755SRobert Johnston 		case '\t':
626b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\t");
627b6955755SRobert Johnston 			break;
628b6955755SRobert Johnston 		case '\v':
629b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\v");
630b6955755SRobert Johnston 			break;
631b6955755SRobert Johnston 		case '\'':
632b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\'");
633b6955755SRobert Johnston 			break;
634b6955755SRobert Johnston 		case '"':
635b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\\"");
636b6955755SRobert Johnston 			break;
637b6955755SRobert Johnston 		case '\\':
638b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\\\");
639b6955755SRobert Johnston 			break;
640b6955755SRobert Johnston 		default:
641b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "\\x%02x", (uchar_t)c);
642b6955755SRobert Johnston 		}
643b6955755SRobert Johnston 	}
644b6955755SRobert Johnston }
645b6955755SRobert Johnston 
646b6955755SRobert Johnston /*
647b6955755SRobert Johnston  * Print the value of the specified nvpair into the supplied buffer.
648b6955755SRobert Johnston  *
649b6955755SRobert Johnston  * For nvpairs that are arrays types, passing -1 as the idx param indicates
650b6955755SRobert Johnston  * that we want to print all of the elements in the array.
651b6955755SRobert Johnston  *
652b6955755SRobert Johnston  * Returns 0 on success, 1 otherwise.
653b6955755SRobert Johnston  */
654b6955755SRobert Johnston 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)655b6955755SRobert Johnston fmd_msg_nv_print_items(fmd_msg_buf_t *b, nvpair_t *nvp,
656b6955755SRobert Johnston     data_type_t type, void *p, uint_t n, uint_t idx)
657b6955755SRobert Johnston {
658b6955755SRobert Johnston 	const struct fmd_msg_nv_type *nvt = fmd_msg_nv_type_lookup(type);
659b6955755SRobert Johnston 	uint_t i;
660b6955755SRobert Johnston 
661b6955755SRobert Johnston 	if (idx != -1u) {
662b6955755SRobert Johnston 		if (idx >= n) {
663b6955755SRobert Johnston 			return (fmd_msg_nv_error("index %u out-of-range for "
664b6955755SRobert Johnston 			    "array %s: valid range is [0 .. %u]\n",
665b6955755SRobert Johnston 			    idx, nvpair_name(nvp), n ? n - 1 : 0));
666b6955755SRobert Johnston 		}
667b6955755SRobert Johnston 		p = (uchar_t *)p + nvt->nvt_size * idx;
668b6955755SRobert Johnston 		n = 1;
669b6955755SRobert Johnston 	}
670b6955755SRobert Johnston 
671b6955755SRobert Johnston 	for (i = 0; i < n; i++, p = (uchar_t *)p + nvt->nvt_size) {
672b6955755SRobert Johnston 		if (i > 0)
673b6955755SRobert Johnston 			fmd_msg_buf_printf(b, " "); /* array item delimiter */
674b6955755SRobert Johnston 
675b6955755SRobert Johnston 		switch (type) {
676b6955755SRobert Johnston 		case DATA_TYPE_INT8:
677b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%d", *(int8_t *)p);
678b6955755SRobert Johnston 			break;
679b6955755SRobert Johnston 
680b6955755SRobert Johnston 		case DATA_TYPE_INT16:
681b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%d", *(int16_t *)p);
682b6955755SRobert Johnston 			break;
683b6955755SRobert Johnston 
684b6955755SRobert Johnston 		case DATA_TYPE_INT32:
685b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%d", *(int32_t *)p);
686b6955755SRobert Johnston 			break;
687b6955755SRobert Johnston 
688b6955755SRobert Johnston 		case DATA_TYPE_INT64:
689b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%lld", *(longlong_t *)p);
690b6955755SRobert Johnston 			break;
691b6955755SRobert Johnston 
692b6955755SRobert Johnston 		case DATA_TYPE_UINT8:
693b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%u", *(uint8_t *)p);
694b6955755SRobert Johnston 			break;
695b6955755SRobert Johnston 
696b6955755SRobert Johnston 		case DATA_TYPE_UINT16:
697b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%u", *(uint16_t *)p);
698b6955755SRobert Johnston 			break;
699b6955755SRobert Johnston 
700b6955755SRobert Johnston 		case DATA_TYPE_UINT32:
701b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%u", *(uint32_t *)p);
702b6955755SRobert Johnston 			break;
703b6955755SRobert Johnston 
704b6955755SRobert Johnston 		case DATA_TYPE_UINT64:
705b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%llu", *(u_longlong_t *)p);
706b6955755SRobert Johnston 			break;
707b6955755SRobert Johnston 
708b6955755SRobert Johnston 		case DATA_TYPE_BYTE:
709b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "0x%x", *(uchar_t *)p);
710b6955755SRobert Johnston 			break;
711b6955755SRobert Johnston 
712b6955755SRobert Johnston 		case DATA_TYPE_BOOLEAN_VALUE:
713b6955755SRobert Johnston 			fmd_msg_buf_printf(b,
714b6955755SRobert Johnston 			    *(boolean_t *)p ? "true" : "false");
715b6955755SRobert Johnston 			break;
716b6955755SRobert Johnston 
717b6955755SRobert Johnston 		case DATA_TYPE_HRTIME:
718b6955755SRobert Johnston 			fmd_msg_buf_printf(b, "%lld", *(longlong_t *)p);
719b6955755SRobert Johnston 			break;
720b6955755SRobert Johnston 
721b6955755SRobert Johnston 		case DATA_TYPE_STRING:
722b6955755SRobert Johnston 			fmd_msg_nv_print_string(b, *(char **)p);
723b6955755SRobert Johnston 			break;
724b6955755SRobert Johnston 		}
725b6955755SRobert Johnston 	}
726b6955755SRobert Johnston 
727b6955755SRobert Johnston 	return (0);
728b6955755SRobert Johnston }
729b6955755SRobert Johnston 
730b6955755SRobert Johnston /*
731b6955755SRobert Johnston  * Writes the value of the specified nvpair to the supplied buffer.
732b6955755SRobert Johnston  *
733b6955755SRobert Johnston  * Returns 0 on success, 1 otherwise.
734b6955755SRobert Johnston  */
735b6955755SRobert Johnston static int
fmd_msg_nv_print_nvpair(fmd_msg_buf_t * b,nvpair_t * nvp,uint_t idx)736b6955755SRobert Johnston fmd_msg_nv_print_nvpair(fmd_msg_buf_t *b, nvpair_t *nvp, uint_t idx)
737b6955755SRobert Johnston {
738b6955755SRobert Johnston 	data_type_t type = nvpair_type(nvp);
739b6955755SRobert Johnston 	const struct fmd_msg_nv_type *nvt = fmd_msg_nv_type_lookup(type);
740b6955755SRobert Johnston 
741b6955755SRobert Johnston 	uint64_t v;
742b6955755SRobert Johnston 	void *a;
743b6955755SRobert Johnston 	uint_t n;
744b6955755SRobert Johnston 	int err;
745b6955755SRobert Johnston 
746b6955755SRobert Johnston 	if (nvt->nvt_type == DATA_TYPE_BOOLEAN) {
747b6955755SRobert Johnston 		fmd_msg_buf_printf(b, "true");
748b6955755SRobert Johnston 		err = 0;
749b6955755SRobert Johnston 	} else if (nvt->nvt_array != NULL) {
750b6955755SRobert Johnston 		(void) nvt->nvt_array(nvp, &a, &n);
751b6955755SRobert Johnston 		err = fmd_msg_nv_print_items(b, nvp, nvt->nvt_base, a, n, idx);
752b6955755SRobert Johnston 	} else if (nvt->nvt_value != NULL) {
753b6955755SRobert Johnston 		(void) nvt->nvt_value(nvp, &v);
754b6955755SRobert Johnston 		err = fmd_msg_nv_print_items(b, nvp, nvt->nvt_base, &v, 1, idx);
755b6955755SRobert Johnston 	} else {
756b6955755SRobert Johnston 		err = fmd_msg_nv_error("unknown data type %u", type);
757b6955755SRobert Johnston 	}
758b6955755SRobert Johnston 
759b6955755SRobert Johnston 	return (err);
760b6955755SRobert Johnston }
761b6955755SRobert Johnston 
762b6955755SRobert Johnston /*
763b6955755SRobert Johnston  * Consume a token from the specified string, fill in the specified token
764b6955755SRobert Johnston  * struct, and return the new string position from which to continue parsing.
765b6955755SRobert Johnston  */
766b6955755SRobert Johnston static char *
fmd_msg_nv_parse_token(char * s,fmd_msg_nv_token_t * tp)767b6955755SRobert Johnston fmd_msg_nv_parse_token(char *s, fmd_msg_nv_token_t *tp)
768b6955755SRobert Johnston {
769b6955755SRobert Johnston 	char *p = s, *q, c = *s;
770b6955755SRobert Johnston 
771b6955755SRobert Johnston 	/*
772b6955755SRobert Johnston 	 * Skip whitespace and then look for an integer token first.  We can't
773b6955755SRobert Johnston 	 * use isspace() or isdigit() because we're in setlocale() context now.
774b6955755SRobert Johnston 	 */
775b6955755SRobert Johnston 	while (c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r')
776b6955755SRobert Johnston 		c = *++p;
777b6955755SRobert Johnston 
778b6955755SRobert Johnston 	if (c >= '0' && c <= '9') {
779b6955755SRobert Johnston 		errno = 0;
780b6955755SRobert Johnston 		tp->t_data.tu_int = strtoul(p, &q, 0);
781b6955755SRobert Johnston 
782b6955755SRobert Johnston 		if (errno != 0 || p == q) {
783b6955755SRobert Johnston 			tp->t_kind = T_ERR;
784b6955755SRobert Johnston 			return (p);
785b6955755SRobert Johnston 		}
786b6955755SRobert Johnston 
787b6955755SRobert Johnston 		tp->t_kind = T_INT;
788b6955755SRobert Johnston 		return (q);
789b6955755SRobert Johnston 	}
790b6955755SRobert Johnston 
791b6955755SRobert Johnston 	/*
792b6955755SRobert Johnston 	 * Look for a name-value pair identifier, which we define to be the
793b6955755SRobert Johnston 	 * regular expression [a-zA-Z_][a-zA-Z0-9_-]*  (NOTE: Ideally "-" would
794b6955755SRobert Johnston 	 * not be allowed here and we would require ISO C identifiers, but many
795b6955755SRobert Johnston 	 * FMA event members use hyphens.)  This code specifically cannot use
796b6955755SRobert Johnston 	 * the isspace(), isalnum() etc. macros because we are currently in the
797b6955755SRobert Johnston 	 * context of an earlier call to setlocale() that may have installed a
798b6955755SRobert Johnston 	 * non-C locale, but this code needs to always operate on C characters.
799b6955755SRobert Johnston 	 */
800b6955755SRobert Johnston 	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
801b6955755SRobert Johnston 		for (q = p + 1; (c = *q) != '\0'; q++) {
802b6955755SRobert Johnston 			if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') &&
803b6955755SRobert Johnston 			    (c < '0' || c > '9') && (c != '_' && c != '-'))
804b6955755SRobert Johnston 				break;
805b6955755SRobert Johnston 		}
806b6955755SRobert Johnston 
807b6955755SRobert Johnston 		if (sizeof (tp->t_data.tu_str) <= (size_t)(q - p)) {
808b6955755SRobert Johnston 			tp->t_kind = T_ERR;
809b6955755SRobert Johnston 			return (p);
810b6955755SRobert Johnston 		}
811b6955755SRobert Johnston 
812b6955755SRobert Johnston 		bcopy(p, tp->t_data.tu_str, (size_t)(q - p));
813b6955755SRobert Johnston 		tp->t_data.tu_str[(size_t)(q - p)] = '\0';
814b6955755SRobert Johnston 		tp->t_kind = T_IDENT;
815b6955755SRobert Johnston 		return (q);
816b6955755SRobert Johnston 	}
817b6955755SRobert Johnston 
818b6955755SRobert Johnston 	switch (c) {
819b6955755SRobert Johnston 	case '\0':
820b6955755SRobert Johnston 		tp->t_kind = T_EOF;
821b6955755SRobert Johnston 		return (p);
822b6955755SRobert Johnston 	case '.':
823b6955755SRobert Johnston 		tp->t_kind = T_DOT;
824b6955755SRobert Johnston 		return (p + 1);
825b6955755SRobert Johnston 	case '[':
826b6955755SRobert Johnston 		tp->t_kind = T_LBRAC;
827b6955755SRobert Johnston 		return (p + 1);
828b6955755SRobert Johnston 	case ']':
829b6955755SRobert Johnston 		tp->t_kind = T_RBRAC;
830b6955755SRobert Johnston 		return (p + 1);
831b6955755SRobert Johnston 	default:
832b6955755SRobert Johnston 		tp->t_kind = T_ERR;
833b6955755SRobert Johnston 		return (p);
834b6955755SRobert Johnston 	}
835b6955755SRobert Johnston }
836b6955755SRobert Johnston 
837b6955755SRobert Johnston static int
fmd_msg_nv_parse_error(const char * s,fmd_msg_nv_token_t * tp)838b6955755SRobert Johnston fmd_msg_nv_parse_error(const char *s, fmd_msg_nv_token_t *tp)
839b6955755SRobert Johnston {
840b6955755SRobert Johnston 	if (tp->t_kind == T_ERR)
841b6955755SRobert Johnston 		return (fmd_msg_nv_error("illegal character at \"%s\"\n", s));
842b6955755SRobert Johnston 	else
843b6955755SRobert Johnston 		return (fmd_msg_nv_error("syntax error near \"%s\"\n", s));
844b6955755SRobert Johnston }
845b6955755SRobert Johnston 
846b6955755SRobert Johnston /*
847b6955755SRobert Johnston  * Parse an array expression for referencing an element of the specified
848b6955755SRobert Johnston  * nvpair_t, which is expected to be of an array type.  If it's an array of
849b6955755SRobert Johnston  * intrinsics, print the specified value.  If it's an array of nvlist_t's,
850b6955755SRobert Johnston  * call fmd_msg_nv_parse_nvlist() recursively to continue parsing.
851b6955755SRobert Johnston  */
852b6955755SRobert Johnston static int
fmd_msg_nv_parse_array(fmd_msg_buf_t * b,nvpair_t * nvp,char * s1)853b6955755SRobert Johnston fmd_msg_nv_parse_array(fmd_msg_buf_t *b, nvpair_t *nvp, char *s1)
854b6955755SRobert Johnston {
855b6955755SRobert Johnston 	fmd_msg_nv_token_t t;
856b6955755SRobert Johnston 	nvlist_t **nva;
857b6955755SRobert Johnston 	uint_t i, n;
858b6955755SRobert Johnston 	char *s2;
859b6955755SRobert Johnston 
860b6955755SRobert Johnston 	if (fmd_msg_nv_type_lookup(nvpair_type(nvp))->nvt_array == NULL) {
861b6955755SRobert Johnston 		return (fmd_msg_nv_error("inappropriate use of operator [ ]: "
862b6955755SRobert Johnston 		    "element '%s' is not an array\n", nvpair_name(nvp)));
863b6955755SRobert Johnston 	}
864b6955755SRobert Johnston 
865b6955755SRobert Johnston 	s2 = fmd_msg_nv_parse_token(s1, &t);
866b6955755SRobert Johnston 	i = t.t_data.tu_int;
867b6955755SRobert Johnston 
868b6955755SRobert Johnston 	if (t.t_kind != T_INT)
869b6955755SRobert Johnston 		return (fmd_msg_nv_error("expected integer index after [\n"));
870b6955755SRobert Johnston 
871b6955755SRobert Johnston 	s2 = fmd_msg_nv_parse_token(s2, &t);
872b6955755SRobert Johnston 
873b6955755SRobert Johnston 	if (t.t_kind != T_RBRAC)
874b6955755SRobert Johnston 		return (fmd_msg_nv_error("expected ] after [ %u\n", i));
875b6955755SRobert Johnston 
876b6955755SRobert Johnston 	/*
877b6955755SRobert Johnston 	 * An array of nvlist is different from other array types in that it
878b6955755SRobert Johnston 	 * permits us to continue parsing instead of printing a terminal node.
879b6955755SRobert Johnston 	 */
880b6955755SRobert Johnston 	if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) {
881b6955755SRobert Johnston 		(void) nvpair_value_nvlist_array(nvp, &nva, &n);
882b6955755SRobert Johnston 
883b6955755SRobert Johnston 		if (i >= n) {
884b6955755SRobert Johnston 			return (fmd_msg_nv_error("index %u out-of-range for "
885b6955755SRobert Johnston 			    "array %s: valid range is [0 .. %u]\n",
886b6955755SRobert Johnston 			    i, nvpair_name(nvp), n ? n - 1 : 0));
887b6955755SRobert Johnston 		}
888b6955755SRobert Johnston 
889b6955755SRobert Johnston 		return (fmd_msg_nv_parse_nvlist(b, nva[i], s2));
890b6955755SRobert Johnston 	}
891b6955755SRobert Johnston 
892b6955755SRobert Johnston 	(void) fmd_msg_nv_parse_token(s2, &t);
893b6955755SRobert Johnston 
894b6955755SRobert Johnston 	if (t.t_kind != T_EOF) {
895b6955755SRobert Johnston 		return (fmd_msg_nv_error("expected end-of-string "
896b6955755SRobert Johnston 		    "in expression instead of \"%s\"\n", s2));
897b6955755SRobert Johnston 	}
898b6955755SRobert Johnston 
899b6955755SRobert Johnston 	return (fmd_msg_nv_print_nvpair(b, nvp, i));
900b6955755SRobert Johnston }
901b6955755SRobert Johnston 
902b6955755SRobert Johnston /*
903b6955755SRobert Johnston  * Parse an expression rooted at an nvpair_t.  If we see EOF, print the entire
904b6955755SRobert Johnston  * nvpair.  If we see LBRAC, parse an array expression.  If we see DOT, call
905b6955755SRobert Johnston  * fmd_msg_nv_parse_nvname() recursively to dereference an embedded member.
906b6955755SRobert Johnston  */
907b6955755SRobert Johnston static int
fmd_msg_nv_parse_nvpair(fmd_msg_buf_t * b,nvpair_t * nvp,char * s1)908b6955755SRobert Johnston fmd_msg_nv_parse_nvpair(fmd_msg_buf_t *b, nvpair_t *nvp, char *s1)
909b6955755SRobert Johnston {
910b6955755SRobert Johnston 	fmd_msg_nv_token_t t;
911b6955755SRobert Johnston 	nvlist_t *nvl;
912b6955755SRobert Johnston 	char *s2;
913b6955755SRobert Johnston 
914b6955755SRobert Johnston 	s2 = fmd_msg_nv_parse_token(s1, &t);
915b6955755SRobert Johnston 
916b6955755SRobert Johnston 	if (t.t_kind == T_EOF)
917b6955755SRobert Johnston 		return (fmd_msg_nv_print_nvpair(b, nvp, -1));
918b6955755SRobert Johnston 
919b6955755SRobert Johnston 	if (t.t_kind == T_LBRAC)
920b6955755SRobert Johnston 		return (fmd_msg_nv_parse_array(b, nvp, s2));
921b6955755SRobert Johnston 
922b6955755SRobert Johnston 	if (t.t_kind != T_DOT)
923b6955755SRobert Johnston 		return (fmd_msg_nv_parse_error(s1, &t));
924b6955755SRobert Johnston 
925b6955755SRobert Johnston 	if (nvpair_type(nvp) != DATA_TYPE_NVLIST) {
926b6955755SRobert Johnston 		return (fmd_msg_nv_error("inappropriate use of operator '.': "
927b6955755SRobert Johnston 		    "element '%s' is not of type nvlist\n", nvpair_name(nvp)));
928b6955755SRobert Johnston 	}
929b6955755SRobert Johnston 
930b6955755SRobert Johnston 	(void) nvpair_value_nvlist(nvp, &nvl);
931b6955755SRobert Johnston 	return (fmd_msg_nv_parse_nvname(b, nvl, s2));
932b6955755SRobert Johnston }
933b6955755SRobert Johnston 
934b6955755SRobert Johnston /*
935b6955755SRobert Johnston  * Parse an expression for a name-value pair name (IDENT).  If we find a match
936b6955755SRobert Johnston  * continue parsing with the corresponding nvpair_t.
937b6955755SRobert Johnston  */
938b6955755SRobert Johnston static int
fmd_msg_nv_parse_nvname(fmd_msg_buf_t * b,nvlist_t * nvl,char * s1)939b6955755SRobert Johnston fmd_msg_nv_parse_nvname(fmd_msg_buf_t *b, nvlist_t *nvl, char *s1)
940b6955755SRobert Johnston {
941b6955755SRobert Johnston 	nvpair_t *nvp = NULL;
942b6955755SRobert Johnston 	fmd_msg_nv_token_t t;
943b6955755SRobert Johnston 	char *s2;
944b6955755SRobert Johnston 
945b6955755SRobert Johnston 	s2 = fmd_msg_nv_parse_token(s1, &t);
946b6955755SRobert Johnston 
947b6955755SRobert Johnston 	if (t.t_kind != T_IDENT)
948b6955755SRobert Johnston 		return (fmd_msg_nv_parse_error(s1, &t));
949b6955755SRobert Johnston 
950b6955755SRobert Johnston 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
951b6955755SRobert Johnston 		if (strcmp(nvpair_name(nvp), t.t_data.tu_str) == 0)
952b6955755SRobert Johnston 			break;
953b6955755SRobert Johnston 	}
954b6955755SRobert Johnston 
955b6955755SRobert Johnston 	if (nvp == NULL) {
956b6955755SRobert Johnston 		return (fmd_msg_nv_error("no such name-value pair "
957b6955755SRobert Johnston 		    "member: %s\n", t.t_data.tu_str));
958b6955755SRobert Johnston 	}
959b6955755SRobert Johnston 
960b6955755SRobert Johnston 	return (fmd_msg_nv_parse_nvpair(b, nvp, s2));
961b6955755SRobert Johnston }
962b6955755SRobert Johnston 
963b6955755SRobert Johnston /*
964b6955755SRobert Johnston  * Parse an expression rooted at an nvlist: if we see EOF, print nothing.
965b6955755SRobert Johnston  * If we see DOT, continue parsing to retrieve a name-value pair name.
966b6955755SRobert Johnston  */
967b6955755SRobert Johnston static int
fmd_msg_nv_parse_nvlist(fmd_msg_buf_t * b,nvlist_t * nvl,char * s1)968b6955755SRobert Johnston fmd_msg_nv_parse_nvlist(fmd_msg_buf_t *b, nvlist_t *nvl, char *s1)
969b6955755SRobert Johnston {
970b6955755SRobert Johnston 	fmd_msg_nv_token_t t;
971b6955755SRobert Johnston 	char *s2;
972b6955755SRobert Johnston 
973b6955755SRobert Johnston 	s2 = fmd_msg_nv_parse_token(s1, &t);
974b6955755SRobert Johnston 
975b6955755SRobert Johnston 	if (t.t_kind == T_EOF)
976b6955755SRobert Johnston 		return (0);
977b6955755SRobert Johnston 
978b6955755SRobert Johnston 	if (t.t_kind == T_DOT)
979b6955755SRobert Johnston 		return (fmd_msg_nv_parse_nvname(b, nvl, s2));
980b6955755SRobert Johnston 
981b6955755SRobert Johnston 	return (fmd_msg_nv_parse_error(s1, &t));
982b6955755SRobert Johnston }
983b6955755SRobert Johnston 
984b6955755SRobert Johnston /*
985b6955755SRobert Johnston  * This function is the main engine for formatting an event message item, such
986b6955755SRobert Johnston  * as the Description field.  It loads the item text from a message object,
987b6955755SRobert Johnston  * expands any variables defined in the item text, and then returns a newly-
988b6955755SRobert Johnston  * allocated multi-byte string with the localized message text, or NULL with
989b6955755SRobert Johnston  * errno set if an error occurred.
990b6955755SRobert Johnston  */
991b6955755SRobert Johnston 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)992b6955755SRobert Johnston fmd_msg_getitem_locked(fmd_msg_hdl_t *h,
993b6955755SRobert Johnston     nvlist_t *nvl, const char *dict, const char *code, fmd_msg_item_t item)
994b6955755SRobert Johnston {
995b6955755SRobert Johnston 	const char *istr = fmd_msg_items[item];
996b6955755SRobert Johnston 	size_t len = strlen(code) + 1 + strlen(istr) + 1;
997b6955755SRobert Johnston 	char *key = alloca(len);
998b6955755SRobert Johnston 
999b6955755SRobert Johnston 	fmd_msg_buf_t buf;
1000b6955755SRobert Johnston 	wchar_t *c, *u, *w, *p, *q;
1001b6955755SRobert Johnston 
1002b6955755SRobert Johnston 	const char *url, *txt;
1003b6955755SRobert Johnston 	char *s, *expr;
1004b6955755SRobert Johnston 	size_t elen;
1005b6955755SRobert Johnston 	int i;
1006b6955755SRobert Johnston 
1007b6955755SRobert Johnston 	assert(fmd_msg_lock_held(h));
1008b6955755SRobert Johnston 
1009b6955755SRobert Johnston 	/*
1010b6955755SRobert Johnston 	 * If <dict>.mo defines an item with the key <FMD_MSG_URLKEY> then it
1011b6955755SRobert Johnston 	 * is used as the URL; otherwise the default from our handle is used.
1012b6955755SRobert Johnston 	 * Once we have the multi-byte URL, convert it to wide-character form.
1013b6955755SRobert Johnston 	 */
1014b6955755SRobert Johnston 	if ((url = dgettext(dict, FMD_MSG_URLKEY)) == FMD_MSG_URLKEY)
1015b6955755SRobert Johnston 		url = h->fmh_urlbase;
1016b6955755SRobert Johnston 
1017b6955755SRobert Johnston 	/*
1018b6955755SRobert Johnston 	 * If the item is FMD_MSG_ITEM_URL, then its value is directly computed
1019b6955755SRobert Johnston 	 * as the URL base concatenated with the code.  Otherwise the item text
1020b6955755SRobert Johnston 	 * is derived by looking up the key <code>.<istr> in the dict object.
1021b6955755SRobert Johnston 	 * Once we're done, convert the 'txt' multi-byte to wide-character.
1022b6955755SRobert Johnston 	 */
1023b6955755SRobert Johnston 	if (item == FMD_MSG_ITEM_URL) {
1024b6955755SRobert Johnston 		len = strlen(url) + strlen(code) + 1;
1025b6955755SRobert Johnston 		key = alloca(len);
1026b6955755SRobert Johnston 		(void) snprintf(key, len, "%s%s", url, code);
1027b6955755SRobert Johnston 		txt = key;
1028b6955755SRobert Johnston 	} else {
1029b6955755SRobert Johnston 		len = strlen(code) + 1 + strlen(istr) + 1;
1030b6955755SRobert Johnston 		key = alloca(len);
1031b6955755SRobert Johnston 		(void) snprintf(key, len, "%s.%s", code, istr);
1032b6955755SRobert Johnston 		txt = dgettext(dict, key);
1033b6955755SRobert Johnston 	}
1034b6955755SRobert Johnston 
1035b6955755SRobert Johnston 	c = fmd_msg_mbstowcs(code);
1036b6955755SRobert Johnston 	u = fmd_msg_mbstowcs(url);
1037b6955755SRobert Johnston 	w = fmd_msg_mbstowcs(txt);
1038b6955755SRobert Johnston 
1039b6955755SRobert Johnston 	if (c == NULL || u == NULL || w == NULL) {
1040b6955755SRobert Johnston 		free(c);
1041b6955755SRobert Johnston 		free(u);
1042b6955755SRobert Johnston 		free(w);
1043b6955755SRobert Johnston 		return (NULL);
1044b6955755SRobert Johnston 	}
1045b6955755SRobert Johnston 
1046b6955755SRobert Johnston 	/*
1047b6955755SRobert Johnston 	 * Now expand any escape sequences in the string, storing the final
1048b6955755SRobert Johnston 	 * text in 'buf' in wide-character format, and then convert it back
1049b6955755SRobert Johnston 	 * to multi-byte for return.  We expand the following sequences:
1050b6955755SRobert Johnston 	 *
1051b6955755SRobert Johnston 	 * %%   - literal % character
1052b6955755SRobert Johnston 	 * %s   - base URL for knowledge articles
1053b6955755SRobert Johnston 	 * %<x> - expression x in the current event, if any
1054b6955755SRobert Johnston 	 *
1055b6955755SRobert Johnston 	 * If an invalid sequence is present, it is elided so we can safely
1056b6955755SRobert Johnston 	 * reserve any future characters for other types of expansions.
1057b6955755SRobert Johnston 	 */
1058b6955755SRobert Johnston 	fmd_msg_buf_init(&buf);
1059b6955755SRobert Johnston 
1060b6955755SRobert Johnston 	for (q = w, p = w; (p = wcschr(p, L'%')) != NULL; q = p) {
1061b6955755SRobert Johnston 		if (p > q)
1062b6955755SRobert Johnston 			fmd_msg_buf_write(&buf, q, (size_t)(p - q));
1063b6955755SRobert Johnston 
1064b6955755SRobert Johnston 		switch (p[1]) {
1065b6955755SRobert Johnston 		case L'%':
1066b6955755SRobert Johnston 			fmd_msg_buf_write(&buf, p, 1);
1067b6955755SRobert Johnston 			p += 2;
1068b6955755SRobert Johnston 			break;
1069b6955755SRobert Johnston 
1070b6955755SRobert Johnston 		case L's':
1071b6955755SRobert Johnston 			fmd_msg_buf_write(&buf, u, wcslen(u));
1072b6955755SRobert Johnston 			fmd_msg_buf_write(&buf, c, wcslen(c));
1073b6955755SRobert Johnston 
1074b6955755SRobert Johnston 			p += 2;
1075b6955755SRobert Johnston 			break;
1076b6955755SRobert Johnston 
1077b6955755SRobert Johnston 		case L'<':
1078b6955755SRobert Johnston 			q = p + 2;
1079b6955755SRobert Johnston 			p = wcschr(p + 2, L'>');
1080b6955755SRobert Johnston 
1081b6955755SRobert Johnston 			if (p == NULL)
1082b6955755SRobert Johnston 				goto eos;
1083b6955755SRobert Johnston 
1084b6955755SRobert Johnston 			/*
1085b6955755SRobert Johnston 			 * The expression in %< > must be an ASCII string: as
1086b6955755SRobert Johnston 			 * such allocate its length in bytes plus an extra
1087b6955755SRobert Johnston 			 * MB_CUR_MAX for slop if a multi-byte character is in
1088b6955755SRobert Johnston 			 * there, plus another byte for \0.  Since we move a
1089b6955755SRobert Johnston 			 * byte at a time, any multi-byte chars will just be
1090b6955755SRobert Johnston 			 * silently overwritten and fail to parse, which is ok.
1091b6955755SRobert Johnston 			 */
1092b6955755SRobert Johnston 			elen = (size_t)(p - q);
1093b6955755SRobert Johnston 			expr = malloc(elen + MB_CUR_MAX + 1);
1094b6955755SRobert Johnston 
1095b6955755SRobert Johnston 			if (expr == NULL) {
1096b6955755SRobert Johnston 				buf.fmb_error = ENOMEM;
1097b6955755SRobert Johnston 				goto eos;
1098b6955755SRobert Johnston 			}
1099b6955755SRobert Johnston 
1100b6955755SRobert Johnston 			for (i = 0; i < elen; i++)
1101b6955755SRobert Johnston 				(void) wctomb(&expr[i], q[i]);
1102b6955755SRobert Johnston 
1103b6955755SRobert Johnston 			expr[i] = '\0';
1104b6955755SRobert Johnston 
1105b6955755SRobert Johnston 			if (nvl != NULL)
1106b6955755SRobert Johnston 				(void) fmd_msg_nv_parse_nvname(&buf, nvl, expr);
1107b6955755SRobert Johnston 			else
1108b6955755SRobert Johnston 				fmd_msg_buf_printf(&buf, "%%<%s>", expr);
1109b6955755SRobert Johnston 
1110b6955755SRobert Johnston 			free(expr);
1111b6955755SRobert Johnston 			p++;
1112b6955755SRobert Johnston 			break;
1113b6955755SRobert Johnston 
1114b6955755SRobert Johnston 		case L'\0':
1115b6955755SRobert Johnston 			goto eos;
1116b6955755SRobert Johnston 
1117b6955755SRobert Johnston 		default:
1118b6955755SRobert Johnston 			p += 2;
1119b6955755SRobert Johnston 			break;
1120b6955755SRobert Johnston 		}
1121b6955755SRobert Johnston 	}
1122b6955755SRobert Johnston eos:
1123b6955755SRobert Johnston 	fmd_msg_buf_write(&buf, q, wcslen(q) + 1);
1124b6955755SRobert Johnston 
1125b6955755SRobert Johnston 	free(c);
1126b6955755SRobert Johnston 	free(u);
1127b6955755SRobert Johnston 	free(w);
1128b6955755SRobert Johnston 
1129b6955755SRobert Johnston 	s = fmd_msg_buf_read(&buf);
1130b6955755SRobert Johnston 	fmd_msg_buf_fini(&buf);
1131b6955755SRobert Johnston 
1132b6955755SRobert Johnston 	return (s);
1133b6955755SRobert Johnston }
1134b6955755SRobert Johnston 
1135f6e214c7SGavin Maltby /*
1136f6e214c7SGavin Maltby  * This is a private interface used by the notification daemons to parse tokens
1137f6e214c7SGavin Maltby  * in user-supplied message templates.
1138f6e214c7SGavin Maltby  */
1139f6e214c7SGavin Maltby char *
fmd_msg_decode_tokens(nvlist_t * nvl,const char * msg,const char * url)1140f6e214c7SGavin Maltby fmd_msg_decode_tokens(nvlist_t *nvl, const char *msg, const char *url)
1141f6e214c7SGavin Maltby {
1142f6e214c7SGavin Maltby 	fmd_msg_buf_t buf;
1143f6e214c7SGavin Maltby 	wchar_t *h, *u, *w, *p, *q;
1144f6e214c7SGavin Maltby 
1145f6e214c7SGavin Maltby 	char *s, *expr, host[MAXHOSTNAMELEN + 1];
1146f6e214c7SGavin Maltby 	size_t elen;
1147f6e214c7SGavin Maltby 	int i;
1148f6e214c7SGavin Maltby 
1149f6e214c7SGavin Maltby 	u = fmd_msg_mbstowcs(url);
1150f6e214c7SGavin Maltby 
1151f6e214c7SGavin Maltby 	(void) gethostname(host, MAXHOSTNAMELEN + 1);
1152f6e214c7SGavin Maltby 	h = fmd_msg_mbstowcs(host);
1153f6e214c7SGavin Maltby 
1154f6e214c7SGavin Maltby 	if ((w = fmd_msg_mbstowcs(msg)) == NULL)
1155f6e214c7SGavin Maltby 		return (NULL);
1156f6e214c7SGavin Maltby 
1157f6e214c7SGavin Maltby 	/*
1158f6e214c7SGavin Maltby 	 * Now expand any escape sequences in the string, storing the final
1159f6e214c7SGavin Maltby 	 * text in 'buf' in wide-character format, and then convert it back
1160f6e214c7SGavin Maltby 	 * to multi-byte for return.  We expand the following sequences:
1161f6e214c7SGavin Maltby 	 *
1162f6e214c7SGavin Maltby 	 * %%   - literal % character
1163f6e214c7SGavin Maltby 	 * %h   - hostname
1164f6e214c7SGavin Maltby 	 * %s   - base URL for knowledge articles
1165f6e214c7SGavin Maltby 	 * %<x> - expression x in the current event, if any
1166f6e214c7SGavin Maltby 	 *
1167f6e214c7SGavin Maltby 	 * If an invalid sequence is present, it is elided so we can safely
1168f6e214c7SGavin Maltby 	 * reserve any future characters for other types of expansions.
1169f6e214c7SGavin Maltby 	 */
1170f6e214c7SGavin Maltby 	fmd_msg_buf_init(&buf);
1171f6e214c7SGavin Maltby 
1172f6e214c7SGavin Maltby 	for (q = w, p = w; (p = wcschr(p, L'%')) != NULL; q = p) {
1173f6e214c7SGavin Maltby 		if (p > q)
1174f6e214c7SGavin Maltby 			fmd_msg_buf_write(&buf, q, (size_t)(p - q));
1175f6e214c7SGavin Maltby 
1176f6e214c7SGavin Maltby 		switch (p[1]) {
1177f6e214c7SGavin Maltby 		case L'%':
1178f6e214c7SGavin Maltby 			fmd_msg_buf_write(&buf, p, 1);
1179f6e214c7SGavin Maltby 			p += 2;
1180f6e214c7SGavin Maltby 			break;
1181f6e214c7SGavin Maltby 
1182f6e214c7SGavin Maltby 		case L'h':
1183f6e214c7SGavin Maltby 			if (h != NULL)
1184f6e214c7SGavin Maltby 				fmd_msg_buf_write(&buf, h, wcslen(h));
1185f6e214c7SGavin Maltby 
1186f6e214c7SGavin Maltby 			p += 2;
1187f6e214c7SGavin Maltby 			break;
1188f6e214c7SGavin Maltby 
1189f6e214c7SGavin Maltby 		case L's':
1190f6e214c7SGavin Maltby 			if (u != NULL)
1191f6e214c7SGavin Maltby 				fmd_msg_buf_write(&buf, u, wcslen(u));
1192f6e214c7SGavin Maltby 
1193f6e214c7SGavin Maltby 			p += 2;
1194f6e214c7SGavin Maltby 			break;
1195f6e214c7SGavin Maltby 
1196f6e214c7SGavin Maltby 		case L'<':
1197f6e214c7SGavin Maltby 			q = p + 2;
1198f6e214c7SGavin Maltby 			p = wcschr(p + 2, L'>');
1199f6e214c7SGavin Maltby 
1200f6e214c7SGavin Maltby 			if (p == NULL)
1201f6e214c7SGavin Maltby 				goto eos;
1202f6e214c7SGavin Maltby 
1203f6e214c7SGavin Maltby 			/*
1204f6e214c7SGavin Maltby 			 * The expression in %< > must be an ASCII string: as
1205f6e214c7SGavin Maltby 			 * such allocate its length in bytes plus an extra
1206f6e214c7SGavin Maltby 			 * MB_CUR_MAX for slop if a multi-byte character is in
1207f6e214c7SGavin Maltby 			 * there, plus another byte for \0.  Since we move a
1208f6e214c7SGavin Maltby 			 * byte at a time, any multi-byte chars will just be
1209f6e214c7SGavin Maltby 			 * silently overwritten and fail to parse, which is ok.
1210f6e214c7SGavin Maltby 			 */
1211f6e214c7SGavin Maltby 			elen = (size_t)(p - q);
1212f6e214c7SGavin Maltby 			expr = malloc(elen + MB_CUR_MAX + 1);
1213f6e214c7SGavin Maltby 
1214f6e214c7SGavin Maltby 			if (expr == NULL) {
1215f6e214c7SGavin Maltby 				buf.fmb_error = ENOMEM;
1216f6e214c7SGavin Maltby 				goto eos;
1217f6e214c7SGavin Maltby 			}
1218f6e214c7SGavin Maltby 
1219f6e214c7SGavin Maltby 			for (i = 0; i < elen; i++)
1220f6e214c7SGavin Maltby 				(void) wctomb(&expr[i], q[i]);
1221f6e214c7SGavin Maltby 
1222f6e214c7SGavin Maltby 			expr[i] = '\0';
1223f6e214c7SGavin Maltby 
1224f6e214c7SGavin Maltby 			if (nvl != NULL)
1225f6e214c7SGavin Maltby 				(void) fmd_msg_nv_parse_nvname(&buf, nvl, expr);
1226f6e214c7SGavin Maltby 			else
1227f6e214c7SGavin Maltby 				fmd_msg_buf_printf(&buf, "%%<%s>", expr);
1228f6e214c7SGavin Maltby 
1229f6e214c7SGavin Maltby 			free(expr);
1230f6e214c7SGavin Maltby 			p++;
1231f6e214c7SGavin Maltby 			break;
1232f6e214c7SGavin Maltby 
1233f6e214c7SGavin Maltby 		case L'\0':
1234f6e214c7SGavin Maltby 			goto eos;
1235f6e214c7SGavin Maltby 
1236f6e214c7SGavin Maltby 		default:
1237f6e214c7SGavin Maltby 			p += 2;
1238f6e214c7SGavin Maltby 			break;
1239f6e214c7SGavin Maltby 		}
1240f6e214c7SGavin Maltby 	}
1241f6e214c7SGavin Maltby eos:
1242f6e214c7SGavin Maltby 	fmd_msg_buf_write(&buf, q, wcslen(q) + 1);
1243f6e214c7SGavin Maltby 
1244f6e214c7SGavin Maltby 	free(h);
1245f6e214c7SGavin Maltby 	free(u);
1246f6e214c7SGavin Maltby 	free(w);
1247f6e214c7SGavin Maltby 
1248f6e214c7SGavin Maltby 	s = fmd_msg_buf_read(&buf);
1249f6e214c7SGavin Maltby 	fmd_msg_buf_fini(&buf);
1250f6e214c7SGavin Maltby 
1251f6e214c7SGavin Maltby 	return (s);
1252f6e214c7SGavin Maltby }
1253f6e214c7SGavin Maltby 
1254b6955755SRobert Johnston /*
1255b6955755SRobert Johnston  * This function is the main engine for formatting an entire event message.
1256b6955755SRobert Johnston  * It retrieves the master format string for an event, formats the individual
1257b6955755SRobert Johnston  * items, and then produces the final string composing all of the items.  The
1258b6955755SRobert Johnston  * result is a newly-allocated multi-byte string of the localized message
1259b6955755SRobert Johnston  * text, or NULL with errno set if an error occurred.
1260b6955755SRobert Johnston  */
1261b6955755SRobert Johnston static char *
fmd_msg_gettext_locked(fmd_msg_hdl_t * h,nvlist_t * nvl,const char * dict,const char * code)1262b6955755SRobert Johnston fmd_msg_gettext_locked(fmd_msg_hdl_t *h,
1263b6955755SRobert Johnston     nvlist_t *nvl, const char *dict, const char *code)
1264b6955755SRobert Johnston {
1265b6955755SRobert Johnston 	char *items[FMD_MSG_ITEM_MAX];
1266b6955755SRobert Johnston 	const char *format;
1267b6955755SRobert Johnston 	char *buf = NULL;
1268b6955755SRobert Johnston 	size_t len;
1269b6955755SRobert Johnston 	int i;
1270b6955755SRobert Johnston 
1271b6955755SRobert Johnston 	nvlist_t *fmri, *auth;
1272b6955755SRobert Johnston 	struct tm tm, *tmp;
1273b6955755SRobert Johnston 
1274b6955755SRobert Johnston 	int64_t *tv;
1275b6955755SRobert Johnston 	uint_t tn = 0;
1276b6955755SRobert Johnston 	time_t sec;
1277b6955755SRobert Johnston 	char date[64];
1278b6955755SRobert Johnston 
1279b6955755SRobert Johnston 	char *uuid, *src_name, *src_vers;
12809c94f155SCheng Sean Ye 	char *platform, *server, *csn;
1281b6955755SRobert Johnston 
1282b6955755SRobert Johnston 	assert(fmd_msg_lock_held(h));
1283b6955755SRobert Johnston 	bzero(items, sizeof (items));
1284b6955755SRobert Johnston 
1285b6955755SRobert Johnston 	for (i = 0; i < FMD_MSG_ITEM_MAX; i++) {
1286b6955755SRobert Johnston 		items[i] = fmd_msg_getitem_locked(h, nvl, dict, code, i);
1287b6955755SRobert Johnston 		if (items[i] == NULL)
1288b6955755SRobert Johnston 			goto out;
1289b6955755SRobert Johnston 	}
1290b6955755SRobert Johnston 
1291b6955755SRobert Johnston 	/*
1292b6955755SRobert Johnston 	 * If <dict>.mo defines an item with the key <FMD_MSG_TEMPLATE> then it
1293b6955755SRobert Johnston 	 * is used as the format; otherwise the default from FMD.mo is used.
1294b6955755SRobert Johnston 	 */
1295b6955755SRobert Johnston 	if ((format = dgettext(dict, FMD_MSG_TEMPLATE)) == FMD_MSG_TEMPLATE)
1296b6955755SRobert Johnston 		format = h->fmh_template;
1297b6955755SRobert Johnston 
1298b6955755SRobert Johnston 	if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0)
1299b6955755SRobert Johnston 		uuid = (char *)FMD_MSG_MISSING;
1300b6955755SRobert Johnston 
1301b6955755SRobert Johnston 	if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME,
1302b6955755SRobert Johnston 	    &tv, &tn) == 0 && tn == 2 && (sec = (time_t)tv[0]) != (time_t)-1 &&
1303b6955755SRobert Johnston 	    (tmp = localtime_r(&sec, &tm)) != NULL)
1304f6e214c7SGavin Maltby 		(void) strftime(date, sizeof (date), "%a %b %e %H:%M:%S %Z %Y",
1305f6e214c7SGavin Maltby 		    tmp);
1306b6955755SRobert Johnston 	else
1307b6955755SRobert Johnston 		(void) strlcpy(date, FMD_MSG_MISSING, sizeof (date));
1308b6955755SRobert Johnston 
1309b6955755SRobert Johnston 	/*
1310b6955755SRobert Johnston 	 * Extract the relevant identifying elements of the FMRI and authority.
1311b6955755SRobert Johnston 	 * Note: for now, we ignore FM_FMRI_AUTH_DOMAIN (only for SPs).
1312b6955755SRobert Johnston 	 */
1313b6955755SRobert Johnston 	if (nvlist_lookup_nvlist(nvl, FM_SUSPECT_DE, &fmri) != 0)
1314b6955755SRobert Johnston 		fmri = NULL;
1315b6955755SRobert Johnston 
1316b6955755SRobert Johnston 	if (nvlist_lookup_nvlist(fmri, FM_FMRI_AUTHORITY, &auth) != 0)
1317b6955755SRobert Johnston 		auth = NULL;
1318b6955755SRobert Johnston 
1319b6955755SRobert Johnston 	if (nvlist_lookup_string(fmri, FM_FMRI_FMD_NAME, &src_name) != 0)
1320b6955755SRobert Johnston 		src_name = (char *)FMD_MSG_MISSING;
1321b6955755SRobert Johnston 
1322b6955755SRobert Johnston 	if (nvlist_lookup_string(fmri, FM_FMRI_FMD_VERSION, &src_vers) != 0)
1323b6955755SRobert Johnston 		src_vers = (char *)FMD_MSG_MISSING;
1324b6955755SRobert Johnston 
1325b6955755SRobert Johnston 	if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &platform) != 0)
1326b6955755SRobert Johnston 		platform = (char *)FMD_MSG_MISSING;
1327b6955755SRobert Johnston 
1328b6955755SRobert Johnston 	if (nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server) != 0)
1329b6955755SRobert Johnston 		server = (char *)FMD_MSG_MISSING;
1330b6955755SRobert Johnston 
13319c94f155SCheng Sean Ye 	if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN, &csn) != 0 &&
13329c94f155SCheng Sean Ye 	    nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &csn) != 0)
13339c94f155SCheng Sean Ye 		csn = (char *)FMD_MSG_MISSING;
1334b6955755SRobert Johnston 
1335b6955755SRobert Johnston 	/*
1336b6955755SRobert Johnston 	 * Format the message once to get its length, allocate a buffer, and
1337b6955755SRobert Johnston 	 * then format the message again into the buffer to return it.
1338b6955755SRobert Johnston 	 */
1339b6955755SRobert Johnston 	len = snprintf(NULL, 0, format, code,
1340b6955755SRobert Johnston 	    items[FMD_MSG_ITEM_TYPE], items[FMD_MSG_ITEM_SEVERITY],
13419c94f155SCheng Sean Ye 	    date, platform, csn, server, src_name, src_vers, uuid,
1342b6955755SRobert Johnston 	    items[FMD_MSG_ITEM_DESC], items[FMD_MSG_ITEM_RESPONSE],
1343b6955755SRobert Johnston 	    items[FMD_MSG_ITEM_IMPACT], items[FMD_MSG_ITEM_ACTION]);
1344b6955755SRobert Johnston 
1345b6955755SRobert Johnston 	if ((buf = malloc(len + 1)) == NULL) {
1346b6955755SRobert Johnston 		errno = ENOMEM;
1347b6955755SRobert Johnston 		goto out;
1348b6955755SRobert Johnston 	}
1349b6955755SRobert Johnston 
1350b6955755SRobert Johnston 	(void) snprintf(buf, len + 1, format, code,
1351b6955755SRobert Johnston 	    items[FMD_MSG_ITEM_TYPE], items[FMD_MSG_ITEM_SEVERITY],
13529c94f155SCheng Sean Ye 	    date, platform, csn, server, src_name, src_vers, uuid,
1353b6955755SRobert Johnston 	    items[FMD_MSG_ITEM_DESC], items[FMD_MSG_ITEM_RESPONSE],
1354b6955755SRobert Johnston 	    items[FMD_MSG_ITEM_IMPACT], items[FMD_MSG_ITEM_ACTION]);
1355b6955755SRobert Johnston out:
1356b6955755SRobert Johnston 	for (i = 0; i < FMD_MSG_ITEM_MAX; i++)
1357b6955755SRobert Johnston 		free(items[i]);
1358b6955755SRobert Johnston 
1359b6955755SRobert Johnston 	return (buf);
1360b6955755SRobert Johnston }
1361b6955755SRobert Johnston 
1362b6955755SRobert Johnston /*
1363b6955755SRobert Johnston  * Common code for fmd_msg_getitem_nv() and fmd_msg_getitem_id(): this function
1364b6955755SRobert Johnston  * handles locking, changing locales and domains, and restoring i18n state.
1365b6955755SRobert Johnston  */
1366b6955755SRobert Johnston static char *
fmd_msg_getitem(fmd_msg_hdl_t * h,const char * locale,nvlist_t * nvl,const char * code,fmd_msg_item_t item)1367b6955755SRobert Johnston fmd_msg_getitem(fmd_msg_hdl_t *h,
1368b6955755SRobert Johnston     const char *locale, nvlist_t *nvl, const char *code, fmd_msg_item_t item)
1369b6955755SRobert Johnston {
1370b6955755SRobert Johnston 	char *old_b, *old_c;
1371b6955755SRobert Johnston 	char *dict, *key, *p, *s;
1372b6955755SRobert Johnston 	size_t len;
1373b6955755SRobert Johnston 	int err;
1374b6955755SRobert Johnston 
1375b6955755SRobert Johnston 	if ((p = strchr(code, '-')) == NULL || p == code) {
1376b6955755SRobert Johnston 		errno = EINVAL;
1377b6955755SRobert Johnston 		return (NULL);
1378b6955755SRobert Johnston 	}
1379b6955755SRobert Johnston 
1380b6955755SRobert Johnston 	if (locale != NULL && strcmp(h->fmh_locale, locale) == 0)
1381b6955755SRobert Johnston 		locale = NULL; /* simplify later tests */
1382b6955755SRobert Johnston 
138323a1cceaSRoger A. Faulkner 	dict = strndupa(code, p - code);
1384b6955755SRobert Johnston 
1385b6955755SRobert Johnston 	fmd_msg_lock();
1386b6955755SRobert Johnston 
1387b6955755SRobert Johnston 	/*
1388b6955755SRobert Johnston 	 * If a non-default text domain binding was requested, save the old
1389b6955755SRobert Johnston 	 * binding perform the re-bind now that fmd_msg_lock() is held.
1390b6955755SRobert Johnston 	 */
1391b6955755SRobert Johnston 	if (h->fmh_binding != NULL) {
1392b6955755SRobert Johnston 		p = bindtextdomain(dict, NULL);
139323a1cceaSRoger A. Faulkner 		old_b = strdupa(p);
1394b6955755SRobert Johnston 		(void) bindtextdomain(dict, h->fmh_binding);
1395b6955755SRobert Johnston 	}
1396b6955755SRobert Johnston 
1397b6955755SRobert Johnston 	/*
1398b6955755SRobert Johnston 	 * Compute the lookup code for FMD_MSG_ITEM_TYPE: we'll use this to
1399b6955755SRobert Johnston 	 * determine if the dictionary contains any data for this code at all.
1400b6955755SRobert Johnston 	 */
1401b6955755SRobert Johnston 	len = strlen(code) + 1 + strlen(fmd_msg_items[FMD_MSG_ITEM_TYPE]) + 1;
1402b6955755SRobert Johnston 	key = alloca(len);
1403b6955755SRobert Johnston 
1404b6955755SRobert Johnston 	(void) snprintf(key, len, "%s.%s",
1405b6955755SRobert Johnston 	    code, fmd_msg_items[FMD_MSG_ITEM_TYPE]);
1406b6955755SRobert Johnston 
1407b6955755SRobert Johnston 	/*
1408b6955755SRobert Johnston 	 * Save the current locale string, and if we've been asked to fetch
1409b6955755SRobert Johnston 	 * the text for a different locale, switch locales now under the lock.
1410b6955755SRobert Johnston 	 */
1411b6955755SRobert Johnston 	p = setlocale(LC_ALL, NULL);
141223a1cceaSRoger A. Faulkner 	old_c = strdupa(p);
1413b6955755SRobert Johnston 
1414b6955755SRobert Johnston 	if (locale != NULL)
1415b6955755SRobert Johnston 		(void) setlocale(LC_ALL, locale);
1416b6955755SRobert Johnston 
1417b6955755SRobert Johnston 	/*
1418b6955755SRobert Johnston 	 * Prefetch the first item: if this isn't found, and we're in a non-
1419b6955755SRobert Johnston 	 * default locale, attempt to fall back to the C locale for this code.
1420b6955755SRobert Johnston 	 */
1421b6955755SRobert Johnston 	if (dgettext(dict, key) == key &&
1422b6955755SRobert Johnston 	    (locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
1423b6955755SRobert Johnston 		(void) setlocale(LC_ALL, "C");
1424b6955755SRobert Johnston 		locale = "C"; /* restore locale */
1425b6955755SRobert Johnston 	}
1426b6955755SRobert Johnston 
1427b6955755SRobert Johnston 	if (dgettext(dict, key) == key) {
1428b6955755SRobert Johnston 		s = NULL;
1429b6955755SRobert Johnston 		err = ENOENT;
1430b6955755SRobert Johnston 	} else {
1431b6955755SRobert Johnston 		s = fmd_msg_getitem_locked(h, nvl, dict, code, item);
1432b6955755SRobert Johnston 		err = errno;
1433b6955755SRobert Johnston 	}
1434b6955755SRobert Johnston 
1435b6955755SRobert Johnston 	if (locale != NULL)
1436b6955755SRobert Johnston 		(void) setlocale(LC_ALL, old_c);
1437b6955755SRobert Johnston 
1438b6955755SRobert Johnston 	if (h->fmh_binding != NULL)
1439b6955755SRobert Johnston 		(void) bindtextdomain(dict, old_b);
1440b6955755SRobert Johnston 
1441b6955755SRobert Johnston 	fmd_msg_unlock();
1442b6955755SRobert Johnston 
1443b6955755SRobert Johnston 	if (s == NULL)
1444b6955755SRobert Johnston 		errno = err;
1445b6955755SRobert Johnston 
1446b6955755SRobert Johnston 	return (s);
1447b6955755SRobert Johnston }
1448b6955755SRobert Johnston 
1449b6955755SRobert Johnston char *
fmd_msg_getitem_nv(fmd_msg_hdl_t * h,const char * locale,nvlist_t * nvl,fmd_msg_item_t item)1450b6955755SRobert Johnston fmd_msg_getitem_nv(fmd_msg_hdl_t *h,
1451b6955755SRobert Johnston     const char *locale, nvlist_t *nvl, fmd_msg_item_t item)
1452b6955755SRobert Johnston {
1453b6955755SRobert Johnston 	char *code;
1454b6955755SRobert Johnston 
1455b6955755SRobert Johnston 	if (item >= FMD_MSG_ITEM_MAX) {
1456b6955755SRobert Johnston 		errno = EINVAL;
1457b6955755SRobert Johnston 		return (NULL);
1458b6955755SRobert Johnston 	}
1459b6955755SRobert Johnston 
1460b6955755SRobert Johnston 	if (nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code) != 0) {
1461b6955755SRobert Johnston 		errno = EINVAL;
1462b6955755SRobert Johnston 		return (NULL);
1463b6955755SRobert Johnston 	}
1464b6955755SRobert Johnston 
1465b6955755SRobert Johnston 	return (fmd_msg_getitem(h, locale, nvl, code, item));
1466b6955755SRobert Johnston }
1467b6955755SRobert Johnston 
1468b6955755SRobert Johnston char *
fmd_msg_getitem_id(fmd_msg_hdl_t * h,const char * locale,const char * code,fmd_msg_item_t item)1469b6955755SRobert Johnston fmd_msg_getitem_id(fmd_msg_hdl_t *h,
1470b6955755SRobert Johnston     const char *locale, const char *code, fmd_msg_item_t item)
1471b6955755SRobert Johnston {
1472b6955755SRobert Johnston 	if (item >= FMD_MSG_ITEM_MAX) {
1473b6955755SRobert Johnston 		errno = EINVAL;
1474b6955755SRobert Johnston 		return (NULL);
1475b6955755SRobert Johnston 	}
1476b6955755SRobert Johnston 
1477b6955755SRobert Johnston 	return (fmd_msg_getitem(h, locale, NULL, code, item));
1478b6955755SRobert Johnston }
1479b6955755SRobert Johnston 
1480f6e214c7SGavin Maltby char *
fmd_msg_gettext_key(fmd_msg_hdl_t * h,const char * locale,const char * dict,const char * key)1481f6e214c7SGavin Maltby fmd_msg_gettext_key(fmd_msg_hdl_t *h,
1482f6e214c7SGavin Maltby     const char *locale, const char *dict, const char *key)
1483f6e214c7SGavin Maltby {
1484f6e214c7SGavin Maltby 	char *old_b, *old_c, *p, *s;
1485f6e214c7SGavin Maltby 
1486f6e214c7SGavin Maltby 	fmd_msg_lock();
1487f6e214c7SGavin Maltby 
1488f6e214c7SGavin Maltby 	/*
1489f6e214c7SGavin Maltby 	 * If a non-default text domain binding was requested, save the old
1490f6e214c7SGavin Maltby 	 * binding perform the re-bind now that fmd_msg_lock() is held.
1491f6e214c7SGavin Maltby 	 */
1492f6e214c7SGavin Maltby 	if (h->fmh_binding != NULL) {
1493f6e214c7SGavin Maltby 		p = bindtextdomain(dict, NULL);
1494f6e214c7SGavin Maltby 		old_b = alloca(strlen(p) + 1);
1495f6e214c7SGavin Maltby 		(void) strcpy(old_b, p);
1496f6e214c7SGavin Maltby 		(void) bindtextdomain(dict, h->fmh_binding);
1497f6e214c7SGavin Maltby 	}
1498f6e214c7SGavin Maltby 
1499f6e214c7SGavin Maltby 	/*
1500f6e214c7SGavin Maltby 	 * Save the current locale string, and if we've been asked to fetch
1501f6e214c7SGavin Maltby 	 * the text for a different locale, switch locales now under the lock.
1502f6e214c7SGavin Maltby 	 */
1503f6e214c7SGavin Maltby 	p = setlocale(LC_ALL, NULL);
1504f6e214c7SGavin Maltby 	old_c = alloca(strlen(p) + 1);
1505f6e214c7SGavin Maltby 	(void) strcpy(old_c, p);
1506f6e214c7SGavin Maltby 
1507f6e214c7SGavin Maltby 	if (locale != NULL)
1508f6e214c7SGavin Maltby 		(void) setlocale(LC_ALL, locale);
1509f6e214c7SGavin Maltby 
1510f6e214c7SGavin Maltby 	/*
1511f6e214c7SGavin Maltby 	 * First attempt to fetch the string in the current locale.  If this
1512f6e214c7SGavin Maltby 	 * fails and we're in a non-default locale, attempt to fall back to the
1513f6e214c7SGavin Maltby 	 * C locale and try again.  If it still fails then we return NULL and
1514f6e214c7SGavin Maltby 	 * set errno.
1515f6e214c7SGavin Maltby 	 */
1516f6e214c7SGavin Maltby 	if ((s = dgettext(dict, key)) == key &&
1517f6e214c7SGavin Maltby 	    (locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
1518f6e214c7SGavin Maltby 		(void) setlocale(LC_ALL, "C");
1519f6e214c7SGavin Maltby 		locale = "C"; /* restore locale */
1520f6e214c7SGavin Maltby 
1521f6e214c7SGavin Maltby 		if ((s = dgettext(dict, key)) == key) {
1522f6e214c7SGavin Maltby 			s = NULL;
1523f6e214c7SGavin Maltby 			errno = ENOENT;
1524f6e214c7SGavin Maltby 		}
1525f6e214c7SGavin Maltby 	}
1526f6e214c7SGavin Maltby 	if (locale != NULL)
1527f6e214c7SGavin Maltby 		(void) setlocale(LC_ALL, old_c);
1528f6e214c7SGavin Maltby 
1529f6e214c7SGavin Maltby 	if (h->fmh_binding != NULL)
1530f6e214c7SGavin Maltby 		(void) bindtextdomain(dict, old_b);
1531f6e214c7SGavin Maltby 
1532f6e214c7SGavin Maltby 	fmd_msg_unlock();
1533f6e214c7SGavin Maltby 
1534f6e214c7SGavin Maltby 	return (s);
1535f6e214c7SGavin Maltby }
1536f6e214c7SGavin Maltby 
1537b6955755SRobert Johnston /*
1538b6955755SRobert Johnston  * Common code for fmd_msg_gettext_nv() and fmd_msg_gettext_id(): this function
1539b6955755SRobert Johnston  * handles locking, changing locales and domains, and restoring i18n state.
1540b6955755SRobert Johnston  */
1541b6955755SRobert Johnston static char *
fmd_msg_gettext(fmd_msg_hdl_t * h,const char * locale,nvlist_t * nvl,const char * code)1542b6955755SRobert Johnston fmd_msg_gettext(fmd_msg_hdl_t *h,
1543b6955755SRobert Johnston     const char *locale, nvlist_t *nvl, const char *code)
1544b6955755SRobert Johnston {
1545b6955755SRobert Johnston 	char *old_b, *old_c;
1546b6955755SRobert Johnston 	char *dict, *key, *p, *s;
1547b6955755SRobert Johnston 	size_t len;
1548b6955755SRobert Johnston 	int err;
1549b6955755SRobert Johnston 
1550b6955755SRobert Johnston 	if ((p = strchr(code, '-')) == NULL || p == code) {
1551b6955755SRobert Johnston 		errno = EINVAL;
1552b6955755SRobert Johnston 		return (NULL);
1553b6955755SRobert Johnston 	}
1554b6955755SRobert Johnston 
1555b6955755SRobert Johnston 	if (locale != NULL && strcmp(h->fmh_locale, locale) == 0)
1556b6955755SRobert Johnston 		locale = NULL; /* simplify later tests */
1557b6955755SRobert Johnston 
155823a1cceaSRoger A. Faulkner 	dict = strndupa(code, p - code);
1559b6955755SRobert Johnston 
1560b6955755SRobert Johnston 	fmd_msg_lock();
1561b6955755SRobert Johnston 
1562b6955755SRobert Johnston 	/*
1563b6955755SRobert Johnston 	 * If a non-default text domain binding was requested, save the old
1564b6955755SRobert Johnston 	 * binding perform the re-bind now that fmd_msg_lock() is held.
1565b6955755SRobert Johnston 	 */
1566b6955755SRobert Johnston 	if (h->fmh_binding != NULL) {
1567b6955755SRobert Johnston 		p = bindtextdomain(dict, NULL);
156823a1cceaSRoger A. Faulkner 		old_b = strdupa(p);
1569b6955755SRobert Johnston 		(void) bindtextdomain(dict, h->fmh_binding);
1570b6955755SRobert Johnston 	}
1571b6955755SRobert Johnston 
1572b6955755SRobert Johnston 	/*
1573b6955755SRobert Johnston 	 * Compute the lookup code for FMD_MSG_ITEM_TYPE: we'll use this to
1574b6955755SRobert Johnston 	 * determine if the dictionary contains any data for this code at all.
1575b6955755SRobert Johnston 	 */
1576b6955755SRobert Johnston 	len = strlen(code) + 1 + strlen(fmd_msg_items[FMD_MSG_ITEM_TYPE]) + 1;
1577b6955755SRobert Johnston 	key = alloca(len);
1578b6955755SRobert Johnston 
1579b6955755SRobert Johnston 	(void) snprintf(key, len, "%s.%s",
1580b6955755SRobert Johnston 	    code, fmd_msg_items[FMD_MSG_ITEM_TYPE]);
1581b6955755SRobert Johnston 
1582b6955755SRobert Johnston 	/*
1583b6955755SRobert Johnston 	 * Save the current locale string, and if we've been asked to fetch
1584b6955755SRobert Johnston 	 * the text for a different locale, switch locales now under the lock.
1585b6955755SRobert Johnston 	 */
1586b6955755SRobert Johnston 	p = setlocale(LC_ALL, NULL);
158723a1cceaSRoger A. Faulkner 	old_c = strdupa(p);
1588b6955755SRobert Johnston 
1589b6955755SRobert Johnston 	if (locale != NULL)
1590b6955755SRobert Johnston 		(void) setlocale(LC_ALL, locale);
1591b6955755SRobert Johnston 
1592b6955755SRobert Johnston 	/*
1593b6955755SRobert Johnston 	 * Prefetch the first item: if this isn't found, and we're in a non-
1594b6955755SRobert Johnston 	 * default locale, attempt to fall back to the C locale for this code.
1595b6955755SRobert Johnston 	 */
1596b6955755SRobert Johnston 	if (dgettext(dict, key) == key &&
1597b6955755SRobert Johnston 	    (locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
1598b6955755SRobert Johnston 		(void) setlocale(LC_ALL, "C");
1599b6955755SRobert Johnston 		locale = "C"; /* restore locale */
1600b6955755SRobert Johnston 	}
1601b6955755SRobert Johnston 
1602b6955755SRobert Johnston 	if (dgettext(dict, key) == key) {
1603b6955755SRobert Johnston 		s = NULL;
1604b6955755SRobert Johnston 		err = ENOENT;
1605b6955755SRobert Johnston 	} else {
1606b6955755SRobert Johnston 		s = fmd_msg_gettext_locked(h, nvl, dict, code);
1607b6955755SRobert Johnston 		err = errno;
1608b6955755SRobert Johnston 	}
1609b6955755SRobert Johnston 
1610b6955755SRobert Johnston 	if (locale != NULL)
1611b6955755SRobert Johnston 		(void) setlocale(LC_ALL, old_c);
1612b6955755SRobert Johnston 
1613b6955755SRobert Johnston 	if (h->fmh_binding != NULL)
1614b6955755SRobert Johnston 		(void) bindtextdomain(dict, old_b);
1615b6955755SRobert Johnston 
1616b6955755SRobert Johnston 	fmd_msg_unlock();
1617b6955755SRobert Johnston 
1618b6955755SRobert Johnston 	if (s == NULL)
1619b6955755SRobert Johnston 		errno = err;
1620b6955755SRobert Johnston 
1621b6955755SRobert Johnston 	return (s);
1622b6955755SRobert Johnston }
1623b6955755SRobert Johnston 
1624b6955755SRobert Johnston char *
fmd_msg_gettext_nv(fmd_msg_hdl_t * h,const char * locale,nvlist_t * nvl)1625b6955755SRobert Johnston fmd_msg_gettext_nv(fmd_msg_hdl_t *h, const char *locale, nvlist_t *nvl)
1626b6955755SRobert Johnston {
1627b6955755SRobert Johnston 	char *code;
1628b6955755SRobert Johnston 
1629b6955755SRobert Johnston 	if (nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code) != 0) {
1630b6955755SRobert Johnston 		errno = EINVAL;
1631b6955755SRobert Johnston 		return (NULL);
1632b6955755SRobert Johnston 	}
1633b6955755SRobert Johnston 
1634b6955755SRobert Johnston 	return (fmd_msg_gettext(h, locale, nvl, code));
1635b6955755SRobert Johnston }
1636b6955755SRobert Johnston 
1637b6955755SRobert Johnston char *
fmd_msg_gettext_id(fmd_msg_hdl_t * h,const char * locale,const char * code)1638b6955755SRobert Johnston fmd_msg_gettext_id(fmd_msg_hdl_t *h, const char *locale, const char *code)
1639b6955755SRobert Johnston {
1640b6955755SRobert Johnston 	return (fmd_msg_gettext(h, locale, NULL, code));
1641b6955755SRobert Johnston }
1642