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(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 *, char *, char *); 83 static int ndmp_smf_get_property(ndmp_scfhandle_t *, char *, char *, size_t); 84 static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, char *); 85 static int ndmp_smf_delete_property(ndmp_scfhandle_t *, char *); 86 static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *, char *, char **); 87 88 /* 89 * This routine send a refresh signal to ndmpd service which cause ndmpd 90 * property table to be refeshed with current ndmpd properties value from SMF. 91 */ 92 int 93 ndmp_service_refresh(void) 94 { 95 if ((smf_get_state(NDMP_INST)) != NULL) 96 return (smf_refresh_instance(NDMP_INST)); 97 98 ndmp_errno = ENDMP_SMF_INTERNAL; 99 return (-1); 100 } 101 102 /* 103 * Returns value of the specified variable/property. The return value is a 104 * string pointer to the locally allocated memory if the config param is 105 * defined otherwise it would be NULL. 106 */ 107 int 108 ndmp_get_prop(char *prop, char **value) 109 { 110 ndmp_scfhandle_t *handle = NULL; 111 char *lval = (char *)malloc(NDMP_PROP_LEN); 112 char *pgname; 113 114 if (!lval) { 115 ndmp_errno = ENDMP_MEM_ALLOC; 116 return (-1); 117 } 118 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) { 119 free(lval); 120 return (-1); 121 } 122 if (ndmp_smf_get_pg_name(handle, prop, &pgname)) { 123 free(lval); 124 ndmp_errno = ENDMP_SMF_PROP_GRP; 125 return (-1); 126 } 127 if (ndmp_smf_create_service_pgroup(handle, pgname)) { 128 ndmp_smf_scf_fini(handle); 129 free(lval); 130 return (-1); 131 } 132 if (ndmp_smf_get_property(handle, prop, lval, NDMP_PROP_LEN) != 0) { 133 ndmp_smf_scf_fini(handle); 134 free(lval); 135 ndmp_errno = ENDMP_SMF_PROP; 136 return (-1); 137 } 138 *value = lval; 139 ndmp_smf_scf_fini(handle); 140 return (0); 141 } 142 143 int 144 ndmp_set_prop(char *env, char *env_val) 145 { 146 ndmp_scfhandle_t *handle = NULL; 147 char *pgname; 148 149 if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) 150 return (-1); 151 152 if (ndmp_smf_get_pg_name(handle, env, &pgname)) { 153 ndmp_errno = ENDMP_SMF_PROP_GRP; 154 return (-1); 155 } 156 157 if (ndmp_smf_create_service_pgroup(handle, pgname)) 158 return (-1); 159 160 if (ndmp_smf_start_transaction(handle)) 161 return (-1); 162 163 if (env_val) { 164 if (ndmp_smf_set_property(handle, env, env_val)) { 165 return (-1); 166 } 167 } else { 168 if (ndmp_smf_delete_property(handle, env)) 169 return (-1); 170 } 171 172 if (ndmp_config_saveenv(handle) != 0) 173 return (-1); 174 175 return (0); 176 } 177 178 static int 179 ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, char *pname, char **pgname) 180 { 181 scf_value_t *value; 182 scf_property_t *prop; 183 int i; 184 185 for (i = 0; i < NPG; i++) { 186 if (scf_service_get_pg(h->scf_service, ndmp_pg[i], 187 h->scf_pg) != 0) 188 return (-1); 189 190 if ((value = scf_value_create(h->scf_handle)) == NULL) 191 return (-1); 192 193 if ((prop = scf_property_create(h->scf_handle)) == NULL) { 194 scf_value_destroy(value); 195 return (-1); 196 } 197 /* 198 * This will fail if property does not exist in the property 199 * group. Check the next property group in case of failure. 200 */ 201 if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) { 202 scf_value_destroy(value); 203 scf_property_destroy(prop); 204 continue; 205 } 206 207 *pgname = ndmp_pg[i]; 208 scf_value_destroy(value); 209 scf_property_destroy(prop); 210 return (0); 211 } 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 default: 334 ndmp_errno = ENDMP_SMF_INTERNAL; 335 return (-1); 336 } 337 } 338 } 339 return (0); 340 } 341 342 /* 343 * Start transaction on current pg in handle. The pg could be service or 344 * instance level. Must be called after pg handle is obtained from create or 345 * get. 346 */ 347 static int 348 ndmp_smf_start_transaction(ndmp_scfhandle_t *handle) 349 { 350 /* 351 * Lookup the property group and create it if it doesn't already 352 * exist. 353 */ 354 if (handle->scf_state == NDMP_SCH_STATE_INIT) { 355 if ((handle->scf_trans = 356 scf_transaction_create(handle->scf_handle)) != NULL) { 357 if (scf_transaction_start(handle->scf_trans, 358 handle->scf_pg) != 0) { 359 scf_transaction_destroy(handle->scf_trans); 360 handle->scf_trans = NULL; 361 ndmp_errno = ENDMP_SMF_INTERNAL; 362 return (-1); 363 } 364 } else { 365 ndmp_errno = ENDMP_SMF_INTERNAL; 366 return (-1); 367 } 368 } 369 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { 370 ndmp_errno = ENDMP_SMF_PERM; 371 return (-1); 372 } 373 374 return (0); 375 } 376 377 /* 378 * Commit the changes that were added to the transaction in the handle. Do all 379 * necessary cleanup. 380 */ 381 static int 382 ndmp_smf_end_transaction(ndmp_scfhandle_t *handle) 383 { 384 if (scf_transaction_commit(handle->scf_trans) < 0) { 385 ndmp_errno = ENDMP_SMF_INTERNAL; 386 return (-1); 387 } 388 389 scf_transaction_destroy_children(handle->scf_trans); 390 scf_transaction_destroy(handle->scf_trans); 391 handle->scf_trans = NULL; 392 393 return (0); 394 } 395 396 /* 397 * Deletes property in current pg 398 */ 399 static int 400 ndmp_smf_delete_property(ndmp_scfhandle_t *handle, char *propname) 401 { 402 scf_transaction_entry_t *entry = NULL; 403 404 /* 405 * Properties must be set in transactions and don't take effect until 406 * the transaction has been ended/committed. 407 */ 408 if ((entry = scf_entry_create(handle->scf_handle)) != NULL) { 409 if (scf_transaction_property_delete(handle->scf_trans, entry, 410 propname) != 0) { 411 scf_entry_destroy(entry); 412 ndmp_errno = ENDMP_SMF_INTERNAL; 413 return (-1); 414 } 415 } else { 416 ndmp_errno = ENDMP_SMF_INTERNAL; 417 return (-1); 418 } 419 if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) { 420 ndmp_errno = ENDMP_SMF_PERM; 421 scf_entry_destroy(entry); 422 return (-1); 423 } 424 425 return (0); 426 } 427 428 /* 429 * Sets property in current pg 430 */ 431 static int 432 ndmp_smf_set_property(ndmp_scfhandle_t *handle, 433 char *propname, char *valstr) 434 { 435 int ret = 0; 436 scf_value_t *value = NULL; 437 scf_transaction_entry_t *entry = NULL; 438 scf_property_t *prop; 439 scf_type_t type; 440 int64_t valint; 441 uint8_t valbool; 442 443 /* 444 * Properties must be set in transactions and don't take effect until 445 * the transaction has been ended/committed. 446 */ 447 if (((value = scf_value_create(handle->scf_handle)) != NULL) && 448 (entry = scf_entry_create(handle->scf_handle)) != NULL) { 449 if (((prop = 450 scf_property_create(handle->scf_handle)) != NULL) && 451 ((scf_pg_get_property(handle->scf_pg, propname, 452 prop)) == 0)) { 453 if (scf_property_get_value(prop, value) == 0) { 454 type = scf_value_type(value); 455 if ((scf_transaction_property_change( 456 handle->scf_trans, entry, propname, 457 type) == 0) || 458 (scf_transaction_property_new( 459 handle->scf_trans, entry, propname, 460 type) == 0)) { 461 switch (type) { 462 case SCF_TYPE_ASTRING: 463 if ((scf_value_set_astring( 464 value, 465 valstr)) != SCF_SUCCESS) 466 ret = -1; 467 break; 468 case SCF_TYPE_INTEGER: 469 valint = strtoll(valstr, 0, 0); 470 scf_value_set_integer(value, 471 valint); 472 break; 473 case SCF_TYPE_BOOLEAN: 474 if (strncmp(valstr, "yes", 3)) 475 valbool = 0; 476 else 477 valbool = 1; 478 scf_value_set_boolean(value, 479 valbool); 480 break; 481 default: 482 ret = -1; 483 } 484 if (scf_entry_add_value(entry, 485 value) != 0) { 486 ret = -1; 487 scf_value_destroy(value); 488 } 489 /* The value is in the transaction */ 490 value = NULL; 491 } 492 /* The entry is in the transaction */ 493 entry = NULL; 494 } else { 495 ret = -1; 496 } 497 } else { 498 ret = -1; 499 } 500 } else { 501 ret = -1; 502 } 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_value_destroy(value); 510 scf_entry_destroy(entry); 511 return (ret); 512 } 513 514 /* 515 * Gets a property value.upto sz size. Caller is responsible to have enough 516 * memory allocated. 517 */ 518 static int 519 ndmp_smf_get_property(ndmp_scfhandle_t *handle, char *propname, 520 char *valstr, size_t sz) 521 { 522 int ret = 0; 523 scf_value_t *value; 524 scf_property_t *prop; 525 scf_type_t type; 526 int64_t valint; 527 uint8_t valbool; 528 char valstrbuf[NDMP_PROP_LEN]; 529 530 if (((value = scf_value_create(handle->scf_handle)) != NULL) && 531 ((prop = scf_property_create(handle->scf_handle)) != NULL) && 532 (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { 533 if (scf_property_get_value(prop, value) == 0) { 534 type = scf_value_type(value); 535 switch (type) { 536 case SCF_TYPE_ASTRING: 537 if (scf_value_get_astring(value, valstr, 538 sz) < 0) { 539 ret = -1; 540 } 541 break; 542 case SCF_TYPE_INTEGER: 543 if (scf_value_get_integer(value, 544 &valint) != 0) { 545 ret = -1; 546 break; 547 } 548 valstrbuf[NDMP_PROP_LEN - 1] = '\0'; 549 (void) strncpy(valstr, lltostr(valint, 550 &valstrbuf[NDMP_PROP_LEN - 1]), 551 NDMP_PROP_LEN); 552 break; 553 case SCF_TYPE_BOOLEAN: 554 if (scf_value_get_boolean(value, 555 &valbool) != 0) { 556 ret = -1; 557 break; 558 } 559 if (valbool == 1) 560 (void) strncpy(valstr, "yes", 4); 561 else 562 (void) strncpy(valstr, "no", 3); 563 break; 564 default: 565 ret = -1; 566 } 567 } else { 568 ret = -1; 569 } 570 } else { 571 ret = -1; 572 } 573 scf_value_destroy(value); 574 scf_property_destroy(prop); 575 return (ret); 576 } 577