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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2013 RackTop Systems.
29 */
30
31 /* helper functions for using libscf with sharemgr */
32
33 #include <libscf.h>
34 #include <libxml/parser.h>
35 #include <libxml/tree.h>
36 #include "libshare.h"
37 #include "libshare_impl.h"
38 #include "scfutil.h"
39 #include <string.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <uuid/uuid.h>
43 #include <sys/param.h>
44 #include <signal.h>
45 #include <sys/time.h>
46 #include <libintl.h>
47
48 ssize_t scf_max_name_len;
49 extern struct sa_proto_plugin *sap_proto_list;
50 extern sa_handle_t get_handle_for_root(xmlNodePtr);
51 static void set_transaction_tstamp(sa_handle_t);
52 /*
53 * The SMF facility uses some properties that must exist. We want to
54 * skip over these when processing protocol options.
55 */
56 static char *skip_props[] = {
57 "modify_authorization",
58 "action_authorization",
59 "value_authorization",
60 NULL
61 };
62
63 /*
64 * sa_scf_fini(handle)
65 *
66 * Must be called when done. Called with the handle allocated in
67 * sa_scf_init(), it cleans up the state and frees any SCF resources
68 * still in use. Called by sa_fini().
69 */
70
71 void
72 sa_scf_fini(scfutilhandle_t *handle)
73 {
74 if (handle != NULL) {
75 int unbind = 0;
76 if (handle->scope != NULL) {
77 unbind = 1;
78 scf_scope_destroy(handle->scope);
79 }
80 if (handle->instance != NULL)
81 scf_instance_destroy(handle->instance);
82 if (handle->service != NULL)
83 scf_service_destroy(handle->service);
84 if (handle->pg != NULL)
85 scf_pg_destroy(handle->pg);
86 if (handle->handle != NULL) {
87 handle->scf_state = SCH_STATE_UNINIT;
88 if (unbind)
89 (void) scf_handle_unbind(handle->handle);
90 scf_handle_destroy(handle->handle);
91 }
92 free(handle);
93 }
94 }
95
96 /*
97 * sa_scf_init()
98 *
99 * Must be called before using any of the SCF functions. Called by
100 * sa_init() during the API setup.
101 */
102
103 scfutilhandle_t *
104 sa_scf_init(sa_handle_t ihandle)
105 {
106 scfutilhandle_t *handle;
107
108 scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
109 if (scf_max_name_len <= 0)
110 scf_max_name_len = SA_MAX_NAME_LEN + 1;
111
112 handle = calloc(1, sizeof (scfutilhandle_t));
113 if (handle == NULL)
114 return (handle);
115
116 ihandle->scfhandle = handle;
117 handle->scf_state = SCH_STATE_INITIALIZING;
118 handle->handle = scf_handle_create(SCF_VERSION);
119 if (handle->handle == NULL) {
120 free(handle);
121 handle = NULL;
122 (void) printf("libshare could not access SMF repository: %s\n",
123 scf_strerror(scf_error()));
124 return (handle);
125 }
126 if (scf_handle_bind(handle->handle) != 0)
127 goto err;
128
129 handle->scope = scf_scope_create(handle->handle);
130 handle->service = scf_service_create(handle->handle);
131 handle->pg = scf_pg_create(handle->handle);
132
133 /* Make sure we have sufficient SMF running */
134 handle->instance = scf_instance_create(handle->handle);
135 if (handle->scope == NULL || handle->service == NULL ||
136 handle->pg == NULL || handle->instance == NULL)
137 goto err;
138 if (scf_handle_get_scope(handle->handle,
139 SCF_SCOPE_LOCAL, handle->scope) != 0)
140 goto err;
141 if (scf_scope_get_service(handle->scope,
142 SA_GROUP_SVC_NAME, handle->service) != 0)
143 goto err;
144
145 handle->scf_state = SCH_STATE_INIT;
146 if (sa_get_instance(handle, "default") != SA_OK) {
147 sa_group_t defgrp;
148 defgrp = sa_create_group(ihandle, "default", NULL);
149 /* Only NFS enabled for "default" group. */
150 if (defgrp != NULL)
151 (void) sa_create_optionset(defgrp, "nfs");
152 }
153
154 return (handle);
155
156 /* Error handling/unwinding */
157 err:
158 (void) sa_scf_fini(handle);
159 (void) printf("libshare SMF initialization problem: %s\n",
160 scf_strerror(scf_error()));
161 return (NULL);
162 }
163
164 /*
165 * get_scf_limit(name)
166 *
167 * Since we use scf_limit a lot and do the same check and return the
168 * same value if it fails, implement as a function for code
169 * simplification. Basically, if name isn't found, return MAXPATHLEN
170 * (1024) so we have a reasonable default buffer size.
171 */
172 static ssize_t
173 get_scf_limit(uint32_t name)
174 {
175 ssize_t vallen;
176
177 vallen = scf_limit(name);
178 if (vallen == (ssize_t)-1)
179 vallen = MAXPATHLEN;
180 return (vallen);
181 }
182
183 /*
184 * skip_property(name)
185 *
186 * Internal function to check to see if a property is an SMF magic
187 * property that needs to be skipped.
188 */
189 static int
190 skip_property(char *name)
191 {
192 int i;
193
194 for (i = 0; skip_props[i] != NULL; i++)
195 if (strcmp(name, skip_props[i]) == 0)
196 return (1);
197 return (0);
198 }
199
200 /*
201 * generate_unique_sharename(sharename)
202 *
203 * Shares are represented in SMF as property groups. Due to share
204 * paths containing characters that are not allowed in SMF names and
205 * the need to be unique, we use UUIDs to construct a unique name.
206 */
207
208 static void
209 generate_unique_sharename(char *sharename)
210 {
211 uuid_t uuid;
212
213 uuid_generate(uuid);
214 (void) strcpy(sharename, "S-");
215 uuid_unparse(uuid, sharename + 2);
216 }
217
218 /*
219 * valid_protocol(proto)
220 *
221 * Check to see if the specified protocol is a valid one for the
222 * general sharemgr facility. We determine this by checking which
223 * plugin protocols were found.
224 */
225
226 static int
227 valid_protocol(char *proto)
228 {
229 struct sa_proto_plugin *plugin;
230 for (plugin = sap_proto_list; plugin != NULL;
231 plugin = plugin->plugin_next)
232 if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
233 return (1);
234 return (0);
235 }
236
237 /*
238 * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
239 *
240 * Extract the name property group and create the specified type of
241 * node on the provided group. type will be optionset or security.
242 */
243
244 static int
245 sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
246 scf_propertygroup_t *pg,
247 char *nodetype, char *proto, char *sectype)
248 {
249 xmlNodePtr node;
250 scf_iter_t *iter;
251 scf_property_t *prop;
252 scf_value_t *value;
253 char *name;
254 char *valuestr;
255 ssize_t vallen;
256 int ret = SA_OK;
257
258 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
259
260 node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
261 if (node == NULL)
262 return (ret);
263
264 if (proto != NULL)
265 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
266 if (sectype != NULL)
267 (void) xmlSetProp(node, (xmlChar *)"sectype",
268 (xmlChar *)sectype);
269 /*
270 * Have node to work with so iterate over the properties
271 * in the pg and create option sub nodes.
272 */
273 iter = scf_iter_create(handle->handle);
274 value = scf_value_create(handle->handle);
275 prop = scf_property_create(handle->handle);
276 name = malloc(scf_max_name_len);
277 valuestr = malloc(vallen);
278 /*
279 * Want to iterate through the properties and add them
280 * to the base optionset.
281 */
282 if (iter == NULL || value == NULL || prop == NULL ||
283 valuestr == NULL || name == NULL) {
284 ret = SA_NO_MEMORY;
285 goto out;
286 }
287 if (scf_iter_pg_properties(iter, pg) == 0) {
288 /* Now iterate the properties in the group */
289 while (scf_iter_next_property(iter, prop) > 0) {
290 /* have a property */
291 if (scf_property_get_name(prop, name,
292 scf_max_name_len) > 0) {
293 sa_property_t saprop;
294 /* Some properties are part of the framework */
295 if (skip_property(name))
296 continue;
297 if (scf_property_get_value(prop, value) != 0)
298 continue;
299 if (scf_value_get_astring(value, valuestr,
300 vallen) < 0)
301 continue;
302 saprop = sa_create_property(name, valuestr);
303 if (saprop != NULL) {
304 /*
305 * Since in SMF, don't
306 * recurse. Use xmlAddChild
307 * directly, instead.
308 */
309 (void) xmlAddChild(node,
310 (xmlNodePtr) saprop);
311 }
312 }
313 }
314 }
315 out:
316 /* cleanup to avoid memory leaks */
317 if (value != NULL)
318 scf_value_destroy(value);
319 if (iter != NULL)
320 scf_iter_destroy(iter);
321 if (prop != NULL)
322 scf_property_destroy(prop);
323 if (name != NULL)
324 free(name);
325 if (valuestr != NULL)
326 free(valuestr);
327
328 return (ret);
329 }
330
331 /*
332 * sa_extract_attrs(root, handle, instance)
333 *
334 * Local function to extract the actual attributes/properties from the
335 * property group of the service instance. These are the well known
336 * attributes of "state" and "zfs". If additional attributes are
337 * added, they should be added here.
338 */
339
340 static void
341 sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
342 scf_instance_t *instance)
343 {
344 scf_property_t *prop;
345 scf_value_t *value;
346 char *valuestr;
347 ssize_t vallen;
348
349 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
350 prop = scf_property_create(handle->handle);
351 value = scf_value_create(handle->handle);
352 valuestr = malloc(vallen);
353 if (prop == NULL || value == NULL || valuestr == NULL ||
354 scf_instance_get_pg(instance, "operation", handle->pg) != 0) {
355 goto out;
356 }
357 /*
358 * Have a property group with desired name so now get
359 * the known attributes.
360 */
361 if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
362 /* Found the property so get the value */
363 if (scf_property_get_value(prop, value) == 0) {
364 if (scf_value_get_astring(value, valuestr,
365 vallen) >= 0) {
366 (void) xmlSetProp(root, (xmlChar *)"state",
367 (xmlChar *)valuestr);
368 }
369 }
370 }
371 if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
372 /* Found the property so get the value */
373 if (scf_property_get_value(prop, value) == 0) {
374 if (scf_value_get_astring(value, valuestr,
375 vallen) > 0) {
376 (void) xmlSetProp(root, (xmlChar *)"zfs",
377 (xmlChar *)valuestr);
378 }
379 }
380 }
381 out:
382 if (valuestr != NULL)
383 free(valuestr);
384 if (value != NULL)
385 scf_value_destroy(value);
386 if (prop != NULL)
387 scf_property_destroy(prop);
388 }
389
390 /*
391 * List of known share attributes.
392 */
393
394 static char *share_attr[] = {
395 "path",
396 "id",
397 "drive-letter",
398 "exclude",
399 NULL,
400 };
401
402 static int
403 is_share_attr(char *name)
404 {
405 int i;
406 for (i = 0; share_attr[i] != NULL; i++)
407 if (strcmp(name, share_attr[i]) == 0)
408 return (1);
409 return (0);
410 }
411
412 /*
413 * _sa_make_resource(node, valuestr)
414 *
415 * Make a resource node on the share node. The valusestr will either
416 * be old format (SMF acceptable string) or new format (pretty much an
417 * arbitrary string with "nnn:" prefixing in order to persist
418 * mapping). The input valuestr will get modified in place. This is
419 * only used in SMF repository parsing. A possible third field will be
420 * a "description" string.
421 */
422
423 static void
424 _sa_make_resource(xmlNodePtr node, char *valuestr)
425 {
426 char *idx;
427 char *name;
428 char *description = NULL;
429
430 idx = valuestr;
431 name = strchr(valuestr, ':');
432 if (name == NULL) {
433 /* this is old form so give an index of "0" */
434 idx = "0";
435 name = valuestr;
436 } else {
437 /* NUL the ':' and move past it */
438 *name++ = '\0';
439 /* There could also be a description string */
440 description = strchr(name, ':');
441 if (description != NULL)
442 *description++ = '\0';
443 }
444 node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL);
445 if (node != NULL) {
446 (void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name);
447 (void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx);
448 /* SMF values are always persistent */
449 (void) xmlSetProp(node, (xmlChar *)"type",
450 (xmlChar *)"persist");
451 if (description != NULL && strlen(description) > 0) {
452 (void) xmlNewChild(node, NULL, (xmlChar *)"description",
453 (xmlChar *)description);
454 }
455 }
456 }
457
458
459 /*
460 * sa_share_from_pgroup
461 *
462 * Extract the share definition from the share property group. We do
463 * some sanity checking to avoid bad data.
464 *
465 * Since this is only constructing the internal data structures, we
466 * don't use the sa_* functions most of the time.
467 */
468 void
469 sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
470 scf_propertygroup_t *pg, char *id)
471 {
472 xmlNodePtr node;
473 char *name;
474 scf_iter_t *iter;
475 scf_property_t *prop;
476 scf_value_t *value;
477 ssize_t vallen;
478 char *valuestr;
479 int ret = SA_OK;
480 int have_path = 0;
481
482 /*
483 * While preliminary check (starts with 'S') passed before
484 * getting here. Need to make sure it is in ID syntax
485 * (Snnnnnn). Note that shares with properties have similar
486 * pgroups.
487 */
488 vallen = strlen(id);
489 if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
490 uuid_t uuid;
491 if (strncmp(id, SA_SHARE_PG_PREFIX,
492 SA_SHARE_PG_PREFIXLEN) != 0 ||
493 uuid_parse(id + 2, uuid) < 0)
494 return;
495 } else {
496 return;
497 }
498
499 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
500
501 iter = scf_iter_create(handle->handle);
502 value = scf_value_create(handle->handle);
503 prop = scf_property_create(handle->handle);
504 name = malloc(scf_max_name_len);
505 valuestr = malloc(vallen);
506
507 /*
508 * Construct the share XML node. It is similar to sa_add_share
509 * but never changes the repository. Also, there won't be any
510 * ZFS or transient shares. Root will be the group it is
511 * associated with.
512 */
513 node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
514 if (node != NULL) {
515 /*
516 * Make sure the UUID part of the property group is
517 * stored in the share "id" property. We use this
518 * later.
519 */
520 (void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
521 (void) xmlSetProp(node, (xmlChar *)"type",
522 (xmlChar *)"persist");
523 }
524
525 if (iter == NULL || value == NULL || prop == NULL || name == NULL)
526 goto out;
527
528 /* Iterate over the share pg properties */
529 if (scf_iter_pg_properties(iter, pg) != 0)
530 goto out;
531
532 while (scf_iter_next_property(iter, prop) > 0) {
533 ret = SA_SYSTEM_ERR; /* assume the worst */
534 if (scf_property_get_name(prop, name, scf_max_name_len) > 0) {
535 if (scf_property_get_value(prop, value) == 0) {
536 if (scf_value_get_astring(value, valuestr,
537 vallen) >= 0) {
538 ret = SA_OK;
539 }
540 } else if (strcmp(name, "resource") == 0) {
541 ret = SA_OK;
542 }
543 }
544 if (ret != SA_OK)
545 continue;
546 /*
547 * Check that we have the "path" property in
548 * name. The string in name will always be nul
549 * terminated if scf_property_get_name()
550 * succeeded.
551 */
552 if (strcmp(name, "path") == 0)
553 have_path = 1;
554 if (is_share_attr(name)) {
555 /*
556 * If a share attr, then simple -
557 * usually path and id name
558 */
559 (void) xmlSetProp(node, (xmlChar *)name,
560 (xmlChar *)valuestr);
561 } else if (strcmp(name, "resource") == 0) {
562 /*
563 * Resource names handled differently since
564 * there can be multiple on each share. The
565 * "resource" id must be preserved since this
566 * will be used by some protocols in mapping
567 * "property spaces" to names and is always
568 * used to create SMF property groups specific
569 * to resources. CIFS needs this. The first
570 * value is present so add and then loop for
571 * any additional. Since this is new and
572 * previous values may exist, handle
573 * conversions.
574 */
575 scf_iter_t *viter;
576 viter = scf_iter_create(handle->handle);
577 if (viter != NULL &&
578 scf_iter_property_values(viter, prop) == 0) {
579 while (scf_iter_next_value(viter, value) > 0) {
580 /* Have a value so process it */
581 if (scf_value_get_ustring(value,
582 valuestr, vallen) >= 0) {
583 /* have a ustring */
584 _sa_make_resource(node,
585 valuestr);
586 } else if (scf_value_get_astring(value,
587 valuestr, vallen) >= 0) {
588 /* have an astring */
589 _sa_make_resource(node,
590 valuestr);
591 }
592 }
593 scf_iter_destroy(viter);
594 }
595 } else {
596 if (strcmp(name, "description") == 0) {
597 /* We have a description node */
598 xmlNodePtr desc;
599 desc = xmlNewChild(node, NULL,
600 (xmlChar *)"description", NULL);
601 if (desc != NULL)
602 xmlNodeSetContent(desc,
603 (xmlChar *)valuestr);
604 }
605 }
606 }
607 out:
608 /*
609 * A share without a path is broken so we want to not include
610 * these. They shouldn't happen but if you kill a sharemgr in
611 * the process of creating a share, it could happen. They
612 * should be harmless. It is also possible that another
613 * sharemgr is running and in the process of creating a share.
614 */
615 if (have_path == 0 && node != NULL) {
616 xmlUnlinkNode(node);
617 xmlFreeNode(node);
618 }
619 if (name != NULL)
620 free(name);
621 if (valuestr != NULL)
622 free(valuestr);
623 if (value != NULL)
624 scf_value_destroy(value);
625 if (iter != NULL)
626 scf_iter_destroy(iter);
627 if (prop != NULL)
628 scf_property_destroy(prop);
629 }
630
631 /*
632 * find_share_by_id(shareid)
633 *
634 * Search all shares in all groups until we find the share represented
635 * by "id".
636 */
637
638 static sa_share_t
639 find_share_by_id(sa_handle_t handle, char *shareid)
640 {
641 sa_group_t group;
642 sa_share_t share = NULL;
643 char *id = NULL;
644 int done = 0;
645
646 for (group = sa_get_group(handle, NULL);
647 group != NULL && !done;
648 group = sa_get_next_group(group)) {
649 for (share = sa_get_share(group, NULL);
650 share != NULL;
651 share = sa_get_next_share(share)) {
652 id = sa_get_share_attr(share, "id");
653 if (id != NULL && strcmp(id, shareid) == 0) {
654 sa_free_attr_string(id);
655 id = NULL;
656 done++;
657 break;
658 }
659 if (id != NULL) {
660 sa_free_attr_string(id);
661 id = NULL;
662 }
663 }
664 }
665 return (share);
666 }
667
668 /*
669 * find_resource_by_index(share, index)
670 *
671 * Search the resource records on the share for the id index.
672 */
673 static sa_resource_t
674 find_resource_by_index(sa_share_t share, char *index)
675 {
676 sa_resource_t resource;
677 sa_resource_t found = NULL;
678 char *id;
679
680 for (resource = sa_get_share_resource(share, NULL);
681 resource != NULL && found == NULL;
682 resource = sa_get_next_resource(resource)) {
683 id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id");
684 if (id != NULL) {
685 if (strcmp(id, index) == 0) {
686 /* found it so save in "found" */
687 found = resource;
688 }
689 sa_free_attr_string(id);
690 }
691 }
692 return (found);
693 }
694
695 /*
696 * sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
697 *
698 * Extract share properties from the SMF property group. More sanity
699 * checks are done and the share object is created. We ignore some
700 * errors that could exist in the repository and only worry about
701 * property groups that validate in naming.
702 */
703
704 static int
705 sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
706 scf_propertygroup_t *pg, char *id, sa_handle_t sahandle)
707 {
708 xmlNodePtr node;
709 char *name = NULL;
710 scf_iter_t *iter = NULL;
711 scf_property_t *prop = NULL;
712 scf_value_t *value = NULL;
713 ssize_t vallen;
714 char *valuestr = NULL;
715 int ret = SA_OK;
716 char *sectype = NULL;
717 char *proto;
718 sa_share_t share;
719 uuid_t uuid;
720
721 /*
722 * While preliminary check (starts with 'S') passed before
723 * getting here. Need to make sure it is in ID syntax
724 * (Snnnnnn). Note that shares with properties have similar
725 * pgroups. If the pg name is more than SA_SHARE_PG_LEN
726 * characters, it is likely one of the protocol/security
727 * versions.
728 */
729 vallen = strlen(id);
730 if (*id != SA_SHARE_PG_PREFIX[0] || vallen <= SA_SHARE_PG_LEN) {
731 /*
732 * It is ok to not have what we thought since someone might
733 * have added a name via SMF.
734 */
735 return (ret);
736 }
737 if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
738 proto = strchr(id, '_');
739 if (proto == NULL)
740 return (ret);
741 *proto++ = '\0';
742 if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
743 return (ret);
744 /*
745 * probably a legal optionset so check a few more
746 * syntax points below.
747 */
748 if (*proto == '\0') {
749 /* not a valid proto (null) */
750 return (ret);
751 }
752
753 sectype = strchr(proto, '_');
754 if (sectype != NULL)
755 *sectype++ = '\0';
756 if (!valid_protocol(proto))
757 return (ret);
758 }
759
760 /*
761 * To get here, we have a valid protocol and possibly a
762 * security. We now have to find the share that it is really
763 * associated with. The "id" portion of the pgroup name will
764 * match.
765 */
766
767 share = find_share_by_id(sahandle, id);
768 if (share == NULL)
769 return (SA_BAD_PATH);
770
771 root = (xmlNodePtr)share;
772
773 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
774
775 if (sectype == NULL)
776 node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
777 else {
778 if (isdigit((int)*sectype)) {
779 sa_resource_t resource;
780 /*
781 * If sectype[0] is a digit, then it is an index into
782 * the resource names. We need to find a resource
783 * record and then get the properties into an
784 * optionset. The optionset becomes the "node" and the
785 * rest is hung off of the share.
786 */
787 resource = find_resource_by_index(share, sectype);
788 if (resource != NULL) {
789 node = xmlNewChild(resource, NULL,
790 (xmlChar *)"optionset", NULL);
791 } else {
792 /* This shouldn't happen. */
793 ret = SA_SYSTEM_ERR;
794 goto out;
795 }
796 } else {
797 /*
798 * If not a digit, then it is a security type
799 * (alternate option space). Security types start with
800 * an alphabetic.
801 */
802 node = xmlNewChild(root, NULL, (xmlChar *)"security",
803 NULL);
804 if (node != NULL)
805 (void) xmlSetProp(node, (xmlChar *)"sectype",
806 (xmlChar *)sectype);
807 }
808 }
809 if (node == NULL) {
810 ret = SA_NO_MEMORY;
811 goto out;
812 }
813
814 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
815 /* now find the properties */
816 iter = scf_iter_create(handle->handle);
817 value = scf_value_create(handle->handle);
818 prop = scf_property_create(handle->handle);
819 name = malloc(scf_max_name_len);
820 valuestr = malloc(vallen);
821
822 if (iter == NULL || value == NULL || prop == NULL || name == NULL)
823 goto out;
824
825 /* iterate over the share pg properties */
826 if (scf_iter_pg_properties(iter, pg) == 0) {
827 while (scf_iter_next_property(iter, prop) > 0) {
828 ret = SA_SYSTEM_ERR; /* assume the worst */
829 if (scf_property_get_name(prop, name,
830 scf_max_name_len) > 0) {
831 if (scf_property_get_value(prop, value) == 0) {
832 if (scf_value_get_astring(value,
833 valuestr, vallen) >= 0) {
834 ret = SA_OK;
835 }
836 }
837 } else {
838 ret = SA_SYSTEM_ERR;
839 }
840 if (ret == SA_OK) {
841 sa_property_t prop;
842 prop = sa_create_property(name, valuestr);
843 if (prop != NULL)
844 prop = (sa_property_t)xmlAddChild(node,
845 (xmlNodePtr)prop);
846 else
847 ret = SA_NO_MEMORY;
848 }
849 }
850 } else {
851 ret = SA_SYSTEM_ERR;
852 }
853 out:
854 if (iter != NULL)
855 scf_iter_destroy(iter);
856 if (value != NULL)
857 scf_value_destroy(value);
858 if (prop != NULL)
859 scf_property_destroy(prop);
860 if (name != NULL)
861 free(name);
862 if (valuestr != NULL)
863 free(valuestr);
864 return (ret);
865 }
866
867 /*
868 * sa_extract_group(root, handle, instance)
869 *
870 * Get the config info for this instance of a group and create the XML
871 * subtree from it.
872 */
873
874 static int
875 sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
876 scf_instance_t *instance, sa_handle_t sahandle)
877 {
878 char *buff;
879 xmlNodePtr node;
880 scf_iter_t *iter;
881 char *proto;
882 char *sectype;
883 boolean_t have_shares = B_FALSE;
884 boolean_t is_default = B_FALSE;
885 boolean_t is_nfs = B_FALSE;
886 int ret = SA_OK;
887 int err;
888
889 buff = malloc(scf_max_name_len);
890 if (buff == NULL)
891 return (SA_NO_MEMORY);
892
893 iter = scf_iter_create(handle->handle);
894 if (iter == NULL) {
895 ret = SA_NO_MEMORY;
896 goto out;
897 }
898
899 if (scf_instance_get_name(instance, buff, scf_max_name_len) > 0) {
900 node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
901 if (node == NULL) {
902 ret = SA_NO_MEMORY;
903 goto out;
904 }
905 (void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
906 if (strcmp(buff, "default") == 0)
907 is_default = B_TRUE;
908
909 sa_extract_attrs(node, handle, instance);
910 /*
911 * Iterate through all the property groups
912 * looking for those with security or
913 * optionset prefixes. The names of the
914 * matching pgroups are parsed to get the
915 * protocol, and for security, the sectype.
916 * Syntax is as follows:
917 * optionset | optionset_<proto>
918 * security_default | security_<proto>_<sectype>
919 * "operation" is handled by
920 * sa_extract_attrs().
921 */
922 if (scf_iter_instance_pgs(iter, instance) != 0) {
923 ret = SA_NO_MEMORY;
924 goto out;
925 }
926 while (scf_iter_next_pg(iter, handle->pg) > 0) {
927 /* Have a pgroup so sort it out */
928 ret = scf_pg_get_name(handle->pg, buff,
929 scf_max_name_len);
930 if (ret <= 0)
931 continue;
932 is_nfs = B_FALSE;
933
934 if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
935 sa_share_from_pgroup(node, handle,
936 handle->pg, buff);
937 have_shares = B_TRUE;
938 } else if (strncmp(buff, "optionset", 9) == 0) {
939 char *nodetype = "optionset";
940 /* Have an optionset */
941 sectype = NULL;
942 proto = strchr(buff, '_');
943 if (proto != NULL) {
944 *proto++ = '\0';
945 sectype = strchr(proto, '_');
946 if (sectype != NULL) {
947 *sectype++ = '\0';
948 nodetype = "security";
949 }
950 is_nfs = strcmp(proto, "nfs") == 0;
951 } else if (strlen(buff) > 9) {
952 /*
953 * This can only occur if
954 * someone has made changes
955 * via an SMF command. Since
956 * this would be an unknown
957 * syntax, we just ignore it.
958 */
959 continue;
960 }
961 /*
962 * If the group is not "default" or is
963 * "default" and is_nfs, then extract the
964 * pgroup. If it is_default and !is_nfs,
965 * then we have an error and should remove
966 * the extraneous protocols. We don't care
967 * about errors on scf_pg_delete since we
968 * might not have permission during an
969 * extract only.
970 */
971 if (!is_default || is_nfs) {
972 ret = sa_extract_pgroup(node, handle,
973 handle->pg, nodetype, proto,
974 sectype);
975 } else {
976 err = scf_pg_delete(handle->pg);
977 if (err == 0)
978 (void) fprintf(stderr,
979 dgettext(TEXT_DOMAIN,
980 "Removed protocol \"%s\" "
981 "from group \"default\"\n"),
982 proto);
983 }
984 } else if (strncmp(buff, "security", 8) == 0) {
985 /*
986 * Have a security (note that
987 * this should change in the
988 * future)
989 */
990 proto = strchr(buff, '_');
991 sectype = NULL;
992 if (proto != NULL) {
993 *proto++ = '\0';
994 sectype = strchr(proto, '_');
995 if (sectype != NULL)
996 *sectype++ = '\0';
997 if (strcmp(proto, "default") == 0)
998 proto = NULL;
999 }
1000 ret = sa_extract_pgroup(node, handle,
1001 handle->pg, "security", proto, sectype);
1002 }
1003 /* Ignore everything else */
1004 }
1005 /*
1006 * Make sure we have a valid default group.
1007 * On first boot, default won't have any
1008 * protocols defined and won't be enabled (but
1009 * should be). "default" only has NFS enabled on it.
1010 */
1011 if (is_default) {
1012 char *state = sa_get_group_attr((sa_group_t)node,
1013 "state");
1014
1015 if (state == NULL) {
1016 /* set attribute to enabled */
1017 (void) sa_set_group_attr((sa_group_t)node,
1018 "state", "enabled");
1019 (void) sa_create_optionset((sa_group_t)node,
1020 "nfs");
1021 } else {
1022 sa_free_attr_string(state);
1023 }
1024 }
1025 /* Do a second pass if shares were found */
1026 if (have_shares && scf_iter_instance_pgs(iter, instance) == 0) {
1027 while (scf_iter_next_pg(iter, handle->pg) > 0) {
1028 /*
1029 * Have a pgroup so see if it is a
1030 * share optionset
1031 */
1032 err = scf_pg_get_name(handle->pg, buff,
1033 scf_max_name_len);
1034 if (err <= 0)
1035 continue;
1036 if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
1037 ret = sa_share_props_from_pgroup(node,
1038 handle, handle->pg, buff,
1039 sahandle);
1040 }
1041 }
1042 }
1043 }
1044 out:
1045 if (iter != NULL)
1046 scf_iter_destroy(iter);
1047 if (buff != NULL)
1048 free(buff);
1049 return (ret);
1050 }
1051
1052 /*
1053 * sa_extract_defaults(root, handle, instance)
1054 *
1055 * Local function to find the default properties that live in the
1056 * default instance's "operation" property group.
1057 */
1058
1059 static void
1060 sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
1061 scf_instance_t *instance)
1062 {
1063 xmlNodePtr node;
1064 scf_property_t *prop;
1065 scf_value_t *value;
1066 char *valuestr;
1067 ssize_t vallen;
1068
1069 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1070 prop = scf_property_create(handle->handle);
1071 value = scf_value_create(handle->handle);
1072 valuestr = malloc(vallen);
1073
1074 if (prop == NULL || value == NULL || vallen == 0 ||
1075 scf_instance_get_pg(instance, "operation", handle->pg) != 0)
1076 goto out;
1077
1078 if (scf_pg_get_property(handle->pg, "legacy-timestamp", prop) != 0)
1079 goto out;
1080
1081 /* Found the property so get the value */
1082 if (scf_property_get_value(prop, value) == 0) {
1083 if (scf_value_get_astring(value, valuestr, vallen) > 0) {
1084 node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
1085 NULL);
1086 if (node != NULL) {
1087 (void) xmlSetProp(node, (xmlChar *)"timestamp",
1088 (xmlChar *)valuestr);
1089 (void) xmlSetProp(node, (xmlChar *)"path",
1090 (xmlChar *)SA_LEGACY_DFSTAB);
1091 }
1092 }
1093 }
1094 out:
1095 if (valuestr != NULL)
1096 free(valuestr);
1097 if (value != NULL)
1098 scf_value_destroy(value);
1099 if (prop != NULL)
1100 scf_property_destroy(prop);
1101 }
1102
1103
1104 /*
1105 * sa_get_config(handle, root, doc, sahandle)
1106 *
1107 * Walk the SMF repository for /network/shares/group and find all the
1108 * instances. These become group names. Then add the XML structure
1109 * below the groups based on property groups and properties.
1110 */
1111 int
1112 sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle)
1113 {
1114 int ret = SA_OK;
1115 scf_instance_t *instance;
1116 scf_iter_t *iter;
1117 char buff[BUFSIZ * 2];
1118
1119 instance = scf_instance_create(handle->handle);
1120 iter = scf_iter_create(handle->handle);
1121 if (instance != NULL && iter != NULL) {
1122 if ((ret = scf_iter_service_instances(iter,
1123 handle->service)) == 0) {
1124 while ((ret = scf_iter_next_instance(iter,
1125 instance)) > 0) {
1126 if (scf_instance_get_name(instance, buff,
1127 sizeof (buff)) > 0) {
1128 if (strcmp(buff, "default") == 0)
1129 sa_extract_defaults(root,
1130 handle, instance);
1131 ret = sa_extract_group(root, handle,
1132 instance, sahandle);
1133 }
1134 }
1135 }
1136 }
1137
1138 /* Always cleanup these */
1139 if (instance != NULL)
1140 scf_instance_destroy(instance);
1141 if (iter != NULL)
1142 scf_iter_destroy(iter);
1143 return (ret);
1144 }
1145
1146 /*
1147 * sa_get_instance(handle, instance)
1148 *
1149 * Get the instance of the group service. This is actually the
1150 * specific group name. The instance is needed for all property and
1151 * control operations.
1152 */
1153
1154 int
1155 sa_get_instance(scfutilhandle_t *handle, char *instname)
1156 {
1157 if (scf_service_get_instance(handle->service, instname,
1158 handle->instance) != 0) {
1159 return (SA_NO_SUCH_GROUP);
1160 }
1161 return (SA_OK);
1162 }
1163
1164 /*
1165 * sa_create_instance(handle, instname)
1166 *
1167 * Create a new SMF service instance. There can only be one with a
1168 * given name.
1169 */
1170
1171 int
1172 sa_create_instance(scfutilhandle_t *handle, char *instname)
1173 {
1174 int ret = SA_OK;
1175 char instance[SA_GROUP_INST_LEN];
1176 if (scf_service_add_instance(handle->service, instname,
1177 handle->instance) != 0) {
1178 /* better error returns need to be added based on real error */
1179 if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
1180 ret = SA_NO_PERMISSION;
1181 else
1182 ret = SA_DUPLICATE_NAME;
1183 } else {
1184 /* have the service created, so enable it */
1185 (void) snprintf(instance, sizeof (instance), "%s:%s",
1186 SA_SVC_FMRI_BASE, instname);
1187 (void) smf_enable_instance(instance, 0);
1188 }
1189 return (ret);
1190 }
1191
1192 /*
1193 * sa_delete_instance(handle, instname)
1194 *
1195 * When a group goes away, we also remove the service instance.
1196 */
1197
1198 int
1199 sa_delete_instance(scfutilhandle_t *handle, char *instname)
1200 {
1201 int ret;
1202
1203 if (strcmp(instname, "default") == 0) {
1204 ret = SA_NO_PERMISSION;
1205 } else {
1206 if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
1207 if (scf_instance_delete(handle->instance) != 0)
1208 /* need better analysis */
1209 ret = SA_NO_PERMISSION;
1210 }
1211 }
1212 return (ret);
1213 }
1214
1215 /*
1216 * sa_create_pgroup(handle, pgroup)
1217 *
1218 * create a new property group
1219 */
1220
1221 int
1222 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
1223 {
1224 int ret = SA_OK;
1225 int persist = 0;
1226
1227 /*
1228 * Only create a handle if it doesn't exist. It is ok to exist
1229 * since the pg handle will be set as a side effect.
1230 */
1231 if (handle->pg == NULL)
1232 handle->pg = scf_pg_create(handle->handle);
1233
1234 /*
1235 * Special case for a non-persistent property group. This is
1236 * internal use only.
1237 */
1238 if (*pgroup == '*') {
1239 persist = SCF_PG_FLAG_NONPERSISTENT;
1240 pgroup++;
1241 }
1242
1243 /*
1244 * If the pgroup exists, we are done. If it doesn't, then we
1245 * need to actually add one to the service instance.
1246 */
1247 if (scf_instance_get_pg(handle->instance,
1248 pgroup, handle->pg) != 0) {
1249
1250 /* Doesn't exist so create one */
1251 if (scf_instance_add_pg(handle->instance, pgroup,
1252 SCF_GROUP_APPLICATION, persist, handle->pg) != 0) {
1253 switch (scf_error()) {
1254 case SCF_ERROR_PERMISSION_DENIED:
1255 ret = SA_NO_PERMISSION;
1256 break;
1257 default:
1258 ret = SA_SYSTEM_ERR;
1259 break;
1260 }
1261 }
1262 }
1263 return (ret);
1264 }
1265
1266 /*
1267 * sa_delete_pgroup(handle, pgroup)
1268 *
1269 * Remove the property group from the current instance of the service,
1270 * but only if it actually exists.
1271 */
1272
1273 int
1274 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
1275 {
1276 int ret = SA_OK;
1277 /*
1278 * Only delete if it does exist.
1279 */
1280 if (scf_instance_get_pg(handle->instance, pgroup, handle->pg) == 0) {
1281 /* does exist so delete it */
1282 if (scf_pg_delete(handle->pg) != 0)
1283 ret = SA_SYSTEM_ERR;
1284 } else {
1285 ret = SA_SYSTEM_ERR;
1286 }
1287 if (ret == SA_SYSTEM_ERR &&
1288 scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1289 ret = SA_NO_PERMISSION;
1290 }
1291 return (ret);
1292 }
1293
1294 /*
1295 * sa_start_transaction(handle, pgroup)
1296 *
1297 * Start an SMF transaction so we can deal with properties. it would
1298 * be nice to not have to expose this, but we have to in order to
1299 * optimize.
1300 *
1301 * Basic model is to hold the transaction in the handle and allow
1302 * property adds/deletes/updates to be added then close the
1303 * transaction (or abort). There may eventually be a need to handle
1304 * other types of transaction mechanisms but we don't do that now.
1305 *
1306 * An sa_start_transaction must be followed by either an
1307 * sa_end_transaction or sa_abort_transaction before another
1308 * sa_start_transaction can be done.
1309 */
1310
1311 int
1312 sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
1313 {
1314 int ret = SA_OK;
1315 /*
1316 * Lookup the property group and create it if it doesn't already
1317 * exist.
1318 */
1319 if (handle == NULL)
1320 return (SA_CONFIG_ERR);
1321
1322 if (handle->scf_state == SCH_STATE_INIT) {
1323 ret = sa_create_pgroup(handle, propgroup);
1324 if (ret == SA_OK) {
1325 handle->trans = scf_transaction_create(handle->handle);
1326 if (handle->trans != NULL) {
1327 if (scf_transaction_start(handle->trans,
1328 handle->pg) != 0) {
1329 ret = SA_SYSTEM_ERR;
1330 }
1331 if (ret != SA_OK) {
1332 scf_transaction_destroy(handle->trans);
1333 handle->trans = NULL;
1334 }
1335 } else {
1336 ret = SA_SYSTEM_ERR;
1337 }
1338 }
1339 }
1340 if (ret == SA_SYSTEM_ERR &&
1341 scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1342 ret = SA_NO_PERMISSION;
1343 }
1344 return (ret);
1345 }
1346
1347
1348 /*
1349 * sa_end_transaction(scfhandle, sahandle)
1350 *
1351 * Commit the changes that were added to the transaction in the
1352 * handle. Do all necessary cleanup.
1353 */
1354
1355 int
1356 sa_end_transaction(scfutilhandle_t *handle, sa_handle_t sahandle)
1357 {
1358 int ret = SA_OK;
1359
1360 if (handle == NULL || handle->trans == NULL || sahandle == NULL) {
1361 ret = SA_SYSTEM_ERR;
1362 } else {
1363 if (scf_transaction_commit(handle->trans) < 0)
1364 ret = SA_SYSTEM_ERR;
1365 scf_transaction_destroy_children(handle->trans);
1366 scf_transaction_destroy(handle->trans);
1367 if (ret == SA_OK)
1368 set_transaction_tstamp(sahandle);
1369 handle->trans = NULL;
1370 }
1371 return (ret);
1372 }
1373
1374 /*
1375 * sa_abort_transaction(handle)
1376 *
1377 * Abort the changes that were added to the transaction in the
1378 * handle. Do all necessary cleanup.
1379 */
1380
1381 void
1382 sa_abort_transaction(scfutilhandle_t *handle)
1383 {
1384 if (handle->trans != NULL) {
1385 scf_transaction_reset_all(handle->trans);
1386 scf_transaction_destroy_children(handle->trans);
1387 scf_transaction_destroy(handle->trans);
1388 handle->trans = NULL;
1389 }
1390 }
1391
1392 /*
1393 * set_transaction_tstamp(sahandle)
1394 *
1395 * After a successful transaction commit, update the timestamp of the
1396 * last transaction. This lets us detect changes from other processes.
1397 */
1398 static void
1399 set_transaction_tstamp(sa_handle_t sahandle)
1400 {
1401 char tstring[32];
1402 struct timeval tv;
1403 scfutilhandle_t *scfhandle;
1404
1405 if (sahandle == NULL || sahandle->scfhandle == NULL)
1406 return;
1407
1408 scfhandle = sahandle->scfhandle;
1409
1410 if (sa_get_instance(scfhandle, "default") != SA_OK)
1411 return;
1412
1413 if (gettimeofday(&tv, NULL) != 0)
1414 return;
1415
1416 if (sa_start_transaction(scfhandle, "*state") != SA_OK)
1417 return;
1418
1419 sahandle->tstrans = TSTAMP((*(timestruc_t *)&tv));
1420 (void) snprintf(tstring, sizeof (tstring), "%lld", sahandle->tstrans);
1421 if (sa_set_property(sahandle->scfhandle, "lastupdate", tstring) ==
1422 SA_OK) {
1423 /*
1424 * While best if it succeeds, a failure doesn't cause
1425 * problems and we will ignore it anyway.
1426 */
1427 (void) scf_transaction_commit(scfhandle->trans);
1428 scf_transaction_destroy_children(scfhandle->trans);
1429 scf_transaction_destroy(scfhandle->trans);
1430 } else {
1431 sa_abort_transaction(scfhandle);
1432 }
1433 }
1434
1435 /*
1436 * sa_set_property(handle, prop, value)
1437 *
1438 * Set a property transaction entry into the pending SMF transaction.
1439 */
1440
1441 int
1442 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
1443 {
1444 int ret = SA_OK;
1445 scf_value_t *value;
1446 scf_transaction_entry_t *entry;
1447 /*
1448 * Properties must be set in transactions and don't take
1449 * effect until the transaction has been ended/committed.
1450 */
1451 value = scf_value_create(handle->handle);
1452 entry = scf_entry_create(handle->handle);
1453 if (value != NULL && entry != NULL) {
1454 if (scf_transaction_property_change(handle->trans, entry,
1455 propname, SCF_TYPE_ASTRING) == 0 ||
1456 scf_transaction_property_new(handle->trans, entry,
1457 propname, SCF_TYPE_ASTRING) == 0) {
1458 if (scf_value_set_astring(value, valstr) == 0) {
1459 if (scf_entry_add_value(entry, value) != 0) {
1460 ret = SA_SYSTEM_ERR;
1461 scf_value_destroy(value);
1462 }
1463 /* The value is in the transaction */
1464 value = NULL;
1465 } else {
1466 /* Value couldn't be constructed */
1467 ret = SA_SYSTEM_ERR;
1468 }
1469 /* The entry is in the transaction */
1470 entry = NULL;
1471 } else {
1472 ret = SA_SYSTEM_ERR;
1473 }
1474 } else {
1475 ret = SA_SYSTEM_ERR;
1476 }
1477 if (ret == SA_SYSTEM_ERR) {
1478 switch (scf_error()) {
1479 case SCF_ERROR_PERMISSION_DENIED:
1480 ret = SA_NO_PERMISSION;
1481 break;
1482 }
1483 }
1484 /*
1485 * Cleanup if there were any errors that didn't leave these
1486 * values where they would be cleaned up later.
1487 */
1488 if (value != NULL)
1489 scf_value_destroy(value);
1490 if (entry != NULL)
1491 scf_entry_destroy(entry);
1492 return (ret);
1493 }
1494
1495 /*
1496 * check_resource(share)
1497 *
1498 * Check to see if share has any persistent resources. We don't want
1499 * to save if they are all transient.
1500 */
1501 static int
1502 check_resource(sa_share_t share)
1503 {
1504 sa_resource_t resource;
1505 int ret = B_FALSE;
1506
1507 for (resource = sa_get_share_resource(share, NULL);
1508 resource != NULL && ret == B_FALSE;
1509 resource = sa_get_next_resource(resource)) {
1510 char *type;
1511 type = sa_get_resource_attr(resource, "type");
1512 if (type != NULL) {
1513 if (strcmp(type, "transient") != 0) {
1514 ret = B_TRUE;
1515 }
1516 sa_free_attr_string(type);
1517 }
1518 }
1519 return (ret);
1520 }
1521
1522 /*
1523 * sa_set_resource_property(handle, prop, value)
1524 *
1525 * set a property transaction entry into the pending SMF
1526 * transaction. We don't want to include any transient resources
1527 */
1528
1529 static int
1530 sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share)
1531 {
1532 int ret = SA_OK;
1533 scf_value_t *value;
1534 scf_transaction_entry_t *entry;
1535 sa_resource_t resource;
1536 char *valstr;
1537 char *idstr;
1538 char *description;
1539 char *propstr = NULL;
1540 size_t strsize;
1541
1542 /* don't bother if no persistent resources */
1543 if (check_resource(share) == B_FALSE)
1544 return (ret);
1545
1546 /*
1547 * properties must be set in transactions and don't take
1548 * effect until the transaction has been ended/committed.
1549 */
1550 entry = scf_entry_create(handle->handle);
1551 if (entry == NULL)
1552 return (SA_SYSTEM_ERR);
1553
1554 if (scf_transaction_property_change(handle->trans, entry,
1555 "resource", SCF_TYPE_ASTRING) != 0 &&
1556 scf_transaction_property_new(handle->trans, entry,
1557 "resource", SCF_TYPE_ASTRING) != 0) {
1558 scf_entry_destroy(entry);
1559 return (SA_SYSTEM_ERR);
1560
1561 }
1562 for (resource = sa_get_share_resource(share, NULL);
1563 resource != NULL;
1564 resource = sa_get_next_resource(resource)) {
1565 value = scf_value_create(handle->handle);
1566 if (value == NULL) {
1567 ret = SA_NO_MEMORY;
1568 break;
1569 }
1570 /* Get size of complete string */
1571 valstr = sa_get_resource_attr(resource, "name");
1572 idstr = sa_get_resource_attr(resource, "id");
1573 description = sa_get_resource_description(resource);
1574 strsize = (valstr != NULL) ? strlen(valstr) : 0;
1575 strsize += (idstr != NULL) ? strlen(idstr) : 0;
1576 strsize += (description != NULL) ? strlen(description) : 0;
1577 if (strsize > 0) {
1578 strsize += 3; /* add nul and ':' */
1579 propstr = (char *)malloc(strsize);
1580 if (propstr == NULL) {
1581 scf_value_destroy(value);
1582 ret = SA_NO_MEMORY;
1583 goto err;
1584 }
1585 if (idstr == NULL)
1586 (void) snprintf(propstr, strsize, "%s",
1587 valstr ? valstr : "");
1588 else
1589 (void) snprintf(propstr, strsize, "%s:%s:%s",
1590 idstr, valstr ? valstr : "",
1591 description ? description : "");
1592 if (scf_value_set_astring(value, propstr) != 0) {
1593 ret = SA_SYSTEM_ERR;
1594 free(propstr);
1595 scf_value_destroy(value);
1596 break;
1597 }
1598 if (scf_entry_add_value(entry, value) != 0) {
1599 ret = SA_SYSTEM_ERR;
1600 free(propstr);
1601 scf_value_destroy(value);
1602 break;
1603 }
1604 /* the value is in the transaction */
1605 value = NULL;
1606 free(propstr);
1607 }
1608 err:
1609 if (valstr != NULL) {
1610 sa_free_attr_string(valstr);
1611 valstr = NULL;
1612 }
1613 if (idstr != NULL) {
1614 sa_free_attr_string(idstr);
1615 idstr = NULL;
1616 }
1617 if (description != NULL) {
1618 sa_free_share_description(description);
1619 description = NULL;
1620 }
1621 }
1622 /* the entry is in the transaction */
1623 entry = NULL;
1624
1625 if (valstr != NULL)
1626 sa_free_attr_string(valstr);
1627 if (idstr != NULL)
1628 sa_free_attr_string(idstr);
1629 if (description != NULL)
1630 sa_free_share_description(description);
1631
1632 if (ret == SA_SYSTEM_ERR) {
1633 switch (scf_error()) {
1634 case SCF_ERROR_PERMISSION_DENIED:
1635 ret = SA_NO_PERMISSION;
1636 break;
1637 }
1638 }
1639 /*
1640 * cleanup if there were any errors that didn't leave
1641 * these values where they would be cleaned up later.
1642 */
1643 if (entry != NULL)
1644 scf_entry_destroy(entry);
1645
1646 return (ret);
1647 }
1648
1649 /*
1650 * sa_commit_share(handle, group, share)
1651 *
1652 * Commit this share to the repository.
1653 * properties are added if they exist but can be added later.
1654 * Need to add to dfstab and sharetab, if appropriate.
1655 */
1656 int
1657 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1658 {
1659 int ret = SA_OK;
1660 char *groupname;
1661 char *name;
1662 char *description;
1663 char *sharename;
1664 ssize_t proplen;
1665 char *propstring;
1666
1667 /*
1668 * Don't commit in the zfs group. We do commit legacy
1669 * (default) and all other groups/shares. ZFS is handled
1670 * through the ZFS configuration rather than SMF.
1671 */
1672
1673 groupname = sa_get_group_attr(group, "name");
1674 if (groupname != NULL) {
1675 if (strcmp(groupname, "zfs") == 0) {
1676 /*
1677 * Adding to the ZFS group will result in the sharenfs
1678 * property being set but we don't want to do anything
1679 * SMF related at this point.
1680 */
1681 sa_free_attr_string(groupname);
1682 return (ret);
1683 }
1684 }
1685
1686 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1687 propstring = malloc(proplen);
1688 if (propstring == NULL)
1689 ret = SA_NO_MEMORY;
1690
1691 if (groupname != NULL && ret == SA_OK) {
1692 ret = sa_get_instance(handle, groupname);
1693 sa_free_attr_string(groupname);
1694 groupname = NULL;
1695 sharename = sa_get_share_attr(share, "id");
1696 if (sharename == NULL) {
1697 /* slipped by */
1698 char shname[SA_SHARE_UUID_BUFLEN];
1699 generate_unique_sharename(shname);
1700 (void) xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
1701 (xmlChar *)shname);
1702 sharename = strdup(shname);
1703 }
1704 if (sharename != NULL) {
1705 sigset_t old, new;
1706 /*
1707 * Have a share name allocated so create a pgroup for
1708 * it. It may already exist, but that is OK. In order
1709 * to avoid creating a share pgroup that doesn't have
1710 * a path property, block signals around the critical
1711 * region of creating the share pgroup and props.
1712 */
1713 (void) sigprocmask(SIG_BLOCK, NULL, &new);
1714 (void) sigaddset(&new, SIGHUP);
1715 (void) sigaddset(&new, SIGINT);
1716 (void) sigaddset(&new, SIGQUIT);
1717 (void) sigaddset(&new, SIGTSTP);
1718 (void) sigprocmask(SIG_SETMASK, &new, &old);
1719
1720 ret = sa_create_pgroup(handle, sharename);
1721 if (ret == SA_OK) {
1722 /*
1723 * Now start the transaction for the
1724 * properties that define this share. They may
1725 * exist so attempt to update before create.
1726 */
1727 ret = sa_start_transaction(handle, sharename);
1728 }
1729 if (ret == SA_OK) {
1730 name = sa_get_share_attr(share, "path");
1731 if (name != NULL) {
1732 /*
1733 * There needs to be a path
1734 * for a share to exist.
1735 */
1736 ret = sa_set_property(handle, "path",
1737 name);
1738 sa_free_attr_string(name);
1739 } else {
1740 ret = SA_NO_MEMORY;
1741 }
1742 }
1743 if (ret == SA_OK) {
1744 name = sa_get_share_attr(share, "drive-letter");
1745 if (name != NULL) {
1746 /* A drive letter may exist for SMB */
1747 ret = sa_set_property(handle,
1748 "drive-letter", name);
1749 sa_free_attr_string(name);
1750 }
1751 }
1752 if (ret == SA_OK) {
1753 name = sa_get_share_attr(share, "exclude");
1754 if (name != NULL) {
1755 /*
1756 * In special cases need to
1757 * exclude proto enable.
1758 */
1759 ret = sa_set_property(handle,
1760 "exclude", name);
1761 sa_free_attr_string(name);
1762 }
1763 }
1764 if (ret == SA_OK) {
1765 /*
1766 * If there are resource names, bundle them up
1767 * and save appropriately.
1768 */
1769 ret = sa_set_resource_property(handle, share);
1770 }
1771
1772 if (ret == SA_OK) {
1773 description = sa_get_share_description(share);
1774 if (description != NULL) {
1775 ret = sa_set_property(handle,
1776 "description",
1777 description);
1778 sa_free_share_description(description);
1779 }
1780 }
1781 /* Make sure we cleanup the transaction */
1782 if (ret == SA_OK) {
1783 sa_handle_t sahandle;
1784 sahandle = sa_find_group_handle(group);
1785 if (sahandle != NULL)
1786 ret = sa_end_transaction(handle,
1787 sahandle);
1788 else
1789 ret = SA_SYSTEM_ERR;
1790 } else {
1791 sa_abort_transaction(handle);
1792 }
1793
1794 (void) sigprocmask(SIG_SETMASK, &old, NULL);
1795
1796 free(sharename);
1797 }
1798 }
1799 if (ret == SA_SYSTEM_ERR) {
1800 int err = scf_error();
1801 if (err == SCF_ERROR_PERMISSION_DENIED)
1802 ret = SA_NO_PERMISSION;
1803 }
1804 if (propstring != NULL)
1805 free(propstring);
1806 if (groupname != NULL)
1807 sa_free_attr_string(groupname);
1808
1809 return (ret);
1810 }
1811
1812 /*
1813 * remove_resources(handle, share, shareid)
1814 *
1815 * If the share has resources, remove all of them and their
1816 * optionsets.
1817 */
1818 static int
1819 remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid)
1820 {
1821 sa_resource_t resource;
1822 sa_optionset_t opt;
1823 char *proto;
1824 char *id;
1825 ssize_t proplen;
1826 char *propstring;
1827 int ret = SA_OK;
1828
1829 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1830 propstring = malloc(proplen);
1831 if (propstring == NULL)
1832 return (SA_NO_MEMORY);
1833
1834 for (resource = sa_get_share_resource(share, NULL);
1835 resource != NULL; resource = sa_get_next_resource(resource)) {
1836 id = sa_get_resource_attr(resource, "id");
1837 if (id == NULL)
1838 continue;
1839 for (opt = sa_get_optionset(resource, NULL);
1840 opt != NULL; opt = sa_get_next_optionset(resource)) {
1841 proto = sa_get_optionset_attr(opt, "type");
1842 if (proto != NULL) {
1843 (void) snprintf(propstring, proplen,
1844 "%s_%s_%s", shareid, proto, id);
1845 ret = sa_delete_pgroup(handle, propstring);
1846 sa_free_attr_string(proto);
1847 }
1848 }
1849 sa_free_attr_string(id);
1850 }
1851 free(propstring);
1852 return (ret);
1853 }
1854
1855 /*
1856 * sa_delete_share(handle, group, share)
1857 *
1858 * Remove the specified share from the group (and service instance).
1859 */
1860
1861 int
1862 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1863 {
1864 int ret = SA_OK;
1865 char *groupname = NULL;
1866 char *shareid = NULL;
1867 sa_optionset_t opt;
1868 sa_security_t sec;
1869 ssize_t proplen;
1870 char *propstring;
1871
1872 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1873 propstring = malloc(proplen);
1874 if (propstring == NULL)
1875 ret = SA_NO_MEMORY;
1876
1877 if (ret == SA_OK) {
1878 groupname = sa_get_group_attr(group, "name");
1879 shareid = sa_get_share_attr(share, "id");
1880 if (groupname == NULL || shareid == NULL) {
1881 ret = SA_CONFIG_ERR;
1882 goto out;
1883 }
1884 ret = sa_get_instance(handle, groupname);
1885 if (ret == SA_OK) {
1886 /* If a share has resources, remove them */
1887 ret = remove_resources(handle, share, shareid);
1888 /* If a share has properties, remove them */
1889 ret = sa_delete_pgroup(handle, shareid);
1890 for (opt = sa_get_optionset(share, NULL);
1891 opt != NULL;
1892 opt = sa_get_next_optionset(opt)) {
1893 char *proto;
1894 proto = sa_get_optionset_attr(opt, "type");
1895 if (proto != NULL) {
1896 (void) snprintf(propstring,
1897 proplen, "%s_%s", shareid,
1898 proto);
1899 ret = sa_delete_pgroup(handle,
1900 propstring);
1901 sa_free_attr_string(proto);
1902 } else {
1903 ret = SA_NO_MEMORY;
1904 }
1905 }
1906 /*
1907 * If a share has security/negotiable
1908 * properties, remove them.
1909 */
1910 for (sec = sa_get_security(share, NULL, NULL);
1911 sec != NULL;
1912 sec = sa_get_next_security(sec)) {
1913 char *proto;
1914 char *sectype;
1915 proto = sa_get_security_attr(sec, "type");
1916 sectype = sa_get_security_attr(sec, "sectype");
1917 if (proto != NULL && sectype != NULL) {
1918 (void) snprintf(propstring, proplen,
1919 "%s_%s_%s", shareid, proto,
1920 sectype);
1921 ret = sa_delete_pgroup(handle,
1922 propstring);
1923 } else {
1924 ret = SA_NO_MEMORY;
1925 }
1926 if (proto != NULL)
1927 sa_free_attr_string(proto);
1928 if (sectype != NULL)
1929 sa_free_attr_string(sectype);
1930 }
1931 }
1932 }
1933 out:
1934 if (groupname != NULL)
1935 sa_free_attr_string(groupname);
1936 if (shareid != NULL)
1937 sa_free_attr_string(shareid);
1938 if (propstring != NULL)
1939 free(propstring);
1940
1941 return (ret);
1942 }