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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <limits.h>
27 #include <strings.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <alloca.h>
32 #include <devid.h>
33 #include <sys/stat.h>
34 #include <libnvpair.h>
35 #include <fm/topo_mod.h>
36 #include <fm/fmd_fmri.h>
37 #include <sys/fm/protocol.h>
38
39 #include <topo_method.h>
40 #include <topo_subr.h>
41 #include <dev.h>
42
43 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
44 topo_instance_t, void *, void *);
45 static void dev_release(topo_mod_t *, tnode_t *);
46 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
47 nvlist_t *, nvlist_t **);
48 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
49 nvlist_t *, nvlist_t **);
50 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
51 nvlist_t *, nvlist_t **);
52 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
53 nvlist_t *, nvlist_t **);
54 static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
55 nvlist_t *, nvlist_t **);
56 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
57 nvlist_t *, nvlist_t **);
58 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
59 nvlist_t *, nvlist_t **);
60
61 static const topo_method_t dev_methods[] = {
62 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
63 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str },
64 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
65 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
66 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
67 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
68 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
69 TOPO_STABILITY_INTERNAL, dev_fmri_present },
70 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
71 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
72 dev_fmri_replaced },
73 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
74 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
75 dev_fmri_unusable },
76 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
77 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
78 dev_fmri_service_state },
79 { NULL }
80 };
81
82 static const topo_modops_t dev_ops =
83 { dev_enum, dev_release };
84 static const topo_modinfo_t dev_info =
85 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops };
86
87 int
dev_init(topo_mod_t * mod,topo_version_t version)88 dev_init(topo_mod_t *mod, topo_version_t version)
89 {
90 if (getenv("TOPOHCDEBUG"))
91 topo_mod_setdebug(mod);
92 topo_mod_dprintf(mod, "initializing dev builtin\n");
93
94 if (version != DEV_VERSION)
95 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
96
97 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) {
98 topo_mod_dprintf(mod, "failed to register dev_info: "
99 "%s\n", topo_mod_errmsg(mod));
100 return (-1);
101 }
102
103 return (0);
104 }
105
106 void
dev_fini(topo_mod_t * mod)107 dev_fini(topo_mod_t *mod)
108 {
109 topo_mod_unregister(mod);
110 }
111
112 /*ARGSUSED*/
113 static int
dev_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * notused1,void * notused2)114 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
115 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
116 {
117 /*
118 * Methods are registered, but there is no enumeration. Should
119 * enumeration be added be sure to cater for global vs non-global
120 * zones.
121 */
122 (void) topo_method_register(mod, pnode, dev_methods);
123 return (0);
124 }
125
126 static void
dev_release(topo_mod_t * mod,tnode_t * node)127 dev_release(topo_mod_t *mod, tnode_t *node)
128 {
129 topo_method_unregister_all(mod, node);
130 }
131
132 static ssize_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)133 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
134 {
135 char *devid = NULL, *tpl0id = NULL;
136 char *devpath = NULL;
137 ssize_t size = 0;
138 uint8_t version;
139 int err;
140
141 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
142 version > FM_DEV_SCHEME_VERSION)
143 return (-1);
144
145 /* Get devid, if present */
146 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid);
147 if (err != 0 && err != ENOENT)
148 return (-1);
149
150 /* Get target-port-l0id, if present */
151 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id);
152 if (err != 0 && err != ENOENT)
153 return (-1);
154
155 /* There must be a device path present */
156 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath);
157 if (err != 0 || devpath == NULL)
158 return (-1);
159
160 /*
161 * dev:///
162 *
163 * The dev scheme does not render fmri authority information
164 * in the string form of an fmri. It is meaningless to
165 * transmit a dev scheme fmri outside of the immediate fault
166 * manager.
167 */
168 topo_fmristr_build(&size,
169 buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///");
170
171 /* device-id part, topo_fmristr_build does nothing if devid is NULL */
172 topo_fmristr_build(&size,
173 buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL);
174
175 /* target-port-l0id part */
176 topo_fmristr_build(&size,
177 buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL);
178
179 /*
180 * device-path part; the devpath should always start with a /
181 * so you'd think we don't need to add a further / prefix here;
182 * however past implementation has always added the / if
183 * there is a devid component so we continue to do that
184 * so strings match exactly as before. So we can have:
185 *
186 * dev:////pci@0,0/...
187 * dev:///<devid-and-tpl0>//pci@0,0/...
188 *
189 * where <devid-and-tpl0> =
190 * [:devid=<devid>][:target-port-l0id=<tpl0>]
191 */
192 topo_fmristr_build(&size, buf, buflen, devpath,
193 devid || tpl0id ? "/" : NULL, NULL);
194
195 return (size);
196 }
197
198 /*ARGSUSED*/
199 static int
dev_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)200 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
201 nvlist_t *nvl, nvlist_t **out)
202 {
203 ssize_t len;
204 char *name = NULL;
205 nvlist_t *fmristr;
206
207 if (version > TOPO_METH_NVL2STR_VERSION)
208 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
209
210 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
211 (name = topo_mod_alloc(mod, len + 1)) == NULL ||
212 fmri_nvl2str(nvl, name, len + 1) == 0) {
213 if (name != NULL)
214 topo_mod_free(mod, name, len + 1);
215 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
216 }
217
218 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
219 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
220 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
221 topo_mod_free(mod, name, len + 1);
222 nvlist_free(fmristr);
223 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
224 }
225 topo_mod_free(mod, name, len + 1);
226 *out = fmristr;
227
228 return (0);
229 }
230
231 /*ARGSUSED*/
232 static int
dev_fmri_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)233 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
234 nvlist_t *in, nvlist_t **out)
235 {
236 char *cur, *devid = NULL, *tpl0id = NULL;
237 char *str, *strcp;
238 nvlist_t *fmri;
239 char *devpath;
240 size_t len;
241 int err;
242
243 if (version > TOPO_METH_STR2NVL_VERSION)
244 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
245
246 if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
247 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
248
249 len = strlen(str);
250
251 /*
252 * We're expecting a string version of a dev scheme FMRI, and
253 * no fmri authority information.
254 *
255 * The shortest legal string would be "dev:////" (len 8) for a string
256 * with no FMRI auth info, no devid or target-port-l0id and
257 * an empty devpath string.
258 */
259 if (len < 8 || strncmp(str, "dev:///", 7) != 0)
260 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
261
262 strcp = alloca(len + 1);
263 (void) memcpy(strcp, str, len);
264 strcp[len] = '\0';
265 cur = strcp + 7; /* already parsed "dev:///" */
266
267 /*
268 * If the first character after the "/" that terminates the (empty)
269 * fmri authority is a colon then we have devid and/or target-port-l0id
270 * info. They could be in either order.
271 *
272 * If not a colon then it must be the / that begins the devpath.
273 */
274 if (*cur == ':') {
275 char *eos, *part[2];
276 int i;
277 /*
278 * Look ahead to the "/" that starts the devpath. If not
279 * found or if straight after the : then we're busted.
280 */
281 eos = devpath = strchr(cur, '/');
282 if (devpath == NULL || devpath == cur + 1)
283 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
284
285 part[0] = ++cur;
286
287 /*
288 * Replace the initial "/" of the devpath with a NUL
289 * to terminate the string before it. We'll undo this
290 * before rendering devpath.
291 */
292 *eos = '\0';
293
294 /*
295 * We should now have a NUL-terminated string matching
296 * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :)
297 * Look for a second colon; if found there must be space
298 * after it for the additional component, but no more colons.
299 */
300 if ((part[1] = strchr(cur, ':')) != NULL) {
301 if (part[1] + 1 == eos ||
302 strchr(part[1] + 1, ':') != NULL)
303 return (topo_mod_seterrno(mod,
304 EMOD_FMRI_MALFORM));
305 *part[1] = '\0'; /* terminate part[0] */
306 part[1]++;
307 }
308
309 for (i = 0; i < 2; i++) {
310 char *eq;
311
312 if (!part[i])
313 continue;
314
315 if ((eq = strchr(part[i], '=')) == NULL ||
316 *(eq + 1) == '\0')
317 return (topo_mod_seterrno(mod,
318 EMOD_FMRI_MALFORM));
319
320 *eq = '\0';
321 if (strcmp(part[i], FM_FMRI_DEV_ID) == 0)
322 devid = eq + 1;
323 else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0)
324 tpl0id = eq + 1;
325 else
326 return (topo_mod_seterrno(mod,
327 EMOD_FMRI_MALFORM));
328 }
329
330 if (devid == NULL && tpl0id == NULL)
331 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
332
333 cur = devpath; /* initial slash is NULled */
334 } else if (*cur != '/') {
335 /* the device-path should start with a slash */
336 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
337 } else {
338 devpath = cur;
339 }
340
341 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
342 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
343
344 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION);
345 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
346
347 if (devid != NULL)
348 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid);
349
350 if (tpl0id != NULL)
351 err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id);
352
353 if (devid != NULL || tpl0id != NULL)
354 *devpath = '/'; /* we NULed this earlier; put it back */
355
356 /* step over repeated initial / in the devpath */
357 while (*(devpath + 1) == '/')
358 devpath++;
359
360 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath);
361
362 if (err != 0) {
363 nvlist_free(fmri);
364 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
365 }
366
367 *out = fmri;
368
369 return (0);
370 }
371
372 /*ARGSUSED*/
373 static int
dev_fmri_present(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)374 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
375 nvlist_t *in, nvlist_t **out)
376 {
377 uint8_t fmversion;
378 char *devpath = NULL;
379 uint32_t present;
380 char *devid = NULL, *path;
381 ddi_devid_t id;
382 ddi_devid_t matchid;
383 di_node_t dnode;
384 struct stat sb;
385 int len;
386
387 if (version > TOPO_METH_PRESENT_VERSION)
388 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
389
390 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
391 fmversion > FM_DEV_SCHEME_VERSION ||
392 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
393 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
394
395 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
396
397 if (devpath == NULL || strlen(devpath) == 0)
398 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
399
400 /*
401 * stat() the device node in devfs. This will tell us if the device is
402 * present or not. Don't stat the minor, just the whole device.
403 * If the device is present and there is a devid, it must also match.
404 * so di_init that one node. No need for DINFOFORCE.
405 */
406 len = strlen(devpath) + strlen("/devices") + 1;
407 path = topo_mod_alloc(mod, len);
408 (void) snprintf(path, len, "/devices%s", devpath);
409 if (devid == NULL) {
410 if (stat(path, &sb) != -1)
411 present = 1;
412 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
413 present = 0;
414 else {
415 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
416 present = 0;
417 else
418 present = 1;
419 di_fini(dnode);
420 }
421 } else {
422 if (stat(path, &sb) == -1)
423 present = 0;
424 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
425 present = 0;
426 else {
427 if ((id = di_devid(dnode)) == NULL ||
428 devid_str_decode(devid, &matchid, NULL) != 0)
429 present = 0;
430 else {
431 if (devid_compare(id, matchid) != 0)
432 present = 0;
433 else
434 present = 1;
435 devid_free(matchid);
436 }
437 di_fini(dnode);
438 }
439 }
440 topo_mod_free(mod, path, len);
441
442 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
443 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
444 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
445 nvlist_free(*out);
446 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
447 }
448
449 return (0);
450 }
451
452 /*ARGSUSED*/
453 static int
dev_fmri_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)454 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
455 nvlist_t *in, nvlist_t **out)
456 {
457 uint8_t fmversion;
458 char *devpath = NULL;
459 uint32_t rval;
460 char *devid = NULL, *path;
461 ddi_devid_t id;
462 ddi_devid_t matchid;
463 di_node_t dnode;
464 struct stat sb;
465 int len;
466
467 if (version > TOPO_METH_REPLACED_VERSION)
468 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
469
470 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
471 fmversion > FM_DEV_SCHEME_VERSION ||
472 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
473 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
474
475 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
476
477 if (devpath == NULL || strlen(devpath) == 0)
478 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
479
480 /*
481 * stat() the device node in devfs. This will tell us if the device is
482 * present or not. Don't stat the minor, just the whole device.
483 * If the device is present and there is a devid, it must also match.
484 * so di_init that one node. No need for DINFOFORCE.
485 */
486 len = strlen(devpath) + strlen("/devices") + 1;
487 path = topo_mod_alloc(mod, len);
488 (void) snprintf(path, len, "/devices%s", devpath);
489 if (devid == NULL) {
490 if (stat(path, &sb) != -1)
491 rval = FMD_OBJ_STATE_UNKNOWN;
492 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
493 rval = FMD_OBJ_STATE_UNKNOWN;
494 else {
495 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
496 rval = FMD_OBJ_STATE_UNKNOWN;
497 else
498 rval = FMD_OBJ_STATE_UNKNOWN;
499 di_fini(dnode);
500 }
501 } else {
502 if (stat(path, &sb) == -1)
503 rval = FMD_OBJ_STATE_UNKNOWN;
504 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
505 rval = FMD_OBJ_STATE_UNKNOWN;
506 else {
507 if ((id = di_devid(dnode)) == NULL ||
508 devid_str_decode(devid, &matchid, NULL) != 0)
509 rval = FMD_OBJ_STATE_UNKNOWN;
510 else {
511 if (devid_compare(id, matchid) != 0)
512 rval = FMD_OBJ_STATE_REPLACED;
513 else
514 rval = FMD_OBJ_STATE_STILL_PRESENT;
515 devid_free(matchid);
516 }
517 di_fini(dnode);
518 }
519 }
520 topo_mod_free(mod, path, len);
521
522 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
523 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
524 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
525 nvlist_free(*out);
526 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
527 }
528
529 return (0);
530 }
531
532 /*ARGSUSED*/
533 static int
dev_fmri_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)534 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
535 nvlist_t *in, nvlist_t **out)
536 {
537 di_node_t dnode;
538 uint8_t fmversion;
539 char *devpath = NULL;
540 uint32_t unusable;
541 uint_t state;
542
543 if (version > TOPO_METH_UNUSABLE_VERSION)
544 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
545
546 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
547 fmversion > FM_DEV_SCHEME_VERSION ||
548 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
549 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
550
551 if (devpath == NULL)
552 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
553
554 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
555 if (errno != ENXIO)
556 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
557 unusable = 1;
558 } else {
559 uint_t retired = di_retired(dnode);
560 state = di_state(dnode);
561 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
562 DI_BUS_QUIESCED | DI_BUS_DOWN)))
563 unusable = 1;
564 else
565 unusable = 0;
566 di_fini(dnode);
567 }
568
569 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
570 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
571 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
572 nvlist_free(*out);
573 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
574 }
575
576 return (0);
577 }
578
579 /*ARGSUSED*/
580 static int
dev_fmri_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)581 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
582 nvlist_t *in, nvlist_t **out)
583 {
584 di_node_t dnode;
585 uint8_t fmversion;
586 char *devpath = NULL;
587 uint32_t service_state;
588 uint_t state;
589
590 if (version > TOPO_METH_SERVICE_STATE_VERSION)
591 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
592
593 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
594 fmversion > FM_DEV_SCHEME_VERSION ||
595 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
596 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
597
598 if (devpath == NULL)
599 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
600
601 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
602 if (errno != ENXIO)
603 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
604 service_state = FMD_SERVICE_STATE_UNUSABLE;
605 } else {
606 uint_t retired = di_retired(dnode);
607 state = di_state(dnode);
608 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
609 DI_BUS_QUIESCED | DI_BUS_DOWN)))
610 service_state = FMD_SERVICE_STATE_UNUSABLE;
611 else if (state & DI_DEVICE_DEGRADED)
612 service_state = FMD_SERVICE_STATE_DEGRADED;
613 else
614 service_state = FMD_SERVICE_STATE_OK;
615 di_fini(dnode);
616 }
617
618 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
619 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
620 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
621 service_state) != 0) {
622 nvlist_free(*out);
623 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
624 }
625
626 return (0);
627 }
628
629 static nvlist_t *
dev_fmri_create(topo_mod_t * mp,const char * id,const char * path)630 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
631 {
632 nvlist_t *out = NULL;
633 int e;
634
635 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
636 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
637 return (NULL);
638 }
639 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
640 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
641 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
642
643 if (id != NULL)
644 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
645
646 if (e == 0)
647 return (out);
648
649 topo_mod_dprintf(mp, "construction of dev nvl failed");
650 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
651 nvlist_free(out);
652 return (NULL);
653 }
654
655 /*ARGSUSED*/
656 static int
dev_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)657 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
658 nvlist_t *in, nvlist_t **out)
659 {
660 nvlist_t *args = NULL;
661 char *path, *id = NULL;
662
663 if (version > TOPO_METH_FMRI_VERSION)
664 return (topo_mod_seterrno(mp, EMOD_VER_NEW));
665
666 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
667 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
668 topo_mod_dprintf(mp, "no path string in method argument\n");
669 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
670 }
671
672 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
673
674 if ((*out = dev_fmri_create(mp, id, path)) == NULL)
675 return (-1);
676 return (0);
677 }
678