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 *
fmd_fmri_alloc(size_t size)45 fmd_fmri_alloc(size_t size)
46 {
47 return (fmd_alloc(size, FMD_SLEEP));
48 }
49
50 void *
fmd_fmri_zalloc(size_t size)51 fmd_fmri_zalloc(size_t size)
52 {
53 return (fmd_zalloc(size, FMD_SLEEP));
54 }
55
56 void
fmd_fmri_free(void * data,size_t size)57 fmd_fmri_free(void *data, size_t size)
58 {
59 fmd_free(data, size);
60 }
61
62 int
fmd_fmri_set_errno(int err)63 fmd_fmri_set_errno(int err)
64 {
65 errno = err;
66 return (-1);
67 }
68
69 void
fmd_fmri_warn(const char * format,...)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
fmd_fmri_uriescape(const char * s,const char * xmark,char * buf,size_t len)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 *
fmd_fmri_auth2str(nvlist_t * nvl)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 *
fmd_fmri_strescape(const char * s)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 *
fmd_fmri_strdup(const char * s)195 fmd_fmri_strdup(const char *s)
196 {
197 return (fmd_strdup(s, FMD_SLEEP));
198 }
199
200 void
fmd_fmri_strfree(char * s)201 fmd_fmri_strfree(char *s)
202 {
203 fmd_strfree(s);
204 }
205
206 const char *
fmd_fmri_get_rootdir(void)207 fmd_fmri_get_rootdir(void)
208 {
209 return (fmd.d_rootdir);
210 }
211
212 const char *
fmd_fmri_get_platform(void)213 fmd_fmri_get_platform(void)
214 {
215 return (fmd.d_platform);
216 }
217
218 uint64_t
fmd_fmri_get_drgen(void)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 *
fmd_fmri_topo_hold(int version)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
fmd_fmri_topo_rele(struct topo_hdl * thp)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 *
nvl2scheme(nvlist_t * nvl)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
fmd_fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)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
fmd_fmri_expand(nvlist_t * nvl)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
fmd_fmri_present(nvlist_t * nvl)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
fmd_fmri_replaced(nvlist_t * nvl)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
fmd_fmri_service_state(nvlist_t * nvl)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
fmd_fmri_unusable(nvlist_t * nvl)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
fmd_fmri_retire(nvlist_t * nvl)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
fmd_fmri_unretire(nvlist_t * nvl)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
fmd_fmri_contains(nvlist_t * er,nvlist_t * ee)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 *
fmd_fmri_translate(nvlist_t * fmri,nvlist_t * auth)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