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