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 * If the pgroup exists, we are done. If it doesn't, then we need to 326 * actually add one to the service instance. 327 */ 328 if (scf_service_get_pg(handle->scf_service, 329 pgroup, handle->scf_pg) != 0) { 330 /* Doesn't exist so create one */ 331 if (scf_service_add_pg(handle->scf_service, pgroup, 332 SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) { 333 err = scf_error(); 334 switch (err) { 335 case SCF_ERROR_PERMISSION_DENIED: 336 ndmp_errno = ENDMP_SMF_PERM; 337 return (-1); 338 default: 339 ndmp_errno = ENDMP_SMF_INTERNAL; 340 return (-1); 341 } 342 } 343 } 344 return (0); 345 } 346 347 /* 348 * Start transaction on current pg in handle. The pg could be service or 349 * instance level. Must be called after pg handle is obtained from create or 350 * get. 351 */ 352 static int 353 ndmp_smf_start_transaction(ndmp_scfhandle_t *handle) 354 { 355 /* 356 * Lookup the property group and create it if it doesn't already 357 * exist. 358 */ 359 if (handle->scf_state == NDMP_SCH_STATE_INIT) { 360 if ((handle->scf_trans = 361 scf_transaction_create(handle->scf_handle)) != NULL) { 362 if (scf_transaction_start(handle->scf_trans, 363 handle->scf_pg) != 0) { 364 scf_transaction_destroy(handle->scf_trans); 365 handle->scf_trans = NULL; 366 ndmp_errno = ENDMP_SMF_INTERNAL; 367 return (-1); 368 } 369 } else { 370 ndmp_errno = ENDMP_SMF_INTERNAL; 371 return (-1); 372 } 373 } 374 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { 375 ndmp_errno = ENDMP_SMF_PERM; 376 return (-1); 377 } 378 379 return (0); 380 } 381 382 /* 383 * Commit the changes that were added to the transaction in the handle. Do all 384 * necessary cleanup. 385 */ 386 static int 387 ndmp_smf_end_transaction(ndmp_scfhandle_t *handle, boolean_t commit) 388 { 389 int rc = 0; 390 391 if (commit) { 392 if (scf_transaction_commit(handle->scf_trans) < 0) { 393 ndmp_errno = ENDMP_SMF_INTERNAL; 394 rc = -1; 395 } 396 } 397 398 scf_transaction_destroy_children(handle->scf_trans); 399 scf_transaction_destroy(handle->scf_trans); 400 handle->scf_trans = NULL; 401 402 return (rc); 403 } 404 405 /* 406 * Deletes property in current pg 407 */ 408 static int 409 ndmp_smf_delete_property(ndmp_scfhandle_t *handle, const char *propname) 410 { 411 scf_transaction_entry_t *entry = NULL; 412 413 /* 414 * Properties must be set in transactions and don't take effect until 415 * the transaction has been ended/committed. 416 */ 417 if ((entry = scf_entry_create(handle->scf_handle)) != NULL) { 418 if (scf_transaction_property_delete(handle->scf_trans, entry, 419 propname) != 0) { 420 scf_entry_destroy(entry); 421 ndmp_errno = ENDMP_SMF_INTERNAL; 422 return (-1); 423 } 424 } else { 425 ndmp_errno = ENDMP_SMF_INTERNAL; 426 return (-1); 427 } 428 if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) { 429 ndmp_errno = ENDMP_SMF_PERM; 430 scf_entry_destroy(entry); 431 return (-1); 432 } 433 434 return (0); 435 } 436 437 /* 438 * Sets property in current pg 439 */ 440 static int 441 ndmp_smf_set_property(ndmp_scfhandle_t *handle, const char *propname, 442 const char *valstr) 443 { 444 int ret = 0; 445 scf_value_t *value = NULL; 446 scf_transaction_entry_t *entry = NULL; 447 scf_property_t *prop = NULL; 448 scf_type_t type; 449 int64_t valint; 450 uint8_t valbool; 451 452 /* 453 * Properties must be set in transactions and don't take effect until 454 * the transaction has been ended/committed. 455 */ 456 if (((value = scf_value_create(handle->scf_handle)) == NULL) || 457 ((entry = scf_entry_create(handle->scf_handle)) == NULL) || 458 ((prop = scf_property_create(handle->scf_handle)) == NULL) || 459 (scf_pg_get_property(handle->scf_pg, propname, prop) != 0) || 460 (scf_property_get_value(prop, value) != 0)) { 461 ret = -1; 462 goto out; 463 } 464 465 type = scf_value_type(value); 466 if ((scf_transaction_property_change(handle->scf_trans, entry, propname, 467 type) != 0) && 468 (scf_transaction_property_new(handle->scf_trans, entry, propname, 469 type) != 0)) { 470 ret = -1; 471 goto out; 472 } 473 474 switch (type) { 475 case SCF_TYPE_ASTRING: 476 if ((scf_value_set_astring(value, valstr)) != SCF_SUCCESS) 477 ret = -1; 478 break; 479 case SCF_TYPE_INTEGER: 480 valint = strtoll(valstr, 0, 0); 481 scf_value_set_integer(value, valint); 482 break; 483 case SCF_TYPE_BOOLEAN: 484 if (strncmp(valstr, "yes", 3)) 485 valbool = 0; 486 else 487 valbool = 1; 488 scf_value_set_boolean(value, valbool); 489 break; 490 default: 491 ret = -1; 492 } 493 if (scf_entry_add_value(entry, value) == 0) { 494 /* The value is in the transaction */ 495 value = NULL; 496 } else { 497 ret = -1; 498 } 499 /* The entry is in the transaction */ 500 entry = NULL; 501 502 out: 503 if (ret == -1) { 504 if ((scf_error() == SCF_ERROR_PERMISSION_DENIED)) 505 ndmp_errno = ENDMP_SMF_PERM; 506 else 507 ndmp_errno = ENDMP_SMF_INTERNAL; 508 } 509 scf_property_destroy(prop); 510 scf_value_destroy(value); 511 scf_entry_destroy(entry); 512 return (ret); 513 } 514 515 /* 516 * Gets a property value.upto sz size. Caller is responsible to have enough 517 * memory allocated. 518 */ 519 static int 520 ndmp_smf_get_property(ndmp_scfhandle_t *handle, const char *propname, 521 char *valstr, size_t sz) 522 { 523 int ret = 0; 524 scf_value_t *value = NULL; 525 scf_property_t *prop = NULL; 526 scf_type_t type; 527 int64_t valint; 528 uint8_t valbool; 529 char valstrbuf[NDMP_PROP_LEN]; 530 531 if (((value = scf_value_create(handle->scf_handle)) != NULL) && 532 ((prop = scf_property_create(handle->scf_handle)) != NULL) && 533 (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { 534 if (scf_property_get_value(prop, value) == 0) { 535 type = scf_value_type(value); 536 switch (type) { 537 case SCF_TYPE_ASTRING: 538 if (scf_value_get_astring(value, valstr, 539 sz) < 0) { 540 ret = -1; 541 } 542 break; 543 case SCF_TYPE_INTEGER: 544 if (scf_value_get_integer(value, 545 &valint) != 0) { 546 ret = -1; 547 break; 548 } 549 valstrbuf[NDMP_PROP_LEN - 1] = '\0'; 550 (void) strncpy(valstr, lltostr(valint, 551 &valstrbuf[NDMP_PROP_LEN - 1]), 552 NDMP_PROP_LEN); 553 break; 554 case SCF_TYPE_BOOLEAN: 555 if (scf_value_get_boolean(value, 556 &valbool) != 0) { 557 ret = -1; 558 break; 559 } 560 if (valbool == 1) 561 (void) strncpy(valstr, "yes", 4); 562 else 563 (void) strncpy(valstr, "no", 3); 564 break; 565 default: 566 ret = -1; 567 } 568 } else { 569 ret = -1; 570 } 571 } else { 572 ret = -1; 573 } 574 scf_value_destroy(value); 575 scf_property_destroy(prop); 576 return (ret); 577 } 578