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