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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * This provides the basic mechanisms (str2nvl and nvl2str) for dealing with 28 * the service schema. The official version of a svc FMRI has the form: 29 * 30 * svc://[scope@][system-fqn]/service[:instance][@contract-id] 31 * 32 * Where 'service' is a slash-delimited list of names. Of these fields, the 33 * scope, constract-id, and system-fqn are rarely used, leaving the much more 34 * common form such as: 35 * 36 * svc:///network/ssh:default 37 * 38 * Note that the SMF software typically uses a shorthard form, where the 39 * authority is elided (svc:/network/ssh:default). As this module deals with 40 * FMA FMRIs, we only support fully specified FMRIs. 41 * 42 * This module does not support enumeration, but implements methods for FMRI 43 * state (present, unusable, service state, and replaced). 44 */ 45 46 #include <fm/topo_mod.h> 47 #include <fm/fmd_fmri.h> 48 #include <sys/fm/protocol.h> 49 #include <topo_method.h> 50 #include <topo_subr.h> 51 #include <topo_prop.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_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t, 71 nvlist_t *, nvlist_t **); 72 73 static const topo_method_t svc_methods[] = { 74 { TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_DESC, 75 TOPO_METH_PROP_GET_VERSION, TOPO_STABILITY_INTERNAL, 76 svc_fmri_prop_get }, 77 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 78 TOPO_STABILITY_INTERNAL, svc_fmri_nvl2str }, 79 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 80 TOPO_STABILITY_INTERNAL, svc_fmri_str2nvl }, 81 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 82 TOPO_STABILITY_INTERNAL, svc_fmri_present }, 83 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 84 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 85 svc_fmri_replaced }, 86 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 87 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 88 svc_fmri_service_state }, 89 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 90 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 91 svc_fmri_unusable }, 92 { NULL } 93 }; 94 95 static int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 96 topo_instance_t, void *, void *); 97 static void svc_release(topo_mod_t *, tnode_t *); 98 99 static const topo_modops_t svc_ops = 100 { svc_enum, svc_release }; 101 static const topo_modinfo_t svc_info = 102 { "svc", FM_FMRI_SCHEME_SVC, SVC_VERSION, &svc_ops }; 103 104 static int 105 svc_error(topo_mod_t *mod) 106 { 107 switch (scf_error()) { 108 case SCF_ERROR_NO_MEMORY: 109 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 110 111 default: 112 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 113 } 114 } 115 116 static scf_handle_t * 117 svc_get_handle(topo_mod_t *mod) 118 { 119 scf_handle_t *hdl = topo_mod_getspecific(mod); 120 121 if (hdl != NULL) 122 return (hdl); 123 124 if ((hdl = scf_handle_create(SCF_VERSION)) == NULL) { 125 (void) svc_error(mod); 126 return (NULL); 127 } 128 129 if (scf_handle_bind(hdl) != 0) { 130 scf_handle_destroy(hdl); 131 (void) svc_error(mod); 132 return (NULL); 133 } 134 135 topo_mod_setspecific(mod, hdl); 136 137 return (hdl); 138 } 139 140 int 141 svc_init(topo_mod_t *mod, topo_version_t version) 142 { 143 if (getenv("TOPOSVCDEBUG")) 144 topo_mod_setdebug(mod); 145 146 if (version != SVC_VERSION) 147 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 148 149 if (topo_mod_register(mod, &svc_info, TOPO_VERSION) != 0) { 150 topo_mod_dprintf(mod, "failed to register svc_info: " 151 "%s\n", topo_mod_errmsg(mod)); 152 return (-1); 153 } 154 155 return (0); 156 } 157 158 void 159 svc_fini(topo_mod_t *mod) 160 { 161 scf_handle_t *hdl = topo_mod_getspecific(mod); 162 163 if (hdl != NULL) 164 scf_handle_destroy(hdl); 165 166 topo_mod_unregister(mod); 167 } 168 169 static tnode_t * 170 svc_create_node(topo_mod_t *mod, tnode_t *pnode, char *fmristr) 171 { 172 nvlist_t *fmri; 173 tnode_t *tn; 174 char *fixed; 175 ssize_t len; 176 int i, j, err; 177 178 /* 179 * the scf_{x}_to_fmri interfaces return short-hand svc-scheme FMRI's 180 * that look like: 181 * 182 * svc:/service[:instance] 183 * 184 * But all our other code assumes a proper svc-scheme FMRI, so we 185 * correct the fmri string before we try to convert it to an nvlist. 186 * 187 * The short-hand version is kept as the label and can be used when 188 * dealing with the SMF libraries and CLI's. 189 */ 190 len = strlen(fmristr) + 1; 191 if ((fixed = topo_mod_zalloc(mod, len + 1)) == NULL) { 192 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 193 topo_mod_dprintf(mod, "topo_mod_zalloc() failed: %s", 194 topo_mod_errmsg(mod)); 195 return (NULL); 196 } 197 for (i = 0, j = 0; i < len; i++) 198 if (i == 5) 199 fixed[i] = '/'; 200 else 201 fixed[i] = fmristr[j++]; 202 fixed[i] = '\0'; 203 204 if (topo_mod_str2nvl(mod, fixed, &fmri) < 0) { 205 topo_mod_dprintf(mod, "topo_mod_str2nvl() failed: %s", 206 topo_mod_errmsg(mod)); 207 topo_mod_free(mod, fixed, len + 1); 208 return (NULL); 209 } 210 topo_mod_free(mod, fixed, len + 1); 211 212 if (topo_node_range_create(mod, pnode, fmristr, 0, 0) < 0) { 213 topo_mod_dprintf(mod, "topo_node_range_create() failed: %s", 214 topo_mod_errmsg(mod)); 215 nvlist_free(fmri); 216 return (NULL); 217 } 218 if ((tn = topo_node_bind(mod, pnode, fmristr, 0, fmri)) == NULL) { 219 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 220 topo_mod_errmsg(mod)); 221 nvlist_free(fmri); 222 return (NULL); 223 } 224 nvlist_free(fmri); 225 226 if (topo_node_label_set(tn, fmristr, &err) != 0) { 227 topo_mod_dprintf(mod, "failed to set label: %s\n", 228 topo_strerror(err)); 229 return (NULL); 230 } 231 (void) topo_method_register(mod, tn, svc_methods); 232 233 return (tn); 234 } 235 236 /*ARGSUSED*/ 237 static int 238 svc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 239 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 240 { 241 scf_handle_t *hdl; 242 scf_scope_t *sc = NULL; 243 scf_iter_t *svc_iter = NULL; 244 scf_iter_t *inst_iter = NULL; 245 scf_service_t *svc = NULL; 246 scf_instance_t *inst = NULL; 247 int ret = -1; 248 char *sfmri, *ifmri; 249 ssize_t slen, ilen; 250 tnode_t *svc_node; 251 252 (void) topo_method_register(mod, pnode, svc_methods); 253 254 if ((hdl = svc_get_handle(mod)) == NULL) 255 goto out; 256 257 if ((sc = scf_scope_create(hdl)) == NULL || 258 (svc = scf_service_create(hdl)) == NULL || 259 (inst = scf_instance_create(hdl)) == NULL || 260 (svc_iter = scf_iter_create(hdl)) == NULL || 261 (inst_iter = scf_iter_create(hdl)) == NULL) 262 goto out; 263 264 if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, sc) != 0) 265 goto out; 266 267 if (scf_iter_scope_services(svc_iter, sc) != 0) 268 goto out; 269 270 while (scf_iter_next_service(svc_iter, svc) == 1) { 271 if (scf_iter_service_instances(inst_iter, svc) != 0) 272 continue; 273 274 if ((slen = scf_service_to_fmri(svc, NULL, 0)) < 0) 275 continue; 276 277 if ((sfmri = topo_mod_zalloc(mod, slen + 1)) == NULL) { 278 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 279 goto out; 280 } 281 if (scf_service_to_fmri(svc, sfmri, slen + 1) == -1) 282 goto out; 283 284 if ((svc_node = svc_create_node(mod, pnode, sfmri)) == NULL) { 285 topo_mod_free(mod, sfmri, slen + 1); 286 /* topo mod errno set */ 287 goto out; 288 } 289 290 while (scf_iter_next_instance(inst_iter, inst) == 1) { 291 if ((ilen = scf_instance_to_fmri(inst, NULL, 0)) < 0) 292 continue; 293 294 if ((ifmri = topo_mod_zalloc(mod, ilen + 1)) 295 == NULL) { 296 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 297 topo_mod_free(mod, sfmri, slen + 1); 298 goto out; 299 } 300 if (scf_instance_to_fmri(inst, ifmri, ilen + 1) == -1) 301 goto out; 302 303 if ((svc_node = svc_create_node(mod, svc_node, ifmri)) 304 == NULL) { 305 topo_mod_free(mod, sfmri, slen + 1); 306 topo_mod_free(mod, ifmri, ilen + 1); 307 /* topo mod errno set */ 308 goto out; 309 } 310 topo_mod_free(mod, ifmri, ilen + 1); 311 } 312 topo_mod_free(mod, sfmri, slen + 1); 313 } 314 ret = 0; 315 out: 316 scf_scope_destroy(sc); 317 scf_service_destroy(svc); 318 scf_instance_destroy(inst); 319 scf_iter_destroy(svc_iter); 320 scf_iter_destroy(inst_iter); 321 322 return (ret); 323 } 324 325 static void 326 svc_release(topo_mod_t *mod, tnode_t *node) 327 { 328 topo_method_unregister_all(mod, node); 329 } 330 331 static boolean_t 332 svc_component_valid(const char *str) 333 { 334 if (str == NULL) 335 return (B_TRUE); 336 337 if (*str == '\0') 338 return (B_FALSE); 339 340 if (strpbrk(str, "@/:") != NULL) 341 return (B_FALSE); 342 343 return (B_TRUE); 344 } 345 346 static int 347 svc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version, 348 nvlist_t *in, nvlist_t **out) 349 { 350 char *svc_name, *svc_inst = NULL; 351 nvlist_t *rsrc, *args; 352 char *pgroup, *pname; 353 tnode_t *svc_node; 354 char *search; 355 size_t len; 356 int err; 357 358 if (version > TOPO_METH_PROP_GET_VERSION) 359 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 360 361 err = nvlist_lookup_string(in, TOPO_PROP_GROUP, &pgroup); 362 err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME, &pname); 363 err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &rsrc); 364 if (err != 0) 365 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 366 367 /* 368 * Private args to prop method are optional 369 */ 370 if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &args)) != 0) { 371 if (err != ENOENT) 372 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 373 else 374 args = NULL; 375 } 376 377 /* 378 * Lookup a topo node named svc:/svc_name[:svc_inst] 379 */ 380 if (nvlist_lookup_string(rsrc, FM_FMRI_SVC_NAME, &svc_name) != 0) 381 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 382 383 (void) nvlist_lookup_string(rsrc, FM_FMRI_SVC_INSTANCE, &svc_inst); 384 385 len = 5 + strlen(svc_name) + 386 (svc_inst != NULL ? 1 + strlen(svc_inst) : 0) + 1; 387 388 if ((search = topo_mod_alloc(mod, len)) == NULL) 389 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 390 391 (void) snprintf(search, len, "svc:/%s", svc_name); 392 svc_node = topo_node_lookup(node, (const char *)search, 0); 393 394 if (svc_node == NULL) { 395 topo_mod_free(mod, search, len); 396 return (topo_mod_seterrno(mod, EMOD_NODE_NOENT)); 397 } 398 399 if (svc_inst != NULL) { 400 (void) snprintf(search, len, "svc:/%s:%s", svc_name, svc_inst); 401 svc_node = topo_node_lookup(svc_node, (const char *)search, 0); 402 if (svc_node == NULL) { 403 topo_mod_free(mod, search, len); 404 return (topo_mod_seterrno(mod, EMOD_NODE_NOENT)); 405 } 406 } 407 408 topo_mod_free(mod, search, len); 409 410 err = 0; 411 (void) topo_prop_getprop(svc_node, pgroup, pname, args, out, &err); 412 413 return (err); 414 } 415 416 /*ARGSUSED*/ 417 static int 418 svc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 419 nvlist_t *nvl, nvlist_t **out) 420 { 421 uint8_t scheme_version; 422 char *scope = NULL; 423 char *fqn = NULL; 424 char *contract = NULL; 425 char *instance = NULL; 426 char *service; 427 int err; 428 char *buf = NULL; 429 size_t buflen = 0; 430 ssize_t size = 0; 431 nvlist_t *fmristr; 432 433 if (version > TOPO_METH_NVL2STR_VERSION) 434 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 435 436 if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 || 437 scheme_version > FM_SVC_SCHEME_VERSION) 438 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 439 440 /* 441 * Check for optional members. 442 */ 443 err = nvlist_lookup_string(nvl, FM_FMRI_SVC_INSTANCE, &instance); 444 if ((err != 0 && err != ENOENT) || !svc_component_valid(instance)) 445 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 446 err = nvlist_lookup_string(nvl, FM_FMRI_SVC_AUTH_SCOPE, &scope); 447 if ((err != 0 && err != ENOENT) || !svc_component_valid(scope)) 448 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 449 err = nvlist_lookup_string(nvl, FM_FMRI_SVC_AUTH_SYSTEM_FQN, &fqn); 450 if ((err != 0 && err != ENOENT) || !svc_component_valid(scope)) 451 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 452 err = nvlist_lookup_string(nvl, FM_FMRI_SVC_CONTRACT_ID, &contract); 453 if ((err != 0 && err != ENOENT) || !svc_component_valid(contract)) 454 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 455 456 /* 457 * Get the service name. 458 */ 459 if (nvlist_lookup_string(nvl, FM_FMRI_SVC_NAME, &service) != 0) 460 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 461 462 /* 463 * We make two passes through this code. The first time through we 464 * calculate the size of buffer that we'll need, and the second time 465 * through we fill it in. 466 */ 467 again: 468 /* 469 * svc://[scope@][system-fqn] 470 */ 471 topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SVC, 472 NULL, "://"); 473 topo_fmristr_build(&size, buf, buflen, scope, NULL, "@"); 474 topo_fmristr_build(&size, buf, buflen, fqn, NULL, NULL); 475 476 /* svc path */ 477 if (*service == '\0') 478 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 479 480 topo_fmristr_build(&size, buf, buflen, service, "/", NULL); 481 482 /* [:instance][@contract-id] */ 483 topo_fmristr_build(&size, buf, buflen, instance, ":", NULL); 484 topo_fmristr_build(&size, buf, buflen, contract, "@", NULL); 485 486 if (buf == NULL) { 487 if ((buf = topo_mod_alloc(mod, size + 1)) == NULL) 488 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 489 490 buflen = size + 1; 491 size = 0; 492 goto again; 493 } 494 495 /* 496 * Construct the nvlist to return as the result. 497 */ 498 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) { 499 topo_mod_strfree(mod, buf); 500 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 501 } 502 503 if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) { 504 topo_mod_strfree(mod, buf); 505 nvlist_free(fmristr); 506 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 507 } 508 topo_mod_strfree(mod, buf); 509 *out = fmristr; 510 511 return (0); 512 } 513 514 /*ARGSUSED*/ 515 static int 516 svc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 517 nvlist_t *in, nvlist_t **out) 518 { 519 nvlist_t *fmri; 520 char *str, *loc, val; 521 522 if (version > TOPO_METH_STR2NVL_VERSION) 523 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 524 525 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 526 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 527 528 if (strncmp(str, "svc://", 6) != 0) 529 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 530 531 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 532 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 533 534 str += 6; 535 if ((loc = strpbrk(str, "@/")) == NULL) 536 goto malformed; 537 538 if (*loc == '@') { 539 /* scope */ 540 *loc = '\0'; 541 if (!svc_component_valid(str)) { 542 *loc = '@'; 543 goto malformed; 544 } 545 546 if (nvlist_add_string(fmri, FM_FMRI_SVC_AUTH_SCOPE, str) != 0) { 547 *loc = '@'; 548 goto nomem; 549 } 550 551 *loc = '@'; 552 str = loc + 1; 553 if ((loc = strchr(str, '/')) == NULL) 554 goto malformed; 555 } 556 557 if (loc != str) { 558 /* system-fqn */ 559 *loc = '\0'; 560 if (!svc_component_valid(str)) { 561 *loc = '/'; 562 goto malformed; 563 } 564 565 if (nvlist_add_string(fmri, FM_FMRI_SVC_AUTH_SYSTEM_FQN, 566 str) != 0) { 567 *loc = '/'; 568 goto nomem; 569 } 570 571 *loc = '/'; 572 } 573 574 str = loc + 1; 575 loc = strpbrk(str, ":@"); 576 577 if (str[0] == '\0' || loc == str) 578 goto malformed; 579 580 if (loc != NULL) { 581 val = *loc; 582 *loc = '\0'; 583 } 584 585 /* service name */ 586 if (nvlist_add_string(fmri, FM_FMRI_SVC_NAME, str) != 0) { 587 if (loc != NULL) 588 *loc = val; 589 goto nomem; 590 } 591 592 if (loc != NULL) 593 *loc = val; 594 595 if (loc != NULL && *loc == ':') { 596 /* instance */ 597 str = loc + 1; 598 if (str[0] == '\0' || str[0] == '@') 599 goto malformed; 600 601 loc = strchr(str, '@'); 602 if (loc != NULL) 603 *loc = '\0'; 604 605 if (nvlist_add_string(fmri, FM_FMRI_SVC_INSTANCE, 606 str) != 0) { 607 if (loc != NULL) 608 *loc = '@'; 609 goto nomem; 610 } 611 612 if (loc != NULL) 613 *loc = '@'; 614 } 615 616 if (loc != NULL) { 617 /* contract-id */ 618 assert(*loc == '@'); 619 str = loc + 1; 620 if (str[0] == '\0') 621 goto malformed; 622 623 if (nvlist_add_string(fmri, FM_FMRI_SVC_CONTRACT_ID, 624 str) != 0) { 625 goto nomem; 626 } 627 } 628 629 if (nvlist_add_uint8(fmri, FM_VERSION, FM_SVC_SCHEME_VERSION) != 0 || 630 nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SVC) != 0) 631 goto nomem; 632 633 *out = fmri; 634 return (0); 635 636 malformed: 637 nvlist_free(fmri); 638 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 639 640 nomem: 641 nvlist_free(fmri); 642 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 643 } 644 645 /* 646 * This common function is shared by all consumers (present, replaced, 647 * service state and unusable). 648 * 649 * svc_get_state succeeds 650 * Case with FMD_SERVICE_STATE_* 651 * ---------------------------- ------------------------ 652 * svc name deleted UNKNOWN 653 * svc name not found UNKNOWN 654 * no fmri instance OK 655 * instance deleted UNKNOWN 656 * instance not found UNKNOWN 657 * 658 * If none of the above apply and this is a call from the "present" 659 * or "replaced" method (presence_only == B_TRUE) then 660 * svc_get_state returns FMD_SERVICE_STATE_OK. 661 * 662 * The "present" method maps a svc_get_state return of UNKNOWN to 663 * "not present" and a svc_get_state return of OK to "present". 664 * 665 * The "replaced" methods maps a return of UNKNOWN to FMD_OBJ_STATE_NOT_PRESENT 666 * and OK to FMD_OBJ_STATE_UNKNOWN. 667 * 668 * For the "service state" and "unusable" methods svc_get_state goes on 669 * to return the instance state as below, and the two methods map that 670 * result as in the last two columns of the following table: 671 * 672 * svc_get_state succeeds Service 673 * Instance state with FMD_SERVICE_STATE_* State Unusable 674 * -------------- ------------------------------- --------------- -------- 675 * none OK OK 676 * uninitialized OK OK 677 * maintenance UNUSABLE UNUSABLE Yes 678 * offline OK OK 679 * disabled OK OK 680 * online OK OK 681 * degraded DEGRADED DEGRADED 682 * legacy_run OK (XXX can we see this?) OK 683 * 684 * Note that *only* "maintenance" state should map to an unusable service state 685 * or unusable status. That's because a service entering maintenance state 686 * is modelled as a defect fault diagnosis in FMA, but there is no 687 * corresponding isolation action from a response agent since the the service 688 * is already isolated by virtue of being in maintenance state. Any transition 689 * from maintenance state, even to offline, is considered a repair. If on 690 * repair fmd does not see the service usable again then the case hangs 691 * around in the "resolved but not all resources back online" state and 692 * further maintenance events for this service will not show up in fmd state 693 * because case duplicate checking code will find the old case. 694 */ 695 696 static int 697 svc_get_state(topo_mod_t *mod, nvlist_t *fmri, boolean_t presence_only, 698 int *ret) 699 { 700 scf_handle_t *hdl; 701 uint8_t fmversion; 702 char *instance, *name; 703 scf_service_t *svc = NULL; 704 scf_scope_t *scope = NULL; 705 scf_instance_t *inst = NULL; 706 scf_property_t *prop = NULL; 707 scf_iter_t *iter = NULL; 708 scf_value_t *val = NULL; 709 scf_propertygroup_t *pg = NULL; 710 int err, retval = 0; 711 ssize_t len; 712 char *state; 713 714 if ((hdl = svc_get_handle(mod)) == NULL) 715 return (-1); 716 717 if (nvlist_lookup_uint8(fmri, FM_VERSION, &fmversion) != 0 || 718 fmversion > FM_SVC_SCHEME_VERSION || 719 nvlist_lookup_string(fmri, FM_FMRI_SVC_NAME, &name) != 0) 720 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 721 722 if ((svc = scf_service_create(hdl)) == NULL || 723 (inst = scf_instance_create(hdl)) == NULL || 724 (scope = scf_scope_create(hdl)) == NULL || 725 (prop = scf_property_create(hdl)) == NULL || 726 (iter = scf_iter_create(hdl)) == NULL || 727 (pg = scf_pg_create(hdl)) == NULL || 728 (val = scf_value_create(hdl)) == NULL) 729 goto error; 730 731 if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, scope) != 0) 732 goto error; 733 734 /* 735 * If we fail to get the service due to _DELETED or _NOT_FOUND, then we 736 * treat this as not present. 737 */ 738 if (scf_scope_get_service(scope, name, svc) != 0) { 739 err = scf_error(); 740 if (err == SCF_ERROR_NOT_FOUND || err == SCF_ERROR_DELETED) { 741 *ret = FMD_SERVICE_STATE_UNKNOWN; 742 goto out; 743 } else { 744 goto error; 745 } 746 } 747 748 if (nvlist_lookup_string(fmri, FM_FMRI_SVC_INSTANCE, &instance) != 0) { 749 *ret = FMD_SERVICE_STATE_OK; 750 goto out; 751 } 752 753 /* 754 * Again, check for _DELETED or _NOT_FOUND. 755 */ 756 if (scf_service_get_instance(svc, instance, inst) != 0) { 757 err = scf_error(); 758 if (err == SCF_ERROR_NOT_FOUND || err == SCF_ERROR_DELETED) { 759 *ret = FMD_SERVICE_STATE_UNKNOWN; 760 goto out; 761 } else { 762 goto error; 763 } 764 } 765 766 /* 767 * For presence, we are done. Otherwise, we need to get the current 768 * state of the instance. 769 */ 770 if (presence_only) { 771 *ret = FMD_SERVICE_STATE_OK; 772 goto out; 773 } 774 775 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != 0 || 776 scf_pg_get_property(pg, SCF_PROPERTY_STATE, prop) != 0 || 777 scf_iter_property_values(iter, prop) != 0 || 778 scf_iter_next_value(iter, val) != 1) 779 goto error; 780 781 if ((len = scf_value_get_astring(val, NULL, 0)) < 0) 782 goto error; 783 784 state = alloca(len + 1); 785 if (scf_value_get_astring(val, state, len + 1) < 0) 786 goto error; 787 788 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) { 789 *ret = FMD_SERVICE_STATE_UNUSABLE; 790 } else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) { 791 *ret = FMD_SERVICE_STATE_DEGRADED; 792 } else { 793 *ret = FMD_SERVICE_STATE_OK; 794 } 795 goto out; 796 797 error: 798 retval = -1; 799 out: 800 scf_value_destroy(val); 801 scf_pg_destroy(pg); 802 scf_iter_destroy(iter); 803 scf_property_destroy(prop); 804 scf_instance_destroy(inst); 805 scf_scope_destroy(scope); 806 scf_service_destroy(svc); 807 return (retval); 808 } 809 810 /*ARGSUSED*/ 811 static int 812 svc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 813 nvlist_t *in, nvlist_t **out) 814 { 815 int state; 816 817 if (version > TOPO_METH_PRESENT_VERSION) 818 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 819 820 if (svc_get_state(mod, in, B_TRUE, &state) != 0) 821 return (-1); 822 823 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 824 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 825 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, 826 state != FMD_SERVICE_STATE_UNKNOWN) != 0) { 827 nvlist_free(*out); 828 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 829 } 830 831 return (0); 832 } 833 834 /*ARGSUSED*/ 835 static int 836 svc_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 837 nvlist_t *in, nvlist_t **out) 838 { 839 int state; 840 841 if (version > TOPO_METH_REPLACED_VERSION) 842 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 843 844 if (svc_get_state(mod, in, B_TRUE, &state) != 0) 845 return (-1); 846 847 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 848 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 849 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, 850 state == FMD_SERVICE_STATE_UNKNOWN ? 851 FMD_OBJ_STATE_NOT_PRESENT : FMD_OBJ_STATE_UNKNOWN) != 0) { 852 nvlist_free(*out); 853 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 854 } 855 856 return (0); 857 } 858 859 /*ARGSUSED*/ 860 static int 861 svc_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 862 nvlist_t *in, nvlist_t **out) 863 { 864 int state; 865 866 if (version > TOPO_METH_SERVICE_STATE_VERSION) 867 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 868 869 if (svc_get_state(mod, in, B_FALSE, &state) != 0) 870 return (-1); 871 872 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 873 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 874 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET, 875 state) != 0) { 876 nvlist_free(*out); 877 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 878 } 879 880 return (0); 881 } 882 883 /*ARGSUSED*/ 884 static int 885 svc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 886 nvlist_t *in, nvlist_t **out) 887 { 888 int state; 889 890 if (version > TOPO_METH_UNUSABLE_VERSION) 891 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 892 893 if (svc_get_state(mod, in, B_FALSE, &state) != 0) 894 return (-1); 895 896 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 897 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 898 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, 899 (state == FMD_SERVICE_STATE_UNKNOWN || 900 state == FMD_SERVICE_STATE_UNUSABLE)) != 0) { 901 nvlist_free(*out); 902 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 903 } 904 905 return (0); 906 } 907