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