1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 * Copyright 2012 Milan Jurik. All rights reserved. 5 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 6 */ 7 8 /* 9 * BSD 3 Clause License 10 * 11 * Copyright (c) 2007, The Storage Networking Industry Association. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in 21 * the documentation and/or other materials provided with the 22 * distribution. 23 * 24 * - Neither the name of The Storage Networking Industry Association (SNIA) 25 * nor the names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior written 27 * permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 33 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 * POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 /* 43 * NDMP configuration management 44 */ 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <synch.h> 48 #include <libintl.h> 49 #include <strings.h> 50 #include <libndmp.h> 51 52 /* NDMP properties configuration */ 53 #define NDMP_GROUP_FMRI_PREFIX "system/ndmpd" 54 #define NDMP_INST "svc:/system/ndmpd:default" 55 #define NDMP_PROP_LEN 600 56 static char *ndmp_pg[] = { 57 "ndmpd", 58 "read" 59 }; 60 #define NPG (sizeof (ndmp_pg) / sizeof (ndmp_pg[0])) 61 62 /* Handle Init states */ 63 #define NDMP_SCH_STATE_UNINIT 0 64 #define NDMP_SCH_STATE_INITIALIZING 1 65 #define NDMP_SCH_STATE_INIT 2 66 67 /* NDMP scf handle structure */ 68 typedef struct ndmp_scfhandle { 69 scf_handle_t *scf_handle; 70 int scf_state; 71 scf_service_t *scf_service; 72 scf_scope_t *scf_scope; 73 scf_transaction_t *scf_trans; 74 scf_propertygroup_t *scf_pg; 75 } ndmp_scfhandle_t; 76 77 static int ndmp_config_saveenv(ndmp_scfhandle_t *); 78 static ndmp_scfhandle_t *ndmp_smf_scf_init(const char *); 79 static void ndmp_smf_scf_fini(ndmp_scfhandle_t *); 80 static int ndmp_smf_start_transaction(ndmp_scfhandle_t *); 81 static int ndmp_smf_end_transaction(ndmp_scfhandle_t *); 82 static int ndmp_smf_set_property(ndmp_scfhandle_t *, const char *, 83 const char *); 84 static int ndmp_smf_get_property(ndmp_scfhandle_t *, const char *, char *, 85 size_t); 86 static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, const char *); 87 static int ndmp_smf_delete_property(ndmp_scfhandle_t *, const char *); 88 static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *, const char *, char **); 89 90 /* 91 * This routine send a refresh signal to ndmpd service which cause ndmpd 92 * property table to be refeshed with current ndmpd properties value from SMF. 93 */ 94 int 95 ndmp_service_refresh(void) 96 { 97 if ((smf_get_state(NDMP_INST)) != NULL) 98 return (smf_refresh_instance(NDMP_INST)); 99 100 ndmp_errno = ENDMP_SMF_INTERNAL; 101 return (-1); 102 } 103 104 /* 105 * Returns value of the specified variable/property. The return value is a 106 * string pointer to the locally allocated memory if the config param is 107 * defined otherwise it would be NULL. 108 */ 109 int 110 ndmp_get_prop(const char *prop, char **value) 111 { 112 ndmp_scfhandle_t *handle = NULL; 113 char *lval = (char *)malloc(NDMP_PROP_LEN); 114 char *pgname; 115 116 if (!lval) { 117 ndmp_errno = ENDMP_MEM_ALLOC; 118 return (-1); 119 } 120 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) { 121 free(lval); 122 return (-1); 123 } 124 if (ndmp_smf_get_pg_name(handle, prop, &pgname)) { 125 free(lval); 126 ndmp_errno = ENDMP_SMF_PROP_GRP; 127 return (-1); 128 } 129 if (ndmp_smf_create_service_pgroup(handle, pgname)) { 130 ndmp_smf_scf_fini(handle); 131 free(lval); 132 return (-1); 133 } 134 if (ndmp_smf_get_property(handle, prop, lval, NDMP_PROP_LEN) != 0) { 135 ndmp_smf_scf_fini(handle); 136 free(lval); 137 ndmp_errno = ENDMP_SMF_PROP; 138 return (-1); 139 } 140 *value = lval; 141 ndmp_smf_scf_fini(handle); 142 return (0); 143 } 144 145 int 146 ndmp_set_prop(const char *env, const char *env_val) 147 { 148 ndmp_scfhandle_t *handle = NULL; 149 char *pgname; 150 151 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) 152 return (-1); 153 154 if (ndmp_smf_get_pg_name(handle, env, &pgname)) { 155 ndmp_errno = ENDMP_SMF_PROP_GRP; 156 return (-1); 157 } 158 159 if (ndmp_smf_create_service_pgroup(handle, pgname)) 160 return (-1); 161 162 if (ndmp_smf_start_transaction(handle)) 163 return (-1); 164 165 if (env_val) { 166 if (ndmp_smf_set_property(handle, env, env_val)) { 167 return (-1); 168 } 169 } else { 170 if (ndmp_smf_delete_property(handle, env)) 171 return (-1); 172 } 173 174 if (ndmp_config_saveenv(handle) != 0) 175 return (-1); 176 177 return (0); 178 } 179 180 static int 181 ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, const char *pname, char **pgname) 182 { 183 scf_value_t *value; 184 scf_property_t *prop; 185 int i; 186 187 for (i = 0; i < NPG; i++) { 188 if (scf_service_get_pg(h->scf_service, ndmp_pg[i], 189 h->scf_pg) != 0) 190 return (-1); 191 192 if ((value = scf_value_create(h->scf_handle)) == NULL) 193 return (-1); 194 195 if ((prop = scf_property_create(h->scf_handle)) == NULL) { 196 scf_value_destroy(value); 197 return (-1); 198 } 199 /* 200 * This will fail if property does not exist in the property 201 * group. Check the next property group in case of failure. 202 */ 203 if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) { 204 scf_value_destroy(value); 205 scf_property_destroy(prop); 206 continue; 207 } 208 209 *pgname = ndmp_pg[i]; 210 scf_value_destroy(value); 211 scf_property_destroy(prop); 212 return (0); 213 } 214 return (-1); 215 } 216 217 /* 218 * Basically commit the transaction. 219 */ 220 static int 221 ndmp_config_saveenv(ndmp_scfhandle_t *handle) 222 { 223 int ret = 0; 224 225 ret = ndmp_smf_end_transaction(handle); 226 227 ndmp_smf_scf_fini(handle); 228 return (ret); 229 } 230 231 /* 232 * Must be called when done. Called with the handle allocated in 233 * ndmp_smf_scf_init(), it cleans up the state and frees any SCF resources 234 * still in use. 235 */ 236 static void 237 ndmp_smf_scf_fini(ndmp_scfhandle_t *handle) 238 { 239 if (handle != NULL) { 240 scf_scope_destroy(handle->scf_scope); 241 scf_service_destroy(handle->scf_service); 242 scf_pg_destroy(handle->scf_pg); 243 handle->scf_state = NDMP_SCH_STATE_UNINIT; 244 (void) scf_handle_unbind(handle->scf_handle); 245 scf_handle_destroy(handle->scf_handle); 246 free(handle); 247 } 248 } 249 250 /* 251 * Must be called before using any of the SCF functions. Returns 252 * ndmp_scfhandle_t pointer if success. 253 */ 254 static ndmp_scfhandle_t * 255 ndmp_smf_scf_init(const char *svc_name) 256 { 257 ndmp_scfhandle_t *handle; 258 259 handle = (ndmp_scfhandle_t *)calloc(1, sizeof (ndmp_scfhandle_t)); 260 if (handle != NULL) { 261 handle->scf_state = NDMP_SCH_STATE_INITIALIZING; 262 if (((handle->scf_handle = 263 scf_handle_create(SCF_VERSION)) != NULL) && 264 (scf_handle_bind(handle->scf_handle) == 0)) { 265 if ((handle->scf_scope = 266 scf_scope_create(handle->scf_handle)) == NULL) 267 goto err; 268 269 if (scf_handle_get_local_scope(handle->scf_handle, 270 handle->scf_scope) != 0) 271 goto err; 272 273 if ((handle->scf_service = 274 scf_service_create(handle->scf_handle)) == NULL) 275 goto err; 276 277 if (scf_scope_get_service(handle->scf_scope, svc_name, 278 handle->scf_service) != SCF_SUCCESS) 279 goto err; 280 281 if ((handle->scf_pg = 282 scf_pg_create(handle->scf_handle)) == NULL) 283 goto err; 284 285 handle->scf_state = NDMP_SCH_STATE_INIT; 286 } else { 287 goto err; 288 } 289 } else { 290 ndmp_errno = ENDMP_MEM_ALLOC; 291 handle = NULL; 292 } 293 return (handle); 294 295 /* Error handling/unwinding */ 296 err: 297 (void) ndmp_smf_scf_fini(handle); 298 ndmp_errno = ENDMP_SMF_INTERNAL; 299 return (NULL); 300 } 301 302 /* 303 * Create a new property group at service level. 304 */ 305 static int 306 ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *handle, const char *pgroup) 307 { 308 int err; 309 310 /* 311 * Only create a handle if it doesn't exist. It is ok to exist since 312 * the pg handle will be set as a side effect. 313 */ 314 if (handle->scf_pg == NULL) { 315 if ((handle->scf_pg = 316 scf_pg_create(handle->scf_handle)) == NULL) 317 ndmp_errno = ENDMP_SMF_INTERNAL; 318 return (-1); 319 } 320 321 /* 322 * If the pgroup exists, we are done. If it doesn't, then we need to 323 * actually add one to the service instance. 324 */ 325 if (scf_service_get_pg(handle->scf_service, 326 pgroup, handle->scf_pg) != 0) { 327 /* Doesn't exist so create one */ 328 if (scf_service_add_pg(handle->scf_service, pgroup, 329 SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) { 330 err = scf_error(); 331 switch (err) { 332 case SCF_ERROR_PERMISSION_DENIED: 333 ndmp_errno = ENDMP_SMF_PERM; 334 return (-1); 335 default: 336 ndmp_errno = ENDMP_SMF_INTERNAL; 337 return (-1); 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, const 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 const char *propname, const 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, const 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