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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /*
27 * Copyright 2020 Joyent, Inc.
28 */
29
30 #include <ctype.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <fm/topo_mod.h>
34 #include <fm/fmd_fmri.h>
35 #include <sys/fm/protocol.h>
36 #include <topo_alloc.h>
37 #include <topo_digraph.h>
38 #include <topo_error.h>
39 #include <topo_hc.h>
40 #include <topo_method.h>
41 #include <topo_subr.h>
42 #include <topo_string.h>
43
44 /*
45 * Topology node properties and method operations may be accessed by FMRI.
46 * The FMRI used to perform property look-ups and method operations is
47 * the FMRI contained in the matching topology node's protocol property
48 * grouping for the resource property. The full range of fmd(8)
49 * scheme plugin operations are supported as long as a backend method is
50 * supplied by a scheme-specific enumerator or the enumerator module that
51 * created the matching topology node. Support for fmd scheme operations
52 * include:
53 *
54 * - expand
55 * - present
56 * - replaced
57 * - contains
58 * - unusable
59 * - service_state
60 * - nvl2str
61 * - retire
62 * - unretire
63 *
64 * In addition, the following operations are supported per-FMRI:
65 *
66 * - str2nvl: convert string-based FMRI to nvlist
67 * - compare: compare two FMRIs
68 * - asru: lookup associated ASRU property by FMRI
69 * - fru: lookup associated FRU by FMRI
70 * - create: an FMRI nvlist by scheme type
71 * - propery lookup
72 *
73 * These routines may only be called by consumers of a topology snapshot.
74 * They may not be called by libtopo enumerator or method modules.
75 */
76
77 /*ARGSUSED*/
78 static int
set_error(topo_hdl_t * thp,int err,int * errp,char * method,nvlist_t * nvlp)79 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
80 {
81 nvlist_free(nvlp);
82
83 topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
84 topo_strerror(err));
85
86 *errp = err;
87 return (-1);
88 }
89
90 /*ARGSUSED*/
91 static nvlist_t *
set_nverror(topo_hdl_t * thp,int err,int * errp,char * method,nvlist_t * nvlp)92 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
93 {
94 nvlist_free(nvlp);
95
96 topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
97 topo_strerror(err));
98
99 *errp = err;
100 return (NULL);
101 }
102
103 int
topo_fmri_nvl2str(topo_hdl_t * thp,nvlist_t * fmri,char ** fmristr,int * err)104 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err)
105 {
106 char *scheme, *str;
107 nvlist_t *out = NULL;
108 tnode_t *rnode;
109
110 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
111 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
112 TOPO_METH_NVL2STR, out));
113
114 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
115 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
116 TOPO_METH_NVL2STR, out));
117
118 if (topo_method_invoke(rnode, TOPO_METH_NVL2STR,
119 TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0)
120 return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out));
121
122 if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0)
123 return (set_error(thp, ETOPO_METHOD_INVAL, err,
124 TOPO_METH_NVL2STR, out));
125
126 if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL)
127 return (set_error(thp, ETOPO_NOMEM, err,
128 TOPO_METH_NVL2STR, out));
129
130 nvlist_free(out);
131
132 return (0);
133 }
134
135 int
topo_fmri_str2nvl(topo_hdl_t * thp,const char * fmristr,nvlist_t ** fmri,int * err)136 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
137 int *err)
138 {
139 char *f, buf[PATH_MAX], *method = TOPO_METH_STR2NVL;
140 nvlist_t *out = NULL, *in = NULL;
141 tnode_t *rnode;
142 boolean_t is_path = B_FALSE;
143
144 /*
145 * For path FMRI's the scheme is encoded in the authority portion of
146 * the FMRI - e.g.
147 *
148 * path://scheme=<scheme>/...
149 */
150 if (strncmp(fmristr, "path://", 7) == 0) {
151 char *scheme_start, *scheme_end;
152
153 is_path = B_TRUE;
154 method = TOPO_METH_PATH_STR2NVL;
155
156 if ((scheme_start = strchr(fmristr, '=')) == NULL) {
157 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
158 TOPO_METH_STR2NVL, in));
159 }
160 scheme_start++;
161 if ((scheme_end = strchr(scheme_start, '/')) == NULL) {
162 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
163 TOPO_METH_STR2NVL, in));
164 }
165 (void) strlcpy(buf, scheme_start,
166 (scheme_end - scheme_start) + 1);
167 } else {
168 (void) strlcpy(buf, fmristr, sizeof (buf));
169
170 if ((f = strchr(buf, ':')) == NULL)
171 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
172 TOPO_METH_STR2NVL, in));
173
174 *f = '\0'; /* strip trailing FMRI path */
175 }
176
177 if (is_path) {
178 topo_digraph_t *tdg;
179
180 if ((tdg = topo_digraph_get(thp, buf)) == NULL) {
181 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
182 TOPO_METH_STR2NVL, in));
183 }
184 rnode = tdg->tdg_rootnode;
185 } else if ((rnode = topo_hdl_root(thp, buf)) == NULL) {
186 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
187 TOPO_METH_STR2NVL, in));
188 }
189
190 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
191 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
192 in));
193
194 if (nvlist_add_string(in, "fmri-string", fmristr) != 0)
195 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
196 in));
197
198 if (topo_method_invoke(rnode, method, TOPO_METH_STR2NVL_VERSION, in,
199 &out, err) != 0)
200 return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
201
202 nvlist_free(in);
203
204 if (out == NULL ||
205 topo_hdl_nvdup(thp, out, fmri) != 0)
206 return (set_error(thp, ETOPO_FMRI_NVL, err,
207 TOPO_METH_STR2NVL, out));
208
209 nvlist_free(out);
210
211 return (0);
212 }
213
214 int
topo_fmri_present(topo_hdl_t * thp,nvlist_t * fmri,int * err)215 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err)
216 {
217 uint32_t present = 0;
218 char *scheme;
219 nvlist_t *out = NULL;
220 tnode_t *rnode;
221
222 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
223 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
224 TOPO_METH_PRESENT, out));
225
226 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
227 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
228 TOPO_METH_PRESENT, out));
229
230 if (topo_method_invoke(rnode, TOPO_METH_PRESENT,
231 TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) {
232 (void) set_error(thp, *err, err, TOPO_METH_PRESENT, out);
233 return (present);
234 }
235
236 (void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present);
237 nvlist_free(out);
238
239 return (present);
240 }
241
242 int
topo_fmri_replaced(topo_hdl_t * thp,nvlist_t * fmri,int * err)243 topo_fmri_replaced(topo_hdl_t *thp, nvlist_t *fmri, int *err)
244 {
245 uint32_t replaced = FMD_OBJ_STATE_NOT_PRESENT;
246 char *scheme;
247 nvlist_t *out = NULL;
248 tnode_t *rnode;
249
250 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
251 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
252 TOPO_METH_REPLACED, out));
253
254 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
255 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
256 TOPO_METH_REPLACED, out));
257
258 if (topo_method_invoke(rnode, TOPO_METH_REPLACED,
259 TOPO_METH_REPLACED_VERSION, fmri, &out, err) < 0) {
260 (void) set_error(thp, *err, err, TOPO_METH_REPLACED, out);
261 return (FMD_OBJ_STATE_UNKNOWN);
262 }
263
264 (void) nvlist_lookup_uint32(out, TOPO_METH_REPLACED_RET, &replaced);
265 nvlist_free(out);
266
267 return (replaced);
268 }
269
270 int
topo_fmri_contains(topo_hdl_t * thp,nvlist_t * fmri,nvlist_t * subfmri,int * err)271 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err)
272 {
273 uint32_t contains;
274 char *scheme;
275 nvlist_t *in = NULL, *out = NULL;
276 tnode_t *rnode;
277
278 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
279 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
280 TOPO_METH_CONTAINS, NULL));
281
282 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
283 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
284 TOPO_METH_CONTAINS, NULL));
285
286 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
287 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
288 NULL));
289
290 if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, fmri) != 0 ||
291 nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, subfmri) != 0)
292 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
293 in));
294
295 if (topo_method_invoke(rnode, TOPO_METH_CONTAINS,
296 TOPO_METH_CONTAINS_VERSION, in, &out, err) < 0)
297 return (set_error(thp, *err, err, TOPO_METH_CONTAINS, in));
298
299 (void) nvlist_lookup_uint32(out, TOPO_METH_CONTAINS_RET, &contains);
300 nvlist_free(in);
301 nvlist_free(out);
302
303 return (contains);
304 }
305
306 int
topo_fmri_unusable(topo_hdl_t * thp,nvlist_t * fmri,int * err)307 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err)
308 {
309 char *scheme;
310 uint32_t unusable = 0;
311 nvlist_t *out = NULL;
312 tnode_t *rnode;
313
314 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
315 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
316 TOPO_METH_UNUSABLE, out));
317
318 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
319 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
320 TOPO_METH_UNUSABLE, out));
321
322 if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE,
323 TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0)
324 return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out));
325
326 (void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable);
327 nvlist_free(out);
328
329 return (unusable);
330 }
331
332 int
topo_fmri_retire(topo_hdl_t * thp,nvlist_t * fmri,int * err)333 topo_fmri_retire(topo_hdl_t *thp, nvlist_t *fmri, int *err)
334 {
335 char *scheme;
336 uint32_t status;
337 nvlist_t *out = NULL;
338 tnode_t *rnode;
339
340 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
341 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
342 TOPO_METH_RETIRE, out));
343
344 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
345 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
346 TOPO_METH_RETIRE, out));
347
348 if (topo_method_invoke(rnode, TOPO_METH_RETIRE,
349 TOPO_METH_RETIRE_VERSION, fmri, &out, err) < 0)
350 return (set_error(thp, *err, err, TOPO_METH_RETIRE, out));
351
352 if (nvlist_lookup_uint32(out, TOPO_METH_RETIRE_RET, &status) != 0)
353 return (set_error(thp, ETOPO_METHOD_FAIL, err,
354 TOPO_METH_RETIRE, out));
355 nvlist_free(out);
356
357 return (status);
358 }
359
360 int
topo_fmri_unretire(topo_hdl_t * thp,nvlist_t * fmri,int * err)361 topo_fmri_unretire(topo_hdl_t *thp, nvlist_t *fmri, int *err)
362 {
363 char *scheme;
364 uint32_t status;
365 nvlist_t *out = NULL;
366 tnode_t *rnode;
367
368 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
369 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
370 TOPO_METH_UNRETIRE, out));
371
372 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
373 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
374 TOPO_METH_UNRETIRE, out));
375
376 if (topo_method_invoke(rnode, TOPO_METH_UNRETIRE,
377 TOPO_METH_UNRETIRE_VERSION, fmri, &out, err) < 0)
378 return (set_error(thp, *err, err, TOPO_METH_UNRETIRE, out));
379
380 if (nvlist_lookup_uint32(out, TOPO_METH_UNRETIRE_RET, &status) != 0) {
381 nvlist_free(out);
382 return (set_error(thp, ETOPO_METHOD_FAIL, err,
383 TOPO_METH_UNRETIRE, out));
384 }
385 nvlist_free(out);
386
387 return (status);
388 }
389
390 int
topo_fmri_service_state(topo_hdl_t * thp,nvlist_t * fmri,int * err)391 topo_fmri_service_state(topo_hdl_t *thp, nvlist_t *fmri, int *err)
392 {
393 char *scheme;
394 uint32_t service_state = FMD_SERVICE_STATE_UNKNOWN;
395 nvlist_t *out = NULL;
396 tnode_t *rnode;
397
398 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
399 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
400 TOPO_METH_SERVICE_STATE, out));
401
402 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
403 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
404 TOPO_METH_SERVICE_STATE, out));
405
406 if (topo_method_invoke(rnode, TOPO_METH_SERVICE_STATE,
407 TOPO_METH_SERVICE_STATE_VERSION, fmri, &out, err) < 0)
408 return (set_error(thp, *err, err, TOPO_METH_SERVICE_STATE,
409 out));
410
411 (void) nvlist_lookup_uint32(out, TOPO_METH_SERVICE_STATE_RET,
412 &service_state);
413 nvlist_free(out);
414
415 return (service_state);
416 }
417
418 int
topo_fmri_expand(topo_hdl_t * thp,nvlist_t * fmri,int * err)419 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
420 {
421 char *scheme;
422 nvlist_t *out = NULL;
423 tnode_t *rnode;
424
425 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
426 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
427 TOPO_METH_EXPAND, out));
428
429 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
430 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
431 TOPO_METH_EXPAND, out));
432
433 if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
434 TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
435 return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
436
437 return (0);
438 }
439
440 static int
fmri_prop(topo_hdl_t * thp,nvlist_t * rsrc,const char * pgname,const char * pname,nvlist_t * args,nvlist_t ** prop,int * err)441 fmri_prop(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
442 const char *pname, nvlist_t *args, nvlist_t **prop,
443 int *err)
444 {
445 int rv;
446 nvlist_t *in = NULL;
447 tnode_t *rnode;
448 char *scheme;
449
450 if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
451 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
452 TOPO_METH_PROP_GET, in));
453
454 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
455 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
456 TOPO_METH_PROP_GET, in));
457
458 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
459 return (set_error(thp, ETOPO_FMRI_NVL, err,
460 TOPO_METH_PROP_GET, in));
461
462 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
463 rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
464 rv |= nvlist_add_string(in, TOPO_PROP_VAL_NAME, pname);
465 if (args != NULL)
466 rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
467 if (rv != 0)
468 return (set_error(thp, ETOPO_FMRI_NVL, err,
469 TOPO_METH_PROP_GET, in));
470
471 *prop = NULL;
472 rv = topo_method_invoke(rnode, TOPO_METH_PROP_GET,
473 TOPO_METH_PROP_GET_VERSION, in, prop, err);
474
475 nvlist_free(in);
476
477 if (rv != 0)
478 return (-1); /* *err is set for us */
479
480 if (*prop == NULL)
481 return (set_error(thp, ETOPO_PROP_NOENT, err,
482 TOPO_METH_PROP_GET, NULL));
483 return (0);
484 }
485
486 int
topo_fmri_asru(topo_hdl_t * thp,nvlist_t * nvl,nvlist_t ** asru,int * err)487 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
488 {
489 nvlist_t *ap, *prop = NULL;
490
491 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU,
492 nvl, &prop, err) < 0)
493 return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
494
495 if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &ap) != 0)
496 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_asru",
497 prop));
498
499 if (topo_hdl_nvdup(thp, ap, asru) < 0)
500 return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_asru",
501 prop));
502
503 nvlist_free(prop);
504
505 return (0);
506 }
507
508 int
topo_fmri_fru(topo_hdl_t * thp,nvlist_t * nvl,nvlist_t ** fru,int * err)509 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
510 {
511 nvlist_t *fp, *prop = NULL;
512
513 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
514 nvl, &prop, err) < 0)
515 return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
516
517 if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0)
518 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_fru",
519 prop));
520
521 if (topo_hdl_nvdup(thp, fp, fru) < 0)
522 return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_fru",
523 prop));
524
525 nvlist_free(prop);
526
527 return (0);
528 }
529
530 int
topo_fmri_label(topo_hdl_t * thp,nvlist_t * nvl,char ** label,int * err)531 topo_fmri_label(topo_hdl_t *thp, nvlist_t *nvl, char **label, int *err)
532 {
533 nvlist_t *prop = NULL;
534 char *lp;
535
536 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
537 NULL, &prop, err) < 0)
538 return (set_error(thp, *err, err, "topo_fmri_label", NULL));
539
540 if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &lp) != 0)
541 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
542 prop));
543
544 if ((*label = topo_hdl_strdup(thp, lp)) == NULL)
545 return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_label",
546 prop));
547
548 nvlist_free(prop);
549
550 return (0);
551 }
552
553 int
topo_fmri_serial(topo_hdl_t * thp,nvlist_t * nvl,char ** serial,int * err)554 topo_fmri_serial(topo_hdl_t *thp, nvlist_t *nvl, char **serial, int *err)
555 {
556 nvlist_t *prop = NULL;
557 char *sp;
558
559 /*
560 * If there is a serial id in the resource fmri, then use that.
561 * Otherwise fall back to looking for a serial id property in the
562 * protocol group.
563 */
564 if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sp) == 0) {
565 if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
566 return (set_error(thp, ETOPO_PROP_NOMEM, err,
567 "topo_fmri_serial", prop));
568 else
569 return (0);
570 }
571
572 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID,
573 NULL, &prop, err) < 0)
574 return (set_error(thp, *err, err, "topo_fmri_serial", NULL));
575
576 if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &sp) != 0)
577 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_serial",
578 prop));
579
580 if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
581 return (set_error(thp, ETOPO_PROP_NOMEM, err,
582 "topo_fmri_serial", prop));
583
584 nvlist_free(prop);
585
586 return (0);
587 }
588
topo_fmri_getprop(topo_hdl_t * thp,nvlist_t * nvl,const char * pg,const char * pname,nvlist_t * args,nvlist_t ** prop,int * err)589 int topo_fmri_getprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
590 const char *pname, nvlist_t *args, nvlist_t **prop,
591 int *err)
592 {
593 *prop = NULL;
594
595 return (fmri_prop(thp, nvl, pg, pname, args, prop, err));
596 }
597
topo_fmri_setprop(topo_hdl_t * thp,nvlist_t * nvl,const char * pg,nvlist_t * prop,int flag,nvlist_t * args,int * err)598 int topo_fmri_setprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
599 nvlist_t *prop, int flag, nvlist_t *args, int *err)
600 {
601 int rv;
602 nvlist_t *in = NULL, *out = NULL;
603 tnode_t *rnode;
604 char *scheme;
605
606 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0)
607 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
608 TOPO_METH_PROP_SET, in));
609
610 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
611 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
612 TOPO_METH_PROP_SET, in));
613
614 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
615 return (set_error(thp, ETOPO_FMRI_NVL, err,
616 TOPO_METH_PROP_SET, in));
617
618 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, nvl);
619 rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pg);
620 rv |= nvlist_add_nvlist(in, TOPO_PROP_VAL, prop);
621 rv |= nvlist_add_int32(in, TOPO_PROP_FLAG, (int32_t)flag);
622 if (args != NULL)
623 rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
624 if (rv != 0)
625 return (set_error(thp, ETOPO_FMRI_NVL, err,
626 TOPO_METH_PROP_SET, in));
627
628 rv = topo_method_invoke(rnode, TOPO_METH_PROP_SET,
629 TOPO_METH_PROP_SET_VERSION, in, &out, err);
630
631 nvlist_free(in);
632
633 /* no return values */
634 nvlist_free(out);
635
636 if (rv)
637 return (-1);
638
639 return (0);
640
641 }
642
643 int
topo_fmri_getpgrp(topo_hdl_t * thp,nvlist_t * rsrc,const char * pgname,nvlist_t ** pgroup,int * err)644 topo_fmri_getpgrp(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
645 nvlist_t **pgroup, int *err)
646 {
647 int rv;
648 nvlist_t *in = NULL;
649 tnode_t *rnode;
650 char *scheme;
651
652 if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
653 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
654 TOPO_METH_PROP_GET, in));
655
656 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
657 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
658 TOPO_METH_PROP_GET, in));
659
660 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
661 return (set_error(thp, ETOPO_FMRI_NVL, err,
662 TOPO_METH_PROP_GET, in));
663
664 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
665 rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
666 if (rv != 0)
667 return (set_error(thp, ETOPO_FMRI_NVL, err,
668 TOPO_METH_PROP_GET, in));
669
670 *pgroup = NULL;
671 rv = topo_method_invoke(rnode, TOPO_METH_PGRP_GET,
672 TOPO_METH_PGRP_GET_VERSION, in, pgroup, err);
673
674 nvlist_free(in);
675
676 if (rv != 0)
677 return (-1); /* *err is set for us */
678
679 if (*pgroup == NULL)
680 return (set_error(thp, ETOPO_PROP_NOENT, err,
681 TOPO_METH_PROP_GET, NULL));
682 return (0);
683 }
684
685 int
topo_fmri_compare(topo_hdl_t * thp,nvlist_t * f1,nvlist_t * f2,int * err)686 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
687 {
688 uint32_t compare;
689 char *scheme1, *scheme2;
690 nvlist_t *in;
691 nvlist_t *out = NULL;
692 tnode_t *rnode;
693
694 if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
695 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
696 TOPO_METH_COMPARE, NULL));
697 if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
698 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
699 TOPO_METH_COMPARE, NULL));
700
701 if (strcmp(scheme1, scheme2) != 0)
702 return (0);
703
704 if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
705 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
706 TOPO_METH_COMPARE, NULL));
707
708 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
709 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
710 NULL));
711
712 if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV1, f1) != 0 ||
713 nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV2, f2) != 0)
714 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
715 in));
716
717 if (topo_method_invoke(rnode, TOPO_METH_COMPARE,
718 TOPO_METH_COMPARE_VERSION, in, &out, err) < 0)
719 return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
720
721 (void) nvlist_lookup_uint32(out, TOPO_METH_COMPARE_RET, &compare);
722 nvlist_free(out);
723 nvlist_free(in);
724
725 return (compare);
726 }
727
728 /*
729 * topo_fmri_create
730 *
731 * If possible, creates an FMRI of the requested version in the
732 * requested scheme. Args are passed as part of the inputs to the
733 * fmri-create method of the scheme.
734 */
735 nvlist_t *
topo_fmri_create(topo_hdl_t * thp,const char * scheme,const char * name,topo_instance_t inst,nvlist_t * nvl,int * err)736 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
737 topo_instance_t inst, nvlist_t *nvl, int *err)
738 {
739 nvlist_t *ins;
740 nvlist_t *out;
741 tnode_t *rnode;
742
743 ins = out = NULL;
744
745 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
746 return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
747 TOPO_METH_FMRI, NULL));
748
749 if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
750 return (set_nverror(thp, ETOPO_FMRI_NVL, err,
751 TOPO_METH_FMRI, NULL));
752
753 if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
754 nvlist_add_uint64(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
755 return (set_nverror(thp, ETOPO_FMRI_NVL, err,
756 TOPO_METH_FMRI, ins));
757 }
758
759 if (nvl != NULL &&
760 nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
761 return (set_nverror(thp, ETOPO_FMRI_NVL, err,
762 TOPO_METH_FMRI, ins));
763 }
764 if (topo_method_invoke(rnode,
765 TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
766 return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
767 }
768 nvlist_free(ins);
769 return (out);
770 }
771
772 /*
773 * These private utility functions are used by fmd to maintain its resource
774 * cache. Because hc instance numbers are not guaranteed, it's possible to
775 * have two different FMRI strings represent the same logical entity. These
776 * functions hide this implementation detail from unknowing consumers such as
777 * fmd.
778 *
779 * Ideally, we'd like to do a str2nvl() and then a full FMRI hash and
780 * comparison, but these functions are designed to be fast and efficient.
781 * Given that there is only a single hc node that has this property
782 * (ses-enclosure), we hard-code this behavior here. If there are more
783 * instances of this behavior in the future, this function could be made more
784 * generic.
785 *
786 * This code also handles changes in the server-id or revision fields of the hc
787 * FMRI, as these fields have no bearing on equivalence of FRUs.
788 */
789 static ulong_t
topo_fmri_strhash_one(const char * fmri,size_t len)790 topo_fmri_strhash_one(const char *fmri, size_t len)
791 {
792 ulong_t g, h = 0;
793 size_t i;
794
795 for (i = 0; i < len; i++) {
796 h = (h << 4) + fmri[i];
797
798 if ((g = (h & 0xf0000000)) != 0) {
799 h ^= (g >> 24);
800 h ^= g;
801 }
802 }
803
804 return (h);
805 }
806
807 static const char *
topo_fmri_next_auth(const char * auth)808 topo_fmri_next_auth(const char *auth)
809 {
810 const char *colon, *slash;
811
812 colon = strchr(auth + 1, ':');
813 slash = strchr(auth, '/');
814
815 if (colon == NULL && slash == NULL)
816 return (NULL);
817
818 if (colon == NULL)
819 return (slash);
820 else if (slash < colon)
821 return (slash);
822 else
823 return (colon);
824 }
825
826 /*
827 * List of authority information we care about. Note that we explicitly ignore
828 * things that are properties of the chassis and not the resource itself:
829 *
830 * FM_FMRI_AUTH_PRODUCT_SN "product-sn"
831 * FM_FMRI_AUTH_PRODUCT "product-id"
832 * FM_FMRI_AUTH_DOMAIN "domain-id"
833 * FM_FMRI_AUTH_SERVER "server-id"
834 * FM_FMRI_AUTH_HOST "host-id"
835 *
836 * We also ignore the "revision" authority member, as that typically indicates
837 * the firmware revision and is not a static property of the FRU. This leaves
838 * the following interesting members:
839 *
840 * FM_FMRI_AUTH_CHASSIS "chassis-id"
841 * FM_FMRI_HC_SERIAL_ID "serial"
842 * FM_FMRI_HC_PART "part"
843 */
844 typedef enum {
845 HC_AUTH_CHASSIS,
846 HC_AUTH_SERIAL,
847 HC_AUTH_PART,
848 HC_AUTH_MAX
849 } hc_auth_type_t;
850
851 static char *hc_auth_table[] = {
852 FM_FMRI_AUTH_CHASSIS,
853 FM_FMRI_HC_SERIAL_ID,
854 FM_FMRI_HC_PART
855 };
856
857 /*
858 * Takes an authority member, with leading ":" and trailing "=", and returns
859 * one of the above types if it's one of the things we care about. If
860 * 'authlen' is specified, it is filled in with the length of the authority
861 * member, including leading and trailing characters.
862 */
863 static hc_auth_type_t
hc_auth_to_type(const char * auth,size_t * authlen)864 hc_auth_to_type(const char *auth, size_t *authlen)
865 {
866 int i;
867 size_t len;
868
869 if (auth[0] != ':')
870 return (HC_AUTH_MAX);
871
872 for (i = 0; i < HC_AUTH_MAX; i++) {
873 len = strlen(hc_auth_table[i]);
874
875 if (strncmp(auth + 1, hc_auth_table[i], len) == 0 &&
876 auth[len + 1] == '=') {
877 if (authlen)
878 *authlen = len + 2;
879 break;
880 }
881 }
882
883 return (i);
884 }
885
886 /*ARGSUSED*/
887 ulong_t
topo_fmri_strhash_internal(topo_hdl_t * thp,const char * fmri,boolean_t noauth)888 topo_fmri_strhash_internal(topo_hdl_t *thp, const char *fmri, boolean_t noauth)
889 {
890 const char *auth, *next;
891 const char *enclosure;
892 ulong_t h;
893 hc_auth_type_t type;
894
895 if (strncmp(fmri, "hc://", 5) != 0)
896 return (topo_fmri_strhash_one(fmri, strlen(fmri)));
897
898 enclosure = strstr(fmri, SES_ENCLOSURE);
899
900 h = 0;
901
902 auth = next = fmri + 5;
903 while (*next != '/') {
904 auth = next;
905
906 if ((next = topo_fmri_next_auth(auth)) == NULL) {
907 next = auth;
908 break;
909 }
910
911 if ((type = hc_auth_to_type(auth, NULL)) == HC_AUTH_MAX)
912 continue;
913
914 if (!noauth || type == HC_AUTH_CHASSIS)
915 h += topo_fmri_strhash_one(auth, next - auth);
916 }
917
918 if (enclosure) {
919 next = enclosure + sizeof (SES_ENCLOSURE);
920 while (isdigit(*next))
921 next++;
922 }
923
924 h += topo_fmri_strhash_one(next, strlen(next));
925
926 return (h);
927 }
928
929 /*ARGSUSED*/
930 ulong_t
topo_fmri_strhash(topo_hdl_t * thp,const char * fmri)931 topo_fmri_strhash(topo_hdl_t *thp, const char *fmri)
932 {
933 return (topo_fmri_strhash_internal(thp, fmri, B_FALSE));
934 }
935
936 /*ARGSUSED*/
937 ulong_t
topo_fmri_strhash_noauth(topo_hdl_t * thp,const char * fmri)938 topo_fmri_strhash_noauth(topo_hdl_t *thp, const char *fmri)
939 {
940 return (topo_fmri_strhash_internal(thp, fmri, B_TRUE));
941 }
942
943
944 static void
topo_fmri_strcmp_parse_auth(const char * auth,const char * authtype[],size_t authlen[])945 topo_fmri_strcmp_parse_auth(const char *auth, const char *authtype[],
946 size_t authlen[])
947 {
948 int i;
949 const char *next;
950 hc_auth_type_t type;
951 size_t len;
952
953 for (i = 0; i < HC_AUTH_MAX; i++)
954 authlen[i] = 0;
955
956 while (*auth != '/' &&
957 (next = topo_fmri_next_auth(auth)) != NULL) {
958 if ((type = hc_auth_to_type(auth, &len)) == HC_AUTH_MAX) {
959 auth = next;
960 continue;
961 }
962
963 authtype[type] = auth + len;
964 authlen[type] = next - (auth + len);
965 auth = next;
966 }
967 }
968
969 /*ARGSUSED*/
970 static boolean_t
topo_fmri_strcmp_internal(topo_hdl_t * thp,const char * a,const char * b,boolean_t noauth)971 topo_fmri_strcmp_internal(topo_hdl_t *thp, const char *a, const char *b,
972 boolean_t noauth)
973 {
974 const char *fmria, *fmrib;
975 const char *autha[HC_AUTH_MAX], *authb[HC_AUTH_MAX];
976 size_t authlena[HC_AUTH_MAX], authlenb[HC_AUTH_MAX];
977 int i;
978
979 /*
980 * For non-hc FMRIs, we don't do anything.
981 */
982 if (strncmp(a, "hc://", 5) != 0 ||
983 strncmp(b, "hc://", 5) != 0)
984 return (strcmp(a, b) == 0);
985
986 /*
987 * Get the portion of the FMRI independent of the authority
988 * information.
989 */
990 fmria = strchr(a + 5, '/');
991 fmrib = strchr(b + 5, '/');
992 if (fmria == NULL || fmrib == NULL)
993 return (strcmp(a, b));
994 fmria++;
995 fmrib++;
996
997 /*
998 * Comparing fmri authority information is a bit of a pain, because
999 * there may be a different number of members, and they can (but
1000 * shouldn't be) in a different order. We need to create a copy of the
1001 * authority and parse it into pieces. Because this function is
1002 * intended to be fast (and not necessarily extensible), we hard-code
1003 * the list of possible authority members in an enum and parse it into
1004 * an array.
1005 */
1006 topo_fmri_strcmp_parse_auth(a + 5, autha, authlena);
1007 topo_fmri_strcmp_parse_auth(b + 5, authb, authlenb);
1008
1009 for (i = 0; i < HC_AUTH_MAX; i++) {
1010 if (noauth && i != HC_AUTH_CHASSIS)
1011 continue;
1012
1013 if (authlena[i] == 0 && authlenb[i] == 0)
1014 continue;
1015
1016 if (authlena[i] != authlenb[i])
1017 return (B_FALSE);
1018
1019 if (strncmp(autha[i], authb[i], authlena[i]) != 0)
1020 return (B_FALSE);
1021 }
1022
1023 /*
1024 * If this is rooted at a ses-enclosure node, skip past the instance
1025 * number, as it has no meaning.
1026 */
1027 if (strncmp(fmria, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0 &&
1028 strncmp(fmrib, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0) {
1029 fmria += sizeof (SES_ENCLOSURE);
1030 fmrib += sizeof (SES_ENCLOSURE);
1031
1032 while (isdigit(*fmria))
1033 fmria++;
1034 while (isdigit(*fmrib))
1035 fmrib++;
1036 }
1037
1038 return (strcmp(fmria, fmrib) == 0);
1039 }
1040
1041 /*ARGSUSED*/
1042 boolean_t
topo_fmri_strcmp(topo_hdl_t * thp,const char * a,const char * b)1043 topo_fmri_strcmp(topo_hdl_t *thp, const char *a, const char *b)
1044 {
1045 return (topo_fmri_strcmp_internal(thp, a, b, B_FALSE));
1046 }
1047
1048 /*ARGSUSED*/
1049 boolean_t
topo_fmri_strcmp_noauth(topo_hdl_t * thp,const char * a,const char * b)1050 topo_fmri_strcmp_noauth(topo_hdl_t *thp, const char *a, const char *b)
1051 {
1052 return (topo_fmri_strcmp_internal(thp, a, b, B_TRUE));
1053 }
1054
1055 int
topo_fmri_facility(topo_hdl_t * thp,nvlist_t * rsrc,const char * fac_type,uint32_t fac_subtype,topo_walk_cb_t cb,void * cb_args,int * err)1056 topo_fmri_facility(topo_hdl_t *thp, nvlist_t *rsrc, const char *fac_type,
1057 uint32_t fac_subtype, topo_walk_cb_t cb, void *cb_args, int *err)
1058 {
1059 int rv;
1060 nvlist_t *in = NULL, *out;
1061 tnode_t *rnode;
1062 char *scheme;
1063
1064 if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
1065 return (set_error(thp, ETOPO_FMRI_MALFORM, err,
1066 TOPO_METH_PROP_GET, in));
1067
1068 if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
1069 return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
1070 TOPO_METH_PROP_GET, in));
1071
1072 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
1073 return (set_error(thp, ETOPO_FMRI_NVL, err,
1074 TOPO_METH_PROP_GET, in));
1075
1076 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
1077 rv |= nvlist_add_string(in, FM_FMRI_FACILITY_TYPE, fac_type);
1078 rv |= nvlist_add_uint32(in, "type", fac_subtype);
1079 #ifdef _LP64
1080 rv |= nvlist_add_uint64(in, "callback", (uint64_t)cb);
1081 rv |= nvlist_add_uint64(in, "callback-args", (uint64_t)cb_args);
1082 #else
1083 rv |= nvlist_add_uint32(in, "callback", (uint32_t)cb);
1084 rv |= nvlist_add_uint32(in, "callback-args", (uint32_t)cb_args);
1085 #endif
1086 if (rv != 0)
1087 return (set_error(thp, ETOPO_FMRI_NVL, err,
1088 TOPO_METH_PROP_GET, in));
1089
1090 rv = topo_method_invoke(rnode, TOPO_METH_FACILITY,
1091 TOPO_METH_FACILITY_VERSION, in, &out, err);
1092
1093 nvlist_free(in);
1094
1095 if (rv != 0)
1096 return (-1); /* *err is set for us */
1097
1098 return (0);
1099 }
1100