1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Share control API 31 */ 32 #include <stdio.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <unistd.h> 39 #include <libxml/parser.h> 40 #include <libxml/tree.h> 41 #include "libshare.h" 42 #include "libshare_impl.h" 43 #include <libscf.h> 44 #include "scfutil.h" 45 #include <ctype.h> 46 #include <libintl.h> 47 #include <thread.h> 48 #include <synch.h> 49 50 #if _NOT_SMF 51 #define CONFIG_FILE "/var/tmp/share.cfg" 52 #define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp" 53 #endif 54 #define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \ 55 (tm.tv_nsec & 0xffffffff)) 56 57 #define DFS_LOCK_FILE "/etc/dfs/fstypes" 58 #define SA_STRSIZE 256 /* max string size for names */ 59 60 /* 61 * internal data structures 62 */ 63 64 extern struct sa_proto_plugin *sap_proto_list; 65 66 /* current SMF/SVC repository handle */ 67 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *); 68 extern int gettransients(sa_handle_impl_t, xmlNodePtr *); 69 extern int sa_valid_property(void *, char *, sa_property_t); 70 extern char *sa_fstype(char *); 71 extern int sa_is_share(void *); 72 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ 73 extern int sa_group_is_zfs(sa_group_t); 74 extern int sa_path_is_zfs(char *); 75 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); 76 extern void update_legacy_config(sa_handle_t); 77 extern int issubdir(char *, char *); 78 extern int sa_zfs_init(sa_handle_impl_t); 79 extern void sa_zfs_fini(sa_handle_impl_t); 80 extern void sablocksigs(sigset_t *); 81 extern void saunblocksigs(sigset_t *); 82 83 /* 84 * Data structures for finding/managing the document root to access 85 * handle mapping. The list isn't expected to grow very large so a 86 * simple list is acceptable. The purpose is to provide a way to start 87 * with a group or share and find the library handle needed for 88 * various operations. 89 */ 90 mutex_t sa_global_lock; 91 struct doc2handle { 92 struct doc2handle *next; 93 xmlNodePtr root; 94 sa_handle_impl_t handle; 95 }; 96 97 /* definitions used in a couple of property functions */ 98 #define SA_PROP_OP_REMOVE 1 99 #define SA_PROP_OP_ADD 2 100 #define SA_PROP_OP_UPDATE 3 101 102 static struct doc2handle *sa_global_handles = NULL; 103 104 /* helper functions */ 105 106 /* 107 * sa_errorstr(err) 108 * 109 * convert an error value to an error string 110 */ 111 112 char * 113 sa_errorstr(int err) 114 { 115 static char errstr[32]; 116 char *ret = NULL; 117 118 switch (err) { 119 case SA_OK: 120 ret = dgettext(TEXT_DOMAIN, "ok"); 121 break; 122 case SA_NO_SUCH_PATH: 123 ret = dgettext(TEXT_DOMAIN, "path doesn't exist"); 124 break; 125 case SA_NO_MEMORY: 126 ret = dgettext(TEXT_DOMAIN, "no memory"); 127 break; 128 case SA_DUPLICATE_NAME: 129 ret = dgettext(TEXT_DOMAIN, "name in use"); 130 break; 131 case SA_BAD_PATH: 132 ret = dgettext(TEXT_DOMAIN, "bad path"); 133 break; 134 case SA_NO_SUCH_GROUP: 135 ret = dgettext(TEXT_DOMAIN, "no such group"); 136 break; 137 case SA_CONFIG_ERR: 138 ret = dgettext(TEXT_DOMAIN, "configuration error"); 139 break; 140 case SA_SYSTEM_ERR: 141 ret = dgettext(TEXT_DOMAIN, "system error"); 142 break; 143 case SA_SYNTAX_ERR: 144 ret = dgettext(TEXT_DOMAIN, "syntax error"); 145 break; 146 case SA_NO_PERMISSION: 147 ret = dgettext(TEXT_DOMAIN, "no permission"); 148 break; 149 case SA_BUSY: 150 ret = dgettext(TEXT_DOMAIN, "busy"); 151 break; 152 case SA_NO_SUCH_PROP: 153 ret = dgettext(TEXT_DOMAIN, "no such property"); 154 break; 155 case SA_INVALID_NAME: 156 ret = dgettext(TEXT_DOMAIN, "invalid name"); 157 break; 158 case SA_INVALID_PROTOCOL: 159 ret = dgettext(TEXT_DOMAIN, "invalid protocol"); 160 break; 161 case SA_NOT_ALLOWED: 162 ret = dgettext(TEXT_DOMAIN, "operation not allowed"); 163 break; 164 case SA_BAD_VALUE: 165 ret = dgettext(TEXT_DOMAIN, "bad property value"); 166 break; 167 case SA_INVALID_SECURITY: 168 ret = dgettext(TEXT_DOMAIN, "invalid security type"); 169 break; 170 case SA_NO_SUCH_SECURITY: 171 ret = dgettext(TEXT_DOMAIN, "security type not found"); 172 break; 173 case SA_VALUE_CONFLICT: 174 ret = dgettext(TEXT_DOMAIN, "property value conflict"); 175 break; 176 case SA_NOT_IMPLEMENTED: 177 ret = dgettext(TEXT_DOMAIN, "not implemented"); 178 break; 179 case SA_INVALID_PATH: 180 ret = dgettext(TEXT_DOMAIN, "invalid path"); 181 break; 182 case SA_NOT_SUPPORTED: 183 ret = dgettext(TEXT_DOMAIN, "operation not supported"); 184 break; 185 case SA_PROP_SHARE_ONLY: 186 ret = dgettext(TEXT_DOMAIN, "property not valid for group"); 187 break; 188 case SA_NOT_SHARED: 189 ret = dgettext(TEXT_DOMAIN, "not shared"); 190 break; 191 default: 192 (void) snprintf(errstr, sizeof (errstr), 193 dgettext(TEXT_DOMAIN, "unknown %d"), err); 194 ret = errstr; 195 } 196 return (ret); 197 } 198 199 /* 200 * Document root to active handle mapping functions. These are only 201 * used internally. A mutex is used to prevent access while the list 202 * is changing. In general, the list will be relatively short - one 203 * item per thread that has called sa_init(). 204 */ 205 206 sa_handle_impl_t 207 get_handle_for_root(xmlNodePtr root) 208 { 209 struct doc2handle *item; 210 211 (void) mutex_lock(&sa_global_lock); 212 for (item = sa_global_handles; item != NULL; item = item->next) { 213 if (item->root == root) 214 break; 215 } 216 (void) mutex_unlock(&sa_global_lock); 217 if (item != NULL) 218 return (item->handle); 219 return (NULL); 220 } 221 222 static int 223 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle) 224 { 225 struct doc2handle *item; 226 int ret = SA_NO_MEMORY; 227 228 item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1); 229 if (item != NULL) { 230 item->root = root; 231 item->handle = handle; 232 (void) mutex_lock(&sa_global_lock); 233 item->next = sa_global_handles; 234 sa_global_handles = item; 235 (void) mutex_unlock(&sa_global_lock); 236 ret = SA_OK; 237 } 238 return (ret); 239 } 240 241 /* 242 * remove_handle_for_root(root) 243 * 244 * Walks the list of handles and removes the one for this "root" from 245 * the list. It is up to the caller to free the data. 246 */ 247 248 static void 249 remove_handle_for_root(xmlNodePtr root) 250 { 251 struct doc2handle *item, *prev; 252 253 (void) mutex_lock(&sa_global_lock); 254 for (prev = NULL, item = sa_global_handles; item != NULL; 255 item = item->next) { 256 if (item->root == root) { 257 /* first in the list */ 258 if (prev == NULL) 259 sa_global_handles = sa_global_handles->next; 260 else 261 prev->next = item->next; 262 /* Item is out of the list so free the list structure */ 263 free(item); 264 break; 265 } 266 prev = item; 267 } 268 (void) mutex_unlock(&sa_global_lock); 269 } 270 271 /* 272 * sa_find_group_handle(sa_group_t group) 273 * 274 * Find the sa_handle_t for the configuration associated with this 275 * group. 276 */ 277 sa_handle_t 278 sa_find_group_handle(sa_group_t group) 279 { 280 xmlNodePtr node = (xmlNodePtr)group; 281 sa_handle_t handle; 282 283 while (node != NULL) { 284 if (strcmp((char *)(node->name), "sharecfg") == 0) { 285 /* have the root so get the handle */ 286 handle = (sa_handle_t)get_handle_for_root(node); 287 return (handle); 288 } 289 node = node->parent; 290 } 291 return (NULL); 292 } 293 294 /* 295 * set_legacy_timestamp(root, path, timevalue) 296 * 297 * add the current timestamp value to the configuration for use in 298 * determining when to update the legacy files. For SMF, this 299 * property is kept in default/operation/legacy_timestamp 300 */ 301 302 static void 303 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval) 304 { 305 xmlNodePtr node; 306 xmlChar *lpath = NULL; 307 sa_handle_impl_t handle; 308 309 /* Have to have a handle or else we weren't initialized. */ 310 handle = get_handle_for_root(root); 311 if (handle == NULL) 312 return; 313 314 for (node = root->xmlChildrenNode; node != NULL; 315 node = node->next) { 316 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 317 /* a possible legacy node for this path */ 318 lpath = xmlGetProp(node, (xmlChar *)"path"); 319 if (lpath != NULL && 320 xmlStrcmp(lpath, (xmlChar *)path) == 0) { 321 xmlFree(lpath); 322 break; 323 } 324 if (lpath != NULL) 325 xmlFree(lpath); 326 } 327 } 328 if (node == NULL) { 329 /* need to create the first legacy timestamp node */ 330 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL); 331 } 332 if (node != NULL) { 333 char tstring[32]; 334 int ret; 335 336 (void) snprintf(tstring, sizeof (tstring), "%lld", tval); 337 xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring); 338 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path); 339 /* now commit to SMF */ 340 ret = sa_get_instance(handle->scfhandle, "default"); 341 if (ret == SA_OK) { 342 ret = sa_start_transaction(handle->scfhandle, 343 "operation"); 344 if (ret == SA_OK) { 345 ret = sa_set_property(handle->scfhandle, 346 "legacy-timestamp", tstring); 347 if (ret == SA_OK) { 348 (void) sa_end_transaction( 349 handle->scfhandle); 350 } else { 351 sa_abort_transaction(handle->scfhandle); 352 } 353 } 354 } 355 } 356 } 357 358 /* 359 * is_shared(share) 360 * 361 * determine if the specified share is currently shared or not. 362 */ 363 static int 364 is_shared(sa_share_t share) 365 { 366 char *shared; 367 int result = 0; /* assume not */ 368 369 shared = sa_get_share_attr(share, "shared"); 370 if (shared != NULL) { 371 if (strcmp(shared, "true") == 0) 372 result = 1; 373 sa_free_attr_string(shared); 374 } 375 return (result); 376 } 377 378 /* 379 * checksubdirgroup(group, newpath, strictness) 380 * 381 * check all the specified newpath against all the paths in the 382 * group. This is a helper function for checksubdir to make it easier 383 * to also check ZFS subgroups. 384 * The strictness values mean: 385 * SA_CHECK_NORMAL == only check newpath against shares that are active 386 * SA_CHECK_STRICT == check newpath against both active shares and those 387 * stored in the repository 388 */ 389 static int 390 checksubdirgroup(sa_group_t group, char *newpath, int strictness) 391 { 392 sa_share_t share; 393 char *path; 394 int issub = SA_OK; 395 396 for (share = sa_get_share(group, NULL); share != NULL; 397 share = sa_get_next_share(share)) { 398 /* 399 * The original behavior of share never checked 400 * against the permanent configuration 401 * (/etc/dfs/dfstab). PIT has a number of cases where 402 * it depends on this older behavior even though it 403 * could be considered incorrect. We may tighten this 404 * up in the future. 405 */ 406 if (strictness == SA_CHECK_NORMAL && !is_shared(share)) 407 continue; 408 409 path = sa_get_share_attr(share, "path"); 410 /* 411 * If path is NULL, then a share is in the process of 412 * construction or someone has modified the property 413 * group inappropriately. It should be 414 * ignored. issubdir() comes from the original share 415 * implementation and does the difficult part of 416 * checking subdirectories. 417 */ 418 if (path == NULL) 419 continue; 420 if (newpath != NULL && 421 (strcmp(path, newpath) == 0 || issubdir(newpath, path) || 422 issubdir(path, newpath))) { 423 sa_free_attr_string(path); 424 path = NULL; 425 issub = SA_INVALID_PATH; 426 break; 427 } 428 sa_free_attr_string(path); 429 path = NULL; 430 } 431 return (issub); 432 } 433 434 /* 435 * checksubdir(newpath, strictness) 436 * 437 * checksubdir determines if the specified path (newpath) is a 438 * subdirectory of another share. It calls checksubdirgroup() to do 439 * the complicated work. The strictness parameter determines how 440 * strict a check to make against the path. The strictness values 441 * mean: SA_CHECK_NORMAL == only check newpath against shares that are 442 * active SA_CHECK_STRICT == check newpath against both active shares 443 * and those * stored in the repository 444 */ 445 static int 446 checksubdir(sa_handle_t handle, char *newpath, int strictness) 447 { 448 sa_group_t group; 449 int issub; 450 char *path = NULL; 451 452 for (issub = 0, group = sa_get_group(handle, NULL); 453 group != NULL && !issub; group = sa_get_next_group(group)) { 454 if (sa_group_is_zfs(group)) { 455 sa_group_t subgroup; 456 for (subgroup = sa_get_sub_group(group); 457 subgroup != NULL && !issub; 458 subgroup = sa_get_next_group(subgroup)) 459 issub = checksubdirgroup(subgroup, newpath, 460 strictness); 461 } else { 462 issub = checksubdirgroup(group, newpath, strictness); 463 } 464 } 465 if (path != NULL) 466 sa_free_attr_string(path); 467 return (issub); 468 } 469 470 /* 471 * validpath(path, strictness) 472 * determine if the provided path is valid for a share. It shouldn't 473 * be a sub-dir of an already shared path or the parent directory of a 474 * share path. 475 */ 476 static int 477 validpath(sa_handle_t handle, char *path, int strictness) 478 { 479 int error = SA_OK; 480 struct stat st; 481 sa_share_t share; 482 char *fstype; 483 484 if (*path != '/') 485 return (SA_BAD_PATH); 486 487 if (stat(path, &st) < 0) { 488 error = SA_NO_SUCH_PATH; 489 } else { 490 share = sa_find_share(handle, path); 491 if (share != NULL) 492 error = SA_DUPLICATE_NAME; 493 494 if (error == SA_OK) { 495 /* 496 * check for special case with file system 497 * that might have restrictions. For now, ZFS 498 * is the only case since it has its own idea 499 * of how to configure shares. We do this 500 * before subdir checking since things like 501 * ZFS will do that for us. This should also 502 * be done via plugin interface. 503 */ 504 fstype = sa_fstype(path); 505 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 506 if (sa_zfs_is_shared(handle, path)) 507 error = SA_INVALID_NAME; 508 } 509 if (fstype != NULL) 510 sa_free_fstype(fstype); 511 } 512 if (error == SA_OK) 513 error = checksubdir(handle, path, strictness); 514 } 515 return (error); 516 } 517 518 /* 519 * check to see if group/share is persistent. 520 */ 521 static int 522 is_persistent(sa_group_t group) 523 { 524 char *type; 525 int persist = 1; 526 527 type = sa_get_group_attr(group, "type"); 528 if (type != NULL && strcmp(type, "transient") == 0) 529 persist = 0; 530 if (type != NULL) 531 sa_free_attr_string(type); 532 return (persist); 533 } 534 535 /* 536 * sa_valid_group_name(name) 537 * 538 * check that the "name" contains only valid characters and otherwise 539 * fits the required naming conventions. Valid names must start with 540 * an alphabetic and the remainder may consist of only alphanumeric 541 * plus the '-' and '_' characters. This name limitation comes from 542 * inherent limitations in SMF. 543 */ 544 545 int 546 sa_valid_group_name(char *name) 547 { 548 int ret = 1; 549 ssize_t len; 550 551 if (name != NULL && isalpha(*name)) { 552 char c; 553 len = strlen(name); 554 if (len < (scf_max_name_len - sizeof ("group:"))) { 555 for (c = *name++; c != '\0' && ret != 0; c = *name++) { 556 if (!isalnum(c) && c != '-' && c != '_') 557 ret = 0; 558 } 559 } else { 560 ret = 0; 561 } 562 } else { 563 ret = 0; 564 } 565 return (ret); 566 } 567 568 569 /* 570 * is_zfs_group(group) 571 * Determine if the specified group is a ZFS sharenfs group 572 */ 573 static int 574 is_zfs_group(sa_group_t group) 575 { 576 int ret = 0; 577 xmlNodePtr parent; 578 xmlChar *zfs; 579 580 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) 581 parent = (xmlNodePtr)sa_get_parent_group(group); 582 else 583 parent = (xmlNodePtr)group; 584 zfs = xmlGetProp(parent, (xmlChar *)"zfs"); 585 if (zfs != NULL) { 586 xmlFree(zfs); 587 ret = 1; 588 } 589 return (ret); 590 } 591 592 /* 593 * sa_optionset_name(optionset, oname, len, id) 594 * return the SMF name for the optionset. If id is not NULL, it 595 * will have the GUID value for a share and should be used 596 * instead of the keyword "optionset" which is used for 597 * groups. If the optionset doesn't have a protocol type 598 * associated with it, "default" is used. This shouldn't happen 599 * at this point but may be desirable in the future if there are 600 * protocol independent properties added. The name is returned in 601 * oname. 602 */ 603 604 static int 605 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) 606 { 607 char *proto; 608 609 if (id == NULL) 610 id = "optionset"; 611 612 proto = sa_get_optionset_attr(optionset, "type"); 613 len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default"); 614 615 if (proto != NULL) 616 sa_free_attr_string(proto); 617 return (len); 618 } 619 620 /* 621 * sa_security_name(optionset, oname, len, id) 622 * 623 * return the SMF name for the security. If id is not NULL, it will 624 * have the GUID value for a share and should be used instead of the 625 * keyword "optionset" which is used for groups. If the optionset 626 * doesn't have a protocol type associated with it, "default" is 627 * used. This shouldn't happen at this point but may be desirable in 628 * the future if there are protocol independent properties added. The 629 * name is returned in oname. The security type is also encoded into 630 * the name. In the future, this wil *be handled a bit differently. 631 */ 632 633 static int 634 sa_security_name(sa_security_t security, char *oname, size_t len, char *id) 635 { 636 char *proto; 637 char *sectype; 638 639 if (id == NULL) 640 id = "optionset"; 641 642 proto = sa_get_security_attr(security, "type"); 643 sectype = sa_get_security_attr(security, "sectype"); 644 len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default", 645 sectype ? sectype : "default"); 646 if (proto != NULL) 647 sa_free_attr_string(proto); 648 if (sectype != NULL) 649 sa_free_attr_string(sectype); 650 return (len); 651 } 652 653 /* 654 * verifydefgroupopts(handle) 655 * 656 * Make sure a "default" group exists and has default protocols enabled. 657 */ 658 static void 659 verifydefgroupopts(sa_handle_t handle) 660 { 661 sa_group_t defgrp; 662 sa_optionset_t opt; 663 defgrp = sa_get_group(handle, "default"); 664 if (defgrp != NULL) { 665 opt = sa_get_optionset(defgrp, NULL); 666 /* 667 * NFS is the default for default group 668 */ 669 if (opt == NULL) 670 opt = sa_create_optionset(defgrp, "nfs"); 671 } 672 } 673 674 /* 675 * sa_init(init_service) 676 * Initialize the API 677 * find all the shared objects 678 * init the tables with all objects 679 * read in the current configuration 680 */ 681 682 #define GETPROP(prop) scf_simple_prop_next_astring(prop) 683 #define CHECKTSTAMP(st, tval) stat(SA_LEGACY_DFSTAB, &st) >= 0 && \ 684 tval != TSTAMP(st.st_ctim) 685 686 sa_handle_t 687 sa_init(int init_service) 688 { 689 struct stat st; 690 int legacy = 0; 691 uint64_t tval = 0; 692 int lockfd; 693 sigset_t old; 694 int updatelegacy = B_FALSE; 695 scf_simple_prop_t *prop; 696 sa_handle_impl_t handle; 697 int err; 698 699 handle = calloc(sizeof (struct sa_handle_impl), 1); 700 701 if (handle != NULL) { 702 /* get protocol specific structures */ 703 (void) proto_plugin_init(); 704 if (init_service & SA_INIT_SHARE_API) { 705 /* 706 * initialize access into libzfs. We use this 707 * when collecting info about ZFS datasets and 708 * shares. 709 */ 710 if (sa_zfs_init(handle) == B_FALSE) { 711 free(handle); 712 (void) proto_plugin_fini(); 713 return (NULL); 714 } 715 /* 716 * since we want to use SMF, initialize an svc handle 717 * and find out what is there. 718 */ 719 handle->scfhandle = sa_scf_init(handle); 720 if (handle->scfhandle != NULL) { 721 /* 722 * Need to lock the extraction of the 723 * configuration if the dfstab file has 724 * changed. Lock everything now and release if 725 * not needed. Use a file that isn't being 726 * manipulated by other parts of the system in 727 * order to not interfere with locking. Using 728 * dfstab doesn't work. 729 */ 730 sablocksigs(&old); 731 lockfd = open(DFS_LOCK_FILE, O_RDWR); 732 if (lockfd >= 0) { 733 extern int errno; 734 errno = 0; 735 (void) lockf(lockfd, F_LOCK, 0); 736 /* 737 * Check whether we are going to need 738 * to merge any dfstab changes. This 739 * is done by comparing the value of 740 * legacy-timestamp with the current 741 * st_ctim of the file. If they are 742 * different, an update is needed and 743 * the file must remain locked until 744 * the merge is done in order to 745 * prevent multiple startups from 746 * changing the SMF repository at the 747 * same time. The first to get the 748 * lock will make any changes before 749 * the others can read the repository. 750 */ 751 prop = scf_simple_prop_get 752 (handle->scfhandle->handle, 753 (const char *)SA_SVC_FMRI_BASE 754 ":default", "operation", 755 "legacy-timestamp"); 756 if (prop != NULL) { 757 char *i64; 758 i64 = GETPROP(prop); 759 if (i64 != NULL) 760 tval = strtoull(i64, 761 NULL, 0); 762 if (CHECKTSTAMP(st, tval)) 763 updatelegacy = B_TRUE; 764 scf_simple_prop_free(prop); 765 } else { 766 /* 767 * We haven't set the 768 * timestamp before so do it. 769 */ 770 updatelegacy = B_TRUE; 771 } 772 } 773 if (updatelegacy == B_FALSE) { 774 /* Don't need the lock anymore */ 775 (void) lockf(lockfd, F_ULOCK, 0); 776 (void) close(lockfd); 777 } 778 779 /* 780 * It is essential that the document tree and 781 * the internal list of roots to handles be 782 * setup before anything that might try to 783 * create a new object is called. The document 784 * tree is the combination of handle->doc and 785 * handle->tree. This allows searches, 786 * etc. when all you have is an object in the 787 * tree. 788 */ 789 handle->doc = xmlNewDoc((xmlChar *)"1.0"); 790 handle->tree = xmlNewNode(NULL, 791 (xmlChar *)"sharecfg"); 792 if (handle->doc != NULL && 793 handle->tree != NULL) { 794 xmlDocSetRootElement(handle->doc, 795 handle->tree); 796 err = add_handle_for_root(handle->tree, 797 handle); 798 if (err == SA_OK) 799 err = sa_get_config( 800 handle->scfhandle, 801 handle->tree, handle); 802 } else { 803 if (handle->doc != NULL) 804 xmlFreeDoc(handle->doc); 805 if (handle->tree != NULL) 806 xmlFreeNode(handle->tree); 807 err = SA_NO_MEMORY; 808 } 809 810 saunblocksigs(&old); 811 812 if (err != SA_OK) { 813 /* 814 * If we couldn't add the tree handle 815 * to the list, then things are going 816 * to fail badly. Might as well undo 817 * everything now and fail the 818 * sa_init(). 819 */ 820 sa_fini(handle); 821 return (NULL); 822 } 823 824 if (tval == 0) { 825 /* 826 * first time so make sure 827 * default is setup 828 */ 829 verifydefgroupopts(handle); 830 } 831 832 if (updatelegacy == B_TRUE) { 833 sablocksigs(&old); 834 getlegacyconfig((sa_handle_t)handle, 835 SA_LEGACY_DFSTAB, &handle->tree); 836 if (stat(SA_LEGACY_DFSTAB, &st) >= 0) 837 set_legacy_timestamp(handle->tree, 838 SA_LEGACY_DFSTAB, 839 TSTAMP(st.st_ctim)); 840 saunblocksigs(&old); 841 /* Safe to unlock now to allow others to run */ 842 (void) lockf(lockfd, F_ULOCK, 0); 843 (void) close(lockfd); 844 } 845 legacy |= sa_get_zfs_shares(handle, "zfs"); 846 legacy |= gettransients(handle, &handle->tree); 847 } 848 } 849 } 850 return ((sa_handle_t)handle); 851 } 852 853 /* 854 * sa_fini(handle) 855 * Uninitialize the API structures including the configuration 856 * data structures and ZFS related data. 857 */ 858 859 void 860 sa_fini(sa_handle_t handle) 861 { 862 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 863 864 if (impl_handle != NULL) { 865 /* 866 * Free the config trees and any other data structures 867 * used in the handle. 868 */ 869 if (impl_handle->doc != NULL) 870 xmlFreeDoc(impl_handle->doc); 871 sa_scf_fini(impl_handle->scfhandle); 872 sa_zfs_fini(impl_handle); 873 874 /* Remove and free the entry in the global list. */ 875 remove_handle_for_root(impl_handle->tree); 876 877 /* Make sure we free the handle */ 878 free(impl_handle); 879 880 /* 881 * If this was the last handle to release, unload the 882 * plugins that were loaded. 883 */ 884 if (sa_global_handles == NULL) 885 (void) proto_plugin_fini(); 886 887 } 888 } 889 890 /* 891 * sa_get_protocols(char **protocol) 892 * Get array of protocols that are supported 893 * Returns pointer to an allocated and NULL terminated 894 * array of strings. Caller must free. 895 * This really should be determined dynamically. 896 * If there aren't any defined, return -1. 897 * Use free() to return memory. 898 */ 899 900 int 901 sa_get_protocols(char ***protocols) 902 { 903 int numproto = -1; 904 905 if (protocols != NULL) { 906 struct sa_proto_plugin *plug; 907 for (numproto = 0, plug = sap_proto_list; plug != NULL; 908 plug = plug->plugin_next) { 909 numproto++; 910 } 911 912 *protocols = calloc(numproto + 1, sizeof (char *)); 913 if (*protocols != NULL) { 914 int ret = 0; 915 for (plug = sap_proto_list; plug != NULL; 916 plug = plug->plugin_next) { 917 /* faking for now */ 918 (*protocols)[ret++] = 919 plug->plugin_ops->sa_protocol; 920 } 921 } else { 922 numproto = -1; 923 } 924 } 925 return (numproto); 926 } 927 928 /* 929 * find_group_by_name(node, group) 930 * 931 * search the XML document subtree specified by node to find the group 932 * specified by group. Searching subtree allows subgroups to be 933 * searched for. 934 */ 935 936 static xmlNodePtr 937 find_group_by_name(xmlNodePtr node, xmlChar *group) 938 { 939 xmlChar *name = NULL; 940 941 for (node = node->xmlChildrenNode; node != NULL; 942 node = node->next) { 943 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 944 /* if no groupname, return the first found */ 945 if (group == NULL) 946 break; 947 name = xmlGetProp(node, (xmlChar *)"name"); 948 if (name != NULL && xmlStrcmp(name, group) == 0) 949 break; 950 if (name != NULL) { 951 xmlFree(name); 952 name = NULL; 953 } 954 } 955 } 956 if (name != NULL) 957 xmlFree(name); 958 return (node); 959 } 960 961 /* 962 * sa_get_group(groupname) 963 * Return the "group" specified. If groupname is NULL, 964 * return the first group of the list of groups. 965 */ 966 sa_group_t 967 sa_get_group(sa_handle_t handle, char *groupname) 968 { 969 xmlNodePtr node = NULL; 970 char *subgroup = NULL; 971 char *group = NULL; 972 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 973 974 if (impl_handle != NULL && impl_handle->tree != NULL) { 975 if (groupname != NULL) { 976 group = strdup(groupname); 977 if (group != NULL) { 978 subgroup = strchr(group, '/'); 979 if (subgroup != NULL) 980 *subgroup++ = '\0'; 981 } 982 } 983 /* 984 * We want to find the, possibly, named group. If 985 * group is not NULL, then lookup the name. If it is 986 * NULL, we only do the find if groupname is also 987 * NULL. This allows lookup of the "first" group in 988 * the internal list. 989 */ 990 if (group != NULL || groupname == NULL) 991 node = find_group_by_name(impl_handle->tree, 992 (xmlChar *)group); 993 994 /* if a subgroup, find it before returning */ 995 if (subgroup != NULL && node != NULL) 996 node = find_group_by_name(node, (xmlChar *)subgroup); 997 } 998 if (node != NULL && (char *)group != NULL) 999 (void) sa_get_instance(impl_handle->scfhandle, (char *)group); 1000 if (group != NULL) 1001 free(group); 1002 return ((sa_group_t)(node)); 1003 } 1004 1005 /* 1006 * sa_get_next_group(group) 1007 * Return the "next" group after the specified group from 1008 * the internal group list. NULL if there are no more. 1009 */ 1010 sa_group_t 1011 sa_get_next_group(sa_group_t group) 1012 { 1013 xmlNodePtr ngroup = NULL; 1014 if (group != NULL) { 1015 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 1016 ngroup = ngroup->next) { 1017 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 1018 break; 1019 } 1020 } 1021 return ((sa_group_t)ngroup); 1022 } 1023 1024 /* 1025 * sa_get_share(group, sharepath) 1026 * Return the share object for the share specified. The share 1027 * must be in the specified group. Return NULL if not found. 1028 */ 1029 sa_share_t 1030 sa_get_share(sa_group_t group, char *sharepath) 1031 { 1032 xmlNodePtr node = NULL; 1033 xmlChar *path; 1034 1035 /* 1036 * For future scalability, this should end up building a cache 1037 * since it will get called regularly by the mountd and info 1038 * services. 1039 */ 1040 if (group != NULL) { 1041 for (node = ((xmlNodePtr)group)->children; node != NULL; 1042 node = node->next) { 1043 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1044 if (sharepath == NULL) { 1045 break; 1046 } else { 1047 /* is it the correct share? */ 1048 path = xmlGetProp(node, 1049 (xmlChar *)"path"); 1050 if (path != NULL && 1051 xmlStrcmp(path, 1052 (xmlChar *)sharepath) == 0) { 1053 xmlFree(path); 1054 break; 1055 } 1056 xmlFree(path); 1057 } 1058 } 1059 } 1060 } 1061 return ((sa_share_t)node); 1062 } 1063 1064 /* 1065 * sa_get_next_share(share) 1066 * Return the next share following the specified share 1067 * from the internal list of shares. Returns NULL if there 1068 * are no more shares. The list is relative to the same 1069 * group. 1070 */ 1071 sa_share_t 1072 sa_get_next_share(sa_share_t share) 1073 { 1074 xmlNodePtr node = NULL; 1075 1076 if (share != NULL) { 1077 for (node = ((xmlNodePtr)share)->next; node != NULL; 1078 node = node->next) { 1079 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1080 break; 1081 } 1082 } 1083 } 1084 return ((sa_share_t)node); 1085 } 1086 1087 /* 1088 * _sa_get_child_node(node, type) 1089 * 1090 * find the child node of the specified node that has "type". This is 1091 * used to implement several internal functions. 1092 */ 1093 1094 static xmlNodePtr 1095 _sa_get_child_node(xmlNodePtr node, xmlChar *type) 1096 { 1097 xmlNodePtr child; 1098 for (child = node->xmlChildrenNode; child != NULL; 1099 child = child->next) 1100 if (xmlStrcmp(child->name, type) == 0) 1101 return (child); 1102 return ((xmlNodePtr)NULL); 1103 } 1104 1105 /* 1106 * find_share(group, path) 1107 * 1108 * Search all the shares in the specified group for one that has the 1109 * specified path. 1110 */ 1111 1112 static sa_share_t 1113 find_share(sa_group_t group, char *sharepath) 1114 { 1115 sa_share_t share; 1116 char *path; 1117 1118 for (share = sa_get_share(group, NULL); share != NULL; 1119 share = sa_get_next_share(share)) { 1120 path = sa_get_share_attr(share, "path"); 1121 if (path != NULL && strcmp(path, sharepath) == 0) { 1122 sa_free_attr_string(path); 1123 break; 1124 } 1125 if (path != NULL) 1126 sa_free_attr_string(path); 1127 } 1128 return (share); 1129 } 1130 1131 /* 1132 * sa_get_sub_group(group) 1133 * 1134 * Get the first sub-group of group. The sa_get_next_group() function 1135 * can be used to get the rest. This is currently only used for ZFS 1136 * sub-groups but could be used to implement a more general mechanism. 1137 */ 1138 1139 sa_group_t 1140 sa_get_sub_group(sa_group_t group) 1141 { 1142 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1143 (xmlChar *)"group")); 1144 } 1145 1146 /* 1147 * sa_find_share(sharepath) 1148 * Finds a share regardless of group. In the future, this 1149 * function should utilize a cache and hash table of some kind. 1150 * The current assumption is that a path will only be shared 1151 * once. In the future, this may change as implementation of 1152 * resource names comes into being. 1153 */ 1154 sa_share_t 1155 sa_find_share(sa_handle_t handle, char *sharepath) 1156 { 1157 sa_group_t group; 1158 sa_group_t zgroup; 1159 sa_share_t share = NULL; 1160 int done = 0; 1161 1162 for (group = sa_get_group(handle, NULL); group != NULL && !done; 1163 group = sa_get_next_group(group)) { 1164 if (is_zfs_group(group)) { 1165 for (zgroup = 1166 (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1167 (xmlChar *)"group"); 1168 zgroup != NULL; 1169 zgroup = sa_get_next_group(zgroup)) { 1170 share = find_share(zgroup, sharepath); 1171 if (share != NULL) 1172 break; 1173 } 1174 } else { 1175 share = find_share(group, sharepath); 1176 } 1177 if (share != NULL) 1178 break; 1179 } 1180 return (share); 1181 } 1182 1183 /* 1184 * sa_check_path(group, path, strictness) 1185 * 1186 * check that path is a valid path relative to the group. Currently, 1187 * we are ignoring the group and checking only the NFS rules. Later, 1188 * we may want to use the group to then check against the protocols 1189 * enabled on the group. The strictness values mean: 1190 * SA_CHECK_NORMAL == only check newpath against shares that are active 1191 * SA_CHECK_STRICT == check newpath against both active shares and those 1192 * stored in the repository 1193 */ 1194 1195 int 1196 sa_check_path(sa_group_t group, char *path, int strictness) 1197 { 1198 sa_handle_t handle; 1199 1200 handle = sa_find_group_handle(group); 1201 return (validpath(handle, path, strictness)); 1202 } 1203 1204 /* 1205 * _sa_add_share(group, sharepath, persist, *error) 1206 * 1207 * common code for all types of add_share. sa_add_share() is the 1208 * public API, we also need to be able to do this when parsing legacy 1209 * files and construction of the internal configuration while 1210 * extracting config info from SMF. 1211 */ 1212 1213 sa_share_t 1214 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1215 { 1216 xmlNodePtr node = NULL; 1217 int err; 1218 1219 err = SA_OK; /* assume success */ 1220 1221 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL); 1222 if (node != NULL) { 1223 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 1224 xmlSetProp(node, (xmlChar *)"type", 1225 persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); 1226 if (persist != SA_SHARE_TRANSIENT) { 1227 /* 1228 * persistent shares come in two flavors: SMF and 1229 * ZFS. Sort this one out based on target group and 1230 * path type. Currently, only NFS is supported in the 1231 * ZFS group and it is always on. 1232 */ 1233 if (sa_group_is_zfs(group) && 1234 sa_path_is_zfs(sharepath)) { 1235 err = sa_zfs_set_sharenfs(group, sharepath, 1); 1236 } else { 1237 sa_handle_impl_t impl_handle; 1238 impl_handle = 1239 (sa_handle_impl_t)sa_find_group_handle( 1240 group); 1241 if (impl_handle != NULL) { 1242 err = sa_commit_share( 1243 impl_handle->scfhandle, group, 1244 (sa_share_t)node); 1245 } else { 1246 err = SA_SYSTEM_ERR; 1247 } 1248 } 1249 } 1250 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { 1251 /* called by the dfstab parser so could be a show */ 1252 err = SA_OK; 1253 } 1254 if (err != SA_OK) { 1255 /* 1256 * we couldn't commit to the repository so undo 1257 * our internal state to reflect reality. 1258 */ 1259 xmlUnlinkNode(node); 1260 xmlFreeNode(node); 1261 node = NULL; 1262 } 1263 } else { 1264 err = SA_NO_MEMORY; 1265 } 1266 if (error != NULL) 1267 *error = err; 1268 return (node); 1269 } 1270 1271 /* 1272 * sa_add_share(group, sharepath, persist, *error) 1273 * 1274 * Add a new share object to the specified group. The share will 1275 * have the specified sharepath and will only be constructed if 1276 * it is a valid path to be shared. NULL is returned on error 1277 * and a detailed error value will be returned via the error 1278 * pointer. 1279 */ 1280 sa_share_t 1281 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1282 { 1283 xmlNodePtr node = NULL; 1284 sa_share_t dup; 1285 int strictness = SA_CHECK_NORMAL; 1286 sa_handle_t handle; 1287 1288 /* 1289 * If the share is to be permanent, use strict checking so a 1290 * bad config doesn't get created. Transient shares only need 1291 * to check against the currently active 1292 * shares. SA_SHARE_PARSER is a modifier used internally to 1293 * indicate that we are being called by the dfstab parser and 1294 * that we need strict checking in all cases. Normally persist 1295 * is in integer value but SA_SHARE_PARSER may be or'd into 1296 * it as an override. 1297 */ 1298 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 1299 strictness = SA_CHECK_STRICT; 1300 1301 handle = sa_find_group_handle(group); 1302 1303 if ((dup = sa_find_share(handle, sharepath)) == NULL && 1304 (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) { 1305 node = _sa_add_share(group, sharepath, persist, error); 1306 } 1307 if (dup != NULL) 1308 *error = SA_DUPLICATE_NAME; 1309 1310 return ((sa_share_t)node); 1311 } 1312 1313 /* 1314 * sa_enable_share(share, protocol) 1315 * Enable the specified share to the specified protocol. 1316 * If protocol is NULL, then all protocols. 1317 */ 1318 int 1319 sa_enable_share(sa_share_t share, char *protocol) 1320 { 1321 char *sharepath; 1322 struct stat st; 1323 int err = 0; 1324 1325 sharepath = sa_get_share_attr(share, "path"); 1326 if (stat(sharepath, &st) < 0) { 1327 err = SA_NO_SUCH_PATH; 1328 } else { 1329 /* tell the server about the share */ 1330 if (protocol != NULL) { 1331 /* lookup protocol specific handler */ 1332 err = sa_proto_share(protocol, share); 1333 if (err == SA_OK) 1334 (void) sa_set_share_attr(share, "shared", 1335 "true"); 1336 } else { 1337 /* 1338 * Tell all protocols. Only NFS for now but 1339 * SMB is coming. 1340 */ 1341 err = sa_proto_share("nfs", share); 1342 (void) sa_set_share_attr(share, "shared", "true"); 1343 } 1344 } 1345 if (sharepath != NULL) 1346 sa_free_attr_string(sharepath); 1347 return (err); 1348 } 1349 1350 /* 1351 * sa_disable_share(share, protocol) 1352 * Disable the specified share to the specified protocol. 1353 * If protocol is NULL, then all protocols. 1354 */ 1355 int 1356 sa_disable_share(sa_share_t share, char *protocol) 1357 { 1358 char *path; 1359 char *shared; 1360 int ret = SA_OK; 1361 1362 path = sa_get_share_attr(share, "path"); 1363 shared = sa_get_share_attr(share, "shared"); 1364 1365 if (protocol != NULL) { 1366 ret = sa_proto_unshare(protocol, path); 1367 } else { 1368 /* need to do all protocols */ 1369 ret = sa_proto_unshare("nfs", path); 1370 } 1371 if (ret == SA_OK) 1372 (void) sa_set_share_attr(share, "shared", NULL); 1373 if (path != NULL) 1374 sa_free_attr_string(path); 1375 if (shared != NULL) 1376 sa_free_attr_string(shared); 1377 return (ret); 1378 } 1379 1380 /* 1381 * sa_remove_share(share) 1382 * 1383 * remove the specified share from its containing group. 1384 * Remove from the SMF or ZFS configuration space. 1385 */ 1386 1387 int 1388 sa_remove_share(sa_share_t share) 1389 { 1390 sa_group_t group; 1391 int ret = SA_OK; 1392 char *type; 1393 int transient = 0; 1394 char *groupname; 1395 char *zfs; 1396 1397 type = sa_get_share_attr(share, "type"); 1398 group = sa_get_parent_group(share); 1399 zfs = sa_get_group_attr(group, "zfs"); 1400 groupname = sa_get_group_attr(group, "name"); 1401 if (type != NULL && strcmp(type, "persist") != 0) 1402 transient = 1; 1403 if (type != NULL) 1404 sa_free_attr_string(type); 1405 1406 /* remove the node from its group then free the memory */ 1407 1408 /* 1409 * need to test if "busy" 1410 */ 1411 /* only do SMF action if permanent */ 1412 if (!transient || zfs != NULL) { 1413 /* remove from legacy dfstab as well as possible SMF */ 1414 ret = sa_delete_legacy(share); 1415 if (ret == SA_OK) { 1416 if (!sa_group_is_zfs(group)) { 1417 sa_handle_impl_t impl_handle; 1418 impl_handle = (sa_handle_impl_t) 1419 sa_find_group_handle(group); 1420 if (impl_handle != NULL) { 1421 ret = sa_delete_share( 1422 impl_handle->scfhandle, group, 1423 share); 1424 } else { 1425 ret = SA_SYSTEM_ERR; 1426 } 1427 } else { 1428 char *sharepath = sa_get_share_attr(share, 1429 "path"); 1430 if (sharepath != NULL) { 1431 ret = sa_zfs_set_sharenfs(group, 1432 sharepath, 0); 1433 sa_free_attr_string(sharepath); 1434 } 1435 } 1436 } 1437 } 1438 if (groupname != NULL) 1439 sa_free_attr_string(groupname); 1440 if (zfs != NULL) 1441 sa_free_attr_string(zfs); 1442 1443 xmlUnlinkNode((xmlNodePtr)share); 1444 xmlFreeNode((xmlNodePtr)share); 1445 return (ret); 1446 } 1447 1448 /* 1449 * sa_move_share(group, share) 1450 * 1451 * move the specified share to the specified group. Update SMF 1452 * appropriately. 1453 */ 1454 1455 int 1456 sa_move_share(sa_group_t group, sa_share_t share) 1457 { 1458 sa_group_t oldgroup; 1459 int ret = SA_OK; 1460 1461 /* remove the node from its group then free the memory */ 1462 1463 oldgroup = sa_get_parent_group(share); 1464 if (oldgroup != group) { 1465 sa_handle_impl_t impl_handle; 1466 xmlUnlinkNode((xmlNodePtr)share); 1467 /* 1468 * now that the share isn't in its old group, add to 1469 * the new one 1470 */ 1471 xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 1472 /* need to deal with SMF */ 1473 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1474 if (impl_handle != NULL) { 1475 /* 1476 * need to remove from old group first and then add to 1477 * new group. Ideally, we would do the other order but 1478 * need to avoid having the share in two groups at the 1479 * same time. 1480 */ 1481 ret = sa_delete_share(impl_handle->scfhandle, oldgroup, 1482 share); 1483 if (ret == SA_OK) 1484 ret = sa_commit_share(impl_handle->scfhandle, 1485 group, share); 1486 } else { 1487 ret = SA_SYSTEM_ERR; 1488 } 1489 } 1490 return (ret); 1491 } 1492 1493 /* 1494 * sa_get_parent_group(share) 1495 * 1496 * Return the containg group for the share. If a group was actually 1497 * passed in, we don't want a parent so return NULL. 1498 */ 1499 1500 sa_group_t 1501 sa_get_parent_group(sa_share_t share) 1502 { 1503 xmlNodePtr node = NULL; 1504 if (share != NULL) { 1505 node = ((xmlNodePtr)share)->parent; 1506 /* 1507 * make sure parent is a group and not sharecfg since 1508 * we may be cheating and passing in a group. 1509 * Eventually, groups of groups might come into being. 1510 */ 1511 if (node == NULL || 1512 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 1513 node = NULL; 1514 } 1515 return ((sa_group_t)node); 1516 } 1517 1518 /* 1519 * _sa_create_group(impl_handle, groupname) 1520 * 1521 * Create a group in the document. The caller will need to deal with 1522 * configuration store and activation. 1523 */ 1524 1525 sa_group_t 1526 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname) 1527 { 1528 xmlNodePtr node = NULL; 1529 1530 if (sa_valid_group_name(groupname)) { 1531 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group", 1532 NULL); 1533 if (node != NULL) { 1534 xmlSetProp(node, (xmlChar *)"name", 1535 (xmlChar *)groupname); 1536 xmlSetProp(node, (xmlChar *)"state", 1537 (xmlChar *)"enabled"); 1538 } 1539 } 1540 return ((sa_group_t)node); 1541 } 1542 1543 /* 1544 * _sa_create_zfs_group(group, groupname) 1545 * 1546 * Create a ZFS subgroup under the specified group. This may 1547 * eventually form the basis of general sub-groups, but is currently 1548 * restricted to ZFS. 1549 */ 1550 sa_group_t 1551 _sa_create_zfs_group(sa_group_t group, char *groupname) 1552 { 1553 xmlNodePtr node = NULL; 1554 1555 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL); 1556 if (node != NULL) { 1557 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1558 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1559 } 1560 1561 return ((sa_group_t)node); 1562 } 1563 1564 /* 1565 * sa_create_group(groupname, *error) 1566 * 1567 * Create a new group with groupname. Need to validate that it is a 1568 * legal name for SMF and the construct the SMF service instance of 1569 * svc:/network/shares/group to implement the group. All necessary 1570 * operational properties must be added to the group at this point 1571 * (via the SMF transaction model). 1572 */ 1573 sa_group_t 1574 sa_create_group(sa_handle_t handle, char *groupname, int *error) 1575 { 1576 xmlNodePtr node = NULL; 1577 sa_group_t group; 1578 int ret; 1579 char rbacstr[SA_STRSIZE]; 1580 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 1581 1582 ret = SA_OK; 1583 1584 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 1585 ret = SA_SYSTEM_ERR; 1586 goto err; 1587 } 1588 1589 group = sa_get_group(handle, groupname); 1590 if (group != NULL) { 1591 ret = SA_DUPLICATE_NAME; 1592 } else { 1593 if (sa_valid_group_name(groupname)) { 1594 node = xmlNewChild(impl_handle->tree, NULL, 1595 (xmlChar *)"group", NULL); 1596 if (node != NULL) { 1597 xmlSetProp(node, (xmlChar *)"name", 1598 (xmlChar *)groupname); 1599 /* default to the group being enabled */ 1600 xmlSetProp(node, (xmlChar *)"state", 1601 (xmlChar *)"enabled"); 1602 ret = sa_create_instance(impl_handle->scfhandle, 1603 groupname); 1604 if (ret == SA_OK) { 1605 ret = sa_start_transaction( 1606 impl_handle->scfhandle, 1607 "operation"); 1608 } 1609 if (ret == SA_OK) { 1610 ret = sa_set_property( 1611 impl_handle->scfhandle, 1612 "state", "enabled"); 1613 if (ret == SA_OK) { 1614 ret = sa_end_transaction( 1615 impl_handle->scfhandle); 1616 } else { 1617 sa_abort_transaction( 1618 impl_handle->scfhandle); 1619 } 1620 } 1621 if (ret == SA_OK) { 1622 /* initialize the RBAC strings */ 1623 ret = sa_start_transaction( 1624 impl_handle->scfhandle, 1625 "general"); 1626 if (ret == SA_OK) { 1627 (void) snprintf(rbacstr, 1628 sizeof (rbacstr), "%s.%s", 1629 SA_RBAC_MANAGE, groupname); 1630 ret = sa_set_property( 1631 impl_handle->scfhandle, 1632 "action_authorization", 1633 rbacstr); 1634 } 1635 if (ret == SA_OK) { 1636 (void) snprintf(rbacstr, 1637 sizeof (rbacstr), "%s.%s", 1638 SA_RBAC_VALUE, groupname); 1639 ret = sa_set_property( 1640 impl_handle->scfhandle, 1641 "value_authorization", 1642 rbacstr); 1643 } 1644 if (ret == SA_OK) { 1645 ret = sa_end_transaction( 1646 impl_handle->scfhandle); 1647 } else { 1648 sa_abort_transaction( 1649 impl_handle->scfhandle); 1650 } 1651 } 1652 if (ret != SA_OK) { 1653 /* 1654 * Couldn't commit the group 1655 * so we need to undo 1656 * internally. 1657 */ 1658 xmlUnlinkNode(node); 1659 xmlFreeNode(node); 1660 node = NULL; 1661 } 1662 } else { 1663 ret = SA_NO_MEMORY; 1664 } 1665 } else { 1666 ret = SA_INVALID_NAME; 1667 } 1668 } 1669 err: 1670 if (error != NULL) 1671 *error = ret; 1672 return ((sa_group_t)node); 1673 } 1674 1675 /* 1676 * sa_remove_group(group) 1677 * 1678 * Remove the specified group. This deletes from the SMF repository. 1679 * All property groups and properties are removed. 1680 */ 1681 1682 int 1683 sa_remove_group(sa_group_t group) 1684 { 1685 char *name; 1686 int ret = SA_OK; 1687 sa_handle_impl_t impl_handle; 1688 1689 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1690 if (impl_handle != NULL) { 1691 name = sa_get_group_attr(group, "name"); 1692 if (name != NULL) { 1693 ret = sa_delete_instance(impl_handle->scfhandle, name); 1694 sa_free_attr_string(name); 1695 } 1696 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 1697 xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 1698 } else { 1699 ret = SA_SYSTEM_ERR; 1700 } 1701 return (ret); 1702 } 1703 1704 /* 1705 * sa_update_config() 1706 * 1707 * Used to update legacy files that need to be updated in bulk 1708 * Currently, this is a placeholder and will go away in a future 1709 * release. 1710 */ 1711 1712 int 1713 sa_update_config(sa_handle_t handle) 1714 { 1715 /* 1716 * do legacy files first so we can tell when they change. 1717 * This will go away when we start updating individual records 1718 * rather than the whole file. 1719 */ 1720 update_legacy_config(handle); 1721 return (SA_OK); 1722 } 1723 1724 /* 1725 * get_node_attr(node, tag) 1726 * 1727 * Get the speficied tag(attribute) if it exists on the node. This is 1728 * used internally by a number of attribute oriented functions. 1729 */ 1730 1731 static char * 1732 get_node_attr(void *nodehdl, char *tag) 1733 { 1734 xmlNodePtr node = (xmlNodePtr)nodehdl; 1735 xmlChar *name = NULL; 1736 1737 if (node != NULL) 1738 name = xmlGetProp(node, (xmlChar *)tag); 1739 return ((char *)name); 1740 } 1741 1742 /* 1743 * get_node_attr(node, tag) 1744 * 1745 * Set the speficied tag(attribute) to the specified value This is 1746 * used internally by a number of attribute oriented functions. It 1747 * doesn't update the repository, only the internal document state. 1748 */ 1749 1750 void 1751 set_node_attr(void *nodehdl, char *tag, char *value) 1752 { 1753 xmlNodePtr node = (xmlNodePtr)nodehdl; 1754 if (node != NULL && tag != NULL) { 1755 if (value != NULL) 1756 xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 1757 else 1758 xmlUnsetProp(node, (xmlChar *)tag); 1759 } 1760 } 1761 1762 /* 1763 * sa_get_group_attr(group, tag) 1764 * 1765 * Get the specied attribute, if defined, for the group. 1766 */ 1767 1768 char * 1769 sa_get_group_attr(sa_group_t group, char *tag) 1770 { 1771 return (get_node_attr((void *)group, tag)); 1772 } 1773 1774 /* 1775 * sa_set_group_attr(group, tag, value) 1776 * 1777 * set the specified tag/attribute on the group using value as its 1778 * value. 1779 * 1780 * This will result in setting the property in the SMF repository as 1781 * well as in the internal document. 1782 */ 1783 1784 int 1785 sa_set_group_attr(sa_group_t group, char *tag, char *value) 1786 { 1787 int ret; 1788 char *groupname; 1789 sa_handle_impl_t impl_handle; 1790 1791 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1792 if (impl_handle != NULL) { 1793 groupname = sa_get_group_attr(group, "name"); 1794 ret = sa_get_instance(impl_handle->scfhandle, groupname); 1795 if (ret == SA_OK) { 1796 set_node_attr((void *)group, tag, value); 1797 ret = sa_start_transaction(impl_handle->scfhandle, 1798 "operation"); 1799 if (ret == SA_OK) { 1800 ret = sa_set_property(impl_handle->scfhandle, 1801 tag, value); 1802 if (ret == SA_OK) 1803 (void) sa_end_transaction( 1804 impl_handle->scfhandle); 1805 else 1806 sa_abort_transaction( 1807 impl_handle->scfhandle); 1808 } 1809 } 1810 if (groupname != NULL) 1811 sa_free_attr_string(groupname); 1812 } else { 1813 ret = SA_SYSTEM_ERR; 1814 } 1815 return (ret); 1816 } 1817 1818 /* 1819 * sa_get_share_attr(share, tag) 1820 * 1821 * Return the value of the tag/attribute set on the specified 1822 * share. Returns NULL if the tag doesn't exist. 1823 */ 1824 1825 char * 1826 sa_get_share_attr(sa_share_t share, char *tag) 1827 { 1828 return (get_node_attr((void *)share, tag)); 1829 } 1830 1831 /* 1832 * sa_get_resource(group, resource) 1833 * 1834 * Search all the shares in the speified group for a share with a 1835 * resource name matching the one specified. 1836 * 1837 * In the future, it may be advantageous to allow group to be NULL and 1838 * search all groups but that isn't needed at present. 1839 */ 1840 1841 sa_share_t 1842 sa_get_resource(sa_group_t group, char *resource) 1843 { 1844 sa_share_t share = NULL; 1845 char *name = NULL; 1846 1847 if (resource != NULL) { 1848 for (share = sa_get_share(group, NULL); share != NULL; 1849 share = sa_get_next_share(share)) { 1850 name = sa_get_share_attr(share, "resource"); 1851 if (name != NULL) { 1852 if (strcmp(name, resource) == 0) 1853 break; 1854 sa_free_attr_string(name); 1855 name = NULL; 1856 } 1857 } 1858 if (name != NULL) 1859 sa_free_attr_string(name); 1860 } 1861 return ((sa_share_t)share); 1862 } 1863 1864 /* 1865 * _sa_set_share_description(share, description) 1866 * 1867 * Add a description tag with text contents to the specified share. 1868 * A separate XML tag is used rather than a property. 1869 */ 1870 1871 xmlNodePtr 1872 _sa_set_share_description(sa_share_t share, char *content) 1873 { 1874 xmlNodePtr node; 1875 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description", 1876 NULL); 1877 xmlNodeSetContent(node, (xmlChar *)content); 1878 return (node); 1879 } 1880 1881 /* 1882 * sa_set_share_attr(share, tag, value) 1883 * 1884 * Set the share attribute specified by tag to the specified value. In 1885 * the case of "resource", enforce a no duplicates in a group rule. If 1886 * the share is not transient, commit the changes to the repository 1887 * else just update the share internally. 1888 */ 1889 1890 int 1891 sa_set_share_attr(sa_share_t share, char *tag, char *value) 1892 { 1893 sa_group_t group; 1894 sa_share_t resource; 1895 int ret = SA_OK; 1896 1897 group = sa_get_parent_group(share); 1898 1899 /* 1900 * There are some attributes that may have specific 1901 * restrictions on them. Initially, only "resource" has 1902 * special meaning that needs to be checked. Only one instance 1903 * of a resource name may exist within a group. 1904 */ 1905 1906 if (strcmp(tag, "resource") == 0) { 1907 resource = sa_get_resource(group, value); 1908 if (resource != share && resource != NULL) 1909 ret = SA_DUPLICATE_NAME; 1910 } 1911 if (ret == SA_OK) { 1912 set_node_attr((void *)share, tag, value); 1913 if (group != NULL) { 1914 char *type; 1915 /* we can probably optimize this some */ 1916 type = sa_get_share_attr(share, "type"); 1917 if (type == NULL || strcmp(type, "transient") != 0) { 1918 sa_handle_impl_t impl_handle; 1919 impl_handle = 1920 (sa_handle_impl_t)sa_find_group_handle( 1921 group); 1922 if (impl_handle != NULL) { 1923 ret = sa_commit_share( 1924 impl_handle->scfhandle, group, 1925 share); 1926 } else { 1927 ret = SA_SYSTEM_ERR; 1928 } 1929 } 1930 if (type != NULL) 1931 sa_free_attr_string(type); 1932 } 1933 } 1934 return (ret); 1935 } 1936 1937 /* 1938 * sa_get_property_attr(prop, tag) 1939 * 1940 * Get the value of the specified property attribute. Standard 1941 * attributes are "type" and "value". 1942 */ 1943 1944 char * 1945 sa_get_property_attr(sa_property_t prop, char *tag) 1946 { 1947 return (get_node_attr((void *)prop, tag)); 1948 } 1949 1950 /* 1951 * sa_get_optionset_attr(prop, tag) 1952 * 1953 * Get the value of the specified property attribute. Standard 1954 * attribute is "type". 1955 */ 1956 1957 char * 1958 sa_get_optionset_attr(sa_property_t optionset, char *tag) 1959 { 1960 return (get_node_attr((void *)optionset, tag)); 1961 1962 } 1963 1964 /* 1965 * sa_set_optionset_attr(optionset, tag, value) 1966 * 1967 * Set the specified attribute(tag) to the specified value on the 1968 * optionset. 1969 */ 1970 1971 void 1972 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 1973 { 1974 set_node_attr((void *)optionset, tag, value); 1975 } 1976 1977 /* 1978 * sa_free_attr_string(string) 1979 * 1980 * Free the string that was returned in one of the sa_get_*_attr() 1981 * functions. 1982 */ 1983 1984 void 1985 sa_free_attr_string(char *string) 1986 { 1987 xmlFree((xmlChar *)string); 1988 } 1989 1990 /* 1991 * sa_get_optionset(group, proto) 1992 * 1993 * Return the optionset, if it exists, that is associated with the 1994 * specified protocol. 1995 */ 1996 1997 sa_optionset_t 1998 sa_get_optionset(void *group, char *proto) 1999 { 2000 xmlNodePtr node; 2001 xmlChar *value = NULL; 2002 2003 for (node = ((xmlNodePtr)group)->children; node != NULL; 2004 node = node->next) { 2005 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2006 value = xmlGetProp(node, (xmlChar *)"type"); 2007 if (proto != NULL) { 2008 if (value != NULL && 2009 xmlStrcmp(value, (xmlChar *)proto) == 0) { 2010 break; 2011 } 2012 if (value != NULL) { 2013 xmlFree(value); 2014 value = NULL; 2015 } 2016 } else { 2017 break; 2018 } 2019 } 2020 } 2021 if (value != NULL) 2022 xmlFree(value); 2023 return ((sa_optionset_t)node); 2024 } 2025 2026 /* 2027 * sa_get_next_optionset(optionset) 2028 * 2029 * Return the next optionset in the group. NULL if this was the last. 2030 */ 2031 2032 sa_optionset_t 2033 sa_get_next_optionset(sa_optionset_t optionset) 2034 { 2035 xmlNodePtr node; 2036 2037 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 2038 node = node->next) { 2039 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2040 break; 2041 } 2042 } 2043 return ((sa_optionset_t)node); 2044 } 2045 2046 /* 2047 * sa_get_security(group, sectype, proto) 2048 * 2049 * Return the security optionset. The internal name is a hold over 2050 * from the implementation and will be changed before the API is 2051 * finalized. This is really a named optionset that can be negotiated 2052 * as a group of properties (like NFS security options). 2053 */ 2054 2055 sa_security_t 2056 sa_get_security(sa_group_t group, char *sectype, char *proto) 2057 { 2058 xmlNodePtr node; 2059 xmlChar *value = NULL; 2060 2061 for (node = ((xmlNodePtr)group)->children; node != NULL; 2062 node = node->next) { 2063 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2064 if (proto != NULL) { 2065 value = xmlGetProp(node, (xmlChar *)"type"); 2066 if (value == NULL || 2067 (value != NULL && 2068 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 2069 /* it doesn't match so continue */ 2070 xmlFree(value); 2071 value = NULL; 2072 continue; 2073 } 2074 } 2075 if (value != NULL) { 2076 xmlFree(value); 2077 value = NULL; 2078 } 2079 /* potential match */ 2080 if (sectype != NULL) { 2081 value = xmlGetProp(node, (xmlChar *)"sectype"); 2082 if (value != NULL && 2083 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 2084 break; 2085 } 2086 } else { 2087 break; 2088 } 2089 } 2090 if (value != NULL) { 2091 xmlFree(value); 2092 value = NULL; 2093 } 2094 } 2095 if (value != NULL) 2096 xmlFree(value); 2097 return ((sa_security_t)node); 2098 } 2099 2100 /* 2101 * sa_get_next_security(security) 2102 * 2103 * Get the next security optionset if one exists. 2104 */ 2105 2106 sa_security_t 2107 sa_get_next_security(sa_security_t security) 2108 { 2109 xmlNodePtr node; 2110 2111 for (node = ((xmlNodePtr)security)->next; node != NULL; 2112 node = node->next) { 2113 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2114 break; 2115 } 2116 } 2117 return ((sa_security_t)node); 2118 } 2119 2120 /* 2121 * sa_get_property(optionset, prop) 2122 * 2123 * Get the property object with the name specified in prop from the 2124 * optionset. 2125 */ 2126 2127 sa_property_t 2128 sa_get_property(sa_optionset_t optionset, char *prop) 2129 { 2130 xmlNodePtr node = (xmlNodePtr)optionset; 2131 xmlChar *value = NULL; 2132 2133 if (optionset == NULL) 2134 return (NULL); 2135 2136 for (node = node->children; node != NULL; 2137 node = node->next) { 2138 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2139 if (prop == NULL) 2140 break; 2141 value = xmlGetProp(node, (xmlChar *)"type"); 2142 if (value != NULL && 2143 xmlStrcmp(value, (xmlChar *)prop) == 0) { 2144 break; 2145 } 2146 if (value != NULL) { 2147 xmlFree(value); 2148 value = NULL; 2149 } 2150 } 2151 } 2152 if (value != NULL) 2153 xmlFree(value); 2154 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2155 /* 2156 * avoid a non option node -- it is possible to be a 2157 * text node 2158 */ 2159 node = NULL; 2160 } 2161 return ((sa_property_t)node); 2162 } 2163 2164 /* 2165 * sa_get_next_property(property) 2166 * 2167 * Get the next property following the specified property. NULL if 2168 * this was the last. 2169 */ 2170 2171 sa_property_t 2172 sa_get_next_property(sa_property_t property) 2173 { 2174 xmlNodePtr node; 2175 2176 for (node = ((xmlNodePtr)property)->next; node != NULL; 2177 node = node->next) { 2178 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2179 break; 2180 } 2181 } 2182 return ((sa_property_t)node); 2183 } 2184 2185 /* 2186 * sa_set_share_description(share, content) 2187 * 2188 * Set the description of share to content. 2189 */ 2190 2191 int 2192 sa_set_share_description(sa_share_t share, char *content) 2193 { 2194 xmlNodePtr node; 2195 sa_group_t group; 2196 int ret = SA_OK; 2197 2198 for (node = ((xmlNodePtr)share)->children; node != NULL; 2199 node = node->next) { 2200 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2201 break; 2202 } 2203 } 2204 group = sa_get_parent_group(share); 2205 /* no existing description but want to add */ 2206 if (node == NULL && content != NULL) { 2207 /* add a description */ 2208 node = _sa_set_share_description(share, content); 2209 } else if (node != NULL && content != NULL) { 2210 /* update a description */ 2211 xmlNodeSetContent(node, (xmlChar *)content); 2212 } else if (node != NULL && content == NULL) { 2213 /* remove an existing description */ 2214 xmlUnlinkNode(node); 2215 xmlFreeNode(node); 2216 } 2217 if (group != NULL && is_persistent((sa_group_t)share)) { 2218 sa_handle_impl_t impl_handle; 2219 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2220 if (impl_handle != NULL) { 2221 ret = sa_commit_share(impl_handle->scfhandle, group, 2222 share); 2223 } else { 2224 ret = SA_SYSTEM_ERR; 2225 } 2226 } 2227 return (ret); 2228 } 2229 2230 /* 2231 * fixproblemchars(string) 2232 * 2233 * don't want any newline or tab characters in the text since these 2234 * could break display of data and legacy file formats. 2235 */ 2236 static void 2237 fixproblemchars(char *str) 2238 { 2239 int c; 2240 for (c = *str; c != '\0'; c = *++str) { 2241 if (c == '\t' || c == '\n') 2242 *str = ' '; 2243 else if (c == '"') 2244 *str = '\''; 2245 } 2246 } 2247 2248 /* 2249 * sa_get_share_description(share) 2250 * 2251 * Return the description text for the specified share if it 2252 * exists. NULL if no description exists. 2253 */ 2254 2255 char * 2256 sa_get_share_description(sa_share_t share) 2257 { 2258 xmlChar *description = NULL; 2259 xmlNodePtr node; 2260 2261 for (node = ((xmlNodePtr)share)->children; node != NULL; 2262 node = node->next) { 2263 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2264 break; 2265 } 2266 } 2267 if (node != NULL) { 2268 description = xmlNodeGetContent((xmlNodePtr)share); 2269 fixproblemchars((char *)description); 2270 } 2271 return ((char *)description); 2272 } 2273 2274 /* 2275 * sa_free(share_description(description) 2276 * 2277 * Free the description string. 2278 */ 2279 2280 void 2281 sa_free_share_description(char *description) 2282 { 2283 xmlFree((xmlChar *)description); 2284 } 2285 2286 /* 2287 * sa_create_optionset(group, proto) 2288 * 2289 * Create an optionset for the specified protocol in the specied 2290 * group. This is manifested as a property group within SMF. 2291 */ 2292 2293 sa_optionset_t 2294 sa_create_optionset(sa_group_t group, char *proto) 2295 { 2296 sa_optionset_t optionset; 2297 sa_group_t parent = group; 2298 2299 optionset = sa_get_optionset(group, proto); 2300 if (optionset != NULL) { 2301 /* can't have a duplicate protocol */ 2302 optionset = NULL; 2303 } else { 2304 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 2305 NULL, (xmlChar *)"optionset", NULL); 2306 /* 2307 * only put to repository if on a group and we were 2308 * able to create an optionset. 2309 */ 2310 if (optionset != NULL) { 2311 char oname[SA_STRSIZE]; 2312 char *groupname; 2313 char *id = NULL; 2314 2315 if (sa_is_share(group)) 2316 parent = sa_get_parent_group((sa_share_t)group); 2317 2318 sa_set_optionset_attr(optionset, "type", proto); 2319 2320 if (sa_is_share(group)) { 2321 id = sa_get_share_attr((sa_share_t)group, "id"); 2322 } 2323 (void) sa_optionset_name(optionset, oname, 2324 sizeof (oname), id); 2325 groupname = sa_get_group_attr(parent, "name"); 2326 if (groupname != NULL && is_persistent(group)) { 2327 sa_handle_impl_t impl_handle; 2328 impl_handle = (sa_handle_impl_t) 2329 sa_find_group_handle(group); 2330 assert(impl_handle != NULL); 2331 if (impl_handle != NULL) { 2332 (void) sa_get_instance( 2333 impl_handle->scfhandle, 2334 groupname); 2335 (void) sa_create_pgroup( 2336 impl_handle->scfhandle, oname); 2337 } 2338 } 2339 if (groupname != NULL) 2340 sa_free_attr_string(groupname); 2341 if (id != NULL) 2342 sa_free_attr_string(id); 2343 } 2344 } 2345 return (optionset); 2346 } 2347 2348 /* 2349 * sa_get_property_parent(property) 2350 * 2351 * Given a property, return the object it is a property of. This will 2352 * be an optionset of some type. 2353 */ 2354 2355 static sa_optionset_t 2356 sa_get_property_parent(sa_property_t property) 2357 { 2358 xmlNodePtr node = NULL; 2359 2360 if (property != NULL) 2361 node = ((xmlNodePtr)property)->parent; 2362 return ((sa_optionset_t)node); 2363 } 2364 2365 /* 2366 * sa_get_optionset_parent(optionset) 2367 * 2368 * Return the parent of the specified optionset. This could be a group 2369 * or a share. 2370 */ 2371 2372 static sa_group_t 2373 sa_get_optionset_parent(sa_optionset_t optionset) 2374 { 2375 xmlNodePtr node = NULL; 2376 2377 if (optionset != NULL) 2378 node = ((xmlNodePtr)optionset)->parent; 2379 return ((sa_group_t)node); 2380 } 2381 2382 /* 2383 * zfs_needs_update(share) 2384 * 2385 * In order to avoid making multiple updates to a ZFS share when 2386 * setting properties, the share attribute "changed" will be set to 2387 * true when a property is added or modifed. When done adding 2388 * properties, we can then detect that an update is needed. We then 2389 * clear the state here to detect additional changes. 2390 */ 2391 2392 static int 2393 zfs_needs_update(sa_share_t share) 2394 { 2395 char *attr; 2396 int result = 0; 2397 2398 attr = sa_get_share_attr(share, "changed"); 2399 if (attr != NULL) { 2400 sa_free_attr_string(attr); 2401 result = 1; 2402 } 2403 set_node_attr((void *)share, "changed", NULL); 2404 return (result); 2405 } 2406 2407 /* 2408 * zfs_set_update(share) 2409 * 2410 * Set the changed attribute of the share to true. 2411 */ 2412 2413 static void 2414 zfs_set_update(sa_share_t share) 2415 { 2416 set_node_attr((void *)share, "changed", "true"); 2417 } 2418 2419 /* 2420 * sa_commit_properties(optionset, clear) 2421 * 2422 * Check if SMF or ZFS config and either update or abort the pending 2423 * changes. 2424 */ 2425 2426 int 2427 sa_commit_properties(sa_optionset_t optionset, int clear) 2428 { 2429 sa_group_t group; 2430 sa_group_t parent; 2431 int zfs = 0; 2432 int needsupdate = 0; 2433 int ret = SA_OK; 2434 sa_handle_impl_t impl_handle; 2435 2436 group = sa_get_optionset_parent(optionset); 2437 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2438 /* only update ZFS if on a share */ 2439 parent = sa_get_parent_group(group); 2440 zfs++; 2441 if (parent != NULL && is_zfs_group(parent)) 2442 needsupdate = zfs_needs_update(group); 2443 else 2444 zfs = 0; 2445 } 2446 if (zfs) { 2447 if (!clear && needsupdate) 2448 ret = sa_zfs_update((sa_share_t)group); 2449 } else { 2450 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2451 if (impl_handle != NULL) { 2452 if (clear) { 2453 (void) sa_abort_transaction( 2454 impl_handle->scfhandle); 2455 } else { 2456 ret = sa_end_transaction( 2457 impl_handle->scfhandle); 2458 } 2459 } else { 2460 ret = SA_SYSTEM_ERR; 2461 } 2462 } 2463 return (ret); 2464 } 2465 2466 /* 2467 * sa_destroy_optionset(optionset) 2468 * 2469 * Remove the optionset from its group. Update the repostory to 2470 * reflect this change. 2471 */ 2472 2473 int 2474 sa_destroy_optionset(sa_optionset_t optionset) 2475 { 2476 char name[SA_STRSIZE]; 2477 int len; 2478 int ret; 2479 char *id = NULL; 2480 sa_group_t group; 2481 int ispersist = 1; 2482 2483 /* now delete the prop group */ 2484 group = sa_get_optionset_parent(optionset); 2485 if (group != NULL && sa_is_share(group)) { 2486 ispersist = is_persistent(group); 2487 id = sa_get_share_attr((sa_share_t)group, "id"); 2488 } 2489 if (ispersist) { 2490 sa_handle_impl_t impl_handle; 2491 len = sa_optionset_name(optionset, name, sizeof (name), id); 2492 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2493 if (impl_handle != NULL) { 2494 if (len > 0) { 2495 ret = sa_delete_pgroup(impl_handle->scfhandle, 2496 name); 2497 } 2498 } else { 2499 ret = SA_SYSTEM_ERR; 2500 } 2501 } 2502 xmlUnlinkNode((xmlNodePtr)optionset); 2503 xmlFreeNode((xmlNodePtr)optionset); 2504 if (id != NULL) 2505 sa_free_attr_string(id); 2506 return (ret); 2507 } 2508 2509 /* private to the implementation */ 2510 int 2511 _sa_remove_optionset(sa_optionset_t optionset) 2512 { 2513 int ret = SA_OK; 2514 2515 xmlUnlinkNode((xmlNodePtr)optionset); 2516 xmlFreeNode((xmlNodePtr)optionset); 2517 return (ret); 2518 } 2519 2520 /* 2521 * sa_create_security(group, sectype, proto) 2522 * 2523 * Create a security optionset (one that has a type name and a 2524 * proto). Security is left over from a pure NFS implementation. The 2525 * naming will change in the future when the API is released. 2526 */ 2527 sa_security_t 2528 sa_create_security(sa_group_t group, char *sectype, char *proto) 2529 { 2530 sa_security_t security; 2531 char *id = NULL; 2532 sa_group_t parent; 2533 char *groupname = NULL; 2534 2535 if (group != NULL && sa_is_share(group)) { 2536 id = sa_get_share_attr((sa_share_t)group, "id"); 2537 parent = sa_get_parent_group(group); 2538 if (parent != NULL) 2539 groupname = sa_get_group_attr(parent, "name"); 2540 } else if (group != NULL) { 2541 groupname = sa_get_group_attr(group, "name"); 2542 } 2543 2544 security = sa_get_security(group, sectype, proto); 2545 if (security != NULL) { 2546 /* can't have a duplicate security option */ 2547 security = NULL; 2548 } else { 2549 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2550 NULL, (xmlChar *)"security", NULL); 2551 if (security != NULL) { 2552 char oname[SA_STRSIZE]; 2553 sa_set_security_attr(security, "type", proto); 2554 2555 sa_set_security_attr(security, "sectype", sectype); 2556 (void) sa_security_name(security, oname, 2557 sizeof (oname), id); 2558 if (groupname != NULL && is_persistent(group)) { 2559 sa_handle_impl_t impl_handle; 2560 impl_handle = 2561 (sa_handle_impl_t)sa_find_group_handle( 2562 group); 2563 if (impl_handle != NULL) { 2564 (void) sa_get_instance( 2565 impl_handle->scfhandle, groupname); 2566 (void) sa_create_pgroup( 2567 impl_handle->scfhandle, oname); 2568 } 2569 } 2570 } 2571 } 2572 if (groupname != NULL) 2573 sa_free_attr_string(groupname); 2574 return (security); 2575 } 2576 2577 /* 2578 * sa_destroy_security(security) 2579 * 2580 * Remove the specified optionset from the document and the 2581 * configuration. 2582 */ 2583 2584 int 2585 sa_destroy_security(sa_security_t security) 2586 { 2587 char name[SA_STRSIZE]; 2588 int len; 2589 int ret = SA_OK; 2590 char *id = NULL; 2591 sa_group_t group; 2592 int iszfs = 0; 2593 int ispersist = 1; 2594 2595 group = sa_get_optionset_parent(security); 2596 2597 if (group != NULL) 2598 iszfs = sa_group_is_zfs(group); 2599 2600 if (group != NULL && !iszfs) { 2601 if (sa_is_share(group)) 2602 ispersist = is_persistent(group); 2603 id = sa_get_share_attr((sa_share_t)group, "id"); 2604 } 2605 if (ispersist) { 2606 len = sa_security_name(security, name, sizeof (name), id); 2607 if (!iszfs && len > 0) { 2608 sa_handle_impl_t impl_handle; 2609 impl_handle = 2610 (sa_handle_impl_t)sa_find_group_handle(group); 2611 if (impl_handle != NULL) { 2612 ret = sa_delete_pgroup(impl_handle->scfhandle, 2613 name); 2614 } else { 2615 ret = SA_SYSTEM_ERR; 2616 } 2617 } 2618 } 2619 xmlUnlinkNode((xmlNodePtr)security); 2620 xmlFreeNode((xmlNodePtr)security); 2621 if (iszfs) 2622 ret = sa_zfs_update(group); 2623 if (id != NULL) 2624 sa_free_attr_string(id); 2625 return (ret); 2626 } 2627 2628 /* 2629 * sa_get_security_attr(optionset, tag) 2630 * 2631 * Return the specified attribute value from the optionset. 2632 */ 2633 2634 char * 2635 sa_get_security_attr(sa_property_t optionset, char *tag) 2636 { 2637 return (get_node_attr((void *)optionset, tag)); 2638 2639 } 2640 2641 /* 2642 * sa_set_security_attr(optionset, tag, value) 2643 * 2644 * Set the optioset attribute specied by tag to the specified value. 2645 */ 2646 2647 void 2648 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2649 { 2650 set_node_attr((void *)optionset, tag, value); 2651 } 2652 2653 /* 2654 * is_nodetype(node, type) 2655 * 2656 * Check to see if node is of the type specified. 2657 */ 2658 2659 static int 2660 is_nodetype(void *node, char *type) 2661 { 2662 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2663 } 2664 2665 2666 /* 2667 * add_or_update() 2668 * 2669 * Add or update a property. Pulled out of sa_set_prop_by_prop for 2670 * readability. 2671 */ 2672 static int 2673 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value, 2674 scf_transaction_entry_t *entry, char *name, char *valstr) 2675 { 2676 int ret = SA_SYSTEM_ERR; 2677 2678 if (value != NULL) { 2679 if (type == SA_PROP_OP_ADD) 2680 ret = scf_transaction_property_new(scf_handle->trans, 2681 entry, name, SCF_TYPE_ASTRING); 2682 else 2683 ret = scf_transaction_property_change(scf_handle->trans, 2684 entry, name, SCF_TYPE_ASTRING); 2685 if (ret == 0) { 2686 ret = scf_value_set_astring(value, valstr); 2687 if (ret == 0) 2688 ret = scf_entry_add_value(entry, value); 2689 if (ret == 0) 2690 return (ret); 2691 scf_value_destroy(value); 2692 } else { 2693 scf_entry_destroy(entry); 2694 } 2695 } 2696 return (SA_SYSTEM_ERR); 2697 } 2698 2699 /* 2700 * sa_set_prop_by_prop(optionset, group, prop, type) 2701 * 2702 * Add/remove/update the specified property prop into the optionset or 2703 * share. If a share, sort out which property group based on GUID. In 2704 * all cases, the appropriate transaction is set (or ZFS share is 2705 * marked as needing an update) 2706 */ 2707 2708 static int 2709 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 2710 sa_property_t prop, int type) 2711 { 2712 char *name; 2713 char *valstr; 2714 int ret = SA_OK; 2715 scf_transaction_entry_t *entry; 2716 scf_value_t *value; 2717 int opttype; /* 1 == optionset, 0 == security */ 2718 char *id = NULL; 2719 int iszfs = 0; 2720 int isshare = 0; 2721 sa_group_t parent = NULL; 2722 sa_handle_impl_t impl_handle; 2723 scfutilhandle_t *scf_handle; 2724 2725 if (!is_persistent(group)) { 2726 /* 2727 * if the group/share is not persistent we don't need 2728 * to do anything here 2729 */ 2730 return (SA_OK); 2731 } 2732 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2733 if (impl_handle == NULL || impl_handle->scfhandle == NULL) 2734 return (SA_SYSTEM_ERR); 2735 scf_handle = impl_handle->scfhandle; 2736 name = sa_get_property_attr(prop, "type"); 2737 valstr = sa_get_property_attr(prop, "value"); 2738 entry = scf_entry_create(scf_handle->handle); 2739 opttype = is_nodetype((void *)optionset, "optionset"); 2740 2741 if (valstr != NULL && entry != NULL) { 2742 if (sa_is_share(group)) { 2743 isshare = 1; 2744 parent = sa_get_parent_group(group); 2745 if (parent != NULL) 2746 iszfs = is_zfs_group(parent); 2747 } else { 2748 iszfs = is_zfs_group(group); 2749 } 2750 if (!iszfs) { 2751 if (scf_handle->trans == NULL) { 2752 char oname[SA_STRSIZE]; 2753 char *groupname = NULL; 2754 if (isshare) { 2755 if (parent != NULL) { 2756 groupname = 2757 sa_get_group_attr(parent, 2758 "name"); 2759 } 2760 id = 2761 sa_get_share_attr((sa_share_t)group, 2762 "id"); 2763 } else { 2764 groupname = sa_get_group_attr(group, 2765 "name"); 2766 } 2767 if (groupname != NULL) { 2768 ret = sa_get_instance(scf_handle, 2769 groupname); 2770 sa_free_attr_string(groupname); 2771 } 2772 if (opttype) 2773 (void) sa_optionset_name(optionset, 2774 oname, sizeof (oname), id); 2775 else 2776 (void) sa_security_name(optionset, 2777 oname, sizeof (oname), id); 2778 ret = sa_start_transaction(scf_handle, oname); 2779 } 2780 if (ret == SA_OK) { 2781 switch (type) { 2782 case SA_PROP_OP_REMOVE: 2783 ret = scf_transaction_property_delete( 2784 scf_handle->trans, entry, name); 2785 break; 2786 case SA_PROP_OP_ADD: 2787 case SA_PROP_OP_UPDATE: 2788 value = scf_value_create( 2789 scf_handle->handle); 2790 ret = add_or_update(scf_handle, type, 2791 value, entry, name, valstr); 2792 break; 2793 } 2794 } 2795 } else { 2796 /* 2797 * ZFS update. The calling function would have updated 2798 * the internal XML structure. Just need to flag it as 2799 * changed for ZFS. 2800 */ 2801 zfs_set_update((sa_share_t)group); 2802 } 2803 } 2804 2805 if (name != NULL) 2806 sa_free_attr_string(name); 2807 if (valstr != NULL) 2808 sa_free_attr_string(valstr); 2809 else if (entry != NULL) 2810 scf_entry_destroy(entry); 2811 2812 if (ret == -1) 2813 ret = SA_SYSTEM_ERR; 2814 2815 return (ret); 2816 } 2817 2818 /* 2819 * sa_create_property(name, value) 2820 * 2821 * Create a new property with the specified name and value. 2822 */ 2823 2824 sa_property_t 2825 sa_create_property(char *name, char *value) 2826 { 2827 xmlNodePtr node; 2828 2829 node = xmlNewNode(NULL, (xmlChar *)"option"); 2830 if (node != NULL) { 2831 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 2832 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 2833 } 2834 return ((sa_property_t)node); 2835 } 2836 2837 /* 2838 * sa_add_property(object, property) 2839 * 2840 * Add the specified property to the object. Issue the appropriate 2841 * transaction or mark a ZFS object as needing an update. 2842 */ 2843 2844 int 2845 sa_add_property(void *object, sa_property_t property) 2846 { 2847 int ret = SA_OK; 2848 sa_group_t parent; 2849 sa_group_t group; 2850 char *proto; 2851 2852 proto = sa_get_optionset_attr(object, "type"); 2853 if (property != NULL) { 2854 if ((ret = sa_valid_property(object, proto, property)) == 2855 SA_OK) { 2856 property = (sa_property_t)xmlAddChild( 2857 (xmlNodePtr)object, (xmlNodePtr)property); 2858 } else { 2859 if (proto != NULL) 2860 sa_free_attr_string(proto); 2861 return (ret); 2862 } 2863 } 2864 2865 if (proto != NULL) 2866 sa_free_attr_string(proto); 2867 2868 parent = sa_get_parent_group(object); 2869 if (!is_persistent(parent)) { 2870 return (ret); 2871 } 2872 2873 if (sa_is_share(parent)) 2874 group = sa_get_parent_group(parent); 2875 else 2876 group = parent; 2877 2878 if (property == NULL) { 2879 ret = SA_NO_MEMORY; 2880 } else { 2881 char oname[SA_STRSIZE]; 2882 2883 if (!is_zfs_group(group)) { 2884 char *id = NULL; 2885 sa_handle_impl_t impl_handle; 2886 scfutilhandle_t *scf_handle; 2887 2888 impl_handle = (sa_handle_impl_t)sa_find_group_handle( 2889 group); 2890 if (impl_handle == NULL || 2891 impl_handle->scfhandle == NULL) 2892 ret = SA_SYSTEM_ERR; 2893 if (ret == SA_OK) { 2894 scf_handle = impl_handle->scfhandle; 2895 if (sa_is_share((sa_group_t)parent)) { 2896 id = sa_get_share_attr( 2897 (sa_share_t)parent, "id"); 2898 } 2899 if (scf_handle->trans == NULL) { 2900 if (is_nodetype(object, "optionset")) { 2901 (void) sa_optionset_name( 2902 (sa_optionset_t)object, 2903 oname, sizeof (oname), id); 2904 } else { 2905 (void) sa_security_name( 2906 (sa_optionset_t)object, 2907 oname, sizeof (oname), id); 2908 } 2909 ret = sa_start_transaction(scf_handle, 2910 oname); 2911 } 2912 if (ret == SA_OK) { 2913 char *name; 2914 char *value; 2915 name = sa_get_property_attr(property, 2916 "type"); 2917 value = sa_get_property_attr(property, 2918 "value"); 2919 if (name != NULL && value != NULL) { 2920 if (scf_handle->scf_state == 2921 SCH_STATE_INIT) { 2922 ret = sa_set_property( 2923 scf_handle, name, 2924 value); 2925 } 2926 } else { 2927 ret = SA_CONFIG_ERR; 2928 } 2929 if (name != NULL) 2930 sa_free_attr_string( 2931 name); 2932 if (value != NULL) 2933 sa_free_attr_string(value); 2934 } 2935 if (id != NULL) 2936 sa_free_attr_string(id); 2937 } 2938 } else { 2939 /* 2940 * ZFS is a special case. We do want 2941 * to allow editing property/security 2942 * lists since we can have a better 2943 * syntax and we also want to keep 2944 * things consistent when possible. 2945 * 2946 * Right now, we defer until the 2947 * sa_commit_properties so we can get 2948 * them all at once. We do need to 2949 * mark the share as "changed" 2950 */ 2951 zfs_set_update((sa_share_t)parent); 2952 } 2953 } 2954 return (ret); 2955 } 2956 2957 /* 2958 * sa_remove_property(property) 2959 * 2960 * Remove the specied property from its containing object. Update the 2961 * repository as appropriate. 2962 */ 2963 2964 int 2965 sa_remove_property(sa_property_t property) 2966 { 2967 int ret = SA_OK; 2968 2969 if (property != NULL) { 2970 sa_optionset_t optionset; 2971 sa_group_t group; 2972 optionset = sa_get_property_parent(property); 2973 if (optionset != NULL) { 2974 group = sa_get_optionset_parent(optionset); 2975 if (group != NULL) { 2976 ret = sa_set_prop_by_prop(optionset, group, 2977 property, SA_PROP_OP_REMOVE); 2978 } 2979 } 2980 xmlUnlinkNode((xmlNodePtr)property); 2981 xmlFreeNode((xmlNodePtr)property); 2982 } else { 2983 ret = SA_NO_SUCH_PROP; 2984 } 2985 return (ret); 2986 } 2987 2988 /* 2989 * sa_update_property(property, value) 2990 * 2991 * Update the specified property to the new value. If value is NULL, 2992 * we currently treat this as a remove. 2993 */ 2994 2995 int 2996 sa_update_property(sa_property_t property, char *value) 2997 { 2998 int ret = SA_OK; 2999 if (value == NULL) { 3000 return (sa_remove_property(property)); 3001 } else { 3002 sa_optionset_t optionset; 3003 sa_group_t group; 3004 set_node_attr((void *)property, "value", value); 3005 optionset = sa_get_property_parent(property); 3006 if (optionset != NULL) { 3007 group = sa_get_optionset_parent(optionset); 3008 if (group != NULL) { 3009 ret = sa_set_prop_by_prop(optionset, group, 3010 property, SA_PROP_OP_UPDATE); 3011 } 3012 } else { 3013 ret = SA_NO_SUCH_PROP; 3014 } 3015 } 3016 return (ret); 3017 } 3018 3019 /* 3020 * sa_get_protocol_property(propset, prop) 3021 * 3022 * Get the specified protocol specific property. These are global to 3023 * the protocol and not specific to a group or share. 3024 */ 3025 3026 sa_property_t 3027 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 3028 { 3029 xmlNodePtr node = (xmlNodePtr)propset; 3030 xmlChar *value = NULL; 3031 3032 for (node = node->children; node != NULL; 3033 node = node->next) { 3034 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3035 if (prop == NULL) 3036 break; 3037 value = xmlGetProp(node, (xmlChar *)"type"); 3038 if (value != NULL && 3039 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 3040 break; 3041 } 3042 if (value != NULL) { 3043 xmlFree(value); 3044 value = NULL; 3045 } 3046 } 3047 } 3048 if (value != NULL) 3049 xmlFree(value); 3050 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 3051 /* 3052 * avoid a non option node -- it is possible to be a 3053 * text node 3054 */ 3055 node = NULL; 3056 } 3057 return ((sa_property_t)node); 3058 } 3059 3060 /* 3061 * sa_get_next_protocol_property(prop) 3062 * 3063 * Get the next protocol specific property in the list. 3064 */ 3065 3066 sa_property_t 3067 sa_get_next_protocol_property(sa_property_t prop) 3068 { 3069 xmlNodePtr node; 3070 3071 for (node = ((xmlNodePtr)prop)->next; node != NULL; 3072 node = node->next) { 3073 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3074 break; 3075 } 3076 } 3077 return ((sa_property_t)node); 3078 } 3079 3080 /* 3081 * sa_set_protocol_property(prop, value) 3082 * 3083 * Set the specified property to have the new value. The protocol 3084 * specific plugin will then be called to update the property. 3085 */ 3086 3087 int 3088 sa_set_protocol_property(sa_property_t prop, char *value) 3089 { 3090 sa_protocol_properties_t propset; 3091 char *proto; 3092 int ret = SA_INVALID_PROTOCOL; 3093 3094 propset = ((xmlNodePtr)prop)->parent; 3095 if (propset != NULL) { 3096 proto = sa_get_optionset_attr(propset, "type"); 3097 if (proto != NULL) { 3098 set_node_attr((xmlNodePtr)prop, "value", value); 3099 ret = sa_proto_set_property(proto, prop); 3100 sa_free_attr_string(proto); 3101 } 3102 } 3103 return (ret); 3104 } 3105 3106 /* 3107 * sa_add_protocol_property(propset, prop) 3108 * 3109 * Add a new property to the protocol sepcific property set. 3110 */ 3111 3112 int 3113 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 3114 { 3115 xmlNodePtr node; 3116 3117 /* should check for legitimacy */ 3118 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 3119 if (node != NULL) 3120 return (SA_OK); 3121 return (SA_NO_MEMORY); 3122 } 3123 3124 /* 3125 * sa_create_protocol_properties(proto) 3126 * 3127 * Create a protocol specifity property set. 3128 */ 3129 3130 sa_protocol_properties_t 3131 sa_create_protocol_properties(char *proto) 3132 { 3133 xmlNodePtr node; 3134 3135 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 3136 if (node != NULL) 3137 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 3138 return (node); 3139 } 3140