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 rsrc {
278 	int rs_err;
279 	int rs_flag;
280 	nvlist_t **rs_fprop;
281 	nvlist_t *rs_priv;
282 };
283 
284 /*ARGSUSED*/
285 static int
286 get_prop(topo_hdl_t *thp, tnode_t *node, void *pdata)
287 {
288 	struct rsrc *rsp = (struct rsrc *)pdata;
289 
290 	if (rsp->rs_flag == 0) {
291 		if (topo_node_asru(node, rsp->rs_fprop, rsp->rs_priv,
292 		    &rsp->rs_err) < 0)
293 			return (-1);
294 
295 		return (0);
296 	} else {
297 		if (topo_node_fru(node, rsp->rs_fprop, rsp->rs_priv,
298 		    &rsp->rs_err) < 0)
299 			return (-1);
300 
301 		return (0);
302 	}
303 }
304 
305 int
306 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
307 {
308 	struct rsrc r;
309 
310 	r.rs_flag = 0;
311 	r.rs_err = 0;
312 	r.rs_priv = nvl;
313 	r.rs_fprop = asru;
314 	if (topo_fmri_invoke(thp, nvl, get_prop, &r, err) < 0) {
315 
316 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
317 	}
318 
319 	return (0);
320 }
321 
322 int
323 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru,
324     int *err)
325 {
326 	struct rsrc r;
327 
328 	r.rs_flag = 1;
329 	r.rs_err = 0;
330 	r.rs_priv = nvl;
331 	r.rs_fprop = fru;
332 	if (topo_fmri_invoke(thp, nvl, get_prop, &r, err) < 0) {
333 
334 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
335 	}
336 
337 	return (0);
338 }
339 
340 int
341 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
342 {
343 	int rc;
344 	char *scheme1, *scheme2;
345 	nvlist_t *in;
346 	nvlist_t *out = NULL;
347 	tnode_t *rnode;
348 
349 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
350 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
351 		    TOPO_METH_COMPARE, NULL));
352 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
353 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
354 		    TOPO_METH_COMPARE, NULL));
355 
356 	if (strcmp(scheme1, scheme2) != 0)
357 		return (0);
358 
359 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
360 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
361 		    TOPO_METH_COMPARE, NULL));
362 
363 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
364 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
365 		    NULL));
366 
367 	if (nvlist_add_nvlist(in, "nv1", f1) != 0 ||
368 	    nvlist_add_nvlist(in, "nv2", f2) != 0)
369 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
370 		    in));
371 
372 	if ((rc = topo_method_invoke(rnode, TOPO_METH_COMPARE,
373 	    TOPO_METH_COMPARE_VERSION, in, &out, err)) < 0)
374 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
375 
376 	nvlist_free(in);
377 
378 	return (rc);
379 }
380 
381 struct topo_lookup {
382 	nvlist_t *tl_resource;
383 	topo_walk_cb_t tl_func;
384 	int tl_ret;
385 	void *tl_pdata;
386 };
387 
388 static int
389 walk_lookup(topo_hdl_t *thp, tnode_t *node, void *pdata)
390 {
391 	int rc;
392 	struct topo_lookup *tlp = (struct topo_lookup *)pdata;
393 	nvlist_t *r1, *r2 = tlp->tl_resource;
394 
395 	if (topo_node_resource(node, &r1, &tlp->tl_ret) != 0)
396 		return (TOPO_WALK_ERR);
397 
398 	rc = topo_fmri_compare(thp, r1, r2, &tlp->tl_ret);
399 	nvlist_free(r1);
400 	if (rc == 0)
401 		return (TOPO_WALK_NEXT);
402 	else if (rc == -1)
403 		return (TOPO_WALK_ERR);
404 
405 	tlp->tl_ret = tlp->tl_func(thp, node, tlp->tl_pdata);
406 
407 	return (TOPO_WALK_TERMINATE);
408 }
409 
410 int
411 topo_fmri_invoke(topo_hdl_t *thp, nvlist_t *nvl, topo_walk_cb_t cb_f,
412     void *pdata, int *ret)
413 {
414 	int err;
415 	topo_walk_t *wp;
416 	char *scheme;
417 	struct topo_lookup tl;
418 
419 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme)   != 0)
420 		return (set_error(thp, ETOPO_METHOD_INVAL, ret,
421 		    "topo_fmri_invoke", NULL));
422 
423 	tl.tl_resource = nvl;
424 	tl.tl_func = cb_f;
425 	tl.tl_pdata = pdata;
426 	tl.tl_ret = 0;
427 	if ((wp = topo_walk_init(thp, scheme, walk_lookup, &tl, &err)) == NULL)
428 		return (set_error(thp, err, ret, "topo_fmri_invoke", NULL));
429 
430 	err = topo_walk_step(wp, TOPO_WALK_CHILD);
431 	topo_walk_fini(wp);
432 
433 	if (err == TOPO_WALK_ERR) {
434 		*ret = err;
435 		return (-1);
436 	}
437 
438 	*ret = tl.tl_ret;
439 
440 	return (0);
441 }
442 
443 /*
444  * topo_fmri_create
445  *
446  *	If possible, creates an FMRI of the requested version in the
447  *	requested scheme.  Args are passed as part of the inputs to the
448  *	fmri-create method of the scheme.
449  */
450 nvlist_t *
451 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
452     topo_instance_t inst, nvlist_t *nvl, int *err)
453 {
454 	nvlist_t *ins;
455 	nvlist_t *out;
456 	tnode_t *rnode;
457 
458 	ins = out = NULL;
459 
460 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
461 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
462 		    TOPO_METH_FMRI, NULL));
463 
464 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
465 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
466 		    TOPO_METH_FMRI, NULL));
467 
468 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
469 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
470 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
471 		    TOPO_METH_FMRI, ins));
472 	}
473 
474 	if (nvl != NULL &&
475 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
476 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
477 		    TOPO_METH_FMRI, ins));
478 	}
479 	if (topo_method_invoke(rnode,
480 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
481 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
482 	}
483 	nvlist_free(ins);
484 	return (out);
485 }
486