1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * BSD 3 Clause License 8 * 9 * Copyright (c) 2007, The Storage Networking Industry Association. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * - Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * - Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * - Neither the name of The Storage Networking Industry Association (SNIA) 23 * nor the names of its contributors may be used to endorse or promote 24 * products derived from this software without specific prior written 25 * permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * NDMP configuration management 42 */ 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <synch.h> 46 #include <libintl.h> 47 #include <strings.h> 48 #include <libndmp.h> 49 50 /* NDMP properties configuration */ 51 #define NDMP_GROUP_FMRI_PREFIX "system/ndmpd" 52 #define NDMP_INST "svc:/system/ndmpd:default" 53 #define NDMP_PROP_LEN 600 54 static char *ndmp_pg[] = { 55 "ndmpd", 56 "read" 57 }; 58 #define NPG (sizeof (ndmp_pg) / sizeof (ndmp_pg[0])) 59 60 /* Handle Init states */ 61 #define NDMP_SCH_STATE_UNINIT 0 62 #define NDMP_SCH_STATE_INITIALIZING 1 63 #define NDMP_SCH_STATE_INIT 2 64 65 /* NDMP scf handle structure */ 66 typedef struct ndmp_scfhandle { 67 scf_handle_t *scf_handle; 68 int scf_state; 69 scf_service_t *scf_service; 70 scf_scope_t *scf_scope; 71 scf_transaction_t *scf_trans; 72 scf_propertygroup_t *scf_pg; 73 } ndmp_scfhandle_t; 74 75 static int ndmp_config_saveenv(ndmp_scfhandle_t *); 76 static ndmp_scfhandle_t *ndmp_smf_scf_init(char *); 77 static void ndmp_smf_scf_fini(ndmp_scfhandle_t *); 78 static int ndmp_smf_start_transaction(ndmp_scfhandle_t *); 79 static int ndmp_smf_end_transaction(ndmp_scfhandle_t *); 80 static int ndmp_smf_set_property(ndmp_scfhandle_t *, char *, char *); 81 static int ndmp_smf_get_property(ndmp_scfhandle_t *, char *, char *, size_t); 82 static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, char *); 83 static int ndmp_smf_delete_property(ndmp_scfhandle_t *, char *); 84 static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *, char *, char **); 85 86 /* 87 * This routine send a refresh signal to ndmpd service which cause ndmpd 88 * property table to be refeshed with current ndmpd properties value from SMF. 89 */ 90 int 91 ndmp_service_refresh(void) 92 { 93 if ((smf_get_state(NDMP_INST)) != NULL) 94 return (smf_refresh_instance(NDMP_INST)); 95 96 ndmp_errno = ENDMP_SMF_INTERNAL; 97 return (-1); 98 } 99 100 /* 101 * Returns value of the specified variable/property. The return value is a 102 * string pointer to the locally allocated memory if the config param is 103 * defined otherwise it would be NULL. 104 */ 105 int 106 ndmp_get_prop(char *prop, char **value) 107 { 108 ndmp_scfhandle_t *handle = NULL; 109 char *lval = (char *)malloc(NDMP_PROP_LEN); 110 char *pgname; 111 112 if (!lval) { 113 ndmp_errno = ENDMP_MEM_ALLOC; 114 return (-1); 115 } 116 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) { 117 free(lval); 118 return (-1); 119 } 120 if (ndmp_smf_get_pg_name(handle, prop, &pgname)) { 121 free(lval); 122 ndmp_errno = ENDMP_SMF_PROP_GRP; 123 return (-1); 124 } 125 if (ndmp_smf_create_service_pgroup(handle, pgname)) { 126 ndmp_smf_scf_fini(handle); 127 free(lval); 128 return (-1); 129 } 130 if (ndmp_smf_get_property(handle, prop, lval, NDMP_PROP_LEN) != 0) { 131 ndmp_smf_scf_fini(handle); 132 free(lval); 133 ndmp_errno = ENDMP_SMF_PROP; 134 return (-1); 135 } 136 *value = lval; 137 ndmp_smf_scf_fini(handle); 138 return (0); 139 } 140 141 int 142 ndmp_set_prop(char *env, char *env_val) 143 { 144 ndmp_scfhandle_t *handle = NULL; 145 char *pgname; 146 147 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) 148 return (-1); 149 150 if (ndmp_smf_get_pg_name(handle, env, &pgname)) { 151 ndmp_errno = ENDMP_SMF_PROP_GRP; 152 return (-1); 153 } 154 155 if (ndmp_smf_create_service_pgroup(handle, pgname)) 156 return (-1); 157 158 if (ndmp_smf_start_transaction(handle)) 159 return (-1); 160 161 if (env_val) { 162 if (ndmp_smf_set_property(handle, env, env_val)) { 163 return (-1); 164 } 165 } else { 166 if (ndmp_smf_delete_property(handle, env)) 167 return (-1); 168 } 169 170 if (ndmp_config_saveenv(handle) != 0) 171 return (-1); 172 173 return (0); 174 } 175 176 static int 177 ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, char *pname, char **pgname) 178 { 179 scf_value_t *value; 180 scf_property_t *prop; 181 int i; 182 183 for (i = 0; i < NPG; i++) { 184 if (scf_service_get_pg(h->scf_service, ndmp_pg[i], 185 h->scf_pg) != 0) 186 return (-1); 187 188 if ((value = scf_value_create(h->scf_handle)) == NULL) 189 return (-1); 190 191 if ((prop = scf_property_create(h->scf_handle)) == NULL) { 192 scf_value_destroy(value); 193 return (-1); 194 } 195 /* 196 * This will fail if property does not exist in the property 197 * group. Check the next property group in case of failure. 198 */ 199 if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) { 200 scf_value_destroy(value); 201 scf_property_destroy(prop); 202 continue; 203 } 204 205 *pgname = ndmp_pg[i]; 206 scf_value_destroy(value); 207 scf_property_destroy(prop); 208 return (0); 209 } 210 scf_value_destroy(value); 211 scf_property_destroy(prop); 212 return (-1); 213 } 214 215 /* 216 * Basically commit the transaction. 217 */ 218 static int 219 ndmp_config_saveenv(ndmp_scfhandle_t *handle) 220 { 221 int ret = 0; 222 223 ret = ndmp_smf_end_transaction(handle); 224 225 ndmp_smf_scf_fini(handle); 226 return (ret); 227 } 228 229 /* 230 * Must be called when done. Called with the handle allocated in 231 * ndmp_smf_scf_init(), it cleans up the state and frees any SCF resources 232 * still in use. 233 */ 234 static void 235 ndmp_smf_scf_fini(ndmp_scfhandle_t *handle) 236 { 237 if (handle != NULL) { 238 scf_scope_destroy(handle->scf_scope); 239 scf_service_destroy(handle->scf_service); 240 scf_pg_destroy(handle->scf_pg); 241 handle->scf_state = NDMP_SCH_STATE_UNINIT; 242 (void) scf_handle_unbind(handle->scf_handle); 243 scf_handle_destroy(handle->scf_handle); 244 free(handle); 245 } 246 } 247 248 /* 249 * Must be called before using any of the SCF functions. Returns 250 * ndmp_scfhandle_t pointer if success. 251 */ 252 static ndmp_scfhandle_t * 253 ndmp_smf_scf_init(char *svc_name) 254 { 255 ndmp_scfhandle_t *handle; 256 257 handle = (ndmp_scfhandle_t *)calloc(1, sizeof (ndmp_scfhandle_t)); 258 if (handle != NULL) { 259 handle->scf_state = NDMP_SCH_STATE_INITIALIZING; 260 if (((handle->scf_handle = 261 scf_handle_create(SCF_VERSION)) != NULL) && 262 (scf_handle_bind(handle->scf_handle) == 0)) { 263 if ((handle->scf_scope = 264 scf_scope_create(handle->scf_handle)) == NULL) 265 goto err; 266 267 if (scf_handle_get_local_scope(handle->scf_handle, 268 handle->scf_scope) != 0) 269 goto err; 270 271 if ((handle->scf_service = 272 scf_service_create(handle->scf_handle)) == NULL) 273 goto err; 274 275 if (scf_scope_get_service(handle->scf_scope, svc_name, 276 handle->scf_service) != SCF_SUCCESS) 277 goto err; 278 279 if ((handle->scf_pg = 280 scf_pg_create(handle->scf_handle)) == NULL) 281 goto err; 282 283 handle->scf_state = NDMP_SCH_STATE_INIT; 284 } else { 285 goto err; 286 } 287 } else { 288 ndmp_errno = ENDMP_MEM_ALLOC; 289 handle = NULL; 290 } 291 return (handle); 292 293 /* Error handling/unwinding */ 294 err: 295 (void) ndmp_smf_scf_fini(handle); 296 ndmp_errno = ENDMP_SMF_INTERNAL; 297 return (NULL); 298 } 299 300 /* 301 * Create a new property group at service level. 302 */ 303 static int 304 ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *handle, char *pgroup) 305 { 306 int err; 307 308 /* 309 * Only create a handle if it doesn't exist. It is ok to exist since 310 * the pg handle will be set as a side effect. 311 */ 312 if (handle->scf_pg == NULL) { 313 if ((handle->scf_pg = 314 scf_pg_create(handle->scf_handle)) == NULL) 315 ndmp_errno = ENDMP_SMF_INTERNAL; 316 return (-1); 317 } 318 319 /* 320 * If the pgroup exists, we are done. If it doesn't, then we need to 321 * actually add one to the service instance. 322 */ 323 if (scf_service_get_pg(handle->scf_service, 324 pgroup, handle->scf_pg) != 0) { 325 /* Doesn't exist so create one */ 326 if (scf_service_add_pg(handle->scf_service, pgroup, 327 SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) { 328 err = scf_error(); 329 switch (err) { 330 case SCF_ERROR_PERMISSION_DENIED: 331 ndmp_errno = ENDMP_SMF_PERM; 332 return (-1); 333 break; 334 default: 335 ndmp_errno = ENDMP_SMF_INTERNAL; 336 return (-1); 337 break; 338 } 339 } 340 } 341 return (0); 342 } 343 344 /* 345 * Start transaction on current pg in handle. The pg could be service or 346 * instance level. Must be called after pg handle is obtained from create or 347 * get. 348 */ 349 static int 350 ndmp_smf_start_transaction(ndmp_scfhandle_t *handle) 351 { 352 /* 353 * Lookup the property group and create it if it doesn't already 354 * exist. 355 */ 356 if (handle->scf_state == NDMP_SCH_STATE_INIT) { 357 if ((handle->scf_trans = 358 scf_transaction_create(handle->scf_handle)) != NULL) { 359 if (scf_transaction_start(handle->scf_trans, 360 handle->scf_pg) != 0) { 361 scf_transaction_destroy(handle->scf_trans); 362 handle->scf_trans = NULL; 363 ndmp_errno = ENDMP_SMF_INTERNAL; 364 return (-1); 365 } 366 } else { 367 ndmp_errno = ENDMP_SMF_INTERNAL; 368 return (-1); 369 } 370 } 371 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { 372 ndmp_errno = ENDMP_SMF_PERM; 373 return (-1); 374 } 375 376 return (0); 377 } 378 379 /* 380 * Commit the changes that were added to the transaction in the handle. Do all 381 * necessary cleanup. 382 */ 383 static int 384 ndmp_smf_end_transaction(ndmp_scfhandle_t *handle) 385 { 386 if (scf_transaction_commit(handle->scf_trans) < 0) { 387 ndmp_errno = ENDMP_SMF_INTERNAL; 388 return (-1); 389 } 390 391 scf_transaction_destroy_children(handle->scf_trans); 392 scf_transaction_destroy(handle->scf_trans); 393 handle->scf_trans = NULL; 394 395 return (0); 396 } 397 398 /* 399 * Deletes property in current pg 400 */ 401 static int 402 ndmp_smf_delete_property(ndmp_scfhandle_t *handle, char *propname) 403 { 404 scf_transaction_entry_t *entry = NULL; 405 406 /* 407 * Properties must be set in transactions and don't take effect until 408 * the transaction has been ended/committed. 409 */ 410 if ((entry = scf_entry_create(handle->scf_handle)) != NULL) { 411 if (scf_transaction_property_delete(handle->scf_trans, entry, 412 propname) != 0) { 413 scf_entry_destroy(entry); 414 ndmp_errno = ENDMP_SMF_INTERNAL; 415 return (-1); 416 } 417 } else { 418 ndmp_errno = ENDMP_SMF_INTERNAL; 419 return (-1); 420 } 421 if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) { 422 ndmp_errno = ENDMP_SMF_PERM; 423 scf_entry_destroy(entry); 424 return (-1); 425 } 426 427 return (0); 428 } 429 430 /* 431 * Sets property in current pg 432 */ 433 static int 434 ndmp_smf_set_property(ndmp_scfhandle_t *handle, 435 char *propname, char *valstr) 436 { 437 int ret = 0; 438 scf_value_t *value = NULL; 439 scf_transaction_entry_t *entry = NULL; 440 scf_property_t *prop; 441 scf_type_t type; 442 int64_t valint; 443 uint8_t valbool; 444 445 /* 446 * Properties must be set in transactions and don't take effect until 447 * the transaction has been ended/committed. 448 */ 449 if (((value = scf_value_create(handle->scf_handle)) != NULL) && 450 (entry = scf_entry_create(handle->scf_handle)) != NULL) { 451 if (((prop = 452 scf_property_create(handle->scf_handle)) != NULL) && 453 ((scf_pg_get_property(handle->scf_pg, propname, 454 prop)) == 0)) { 455 if (scf_property_get_value(prop, value) == 0) { 456 type = scf_value_type(value); 457 if ((scf_transaction_property_change( 458 handle->scf_trans, entry, propname, 459 type) == 0) || 460 (scf_transaction_property_new( 461 handle->scf_trans, entry, propname, 462 type) == 0)) { 463 switch (type) { 464 case SCF_TYPE_ASTRING: 465 if ((scf_value_set_astring( 466 value, 467 valstr)) != SCF_SUCCESS) 468 ret = -1; 469 break; 470 case SCF_TYPE_INTEGER: 471 valint = strtoll(valstr, 0, 0); 472 scf_value_set_integer(value, 473 valint); 474 break; 475 case SCF_TYPE_BOOLEAN: 476 if (strncmp(valstr, "yes", 3)) 477 valbool = 0; 478 else 479 valbool = 1; 480 scf_value_set_boolean(value, 481 valbool); 482 break; 483 default: 484 ret = -1; 485 } 486 if (scf_entry_add_value(entry, 487 value) != 0) { 488 ret = -1; 489 scf_value_destroy(value); 490 } 491 /* The value is in the transaction */ 492 value = NULL; 493 } 494 /* The entry is in the transaction */ 495 entry = NULL; 496 } else { 497 ret = -1; 498 } 499 } else { 500 ret = -1; 501 } 502 } else { 503 ret = -1; 504 } 505 if (ret == -1) { 506 if ((scf_error() == SCF_ERROR_PERMISSION_DENIED)) 507 ndmp_errno = ENDMP_SMF_PERM; 508 else 509 ndmp_errno = ENDMP_SMF_INTERNAL; 510 } 511 scf_value_destroy(value); 512 scf_entry_destroy(entry); 513 return (ret); 514 } 515 516 /* 517 * Gets a property value.upto sz size. Caller is responsible to have enough 518 * memory allocated. 519 */ 520 static int 521 ndmp_smf_get_property(ndmp_scfhandle_t *handle, char *propname, 522 char *valstr, size_t sz) 523 { 524 int ret = 0; 525 scf_value_t *value; 526 scf_property_t *prop; 527 scf_type_t type; 528 int64_t valint; 529 uint8_t valbool; 530 char valstrbuf[NDMP_PROP_LEN]; 531 532 if (((value = scf_value_create(handle->scf_handle)) != NULL) && 533 ((prop = scf_property_create(handle->scf_handle)) != NULL) && 534 (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { 535 if (scf_property_get_value(prop, value) == 0) { 536 type = scf_value_type(value); 537 switch (type) { 538 case SCF_TYPE_ASTRING: 539 if (scf_value_get_astring(value, valstr, 540 sz) < 0) { 541 ret = -1; 542 } 543 break; 544 case SCF_TYPE_INTEGER: 545 if (scf_value_get_integer(value, 546 &valint) != 0) { 547 ret = -1; 548 break; 549 } 550 valstrbuf[NDMP_PROP_LEN - 1] = '\0'; 551 (void) strncpy(valstr, lltostr(valint, 552 &valstrbuf[NDMP_PROP_LEN - 1]), 553 NDMP_PROP_LEN); 554 break; 555 case SCF_TYPE_BOOLEAN: 556 if (scf_value_get_boolean(value, 557 &valbool) != 0) { 558 ret = -1; 559 break; 560 } 561 if (valbool == 1) 562 (void) strncpy(valstr, "yes", 4); 563 else 564 (void) strncpy(valstr, "no", 3); 565 break; 566 default: 567 ret = -1; 568 } 569 } else { 570 ret = -1; 571 } 572 } else { 573 ret = -1; 574 } 575 scf_value_destroy(value); 576 scf_property_destroy(prop); 577 return (ret); 578 } 579