Home | History | Annotate | Download | only in common
      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 #include <limits.h>
     28 #include <strings.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 #include <stdio.h>
     32 #include <alloca.h>
     33 #include <devid.h>
     34 #include <sys/stat.h>
     35 #include <libnvpair.h>
     36 #include <fm/topo_mod.h>
     37 #include <fm/fmd_fmri.h>
     38 #include <sys/fm/protocol.h>
     39 
     40 #include <topo_method.h>
     41 #include <topo_subr.h>
     42 #include <dev.h>
     43 
     44 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
     45     topo_instance_t, void *, void *);
     46 static void dev_release(topo_mod_t *, tnode_t *);
     47 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
     48     nvlist_t *, nvlist_t **);
     49 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
     50     nvlist_t *, nvlist_t **);
     51 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
     52     nvlist_t *, nvlist_t **);
     53 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
     54     nvlist_t *, nvlist_t **);
     55 static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
     56     nvlist_t *, nvlist_t **);
     57 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
     58     nvlist_t *, nvlist_t **);
     59 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
     60     nvlist_t *, nvlist_t **);
     61 
     62 static const topo_method_t dev_methods[] = {
     63 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
     64 	    TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str },
     65 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
     66 	    TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
     67 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
     68 	    TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
     69 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
     70 	    TOPO_STABILITY_INTERNAL, dev_fmri_present },
     71 	{ TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
     72 	    TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
     73 	    dev_fmri_replaced },
     74 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
     75 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
     76 	    dev_fmri_unusable },
     77 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
     78 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
     79 	    dev_fmri_service_state },
     80 	{ NULL }
     81 };
     82 
     83 static const topo_modops_t dev_ops =
     84 	{ dev_enum, dev_release };
     85 static const topo_modinfo_t dev_info =
     86 	{ "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops };
     87 
     88 int
     89 dev_init(topo_mod_t *mod, topo_version_t version)
     90 {
     91 	if (getenv("TOPOHCDEBUG"))
     92 		topo_mod_setdebug(mod);
     93 	topo_mod_dprintf(mod, "initializing dev builtin\n");
     94 
     95 	if (version != DEV_VERSION)
     96 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
     97 
     98 	if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) {
     99 		topo_mod_dprintf(mod, "failed to register dev_info: "
    100 		    "%s\n", topo_mod_errmsg(mod));
    101 		return (-1);
    102 	}
    103 
    104 	return (0);
    105 }
    106 
    107 void
    108 dev_fini(topo_mod_t *mod)
    109 {
    110 	topo_mod_unregister(mod);
    111 }
    112 
    113 /*ARGSUSED*/
    114 static int
    115 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
    116     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
    117 {
    118 	(void) topo_method_register(mod, pnode, dev_methods);
    119 	return (0);
    120 }
    121 
    122 static void
    123 dev_release(topo_mod_t *mod, tnode_t *node)
    124 {
    125 	topo_method_unregister_all(mod, node);
    126 }
    127 
    128 static ssize_t
    129 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
    130 {
    131 	nvlist_t *anvl = NULL;
    132 	nvpair_t *apair;
    133 	uint8_t version;
    134 	ssize_t size = 0;
    135 	char *devid = NULL;
    136 	char *devpath = NULL;
    137 	char *aname, *aval;
    138 	int err;
    139 
    140 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
    141 	    version > FM_DEV_SCHEME_VERSION)
    142 		return (-1);
    143 
    144 	/* Get authority, if present */
    145 	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
    146 	if (err != 0 && err != ENOENT)
    147 		return (-1);
    148 
    149 	/* Get devid, if present */
    150 	err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid);
    151 	if (err != 0 && err != ENOENT)
    152 		return (-1);
    153 
    154 	/* There must be a device path present */
    155 	err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath);
    156 	if (err != 0 || devpath == NULL)
    157 		return (-1);
    158 
    159 
    160 	/* dev:// */
    161 	topo_fmristr_build(&size,
    162 	    buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://");
    163 
    164 	/* authority, if any */
    165 	if (anvl != NULL) {
    166 		for (apair = nvlist_next_nvpair(anvl, NULL);
    167 		    apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) {
    168 			if (nvpair_type(apair) != DATA_TYPE_STRING ||
    169 			    nvpair_value_string(apair, &aval) != 0)
    170 				continue;
    171 			aname = nvpair_name(apair);
    172 			topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL);
    173 			topo_fmristr_build(&size, buf, buflen, "=",
    174 			    aname, aval);
    175 		}
    176 	}
    177 
    178 	/* device-id part, topo_fmristr_build does nothing if devid is NULL */
    179 	topo_fmristr_build(&size,
    180 	    buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL);
    181 
    182 	/* device-path part */
    183 	topo_fmristr_build(&size, buf, buflen, devpath, "/", NULL);
    184 
    185 	return (size);
    186 }
    187 
    188 /*ARGSUSED*/
    189 static int
    190 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    191     nvlist_t *nvl, nvlist_t **out)
    192 {
    193 	ssize_t len;
    194 	char *name = NULL;
    195 	nvlist_t *fmristr;
    196 
    197 	if (version > TOPO_METH_NVL2STR_VERSION)
    198 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    199 
    200 	if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
    201 	    (name = topo_mod_alloc(mod, len + 1)) == NULL ||
    202 	    fmri_nvl2str(nvl, name, len + 1) == 0) {
    203 		if (name != NULL)
    204 			topo_mod_free(mod, name, len + 1);
    205 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    206 	}
    207 
    208 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
    209 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    210 	if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
    211 		topo_mod_free(mod, name, len + 1);
    212 		nvlist_free(fmristr);
    213 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    214 	}
    215 	topo_mod_free(mod, name, len + 1);
    216 	*out = fmristr;
    217 
    218 	return (0);
    219 }
    220 
    221 /*ARGSUSED*/
    222 static int
    223 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    224     nvlist_t *in, nvlist_t **out)
    225 {
    226 	nvlist_t *fmri;
    227 	char *devpath;
    228 	char *devid = NULL;
    229 	char *str;
    230 	int err;
    231 
    232 	if (version > TOPO_METH_STR2NVL_VERSION)
    233 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    234 
    235 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
    236 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    237 
    238 	/* We're expecting a string version of a dev scheme FMRI */
    239 	if (strncmp(str, "dev:///", 7) != 0)
    240 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    241 
    242 	devpath = str + 7;
    243 	if (strncmp(devpath, ":" FM_FMRI_DEV_ID "=", 7) == 0) {
    244 		char *n;
    245 		int len;
    246 
    247 		n = strchr(devpath + 7, '/');
    248 		if (n == NULL)
    249 			return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    250 		len = n - (devpath + 7);
    251 		devid = alloca(len + 1);
    252 		(void) memcpy(devid, devpath + 7, len);
    253 		devid[len] = 0;
    254 		devpath = n + 1;
    255 	}
    256 
    257 	/* the device-path should start with a slash */
    258 	if (*devpath != '/')
    259 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    260 
    261 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
    262 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    263 
    264 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION);
    265 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
    266 	err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath);
    267 	if (devid != NULL)
    268 		err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid);
    269 
    270 	if (err != 0) {
    271 		nvlist_free(fmri);
    272 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    273 	}
    274 	*out = fmri;
    275 
    276 	return (0);
    277 }
    278 
    279 /*ARGSUSED*/
    280 static int
    281 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    282     nvlist_t *in, nvlist_t **out)
    283 {
    284 	uint8_t fmversion;
    285 	char *devpath = NULL;
    286 	uint32_t present;
    287 	char *devid = NULL, *path;
    288 	ddi_devid_t id;
    289 	ddi_devid_t matchid;
    290 	di_node_t dnode;
    291 	struct stat sb;
    292 	int len;
    293 
    294 	if (version > TOPO_METH_PRESENT_VERSION)
    295 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    296 
    297 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
    298 	    fmversion > FM_DEV_SCHEME_VERSION ||
    299 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
    300 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    301 
    302 	(void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
    303 
    304 	if (devpath == NULL || strlen(devpath) == 0)
    305 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    306 
    307 	/*
    308 	 * stat() the device node in devfs. This will tell us if the device is
    309 	 * present or not. Don't stat the minor,  just the whole device.
    310 	 * If the device is present and there is a devid, it must also match.
    311 	 * so di_init that one node. No need for DINFOFORCE.
    312 	 */
    313 	len = strlen(devpath) + strlen("/devices") + 1;
    314 	path = topo_mod_alloc(mod, len);
    315 	(void) snprintf(path, len, "/devices%s", devpath);
    316 	if (devid == NULL) {
    317 		if (stat(path, &sb) != -1)
    318 			present = 1;
    319 		else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
    320 			present = 0;
    321 		else {
    322 			if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
    323 				present = 0;
    324 			else
    325 				present = 1;
    326 			di_fini(dnode);
    327 		}
    328 	} else {
    329 		if (stat(path, &sb) == -1)
    330 			present = 0;
    331 		else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
    332 			present = 0;
    333 		else {
    334 			if ((id = di_devid(dnode)) == NULL ||
    335 			    devid_str_decode(devid, &matchid, NULL) != 0)
    336 				present = 0;
    337 			else {
    338 				if (devid_compare(id, matchid) != 0)
    339 					present = 0;
    340 				else
    341 					present = 1;
    342 				devid_free(matchid);
    343 			}
    344 			di_fini(dnode);
    345 		}
    346 	}
    347 	topo_mod_free(mod, path, len);
    348 
    349 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    350 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    351 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
    352 		nvlist_free(*out);
    353 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    354 	}
    355 
    356 	return (0);
    357 }
    358 
    359 /*ARGSUSED*/
    360 static int
    361 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    362     nvlist_t *in, nvlist_t **out)
    363 {
    364 	uint8_t fmversion;
    365 	char *devpath = NULL;
    366 	uint32_t rval;
    367 	char *devid = NULL, *path;
    368 	ddi_devid_t id;
    369 	ddi_devid_t matchid;
    370 	di_node_t dnode;
    371 	struct stat sb;
    372 	int len;
    373 
    374 	if (version > TOPO_METH_REPLACED_VERSION)
    375 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    376 
    377 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
    378 	    fmversion > FM_DEV_SCHEME_VERSION ||
    379 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
    380 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    381 
    382 	(void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
    383 
    384 	if (devpath == NULL || strlen(devpath) == 0)
    385 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    386 
    387 	/*
    388 	 * stat() the device node in devfs. This will tell us if the device is
    389 	 * present or not. Don't stat the minor,  just the whole device.
    390 	 * If the device is present and there is a devid, it must also match.
    391 	 * so di_init that one node. No need for DINFOFORCE.
    392 	 */
    393 	len = strlen(devpath) + strlen("/devices") + 1;
    394 	path = topo_mod_alloc(mod, len);
    395 	(void) snprintf(path, len, "/devices%s", devpath);
    396 	if (devid == NULL) {
    397 		if (stat(path, &sb) != -1)
    398 			rval = FMD_OBJ_STATE_UNKNOWN;
    399 		else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
    400 			rval = FMD_OBJ_STATE_NOT_PRESENT;
    401 		else {
    402 			if (di_lookup_node(dnode, devpath) == DI_NODE_NIL)
    403 				rval = FMD_OBJ_STATE_NOT_PRESENT;
    404 			else
    405 				rval = FMD_OBJ_STATE_UNKNOWN;
    406 			di_fini(dnode);
    407 		}
    408 	} else {
    409 		if (stat(path, &sb) == -1)
    410 			rval = FMD_OBJ_STATE_NOT_PRESENT;
    411 		else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
    412 			rval = FMD_OBJ_STATE_NOT_PRESENT;
    413 		else {
    414 			if ((id = di_devid(dnode)) == NULL ||
    415 			    devid_str_decode(devid, &matchid, NULL) != 0)
    416 				rval = FMD_OBJ_STATE_UNKNOWN;
    417 			else {
    418 				if (devid_compare(id, matchid) != 0)
    419 					rval = FMD_OBJ_STATE_REPLACED;
    420 				else
    421 					rval = FMD_OBJ_STATE_STILL_PRESENT;
    422 				devid_free(matchid);
    423 			}
    424 			di_fini(dnode);
    425 		}
    426 	}
    427 	topo_mod_free(mod, path, len);
    428 
    429 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    430 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    431 	if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
    432 		nvlist_free(*out);
    433 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    434 	}
    435 
    436 	return (0);
    437 }
    438 
    439 /*ARGSUSED*/
    440 static int
    441 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    442     nvlist_t *in, nvlist_t **out)
    443 {
    444 	di_node_t dnode;
    445 	uint8_t fmversion;
    446 	char *devpath = NULL;
    447 	uint32_t unusable;
    448 	uint_t state;
    449 
    450 	if (version > TOPO_METH_UNUSABLE_VERSION)
    451 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    452 
    453 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
    454 	    fmversion > FM_DEV_SCHEME_VERSION ||
    455 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
    456 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    457 
    458 	if (devpath == NULL)
    459 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    460 
    461 	if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
    462 		if (errno != ENXIO)
    463 			return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
    464 		unusable = 1;
    465 	} else {
    466 		uint_t retired = di_retired(dnode);
    467 		state = di_state(dnode);
    468 		if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
    469 		    DI_BUS_QUIESCED | DI_BUS_DOWN)))
    470 			unusable = 1;
    471 		else
    472 			unusable = 0;
    473 		di_fini(dnode);
    474 	}
    475 
    476 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    477 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    478 	if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
    479 		nvlist_free(*out);
    480 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    481 	}
    482 
    483 	return (0);
    484 }
    485 
    486 /*ARGSUSED*/
    487 static int
    488 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    489     nvlist_t *in, nvlist_t **out)
    490 {
    491 	di_node_t dnode;
    492 	uint8_t fmversion;
    493 	char *devpath = NULL;
    494 	uint32_t service_state;
    495 	uint_t state;
    496 
    497 	if (version > TOPO_METH_SERVICE_STATE_VERSION)
    498 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    499 
    500 	if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
    501 	    fmversion > FM_DEV_SCHEME_VERSION ||
    502 	    nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
    503 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    504 
    505 	if (devpath == NULL)
    506 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    507 
    508 	if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
    509 		if (errno != ENXIO)
    510 			return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
    511 		service_state = FMD_SERVICE_STATE_UNUSABLE;
    512 	} else {
    513 		uint_t retired = di_retired(dnode);
    514 		state = di_state(dnode);
    515 		if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
    516 		    DI_BUS_QUIESCED | DI_BUS_DOWN)))
    517 			service_state = FMD_SERVICE_STATE_UNUSABLE;
    518 		else if (state & DI_DEVICE_DEGRADED)
    519 			service_state = FMD_SERVICE_STATE_DEGRADED;
    520 		else
    521 			service_state = FMD_SERVICE_STATE_OK;
    522 		di_fini(dnode);
    523 	}
    524 
    525 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    526 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    527 	if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
    528 	    service_state) != 0) {
    529 		nvlist_free(*out);
    530 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    531 	}
    532 
    533 	return (0);
    534 }
    535 
    536 static nvlist_t *
    537 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
    538 {
    539 	nvlist_t *out = NULL;
    540 	int e;
    541 
    542 	if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) {
    543 		(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
    544 		return (NULL);
    545 	}
    546 	e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
    547 	e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION);
    548 	e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path);
    549 
    550 	if (id != NULL)
    551 		e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id);
    552 
    553 	if (e == 0)
    554 		return (out);
    555 
    556 	topo_mod_dprintf(mp, "construction of dev nvl failed");
    557 	(void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
    558 	nvlist_free(out);
    559 	return (NULL);
    560 }
    561 
    562 /*ARGSUSED*/
    563 static int
    564 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
    565     nvlist_t *in, nvlist_t **out)
    566 {
    567 	nvlist_t *args = NULL;
    568 	char *path, *id = NULL;
    569 
    570 	if (version > TOPO_METH_FMRI_VERSION)
    571 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
    572 
    573 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
    574 	    nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) {
    575 		topo_mod_dprintf(mp, "no path string in method argument\n");
    576 		return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
    577 	}
    578 
    579 	(void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id);
    580 
    581 	if ((*out = dev_fmri_create(mp, id, path)) == NULL)
    582 		return (-1);
    583 	return (0);
    584 }
    585