xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c (revision c8589f13ba961772dd5a0d699c5bb926f3006c33)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <string.h>
30 #include <limits.h>
31 #include <fm/topo_mod.h>
32 #include <sys/fm/protocol.h>
33 #include <topo_alloc.h>
34 #include <topo_error.h>
35 #include <topo_method.h>
36 #include <topo_subr.h>
37 #include <topo_string.h>
38 
39 /*ARGSUSED*/
40 static int
41 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
42 {
43 	if (nvlp != NULL)
44 		nvlist_free(nvlp);
45 
46 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
47 	    topo_strerror(err));
48 
49 	*errp = err;
50 	return (-1);
51 }
52 
53 /*ARGSUSED*/
54 static nvlist_t *
55 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
56 {
57 	if (nvlp != NULL)
58 		nvlist_free(nvlp);
59 
60 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
61 	    topo_strerror(err));
62 
63 	*errp = err;
64 	return (NULL);
65 }
66 
67 int
68 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err)
69 {
70 	char *scheme, *str;
71 	nvlist_t *out = NULL;
72 	tnode_t *rnode;
73 
74 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
75 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
76 		    TOPO_METH_NVL2STR, out));
77 
78 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
79 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
80 		    TOPO_METH_NVL2STR, out));
81 
82 	if (topo_method_invoke(rnode, TOPO_METH_NVL2STR,
83 	    TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0)
84 		return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out));
85 
86 	if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0)
87 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
88 		    TOPO_METH_NVL2STR, out));
89 
90 	if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL)
91 		return (set_error(thp, ETOPO_NOMEM, err,
92 		    TOPO_METH_NVL2STR, out));
93 
94 	nvlist_free(out);
95 
96 	return (0);
97 }
98 
99 int
100 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
101     int *err)
102 {
103 	char *f, buf[PATH_MAX];
104 	nvlist_t *out = NULL, *in = NULL;
105 	tnode_t *rnode;
106 
107 	(void) strlcpy(buf, fmristr, sizeof (buf));
108 	if ((f = strchr(buf, ':')) == NULL)
109 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
110 		    TOPO_METH_STR2NVL, in));
111 
112 	*f = '\0'; /* strip trailing FMRI path */
113 
114 	if ((rnode = topo_hdl_root(thp, buf)) == NULL)
115 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
116 		    TOPO_METH_STR2NVL, in));
117 
118 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
119 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
120 		    in));
121 
122 	if (nvlist_add_string(in, "fmri-string", fmristr) != 0)
123 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
124 		    in));
125 
126 	if (topo_method_invoke(rnode, TOPO_METH_STR2NVL,
127 	    TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0)
128 		return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
129 
130 	if (out == NULL ||
131 	    topo_hdl_nvdup(thp, out, fmri) != 0)
132 		return (set_error(thp, ETOPO_FMRI_NVL, err,
133 		    TOPO_METH_STR2NVL, in));
134 
135 	nvlist_free(out);
136 	nvlist_free(in);
137 
138 	return (0);
139 }
140 
141 /* ARGSUSED */
142 static int
143 is_present(topo_hdl_t *thp, tnode_t *node, void *data)
144 {
145 	int err;
146 	uint32_t present = 0;
147 	nvlist_t *out = NULL;
148 	nvlist_t *fmri = (nvlist_t *)data;
149 
150 	if (topo_method_invoke(node, TOPO_METH_PRESENT,
151 	    TOPO_METH_PRESENT_VERSION, fmri, &out, &err) < 0) {
152 		if (out != NULL)
153 			nvlist_free(out);
154 		return (present);
155 	}
156 
157 	(void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present);
158 
159 	nvlist_free(out);
160 
161 	return (present);
162 }
163 
164 int
165 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err)
166 {
167 	int ret = 0;
168 	uint32_t present = 0;
169 	char *scheme;
170 	nvlist_t *out = NULL;
171 	tnode_t *rnode;
172 
173 	if (topo_fmri_invoke(thp, fmri, is_present, fmri, &ret) == 0)
174 		return (ret);
175 
176 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
177 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
178 		    TOPO_METH_PRESENT, out));
179 
180 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
181 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
182 		    TOPO_METH_PRESENT, out));
183 
184 	if (topo_method_invoke(rnode, TOPO_METH_PRESENT,
185 	    TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) {
186 		(void) set_error(thp, *err, err, TOPO_METH_PRESENT, out);
187 		return (present);
188 	}
189 
190 	(void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present);
191 	nvlist_free(out);
192 
193 	return (present);
194 }
195 
196 int
197 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err)
198 {
199 	int rc;
200 	char *scheme;
201 	nvlist_t *in, *out = NULL;
202 	tnode_t *rnode;
203 
204 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
205 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
206 		    TOPO_METH_CONTAINS, out));
207 
208 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
209 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
210 		    TOPO_METH_CONTAINS, out));
211 
212 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
213 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
214 		    out));
215 
216 	if (nvlist_add_nvlist(in, "fmri", fmri) != 0 ||
217 	    nvlist_add_nvlist(in, "subfmri", subfmri) != 0)
218 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
219 		    out));
220 
221 	if (topo_hdl_nvalloc(thp, &out, NV_UNIQUE_NAME) != 0)
222 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
223 		    out));
224 
225 	if ((rc = topo_method_invoke(rnode, TOPO_METH_CONTAINS,
226 	    TOPO_METH_CONTAINS_VERSION, fmri, &out, err)) < 0)
227 		return (set_error(thp, *err, err, TOPO_METH_CONTAINS, out));
228 
229 	return (rc);
230 }
231 
232 int
233 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err)
234 {
235 	char *scheme;
236 	uint32_t unusable = 0;
237 	nvlist_t *out = NULL;
238 	tnode_t *rnode;
239 
240 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
241 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
242 		    TOPO_METH_UNUSABLE, out));
243 
244 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
245 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
246 		    TOPO_METH_UNUSABLE, out));
247 
248 	if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE,
249 	    TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0)
250 		return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out));
251 
252 	(void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable);
253 	nvlist_free(out);
254 
255 	return (unusable);
256 }
257 
258 int
259 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
260 {
261 	char *scheme;
262 	nvlist_t *out = NULL;
263 	tnode_t *rnode;
264 
265 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
266 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
267 		    TOPO_METH_EXPAND, out));
268 
269 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
270 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
271 		    TOPO_METH_EXPAND, out));
272 
273 	if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
274 	    TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
275 		return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
276 
277 	return (0);
278 }
279 
280 struct prop_lookup {
281 	int pl_err;
282 	topo_type_t pl_type;
283 	const char *pl_group;
284 	const char *pl_name;
285 	nvlist_t **pl_prop;
286 	nvlist_t *pl_resource;
287 };
288 
289 static int
290 prop_lookup(topo_hdl_t *thp, tnode_t *node, void *pdata)
291 {
292 	int rc;
293 	nvlist_t *r1;
294 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
295 
296 	if (topo_node_resource(node, &r1, &plp->pl_err) != 0)
297 		return (TOPO_WALK_ERR);
298 
299 	rc = topo_fmri_compare(thp, r1, plp->pl_resource, &plp->pl_err);
300 	nvlist_free(r1);
301 	if (rc == 0)
302 		return (TOPO_WALK_NEXT);
303 	if (rc < 0)
304 		return (TOPO_WALK_ERR);
305 
306 	/*
307 	 * Special case for dynamically created ASRU and FRU
308 	 */
309 	if (strcmp(plp->pl_group, TOPO_PGROUP_PROTOCOL) == 0) {
310 		if (strcmp(plp->pl_name, TOPO_PROP_ASRU) == 0) {
311 			if (topo_node_asru(node, plp->pl_prop, plp->pl_resource,
312 			    &plp->pl_err) < 0) {
313 				return (TOPO_WALK_ERR);
314 			}
315 			return (0);
316 		} else if (strcmp(plp->pl_name, TOPO_PROP_FRU) == 0) {
317 			if (topo_node_fru(node, plp->pl_prop, plp->pl_resource,
318 			    &plp->pl_err) < 0) {
319 				return (TOPO_WALK_ERR);
320 			}
321 			return (0);
322 		}
323 	}
324 
325 	switch (plp->pl_type) {
326 		case TOPO_TYPE_STRING:
327 		{
328 			char *str;
329 			if (topo_prop_get_string(node, plp->pl_group,
330 			    plp->pl_name, &str, &plp->pl_err) < 0)
331 				return (TOPO_WALK_ERR);
332 
333 			if (nvlist_add_string(*plp->pl_prop, "prop", str)
334 			    != 0) {
335 				topo_hdl_strfree(thp, str);
336 				plp->pl_err = ETOPO_PROP_NVL;
337 				return (TOPO_WALK_ERR);
338 			}
339 			topo_hdl_strfree(thp, str);
340 			return (TOPO_WALK_TERMINATE);
341 		}
342 		default:
343 			plp->pl_err = ETOPO_PROP_TYPE;
344 			return (TOPO_WALK_ERR);
345 	}
346 
347 }
348 
349 static int
350 fmri_prop(topo_hdl_t *thp, nvlist_t *resource, const char *pgname,
351     const char *pname, topo_type_t type, nvlist_t **prop, int *err)
352 {
353 	int rc;
354 	topo_walk_t *wp;
355 	char *scheme;
356 	struct prop_lookup pl;
357 
358 	if (nvlist_lookup_string(resource, FM_FMRI_SCHEME, &scheme)   != 0)
359 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
360 		    "fmri_prop", NULL));
361 
362 	*prop = NULL;
363 	pl.pl_resource = resource;
364 	pl.pl_err = 0;
365 	pl.pl_type = type;
366 	pl.pl_group = pgname;
367 	pl.pl_name = pname;
368 	pl.pl_prop = prop;
369 	if ((wp = topo_walk_init(thp, scheme, prop_lookup, &pl, err)) == NULL)
370 		return (set_error(thp, pl.pl_err, err, "fmri_prop", NULL));
371 
372 	rc = topo_walk_step(wp, TOPO_WALK_CHILD);
373 	topo_walk_fini(wp);
374 
375 	if (rc == TOPO_WALK_ERR) {
376 		return (set_error(thp, pl.pl_err, err, "fmri_prop", NULL));
377 	}
378 
379 	/*
380 	 * Walk terminated without finding resource or property
381 	 */
382 	if (*prop == NULL)
383 		return (set_error(thp, ETOPO_PROP_NOENT, err, "fmri_prop",
384 		    NULL));
385 
386 	return (0);
387 }
388 
389 int
390 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
391 {
392 
393 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU,
394 	    TOPO_TYPE_FMRI, asru, err) < 0)
395 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
396 
397 	return (0);
398 }
399 
400 int
401 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
402 {
403 
404 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
405 	    TOPO_TYPE_FMRI, fru, err) < 0)
406 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
407 
408 	return (0);
409 }
410 
411 int
412 topo_fmri_label(topo_hdl_t *thp, nvlist_t *fmri, char **label, int *err)
413 {
414 	nvlist_t *nvl, *fru;
415 	char *str;
416 
417 	if (topo_fmri_fru(thp, fmri, &fru, err) < 0)
418 		return (set_error(thp, *err, err, "topo_fmri_label", NULL));
419 
420 	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0)
421 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
422 		    NULL));
423 
424 	if (fmri_prop(thp, fru, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
425 	    TOPO_TYPE_STRING, &nvl, err) < 0) {
426 		nvlist_free(fru);
427 		return (set_error(thp, *err, err, "topo_fmri_label", nvl));
428 	}
429 
430 	nvlist_free(fru);
431 
432 	if (nvlist_lookup_string(nvl, "prop", &str) != 0)
433 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
434 		    nvl));
435 
436 	if ((*label = topo_hdl_strdup(thp, str)) == NULL)
437 		return (set_error(thp, ETOPO_PROP_NOMEM, err,
438 		    "topo_fmri_label", nvl));
439 
440 	nvlist_free(nvl);
441 
442 	return (0);
443 }
444 
445 int
446 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
447 {
448 	int rc;
449 	char *scheme1, *scheme2;
450 	nvlist_t *in;
451 	nvlist_t *out = NULL;
452 	tnode_t *rnode;
453 
454 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
455 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
456 		    TOPO_METH_COMPARE, NULL));
457 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
458 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
459 		    TOPO_METH_COMPARE, NULL));
460 
461 	if (strcmp(scheme1, scheme2) != 0)
462 		return (0);
463 
464 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
465 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
466 		    TOPO_METH_COMPARE, NULL));
467 
468 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
469 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
470 		    NULL));
471 
472 	if (nvlist_add_nvlist(in, "nv1", f1) != 0 ||
473 	    nvlist_add_nvlist(in, "nv2", f2) != 0)
474 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
475 		    in));
476 
477 	if ((rc = topo_method_invoke(rnode, TOPO_METH_COMPARE,
478 	    TOPO_METH_COMPARE_VERSION, in, &out, err)) < 0)
479 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
480 
481 	nvlist_free(in);
482 
483 	return (rc);
484 }
485 
486 struct topo_invoke {
487 	nvlist_t *tl_resource;
488 	topo_walk_cb_t tl_func;
489 	int tl_ret;
490 	void *tl_pdata;
491 };
492 
493 static int
494 walk_invoke(topo_hdl_t *thp, tnode_t *node, void *pdata)
495 {
496 	int rc;
497 	struct topo_invoke *tlp = (struct topo_invoke *)pdata;
498 	nvlist_t *r1, *r2 = tlp->tl_resource;
499 
500 	if (topo_node_resource(node, &r1, &tlp->tl_ret) != 0)
501 		return (TOPO_WALK_ERR);
502 
503 	rc = topo_fmri_compare(thp, r1, r2, &tlp->tl_ret);
504 	nvlist_free(r1);
505 	if (rc == 0)
506 		return (TOPO_WALK_NEXT);
507 	else if (rc == -1)
508 		return (TOPO_WALK_ERR);
509 
510 	tlp->tl_ret = tlp->tl_func(thp, node, tlp->tl_pdata);
511 
512 	return (TOPO_WALK_TERMINATE);
513 }
514 
515 int
516 topo_fmri_invoke(topo_hdl_t *thp, nvlist_t *nvl, topo_walk_cb_t cb_f,
517     void *pdata, int *ret)
518 {
519 	int err;
520 	topo_walk_t *wp;
521 	char *scheme;
522 	struct topo_invoke tl;
523 
524 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme)   != 0)
525 		return (set_error(thp, ETOPO_METHOD_INVAL, ret,
526 		    "topo_fmri_invoke", NULL));
527 
528 	tl.tl_resource = nvl;
529 	tl.tl_func = cb_f;
530 	tl.tl_pdata = pdata;
531 	tl.tl_ret = 0;
532 	if ((wp = topo_walk_init(thp, scheme, walk_invoke, &tl, &err)) == NULL)
533 		return (set_error(thp, err, ret, "topo_fmri_invoke", NULL));
534 
535 	err = topo_walk_step(wp, TOPO_WALK_CHILD);
536 	topo_walk_fini(wp);
537 
538 	if (err == TOPO_WALK_ERR) {
539 		*ret = err;
540 		return (-1);
541 	}
542 
543 	*ret = tl.tl_ret;
544 
545 	return (0);
546 }
547 
548 /*
549  * topo_fmri_create
550  *
551  *	If possible, creates an FMRI of the requested version in the
552  *	requested scheme.  Args are passed as part of the inputs to the
553  *	fmri-create method of the scheme.
554  */
555 nvlist_t *
556 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
557     topo_instance_t inst, nvlist_t *nvl, int *err)
558 {
559 	nvlist_t *ins;
560 	nvlist_t *out;
561 	tnode_t *rnode;
562 
563 	ins = out = NULL;
564 
565 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
566 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
567 		    TOPO_METH_FMRI, NULL));
568 
569 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
570 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
571 		    TOPO_METH_FMRI, NULL));
572 
573 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
574 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
575 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
576 		    TOPO_METH_FMRI, ins));
577 	}
578 
579 	if (nvl != NULL &&
580 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
581 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
582 		    TOPO_METH_FMRI, ins));
583 	}
584 	if (topo_method_invoke(rnode,
585 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
586 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
587 	}
588 	nvlist_free(ins);
589 	return (out);
590 }
591