1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <ctype.h>
28#include <errno.h>
29#include <stdarg.h>
30
31#include <fmd_alloc.h>
32#include <fmd_subr.h>
33#include <fmd_error.h>
34#include <fmd_string.h>
35#include <fmd_scheme.h>
36#include <fmd_fmri.h>
37#include <fmd_topo.h>
38#include <fmd.h>
39
40/*
41 * Interfaces to be used by the plugins
42 */
43
44void *
45fmd_fmri_alloc(size_t size)
46{
47	return (fmd_alloc(size, FMD_SLEEP));
48}
49
50void *
51fmd_fmri_zalloc(size_t size)
52{
53	return (fmd_zalloc(size, FMD_SLEEP));
54}
55
56void
57fmd_fmri_free(void *data, size_t size)
58{
59	fmd_free(data, size);
60}
61
62int
63fmd_fmri_set_errno(int err)
64{
65	errno = err;
66	return (-1);
67}
68
69void
70fmd_fmri_warn(const char *format, ...)
71{
72	va_list ap;
73
74	va_start(ap, format);
75	fmd_verror(EFMD_FMRI_SCHEME, format, ap);
76	va_end(ap);
77}
78
79/*
80 * Convert an input string to a URI escaped string and return the new string.
81 * RFC2396 Section 2.4 says that data must be escaped if it does not have a
82 * representation using an unreserved character, where an unreserved character
83 * is one that is either alphanumeric or one of the marks defined in S2.3.
84 */
85static size_t
86fmd_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len)
87{
88	static const char rfc2396_mark[] = "-_.!~*'()";
89	static const char hex_digits[] = "0123456789ABCDEF";
90	static const char empty_str[] = "";
91
92	const char *p;
93	char c, *q;
94	size_t n = 0;
95
96	if (s == NULL)
97		s = empty_str;
98
99	if (xmark == NULL)
100		xmark = empty_str;
101
102	for (p = s; (c = *p) != '\0'; p++) {
103		if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c))
104			n++;	/* represent c as itself */
105		else
106			n += 3; /* represent c as escape */
107	}
108
109	if (buf == NULL)
110		return (n);
111
112	for (p = s, q = buf; (c = *p) != '\0' && q < buf + len; p++) {
113		if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) {
114			*q++ = c;
115		} else {
116			*q++ = '%';
117			*q++ = hex_digits[((uchar_t)c & 0xf0) >> 4];
118			*q++ = hex_digits[(uchar_t)c & 0xf];
119		}
120	}
121
122	if (q == buf + len)
123		q--; /* len is too small: truncate output string */
124
125	*q = '\0';
126	return (n);
127}
128
129/*
130 * Convert a name-value pair list representing an FMRI authority into the
131 * corresponding RFC2396 string representation and return the new string.
132 */
133char *
134fmd_fmri_auth2str(nvlist_t *nvl)
135{
136	nvpair_t *nvp;
137	char *s, *p, *v;
138	size_t n = 0;
139
140	for (nvp = nvlist_next_nvpair(nvl, NULL);
141	    nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
142
143		if (nvpair_type(nvp) != DATA_TYPE_STRING)
144			continue; /* do not format non-string elements */
145
146		n += fmd_fmri_uriescape(nvpair_name(nvp), NULL, NULL, 0) + 1;
147		(void) nvpair_value_string(nvp, &v);
148		n += fmd_fmri_uriescape(v, ":", NULL, 0) + 1;
149	}
150
151	p = s = fmd_alloc(n, FMD_SLEEP);
152
153	for (nvp = nvlist_next_nvpair(nvl, NULL);
154	    nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp)) {
155
156		if (nvpair_type(nvp) != DATA_TYPE_STRING)
157			continue; /* do not format non-string elements */
158
159		if (p != s)
160			*p++ = ',';
161
162		p += fmd_fmri_uriescape(nvpair_name(nvp), NULL, p, n);
163		*p++ = '=';
164		(void) nvpair_value_string(nvp, &v);
165		p += fmd_fmri_uriescape(v, ":", p, n);
166	}
167
168	return (s);
169}
170
171/*
172 * Convert an input string to a URI escaped string and return the new string.
173 * We amend the unreserved character list to include commas and colons,
174 * as both are needed to make FMRIs readable without escaping.  We also permit
175 * "/" to pass through unescaped as any path delimiters used by the event
176 * creator are presumably intended to appear in the final path.
177 */
178char *
179fmd_fmri_strescape(const char *s)
180{
181	char *s2;
182	size_t n;
183
184	if (s == NULL)
185		return (NULL);
186
187	n = fmd_fmri_uriescape(s, ":,/", NULL, 0);
188	s2 = fmd_alloc(n + 1, FMD_SLEEP);
189	(void) fmd_fmri_uriescape(s, ":,/", s2, n + 1);
190
191	return (s2);
192}
193
194char *
195fmd_fmri_strdup(const char *s)
196{
197	return (fmd_strdup(s, FMD_SLEEP));
198}
199
200void
201fmd_fmri_strfree(char *s)
202{
203	fmd_strfree(s);
204}
205
206const char *
207fmd_fmri_get_rootdir(void)
208{
209	return (fmd.d_rootdir);
210}
211
212const char *
213fmd_fmri_get_platform(void)
214{
215	return (fmd.d_platform);
216}
217
218uint64_t
219fmd_fmri_get_drgen(void)
220{
221	uint64_t gen;
222
223	(void) pthread_mutex_lock(&fmd.d_stats_lock);
224	gen = fmd.d_stats->ds_dr_gen.fmds_value.ui64;
225	(void) pthread_mutex_unlock(&fmd.d_stats_lock);
226
227	return (gen);
228}
229
230struct topo_hdl *
231fmd_fmri_topo_hold(int version)
232{
233	fmd_topo_t *ftp;
234
235	if (version != TOPO_VERSION)
236		return (NULL);
237
238	ftp = fmd_topo_hold();
239
240	return (ftp->ft_hdl);
241}
242
243void
244fmd_fmri_topo_rele(struct topo_hdl *thp)
245{
246	fmd_topo_rele_hdl(thp);
247}
248
249/*
250 * Interfaces for users of the plugins
251 */
252
253static fmd_scheme_t *
254nvl2scheme(nvlist_t *nvl)
255{
256	char *name;
257
258	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) {
259		(void) fmd_set_errno(EFMD_FMRI_INVAL);
260		return (NULL);
261	}
262
263	return (fmd_scheme_hash_lookup(fmd.d_schemes, name));
264}
265
266ssize_t
267fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
268{
269	fmd_scheme_t *sp;
270	char c;
271	ssize_t rv;
272
273	if (buf == NULL && buflen == 0) {
274		buf = &c;
275		buflen = sizeof (c);
276	}
277
278	if ((sp = nvl2scheme(nvl)) == NULL)
279		return (-1); /* errno is set for us */
280
281	(void) pthread_mutex_lock(&sp->sch_opslock);
282	ASSERT(buf != NULL || buflen == 0);
283	rv = sp->sch_ops.sop_nvl2str(nvl, buf, buflen);
284	(void) pthread_mutex_unlock(&sp->sch_opslock);
285
286	fmd_scheme_hash_release(fmd.d_schemes, sp);
287	return (rv);
288}
289
290int
291fmd_fmri_expand(nvlist_t *nvl)
292{
293	fmd_scheme_t *sp;
294	int rv;
295
296	if ((sp = nvl2scheme(nvl)) == NULL)
297		return (-1); /* errno is set for us */
298
299	(void) pthread_mutex_lock(&sp->sch_opslock);
300	rv = sp->sch_ops.sop_expand(nvl);
301	(void) pthread_mutex_unlock(&sp->sch_opslock);
302
303	fmd_scheme_hash_release(fmd.d_schemes, sp);
304	return (rv);
305}
306
307int
308fmd_fmri_present(nvlist_t *nvl)
309{
310	fmd_scheme_t *sp;
311	int rv;
312
313	if ((sp = nvl2scheme(nvl)) == NULL)
314		return (-1); /* errno is set for us */
315
316	(void) pthread_mutex_lock(&sp->sch_opslock);
317	rv = sp->sch_ops.sop_present(nvl);
318	(void) pthread_mutex_unlock(&sp->sch_opslock);
319
320	fmd_scheme_hash_release(fmd.d_schemes, sp);
321	return (rv);
322}
323
324int
325fmd_fmri_replaced(nvlist_t *nvl)
326{
327	fmd_scheme_t *sp;
328	int rv;
329
330	if ((sp = nvl2scheme(nvl)) == NULL)
331		return (-1); /* errno is set for us */
332
333	(void) pthread_mutex_lock(&sp->sch_opslock);
334	rv = sp->sch_ops.sop_replaced(nvl);
335	(void) pthread_mutex_unlock(&sp->sch_opslock);
336
337	fmd_scheme_hash_release(fmd.d_schemes, sp);
338	return (rv);
339}
340
341int
342fmd_fmri_service_state(nvlist_t *nvl)
343{
344	fmd_scheme_t *sp;
345	int rv;
346
347	if ((sp = nvl2scheme(nvl)) == NULL)
348		return (-1); /* errno is set for us */
349
350	(void) pthread_mutex_lock(&sp->sch_opslock);
351	rv = sp->sch_ops.sop_service_state(nvl);
352	(void) pthread_mutex_unlock(&sp->sch_opslock);
353
354	fmd_scheme_hash_release(fmd.d_schemes, sp);
355	return (rv);
356}
357
358int
359fmd_fmri_unusable(nvlist_t *nvl)
360{
361	fmd_scheme_t *sp;
362	int rv;
363
364	if ((sp = nvl2scheme(nvl)) == NULL)
365		return (-1); /* errno is set for us */
366
367	(void) pthread_mutex_lock(&sp->sch_opslock);
368	rv = sp->sch_ops.sop_unusable(nvl);
369	(void) pthread_mutex_unlock(&sp->sch_opslock);
370
371	fmd_scheme_hash_release(fmd.d_schemes, sp);
372	return (rv);
373}
374
375/*
376 * Someday we'll retire the scheme plugins.  For the
377 * retire/unretire operations, the topo interfaces
378 * are called directly.
379 */
380int
381fmd_fmri_retire(nvlist_t *nvl)
382{
383	topo_hdl_t *thp;
384	int rv, err;
385
386	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
387		return (-1);
388
389	rv = topo_fmri_retire(thp, nvl, &err);
390	fmd_fmri_topo_rele(thp);
391
392	return (rv);
393}
394
395int
396fmd_fmri_unretire(nvlist_t *nvl)
397{
398	topo_hdl_t *thp;
399	int rv, err;
400
401	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
402		return (-1);
403
404	rv = topo_fmri_unretire(thp, nvl, &err);
405	fmd_fmri_topo_rele(thp);
406
407	return (rv);
408}
409
410int
411fmd_fmri_contains(nvlist_t *er, nvlist_t *ee)
412{
413	fmd_scheme_t *sp;
414	char *ername, *eename;
415	int rv;
416
417	if (nvlist_lookup_string(er, FM_FMRI_SCHEME, &ername) != 0 ||
418	    nvlist_lookup_string(ee, FM_FMRI_SCHEME, &eename) != 0 ||
419	    strcmp(ername, eename) != 0)
420		return (fmd_set_errno(EFMD_FMRI_INVAL));
421
422	if ((sp = fmd_scheme_hash_lookup(fmd.d_schemes, ername)) == NULL)
423		return (-1); /* errno is set for us */
424
425	(void) pthread_mutex_lock(&sp->sch_opslock);
426	rv = sp->sch_ops.sop_contains(er, ee);
427	(void) pthread_mutex_unlock(&sp->sch_opslock);
428
429	fmd_scheme_hash_release(fmd.d_schemes, sp);
430	return (rv);
431}
432
433nvlist_t *
434fmd_fmri_translate(nvlist_t *fmri, nvlist_t *auth)
435{
436	fmd_scheme_t *sp;
437	nvlist_t *nvl;
438
439	if ((sp = nvl2scheme(fmri)) == NULL)
440		return (NULL); /* errno is set for us */
441
442	(void) pthread_mutex_lock(&sp->sch_opslock);
443	nvl = sp->sch_ops.sop_translate(fmri, auth);
444	(void) pthread_mutex_unlock(&sp->sch_opslock);
445
446	fmd_scheme_hash_release(fmd.d_schemes, sp);
447	return (nvl);
448}
449