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_subr.h>
36 #include <topo_string.h>
37 
38 /*ARGSUSED*/
39 static int
40 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
41 {
42 	if (nvlp != NULL)
43 		nvlist_free(nvlp);
44 
45 	topo_dprintf(TOPO_DBG_ERR, "%s failed: %s\n", method,
46 	    topo_strerror(err));
47 
48 	*errp = err;
49 	return (-1);
50 }
51 
52 /*ARGSUSED*/
53 static nvlist_t *
54 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
55 {
56 	if (nvlp != NULL)
57 		nvlist_free(nvlp);
58 
59 	topo_dprintf(TOPO_DBG_ERR, "%s failed: %s\n", method,
60 	    topo_strerror(err));
61 
62 	*errp = err;
63 	return (NULL);
64 }
65 
66 int
67 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err)
68 {
69 	char *scheme, *str;
70 	nvlist_t *out = NULL;
71 	tnode_t *rnode;
72 
73 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
74 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
75 		    TOPO_METH_NVL2STR, out));
76 
77 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
78 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
79 		    TOPO_METH_NVL2STR, out));
80 
81 	if (topo_method_invoke(rnode, TOPO_METH_NVL2STR,
82 	    TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0)
83 		return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out));
84 
85 	if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0)
86 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
87 		    TOPO_METH_NVL2STR, out));
88 
89 	if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL)
90 		return (set_error(thp, ETOPO_NOMEM, err,
91 		    TOPO_METH_NVL2STR, out));
92 
93 	nvlist_free(out);
94 
95 	return (0);
96 }
97 
98 int
99 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
100     int *err)
101 {
102 	char *f, scheme[PATH_MAX];
103 	nvlist_t *out = NULL, *in = NULL;
104 	tnode_t *rnode;
105 
106 	(void) strlcpy(scheme, fmristr, sizeof (scheme));
107 	if ((f = strrchr(scheme, ':')) == NULL)
108 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
109 		    TOPO_METH_STR2NVL, in));
110 
111 	*f = '\0'; /* strip trailing FMRI path */
112 
113 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
114 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
115 		    TOPO_METH_STR2NVL, in));
116 
117 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
118 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
119 		    in));
120 
121 	if (nvlist_add_string(in, "fmri-string", fmristr) != 0)
122 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
123 		    in));
124 
125 	if (topo_method_invoke(rnode, TOPO_METH_STR2NVL,
126 	    TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0)
127 		return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
128 
129 	if (out == NULL ||
130 	    topo_hdl_nvdup(thp, out, fmri) != 0)
131 		return (set_error(thp, ETOPO_FMRI_NVL, err,
132 		    TOPO_METH_STR2NVL, in));
133 
134 	nvlist_free(out);
135 	nvlist_free(in);
136 
137 	return (0);
138 }
139 
140 int
141 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err)
142 {
143 	int rc;
144 	char *scheme;
145 	nvlist_t *out = NULL;
146 	tnode_t *rnode;
147 
148 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
149 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
150 		    TOPO_METH_PRESENT, out));
151 
152 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
153 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
154 		    TOPO_METH_PRESENT, out));
155 
156 	if ((rc = topo_method_invoke(rnode, TOPO_METH_PRESENT,
157 	    TOPO_METH_PRESENT_VERSION, fmri, &out, err)) < 0)
158 		return (set_error(thp, *err, err, TOPO_METH_PRESENT, out));
159 
160 	return (rc);
161 }
162 
163 int
164 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err)
165 {
166 	int rc;
167 	char *scheme;
168 	nvlist_t *in, *out = NULL;
169 	tnode_t *rnode;
170 
171 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
172 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
173 		    TOPO_METH_CONTAINS, out));
174 
175 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
176 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
177 		    TOPO_METH_CONTAINS, out));
178 
179 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
180 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
181 		    out));
182 
183 	if (nvlist_add_nvlist(in, "fmri", fmri) != 0 ||
184 	    nvlist_add_nvlist(in, "subfmri", subfmri) != 0)
185 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
186 		    out));
187 
188 	if (topo_hdl_nvalloc(thp, &out, NV_UNIQUE_NAME) != 0)
189 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
190 		    out));
191 
192 	if ((rc = topo_method_invoke(rnode, TOPO_METH_CONTAINS,
193 	    TOPO_METH_CONTAINS_VERSION, fmri, &out, err)) < 0)
194 		return (set_error(thp, *err, err, TOPO_METH_CONTAINS, out));
195 
196 	return (rc);
197 }
198 
199 int
200 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err)
201 {
202 	int rc;
203 	char *scheme;
204 	nvlist_t *out = NULL;
205 	tnode_t *rnode;
206 
207 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
208 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
209 		    TOPO_METH_UNUSABLE, out));
210 
211 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
212 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
213 		    TOPO_METH_UNUSABLE, out));
214 
215 	if ((rc = topo_method_invoke(rnode, TOPO_METH_UNUSABLE,
216 	    TOPO_METH_UNUSABLE_VERSION, fmri, &out, err)) < 0)
217 		return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out));
218 
219 	return (rc);
220 }
221 
222 int
223 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
224 {
225 	char *scheme;
226 	nvlist_t *out = NULL;
227 	tnode_t *rnode;
228 
229 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
230 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
231 		    TOPO_METH_EXPAND, out));
232 
233 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
234 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
235 		    TOPO_METH_EXPAND, out));
236 
237 	if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
238 	    TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
239 		return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
240 
241 	return (0);
242 }
243 
244 struct rsrc {
245 	int rs_err;
246 	int rs_flag;
247 	nvlist_t **rs_fprop;
248 	nvlist_t *rs_priv;
249 };
250 
251 /*ARGSUSED*/
252 static int
253 get_prop(topo_hdl_t *thp, tnode_t *node, void *pdata)
254 {
255 	struct rsrc *rsp = (struct rsrc *)pdata;
256 
257 	if (rsp->rs_flag == 0) {
258 		if (topo_node_asru(node, rsp->rs_fprop, rsp->rs_priv,
259 		    &rsp->rs_err) < 0)
260 			return (-1);
261 
262 		return (0);
263 	} else {
264 		if (topo_node_fru(node, rsp->rs_fprop, rsp->rs_priv,
265 		    &rsp->rs_err) < 0)
266 			return (-1);
267 
268 		return (0);
269 	}
270 }
271 
272 int
273 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
274 {
275 	char *uuid = NULL;
276 	struct rsrc r;
277 
278 	if (thp->th_uuid == NULL) {
279 		if ((uuid = topo_snap_hold(thp, NULL, err)) == NULL)
280 			return (set_error(thp, *err, err, "topo_fmri_asru",
281 			    NULL));
282 	}
283 
284 	r.rs_flag = 0;
285 	r.rs_err = 0;
286 	r.rs_priv = nvl;
287 	r.rs_fprop = asru;
288 	if (topo_fmri_invoke(thp, nvl, get_prop, &r, err) < 0) {
289 		if (uuid != NULL) {
290 			topo_hdl_strfree(thp, uuid);
291 			topo_snap_release(thp);
292 		}
293 
294 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
295 	}
296 
297 	if (uuid != NULL) {
298 		topo_hdl_strfree(thp, uuid);
299 		topo_snap_release(thp);
300 	}
301 
302 	return (0);
303 }
304 
305 int
306 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru,
307     int *err)
308 {
309 	char *uuid = NULL;
310 	struct rsrc r;
311 
312 	if (thp->th_uuid == NULL) {
313 		if ((uuid = topo_snap_hold(thp, NULL, err)) == NULL)
314 			return (set_error(thp, *err, err, "topo_fmri_fru",
315 			    NULL));
316 	}
317 
318 	r.rs_flag = 1;
319 	r.rs_err = 0;
320 	r.rs_priv = nvl;
321 	r.rs_fprop = fru;
322 	if (topo_fmri_invoke(thp, nvl, get_prop, &r, err) < 0) {
323 		if (uuid != NULL) {
324 			topo_hdl_strfree(thp, uuid);
325 			topo_snap_release(thp);
326 		}
327 
328 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
329 	}
330 
331 	if (uuid != NULL) {
332 		topo_hdl_strfree(thp, uuid);
333 		topo_snap_release(thp);
334 	}
335 
336 	return (0);
337 }
338 
339 int
340 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
341 {
342 	int rc;
343 	char *scheme1, *scheme2;
344 	nvlist_t *in;
345 	nvlist_t *out = NULL;
346 	tnode_t *rnode;
347 
348 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
349 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
350 		    TOPO_METH_COMPARE, NULL));
351 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
352 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
353 		    TOPO_METH_COMPARE, NULL));
354 
355 	if (strcmp(scheme1, scheme2) != 0)
356 		return (0);
357 
358 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
359 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
360 		    TOPO_METH_COMPARE, NULL));
361 
362 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
363 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
364 		    NULL));
365 
366 	if (nvlist_add_nvlist(in, "nv1", f1) != 0 ||
367 	    nvlist_add_nvlist(in, "nv2", f2) != 0)
368 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
369 		    in));
370 
371 	if ((rc = topo_method_invoke(rnode, TOPO_METH_COMPARE,
372 	    TOPO_METH_COMPARE_VERSION, in, &out, err)) < 0)
373 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
374 
375 	nvlist_free(in);
376 
377 	return (rc);
378 }
379 
380 struct topo_lookup {
381 	nvlist_t *tl_resource;
382 	topo_walk_cb_t tl_func;
383 	int tl_err;
384 	void *tl_pdata;
385 };
386 
387 static int
388 walk_lookup(topo_hdl_t *thp, tnode_t *node, void *pdata)
389 {
390 	int rc;
391 	struct topo_lookup *tlp = (struct topo_lookup *)pdata;
392 	nvlist_t *r1, *r2 = tlp->tl_resource;
393 
394 	if (topo_node_resource(node, &r1, &tlp->tl_err) != 0)
395 		return (TOPO_WALK_ERR);
396 
397 	rc = topo_fmri_compare(thp, r1, r2, &tlp->tl_err);
398 	nvlist_free(r1);
399 	if (rc == 0)
400 		return (TOPO_WALK_NEXT);
401 	else if (rc == -1)
402 		return (TOPO_WALK_ERR);
403 
404 	tlp->tl_err = tlp->tl_func(thp, node, tlp->tl_pdata);
405 
406 	return (TOPO_WALK_TERMINATE);
407 }
408 
409 int
410 topo_fmri_invoke(topo_hdl_t *thp, nvlist_t *nvl, topo_walk_cb_t cb_f,
411     void *pdata, int *err)
412 {
413 	topo_walk_t *wp;
414 	char *scheme;
415 	struct topo_lookup tl;
416 
417 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme)	 != 0)
418 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
419 		    "topo_fmri_invoke", NULL));
420 
421 	tl.tl_resource = nvl;
422 	tl.tl_func = cb_f;
423 	tl.tl_pdata = pdata;
424 	tl.tl_err = 0;
425 	if ((wp = topo_walk_init(thp, scheme, walk_lookup, &tl, err)) == NULL)
426 		return (set_error(thp, *err, err, "topo_fmri_invoke", NULL));
427 
428 	(void) topo_walk_step(wp, TOPO_WALK_CHILD);
429 	topo_walk_fini(wp);
430 
431 	if (tl.tl_err != 0) {
432 		*err = tl.tl_err;
433 		return (-1);
434 	}
435 
436 	return (0);
437 }
438 
439 /*
440  * topo_fmri_create
441  *
442  *	If possible, creates an FMRI of the requested version in the
443  *	requested scheme.  Args are passed as part of the inputs to the
444  *	fmri-create method of the scheme.
445  */
446 nvlist_t *
447 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
448     topo_instance_t inst, nvlist_t *nvl, int *err)
449 {
450 	nvlist_t *ins;
451 	nvlist_t *out;
452 	tnode_t *rnode;
453 
454 	ins = out = NULL;
455 
456 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
457 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
458 		    TOPO_METH_FMRI, NULL));
459 
460 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
461 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
462 		    TOPO_METH_FMRI, NULL));
463 
464 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
465 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
466 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
467 		    TOPO_METH_FMRI, ins));
468 	}
469 
470 	if (nvl != NULL &&
471 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
472 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
473 		    TOPO_METH_FMRI, ins));
474 	}
475 	if (topo_method_invoke(rnode,
476 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
477 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
478 	}
479 	nvlist_free(ins);
480 	return (out);
481 }
482