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( 838 handle->tree, 839 SA_LEGACY_DFSTAB, 840 TSTAMP(st.st_ctim)); 841 saunblocksigs(&old); 842 /* 843 * Safe to unlock now to allow 844 * others to run 845 */ 846 (void) lockf(lockfd, F_ULOCK, 0); 847 (void) close(lockfd); 848 } 849 legacy |= sa_get_zfs_shares(handle, "zfs"); 850 legacy |= gettransients(handle, &handle->tree); 851 } 852 } 853 } 854 return ((sa_handle_t)handle); 855 } 856 857 /* 858 * sa_fini(handle) 859 * Uninitialize the API structures including the configuration 860 * data structures and ZFS related data. 861 */ 862 863 void 864 sa_fini(sa_handle_t handle) 865 { 866 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 867 868 if (impl_handle != NULL) { 869 /* 870 * Free the config trees and any other data structures 871 * used in the handle. 872 */ 873 if (impl_handle->doc != NULL) 874 xmlFreeDoc(impl_handle->doc); 875 sa_scf_fini(impl_handle->scfhandle); 876 sa_zfs_fini(impl_handle); 877 878 /* Remove and free the entry in the global list. */ 879 remove_handle_for_root(impl_handle->tree); 880 881 /* Make sure we free the handle */ 882 free(impl_handle); 883 884 /* 885 * If this was the last handle to release, unload the 886 * plugins that were loaded. 887 */ 888 if (sa_global_handles == NULL) 889 (void) proto_plugin_fini(); 890 891 } 892 } 893 894 /* 895 * sa_get_protocols(char **protocol) 896 * Get array of protocols that are supported 897 * Returns pointer to an allocated and NULL terminated 898 * array of strings. Caller must free. 899 * This really should be determined dynamically. 900 * If there aren't any defined, return -1. 901 * Use free() to return memory. 902 */ 903 904 int 905 sa_get_protocols(char ***protocols) 906 { 907 int numproto = -1; 908 909 if (protocols != NULL) { 910 struct sa_proto_plugin *plug; 911 for (numproto = 0, plug = sap_proto_list; plug != NULL; 912 plug = plug->plugin_next) { 913 numproto++; 914 } 915 916 *protocols = calloc(numproto + 1, sizeof (char *)); 917 if (*protocols != NULL) { 918 int ret = 0; 919 for (plug = sap_proto_list; plug != NULL; 920 plug = plug->plugin_next) { 921 /* faking for now */ 922 (*protocols)[ret++] = 923 plug->plugin_ops->sa_protocol; 924 } 925 } else { 926 numproto = -1; 927 } 928 } 929 return (numproto); 930 } 931 932 /* 933 * find_group_by_name(node, group) 934 * 935 * search the XML document subtree specified by node to find the group 936 * specified by group. Searching subtree allows subgroups to be 937 * searched for. 938 */ 939 940 static xmlNodePtr 941 find_group_by_name(xmlNodePtr node, xmlChar *group) 942 { 943 xmlChar *name = NULL; 944 945 for (node = node->xmlChildrenNode; node != NULL; 946 node = node->next) { 947 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 948 /* if no groupname, return the first found */ 949 if (group == NULL) 950 break; 951 name = xmlGetProp(node, (xmlChar *)"name"); 952 if (name != NULL && xmlStrcmp(name, group) == 0) 953 break; 954 if (name != NULL) { 955 xmlFree(name); 956 name = NULL; 957 } 958 } 959 } 960 if (name != NULL) 961 xmlFree(name); 962 return (node); 963 } 964 965 /* 966 * sa_get_group(groupname) 967 * Return the "group" specified. If groupname is NULL, 968 * return the first group of the list of groups. 969 */ 970 sa_group_t 971 sa_get_group(sa_handle_t handle, char *groupname) 972 { 973 xmlNodePtr node = NULL; 974 char *subgroup = NULL; 975 char *group = NULL; 976 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 977 978 if (impl_handle != NULL && impl_handle->tree != NULL) { 979 if (groupname != NULL) { 980 group = strdup(groupname); 981 if (group != NULL) { 982 subgroup = strchr(group, '/'); 983 if (subgroup != NULL) 984 *subgroup++ = '\0'; 985 } 986 } 987 /* 988 * We want to find the, possibly, named group. If 989 * group is not NULL, then lookup the name. If it is 990 * NULL, we only do the find if groupname is also 991 * NULL. This allows lookup of the "first" group in 992 * the internal list. 993 */ 994 if (group != NULL || groupname == NULL) 995 node = find_group_by_name(impl_handle->tree, 996 (xmlChar *)group); 997 998 /* if a subgroup, find it before returning */ 999 if (subgroup != NULL && node != NULL) 1000 node = find_group_by_name(node, (xmlChar *)subgroup); 1001 } 1002 if (node != NULL && (char *)group != NULL) 1003 (void) sa_get_instance(impl_handle->scfhandle, (char *)group); 1004 if (group != NULL) 1005 free(group); 1006 return ((sa_group_t)(node)); 1007 } 1008 1009 /* 1010 * sa_get_next_group(group) 1011 * Return the "next" group after the specified group from 1012 * the internal group list. NULL if there are no more. 1013 */ 1014 sa_group_t 1015 sa_get_next_group(sa_group_t group) 1016 { 1017 xmlNodePtr ngroup = NULL; 1018 if (group != NULL) { 1019 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 1020 ngroup = ngroup->next) { 1021 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 1022 break; 1023 } 1024 } 1025 return ((sa_group_t)ngroup); 1026 } 1027 1028 /* 1029 * sa_get_share(group, sharepath) 1030 * Return the share object for the share specified. The share 1031 * must be in the specified group. Return NULL if not found. 1032 */ 1033 sa_share_t 1034 sa_get_share(sa_group_t group, char *sharepath) 1035 { 1036 xmlNodePtr node = NULL; 1037 xmlChar *path; 1038 1039 /* 1040 * For future scalability, this should end up building a cache 1041 * since it will get called regularly by the mountd and info 1042 * services. 1043 */ 1044 if (group != NULL) { 1045 for (node = ((xmlNodePtr)group)->children; node != NULL; 1046 node = node->next) { 1047 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1048 if (sharepath == NULL) { 1049 break; 1050 } else { 1051 /* is it the correct share? */ 1052 path = xmlGetProp(node, 1053 (xmlChar *)"path"); 1054 if (path != NULL && 1055 xmlStrcmp(path, 1056 (xmlChar *)sharepath) == 0) { 1057 xmlFree(path); 1058 break; 1059 } 1060 xmlFree(path); 1061 } 1062 } 1063 } 1064 } 1065 return ((sa_share_t)node); 1066 } 1067 1068 /* 1069 * sa_get_next_share(share) 1070 * Return the next share following the specified share 1071 * from the internal list of shares. Returns NULL if there 1072 * are no more shares. The list is relative to the same 1073 * group. 1074 */ 1075 sa_share_t 1076 sa_get_next_share(sa_share_t share) 1077 { 1078 xmlNodePtr node = NULL; 1079 1080 if (share != NULL) { 1081 for (node = ((xmlNodePtr)share)->next; node != NULL; 1082 node = node->next) { 1083 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1084 break; 1085 } 1086 } 1087 } 1088 return ((sa_share_t)node); 1089 } 1090 1091 /* 1092 * _sa_get_child_node(node, type) 1093 * 1094 * find the child node of the specified node that has "type". This is 1095 * used to implement several internal functions. 1096 */ 1097 1098 static xmlNodePtr 1099 _sa_get_child_node(xmlNodePtr node, xmlChar *type) 1100 { 1101 xmlNodePtr child; 1102 for (child = node->xmlChildrenNode; child != NULL; 1103 child = child->next) 1104 if (xmlStrcmp(child->name, type) == 0) 1105 return (child); 1106 return ((xmlNodePtr)NULL); 1107 } 1108 1109 /* 1110 * find_share(group, path) 1111 * 1112 * Search all the shares in the specified group for one that has the 1113 * specified path. 1114 */ 1115 1116 static sa_share_t 1117 find_share(sa_group_t group, char *sharepath) 1118 { 1119 sa_share_t share; 1120 char *path; 1121 1122 for (share = sa_get_share(group, NULL); share != NULL; 1123 share = sa_get_next_share(share)) { 1124 path = sa_get_share_attr(share, "path"); 1125 if (path != NULL && strcmp(path, sharepath) == 0) { 1126 sa_free_attr_string(path); 1127 break; 1128 } 1129 if (path != NULL) 1130 sa_free_attr_string(path); 1131 } 1132 return (share); 1133 } 1134 1135 /* 1136 * sa_get_sub_group(group) 1137 * 1138 * Get the first sub-group of group. The sa_get_next_group() function 1139 * can be used to get the rest. This is currently only used for ZFS 1140 * sub-groups but could be used to implement a more general mechanism. 1141 */ 1142 1143 sa_group_t 1144 sa_get_sub_group(sa_group_t group) 1145 { 1146 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1147 (xmlChar *)"group")); 1148 } 1149 1150 /* 1151 * sa_find_share(sharepath) 1152 * Finds a share regardless of group. In the future, this 1153 * function should utilize a cache and hash table of some kind. 1154 * The current assumption is that a path will only be shared 1155 * once. In the future, this may change as implementation of 1156 * resource names comes into being. 1157 */ 1158 sa_share_t 1159 sa_find_share(sa_handle_t handle, char *sharepath) 1160 { 1161 sa_group_t group; 1162 sa_group_t zgroup; 1163 sa_share_t share = NULL; 1164 int done = 0; 1165 1166 for (group = sa_get_group(handle, NULL); group != NULL && !done; 1167 group = sa_get_next_group(group)) { 1168 if (is_zfs_group(group)) { 1169 for (zgroup = 1170 (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1171 (xmlChar *)"group"); 1172 zgroup != NULL; 1173 zgroup = sa_get_next_group(zgroup)) { 1174 share = find_share(zgroup, sharepath); 1175 if (share != NULL) 1176 break; 1177 } 1178 } else { 1179 share = find_share(group, sharepath); 1180 } 1181 if (share != NULL) 1182 break; 1183 } 1184 return (share); 1185 } 1186 1187 /* 1188 * sa_check_path(group, path, strictness) 1189 * 1190 * check that path is a valid path relative to the group. Currently, 1191 * we are ignoring the group and checking only the NFS rules. Later, 1192 * we may want to use the group to then check against the protocols 1193 * enabled on the group. The strictness values mean: 1194 * SA_CHECK_NORMAL == only check newpath against shares that are active 1195 * SA_CHECK_STRICT == check newpath against both active shares and those 1196 * stored in the repository 1197 */ 1198 1199 int 1200 sa_check_path(sa_group_t group, char *path, int strictness) 1201 { 1202 sa_handle_t handle; 1203 1204 handle = sa_find_group_handle(group); 1205 return (validpath(handle, path, strictness)); 1206 } 1207 1208 /* 1209 * _sa_add_share(group, sharepath, persist, *error) 1210 * 1211 * common code for all types of add_share. sa_add_share() is the 1212 * public API, we also need to be able to do this when parsing legacy 1213 * files and construction of the internal configuration while 1214 * extracting config info from SMF. 1215 */ 1216 1217 sa_share_t 1218 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1219 { 1220 xmlNodePtr node = NULL; 1221 int err; 1222 1223 err = SA_OK; /* assume success */ 1224 1225 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL); 1226 if (node != NULL) { 1227 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 1228 xmlSetProp(node, (xmlChar *)"type", 1229 persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); 1230 if (persist != SA_SHARE_TRANSIENT) { 1231 /* 1232 * persistent shares come in two flavors: SMF and 1233 * ZFS. Sort this one out based on target group and 1234 * path type. Currently, only NFS is supported in the 1235 * ZFS group and it is always on. 1236 */ 1237 if (sa_group_is_zfs(group) && 1238 sa_path_is_zfs(sharepath)) { 1239 err = sa_zfs_set_sharenfs(group, sharepath, 1); 1240 } else { 1241 sa_handle_impl_t impl_handle; 1242 impl_handle = 1243 (sa_handle_impl_t)sa_find_group_handle( 1244 group); 1245 if (impl_handle != NULL) { 1246 err = sa_commit_share( 1247 impl_handle->scfhandle, group, 1248 (sa_share_t)node); 1249 } else { 1250 err = SA_SYSTEM_ERR; 1251 } 1252 } 1253 } 1254 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { 1255 /* called by the dfstab parser so could be a show */ 1256 err = SA_OK; 1257 } 1258 if (err != SA_OK) { 1259 /* 1260 * we couldn't commit to the repository so undo 1261 * our internal state to reflect reality. 1262 */ 1263 xmlUnlinkNode(node); 1264 xmlFreeNode(node); 1265 node = NULL; 1266 } 1267 } else { 1268 err = SA_NO_MEMORY; 1269 } 1270 if (error != NULL) 1271 *error = err; 1272 return (node); 1273 } 1274 1275 /* 1276 * sa_add_share(group, sharepath, persist, *error) 1277 * 1278 * Add a new share object to the specified group. The share will 1279 * have the specified sharepath and will only be constructed if 1280 * it is a valid path to be shared. NULL is returned on error 1281 * and a detailed error value will be returned via the error 1282 * pointer. 1283 */ 1284 sa_share_t 1285 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1286 { 1287 xmlNodePtr node = NULL; 1288 sa_share_t dup; 1289 int strictness = SA_CHECK_NORMAL; 1290 sa_handle_t handle; 1291 1292 /* 1293 * If the share is to be permanent, use strict checking so a 1294 * bad config doesn't get created. Transient shares only need 1295 * to check against the currently active 1296 * shares. SA_SHARE_PARSER is a modifier used internally to 1297 * indicate that we are being called by the dfstab parser and 1298 * that we need strict checking in all cases. Normally persist 1299 * is in integer value but SA_SHARE_PARSER may be or'd into 1300 * it as an override. 1301 */ 1302 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 1303 strictness = SA_CHECK_STRICT; 1304 1305 handle = sa_find_group_handle(group); 1306 1307 if ((dup = sa_find_share(handle, sharepath)) == NULL && 1308 (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) { 1309 node = _sa_add_share(group, sharepath, persist, error); 1310 } 1311 if (dup != NULL) 1312 *error = SA_DUPLICATE_NAME; 1313 1314 return ((sa_share_t)node); 1315 } 1316 1317 /* 1318 * sa_enable_share(share, protocol) 1319 * Enable the specified share to the specified protocol. 1320 * If protocol is NULL, then all protocols. 1321 */ 1322 int 1323 sa_enable_share(sa_share_t share, char *protocol) 1324 { 1325 char *sharepath; 1326 struct stat st; 1327 int err = 0; 1328 1329 sharepath = sa_get_share_attr(share, "path"); 1330 if (stat(sharepath, &st) < 0) { 1331 err = SA_NO_SUCH_PATH; 1332 } else { 1333 /* tell the server about the share */ 1334 if (protocol != NULL) { 1335 /* lookup protocol specific handler */ 1336 err = sa_proto_share(protocol, share); 1337 if (err == SA_OK) 1338 (void) sa_set_share_attr(share, "shared", 1339 "true"); 1340 } else { 1341 /* 1342 * Tell all protocols. Only NFS for now but 1343 * SMB is coming. 1344 */ 1345 err = sa_proto_share("nfs", share); 1346 (void) sa_set_share_attr(share, "shared", "true"); 1347 } 1348 } 1349 if (sharepath != NULL) 1350 sa_free_attr_string(sharepath); 1351 return (err); 1352 } 1353 1354 /* 1355 * sa_disable_share(share, protocol) 1356 * Disable the specified share to the specified protocol. 1357 * If protocol is NULL, then all protocols. 1358 */ 1359 int 1360 sa_disable_share(sa_share_t share, char *protocol) 1361 { 1362 char *path; 1363 char *shared; 1364 int ret = SA_OK; 1365 1366 path = sa_get_share_attr(share, "path"); 1367 shared = sa_get_share_attr(share, "shared"); 1368 1369 if (protocol != NULL) { 1370 ret = sa_proto_unshare(share, protocol, path); 1371 } else { 1372 /* need to do all protocols */ 1373 ret = sa_proto_unshare(share, "nfs", path); 1374 } 1375 if (ret == SA_OK) 1376 (void) sa_set_share_attr(share, "shared", NULL); 1377 if (path != NULL) 1378 sa_free_attr_string(path); 1379 if (shared != NULL) 1380 sa_free_attr_string(shared); 1381 return (ret); 1382 } 1383 1384 /* 1385 * sa_remove_share(share) 1386 * 1387 * remove the specified share from its containing group. 1388 * Remove from the SMF or ZFS configuration space. 1389 */ 1390 1391 int 1392 sa_remove_share(sa_share_t share) 1393 { 1394 sa_group_t group; 1395 int ret = SA_OK; 1396 char *type; 1397 int transient = 0; 1398 char *groupname; 1399 char *zfs; 1400 1401 type = sa_get_share_attr(share, "type"); 1402 group = sa_get_parent_group(share); 1403 zfs = sa_get_group_attr(group, "zfs"); 1404 groupname = sa_get_group_attr(group, "name"); 1405 if (type != NULL && strcmp(type, "persist") != 0) 1406 transient = 1; 1407 if (type != NULL) 1408 sa_free_attr_string(type); 1409 1410 /* remove the node from its group then free the memory */ 1411 1412 /* 1413 * need to test if "busy" 1414 */ 1415 /* only do SMF action if permanent */ 1416 if (!transient || zfs != NULL) { 1417 /* remove from legacy dfstab as well as possible SMF */ 1418 ret = sa_delete_legacy(share); 1419 if (ret == SA_OK) { 1420 if (!sa_group_is_zfs(group)) { 1421 sa_handle_impl_t impl_handle; 1422 impl_handle = (sa_handle_impl_t) 1423 sa_find_group_handle(group); 1424 if (impl_handle != NULL) { 1425 ret = sa_delete_share( 1426 impl_handle->scfhandle, group, 1427 share); 1428 } else { 1429 ret = SA_SYSTEM_ERR; 1430 } 1431 } else { 1432 char *sharepath = sa_get_share_attr(share, 1433 "path"); 1434 if (sharepath != NULL) { 1435 ret = sa_zfs_set_sharenfs(group, 1436 sharepath, 0); 1437 sa_free_attr_string(sharepath); 1438 } 1439 } 1440 } 1441 } 1442 if (groupname != NULL) 1443 sa_free_attr_string(groupname); 1444 if (zfs != NULL) 1445 sa_free_attr_string(zfs); 1446 1447 xmlUnlinkNode((xmlNodePtr)share); 1448 xmlFreeNode((xmlNodePtr)share); 1449 return (ret); 1450 } 1451 1452 /* 1453 * sa_move_share(group, share) 1454 * 1455 * move the specified share to the specified group. Update SMF 1456 * appropriately. 1457 */ 1458 1459 int 1460 sa_move_share(sa_group_t group, sa_share_t share) 1461 { 1462 sa_group_t oldgroup; 1463 int ret = SA_OK; 1464 1465 /* remove the node from its group then free the memory */ 1466 1467 oldgroup = sa_get_parent_group(share); 1468 if (oldgroup != group) { 1469 sa_handle_impl_t impl_handle; 1470 xmlUnlinkNode((xmlNodePtr)share); 1471 /* 1472 * now that the share isn't in its old group, add to 1473 * the new one 1474 */ 1475 xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 1476 /* need to deal with SMF */ 1477 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1478 if (impl_handle != NULL) { 1479 /* 1480 * need to remove from old group first and then add to 1481 * new group. Ideally, we would do the other order but 1482 * need to avoid having the share in two groups at the 1483 * same time. 1484 */ 1485 ret = sa_delete_share(impl_handle->scfhandle, oldgroup, 1486 share); 1487 if (ret == SA_OK) 1488 ret = sa_commit_share(impl_handle->scfhandle, 1489 group, share); 1490 } else { 1491 ret = SA_SYSTEM_ERR; 1492 } 1493 } 1494 return (ret); 1495 } 1496 1497 /* 1498 * sa_get_parent_group(share) 1499 * 1500 * Return the containg group for the share. If a group was actually 1501 * passed in, we don't want a parent so return NULL. 1502 */ 1503 1504 sa_group_t 1505 sa_get_parent_group(sa_share_t share) 1506 { 1507 xmlNodePtr node = NULL; 1508 if (share != NULL) { 1509 node = ((xmlNodePtr)share)->parent; 1510 /* 1511 * make sure parent is a group and not sharecfg since 1512 * we may be cheating and passing in a group. 1513 * Eventually, groups of groups might come into being. 1514 */ 1515 if (node == NULL || 1516 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 1517 node = NULL; 1518 } 1519 return ((sa_group_t)node); 1520 } 1521 1522 /* 1523 * _sa_create_group(impl_handle, groupname) 1524 * 1525 * Create a group in the document. The caller will need to deal with 1526 * configuration store and activation. 1527 */ 1528 1529 sa_group_t 1530 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname) 1531 { 1532 xmlNodePtr node = NULL; 1533 1534 if (sa_valid_group_name(groupname)) { 1535 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group", 1536 NULL); 1537 if (node != NULL) { 1538 xmlSetProp(node, (xmlChar *)"name", 1539 (xmlChar *)groupname); 1540 xmlSetProp(node, (xmlChar *)"state", 1541 (xmlChar *)"enabled"); 1542 } 1543 } 1544 return ((sa_group_t)node); 1545 } 1546 1547 /* 1548 * _sa_create_zfs_group(group, groupname) 1549 * 1550 * Create a ZFS subgroup under the specified group. This may 1551 * eventually form the basis of general sub-groups, but is currently 1552 * restricted to ZFS. 1553 */ 1554 sa_group_t 1555 _sa_create_zfs_group(sa_group_t group, char *groupname) 1556 { 1557 xmlNodePtr node = NULL; 1558 1559 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL); 1560 if (node != NULL) { 1561 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1562 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1563 } 1564 1565 return ((sa_group_t)node); 1566 } 1567 1568 /* 1569 * sa_create_group(groupname, *error) 1570 * 1571 * Create a new group with groupname. Need to validate that it is a 1572 * legal name for SMF and the construct the SMF service instance of 1573 * svc:/network/shares/group to implement the group. All necessary 1574 * operational properties must be added to the group at this point 1575 * (via the SMF transaction model). 1576 */ 1577 sa_group_t 1578 sa_create_group(sa_handle_t handle, char *groupname, int *error) 1579 { 1580 xmlNodePtr node = NULL; 1581 sa_group_t group; 1582 int ret; 1583 char rbacstr[SA_STRSIZE]; 1584 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 1585 1586 ret = SA_OK; 1587 1588 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 1589 ret = SA_SYSTEM_ERR; 1590 goto err; 1591 } 1592 1593 group = sa_get_group(handle, groupname); 1594 if (group != NULL) { 1595 ret = SA_DUPLICATE_NAME; 1596 } else { 1597 if (sa_valid_group_name(groupname)) { 1598 node = xmlNewChild(impl_handle->tree, NULL, 1599 (xmlChar *)"group", NULL); 1600 if (node != NULL) { 1601 xmlSetProp(node, (xmlChar *)"name", 1602 (xmlChar *)groupname); 1603 /* default to the group being enabled */ 1604 xmlSetProp(node, (xmlChar *)"state", 1605 (xmlChar *)"enabled"); 1606 ret = sa_create_instance(impl_handle->scfhandle, 1607 groupname); 1608 if (ret == SA_OK) { 1609 ret = sa_start_transaction( 1610 impl_handle->scfhandle, 1611 "operation"); 1612 } 1613 if (ret == SA_OK) { 1614 ret = sa_set_property( 1615 impl_handle->scfhandle, 1616 "state", "enabled"); 1617 if (ret == SA_OK) { 1618 ret = sa_end_transaction( 1619 impl_handle->scfhandle); 1620 } else { 1621 sa_abort_transaction( 1622 impl_handle->scfhandle); 1623 } 1624 } 1625 if (ret == SA_OK) { 1626 /* initialize the RBAC strings */ 1627 ret = sa_start_transaction( 1628 impl_handle->scfhandle, 1629 "general"); 1630 if (ret == SA_OK) { 1631 (void) snprintf(rbacstr, 1632 sizeof (rbacstr), "%s.%s", 1633 SA_RBAC_MANAGE, groupname); 1634 ret = sa_set_property( 1635 impl_handle->scfhandle, 1636 "action_authorization", 1637 rbacstr); 1638 } 1639 if (ret == SA_OK) { 1640 (void) snprintf(rbacstr, 1641 sizeof (rbacstr), "%s.%s", 1642 SA_RBAC_VALUE, groupname); 1643 ret = sa_set_property( 1644 impl_handle->scfhandle, 1645 "value_authorization", 1646 rbacstr); 1647 } 1648 if (ret == SA_OK) { 1649 ret = sa_end_transaction( 1650 impl_handle->scfhandle); 1651 } else { 1652 sa_abort_transaction( 1653 impl_handle->scfhandle); 1654 } 1655 } 1656 if (ret != SA_OK) { 1657 /* 1658 * Couldn't commit the group 1659 * so we need to undo 1660 * internally. 1661 */ 1662 xmlUnlinkNode(node); 1663 xmlFreeNode(node); 1664 node = NULL; 1665 } 1666 } else { 1667 ret = SA_NO_MEMORY; 1668 } 1669 } else { 1670 ret = SA_INVALID_NAME; 1671 } 1672 } 1673 err: 1674 if (error != NULL) 1675 *error = ret; 1676 return ((sa_group_t)node); 1677 } 1678 1679 /* 1680 * sa_remove_group(group) 1681 * 1682 * Remove the specified group. This deletes from the SMF repository. 1683 * All property groups and properties are removed. 1684 */ 1685 1686 int 1687 sa_remove_group(sa_group_t group) 1688 { 1689 char *name; 1690 int ret = SA_OK; 1691 sa_handle_impl_t impl_handle; 1692 1693 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1694 if (impl_handle != NULL) { 1695 name = sa_get_group_attr(group, "name"); 1696 if (name != NULL) { 1697 ret = sa_delete_instance(impl_handle->scfhandle, name); 1698 sa_free_attr_string(name); 1699 } 1700 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 1701 xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 1702 } else { 1703 ret = SA_SYSTEM_ERR; 1704 } 1705 return (ret); 1706 } 1707 1708 /* 1709 * sa_update_config() 1710 * 1711 * Used to update legacy files that need to be updated in bulk 1712 * Currently, this is a placeholder and will go away in a future 1713 * release. 1714 */ 1715 1716 int 1717 sa_update_config(sa_handle_t handle) 1718 { 1719 /* 1720 * do legacy files first so we can tell when they change. 1721 * This will go away when we start updating individual records 1722 * rather than the whole file. 1723 */ 1724 update_legacy_config(handle); 1725 return (SA_OK); 1726 } 1727 1728 /* 1729 * get_node_attr(node, tag) 1730 * 1731 * Get the speficied tag(attribute) if it exists on the node. This is 1732 * used internally by a number of attribute oriented functions. 1733 */ 1734 1735 static char * 1736 get_node_attr(void *nodehdl, char *tag) 1737 { 1738 xmlNodePtr node = (xmlNodePtr)nodehdl; 1739 xmlChar *name = NULL; 1740 1741 if (node != NULL) 1742 name = xmlGetProp(node, (xmlChar *)tag); 1743 return ((char *)name); 1744 } 1745 1746 /* 1747 * get_node_attr(node, tag) 1748 * 1749 * Set the speficied tag(attribute) to the specified value This is 1750 * used internally by a number of attribute oriented functions. It 1751 * doesn't update the repository, only the internal document state. 1752 */ 1753 1754 void 1755 set_node_attr(void *nodehdl, char *tag, char *value) 1756 { 1757 xmlNodePtr node = (xmlNodePtr)nodehdl; 1758 if (node != NULL && tag != NULL) { 1759 if (value != NULL) 1760 xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 1761 else 1762 xmlUnsetProp(node, (xmlChar *)tag); 1763 } 1764 } 1765 1766 /* 1767 * sa_get_group_attr(group, tag) 1768 * 1769 * Get the specied attribute, if defined, for the group. 1770 */ 1771 1772 char * 1773 sa_get_group_attr(sa_group_t group, char *tag) 1774 { 1775 return (get_node_attr((void *)group, tag)); 1776 } 1777 1778 /* 1779 * sa_set_group_attr(group, tag, value) 1780 * 1781 * set the specified tag/attribute on the group using value as its 1782 * value. 1783 * 1784 * This will result in setting the property in the SMF repository as 1785 * well as in the internal document. 1786 */ 1787 1788 int 1789 sa_set_group_attr(sa_group_t group, char *tag, char *value) 1790 { 1791 int ret; 1792 char *groupname; 1793 sa_handle_impl_t impl_handle; 1794 1795 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1796 if (impl_handle != NULL) { 1797 groupname = sa_get_group_attr(group, "name"); 1798 ret = sa_get_instance(impl_handle->scfhandle, groupname); 1799 if (ret == SA_OK) { 1800 set_node_attr((void *)group, tag, value); 1801 ret = sa_start_transaction(impl_handle->scfhandle, 1802 "operation"); 1803 if (ret == SA_OK) { 1804 ret = sa_set_property(impl_handle->scfhandle, 1805 tag, value); 1806 if (ret == SA_OK) 1807 (void) sa_end_transaction( 1808 impl_handle->scfhandle); 1809 else 1810 sa_abort_transaction( 1811 impl_handle->scfhandle); 1812 } 1813 } 1814 if (groupname != NULL) 1815 sa_free_attr_string(groupname); 1816 } else { 1817 ret = SA_SYSTEM_ERR; 1818 } 1819 return (ret); 1820 } 1821 1822 /* 1823 * sa_get_share_attr(share, tag) 1824 * 1825 * Return the value of the tag/attribute set on the specified 1826 * share. Returns NULL if the tag doesn't exist. 1827 */ 1828 1829 char * 1830 sa_get_share_attr(sa_share_t share, char *tag) 1831 { 1832 return (get_node_attr((void *)share, tag)); 1833 } 1834 1835 /* 1836 * sa_get_resource(group, resource) 1837 * 1838 * Search all the shares in the speified group for a share with a 1839 * resource name matching the one specified. 1840 * 1841 * In the future, it may be advantageous to allow group to be NULL and 1842 * search all groups but that isn't needed at present. 1843 */ 1844 1845 sa_share_t 1846 sa_get_resource(sa_group_t group, char *resource) 1847 { 1848 sa_share_t share = NULL; 1849 char *name = NULL; 1850 1851 if (resource != NULL) { 1852 for (share = sa_get_share(group, NULL); share != NULL; 1853 share = sa_get_next_share(share)) { 1854 name = sa_get_share_attr(share, "resource"); 1855 if (name != NULL) { 1856 if (strcmp(name, resource) == 0) 1857 break; 1858 sa_free_attr_string(name); 1859 name = NULL; 1860 } 1861 } 1862 if (name != NULL) 1863 sa_free_attr_string(name); 1864 } 1865 return ((sa_share_t)share); 1866 } 1867 1868 /* 1869 * _sa_set_share_description(share, description) 1870 * 1871 * Add a description tag with text contents to the specified share. 1872 * A separate XML tag is used rather than a property. 1873 */ 1874 1875 xmlNodePtr 1876 _sa_set_share_description(sa_share_t share, char *content) 1877 { 1878 xmlNodePtr node; 1879 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description", 1880 NULL); 1881 xmlNodeSetContent(node, (xmlChar *)content); 1882 return (node); 1883 } 1884 1885 /* 1886 * sa_set_share_attr(share, tag, value) 1887 * 1888 * Set the share attribute specified by tag to the specified value. In 1889 * the case of "resource", enforce a no duplicates in a group rule. If 1890 * the share is not transient, commit the changes to the repository 1891 * else just update the share internally. 1892 */ 1893 1894 int 1895 sa_set_share_attr(sa_share_t share, char *tag, char *value) 1896 { 1897 sa_group_t group; 1898 sa_share_t resource; 1899 int ret = SA_OK; 1900 1901 group = sa_get_parent_group(share); 1902 1903 /* 1904 * There are some attributes that may have specific 1905 * restrictions on them. Initially, only "resource" has 1906 * special meaning that needs to be checked. Only one instance 1907 * of a resource name may exist within a group. 1908 */ 1909 1910 if (strcmp(tag, "resource") == 0) { 1911 resource = sa_get_resource(group, value); 1912 if (resource != share && resource != NULL) 1913 ret = SA_DUPLICATE_NAME; 1914 } 1915 if (ret == SA_OK) { 1916 set_node_attr((void *)share, tag, value); 1917 if (group != NULL) { 1918 char *type; 1919 /* we can probably optimize this some */ 1920 type = sa_get_share_attr(share, "type"); 1921 if (type == NULL || strcmp(type, "transient") != 0) { 1922 sa_handle_impl_t impl_handle; 1923 impl_handle = 1924 (sa_handle_impl_t)sa_find_group_handle( 1925 group); 1926 if (impl_handle != NULL) { 1927 ret = sa_commit_share( 1928 impl_handle->scfhandle, group, 1929 share); 1930 } else { 1931 ret = SA_SYSTEM_ERR; 1932 } 1933 } 1934 if (type != NULL) 1935 sa_free_attr_string(type); 1936 } 1937 } 1938 return (ret); 1939 } 1940 1941 /* 1942 * sa_get_property_attr(prop, tag) 1943 * 1944 * Get the value of the specified property attribute. Standard 1945 * attributes are "type" and "value". 1946 */ 1947 1948 char * 1949 sa_get_property_attr(sa_property_t prop, char *tag) 1950 { 1951 return (get_node_attr((void *)prop, tag)); 1952 } 1953 1954 /* 1955 * sa_get_optionset_attr(prop, tag) 1956 * 1957 * Get the value of the specified property attribute. Standard 1958 * attribute is "type". 1959 */ 1960 1961 char * 1962 sa_get_optionset_attr(sa_property_t optionset, char *tag) 1963 { 1964 return (get_node_attr((void *)optionset, tag)); 1965 1966 } 1967 1968 /* 1969 * sa_set_optionset_attr(optionset, tag, value) 1970 * 1971 * Set the specified attribute(tag) to the specified value on the 1972 * optionset. 1973 */ 1974 1975 void 1976 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 1977 { 1978 set_node_attr((void *)optionset, tag, value); 1979 } 1980 1981 /* 1982 * sa_free_attr_string(string) 1983 * 1984 * Free the string that was returned in one of the sa_get_*_attr() 1985 * functions. 1986 */ 1987 1988 void 1989 sa_free_attr_string(char *string) 1990 { 1991 xmlFree((xmlChar *)string); 1992 } 1993 1994 /* 1995 * sa_get_optionset(group, proto) 1996 * 1997 * Return the optionset, if it exists, that is associated with the 1998 * specified protocol. 1999 */ 2000 2001 sa_optionset_t 2002 sa_get_optionset(void *group, char *proto) 2003 { 2004 xmlNodePtr node; 2005 xmlChar *value = NULL; 2006 2007 for (node = ((xmlNodePtr)group)->children; node != NULL; 2008 node = node->next) { 2009 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2010 value = xmlGetProp(node, (xmlChar *)"type"); 2011 if (proto != NULL) { 2012 if (value != NULL && 2013 xmlStrcmp(value, (xmlChar *)proto) == 0) { 2014 break; 2015 } 2016 if (value != NULL) { 2017 xmlFree(value); 2018 value = NULL; 2019 } 2020 } else { 2021 break; 2022 } 2023 } 2024 } 2025 if (value != NULL) 2026 xmlFree(value); 2027 return ((sa_optionset_t)node); 2028 } 2029 2030 /* 2031 * sa_get_next_optionset(optionset) 2032 * 2033 * Return the next optionset in the group. NULL if this was the last. 2034 */ 2035 2036 sa_optionset_t 2037 sa_get_next_optionset(sa_optionset_t optionset) 2038 { 2039 xmlNodePtr node; 2040 2041 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 2042 node = node->next) { 2043 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2044 break; 2045 } 2046 } 2047 return ((sa_optionset_t)node); 2048 } 2049 2050 /* 2051 * sa_get_security(group, sectype, proto) 2052 * 2053 * Return the security optionset. The internal name is a hold over 2054 * from the implementation and will be changed before the API is 2055 * finalized. This is really a named optionset that can be negotiated 2056 * as a group of properties (like NFS security options). 2057 */ 2058 2059 sa_security_t 2060 sa_get_security(sa_group_t group, char *sectype, char *proto) 2061 { 2062 xmlNodePtr node; 2063 xmlChar *value = NULL; 2064 2065 for (node = ((xmlNodePtr)group)->children; node != NULL; 2066 node = node->next) { 2067 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2068 if (proto != NULL) { 2069 value = xmlGetProp(node, (xmlChar *)"type"); 2070 if (value == NULL || 2071 (value != NULL && 2072 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 2073 /* it doesn't match so continue */ 2074 xmlFree(value); 2075 value = NULL; 2076 continue; 2077 } 2078 } 2079 if (value != NULL) { 2080 xmlFree(value); 2081 value = NULL; 2082 } 2083 /* potential match */ 2084 if (sectype != NULL) { 2085 value = xmlGetProp(node, (xmlChar *)"sectype"); 2086 if (value != NULL && 2087 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 2088 break; 2089 } 2090 } else { 2091 break; 2092 } 2093 } 2094 if (value != NULL) { 2095 xmlFree(value); 2096 value = NULL; 2097 } 2098 } 2099 if (value != NULL) 2100 xmlFree(value); 2101 return ((sa_security_t)node); 2102 } 2103 2104 /* 2105 * sa_get_next_security(security) 2106 * 2107 * Get the next security optionset if one exists. 2108 */ 2109 2110 sa_security_t 2111 sa_get_next_security(sa_security_t security) 2112 { 2113 xmlNodePtr node; 2114 2115 for (node = ((xmlNodePtr)security)->next; node != NULL; 2116 node = node->next) { 2117 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2118 break; 2119 } 2120 } 2121 return ((sa_security_t)node); 2122 } 2123 2124 /* 2125 * sa_get_property(optionset, prop) 2126 * 2127 * Get the property object with the name specified in prop from the 2128 * optionset. 2129 */ 2130 2131 sa_property_t 2132 sa_get_property(sa_optionset_t optionset, char *prop) 2133 { 2134 xmlNodePtr node = (xmlNodePtr)optionset; 2135 xmlChar *value = NULL; 2136 2137 if (optionset == NULL) 2138 return (NULL); 2139 2140 for (node = node->children; node != NULL; 2141 node = node->next) { 2142 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2143 if (prop == NULL) 2144 break; 2145 value = xmlGetProp(node, (xmlChar *)"type"); 2146 if (value != NULL && 2147 xmlStrcmp(value, (xmlChar *)prop) == 0) { 2148 break; 2149 } 2150 if (value != NULL) { 2151 xmlFree(value); 2152 value = NULL; 2153 } 2154 } 2155 } 2156 if (value != NULL) 2157 xmlFree(value); 2158 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2159 /* 2160 * avoid a non option node -- it is possible to be a 2161 * text node 2162 */ 2163 node = NULL; 2164 } 2165 return ((sa_property_t)node); 2166 } 2167 2168 /* 2169 * sa_get_next_property(property) 2170 * 2171 * Get the next property following the specified property. NULL if 2172 * this was the last. 2173 */ 2174 2175 sa_property_t 2176 sa_get_next_property(sa_property_t property) 2177 { 2178 xmlNodePtr node; 2179 2180 for (node = ((xmlNodePtr)property)->next; node != NULL; 2181 node = node->next) { 2182 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2183 break; 2184 } 2185 } 2186 return ((sa_property_t)node); 2187 } 2188 2189 /* 2190 * sa_set_share_description(share, content) 2191 * 2192 * Set the description of share to content. 2193 */ 2194 2195 int 2196 sa_set_share_description(sa_share_t share, char *content) 2197 { 2198 xmlNodePtr node; 2199 sa_group_t group; 2200 int ret = SA_OK; 2201 2202 for (node = ((xmlNodePtr)share)->children; node != NULL; 2203 node = node->next) { 2204 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2205 break; 2206 } 2207 } 2208 group = sa_get_parent_group(share); 2209 /* no existing description but want to add */ 2210 if (node == NULL && content != NULL) { 2211 /* add a description */ 2212 node = _sa_set_share_description(share, content); 2213 } else if (node != NULL && content != NULL) { 2214 /* update a description */ 2215 xmlNodeSetContent(node, (xmlChar *)content); 2216 } else if (node != NULL && content == NULL) { 2217 /* remove an existing description */ 2218 xmlUnlinkNode(node); 2219 xmlFreeNode(node); 2220 } 2221 if (group != NULL && is_persistent((sa_group_t)share)) { 2222 sa_handle_impl_t impl_handle; 2223 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2224 if (impl_handle != NULL) { 2225 ret = sa_commit_share(impl_handle->scfhandle, group, 2226 share); 2227 } else { 2228 ret = SA_SYSTEM_ERR; 2229 } 2230 } 2231 return (ret); 2232 } 2233 2234 /* 2235 * fixproblemchars(string) 2236 * 2237 * don't want any newline or tab characters in the text since these 2238 * could break display of data and legacy file formats. 2239 */ 2240 static void 2241 fixproblemchars(char *str) 2242 { 2243 int c; 2244 for (c = *str; c != '\0'; c = *++str) { 2245 if (c == '\t' || c == '\n') 2246 *str = ' '; 2247 else if (c == '"') 2248 *str = '\''; 2249 } 2250 } 2251 2252 /* 2253 * sa_get_share_description(share) 2254 * 2255 * Return the description text for the specified share if it 2256 * exists. NULL if no description exists. 2257 */ 2258 2259 char * 2260 sa_get_share_description(sa_share_t share) 2261 { 2262 xmlChar *description = NULL; 2263 xmlNodePtr node; 2264 2265 for (node = ((xmlNodePtr)share)->children; node != NULL; 2266 node = node->next) { 2267 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2268 break; 2269 } 2270 } 2271 if (node != NULL) { 2272 description = xmlNodeGetContent((xmlNodePtr)share); 2273 fixproblemchars((char *)description); 2274 } 2275 return ((char *)description); 2276 } 2277 2278 /* 2279 * sa_free(share_description(description) 2280 * 2281 * Free the description string. 2282 */ 2283 2284 void 2285 sa_free_share_description(char *description) 2286 { 2287 xmlFree((xmlChar *)description); 2288 } 2289 2290 /* 2291 * sa_create_optionset(group, proto) 2292 * 2293 * Create an optionset for the specified protocol in the specied 2294 * group. This is manifested as a property group within SMF. 2295 */ 2296 2297 sa_optionset_t 2298 sa_create_optionset(sa_group_t group, char *proto) 2299 { 2300 sa_optionset_t optionset; 2301 sa_group_t parent = group; 2302 2303 optionset = sa_get_optionset(group, proto); 2304 if (optionset != NULL) { 2305 /* can't have a duplicate protocol */ 2306 optionset = NULL; 2307 } else { 2308 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 2309 NULL, (xmlChar *)"optionset", NULL); 2310 /* 2311 * only put to repository if on a group and we were 2312 * able to create an optionset. 2313 */ 2314 if (optionset != NULL) { 2315 char oname[SA_STRSIZE]; 2316 char *groupname; 2317 char *id = NULL; 2318 2319 if (sa_is_share(group)) 2320 parent = sa_get_parent_group((sa_share_t)group); 2321 2322 sa_set_optionset_attr(optionset, "type", proto); 2323 2324 if (sa_is_share(group)) { 2325 id = sa_get_share_attr((sa_share_t)group, "id"); 2326 } 2327 (void) sa_optionset_name(optionset, oname, 2328 sizeof (oname), id); 2329 groupname = sa_get_group_attr(parent, "name"); 2330 if (groupname != NULL && is_persistent(group)) { 2331 sa_handle_impl_t impl_handle; 2332 impl_handle = (sa_handle_impl_t) 2333 sa_find_group_handle(group); 2334 assert(impl_handle != NULL); 2335 if (impl_handle != NULL) { 2336 (void) sa_get_instance( 2337 impl_handle->scfhandle, 2338 groupname); 2339 (void) sa_create_pgroup( 2340 impl_handle->scfhandle, oname); 2341 } 2342 } 2343 if (groupname != NULL) 2344 sa_free_attr_string(groupname); 2345 if (id != NULL) 2346 sa_free_attr_string(id); 2347 } 2348 } 2349 return (optionset); 2350 } 2351 2352 /* 2353 * sa_get_property_parent(property) 2354 * 2355 * Given a property, return the object it is a property of. This will 2356 * be an optionset of some type. 2357 */ 2358 2359 static sa_optionset_t 2360 sa_get_property_parent(sa_property_t property) 2361 { 2362 xmlNodePtr node = NULL; 2363 2364 if (property != NULL) 2365 node = ((xmlNodePtr)property)->parent; 2366 return ((sa_optionset_t)node); 2367 } 2368 2369 /* 2370 * sa_get_optionset_parent(optionset) 2371 * 2372 * Return the parent of the specified optionset. This could be a group 2373 * or a share. 2374 */ 2375 2376 static sa_group_t 2377 sa_get_optionset_parent(sa_optionset_t optionset) 2378 { 2379 xmlNodePtr node = NULL; 2380 2381 if (optionset != NULL) 2382 node = ((xmlNodePtr)optionset)->parent; 2383 return ((sa_group_t)node); 2384 } 2385 2386 /* 2387 * zfs_needs_update(share) 2388 * 2389 * In order to avoid making multiple updates to a ZFS share when 2390 * setting properties, the share attribute "changed" will be set to 2391 * true when a property is added or modifed. When done adding 2392 * properties, we can then detect that an update is needed. We then 2393 * clear the state here to detect additional changes. 2394 */ 2395 2396 static int 2397 zfs_needs_update(sa_share_t share) 2398 { 2399 char *attr; 2400 int result = 0; 2401 2402 attr = sa_get_share_attr(share, "changed"); 2403 if (attr != NULL) { 2404 sa_free_attr_string(attr); 2405 result = 1; 2406 } 2407 set_node_attr((void *)share, "changed", NULL); 2408 return (result); 2409 } 2410 2411 /* 2412 * zfs_set_update(share) 2413 * 2414 * Set the changed attribute of the share to true. 2415 */ 2416 2417 static void 2418 zfs_set_update(sa_share_t share) 2419 { 2420 set_node_attr((void *)share, "changed", "true"); 2421 } 2422 2423 /* 2424 * sa_commit_properties(optionset, clear) 2425 * 2426 * Check if SMF or ZFS config and either update or abort the pending 2427 * changes. 2428 */ 2429 2430 int 2431 sa_commit_properties(sa_optionset_t optionset, int clear) 2432 { 2433 sa_group_t group; 2434 sa_group_t parent; 2435 int zfs = 0; 2436 int needsupdate = 0; 2437 int ret = SA_OK; 2438 sa_handle_impl_t impl_handle; 2439 2440 group = sa_get_optionset_parent(optionset); 2441 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2442 /* only update ZFS if on a share */ 2443 parent = sa_get_parent_group(group); 2444 zfs++; 2445 if (parent != NULL && is_zfs_group(parent)) 2446 needsupdate = zfs_needs_update(group); 2447 else 2448 zfs = 0; 2449 } 2450 if (zfs) { 2451 if (!clear && needsupdate) 2452 ret = sa_zfs_update((sa_share_t)group); 2453 } else { 2454 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2455 if (impl_handle != NULL) { 2456 if (clear) { 2457 (void) sa_abort_transaction( 2458 impl_handle->scfhandle); 2459 } else { 2460 ret = sa_end_transaction( 2461 impl_handle->scfhandle); 2462 } 2463 } else { 2464 ret = SA_SYSTEM_ERR; 2465 } 2466 } 2467 return (ret); 2468 } 2469 2470 /* 2471 * sa_destroy_optionset(optionset) 2472 * 2473 * Remove the optionset from its group. Update the repostory to 2474 * reflect this change. 2475 */ 2476 2477 int 2478 sa_destroy_optionset(sa_optionset_t optionset) 2479 { 2480 char name[SA_STRSIZE]; 2481 int len; 2482 int ret; 2483 char *id = NULL; 2484 sa_group_t group; 2485 int ispersist = 1; 2486 2487 /* now delete the prop group */ 2488 group = sa_get_optionset_parent(optionset); 2489 if (group != NULL && sa_is_share(group)) { 2490 ispersist = is_persistent(group); 2491 id = sa_get_share_attr((sa_share_t)group, "id"); 2492 } 2493 if (ispersist) { 2494 sa_handle_impl_t impl_handle; 2495 len = sa_optionset_name(optionset, name, sizeof (name), id); 2496 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2497 if (impl_handle != NULL) { 2498 if (len > 0) { 2499 ret = sa_delete_pgroup(impl_handle->scfhandle, 2500 name); 2501 } 2502 } else { 2503 ret = SA_SYSTEM_ERR; 2504 } 2505 } 2506 xmlUnlinkNode((xmlNodePtr)optionset); 2507 xmlFreeNode((xmlNodePtr)optionset); 2508 if (id != NULL) 2509 sa_free_attr_string(id); 2510 return (ret); 2511 } 2512 2513 /* private to the implementation */ 2514 int 2515 _sa_remove_optionset(sa_optionset_t optionset) 2516 { 2517 int ret = SA_OK; 2518 2519 xmlUnlinkNode((xmlNodePtr)optionset); 2520 xmlFreeNode((xmlNodePtr)optionset); 2521 return (ret); 2522 } 2523 2524 /* 2525 * sa_create_security(group, sectype, proto) 2526 * 2527 * Create a security optionset (one that has a type name and a 2528 * proto). Security is left over from a pure NFS implementation. The 2529 * naming will change in the future when the API is released. 2530 */ 2531 sa_security_t 2532 sa_create_security(sa_group_t group, char *sectype, char *proto) 2533 { 2534 sa_security_t security; 2535 char *id = NULL; 2536 sa_group_t parent; 2537 char *groupname = NULL; 2538 2539 if (group != NULL && sa_is_share(group)) { 2540 id = sa_get_share_attr((sa_share_t)group, "id"); 2541 parent = sa_get_parent_group(group); 2542 if (parent != NULL) 2543 groupname = sa_get_group_attr(parent, "name"); 2544 } else if (group != NULL) { 2545 groupname = sa_get_group_attr(group, "name"); 2546 } 2547 2548 security = sa_get_security(group, sectype, proto); 2549 if (security != NULL) { 2550 /* can't have a duplicate security option */ 2551 security = NULL; 2552 } else { 2553 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2554 NULL, (xmlChar *)"security", NULL); 2555 if (security != NULL) { 2556 char oname[SA_STRSIZE]; 2557 sa_set_security_attr(security, "type", proto); 2558 2559 sa_set_security_attr(security, "sectype", sectype); 2560 (void) sa_security_name(security, oname, 2561 sizeof (oname), id); 2562 if (groupname != NULL && is_persistent(group)) { 2563 sa_handle_impl_t impl_handle; 2564 impl_handle = 2565 (sa_handle_impl_t)sa_find_group_handle( 2566 group); 2567 if (impl_handle != NULL) { 2568 (void) sa_get_instance( 2569 impl_handle->scfhandle, groupname); 2570 (void) sa_create_pgroup( 2571 impl_handle->scfhandle, oname); 2572 } 2573 } 2574 } 2575 } 2576 if (groupname != NULL) 2577 sa_free_attr_string(groupname); 2578 return (security); 2579 } 2580 2581 /* 2582 * sa_destroy_security(security) 2583 * 2584 * Remove the specified optionset from the document and the 2585 * configuration. 2586 */ 2587 2588 int 2589 sa_destroy_security(sa_security_t security) 2590 { 2591 char name[SA_STRSIZE]; 2592 int len; 2593 int ret = SA_OK; 2594 char *id = NULL; 2595 sa_group_t group; 2596 int iszfs = 0; 2597 int ispersist = 1; 2598 2599 group = sa_get_optionset_parent(security); 2600 2601 if (group != NULL) 2602 iszfs = sa_group_is_zfs(group); 2603 2604 if (group != NULL && !iszfs) { 2605 if (sa_is_share(group)) 2606 ispersist = is_persistent(group); 2607 id = sa_get_share_attr((sa_share_t)group, "id"); 2608 } 2609 if (ispersist) { 2610 len = sa_security_name(security, name, sizeof (name), id); 2611 if (!iszfs && len > 0) { 2612 sa_handle_impl_t impl_handle; 2613 impl_handle = 2614 (sa_handle_impl_t)sa_find_group_handle(group); 2615 if (impl_handle != NULL) { 2616 ret = sa_delete_pgroup(impl_handle->scfhandle, 2617 name); 2618 } else { 2619 ret = SA_SYSTEM_ERR; 2620 } 2621 } 2622 } 2623 xmlUnlinkNode((xmlNodePtr)security); 2624 xmlFreeNode((xmlNodePtr)security); 2625 if (iszfs) 2626 ret = sa_zfs_update(group); 2627 if (id != NULL) 2628 sa_free_attr_string(id); 2629 return (ret); 2630 } 2631 2632 /* 2633 * sa_get_security_attr(optionset, tag) 2634 * 2635 * Return the specified attribute value from the optionset. 2636 */ 2637 2638 char * 2639 sa_get_security_attr(sa_property_t optionset, char *tag) 2640 { 2641 return (get_node_attr((void *)optionset, tag)); 2642 2643 } 2644 2645 /* 2646 * sa_set_security_attr(optionset, tag, value) 2647 * 2648 * Set the optioset attribute specied by tag to the specified value. 2649 */ 2650 2651 void 2652 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2653 { 2654 set_node_attr((void *)optionset, tag, value); 2655 } 2656 2657 /* 2658 * is_nodetype(node, type) 2659 * 2660 * Check to see if node is of the type specified. 2661 */ 2662 2663 static int 2664 is_nodetype(void *node, char *type) 2665 { 2666 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2667 } 2668 2669 2670 /* 2671 * add_or_update() 2672 * 2673 * Add or update a property. Pulled out of sa_set_prop_by_prop for 2674 * readability. 2675 */ 2676 static int 2677 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value, 2678 scf_transaction_entry_t *entry, char *name, char *valstr) 2679 { 2680 int ret = SA_SYSTEM_ERR; 2681 2682 if (value != NULL) { 2683 if (type == SA_PROP_OP_ADD) 2684 ret = scf_transaction_property_new(scf_handle->trans, 2685 entry, name, SCF_TYPE_ASTRING); 2686 else 2687 ret = scf_transaction_property_change(scf_handle->trans, 2688 entry, name, SCF_TYPE_ASTRING); 2689 if (ret == 0) { 2690 ret = scf_value_set_astring(value, valstr); 2691 if (ret == 0) 2692 ret = scf_entry_add_value(entry, value); 2693 if (ret == 0) 2694 return (ret); 2695 scf_value_destroy(value); 2696 } else { 2697 scf_entry_destroy(entry); 2698 } 2699 } 2700 return (SA_SYSTEM_ERR); 2701 } 2702 2703 /* 2704 * sa_set_prop_by_prop(optionset, group, prop, type) 2705 * 2706 * Add/remove/update the specified property prop into the optionset or 2707 * share. If a share, sort out which property group based on GUID. In 2708 * all cases, the appropriate transaction is set (or ZFS share is 2709 * marked as needing an update) 2710 */ 2711 2712 static int 2713 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 2714 sa_property_t prop, int type) 2715 { 2716 char *name; 2717 char *valstr; 2718 int ret = SA_OK; 2719 scf_transaction_entry_t *entry; 2720 scf_value_t *value; 2721 int opttype; /* 1 == optionset, 0 == security */ 2722 char *id = NULL; 2723 int iszfs = 0; 2724 int isshare = 0; 2725 sa_group_t parent = NULL; 2726 sa_handle_impl_t impl_handle; 2727 scfutilhandle_t *scf_handle; 2728 2729 if (!is_persistent(group)) { 2730 /* 2731 * if the group/share is not persistent we don't need 2732 * to do anything here 2733 */ 2734 return (SA_OK); 2735 } 2736 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2737 if (impl_handle == NULL || impl_handle->scfhandle == NULL) 2738 return (SA_SYSTEM_ERR); 2739 scf_handle = impl_handle->scfhandle; 2740 name = sa_get_property_attr(prop, "type"); 2741 valstr = sa_get_property_attr(prop, "value"); 2742 entry = scf_entry_create(scf_handle->handle); 2743 opttype = is_nodetype((void *)optionset, "optionset"); 2744 2745 if (valstr != NULL && entry != NULL) { 2746 if (sa_is_share(group)) { 2747 isshare = 1; 2748 parent = sa_get_parent_group(group); 2749 if (parent != NULL) 2750 iszfs = is_zfs_group(parent); 2751 } else { 2752 iszfs = is_zfs_group(group); 2753 } 2754 if (!iszfs) { 2755 if (scf_handle->trans == NULL) { 2756 char oname[SA_STRSIZE]; 2757 char *groupname = NULL; 2758 if (isshare) { 2759 if (parent != NULL) { 2760 groupname = 2761 sa_get_group_attr(parent, 2762 "name"); 2763 } 2764 id = 2765 sa_get_share_attr((sa_share_t)group, 2766 "id"); 2767 } else { 2768 groupname = sa_get_group_attr(group, 2769 "name"); 2770 } 2771 if (groupname != NULL) { 2772 ret = sa_get_instance(scf_handle, 2773 groupname); 2774 sa_free_attr_string(groupname); 2775 } 2776 if (opttype) 2777 (void) sa_optionset_name(optionset, 2778 oname, sizeof (oname), id); 2779 else 2780 (void) sa_security_name(optionset, 2781 oname, sizeof (oname), id); 2782 ret = sa_start_transaction(scf_handle, oname); 2783 } 2784 if (ret == SA_OK) { 2785 switch (type) { 2786 case SA_PROP_OP_REMOVE: 2787 ret = scf_transaction_property_delete( 2788 scf_handle->trans, entry, name); 2789 break; 2790 case SA_PROP_OP_ADD: 2791 case SA_PROP_OP_UPDATE: 2792 value = scf_value_create( 2793 scf_handle->handle); 2794 ret = add_or_update(scf_handle, type, 2795 value, entry, name, valstr); 2796 break; 2797 } 2798 } 2799 } else { 2800 /* 2801 * ZFS update. The calling function would have updated 2802 * the internal XML structure. Just need to flag it as 2803 * changed for ZFS. 2804 */ 2805 zfs_set_update((sa_share_t)group); 2806 } 2807 } 2808 2809 if (name != NULL) 2810 sa_free_attr_string(name); 2811 if (valstr != NULL) 2812 sa_free_attr_string(valstr); 2813 else if (entry != NULL) 2814 scf_entry_destroy(entry); 2815 2816 if (ret == -1) 2817 ret = SA_SYSTEM_ERR; 2818 2819 return (ret); 2820 } 2821 2822 /* 2823 * sa_create_property(name, value) 2824 * 2825 * Create a new property with the specified name and value. 2826 */ 2827 2828 sa_property_t 2829 sa_create_property(char *name, char *value) 2830 { 2831 xmlNodePtr node; 2832 2833 node = xmlNewNode(NULL, (xmlChar *)"option"); 2834 if (node != NULL) { 2835 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 2836 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 2837 } 2838 return ((sa_property_t)node); 2839 } 2840 2841 /* 2842 * sa_add_property(object, property) 2843 * 2844 * Add the specified property to the object. Issue the appropriate 2845 * transaction or mark a ZFS object as needing an update. 2846 */ 2847 2848 int 2849 sa_add_property(void *object, sa_property_t property) 2850 { 2851 int ret = SA_OK; 2852 sa_group_t parent; 2853 sa_group_t group; 2854 char *proto; 2855 2856 proto = sa_get_optionset_attr(object, "type"); 2857 if (property != NULL) { 2858 if ((ret = sa_valid_property(object, proto, property)) == 2859 SA_OK) { 2860 property = (sa_property_t)xmlAddChild( 2861 (xmlNodePtr)object, (xmlNodePtr)property); 2862 } else { 2863 if (proto != NULL) 2864 sa_free_attr_string(proto); 2865 return (ret); 2866 } 2867 } 2868 2869 if (proto != NULL) 2870 sa_free_attr_string(proto); 2871 2872 parent = sa_get_parent_group(object); 2873 if (!is_persistent(parent)) { 2874 return (ret); 2875 } 2876 2877 if (sa_is_share(parent)) 2878 group = sa_get_parent_group(parent); 2879 else 2880 group = parent; 2881 2882 if (property == NULL) { 2883 ret = SA_NO_MEMORY; 2884 } else { 2885 char oname[SA_STRSIZE]; 2886 2887 if (!is_zfs_group(group)) { 2888 char *id = NULL; 2889 sa_handle_impl_t impl_handle; 2890 scfutilhandle_t *scf_handle; 2891 2892 impl_handle = (sa_handle_impl_t)sa_find_group_handle( 2893 group); 2894 if (impl_handle == NULL || 2895 impl_handle->scfhandle == NULL) 2896 ret = SA_SYSTEM_ERR; 2897 if (ret == SA_OK) { 2898 scf_handle = impl_handle->scfhandle; 2899 if (sa_is_share((sa_group_t)parent)) { 2900 id = sa_get_share_attr( 2901 (sa_share_t)parent, "id"); 2902 } 2903 if (scf_handle->trans == NULL) { 2904 if (is_nodetype(object, "optionset")) { 2905 (void) sa_optionset_name( 2906 (sa_optionset_t)object, 2907 oname, sizeof (oname), id); 2908 } else { 2909 (void) sa_security_name( 2910 (sa_optionset_t)object, 2911 oname, sizeof (oname), id); 2912 } 2913 ret = sa_start_transaction(scf_handle, 2914 oname); 2915 } 2916 if (ret == SA_OK) { 2917 char *name; 2918 char *value; 2919 name = sa_get_property_attr(property, 2920 "type"); 2921 value = sa_get_property_attr(property, 2922 "value"); 2923 if (name != NULL && value != NULL) { 2924 if (scf_handle->scf_state == 2925 SCH_STATE_INIT) { 2926 ret = sa_set_property( 2927 scf_handle, name, 2928 value); 2929 } 2930 } else { 2931 ret = SA_CONFIG_ERR; 2932 } 2933 if (name != NULL) 2934 sa_free_attr_string( 2935 name); 2936 if (value != NULL) 2937 sa_free_attr_string(value); 2938 } 2939 if (id != NULL) 2940 sa_free_attr_string(id); 2941 } 2942 } else { 2943 /* 2944 * ZFS is a special case. We do want 2945 * to allow editing property/security 2946 * lists since we can have a better 2947 * syntax and we also want to keep 2948 * things consistent when possible. 2949 * 2950 * Right now, we defer until the 2951 * sa_commit_properties so we can get 2952 * them all at once. We do need to 2953 * mark the share as "changed" 2954 */ 2955 zfs_set_update((sa_share_t)parent); 2956 } 2957 } 2958 return (ret); 2959 } 2960 2961 /* 2962 * sa_remove_property(property) 2963 * 2964 * Remove the specied property from its containing object. Update the 2965 * repository as appropriate. 2966 */ 2967 2968 int 2969 sa_remove_property(sa_property_t property) 2970 { 2971 int ret = SA_OK; 2972 2973 if (property != NULL) { 2974 sa_optionset_t optionset; 2975 sa_group_t group; 2976 optionset = sa_get_property_parent(property); 2977 if (optionset != NULL) { 2978 group = sa_get_optionset_parent(optionset); 2979 if (group != NULL) { 2980 ret = sa_set_prop_by_prop(optionset, group, 2981 property, SA_PROP_OP_REMOVE); 2982 } 2983 } 2984 xmlUnlinkNode((xmlNodePtr)property); 2985 xmlFreeNode((xmlNodePtr)property); 2986 } else { 2987 ret = SA_NO_SUCH_PROP; 2988 } 2989 return (ret); 2990 } 2991 2992 /* 2993 * sa_update_property(property, value) 2994 * 2995 * Update the specified property to the new value. If value is NULL, 2996 * we currently treat this as a remove. 2997 */ 2998 2999 int 3000 sa_update_property(sa_property_t property, char *value) 3001 { 3002 int ret = SA_OK; 3003 if (value == NULL) { 3004 return (sa_remove_property(property)); 3005 } else { 3006 sa_optionset_t optionset; 3007 sa_group_t group; 3008 set_node_attr((void *)property, "value", value); 3009 optionset = sa_get_property_parent(property); 3010 if (optionset != NULL) { 3011 group = sa_get_optionset_parent(optionset); 3012 if (group != NULL) { 3013 ret = sa_set_prop_by_prop(optionset, group, 3014 property, SA_PROP_OP_UPDATE); 3015 } 3016 } else { 3017 ret = SA_NO_SUCH_PROP; 3018 } 3019 } 3020 return (ret); 3021 } 3022 3023 /* 3024 * sa_get_protocol_property(propset, prop) 3025 * 3026 * Get the specified protocol specific property. These are global to 3027 * the protocol and not specific to a group or share. 3028 */ 3029 3030 sa_property_t 3031 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 3032 { 3033 xmlNodePtr node = (xmlNodePtr)propset; 3034 xmlChar *value = NULL; 3035 3036 for (node = node->children; node != NULL; 3037 node = node->next) { 3038 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3039 if (prop == NULL) 3040 break; 3041 value = xmlGetProp(node, (xmlChar *)"type"); 3042 if (value != NULL && 3043 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 3044 break; 3045 } 3046 if (value != NULL) { 3047 xmlFree(value); 3048 value = NULL; 3049 } 3050 } 3051 } 3052 if (value != NULL) 3053 xmlFree(value); 3054 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 3055 /* 3056 * avoid a non option node -- it is possible to be a 3057 * text node 3058 */ 3059 node = NULL; 3060 } 3061 return ((sa_property_t)node); 3062 } 3063 3064 /* 3065 * sa_get_next_protocol_property(prop) 3066 * 3067 * Get the next protocol specific property in the list. 3068 */ 3069 3070 sa_property_t 3071 sa_get_next_protocol_property(sa_property_t prop) 3072 { 3073 xmlNodePtr node; 3074 3075 for (node = ((xmlNodePtr)prop)->next; node != NULL; 3076 node = node->next) { 3077 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3078 break; 3079 } 3080 } 3081 return ((sa_property_t)node); 3082 } 3083 3084 /* 3085 * sa_set_protocol_property(prop, value) 3086 * 3087 * Set the specified property to have the new value. The protocol 3088 * specific plugin will then be called to update the property. 3089 */ 3090 3091 int 3092 sa_set_protocol_property(sa_property_t prop, char *value) 3093 { 3094 sa_protocol_properties_t propset; 3095 char *proto; 3096 int ret = SA_INVALID_PROTOCOL; 3097 3098 propset = ((xmlNodePtr)prop)->parent; 3099 if (propset != NULL) { 3100 proto = sa_get_optionset_attr(propset, "type"); 3101 if (proto != NULL) { 3102 set_node_attr((xmlNodePtr)prop, "value", value); 3103 ret = sa_proto_set_property(proto, prop); 3104 sa_free_attr_string(proto); 3105 } 3106 } 3107 return (ret); 3108 } 3109 3110 /* 3111 * sa_add_protocol_property(propset, prop) 3112 * 3113 * Add a new property to the protocol sepcific property set. 3114 */ 3115 3116 int 3117 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 3118 { 3119 xmlNodePtr node; 3120 3121 /* should check for legitimacy */ 3122 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 3123 if (node != NULL) 3124 return (SA_OK); 3125 return (SA_NO_MEMORY); 3126 } 3127 3128 /* 3129 * sa_create_protocol_properties(proto) 3130 * 3131 * Create a protocol specifity property set. 3132 */ 3133 3134 sa_protocol_properties_t 3135 sa_create_protocol_properties(char *proto) 3136 { 3137 xmlNodePtr node; 3138 3139 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 3140 if (node != NULL) 3141 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 3142 return (node); 3143 } 3144