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