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 /*
     28  * This provides the basic mechanisms (str2nvl and nvl2str) for dealing with
     29  * the service schema.  The official version of a svc FMRI has the form:
     30  *
     31  * 	svc://[scope@][system-fqn]/service[:instance][@contract-id]
     32  *
     33  * Where 'service' is a slash-delimited list of names.  Of these fields, the
     34  * scope, constract-id, and system-fqn are rarely used, leaving the much more
     35  * common form such as:
     36  *
     37  * 	svc:///network/ssh:default
     38  *
     39  * Note that the SMF software typically uses a shorthard form, where the
     40  * authority is elided (svc:/network/ssh:default).  As this module deals with
     41  * FMA FMRIs, we only support fully specified FMRIs.
     42  *
     43  * This module does not support enumeration, but implements methods for FMRI
     44  * state (present, unusable, service state, and replaced).
     45  */
     46 
     47 #include <fm/topo_mod.h>
     48 #include <fm/fmd_fmri.h>
     49 #include <sys/fm/protocol.h>
     50 #include <topo_method.h>
     51 #include <topo_subr.h>
     52 #include <alloca.h>
     53 #include <assert.h>
     54 #include <svc.h>
     55 #include <strings.h>
     56 #include <libscf.h>
     57 
     58 static int svc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
     59     nvlist_t *, nvlist_t **);
     60 static int svc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
     61     nvlist_t *, nvlist_t **);
     62 static int svc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
     63     nvlist_t *, nvlist_t **);
     64 static int svc_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
     65     nvlist_t *, nvlist_t **);
     66 static int svc_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
     67     nvlist_t *, nvlist_t **);
     68 static int svc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
     69     nvlist_t *, nvlist_t **);
     70 static int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
     71     topo_instance_t, void *, void *);
     72 static void svc_release(topo_mod_t *, tnode_t *);
     73 
     74 static const topo_method_t svc_methods[] = {
     75 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
     76 	    TOPO_STABILITY_INTERNAL, svc_fmri_nvl2str },
     77 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
     78 	    TOPO_STABILITY_INTERNAL, svc_fmri_str2nvl },
     79 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
     80 	    TOPO_STABILITY_INTERNAL, svc_fmri_present },
     81 	{ TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
     82 	    TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
     83 	    svc_fmri_replaced },
     84 	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
     85 	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
     86 	    svc_fmri_service_state },
     87 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
     88 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
     89 	    svc_fmri_unusable },
     90 	{ NULL }
     91 };
     92 
     93 static const topo_modops_t svc_ops =
     94 	{ svc_enum, svc_release };
     95 static const topo_modinfo_t svc_info =
     96 	{ "svc", FM_FMRI_SCHEME_SVC, SVC_VERSION, &svc_ops };
     97 
     98 static int
     99 svc_error(topo_mod_t *mod)
    100 {
    101 	switch (scf_error()) {
    102 	case SCF_ERROR_NO_MEMORY:
    103 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    104 
    105 	default:
    106 		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
    107 	}
    108 }
    109 
    110 static scf_handle_t *
    111 svc_get_handle(topo_mod_t *mod)
    112 {
    113 	scf_handle_t *hdl = topo_mod_getspecific(mod);
    114 
    115 	if (hdl != NULL)
    116 		return (hdl);
    117 
    118 	if ((hdl = scf_handle_create(SCF_VERSION)) == NULL) {
    119 		(void) svc_error(mod);
    120 		return (NULL);
    121 	}
    122 
    123 	if (scf_handle_bind(hdl) != 0) {
    124 		scf_handle_destroy(hdl);
    125 		(void) svc_error(mod);
    126 		return (NULL);
    127 	}
    128 
    129 	topo_mod_setspecific(mod, hdl);
    130 
    131 	return (hdl);
    132 }
    133 
    134 int
    135 svc_init(topo_mod_t *mod, topo_version_t version)
    136 {
    137 	if (version != SVC_VERSION)
    138 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    139 
    140 	if (topo_mod_register(mod, &svc_info, TOPO_VERSION) != 0) {
    141 		topo_mod_dprintf(mod, "failed to register svc_info: "
    142 		    "%s\n", topo_mod_errmsg(mod));
    143 		return (-1);
    144 	}
    145 
    146 	return (0);
    147 }
    148 
    149 void
    150 svc_fini(topo_mod_t *mod)
    151 {
    152 	scf_handle_t *hdl = topo_mod_getspecific(mod);
    153 
    154 	if (hdl != NULL)
    155 		scf_handle_destroy(hdl);
    156 
    157 	topo_mod_unregister(mod);
    158 }
    159 
    160 /*ARGSUSED*/
    161 static int
    162 svc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
    163     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
    164 {
    165 	(void) topo_method_register(mod, pnode, svc_methods);
    166 	return (0);
    167 }
    168 
    169 static void
    170 svc_release(topo_mod_t *mod, tnode_t *node)
    171 {
    172 	topo_method_unregister_all(mod, node);
    173 }
    174 
    175 static boolean_t
    176 svc_component_valid(const char *str)
    177 {
    178 	if (str == NULL)
    179 		return (B_TRUE);
    180 
    181 	if (*str == '\0')
    182 		return (B_FALSE);
    183 
    184 	if (strpbrk(str, "@/:") != NULL)
    185 		return (B_FALSE);
    186 
    187 	return (B_TRUE);
    188 }
    189 
    190 /*ARGSUSED*/
    191 static int
    192 svc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    193     nvlist_t *nvl, nvlist_t **out)
    194 {
    195 	uint8_t scheme_version;
    196 	char *scope = NULL;
    197 	char *fqn = NULL;
    198 	char *contract = NULL;
    199 	char *instance = NULL;
    200 	char *service;
    201 	int err;
    202 	char *buf = NULL;
    203 	size_t buflen = 0;
    204 	ssize_t size = 0;
    205 	nvlist_t *fmristr;
    206 
    207 	if (version > TOPO_METH_NVL2STR_VERSION)
    208 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    209 
    210 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 ||
    211 	    scheme_version > FM_SVC_SCHEME_VERSION)
    212 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    213 
    214 	/*
    215 	 * Check for optional members.
    216 	 */
    217 	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_INSTANCE, &instance);
    218 	if ((err != 0 && err != ENOENT) || !svc_component_valid(instance))
    219 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    220 	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_AUTH_SCOPE, &scope);
    221 	if ((err != 0 && err != ENOENT) || !svc_component_valid(scope))
    222 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    223 	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_AUTH_SYSTEM_FQN, &fqn);
    224 	if ((err != 0 && err != ENOENT) || !svc_component_valid(scope))
    225 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    226 	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_CONTRACT_ID, &contract);
    227 	if ((err != 0 && err != ENOENT) || !svc_component_valid(contract))
    228 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    229 
    230 	/*
    231 	 * Get the service name.
    232 	 */
    233 	if (nvlist_lookup_string(nvl, FM_FMRI_SVC_NAME, &service) != 0)
    234 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    235 
    236 	/*
    237 	 * We make two passes through this code.  The first time through we
    238 	 * calculate the size of buffer that we'll need, and the second time
    239 	 * through we fill it in.
    240 	 */
    241 again:
    242 	/*
    243 	 * svc://[scope@][system-fqn]
    244 	 */
    245 	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SVC,
    246 	    NULL, "://");
    247 	topo_fmristr_build(&size, buf, buflen, scope, NULL, "@");
    248 	topo_fmristr_build(&size, buf, buflen, fqn, NULL, NULL);
    249 
    250 	/* svc path */
    251 	if (*service == '\0')
    252 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    253 
    254 	topo_fmristr_build(&size, buf, buflen, service, "/", NULL);
    255 
    256 	/* [:instance][@contract-id] */
    257 	topo_fmristr_build(&size, buf, buflen, instance, ":", NULL);
    258 	topo_fmristr_build(&size, buf, buflen, contract, "@", NULL);
    259 
    260 	if (buf == NULL) {
    261 		if ((buf = topo_mod_alloc(mod, size + 1)) == NULL)
    262 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
    263 
    264 		buflen = size + 1;
    265 		size = 0;
    266 		goto again;
    267 	}
    268 
    269 	/*
    270 	 * Construct the nvlist to return as the result.
    271 	 */
    272 	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
    273 		topo_mod_strfree(mod, buf);
    274 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    275 	}
    276 
    277 	if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) {
    278 		topo_mod_strfree(mod, buf);
    279 		nvlist_free(fmristr);
    280 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    281 	}
    282 	topo_mod_strfree(mod, buf);
    283 	*out = fmristr;
    284 
    285 	return (0);
    286 }
    287 
    288 /*ARGSUSED*/
    289 static int
    290 svc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    291     nvlist_t *in, nvlist_t **out)
    292 {
    293 	nvlist_t *fmri;
    294 	char *str, *loc, val;
    295 
    296 	if (version > TOPO_METH_STR2NVL_VERSION)
    297 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    298 
    299 	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
    300 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    301 
    302 	if (strncmp(str, "svc://", 6) != 0)
    303 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    304 
    305 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
    306 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    307 
    308 	str += 6;
    309 	if ((loc = strpbrk(str, "@/")) == NULL)
    310 		goto malformed;
    311 
    312 	if (*loc == '@') {
    313 		/* scope */
    314 		*loc = '\0';
    315 		if (!svc_component_valid(str)) {
    316 			*loc = '@';
    317 			goto malformed;
    318 		}
    319 
    320 		if (nvlist_add_string(fmri, FM_FMRI_SVC_AUTH_SCOPE, str) != 0) {
    321 			*loc = '@';
    322 			goto nomem;
    323 		}
    324 
    325 		*loc = '@';
    326 		str = loc + 1;
    327 		if ((loc = strchr(str, '/')) == NULL)
    328 			goto malformed;
    329 	}
    330 
    331 	if (loc != str) {
    332 		/* system-fqn */
    333 		*loc = '\0';
    334 		if (!svc_component_valid(str)) {
    335 			*loc = '/';
    336 			goto malformed;
    337 		}
    338 
    339 		if (nvlist_add_string(fmri, FM_FMRI_SVC_AUTH_SYSTEM_FQN,
    340 		    str) != 0) {
    341 			*loc = '/';
    342 			goto nomem;
    343 		}
    344 
    345 		*loc = '/';
    346 	}
    347 
    348 	str = loc + 1;
    349 	loc = strpbrk(str, ":@");
    350 
    351 	if (str[0] == '\0' || loc == str)
    352 		goto malformed;
    353 
    354 	if (loc != NULL) {
    355 		val = *loc;
    356 		*loc = '\0';
    357 	}
    358 
    359 	/* service name */
    360 	if (nvlist_add_string(fmri, FM_FMRI_SVC_NAME, str) != 0) {
    361 		if (loc != NULL)
    362 			*loc = val;
    363 		goto nomem;
    364 	}
    365 
    366 	if (loc != NULL)
    367 		*loc = val;
    368 
    369 	if (loc != NULL && *loc == ':') {
    370 		/* instance */
    371 		str = loc + 1;
    372 		if (str[0] == '\0' || str[0] == '@')
    373 			goto malformed;
    374 
    375 		loc = strchr(str, '@');
    376 		if (loc != NULL)
    377 			*loc = '\0';
    378 
    379 		if (nvlist_add_string(fmri, FM_FMRI_SVC_INSTANCE,
    380 		    str) != 0) {
    381 			if (loc != NULL)
    382 				*loc = '@';
    383 			goto nomem;
    384 		}
    385 
    386 		if (loc != NULL)
    387 			*loc = '@';
    388 	}
    389 
    390 	if (loc != NULL) {
    391 		/* contract-id */
    392 		assert(*loc == '@');
    393 		str = loc + 1;
    394 		if (str[0] == '\0')
    395 			goto malformed;
    396 
    397 		if (nvlist_add_string(fmri, FM_FMRI_SVC_CONTRACT_ID,
    398 		    str) != 0) {
    399 			goto nomem;
    400 		}
    401 	}
    402 
    403 	if (nvlist_add_uint8(fmri, FM_VERSION, FM_SVC_SCHEME_VERSION) != 0 ||
    404 	    nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SVC) != 0)
    405 		goto nomem;
    406 
    407 	*out = fmri;
    408 	return (0);
    409 
    410 malformed:
    411 	nvlist_free(fmri);
    412 	return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    413 
    414 nomem:
    415 	nvlist_free(fmri);
    416 	return (topo_mod_seterrno(mod, EMOD_NOMEM));
    417 }
    418 
    419 /*
    420  * This common function is shared by all consumers (present, unusable, and
    421  * service_state).  It returns one of the FMD_SERVICE_STATE_* states, where
    422  * FMD_SERVICE_STATE_UNKNOWN means that the FMRI is not present.
    423  */
    424 static int
    425 svc_get_state(topo_mod_t *mod, nvlist_t *fmri, boolean_t presence_only,
    426     int *ret)
    427 {
    428 	scf_handle_t *hdl;
    429 	uint8_t fmversion;
    430 	char *instance, *name;
    431 	scf_service_t *svc = NULL;
    432 	scf_scope_t *scope = NULL;
    433 	scf_instance_t *inst = NULL;
    434 	scf_property_t *prop = NULL;
    435 	scf_iter_t *iter = NULL;
    436 	scf_value_t *val = NULL;
    437 	scf_propertygroup_t *pg = NULL;
    438 	int err, retval = 0;
    439 	ssize_t len;
    440 	char *state;
    441 
    442 	if ((hdl = svc_get_handle(mod)) == NULL)
    443 		return (-1);
    444 
    445 	if (nvlist_lookup_uint8(fmri, FM_VERSION, &fmversion) != 0 ||
    446 	    fmversion > FM_SVC_SCHEME_VERSION ||
    447 	    nvlist_lookup_string(fmri, FM_FMRI_SVC_NAME, &name) != 0)
    448 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    449 
    450 	if ((svc = scf_service_create(hdl)) == NULL ||
    451 	    (inst = scf_instance_create(hdl)) == NULL ||
    452 	    (scope = scf_scope_create(hdl)) == NULL ||
    453 	    (prop = scf_property_create(hdl)) == NULL ||
    454 	    (iter = scf_iter_create(hdl)) == NULL ||
    455 	    (pg = scf_pg_create(hdl)) == NULL ||
    456 	    (val = scf_value_create(hdl)) == NULL)
    457 		goto error;
    458 
    459 	if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, scope) != 0)
    460 		goto error;
    461 
    462 	/*
    463 	 * If we fail to get the service due to _DELETED or _NOT_FOUND, then we
    464 	 * treat this as not present.
    465 	 */
    466 	if (scf_scope_get_service(scope, name, svc) != 0) {
    467 		err = scf_error();
    468 		if (err == SCF_ERROR_NOT_FOUND || err == SCF_ERROR_DELETED) {
    469 			*ret = FMD_SERVICE_STATE_UNKNOWN;
    470 			goto out;
    471 		} else {
    472 			goto error;
    473 		}
    474 	}
    475 
    476 	/*
    477 	 * If there is no instance, then it is always present, and always
    478 	 * usuable.
    479 	 */
    480 	if (nvlist_lookup_string(fmri, FM_FMRI_SVC_INSTANCE, &instance) != 0) {
    481 		*ret = FMD_SERVICE_STATE_OK;
    482 		goto out;
    483 	}
    484 
    485 	/*
    486 	 * Again, check for _DELETED or _NOT_FOUND.
    487 	 */
    488 	if (scf_service_get_instance(svc, instance, inst) != 0) {
    489 		err = scf_error();
    490 		if (err == SCF_ERROR_NOT_FOUND || err == SCF_ERROR_DELETED) {
    491 			*ret = FMD_SERVICE_STATE_UNKNOWN;
    492 			goto out;
    493 		} else {
    494 			goto error;
    495 		}
    496 	}
    497 
    498 	/*
    499 	 * For presence, we are done.  Otherwise, we need to get the current
    500 	 * state of the instance.
    501 	 */
    502 	if (presence_only) {
    503 		*ret = FMD_SERVICE_STATE_OK;
    504 		goto out;
    505 	}
    506 
    507 	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != 0 ||
    508 	    scf_pg_get_property(pg, SCF_PROPERTY_STATE, prop) != 0 ||
    509 	    scf_iter_property_values(iter, prop) != 0 ||
    510 	    scf_iter_next_value(iter, val) != 1)
    511 		goto error;
    512 
    513 	if ((len = scf_value_get_astring(val, NULL, 0)) < 0)
    514 		goto error;
    515 
    516 	state = alloca(len + 1);
    517 	if (scf_value_get_astring(val, state, len + 1) < 0)
    518 		goto error;
    519 
    520 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
    521 		*ret = FMD_SERVICE_STATE_UNUSABLE;
    522 	else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
    523 		*ret = FMD_SERVICE_STATE_DEGRADED;
    524 	else
    525 		*ret = FMD_SERVICE_STATE_OK;
    526 	goto out;
    527 
    528 error:
    529 	retval = -1;
    530 out:
    531 	scf_value_destroy(val);
    532 	scf_pg_destroy(pg);
    533 	scf_iter_destroy(iter);
    534 	scf_property_destroy(prop);
    535 	scf_instance_destroy(inst);
    536 	scf_scope_destroy(scope);
    537 	scf_service_destroy(svc);
    538 	return (retval);
    539 }
    540 
    541 /*ARGSUSED*/
    542 static int
    543 svc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    544     nvlist_t *in, nvlist_t **out)
    545 {
    546 	int state;
    547 
    548 	if (version > TOPO_METH_PRESENT_VERSION)
    549 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    550 
    551 	if (svc_get_state(mod, in, B_TRUE, &state) != 0)
    552 		return (-1);
    553 
    554 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    555 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    556 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET,
    557 	    state != FMD_SERVICE_STATE_UNKNOWN) != 0) {
    558 		nvlist_free(*out);
    559 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    560 	}
    561 
    562 	return (0);
    563 }
    564 
    565 /*ARGSUSED*/
    566 static int
    567 svc_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    568     nvlist_t *in, nvlist_t **out)
    569 {
    570 	int state;
    571 
    572 	if (version > TOPO_METH_REPLACED_VERSION)
    573 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    574 
    575 	if (svc_get_state(mod, in, B_TRUE, &state) != 0)
    576 		return (-1);
    577 
    578 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    579 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    580 	if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET,
    581 	    state == FMD_SERVICE_STATE_UNKNOWN ?
    582 	    FMD_OBJ_STATE_NOT_PRESENT : FMD_OBJ_STATE_UNKNOWN) != 0) {
    583 		nvlist_free(*out);
    584 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    585 	}
    586 
    587 	return (0);
    588 }
    589 
    590 /*ARGSUSED*/
    591 static int
    592 svc_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    593     nvlist_t *in, nvlist_t **out)
    594 {
    595 	int state;
    596 
    597 	if (version > TOPO_METH_SERVICE_STATE_VERSION)
    598 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    599 
    600 	if (svc_get_state(mod, in, B_FALSE, &state) != 0)
    601 		return (-1);
    602 
    603 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    604 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    605 	if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
    606 	    state) != 0) {
    607 		nvlist_free(*out);
    608 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    609 	}
    610 
    611 	return (0);
    612 }
    613 
    614 /*ARGSUSED*/
    615 static int
    616 svc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    617     nvlist_t *in, nvlist_t **out)
    618 {
    619 	int state;
    620 
    621 	if (version > TOPO_METH_UNUSABLE_VERSION)
    622 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    623 
    624 	if (svc_get_state(mod, in, B_FALSE, &state) != 0)
    625 		return (-1);
    626 
    627 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
    628 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    629 	if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET,
    630 	    (state == FMD_SERVICE_STATE_UNKNOWN ||
    631 	    state == FMD_SERVICE_STATE_UNUSABLE)) != 0) {
    632 		nvlist_free(*out);
    633 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    634 	}
    635 
    636 	return (0);
    637 }
    638