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 2006 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 	int rc;
236 	char *scheme;
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 ((rc = 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 	return (rc);
253 }
254 
255 int
256 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
257 {
258 	char *scheme;
259 	nvlist_t *out = NULL;
260 	tnode_t *rnode;
261 
262 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
263 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
264 		    TOPO_METH_EXPAND, out));
265 
266 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
267 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
268 		    TOPO_METH_EXPAND, out));
269 
270 	if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
271 	    TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
272 		return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
273 
274 	return (0);
275 }
276 
277 struct prop_lookup {
278 	int pl_err;
279 	topo_type_t pl_type;
280 	const char *pl_group;
281 	const char *pl_name;
282 	nvlist_t **pl_prop;
283 	nvlist_t *pl_resource;
284 };
285 
286 static int
287 prop_lookup(topo_hdl_t *thp, tnode_t *node, void *pdata)
288 {
289 	int rc;
290 	nvlist_t *r1;
291 	struct prop_lookup *plp = (struct prop_lookup *)pdata;
292 
293 	if (topo_node_resource(node, &r1, &plp->pl_err) != 0)
294 		return (TOPO_WALK_ERR);
295 
296 	rc = topo_fmri_compare(thp, r1, plp->pl_resource, &plp->pl_err);
297 	nvlist_free(r1);
298 	if (rc == 0)
299 		return (TOPO_WALK_NEXT);
300 	if (rc < 0)
301 		return (TOPO_WALK_ERR);
302 
303 	/*
304 	 * Special case for dynamically created ASRU and FRU
305 	 */
306 	if (strcmp(plp->pl_group, TOPO_PGROUP_PROTOCOL) == 0) {
307 		if (strcmp(plp->pl_name, TOPO_PROP_ASRU) == 0) {
308 			if (topo_node_asru(node, plp->pl_prop, plp->pl_resource,
309 			    &plp->pl_err) < 0) {
310 				return (TOPO_WALK_ERR);
311 			}
312 			return (0);
313 		} else if (strcmp(plp->pl_name, TOPO_PROP_FRU) == 0) {
314 			if (topo_node_fru(node, plp->pl_prop, plp->pl_resource,
315 			    &plp->pl_err) < 0) {
316 				return (TOPO_WALK_ERR);
317 			}
318 			return (0);
319 		}
320 	}
321 
322 	switch (plp->pl_type) {
323 		case TOPO_TYPE_STRING:
324 		{
325 			char *str;
326 			if (topo_prop_get_string(node, plp->pl_group,
327 			    plp->pl_name, &str, &plp->pl_err) < 0)
328 				return (TOPO_WALK_ERR);
329 
330 			if (nvlist_add_string(*plp->pl_prop, "prop", str)
331 			    != 0) {
332 				topo_hdl_strfree(thp, str);
333 				plp->pl_err = ETOPO_PROP_NVL;
334 				return (TOPO_WALK_ERR);
335 			}
336 			topo_hdl_strfree(thp, str);
337 			return (TOPO_WALK_TERMINATE);
338 		}
339 		default:
340 			plp->pl_err = ETOPO_PROP_TYPE;
341 			return (TOPO_WALK_ERR);
342 	}
343 
344 }
345 
346 static int
347 fmri_prop(topo_hdl_t *thp, nvlist_t *resource, const char *pgname,
348     const char *pname, topo_type_t type, nvlist_t **prop, int *err)
349 {
350 	int rc;
351 	topo_walk_t *wp;
352 	char *scheme;
353 	struct prop_lookup pl;
354 
355 	if (nvlist_lookup_string(resource, FM_FMRI_SCHEME, &scheme)   != 0)
356 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
357 		    "fmri_prop", NULL));
358 
359 	pl.pl_resource = resource;
360 	pl.pl_err = 0;
361 	pl.pl_type = type;
362 	pl.pl_group = pgname;
363 	pl.pl_name = pname;
364 	pl.pl_prop = prop;
365 	if ((wp = topo_walk_init(thp, scheme, prop_lookup, &pl, err)) == NULL)
366 		return (set_error(thp, pl.pl_err, err, "fmri_prop", NULL));
367 
368 	rc = topo_walk_step(wp, TOPO_WALK_CHILD);
369 	topo_walk_fini(wp);
370 
371 	if (rc == TOPO_WALK_ERR) {
372 		return (set_error(thp, pl.pl_err, err, "fmri_prop", NULL));
373 	}
374 
375 	return (0);
376 }
377 
378 int
379 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
380 {
381 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU,
382 	    TOPO_TYPE_FMRI, asru, err) < 0)
383 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
384 
385 	return (0);
386 }
387 
388 int
389 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
390 {
391 
392 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
393 	    TOPO_TYPE_FMRI, fru, err) < 0)
394 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
395 
396 	return (0);
397 }
398 
399 int
400 topo_fmri_label(topo_hdl_t *thp, nvlist_t *fmri, char **label, int *err)
401 {
402 	nvlist_t *nvl, *fru;
403 	char *str;
404 
405 	if (topo_fmri_fru(thp, fmri, &fru, err) < 0)
406 		return (set_error(thp, *err, err, "topo_fmri_label", NULL));
407 
408 	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0)
409 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
410 		    NULL));
411 
412 	if (fmri_prop(thp, fru, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
413 	    TOPO_TYPE_STRING, &nvl, err) < 0) {
414 		nvlist_free(fru);
415 		return (set_error(thp, *err, err, "topo_fmri_label", nvl));
416 	}
417 
418 	nvlist_free(fru);
419 
420 	if (nvlist_lookup_string(nvl, "prop", &str) != 0)
421 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
422 		    nvl));
423 
424 	if ((*label = topo_hdl_strdup(thp, str)) == NULL)
425 		return (set_error(thp, ETOPO_PROP_NOMEM, err,
426 		    "topo_fmri_label", nvl));
427 
428 	nvlist_free(nvl);
429 
430 	return (0);
431 }
432 
433 int
434 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
435 {
436 	int rc;
437 	char *scheme1, *scheme2;
438 	nvlist_t *in;
439 	nvlist_t *out = NULL;
440 	tnode_t *rnode;
441 
442 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
443 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
444 		    TOPO_METH_COMPARE, NULL));
445 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
446 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
447 		    TOPO_METH_COMPARE, NULL));
448 
449 	if (strcmp(scheme1, scheme2) != 0)
450 		return (0);
451 
452 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
453 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
454 		    TOPO_METH_COMPARE, NULL));
455 
456 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
457 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
458 		    NULL));
459 
460 	if (nvlist_add_nvlist(in, "nv1", f1) != 0 ||
461 	    nvlist_add_nvlist(in, "nv2", f2) != 0)
462 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
463 		    in));
464 
465 	if ((rc = topo_method_invoke(rnode, TOPO_METH_COMPARE,
466 	    TOPO_METH_COMPARE_VERSION, in, &out, err)) < 0)
467 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
468 
469 	nvlist_free(in);
470 
471 	return (rc);
472 }
473 
474 struct topo_invoke {
475 	nvlist_t *tl_resource;
476 	topo_walk_cb_t tl_func;
477 	int tl_ret;
478 	void *tl_pdata;
479 };
480 
481 static int
482 walk_invoke(topo_hdl_t *thp, tnode_t *node, void *pdata)
483 {
484 	int rc;
485 	struct topo_invoke *tlp = (struct topo_invoke *)pdata;
486 	nvlist_t *r1, *r2 = tlp->tl_resource;
487 
488 	if (topo_node_resource(node, &r1, &tlp->tl_ret) != 0)
489 		return (TOPO_WALK_ERR);
490 
491 	rc = topo_fmri_compare(thp, r1, r2, &tlp->tl_ret);
492 	nvlist_free(r1);
493 	if (rc == 0)
494 		return (TOPO_WALK_NEXT);
495 	else if (rc == -1)
496 		return (TOPO_WALK_ERR);
497 
498 	tlp->tl_ret = tlp->tl_func(thp, node, tlp->tl_pdata);
499 
500 	return (TOPO_WALK_TERMINATE);
501 }
502 
503 int
504 topo_fmri_invoke(topo_hdl_t *thp, nvlist_t *nvl, topo_walk_cb_t cb_f,
505     void *pdata, int *ret)
506 {
507 	int err;
508 	topo_walk_t *wp;
509 	char *scheme;
510 	struct topo_invoke tl;
511 
512 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme)   != 0)
513 		return (set_error(thp, ETOPO_METHOD_INVAL, ret,
514 		    "topo_fmri_invoke", NULL));
515 
516 	tl.tl_resource = nvl;
517 	tl.tl_func = cb_f;
518 	tl.tl_pdata = pdata;
519 	tl.tl_ret = 0;
520 	if ((wp = topo_walk_init(thp, scheme, walk_invoke, &tl, &err)) == NULL)
521 		return (set_error(thp, err, ret, "topo_fmri_invoke", NULL));
522 
523 	err = topo_walk_step(wp, TOPO_WALK_CHILD);
524 	topo_walk_fini(wp);
525 
526 	if (err == TOPO_WALK_ERR) {
527 		*ret = err;
528 		return (-1);
529 	}
530 
531 	*ret = tl.tl_ret;
532 
533 	return (0);
534 }
535 
536 /*
537  * topo_fmri_create
538  *
539  *	If possible, creates an FMRI of the requested version in the
540  *	requested scheme.  Args are passed as part of the inputs to the
541  *	fmri-create method of the scheme.
542  */
543 nvlist_t *
544 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
545     topo_instance_t inst, nvlist_t *nvl, int *err)
546 {
547 	nvlist_t *ins;
548 	nvlist_t *out;
549 	tnode_t *rnode;
550 
551 	ins = out = NULL;
552 
553 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
554 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
555 		    TOPO_METH_FMRI, NULL));
556 
557 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
558 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
559 		    TOPO_METH_FMRI, NULL));
560 
561 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
562 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
563 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
564 		    TOPO_METH_FMRI, ins));
565 	}
566 
567 	if (nvl != NULL &&
568 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
569 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
570 		    TOPO_METH_FMRI, ins));
571 	}
572 	if (topo_method_invoke(rnode,
573 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
574 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
575 	}
576 	nvlist_free(ins);
577 	return (out);
578 }
579