xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_fmri.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
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 
44 void *
45 fmd_fmri_alloc(size_t size)
46 {
47 	return (fmd_alloc(size, FMD_SLEEP));
48 }
49 
50 void *
51 fmd_fmri_zalloc(size_t size)
52 {
53 	return (fmd_zalloc(size, FMD_SLEEP));
54 }
55 
56 void
57 fmd_fmri_free(void *data, size_t size)
58 {
59 	fmd_free(data, size);
60 }
61 
62 int
63 fmd_fmri_set_errno(int err)
64 {
65 	errno = err;
66 	return (-1);
67 }
68 
69 void
70 fmd_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  */
85 static size_t
86 fmd_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  */
133 char *
134 fmd_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  */
178 char *
179 fmd_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 
194 char *
195 fmd_fmri_strdup(const char *s)
196 {
197 	return (fmd_strdup(s, FMD_SLEEP));
198 }
199 
200 void
201 fmd_fmri_strfree(char *s)
202 {
203 	fmd_strfree(s);
204 }
205 
206 const char *
207 fmd_fmri_get_rootdir(void)
208 {
209 	return (fmd.d_rootdir);
210 }
211 
212 const char *
213 fmd_fmri_get_platform(void)
214 {
215 	return (fmd.d_platform);
216 }
217 
218 uint64_t
219 fmd_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 
230 struct topo_hdl *
231 fmd_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 
243 void
244 fmd_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 
253 static fmd_scheme_t *
254 nvl2scheme(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 
266 ssize_t
267 fmd_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 
290 int
291 fmd_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 
307 int
308 fmd_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 
324 int
325 fmd_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 
341 int
342 fmd_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 
358 int
359 fmd_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  */
380 int
381 fmd_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 
395 int
396 fmd_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 
410 int
411 fmd_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 
433 nvlist_t *
434 fmd_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