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 2015 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 *, boolean_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 *, boolean_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 int rc = smf_refresh_instance(NDMP_INST); 98 99 if (rc != 0) 100 ndmp_errno = ENDMP_SMF_INTERNAL; 101 return (rc); 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; 113 char *lval; 114 char *pgname; 115 116 *value = NULL; 117 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) { 118 return (-1); 119 } 120 if (ndmp_smf_get_pg_name(handle, prop, &pgname)) { 121 ndmp_smf_scf_fini(handle); 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 return (-1); 128 } 129 if ((lval = malloc(NDMP_PROP_LEN)) == NULL) { 130 ndmp_smf_scf_fini(handle); 131 ndmp_errno = ENDMP_MEM_ALLOC; 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; 149 char *pgname; 150 int rc; 151 152 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) 153 return (-1); 154 155 if (ndmp_smf_get_pg_name(handle, env, &pgname)) { 156 ndmp_smf_scf_fini(handle); 157 ndmp_errno = ENDMP_SMF_PROP_GRP; 158 return (-1); 159 } 160 161 if (ndmp_smf_create_service_pgroup(handle, pgname)) { 162 ndmp_smf_scf_fini(handle); 163 return (-1); 164 } 165 166 if (ndmp_smf_start_transaction(handle)) { 167 ndmp_smf_scf_fini(handle); 168 return (-1); 169 } 170 171 if (env_val) 172 rc = ndmp_smf_set_property(handle, env, env_val); 173 else 174 rc = ndmp_smf_delete_property(handle, env); 175 176 if (ndmp_config_saveenv(handle, (rc == 0)) == 0) 177 return (rc); 178 else 179 return (-1); 180 } 181 182 static int 183 ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, const char *pname, char **pgname) 184 { 185 scf_value_t *value; 186 scf_property_t *prop; 187 int i; 188 189 for (i = 0; i < NPG; i++) { 190 if (scf_service_get_pg(h->scf_service, ndmp_pg[i], 191 h->scf_pg) != 0) 192 return (-1); 193 194 if ((value = scf_value_create(h->scf_handle)) == NULL) 195 return (-1); 196 197 if ((prop = scf_property_create(h->scf_handle)) == NULL) { 198 scf_value_destroy(value); 199 return (-1); 200 } 201 /* 202 * This will fail if property does not exist in the property 203 * group. Check the next property group in case of failure. 204 */ 205 if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) { 206 scf_value_destroy(value); 207 scf_property_destroy(prop); 208 continue; 209 } 210 211 *pgname = ndmp_pg[i]; 212 scf_value_destroy(value); 213 scf_property_destroy(prop); 214 return (0); 215 } 216 return (-1); 217 } 218 219 /* 220 * Basically commit the transaction. 221 */ 222 static int 223 ndmp_config_saveenv(ndmp_scfhandle_t *handle, boolean_t commit) 224 { 225 int ret = 0; 226 227 ret = ndmp_smf_end_transaction(handle, commit); 228 229 ndmp_smf_scf_fini(handle); 230 return (ret); 231 } 232 233 /* 234 * Must be called when done. Called with the handle allocated in 235 * ndmp_smf_scf_init(), it cleans up the state and frees any SCF resources 236 * still in use. 237 */ 238 static void 239 ndmp_smf_scf_fini(ndmp_scfhandle_t *handle) 240 { 241 if (handle == NULL) 242 return; 243 244 scf_scope_destroy(handle->scf_scope); 245 scf_service_destroy(handle->scf_service); 246 scf_pg_destroy(handle->scf_pg); 247 handle->scf_state = NDMP_SCH_STATE_UNINIT; 248 (void) scf_handle_unbind(handle->scf_handle); 249 scf_handle_destroy(handle->scf_handle); 250 free(handle); 251 } 252 253 /* 254 * Must be called before using any of the SCF functions. Returns 255 * ndmp_scfhandle_t pointer if success. 256 */ 257 static ndmp_scfhandle_t * 258 ndmp_smf_scf_init(const char *svc_name) 259 { 260 ndmp_scfhandle_t *handle; 261 262 handle = (ndmp_scfhandle_t *)calloc(1, sizeof (ndmp_scfhandle_t)); 263 if (handle != NULL) { 264 handle->scf_state = NDMP_SCH_STATE_INITIALIZING; 265 if (((handle->scf_handle = 266 scf_handle_create(SCF_VERSION)) != NULL) && 267 (scf_handle_bind(handle->scf_handle) == 0)) { 268 if ((handle->scf_scope = 269 scf_scope_create(handle->scf_handle)) == NULL) 270 goto err; 271 272 if (scf_handle_get_local_scope(handle->scf_handle, 273 handle->scf_scope) != 0) 274 goto err; 275 276 if ((handle->scf_service = 277 scf_service_create(handle->scf_handle)) == NULL) 278 goto err; 279 280 if (scf_scope_get_service(handle->scf_scope, svc_name, 281 handle->scf_service) != SCF_SUCCESS) 282 goto err; 283 284 if ((handle->scf_pg = 285 scf_pg_create(handle->scf_handle)) == NULL) 286 goto err; 287 288 handle->scf_state = NDMP_SCH_STATE_INIT; 289 } else { 290 goto err; 291 } 292 } else { 293 ndmp_errno = ENDMP_MEM_ALLOC; 294 handle = NULL; 295 } 296 return (handle); 297 298 /* Error handling/unwinding */ 299 err: 300 (void) ndmp_smf_scf_fini(handle); 301 ndmp_errno = ENDMP_SMF_INTERNAL; 302 return (NULL); 303 } 304 305 /* 306 * Create a new property group at service level. 307 */ 308 static int 309 ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *handle, const char *pgroup) 310 { 311 int err; 312 313 /* 314 * Only create a handle if it doesn't exist. It is ok to exist since 315 * the pg handle will be set as a side effect. 316 */ 317 if (handle->scf_pg == NULL) { 318 if ((handle->scf_pg = 319 scf_pg_create(handle->scf_handle)) == NULL) { 320 ndmp_errno = ENDMP_SMF_INTERNAL; 321 return (-1); 322 } 323 } 324 325 /* 326 * If the pgroup exists, we are done. If it doesn't, then we need to 327 * actually add one to the service instance. 328 */ 329 if (scf_service_get_pg(handle->scf_service, 330 pgroup, handle->scf_pg) != 0) { 331 /* Doesn't exist so create one */ 332 if (scf_service_add_pg(handle->scf_service, pgroup, 333 SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) { 334 err = scf_error(); 335 switch (err) { 336 case SCF_ERROR_PERMISSION_DENIED: 337 ndmp_errno = ENDMP_SMF_PERM; 338 return (-1); 339 default: 340 ndmp_errno = ENDMP_SMF_INTERNAL; 341 return (-1); 342 } 343 } 344 } 345 return (0); 346 } 347 348 /* 349 * Start transaction on current pg in handle. The pg could be service or 350 * instance level. Must be called after pg handle is obtained from create or 351 * get. 352 */ 353 static int 354 ndmp_smf_start_transaction(ndmp_scfhandle_t *handle) 355 { 356 /* 357 * Lookup the property group and create it if it doesn't already 358 * exist. 359 */ 360 if (handle->scf_state == NDMP_SCH_STATE_INIT) { 361 if ((handle->scf_trans = 362 scf_transaction_create(handle->scf_handle)) != NULL) { 363 if (scf_transaction_start(handle->scf_trans, 364 handle->scf_pg) != 0) { 365 scf_transaction_destroy(handle->scf_trans); 366 handle->scf_trans = NULL; 367 ndmp_errno = ENDMP_SMF_INTERNAL; 368 return (-1); 369 } 370 } else { 371 ndmp_errno = ENDMP_SMF_INTERNAL; 372 return (-1); 373 } 374 } 375 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { 376 ndmp_errno = ENDMP_SMF_PERM; 377 return (-1); 378 } 379 380 return (0); 381 } 382 383 /* 384 * Commit the changes that were added to the transaction in the handle. Do all 385 * necessary cleanup. 386 */ 387 static int 388 ndmp_smf_end_transaction(ndmp_scfhandle_t *handle, boolean_t commit) 389 { 390 int rc = 0; 391 392 if (commit) { 393 if (scf_transaction_commit(handle->scf_trans) < 0) { 394 ndmp_errno = ENDMP_SMF_INTERNAL; 395 rc = -1; 396 } 397 } 398 399 scf_transaction_destroy_children(handle->scf_trans); 400 scf_transaction_destroy(handle->scf_trans); 401 handle->scf_trans = NULL; 402 403 return (rc); 404 } 405 406 /* 407 * Deletes property in current pg 408 */ 409 static int 410 ndmp_smf_delete_property(ndmp_scfhandle_t *handle, const char *propname) 411 { 412 scf_transaction_entry_t *entry = NULL; 413 414 /* 415 * Properties must be set in transactions and don't take effect until 416 * the transaction has been ended/committed. 417 */ 418 if ((entry = scf_entry_create(handle->scf_handle)) != NULL) { 419 if (scf_transaction_property_delete(handle->scf_trans, entry, 420 propname) != 0) { 421 scf_entry_destroy(entry); 422 ndmp_errno = ENDMP_SMF_INTERNAL; 423 return (-1); 424 } 425 } else { 426 ndmp_errno = ENDMP_SMF_INTERNAL; 427 return (-1); 428 } 429 if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) { 430 ndmp_errno = ENDMP_SMF_PERM; 431 scf_entry_destroy(entry); 432 return (-1); 433 } 434 435 return (0); 436 } 437 438 /* 439 * Sets property in current pg 440 */ 441 static int 442 ndmp_smf_set_property(ndmp_scfhandle_t *handle, const char *propname, 443 const char *valstr) 444 { 445 int ret = 0; 446 scf_value_t *value = NULL; 447 scf_transaction_entry_t *entry = NULL; 448 scf_property_t *prop = NULL; 449 scf_type_t type; 450 int64_t valint; 451 uint8_t valbool; 452 453 /* 454 * Properties must be set in transactions and don't take effect until 455 * the transaction has been ended/committed. 456 */ 457 if (((value = scf_value_create(handle->scf_handle)) == NULL) || 458 ((entry = scf_entry_create(handle->scf_handle)) == NULL) || 459 ((prop = scf_property_create(handle->scf_handle)) == NULL) || 460 (scf_pg_get_property(handle->scf_pg, propname, prop) != 0) || 461 (scf_property_get_value(prop, value) != 0)) { 462 ret = -1; 463 goto out; 464 } 465 466 type = scf_value_type(value); 467 if ((scf_transaction_property_change(handle->scf_trans, entry, propname, 468 type) != 0) && 469 (scf_transaction_property_new(handle->scf_trans, entry, propname, 470 type) != 0)) { 471 ret = -1; 472 goto out; 473 } 474 475 switch (type) { 476 case SCF_TYPE_ASTRING: 477 if ((scf_value_set_astring(value, valstr)) != SCF_SUCCESS) 478 ret = -1; 479 break; 480 case SCF_TYPE_INTEGER: 481 valint = strtoll(valstr, 0, 0); 482 scf_value_set_integer(value, valint); 483 break; 484 case SCF_TYPE_BOOLEAN: 485 if (strncmp(valstr, "yes", 3)) 486 valbool = 0; 487 else 488 valbool = 1; 489 scf_value_set_boolean(value, valbool); 490 break; 491 default: 492 ret = -1; 493 } 494 if (scf_entry_add_value(entry, value) == 0) { 495 /* The value is in the transaction */ 496 value = NULL; 497 } else { 498 ret = -1; 499 } 500 /* The entry is in the transaction */ 501 entry = NULL; 502 503 out: 504 if (ret == -1) { 505 if ((scf_error() == SCF_ERROR_PERMISSION_DENIED)) 506 ndmp_errno = ENDMP_SMF_PERM; 507 else 508 ndmp_errno = ENDMP_SMF_INTERNAL; 509 } 510 scf_property_destroy(prop); 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 = NULL; 526 scf_property_t *prop = NULL; 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