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 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24 /*
25 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2017 RackTop Systems.
27 */
28
29
30 /*
31 * XML document manipulation routines
32 *
33 * These routines provide translation to and from the internal representation to
34 * XML. Directionally-oriented verbs are with respect to the external source,
35 * so lxml_get_service() fetches a service from the XML file into the
36 * internal representation.
37 */
38
39 #include <libxml/parser.h>
40 #include <libxml/xinclude.h>
41
42 #include <assert.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #include <libintl.h>
46 #include <libscf.h>
47 #include <libscf_priv.h>
48 #include <libuutil.h>
49 #include <sasl/saslutil.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <limits.h>
53
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <unistd.h>
57
58 #include <sys/param.h>
59 #include "manifest_hash.h"
60
61 #include "svccfg.h"
62 #include "notify_params.h"
63
64 /*
65 * snprintf(3C) format strings for constructing property names that include
66 * the locale designation. Use %s to indicate where the locale should go.
67 *
68 * The VALUE_* symbols are an exception. The firs %s will be replaced with
69 * "value_". The second %s will be replaced by the name of the value and
70 * %%s will be replaced by the locale designation. These formats are
71 * processed twice by snprintf(3C). The first time captures the value name
72 * and the second time captures the locale.
73 */
74 #define LOCALE_ONLY_FMT ("%s")
75 #define COMMON_NAME_FMT ("common_name_%s")
76 #define DESCRIPTION_FMT ("description_%s")
77 #define UNITS_FMT ("units_%s")
78 #define VALUE_COMMON_NAME_FMT ("%s%s_common_name_%%s")
79 #define VALUE_DESCRIPTION_FMT ("%s%s_description_%%s")
80
81 /* Attribute names */
82 const char * const delete_attr = "delete";
83 const char * const enabled_attr = "enabled";
84 const char * const lang_attr = "lang";
85 const char * const manpath_attr = "manpath";
86 const char * const max_attr = "max";
87 const char * const min_attr = "min";
88 const char * const name_attr = "name";
89 const char * const override_attr = "override";
90 const char * const required_attr = "required";
91 const char * const section_attr = "section";
92 const char * const set_attr = "set";
93 const char * const target_attr = "target";
94 const char * const timeout_seconds_attr = "timeout_seconds";
95 const char * const title_attr = "title";
96 const char * const type_attr = "type";
97 const char * const uri_attr = "uri";
98 const char * const value_attr = "value";
99 const char * const version_attr = "version";
100 const char * const xml_lang_attr = "xml:lang";
101 const char * const active_attr = "active";
102
103 /* Attribute values */
104 const char * const all_value = "all";
105
106 const char * const true = "true";
107 const char * const false = "false";
108
109 /*
110 * The following list must be kept in the same order as that of
111 * element_t array
112 */
113 static const char *lxml_elements[] = {
114 "astring_list", /* SC_ASTRING */
115 "boolean_list", /* SC_BOOLEAN */
116 "cardinality", /* SC_CARDINALITY */
117 "choices", /* SC_CHOICES */
118 "common_name", /* SC_COMMON_NAME */
119 "constraints", /* SC_CONSTRAINTS */
120 "count_list", /* SC_COUNT */
121 "create_default_instance", /* SC_INSTANCE_CREATE_DEFAULT */
122 "dependency", /* SC_DEPENDENCY */
123 "dependent", /* SC_DEPENDENT */
124 "description", /* SC_DESCRIPTION */
125 "doc_link", /* SC_DOC_LINK */
126 "documentation", /* SC_DOCUMENTATION */
127 "enabled", /* SC_ENABLED */
128 "event", /* SC_EVENT */
129 "exec_method", /* SC_EXEC_METHOD */
130 "fmri_list", /* SC_FMRI */
131 "host_list", /* SC_HOST */
132 "hostname_list", /* SC_HOSTNAME */
133 "include_values", /* SC_INCLUDE_VALUES */
134 "instance", /* SC_INSTANCE */
135 "integer_list", /* SC_INTEGER */
136 "internal_separators", /* SC_INTERNAL_SEPARATORS */
137 "loctext", /* SC_LOCTEXT */
138 "manpage", /* SC_MANPAGE */
139 "method_context", /* SC_METHOD_CONTEXT */
140 "method_credential", /* SC_METHOD_CREDENTIAL */
141 "method_profile", /* SC_METHOD_PROFILE */
142 "method_environment", /* SC_METHOD_ENVIRONMENT */
143 "envvar", /* SC_METHOD_ENVVAR */
144 "net_address_list", /* SC_NET_ADDR */
145 "net_address_v4_list", /* SC_NET_ADDR_V4 */
146 "net_address_v6_list", /* SC_NET_ADDR_V6 */
147 "notification_parameters", /* SC_NOTIFICATION_PARAMETERS */
148 "opaque_list", /* SC_OPAQUE */
149 "parameter", /* SC_PARAMETER */
150 "paramval", /* SC_PARAMVAL */
151 "pg_pattern", /* SC_PG_PATTERN */
152 "prop_pattern", /* SC_PROP_PATTERN */
153 "property", /* SC_PROPERTY */
154 "property_group", /* SC_PROPERTY_GROUP */
155 "propval", /* SC_PROPVAL */
156 "range", /* SC_RANGE */
157 "restarter", /* SC_RESTARTER */
158 "service", /* SC_SERVICE */
159 "service_bundle", /* SC_SERVICE_BUNDLE */
160 "service_fmri", /* SC_SERVICE_FMRI */
161 "single_instance", /* SC_INSTANCE_SINGLE */
162 "stability", /* SC_STABILITY */
163 "template", /* SC_TEMPLATE */
164 "time_list", /* SC_TIME */
165 "type", /* SC_TYPE */
166 "units", /* SC_UNITS */
167 "uri_list", /* SC_URI */
168 "ustring_list", /* SC_USTRING */
169 "value", /* SC_VALUE */
170 "value_node", /* SC_VALUE_NODE */
171 "values", /* SC_VALUES */
172 "visibility", /* SC_VISIBILITY */
173 "xi:fallback", /* SC_XI_FALLBACK */
174 "xi:include" /* SC_XI_INCLUDE */
175 };
176
177 /*
178 * The following list must be kept in the same order as that of
179 * element_t array
180 */
181 static const char *lxml_prop_types[] = {
182 "astring", /* SC_ASTRING */
183 "boolean", /* SC_BOOLEAN */
184 "", /* SC_CARDINALITY */
185 "", /* SC_CHOICES */
186 "", /* SC_COMMON_NAME */
187 "", /* SC_CONSTRAINTS */
188 "count", /* SC_COUNT */
189 "", /* SC_INSTANCE_CREATE_DEFAULT */
190 "", /* SC_DEPENDENCY */
191 "", /* SC_DEPENDENT */
192 "", /* SC_DESCRIPTION */
193 "", /* SC_DOC_LINK */
194 "", /* SC_DOCUMENTATION */
195 "", /* SC_ENABLED */
196 "", /* SC_EVENT */
197 "", /* SC_EXEC_METHOD */
198 "fmri", /* SC_FMRI */
199 "host", /* SC_HOST */
200 "hostname", /* SC_HOSTNAME */
201 "", /* SC_INCLUDE_VALUES */
202 "", /* SC_INSTANCE */
203 "integer", /* SC_INTEGER */
204 "", /* SC_INTERNAL_SEPARATORS */
205 "", /* SC_LOCTEXT */
206 "", /* SC_MANPAGE */
207 "", /* SC_METHOD_CONTEXT */
208 "", /* SC_METHOD_CREDENTIAL */
209 "", /* SC_METHOD_PROFILE */
210 "", /* SC_METHOD_ENVIRONMENT */
211 "", /* SC_METHOD_ENVVAR */
212 "net_address", /* SC_NET_ADDR */
213 "net_address_v4", /* SC_NET_ADDR_V4 */
214 "net_address_v6", /* SC_NET_ADDR_V6 */
215 "", /* SC_NOTIFICATION_PARAMETERS */
216 "opaque", /* SC_OPAQUE */
217 "", /* SC_PARAMETER */
218 "", /* SC_PARAMVAL */
219 "", /* SC_PG_PATTERN */
220 "", /* SC_PROP_PATTERN */
221 "", /* SC_PROPERTY */
222 "", /* SC_PROPERTY_GROUP */
223 "", /* SC_PROPVAL */
224 "", /* SC_RANGE */
225 "", /* SC_RESTARTER */
226 "", /* SC_SERVICE */
227 "", /* SC_SERVICE_BUNDLE */
228 "", /* SC_SERVICE_FMRI */
229 "", /* SC_INSTANCE_SINGLE */
230 "", /* SC_STABILITY */
231 "", /* SC_TEMPLATE */
232 "time", /* SC_TIME */
233 "", /* SC_TYPE */
234 "", /* SC_UNITS */
235 "uri", /* SC_URI */
236 "ustring", /* SC_USTRING */
237 "", /* SC_VALUE */
238 "", /* SC_VALUE_NODE */
239 "", /* SC_VALUES */
240 "", /* SC_VISIBILITY */
241 "", /* SC_XI_FALLBACK */
242 "" /* SC_XI_INCLUDE */
243 };
244
245 int
246 lxml_init()
247 {
248 if (getenv("SVCCFG_NOVALIDATE") == NULL) {
249 /*
250 * DTD validation, with line numbers.
251 */
252 (void) xmlLineNumbersDefault(1);
253 xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
254 xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS;
255 }
256
257 return (0);
258 }
259
260 static bundle_type_t
261 lxml_xlate_bundle_type(xmlChar *type)
262 {
263 if (xmlStrcmp(type, (const xmlChar *)"manifest") == 0)
264 return (SVCCFG_MANIFEST);
265
266 if (xmlStrcmp(type, (const xmlChar *)"profile") == 0)
267 return (SVCCFG_PROFILE);
268
269 if (xmlStrcmp(type, (const xmlChar *)"archive") == 0)
270 return (SVCCFG_ARCHIVE);
271
272 return (SVCCFG_UNKNOWN_BUNDLE);
273 }
274
275 static service_type_t
276 lxml_xlate_service_type(xmlChar *type)
277 {
278 if (xmlStrcmp(type, (const xmlChar *)"service") == 0)
279 return (SVCCFG_SERVICE);
280
281 if (xmlStrcmp(type, (const xmlChar *)"restarter") == 0)
282 return (SVCCFG_RESTARTER);
283
284 if (xmlStrcmp(type, (const xmlChar *)"milestone") == 0)
285 return (SVCCFG_MILESTONE);
286
287 return (SVCCFG_UNKNOWN_SERVICE);
288 }
289
290 static element_t
291 lxml_xlate_element(const xmlChar *tag)
292 {
293 int i;
294
295 for (i = 0; i < sizeof (lxml_elements) / sizeof (char *); i++)
296 if (xmlStrcmp(tag, (const xmlChar *)lxml_elements[i]) == 0)
297 return ((element_t)i);
298
299 return ((element_t)-1);
300 }
301
302 static uint_t
303 lxml_xlate_boolean(const xmlChar *value)
304 {
305 if (xmlStrcmp(value, (const xmlChar *)true) == 0)
306 return (1);
307
308 if (xmlStrcmp(value, (const xmlChar *)false) == 0)
309 return (0);
310
311 uu_die(gettext("illegal boolean value \"%s\"\n"), value);
312
313 /*NOTREACHED*/
314 }
315
316 static scf_type_t
317 lxml_element_to_type(element_t type)
318 {
319 switch (type) {
320 case SC_ASTRING: return (SCF_TYPE_ASTRING);
321 case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN);
322 case SC_COUNT: return (SCF_TYPE_COUNT);
323 case SC_FMRI: return (SCF_TYPE_FMRI);
324 case SC_HOST: return (SCF_TYPE_HOST);
325 case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME);
326 case SC_INTEGER: return (SCF_TYPE_INTEGER);
327 case SC_NET_ADDR: return (SCF_TYPE_NET_ADDR);
328 case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4);
329 case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6);
330 case SC_OPAQUE: return (SCF_TYPE_OPAQUE);
331 case SC_TIME: return (SCF_TYPE_TIME);
332 case SC_URI: return (SCF_TYPE_URI);
333 case SC_USTRING: return (SCF_TYPE_USTRING);
334
335 default:
336 uu_die(gettext("unknown value type (%d)\n"), type);
337 }
338
339 /* NOTREACHED */
340 }
341
342 static element_t
343 lxml_type_to_element(scf_type_t type)
344 {
345 switch (type) {
346 case SCF_TYPE_ASTRING: return (SC_ASTRING);
347 case SCF_TYPE_BOOLEAN: return (SC_BOOLEAN);
348 case SCF_TYPE_COUNT: return (SC_COUNT);
349 case SCF_TYPE_FMRI: return (SC_FMRI);
350 case SCF_TYPE_HOST: return (SC_HOST);
351 case SCF_TYPE_HOSTNAME: return (SC_HOSTNAME);
352 case SCF_TYPE_INTEGER: return (SC_INTEGER);
353 case SCF_TYPE_NET_ADDR: return (SC_NET_ADDR);
354 case SCF_TYPE_NET_ADDR_V4: return (SC_NET_ADDR_V4);
355 case SCF_TYPE_NET_ADDR_V6: return (SC_NET_ADDR_V6);
356 case SCF_TYPE_OPAQUE: return (SC_OPAQUE);
357 case SCF_TYPE_TIME: return (SC_TIME);
358 case SCF_TYPE_URI: return (SC_URI);
359 case SCF_TYPE_USTRING: return (SC_USTRING);
360
361 default:
362 uu_die(gettext("unknown value type (%d)\n"), type);
363 }
364
365 /* NOTREACHED */
366 }
367
368 /*
369 * Create a SCF_TYPE_BOOLEAN property name pname and attach it to the
370 * property group at pgrp. The value of the property will be set from the
371 * attribute named attr. attr must have a value of 0, 1, true or false.
372 *
373 * Zero is returned on success. An error is indicated by -1. It indicates
374 * that either the attribute had an invalid value or that we could not
375 * attach the property to pgrp. The attribute should not have an invalid
376 * value if the DTD is correctly written.
377 */
378 static int
379 new_bool_prop_from_attr(pgroup_t *pgrp, const char *pname, xmlNodePtr n,
380 const char *attr)
381 {
382 uint64_t bool;
383 xmlChar *val;
384 property_t *p;
385 int r;
386
387 val = xmlGetProp(n, (xmlChar *)attr);
388 if (val == NULL)
389 return (0);
390
391 if ((xmlStrcmp(val, (xmlChar *)"0") == 0) ||
392 (xmlStrcmp(val, (xmlChar *)"false") == 0)) {
393 bool = 0;
394 } else if ((xmlStrcmp(val, (xmlChar *)"1") == 0) ||
395 (xmlStrcmp(val, (xmlChar *)"true") == 0)) {
396 bool = 1;
397 } else {
398 xmlFree(val);
399 return (-1);
400 }
401 xmlFree(val);
402 p = internal_property_create(pname, SCF_TYPE_BOOLEAN, 1, bool);
403 r = internal_attach_property(pgrp, p);
404
405 if (r != 0)
406 internal_property_free(p);
407
408 return (r);
409 }
410
411 static int
412 new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
413 xmlNodePtr n, const char *attr)
414 {
415 xmlChar *val;
416 property_t *p;
417 int r;
418
419 val = xmlGetProp(n, (xmlChar *)attr);
420
421 /* Some properties may have multiple values. */
422 p = internal_property_find(pgrp, pname);
423 if (p != NULL) {
424 value_t *v = internal_value_new();
425 v->sc_u.sc_string = (char *)val;
426 v->sc_type = ty;
427 internal_attach_value(p, v);
428 return (0);
429 }
430
431 p = internal_property_create(pname, ty, 1, val);
432 r = internal_attach_property(pgrp, p);
433
434 if (r != 0)
435 internal_property_free(p);
436
437 return (r);
438 }
439
440 static int
441 new_opt_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
442 xmlNodePtr n, const char *attr, const char *dflt)
443 {
444 xmlChar *val;
445 property_t *p;
446 int r;
447
448 val = xmlGetProp(n, (xmlChar *)attr);
449 if (val == NULL) {
450 if (dflt == NULL) {
451 /*
452 * A missing attribute is considered to be a
453 * success in this function, because many of the
454 * attributes are optional. Missing non-optional
455 * attributes will be detected later when template
456 * validation is done.
457 */
458 return (0);
459 } else {
460 val = (xmlChar *)dflt;
461 }
462 }
463
464 p = internal_property_create(pname, ty, 1, val);
465 r = internal_attach_property(pgrp, p);
466
467 if (r != 0)
468 internal_property_free(p);
469
470 return (r);
471 }
472
473 static int
474 lxml_ignorable_block(xmlNodePtr n)
475 {
476 return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 ||
477 xmlStrcmp(n->name, (xmlChar *)"comment") == 0) ? 1 : 0);
478 }
479
480 static void
481 lxml_validate_element(xmlNodePtr n)
482 {
483 xmlValidCtxtPtr vcp;
484
485 if (n->doc == NULL)
486 uu_die(gettext("Could not validate element\n"));
487
488 if (n->doc->extSubset == NULL) {
489 xmlDtdPtr dtd;
490 dtd = xmlParseDTD(NULL, n->doc->intSubset->SystemID);
491
492 if (dtd == NULL) {
493 uu_die(gettext("Could not parse DTD \"%s\".\n"),
494 n->doc->intSubset->SystemID);
495 }
496
497 n->doc->extSubset = dtd;
498 }
499
500 vcp = xmlNewValidCtxt();
501 if (vcp == NULL)
502 uu_die(gettext("could not allocate memory"));
503
504 vcp->warning = xmlParserValidityWarning;
505 vcp->error = xmlParserValidityError;
506
507 if (xmlValidateElement(vcp, n->doc, n) == 0)
508 uu_die(gettext("Document is not valid.\n"));
509
510 xmlFreeValidCtxt(vcp);
511 }
512
513 static int
514 lxml_validate_string_value(scf_type_t type, const char *v)
515 {
516 static scf_value_t *scf_value = NULL;
517 static scf_handle_t *scf_hndl = NULL;
518
519 if (scf_hndl == NULL && (scf_hndl = scf_handle_create(SCF_VERSION)) ==
520 NULL)
521 return (-1);
522
523 if (scf_value == NULL && (scf_value = scf_value_create(scf_hndl)) ==
524 NULL)
525 return (-1);
526
527 return (scf_value_set_from_string(scf_value, type, v));
528 }
529
530 static void
531 lxml_free_str(value_t *val)
532 {
533 free(val->sc_u.sc_string);
534 }
535
536 /*
537 * Take a value_t structure and a type and value. Based on the type
538 * ensure that the value is of that type. If so store the value in
539 * the correct location of the value_t structure.
540 *
541 * If the value is NULL, the value_t structure will have been created
542 * and the value would have ultimately been stored as a string value
543 * but at the time the type was unknown. Now the type should be known
544 * so take the type and value from value_t and validate and store
545 * the value correctly if the value is of the stated type.
546 */
547 void
548 lxml_store_value(value_t *v, element_t type, const xmlChar *value)
549 {
550 char *endptr;
551 int fov = 0;
552 scf_type_t scf_type = SCF_TYPE_INVALID;
553
554 if (value == NULL) {
555 type = lxml_type_to_element(v->sc_type);
556 value = (const xmlChar *)v->sc_u.sc_string;
557 fov = 1;
558 }
559
560 switch (type) {
561 case SC_COUNT:
562 /*
563 * Although an SC_COUNT represents a uint64_t the use
564 * of a negative value is acceptable due to the usage
565 * established by inetd(1M).
566 */
567 errno = 0;
568 v->sc_u.sc_count = strtoull((char *)value, &endptr, 10);
569 if (errno != 0 || endptr == (char *)value || *endptr)
570 uu_die(gettext("illegal value \"%s\" for "
571 "%s (%s)\n"), (char *)value,
572 lxml_prop_types[type],
573 (errno) ? strerror(errno) :
574 gettext("Illegal character"));
575 break;
576 case SC_INTEGER:
577 errno = 0;
578 v->sc_u.sc_integer = strtoll((char *)value, &endptr, 10);
579 if (errno != 0 || *endptr)
580 uu_die(gettext("illegal value \"%s\" for "
581 "%s (%s)\n"), (char *)value,
582 lxml_prop_types[type],
583 (errno) ? strerror(errno) : "Illegal character");
584 break;
585 case SC_OPAQUE:
586 case SC_HOST:
587 case SC_HOSTNAME:
588 case SC_NET_ADDR:
589 case SC_NET_ADDR_V4:
590 case SC_NET_ADDR_V6:
591 case SC_FMRI:
592 case SC_URI:
593 case SC_TIME:
594 case SC_ASTRING:
595 case SC_USTRING:
596 scf_type = lxml_element_to_type(type);
597
598 v->sc_u.sc_string = safe_strdup((const char *)value);
599 if (lxml_validate_string_value(scf_type,
600 v->sc_u.sc_string) != 0)
601 uu_die(gettext("illegal value \"%s\" for "
602 "%s (%s)\n"), (char *)value,
603 lxml_prop_types[type],
604 (scf_error()) ? scf_strerror(scf_error()) :
605 gettext("Illegal format"));
606 v->sc_free = lxml_free_str;
607 break;
608 case SC_BOOLEAN:
609 v->sc_u.sc_count = lxml_xlate_boolean(value);
610 break;
611 default:
612 uu_die(gettext("unknown value type (%d)\n"), type);
613 break;
614 }
615
616 /* Free the old value */
617 if (fov && v->sc_free != NULL)
618 free((char *)value);
619 }
620
621 static value_t *
622 lxml_make_value(element_t type, const xmlChar *value)
623 {
624 value_t *v;
625
626 v = internal_value_new();
627
628 v->sc_type = lxml_element_to_type(type);
629
630 lxml_store_value(v, type, value);
631
632 return (v);
633 }
634
635 static int
636 lxml_get_value(property_t *prop, element_t vtype, xmlNodePtr value)
637 {
638 xmlNodePtr cursor;
639
640 for (cursor = value->xmlChildrenNode; cursor != NULL;
641 cursor = cursor->next) {
642 xmlChar *assigned_value;
643 value_t *v;
644
645 if (lxml_ignorable_block(cursor))
646 continue;
647
648 switch (lxml_xlate_element(cursor->name)) {
649 case SC_VALUE_NODE:
650 if ((assigned_value = xmlGetProp(cursor,
651 (xmlChar *)value_attr)) == NULL)
652 uu_die(gettext("no value on value node?\n"));
653 break;
654 default:
655 uu_die(gettext("value list contains illegal element "
656 "\'%s\'\n"), cursor->name);
657 break;
658 }
659
660 v = lxml_make_value(vtype, assigned_value);
661
662 xmlFree(assigned_value);
663
664 internal_attach_value(prop, v);
665 }
666
667 return (0);
668 }
669
670 static int
671 lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
672 {
673 property_t *p;
674 element_t r;
675 value_t *v;
676 xmlChar *type, *val, *override;
677 int op = pgrp->sc_parent->sc_op;
678
679 p = internal_property_new();
680
681 p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr);
682 if ((p->sc_property_name == NULL) || (*p->sc_property_name == 0))
683 uu_die(gettext("property name missing in group '%s'\n"),
684 pgrp->sc_pgroup_name);
685
686 type = xmlGetProp(propval, (xmlChar *)type_attr);
687 if ((type != NULL) && (*type != 0)) {
688 for (r = 0;
689 r < sizeof (lxml_prop_types) / sizeof (char *); ++r) {
690 if (xmlStrcmp(type,
691 (const xmlChar *)lxml_prop_types[r]) == 0)
692 break;
693 }
694
695 if (r >= sizeof (lxml_prop_types) / sizeof (char *))
696 uu_die(gettext("property type invalid for "
697 "property '%s/%s'\n"), pgrp->sc_pgroup_name,
698 p->sc_property_name);
699
700 p->sc_value_type = lxml_element_to_type(r);
701 } else if (op == SVCCFG_OP_APPLY) {
702 /*
703 * Store the property type as invalid, and the value
704 * as an ASTRING and let the bundle apply code validate
705 * the type/value once the type is found.
706 */
707 est->sc_miss_type = B_TRUE;
708 p->sc_value_type = SCF_TYPE_INVALID;
709 r = SC_ASTRING;
710 } else {
711 uu_die(gettext("property type missing for property '%s/%s'\n"),
712 pgrp->sc_pgroup_name, p->sc_property_name);
713 }
714
715 val = xmlGetProp(propval, (xmlChar *)value_attr);
716 if (val == NULL)
717 uu_die(gettext("property value missing for property '%s/%s'\n"),
718 pgrp->sc_pgroup_name, p->sc_property_name);
719
720 v = lxml_make_value(r, val);
721 xmlFree(val);
722 internal_attach_value(p, v);
723
724 xmlFree(type);
725
726 override = xmlGetProp(propval, (xmlChar *)override_attr);
727 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
728 xmlFree(override);
729
730 return (internal_attach_property(pgrp, p));
731 }
732
733 static int
734 lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
735 {
736 property_t *p;
737 xmlNodePtr cursor;
738 element_t r;
739 xmlChar *type, *override;
740 int op = pgrp->sc_parent->sc_op;
741
742 p = internal_property_new();
743
744 if (((p->sc_property_name = (char *)xmlGetProp(property,
745 (xmlChar *)name_attr)) == NULL) || (*p->sc_property_name == 0))
746 uu_die(gettext("property name missing in group \'%s\'\n"),
747 pgrp->sc_pgroup_name);
748
749 type = xmlGetProp(property, (xmlChar *)type_attr);
750 if ((type != NULL) && (*type != 0)) {
751 for (r = 0;
752 r < sizeof (lxml_prop_types) / sizeof (char *); r++) {
753 if (xmlStrcmp(type,
754 (const xmlChar *)lxml_prop_types[r]) == 0)
755 break;
756 }
757
758 if (r >= sizeof (lxml_prop_types) / sizeof (char *))
759 uu_die(gettext("property type invalid for "
760 "property '%s/%s'\n"), pgrp->sc_pgroup_name,
761 p->sc_property_name);
762
763 p->sc_value_type = lxml_element_to_type(r);
764 } else if (op == SVCCFG_OP_APPLY) {
765 /*
766 * Store the property type as invalid, and let the bundle apply
767 * code validate the type/value once the type is found.
768 */
769 p->sc_value_type = SCF_TYPE_INVALID;
770 est->sc_miss_type = B_TRUE;
771 } else {
772 uu_die(gettext("property type missing for "
773 "property \'%s/%s\'\n"), pgrp->sc_pgroup_name,
774 p->sc_property_name);
775 }
776
777 for (cursor = property->xmlChildrenNode; cursor != NULL;
778 cursor = cursor->next) {
779 if (lxml_ignorable_block(cursor))
780 continue;
781
782 switch (r = lxml_xlate_element(cursor->name)) {
783 case SC_ASTRING:
784 case SC_BOOLEAN:
785 case SC_COUNT:
786 case SC_FMRI:
787 case SC_HOST:
788 case SC_HOSTNAME:
789 case SC_INTEGER:
790 case SC_NET_ADDR:
791 case SC_NET_ADDR_V4:
792 case SC_NET_ADDR_V6:
793 case SC_OPAQUE:
794 case SC_TIME:
795 case SC_URI:
796 case SC_USTRING:
797 /*
798 * If the type is invalid then this is an apply
799 * operation and the type can be taken from the
800 * value list.
801 */
802 if (p->sc_value_type == SCF_TYPE_INVALID) {
803 p->sc_value_type = lxml_element_to_type(r);
804 type = xmlStrdup((const
805 xmlChar *)lxml_prop_types[r]);
806
807 } else if (strcmp(lxml_prop_types[r],
808 (const char *)type) != 0) {
809 uu_die(gettext("property \'%s\' "
810 "type-to-list mismatch\n"),
811 p->sc_property_name);
812 }
813
814 (void) lxml_get_value(p, r, cursor);
815 break;
816 default:
817 uu_die(gettext("unknown value list type: %s\n"),
818 cursor->name);
819 break;
820 }
821 }
822
823 xmlFree(type);
824
825 override = xmlGetProp(property, (xmlChar *)override_attr);
826 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
827 xmlFree(override);
828
829 return (internal_attach_property(pgrp, p));
830 }
831
832 static int
833 lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab)
834 {
835 if (pgrp->sc_parent->sc_op == SVCCFG_OP_APPLY)
836 lxml_validate_element(stab);
837
838 return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY,
839 SCF_TYPE_ASTRING, stab, value_attr));
840 }
841
842 /*
843 * Property groups can go on any of a service, an instance, or a template.
844 */
845 static int
846 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup)
847 {
848 pgroup_t *pg;
849 xmlNodePtr cursor;
850 xmlChar *name, *type, *delete;
851
852 /*
853 * property group attributes:
854 * name: string
855 * type: string | framework | application
856 */
857 name = xmlGetProp(pgroup, (xmlChar *)name_attr);
858 type = xmlGetProp(pgroup, (xmlChar *)type_attr);
859 pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type);
860 xmlFree(name);
861 xmlFree(type);
862
863 /*
864 * Walk the children of this lxml_elements, which are a stability
865 * element, property elements, or propval elements.
866 */
867 for (cursor = pgroup->xmlChildrenNode; cursor != NULL;
868 cursor = cursor->next) {
869 if (lxml_ignorable_block(cursor))
870 continue;
871
872 switch (lxml_xlate_element(cursor->name)) {
873 case SC_STABILITY:
874 (void) lxml_get_pgroup_stability(pg, cursor);
875 break;
876 case SC_PROPERTY:
877 (void) lxml_get_property(pg, cursor);
878 break;
879 case SC_PROPVAL:
880 (void) lxml_get_propval(pg, cursor);
881 break;
882 default:
883 abort();
884 break;
885 }
886 }
887
888 delete = xmlGetProp(pgroup, (xmlChar *)delete_attr);
889 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
890 xmlFree(delete);
891
892 return (0);
893 }
894
895
896 /*
897 * Dependency groups, execution methods can go on either a service or an
898 * instance.
899 */
900
901 static int
902 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile)
903 {
904 property_t *p;
905
906 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
907 1, (uint64_t)1);
908 if (internal_attach_property(pg, p) != 0)
909 return (-1);
910
911 return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE,
912 SCF_TYPE_ASTRING, profile, name_attr));
913 }
914
915 static int
916 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred)
917 {
918 property_t *p;
919
920 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
921 1, (uint64_t)0);
922 if (internal_attach_property(pg, p) != 0)
923 return (-1);
924
925 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING,
926 cred, "user", NULL) != 0)
927 return (-1);
928
929 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING,
930 cred, "group", NULL) != 0)
931 return (-1);
932
933 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS,
934 SCF_TYPE_ASTRING, cred, "supp_groups", NULL) != 0)
935 return (-1);
936
937 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES,
938 SCF_TYPE_ASTRING, cred, "privileges", NULL) != 0)
939 return (-1);
940
941 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES,
942 SCF_TYPE_ASTRING, cred, "limit_privileges", NULL) != 0)
943 return (-1);
944
945 return (0);
946 }
947
948 static char *
949 lxml_get_envvar(xmlNodePtr envvar)
950 {
951 char *name;
952 char *value;
953 char *ret;
954
955 name = (char *)xmlGetProp(envvar, (xmlChar *)name_attr);
956 value = (char *)xmlGetProp(envvar, (xmlChar *)value_attr);
957
958 if (strlen(name) == 0 || strchr(name, '=') != NULL)
959 uu_die(gettext("Invalid environment variable "
960 "\"%s\".\n"), name);
961 if (strstr(name, "SMF_") == name)
962 uu_die(gettext("Invalid environment variable "
963 "\"%s\"; \"SMF_\" prefix is reserved.\n"), name);
964
965 ret = uu_msprintf("%s=%s", name, value);
966 xmlFree(name);
967 xmlFree(value);
968 return (ret);
969 }
970
971 static int
972 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment)
973 {
974 property_t *p;
975 xmlNodePtr cursor;
976 value_t *val;
977
978 p = internal_property_create(SCF_PROPERTY_ENVIRONMENT,
979 SCF_TYPE_ASTRING, 0);
980
981 for (cursor = environment->xmlChildrenNode; cursor != NULL;
982 cursor = cursor->next) {
983 char *tmp;
984
985 if (lxml_ignorable_block(cursor))
986 continue;
987
988 if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR)
989 uu_die(gettext("illegal element \"%s\" on "
990 "method environment for \"%s\"\n"),
991 cursor->name, pg->sc_pgroup_name);
992
993 if ((tmp = lxml_get_envvar(cursor)) == NULL)
994 uu_die(gettext("Out of memory\n"));
995
996 val = internal_value_new();
997 val->sc_u.sc_string = tmp;
998 val->sc_type = SCF_TYPE_ASTRING;
999 val->sc_free = lxml_free_str;
1000 internal_attach_value(p, val);
1001 }
1002
1003 if (internal_attach_property(pg, p) != 0) {
1004 internal_property_free(p);
1005 return (-1);
1006 }
1007
1008 return (0);
1009 }
1010
1011 static int
1012 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx)
1013 {
1014 xmlNodePtr cursor;
1015
1016 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY,
1017 SCF_TYPE_ASTRING, ctx, "working_directory", NULL) != 0)
1018 return (-1);
1019
1020 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT,
1021 SCF_TYPE_ASTRING, ctx, "project", NULL) != 0)
1022 return (-1);
1023
1024 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL,
1025 SCF_TYPE_ASTRING, ctx, "resource_pool", NULL) != 0)
1026 return (-1);
1027
1028 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SECFLAGS,
1029 SCF_TYPE_ASTRING, ctx, "security_flags", NULL) != 0)
1030 return (-1);
1031
1032 for (cursor = ctx->xmlChildrenNode; cursor != NULL;
1033 cursor = cursor->next) {
1034 if (lxml_ignorable_block(cursor))
1035 continue;
1036
1037 switch (lxml_xlate_element(cursor->name)) {
1038 case SC_METHOD_CREDENTIAL:
1039 (void) lxml_get_method_credential(pg, cursor);
1040 break;
1041 case SC_METHOD_PROFILE:
1042 (void) lxml_get_method_profile(pg, cursor);
1043 break;
1044 case SC_METHOD_ENVIRONMENT:
1045 (void) lxml_get_method_environment(pg, cursor);
1046 break;
1047 default:
1048 semerr(gettext("illegal element \'%s\' in method "
1049 "context\n"), (char *)cursor);
1050 break;
1051 }
1052 }
1053
1054 return (0);
1055 }
1056
1057 static int
1058 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx)
1059 {
1060 pgroup_t *pg;
1061
1062 pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT,
1063 (char *)scf_group_framework);
1064
1065 return (lxml_get_method_context(pg, ctx));
1066 }
1067
1068 static int
1069 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
1070 {
1071 pgroup_t *pg;
1072 property_t *p;
1073 xmlChar *name, *timeout, *delete;
1074 xmlNodePtr cursor;
1075 int r = 0;
1076
1077 if (entity->sc_op == SVCCFG_OP_APPLY)
1078 lxml_validate_element(emeth);
1079
1080 name = xmlGetProp(emeth, (xmlChar *)name_attr);
1081 pg = internal_pgroup_find_or_create(entity, (char *)name,
1082 (char *)SCF_GROUP_METHOD);
1083 xmlFree(name);
1084
1085 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
1086 emeth, type_attr) != 0 ||
1087 new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING,
1088 emeth, "exec") != 0)
1089 return (-1);
1090
1091 timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr);
1092 if (timeout != NULL) {
1093 uint64_t u_timeout;
1094 char *endptr;
1095 /*
1096 * Although an SC_COUNT represents a uint64_t the use
1097 * of a negative value is acceptable due to the usage
1098 * established by inetd(1M).
1099 */
1100 errno = 0;
1101 u_timeout = strtoull((char *)timeout, &endptr, 10);
1102 if (errno != 0 || endptr == (char *)timeout || *endptr)
1103 uu_die(gettext("illegal value \"%s\" for "
1104 "timeout_seconds (%s)\n"),
1105 (char *)timeout, (errno) ? strerror(errno):
1106 gettext("Illegal character"));
1107 p = internal_property_create(SCF_PROPERTY_TIMEOUT,
1108 SCF_TYPE_COUNT, 1, u_timeout);
1109 r = internal_attach_property(pg, p);
1110 xmlFree(timeout);
1111 }
1112 if (r != 0)
1113 return (-1);
1114
1115 /*
1116 * There is a possibility that a method context also exists, in which
1117 * case the following attributes are defined: project, resource_pool,
1118 * working_directory, profile, user, group, privileges,
1119 * limit_privileges, security_flags
1120 */
1121 for (cursor = emeth->xmlChildrenNode; cursor != NULL;
1122 cursor = cursor->next) {
1123 if (lxml_ignorable_block(cursor))
1124 continue;
1125
1126 switch (lxml_xlate_element(cursor->name)) {
1127 case SC_STABILITY:
1128 if (lxml_get_pgroup_stability(pg, cursor) != 0)
1129 return (-1);
1130 break;
1131
1132 case SC_METHOD_CONTEXT:
1133 (void) lxml_get_method_context(pg, cursor);
1134 break;
1135
1136 case SC_PROPVAL:
1137 (void) lxml_get_propval(pg, cursor);
1138 break;
1139
1140 case SC_PROPERTY:
1141 (void) lxml_get_property(pg, cursor);
1142 break;
1143
1144 default:
1145 uu_die(gettext("illegal element \"%s\" on "
1146 "execution method \"%s\"\n"), cursor->name,
1147 pg->sc_pgroup_name);
1148 break;
1149 }
1150 }
1151
1152 delete = xmlGetProp(emeth, (xmlChar *)delete_attr);
1153 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1154 xmlFree(delete);
1155
1156 return (0);
1157 }
1158
1159 static int
1160 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency)
1161 {
1162 pgroup_t *pg;
1163 property_t *p;
1164 xmlNodePtr cursor;
1165 xmlChar *name;
1166 xmlChar *delete;
1167
1168 /*
1169 * dependency attributes:
1170 * name: string
1171 * grouping: require_all | require_any | exclude_all | optional_all
1172 * reset_on: string (error | restart | refresh | none)
1173 * type: service / path /host
1174 */
1175
1176 if (entity->sc_op == SVCCFG_OP_APPLY)
1177 lxml_validate_element(dependency);
1178
1179 name = xmlGetProp(dependency, (xmlChar *)name_attr);
1180 pg = internal_pgroup_find_or_create(entity, (char *)name,
1181 (char *)SCF_GROUP_DEPENDENCY);
1182 xmlFree(name);
1183
1184 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
1185 dependency, type_attr) != 0)
1186 return (-1);
1187
1188 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1189 SCF_TYPE_ASTRING, dependency, "restart_on") != 0)
1190 return (-1);
1191
1192 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1193 dependency, "grouping") != 0)
1194 return (-1);
1195
1196 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0);
1197 if (internal_attach_property(pg, p) != 0)
1198 return (-1);
1199
1200 for (cursor = dependency->xmlChildrenNode; cursor != NULL;
1201 cursor = cursor->next) {
1202 xmlChar *value;
1203 value_t *v;
1204
1205 if (lxml_ignorable_block(cursor))
1206 continue;
1207
1208 switch (lxml_xlate_element(cursor->name)) {
1209 case SC_STABILITY:
1210 if (lxml_get_pgroup_stability(pg, cursor) != 0)
1211 return (-1);
1212 break;
1213
1214 case SC_SERVICE_FMRI:
1215 value = xmlGetProp(cursor, (xmlChar *)value_attr);
1216 if (value != NULL) {
1217 if (lxml_validate_string_value(SCF_TYPE_FMRI,
1218 (char *)value) != 0)
1219 uu_die(gettext("illegal value \"%s\" "
1220 "for %s (%s)\n"), (char *)value,
1221 lxml_prop_types[SC_FMRI],
1222 (scf_error()) ?
1223 scf_strerror(scf_error()) :
1224 gettext("Illegal format"));
1225 v = internal_value_new();
1226 v->sc_type = SCF_TYPE_FMRI;
1227 v->sc_u.sc_string = (char *)value;
1228 internal_attach_value(p, v);
1229 }
1230
1231 break;
1232
1233 case SC_PROPVAL:
1234 (void) lxml_get_propval(pg, cursor);
1235 break;
1236
1237 case SC_PROPERTY:
1238 (void) lxml_get_property(pg, cursor);
1239 break;
1240
1241 default:
1242 uu_die(gettext("illegal element \"%s\" on "
1243 "dependency group \"%s\"\n"), cursor->name, name);
1244 break;
1245 }
1246 }
1247
1248 delete = xmlGetProp(dependency, (xmlChar *)delete_attr);
1249 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1250 xmlFree(delete);
1251
1252 return (0);
1253 }
1254
1255 /*
1256 * Dependents are hairy. They should cause a dependency pg to be created in
1257 * another service, but we can't do that here; we'll have to wait until the
1258 * import routines. So for now we'll add the dependency group that should go
1259 * in the other service to the entity's dependent list.
1260 */
1261 static int
1262 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent)
1263 {
1264 xmlChar *name, *or;
1265 xmlNodePtr sf;
1266 xmlChar *fmri, *delete;
1267 pgroup_t *pg;
1268 property_t *p;
1269 xmlNodePtr n;
1270 char *myfmri;
1271
1272 if (entity->sc_op == SVCCFG_OP_APPLY)
1273 lxml_validate_element(dependent);
1274
1275 name = xmlGetProp(dependent, (xmlChar *)name_attr);
1276
1277 if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) {
1278 semerr(gettext("Property group and dependent of entity %s "
1279 "have same name \"%s\".\n"), entity->sc_name, name);
1280 xmlFree(name);
1281 return (-1);
1282 }
1283
1284 or = xmlGetProp(dependent, (xmlChar *)override_attr);
1285
1286 pg = internal_pgroup_new();
1287 pg->sc_pgroup_name = (char *)name;
1288 pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY;
1289 pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0);
1290 xmlFree(or);
1291 if (internal_attach_dependent(entity, pg) != 0) {
1292 xmlFree(name);
1293 internal_pgroup_free(pg);
1294 return (-1);
1295 }
1296
1297 for (sf = dependent->children; sf != NULL; sf = sf->next)
1298 if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0)
1299 break;
1300 assert(sf != NULL);
1301 fmri = xmlGetProp(sf, (xmlChar *)value_attr);
1302 pg->sc_pgroup_fmri = (char *)fmri;
1303
1304 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1305 SCF_TYPE_ASTRING, dependent, "restart_on") != 0)
1306 return (-1);
1307
1308 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1309 dependent, "grouping") != 0)
1310 return (-1);
1311
1312 myfmri = safe_malloc(max_scf_fmri_len + 1);
1313 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) {
1314 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s",
1315 entity->sc_name) < 0)
1316 bad_error("snprintf", errno);
1317 } else {
1318 assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT);
1319 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s",
1320 entity->sc_parent->sc_name, entity->sc_name) < 0)
1321 bad_error("snprintf", errno);
1322 }
1323
1324 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1,
1325 myfmri);
1326 if (internal_attach_property(pg, p) != 0)
1327 return (-1);
1328
1329 /* Create a property to serve as a do-not-export flag. */
1330 p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1,
1331 (uint64_t)1);
1332 if (internal_attach_property(pg, p) != 0)
1333 return (-1);
1334
1335 for (n = sf->next; n != NULL; n = n->next) {
1336 if (lxml_ignorable_block(n))
1337 continue;
1338
1339 switch (lxml_xlate_element(n->name)) {
1340 case SC_STABILITY:
1341 if (new_str_prop_from_attr(pg,
1342 SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n,
1343 value_attr) != 0)
1344 return (-1);
1345 break;
1346
1347 case SC_PROPVAL:
1348 (void) lxml_get_propval(pg, n);
1349 break;
1350
1351 case SC_PROPERTY:
1352 (void) lxml_get_property(pg, n);
1353 break;
1354
1355 default:
1356 uu_die(gettext("unexpected element %s.\n"), n->name);
1357 }
1358 }
1359
1360 /* Go back and fill in defaults. */
1361 if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) {
1362 p = internal_property_create(SCF_PROPERTY_TYPE,
1363 SCF_TYPE_ASTRING, 1, "service");
1364 if (internal_attach_property(pg, p) != 0)
1365 return (-1);
1366 }
1367
1368 delete = xmlGetProp(dependent, (xmlChar *)delete_attr);
1369 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1370 xmlFree(delete);
1371
1372 pg = internal_pgroup_find_or_create(entity, "dependents",
1373 (char *)scf_group_framework);
1374 p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri);
1375 if (internal_attach_property(pg, p) != 0)
1376 return (-1);
1377
1378 return (0);
1379 }
1380
1381 static int
1382 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
1383 {
1384 pgroup_t *pg;
1385 property_t *p;
1386 xmlChar *stabval;
1387
1388 if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) ||
1389 (*stabval == 0)) {
1390 uu_warn(gettext("no stability value found\n"));
1391 stabval = (xmlChar *)safe_strdup("External");
1392 }
1393
1394 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1395 (char *)scf_group_framework);
1396
1397 p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY,
1398 SCF_TYPE_ASTRING, 1, stabval);
1399
1400 return (internal_attach_property(pg, p));
1401 }
1402
1403 static int
1404 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
1405 {
1406 pgroup_t *pg;
1407 property_t *p;
1408 xmlChar *restarter;
1409 xmlNode *cursor;
1410 int r;
1411
1412 /*
1413 * Go find child. Child is a service_fmri element. value attribute
1414 * contains restarter FMRI.
1415 */
1416
1417 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1418 (char *)scf_group_framework);
1419
1420 /*
1421 * Walk its child elements, as appropriate.
1422 */
1423 for (cursor = rstr->xmlChildrenNode; cursor != NULL;
1424 cursor = cursor->next) {
1425 if (lxml_ignorable_block(cursor))
1426 continue;
1427
1428 switch (lxml_xlate_element(cursor->name)) {
1429 case SC_SERVICE_FMRI:
1430 restarter = xmlGetProp(cursor, (xmlChar *)value_attr);
1431 break;
1432 default:
1433 uu_die(gettext("illegal element \"%s\" on restarter "
1434 "element for \"%s\"\n"), cursor->name,
1435 entity->sc_name);
1436 break;
1437 }
1438 }
1439
1440 p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1,
1441 restarter);
1442
1443 r = internal_attach_property(pg, p);
1444 if (r != 0) {
1445 internal_property_free(p);
1446 return (-1);
1447 }
1448
1449 return (0);
1450 }
1451
1452 static void
1453 lxml_get_paramval(pgroup_t *pgrp, const char *propname, xmlNodePtr pval)
1454 {
1455 property_t *p;
1456 char *value;
1457 char *prop;
1458
1459 prop = safe_strdup(propname);
1460
1461 value = (char *)xmlGetProp(pval, (xmlChar *)value_attr);
1462 if (value == NULL || *value == '\0')
1463 uu_die(gettext("property value missing for property '%s/%s'\n"),
1464 pgrp->sc_pgroup_name, propname);
1465 p = internal_property_create(prop, SCF_TYPE_ASTRING, 1, value);
1466
1467 (void) internal_attach_property(pgrp, p);
1468 }
1469
1470 static void
1471 lxml_get_parameter(pgroup_t *pgrp, const char *propname, xmlNodePtr param)
1472 {
1473 property_t *p = internal_property_new();
1474
1475 p->sc_property_name = safe_strdup(propname);
1476 p->sc_value_type = SCF_TYPE_ASTRING;
1477
1478 (void) lxml_get_value(p, SC_ASTRING, param);
1479
1480 (void) internal_attach_property(pgrp, p);
1481 }
1482
1483 static void
1484 lxml_get_type(pgroup_t *pgrp, xmlNodePtr type)
1485 {
1486 property_t *p;
1487 xmlChar *name;
1488 xmlChar *active;
1489 xmlNodePtr cursor;
1490 uint64_t active_val;
1491 size_t sz = max_scf_name_len + 1;
1492 char *propname = safe_malloc(sz);
1493
1494 if (pgrp->sc_parent->sc_op == SVCCFG_OP_APPLY)
1495 lxml_validate_element(type);
1496
1497 name = xmlGetProp(type, (xmlChar *)name_attr);
1498 if (name == NULL || *name == '\0')
1499 uu_die(gettext("attribute name missing in element 'type'\n"));
1500
1501 for (cursor = type->xmlChildrenNode; cursor != NULL;
1502 cursor = cursor->next) {
1503 xmlChar *pname;
1504
1505 if (lxml_ignorable_block(cursor))
1506 continue;
1507
1508 pname = xmlGetProp(cursor, (xmlChar *)name_attr);
1509 if (pname == NULL || *pname == '\0')
1510 uu_die(gettext(
1511 "attribute name missing in sub-element of type\n"));
1512
1513 if (snprintf(propname, sz, "%s,%s", (char *)name,
1514 (char *)pname) >= sz)
1515 uu_die(gettext("name '%s,%s' is too long\n"),
1516 (char *)name, (char *)pname);
1517 xmlFree(pname);
1518
1519 switch (lxml_xlate_element(cursor->name)) {
1520 case SC_PARAMETER:
1521 lxml_get_parameter(pgrp, propname, cursor);
1522 break;
1523
1524 case SC_PARAMVAL:
1525 lxml_get_paramval(pgrp, propname, cursor);
1526 break;
1527
1528 default:
1529 uu_die(gettext("unknown element %s\n"), cursor->name);
1530 }
1531 }
1532
1533 active = xmlGetProp(type, (xmlChar *)active_attr);
1534 if (active == NULL || strcmp(true, (const char *)active) == 0)
1535 active_val = 1;
1536 else
1537 active_val = 0;
1538 xmlFree(active);
1539
1540 if (snprintf(propname, sz, "%s,%s", (char *)name,
1541 SCF_PROPERTY_ACTIVE_POSTFIX) >= sz)
1542 uu_die(gettext("name '%s,%s' is too long\n"),
1543 (char *)name, SCF_PROPERTY_ACTIVE_POSTFIX);
1544
1545 p = internal_property_create(propname, SCF_TYPE_BOOLEAN, 1, active_val);
1546
1547 (void) internal_attach_property(pgrp, p);
1548
1549 xmlFree(name);
1550 }
1551
1552 static void
1553 lxml_get_event(entity_t *entity, const char *pgname, xmlNodePtr np)
1554 {
1555 xmlNodePtr cursor;
1556 pgroup_t *pgrp;
1557
1558 pgrp = internal_pgroup_find_or_create(entity, pgname,
1559 SCF_NOTIFY_PARAMS_PG_TYPE);
1560 for (cursor = np->xmlChildrenNode; cursor != NULL;
1561 cursor = cursor->next) {
1562 if (lxml_ignorable_block(cursor))
1563 continue;
1564
1565 switch (lxml_xlate_element(cursor->name)) {
1566 case SC_EVENT:
1567 continue;
1568
1569 case SC_TYPE:
1570 lxml_get_type(pgrp, cursor);
1571 break;
1572
1573 default:
1574 uu_warn(gettext("illegal element '%s' on "
1575 "notification parameters\n"), cursor->name);
1576 }
1577 }
1578 }
1579
1580 static int
1581 lxml_get_notification_parameters(entity_t *entity, xmlNodePtr np)
1582 {
1583 char *event = NULL;
1584 char **pgs = NULL;
1585 char **p;
1586 char *pgname = NULL;
1587 xmlNodePtr cursor;
1588 int32_t tset, t;
1589 size_t sz = max_scf_name_len + 1;
1590 int count;
1591 int r = -1;
1592
1593 for (count = 0, cursor = np->xmlChildrenNode; cursor != NULL;
1594 cursor = cursor->next) {
1595 if (lxml_ignorable_block(cursor))
1596 continue;
1597
1598 if (lxml_xlate_element(cursor->name) == SC_EVENT) {
1599 xmlChar *s;
1600
1601 count++;
1602 if (count > 1)
1603 uu_die(gettext("Can't have more than 1 element "
1604 "event in a notification parameter\n"));
1605 s = xmlGetProp(cursor, (xmlChar *)value_attr);
1606 if (s == NULL || (event = strdup((char *)s)) == NULL)
1607 uu_die(gettext("couldn't allocate memory"));
1608 xmlFree(s);
1609 }
1610 }
1611
1612 pgs = tokenize(event, ",");
1613
1614 switch (tset = check_tokens(pgs)) {
1615 case INVALID_TOKENS:
1616 uu_die(gettext("Invalid input.\n"));
1617 /*NOTREACHED*/
1618 case MIXED_TOKENS:
1619 semerr(gettext("Can't mix SMF and FMA event definitions\n"));
1620 goto out;
1621 case FMA_TOKENS:
1622 /* make sure this is SCF_NOTIFY_PARAMS_INST */
1623 if (entity->sc_etype != SVCCFG_INSTANCE_OBJECT ||
1624 strcmp(entity->sc_fmri, SCF_NOTIFY_PARAMS_INST) != 0) {
1625 semerr(gettext(
1626 "Non-SMF transition evenst must go to %s\n"),
1627 SCF_NOTIFY_PARAMS_INST);
1628 goto out;
1629 }
1630 pgname = safe_malloc(sz);
1631 for (p = pgs; *p; ++p) {
1632 if (snprintf(pgname, sz, "%s,%s", de_tag(*p),
1633 SCF_NOTIFY_PG_POSTFIX) >= sz)
1634 uu_die(gettext("event name too long: %s\n"),
1635 *p);
1636
1637 lxml_get_event(entity, pgname, np);
1638 }
1639
1640 default: /* smf state transition tokens */
1641 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT &&
1642 strcmp(entity->sc_fmri, SCF_SERVICE_GLOBAL) == 0) {
1643 semerr(gettext(
1644 "Can't set events for global service\n"));
1645 goto out;
1646 }
1647 for (t = 0x1; t < SCF_STATE_ALL; t <<= 1) {
1648 if (t & tset) {
1649 lxml_get_event(entity, tset_to_string(t), np);
1650 }
1651 if ((t << 16) & tset) {
1652 lxml_get_event(entity, tset_to_string(t << 16),
1653 np);
1654 }
1655 }
1656 }
1657
1658 r = 0;
1659 out:
1660 free(pgname);
1661 free(pgs);
1662 free(event);
1663
1664 return (r);
1665 }
1666
1667 /*
1668 * Add a property containing the localized text from the manifest. The
1669 * property is added to the property group at pg. The name of the created
1670 * property is based on the format at pn_format. This is an snprintf(3C)
1671 * format containing a single %s conversion specification. At conversion
1672 * time, the %s is replaced by the locale designation.
1673 *
1674 * source is the source element and it is only used for error messages.
1675 */
1676 static int
1677 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext,
1678 const char *pn_format, const char *source)
1679 {
1680 int extra;
1681 xmlNodePtr cursor;
1682 xmlChar *val;
1683 char *stripped, *cp;
1684 property_t *p;
1685 char *prop_name;
1686 int r;
1687
1688 if (((val = xmlGetProp(loctext, (xmlChar *)xml_lang_attr)) == NULL) ||
1689 (*val == 0)) {
1690 if (((val = xmlGetProp(loctext,
1691 (xmlChar *)lang_attr)) == NULL) || (*val == 0)) {
1692 val = (xmlChar *)"unknown";
1693 }
1694 }
1695
1696 _scf_sanitize_locale((char *)val);
1697 prop_name = safe_malloc(max_scf_name_len + 1);
1698 if ((extra = snprintf(prop_name, max_scf_name_len + 1, pn_format,
1699 val)) >= max_scf_name_len + 1) {
1700 extra -= max_scf_name_len;
1701 uu_die(gettext("%s attribute is %d characters too long for "
1702 "%s in %s\n"),
1703 xml_lang_attr, extra, source, service->sc_name);
1704 }
1705 xmlFree(val);
1706
1707 for (cursor = loctext->xmlChildrenNode; cursor != NULL;
1708 cursor = cursor->next) {
1709 if (strcmp("text", (const char *)cursor->name) == 0) {
1710 break;
1711 } else if (strcmp("comment", (const char *)cursor->name) != 0) {
1712 uu_die(gettext("illegal element \"%s\" on loctext "
1713 "element for \"%s\"\n"), cursor->name,
1714 service->sc_name);
1715 }
1716 }
1717
1718 if (cursor == NULL) {
1719 uu_die(gettext("loctext element has no content for \"%s\"\n"),
1720 service->sc_name);
1721 }
1722
1723 /*
1724 * Remove leading and trailing whitespace.
1725 */
1726 stripped = safe_strdup((const char *)cursor->content);
1727
1728 for (; isspace(*stripped); stripped++)
1729 ;
1730 for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--)
1731 ;
1732 *(cp + 1) = '\0';
1733
1734 p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1,
1735 stripped);
1736
1737 r = internal_attach_property(pg, p);
1738 if (r != 0) {
1739 internal_property_free(p);
1740 free(prop_name);
1741 }
1742
1743 return (r);
1744 }
1745
1746 /*
1747 * This function processes all loctext elements in the current XML element
1748 * designated by container. A property is created for each loctext element
1749 * and added to the property group at pg. The name of the property is
1750 * derived from the loctext language designation using the format at
1751 * pn_format. pn_format should be an snprintf format string containing one
1752 * %s which is replaced by the language designation.
1753 *
1754 * The function returns 0 on success and -1 if it is unable to attach the
1755 * newly created property to pg.
1756 */
1757 static int
1758 lxml_get_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container,
1759 const char *pn_format, const char *source)
1760 {
1761 xmlNodePtr cursor;
1762
1763 /*
1764 * Iterate through one or more loctext elements. The locale is
1765 * used to generate the property name; the contents are the ustring
1766 * value for the property.
1767 */
1768 for (cursor = container->xmlChildrenNode; cursor != NULL;
1769 cursor = cursor->next) {
1770 if (lxml_ignorable_block(cursor))
1771 continue;
1772
1773 switch (lxml_xlate_element(cursor->name)) {
1774 case SC_LOCTEXT:
1775 if (lxml_get_loctext(service, pg, cursor, pn_format,
1776 source))
1777 return (-1);
1778 break;
1779 default:
1780 uu_die(gettext("illegal element \"%s\" on %s element "
1781 "for \"%s\"\n"), cursor->name, container->name,
1782 service->sc_name);
1783 break;
1784 }
1785 }
1786
1787 return (0);
1788 }
1789
1790 /*
1791 * Obtain the specified cardinality attribute and place it in a property
1792 * named prop_name. The converted attribute is placed at *value, and the
1793 * newly created property is returned to propp. NULL is returned to propp
1794 * if the attribute is not provided in the manifest.
1795 *
1796 * 0 is returned upon success, and -1 indicates that the manifest contained
1797 * an invalid cardinality value.
1798 */
1799 static int
1800 lxml_get_cardinality_attribute(entity_t *service, xmlNodePtr cursor,
1801 const char *attr_name, const char *prop_name, uint64_t *value,
1802 property_t **propp)
1803 {
1804 char *c;
1805 property_t *p;
1806 xmlChar *val;
1807 uint64_t count;
1808 char *endptr;
1809
1810 *propp = NULL;
1811 val = xmlGetProp(cursor, (xmlChar *)attr_name);
1812 if (val == NULL)
1813 return (0);
1814 if (*val == 0) {
1815 xmlFree(val);
1816 return (0);
1817 }
1818
1819 /*
1820 * Make sure that the string at val doesn't have a leading minus
1821 * sign. The strtoull() call below does not catch this problem.
1822 */
1823 for (c = (char *)val; *c != 0; c++) {
1824 if (isspace(*c))
1825 continue;
1826 if (isdigit(*c))
1827 break;
1828 semerr(gettext("\"%c\" is not a legal character in the %s "
1829 "attribute of the %s element in %s.\n"), *c,
1830 attr_name, prop_name, service->sc_name);
1831 xmlFree(val);
1832 return (-1);
1833 }
1834 errno = 0;
1835 count = strtoull((char *)val, &endptr, 10);
1836 if (errno != 0 || endptr == (char *)val || *endptr) {
1837 semerr(gettext("\"%s\" is not a legal number for the %s "
1838 "attribute of the %s element in %s.\n"), (char *)val,
1839 attr_name, prop_name, service->sc_name);
1840 xmlFree(val);
1841 return (-1);
1842 }
1843
1844 xmlFree(val);
1845
1846 /* Value is valid. Create the property. */
1847 p = internal_property_create(prop_name, SCF_TYPE_COUNT, 1, count);
1848 *value = count;
1849 *propp = p;
1850 return (0);
1851 }
1852
1853 /*
1854 * The cardinality is specified by two attributes max and min at cursor.
1855 * Both are optional, but if present they must be unsigned integers.
1856 */
1857 static int
1858 lxml_get_tm_cardinality(entity_t *service, pgroup_t *pg, xmlNodePtr cursor)
1859 {
1860 int min_attached = 0;
1861 int compare = 1;
1862 property_t *min_prop;
1863 property_t *max_prop;
1864 uint64_t max;
1865 uint64_t min;
1866 int r;
1867
1868 r = lxml_get_cardinality_attribute(service, cursor, min_attr,
1869 SCF_PROPERTY_TM_CARDINALITY_MIN, &min, &min_prop);
1870 if (r != 0)
1871 return (r);
1872 if (min_prop == NULL)
1873 compare = 0;
1874 r = lxml_get_cardinality_attribute(service, cursor, max_attr,
1875 SCF_PROPERTY_TM_CARDINALITY_MAX, &max, &max_prop);
1876 if (r != 0)
1877 goto errout;
1878 if ((max_prop != NULL) && (compare == 1)) {
1879 if (max < min) {
1880 semerr(gettext("Cardinality max is less than min for "
1881 "the %s element in %s.\n"), pg->sc_pgroup_name,
1882 service->sc_fmri);
1883 goto errout;
1884 }
1885 }
1886
1887 /* Attach the properties to the property group. */
1888 if (min_prop) {
1889 if (internal_attach_property(pg, min_prop) == 0) {
1890 min_attached = 1;
1891 } else {
1892 goto errout;
1893 }
1894 }
1895 if (max_prop) {
1896 if (internal_attach_property(pg, max_prop) != 0) {
1897 if (min_attached)
1898 internal_detach_property(pg, min_prop);
1899 goto errout;
1900 }
1901 }
1902 return (0);
1903
1904 errout:
1905 if (min_prop)
1906 internal_property_free(min_prop);
1907 if (max_prop)
1908 internal_property_free(max_prop);
1909 return (-1);
1910 }
1911
1912 /*
1913 * Get the common_name which is present as localized text at common_name in
1914 * the manifest. The common_name is stored as the value of a property in
1915 * the property group whose name is SCF_PG_TM_COMMON_NAME and type is
1916 * SCF_GROUP_TEMPLATE. This property group will be created in service if
1917 * it is not already there.
1918 */
1919 static int
1920 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
1921 {
1922 pgroup_t *pg;
1923
1924 /*
1925 * Create the property group, if absent.
1926 */
1927 pg = internal_pgroup_find_or_create(service, SCF_PG_TM_COMMON_NAME,
1928 SCF_GROUP_TEMPLATE);
1929
1930 return (lxml_get_all_loctext(service, pg, common_name, LOCALE_ONLY_FMT,
1931 "common_name"));
1932 }
1933
1934 /*
1935 * Get the description which is present as localized text at description in
1936 * the manifest. The description is stored as the value of a property in
1937 * the property group whose name is SCF_PG_TM_DESCRIPTION and type is
1938 * SCF_GROUP_TEMPLATE. This property group will be created in service if
1939 * it is not already there.
1940 */
1941 static int
1942 lxml_get_tm_description(entity_t *service, xmlNodePtr description)
1943 {
1944 pgroup_t *pg;
1945
1946 /*
1947 * Create the property group, if absent.
1948 */
1949 pg = internal_pgroup_find_or_create(service, SCF_PG_TM_DESCRIPTION,
1950 SCF_GROUP_TEMPLATE);
1951
1952 return (lxml_get_all_loctext(service, pg, description,
1953 LOCALE_ONLY_FMT, "description"));
1954 }
1955
1956 static char *
1957 lxml_label_to_groupname(const char *prefix, const char *in)
1958 {
1959 char *out, *cp;
1960 size_t len, piece_len;
1961
1962 out = uu_zalloc(2 * max_scf_name_len + 1);
1963 if (out == NULL)
1964 return (NULL);
1965
1966 (void) strlcpy(out, prefix, 2 * max_scf_name_len + 1);
1967
1968 len = strlcat(out, in, 2 * max_scf_name_len + 1);
1969 if (len > max_scf_name_len) {
1970 /* Use the first half and the second half. */
1971 piece_len = (max_scf_name_len - 2) / 2;
1972
1973 (void) strncpy(out + piece_len, "..", 2);
1974
1975 (void) strcpy(out + piece_len + 2, out + (len - piece_len));
1976 }
1977
1978 /*
1979 * Translate non-property characters to '_'.
1980 */
1981 for (cp = out; *cp != '\0'; ++cp) {
1982 if (!(isalnum(*cp) || *cp == '_' || *cp == '-'))
1983 *cp = '_';
1984 }
1985
1986 return (out);
1987 }
1988
1989 /*
1990 * If *p is NULL, astring_prop_value() first creates a property with the
1991 * name specified in prop_name. The address of the newly created property
1992 * is placed in *p.
1993 *
1994 * In either case, newly created property or existing property, a new
1995 * SCF_TYPE_ASTRING value will created and attached to the property at *p.
1996 * The value of the newly created property is prop_value.
1997 *
1998 * free_flag is used to indicate whether or not the memory at prop_value
1999 * should be freed when the property is freed by a call to
2000 * internal_property_free().
2001 */
2002 static void
2003 astring_prop_value(property_t **p, const char *prop_name, char *prop_value,
2004 boolean_t free_flag)
2005 {
2006 value_t *v;
2007
2008 if (*p == NULL) {
2009 /* Create the property */
2010 *p = internal_property_new();
2011 (*p)->sc_property_name = (char *)prop_name;
2012 (*p)->sc_value_type = SCF_TYPE_ASTRING;
2013 }
2014
2015 /* Add the property value to the property's list of values. */
2016 v = internal_value_new();
2017 v->sc_type = SCF_TYPE_ASTRING;
2018 if (free_flag == B_TRUE)
2019 v->sc_free = lxml_free_str;
2020 v->sc_u.sc_string = prop_value;
2021 internal_attach_value(*p, v);
2022 }
2023
2024 /*
2025 * If p points to a null pointer, create an internal_separators property
2026 * saving the address at p. For each character at seps create a property
2027 * value and attach it to the property at p.
2028 */
2029 static void
2030 seps_to_prop_values(property_t **p, xmlChar *seps)
2031 {
2032 value_t *v;
2033 char val_str[2];
2034
2035 if (*p == NULL) {
2036 *p = internal_property_new();
2037 (*p)->sc_property_name =
2038 (char *)SCF_PROPERTY_INTERNAL_SEPARATORS;
2039 (*p)->sc_value_type = SCF_TYPE_ASTRING;
2040 }
2041
2042 /* Add the values to the property's list. */
2043 val_str[1] = 0; /* Terminate the string. */
2044 for (; *seps != 0; seps++) {
2045 v = internal_value_new();
2046 v->sc_type = (*p)->sc_value_type;
2047 v->sc_free = lxml_free_str;
2048 val_str[0] = *seps;
2049 v->sc_u.sc_string = safe_strdup(val_str);
2050 internal_attach_value(*p, v);
2051 }
2052 }
2053
2054 /*
2055 * Create an internal_separators property and attach it to the property
2056 * group at pg. The separator characters are provided in the text nodes
2057 * that are the children of seps. Each separator character is stored as a
2058 * property value in the internal_separators property.
2059 */
2060 static int
2061 lxml_get_tm_internal_seps(entity_t *service, pgroup_t *pg, xmlNodePtr seps)
2062 {
2063 xmlNodePtr cursor;
2064 property_t *prop = NULL;
2065 int r;
2066
2067 for (cursor = seps->xmlChildrenNode; cursor != NULL;
2068 cursor = cursor->next) {
2069 if (strcmp("text", (const char *)cursor->name) == 0) {
2070 seps_to_prop_values(&prop, cursor->content);
2071 } else if (strcmp("comment", (const char *)cursor->name) != 0) {
2072 uu_die(gettext("illegal element \"%s\" on %s element "
2073 "for \"%s\"\n"), cursor->name, seps->name,
2074 service->sc_name);
2075 }
2076 }
2077 if (prop == NULL) {
2078 semerr(gettext("The %s element in %s had an empty list of "
2079 "separators.\n"), (const char *)seps->name,
2080 service->sc_name);
2081 return (-1);
2082 }
2083 r = internal_attach_property(pg, prop);
2084 if (r != 0)
2085 internal_property_free(prop);
2086 return (r);
2087 }
2088
2089 static int
2090 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
2091 {
2092 pgroup_t *pg;
2093 char *pgname;
2094 char *name;
2095 xmlChar *title;
2096 xmlChar *section;
2097
2098 /*
2099 * Fetch title and section attributes, convert to something sanitized,
2100 * and create property group.
2101 */
2102 title = xmlGetProp(manpage, (xmlChar *)title_attr);
2103 if (title == NULL)
2104 return (-1);
2105 section = xmlGetProp(manpage, (xmlChar *)section_attr);
2106 if (section == NULL) {
2107 xmlFree(title);
2108 return (-1);
2109 }
2110
2111 name = safe_malloc(max_scf_name_len + 1);
2112
2113 /* Find existing property group with underscore separators */
2114 (void) snprintf(name, max_scf_name_len + 1, "%s_%s", title, section);
2115 pgname = lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, name);
2116 pg = internal_pgroup_find(service, pgname, SCF_GROUP_TEMPLATE);
2117
2118 uu_free(pgname);
2119 (void) snprintf(name, max_scf_name_len + 1, "%s%s", title, section);
2120 pgname = lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, name);
2121
2122 if (pg == NULL) {
2123 pg = internal_pgroup_find_or_create(service, pgname,
2124 SCF_GROUP_TEMPLATE);
2125 } else {
2126 /* Rename property group */
2127 free((char *)pg->sc_pgroup_name);
2128 pg->sc_pgroup_name = safe_strdup(pgname);
2129 }
2130
2131 uu_free(pgname);
2132 free(name);
2133 xmlFree(section);
2134 xmlFree(title);
2135
2136
2137 /*
2138 * Each attribute is an astring property within the group.
2139 */
2140 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_TITLE,
2141 SCF_TYPE_ASTRING, manpage, title_attr) != 0 ||
2142 new_str_prop_from_attr(pg, SCF_PROPERTY_TM_SECTION,
2143 SCF_TYPE_ASTRING, manpage, section_attr) != 0 ||
2144 new_str_prop_from_attr(pg, SCF_PROPERTY_TM_MANPATH,
2145 SCF_TYPE_ASTRING, manpage, manpath_attr) != 0)
2146 return (-1);
2147
2148 return (0);
2149 }
2150
2151 static int
2152 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
2153 {
2154 pgroup_t *pg;
2155 char *pgname;
2156 xmlChar *name;
2157
2158 /*
2159 * Fetch name attribute, convert name to something sanitized, and create
2160 * property group.
2161 */
2162 name = xmlGetProp(doc_link, (xmlChar *)name_attr);
2163 if (name == NULL)
2164 return (-1);
2165
2166 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX,
2167 (const char *)name);
2168
2169 pg = internal_pgroup_find_or_create(service, pgname,
2170 (char *)SCF_GROUP_TEMPLATE);
2171
2172 uu_free(pgname);
2173 xmlFree(name);
2174
2175 /*
2176 * Each attribute is an astring property within the group.
2177 */
2178 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, SCF_TYPE_ASTRING,
2179 doc_link, name_attr) != 0 ||
2180 new_str_prop_from_attr(pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING,
2181 doc_link, uri_attr) != 0)
2182 return (-1);
2183
2184 return (0);
2185 }
2186
2187 static int
2188 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
2189 {
2190 xmlNodePtr cursor;
2191
2192 for (cursor = documentation->xmlChildrenNode; cursor != NULL;
2193 cursor = cursor->next) {
2194 if (lxml_ignorable_block(cursor))
2195 continue;
2196
2197 switch (lxml_xlate_element(cursor->name)) {
2198 case SC_MANPAGE:
2199 (void) lxml_get_tm_manpage(service, cursor);
2200 break;
2201 case SC_DOC_LINK:
2202 (void) lxml_get_tm_doclink(service, cursor);
2203 break;
2204 default:
2205 uu_die(gettext("illegal element \"%s\" on template "
2206 "for service \"%s\"\n"),
2207 cursor->name, service->sc_name);
2208 }
2209 }
2210
2211 return (0);
2212 }
2213
2214 static int
2215 lxml_get_prop_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
2216 {
2217 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
2218 SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
2219 return (-1);
2220 }
2221 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
2222 SCF_TYPE_ASTRING, cursor, type_attr, "") != 0) {
2223 return (-1);
2224 }
2225 if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
2226 required_attr) != 0)
2227 return (-1);
2228 return (0);
2229 }
2230
2231 static int
2232 lxml_get_tm_include_values(entity_t *service, pgroup_t *pg,
2233 xmlNodePtr include_values, const char *prop_name)
2234 {
2235 boolean_t attach_to_pg = B_FALSE;
2236 property_t *p;
2237 int r = 0;
2238 char *type;
2239
2240 /* Get the type attribute of the include_values element. */
2241 type = (char *)xmlGetProp(include_values, (const xmlChar *)type_attr);
2242 if ((type == NULL) || (*type == 0)) {
2243 uu_die(gettext("%s element requires a %s attribute in the %s "
2244 "service.\n"), include_values->name, type_attr,
2245 service->sc_name);
2246 }
2247
2248 /* Add the type to the values of the prop_name property. */
2249 p = internal_property_find(pg, prop_name);
2250 if (p == NULL)
2251 attach_to_pg = B_TRUE;
2252 astring_prop_value(&p, prop_name, type, B_FALSE);
2253 if (attach_to_pg == B_TRUE) {
2254 r = internal_attach_property(pg, p);
2255 if (r != 0)
2256 internal_property_free(p);
2257 }
2258 return (r);
2259 }
2260
2261 #define RC_MIN 0
2262 #define RC_MAX 1
2263 #define RC_COUNT 2
2264
2265 /*
2266 * Verify that the strings at min and max are valid numeric strings. Also
2267 * verify that max is numerically >= min.
2268 *
2269 * 0 is returned if the range is valid, and -1 is returned if it is not.
2270 */
2271 static int
2272 verify_range(entity_t *service, xmlNodePtr range, char *min, char *max)
2273 {
2274 char *c;
2275 int i;
2276 int is_signed = 0;
2277 int inverted = 0;
2278 const char *limit[RC_COUNT];
2279 char *strings[RC_COUNT];
2280 uint64_t urange[RC_COUNT]; /* unsigned range. */
2281 int64_t srange[RC_COUNT]; /* signed range. */
2282
2283 strings[RC_MIN] = min;
2284 strings[RC_MAX] = max;
2285 limit[RC_MIN] = min_attr;
2286 limit[RC_MAX] = max_attr;
2287
2288 /* See if the range is signed. */
2289 for (i = 0; (i < RC_COUNT) && (is_signed == 0); i++) {
2290 c = strings[i];
2291 while (isspace(*c)) {
2292 c++;
2293 }
2294 if (*c == '-')
2295 is_signed = 1;
2296 }
2297
2298 /* Attempt to convert the strings. */
2299 for (i = 0; i < RC_COUNT; i++) {
2300 errno = 0;
2301 if (is_signed) {
2302 srange[i] = strtoll(strings[i], &c, 0);
2303 } else {
2304 urange[i] = strtoull(strings[i], &c, 0);
2305 }
2306 if ((errno != 0) || (c == strings[i]) || (*c != 0)) {
2307 /* Conversion failed. */
2308 uu_die(gettext("Unable to convert %s for the %s "
2309 "element in service %s.\n"), limit[i],
2310 (char *)range->name, service->sc_name);
2311 }
2312 }
2313
2314 /* Make sure that min is <= max */
2315 if (is_signed) {
2316 if (srange[RC_MAX] < srange[RC_MIN])
2317 inverted = 1;
2318 } else {
2319 if (urange[RC_MAX] < urange[RC_MIN])
2320 inverted = 1;
2321 }
2322 if (inverted != 0) {
2323 semerr(gettext("Maximum less than minimum for the %s element "
2324 "in service %s.\n"), (char *)range->name,
2325 service->sc_name);
2326 return (-1);
2327 }
2328
2329 return (0);
2330 }
2331
2332 /*
2333 * This, function creates a property named prop_name. The range element
2334 * should have two attributes -- min and max. The property value then
2335 * becomes the concatenation of their value separated by a comma. The
2336 * property is then attached to the property group at pg.
2337 *
2338 * If pg already contains a property with a name of prop_name, it is only
2339 * necessary to create a new value and attach it to the existing property.
2340 */
2341 static int
2342 lxml_get_tm_range(entity_t *service, pgroup_t *pg, xmlNodePtr range,
2343 const char *prop_name)
2344 {
2345 boolean_t attach_to_pg = B_FALSE;
2346 char *max;
2347 char *min;
2348 property_t *p;
2349 char *prop_value;
2350 int r = 0;
2351
2352 /* Get max and min from the XML description. */
2353 max = (char *)xmlGetProp(range, (xmlChar *)max_attr);
2354 if ((max == NULL) || (*max == 0)) {
2355 uu_die(gettext("%s element is missing the %s attribute in "
2356 "service %s.\n"), (char *)range->name, max_attr,
2357 service->sc_name);
2358 }
2359 min = (char *)xmlGetProp(range, (xmlChar *)min_attr);
2360 if ((min == NULL) || (*min == 0)) {
2361 uu_die(gettext("%s element is missing the %s attribute in "
2362 "service %s.\n"), (char *)range->name, min_attr,
2363 service->sc_name);
2364 }
2365 if (verify_range(service, range, min, max) != 0) {
2366 xmlFree(min);
2367 xmlFree(max);
2368 return (-1);
2369 }
2370
2371 /* Property value is concatenation of min and max. */
2372 prop_value = safe_malloc(max_scf_value_len + 1);
2373 if (snprintf(prop_value, max_scf_value_len + 1, "%s,%s", min, max) >=
2374 max_scf_value_len + 1) {
2375 uu_die(gettext("min and max are too long for the %s element "
2376 "of %s.\n"), (char *)range->name, service->sc_name);
2377 }
2378 xmlFree(min);
2379 xmlFree(max);
2380
2381 /*
2382 * If necessary create the property and attach it to the property
2383 * group.
2384 */
2385 p = internal_property_find(pg, prop_name);
2386 if (p == NULL)
2387 attach_to_pg = B_TRUE;
2388 astring_prop_value(&p, prop_name, prop_value, B_TRUE);
2389 if (attach_to_pg == B_TRUE) {
2390 r = internal_attach_property(pg, p);
2391 if (r != 0) {
2392 internal_property_free(p);
2393 }
2394 }
2395 return (r);
2396 }
2397
2398 /*
2399 * Determine how many plain characters are represented by count Base32
2400 * encoded characters. 5 plain text characters are converted to 8 Base32
2401 * characters.
2402 */
2403 static size_t
2404 encoded_count_to_plain(size_t count)
2405 {
2406 return (5 * ((count + 7) / 8));
2407 }
2408
2409 /*
2410 * The value element contains 0 or 1 common_name element followed by 0 or 1
2411 * description element. It also has a required attribute called "name".
2412 * The common_name and description are stored as property values in pg.
2413 * The property names are:
2414 * value_<name>_common_name_<lang>
2415 * value_<name>_description_<lang>
2416 *
2417 * The <name> portion of the preceeding proper names requires more
2418 * explanation. Ideally it would just the name attribute of this value
2419 * element. Unfortunately, the name attribute can contain characters that
2420 * are not legal in a property name. Thus, we base 32 encode the name
2421 * attribute and use that for <name>.
2422 *
2423 * There are cases where the caller needs to know the name, so it is
2424 * returned through the name_value pointer if it is not NULL.
2425 *
2426 * Parameters:
2427 * service - Information about the service that is being
2428 * processed. This function only uses this parameter
2429 * for producing error messages.
2430 *
2431 * pg - The property group to receive the newly created
2432 * properties.
2433 *
2434 * value - Pointer to the value element in the XML tree.
2435 *
2436 * name_value - Address to receive the value of the name attribute.
2437 * The caller must free the memory.
2438 */
2439 static int
2440 lxml_get_tm_value_element(entity_t *service, pgroup_t *pg, xmlNodePtr value,
2441 char **name_value)
2442 {
2443 char *common_name_fmt;
2444 xmlNodePtr cursor;
2445 char *description_fmt;
2446 char *encoded_value = NULL;
2447 size_t extra;
2448 char *value_name;
2449 int r = 0;
2450
2451 common_name_fmt = safe_malloc(max_scf_name_len + 1);
2452 description_fmt = safe_malloc(max_scf_name_len + 1);
2453
2454 /*
2455 * Get the value of our name attribute, so that we can use it to
2456 * construct property names.
2457 */
2458 value_name = (char *)xmlGetProp(value, (xmlChar *)name_attr);
2459 /* The value name must be present, but it can be empty. */
2460 if (value_name == NULL) {
2461 uu_die(gettext("%s element requires a %s attribute in the %s "
2462 "service.\n"), (char *)value->name, name_attr,
2463 service->sc_name);
2464 }
2465
2466 /*
2467 * The value_name may contain characters that are not valid in in a
2468 * property name. So we will encode value_name and then use the
2469 * encoded value in the property name.
2470 */
2471 encoded_value = safe_malloc(max_scf_name_len + 1);
2472 if (scf_encode32(value_name, strlen(value_name), encoded_value,
2473 max_scf_name_len + 1, &extra, SCF_ENCODE32_PAD) != 0) {
2474 extra = encoded_count_to_plain(extra - max_scf_name_len);
2475 uu_die(gettext("Constructed property name is %u characters "
2476 "too long for value \"%s\" in the %s service.\n"),
2477 extra, value_name, service->sc_name);
2478 }
2479 if ((extra = snprintf(common_name_fmt, max_scf_name_len + 1,
2480 VALUE_COMMON_NAME_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
2481 encoded_value)) >= max_scf_name_len + 1) {
2482 extra = encoded_count_to_plain(extra - max_scf_name_len);
2483 uu_die(gettext("Name attribute is "
2484 "%u characters too long for %s in service %s\n"),
2485 extra, (char *)value->name, service->sc_name);
2486 }
2487 if ((extra = snprintf(description_fmt, max_scf_name_len + 1,
2488 VALUE_DESCRIPTION_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
2489 encoded_value)) >= max_scf_name_len + 1) {
2490 extra = encoded_count_to_plain(extra - max_scf_name_len);
2491 uu_die(gettext("Name attribute is "
2492 "%u characters too long for %s in service %s\n"),
2493 extra, (char *)value->name, service->sc_name);
2494 }
2495
2496 for (cursor = value->xmlChildrenNode;
2497 cursor != NULL;
2498 cursor = cursor->next) {
2499 if (lxml_ignorable_block(cursor))
2500 continue;
2501 switch (lxml_xlate_element(cursor->name)) {
2502 case SC_COMMON_NAME:
2503 r = lxml_get_all_loctext(service, pg, cursor,
2504 common_name_fmt, (const char *)cursor->name);
2505 break;
2506 case SC_DESCRIPTION:
2507 r = lxml_get_all_loctext(service, pg, cursor,
2508 description_fmt, (const char *)cursor->name);
2509 break;
2510 default:
2511 uu_die(gettext("\"%s\" is an illegal element in %s "
2512 "of service %s\n"), (char *)cursor->name,
2513 (char *)value->name, service->sc_name);
2514 }
2515 if (r != 0)
2516 break;
2517 }
2518
2519 free(description_fmt);
2520 free(common_name_fmt);
2521 if (r == 0) {
2522 *name_value = safe_strdup(value_name);
2523 }
2524 xmlFree(value_name);
2525 free(encoded_value);
2526 return (r);
2527 }
2528
2529 static int
2530 lxml_get_tm_choices(entity_t *service, pgroup_t *pg, xmlNodePtr choices)
2531 {
2532 xmlNodePtr cursor;
2533 char *name_value;
2534 property_t *name_prop = NULL;
2535 int r = 0;
2536
2537 for (cursor = choices->xmlChildrenNode;
2538 (cursor != NULL) && (r == 0);
2539 cursor = cursor->next) {
2540 if (lxml_ignorable_block(cursor))
2541 continue;
2542 switch (lxml_xlate_element(cursor->name)) {
2543 case SC_INCLUDE_VALUES:
2544 (void) lxml_get_tm_include_values(service, pg, cursor,
2545 SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES);
2546 break;
2547 case SC_RANGE:
2548 r = lxml_get_tm_range(service, pg, cursor,
2549 SCF_PROPERTY_TM_CHOICES_RANGE);
2550 if (r != 0)
2551 goto out;
2552 break;
2553 case SC_VALUE:
2554 r = lxml_get_tm_value_element(service, pg, cursor,
2555 &name_value);
2556 if (r == 0) {
2557 /*
2558 * There is no need to free the memory
2559 * associated with name_value, because the
2560 * property value will end up pointing to
2561 * the memory.
2562 */
2563 astring_prop_value(&name_prop,
2564 SCF_PROPERTY_TM_CHOICES_NAME, name_value,
2565 B_TRUE);
2566 } else {
2567 goto out;
2568 }
2569 break;
2570 default:
2571 uu_die(gettext("%s is an invalid element of "
2572 "choices for service %s.\n"), cursor->name,
2573 service->sc_name);
2574 }
2575 }
2576
2577 out:
2578 /* Attach the name property if we created one. */
2579 if ((r == 0) && (name_prop != NULL)) {
2580 r = internal_attach_property(pg, name_prop);
2581 }
2582 if ((r != 0) && (name_prop != NULL)) {
2583 internal_property_free(name_prop);
2584 }
2585
2586 return (r);
2587 }
2588
2589 static int
2590 lxml_get_tm_constraints(entity_t *service, pgroup_t *pg, xmlNodePtr constraints)
2591 {
2592 xmlNodePtr cursor;
2593 char *name_value;
2594 property_t *name_prop = NULL;
2595 int r = 0;
2596
2597 for (cursor = constraints->xmlChildrenNode;
2598 (cursor != NULL) && (r == 0);
2599 cursor = cursor->next) {
2600 if (lxml_ignorable_block(cursor))
2601 continue;
2602 switch (lxml_xlate_element(cursor->name)) {
2603 case SC_RANGE:
2604 r = lxml_get_tm_range(service, pg, cursor,
2605 SCF_PROPERTY_TM_CONSTRAINT_RANGE);
2606 if (r != 0)
2607 goto out;
2608 break;
2609 case SC_VALUE:
2610 r = lxml_get_tm_value_element(service, pg, cursor,
2611 &name_value);
2612 if (r == 0) {
2613 /*
2614 * There is no need to free the memory
2615 * associated with name_value, because the
2616 * property value will end up pointing to
2617 * the memory.
2618 */
2619 astring_prop_value(&name_prop,
2620 SCF_PROPERTY_TM_CONSTRAINT_NAME, name_value,
2621 B_TRUE);
2622 } else {
2623 goto out;
2624 }
2625 break;
2626 default:
2627 uu_die(gettext("%s is an invalid element of "
2628 "constraints for service %s.\n"), cursor->name,
2629 service->sc_name);
2630 }
2631 }
2632
2633 out:
2634 /* Attach the name property if we created one. */
2635 if ((r == 0) && (name_prop != NULL)) {
2636 r = internal_attach_property(pg, name_prop);
2637 }
2638 if ((r != 0) && (name_prop != NULL)) {
2639 internal_property_free(name_prop);
2640 }
2641
2642 return (r);
2643 }
2644
2645 /*
2646 * The values element contains one or more value elements.
2647 */
2648 static int
2649 lxml_get_tm_values(entity_t *service, pgroup_t *pg, xmlNodePtr values)
2650 {
2651 xmlNodePtr cursor;
2652 char *name_value;
2653 property_t *name_prop = NULL;
2654 int r = 0;
2655
2656 for (cursor = values->xmlChildrenNode;
2657 (cursor != NULL) && (r == 0);
2658 cursor = cursor->next) {
2659 if (lxml_ignorable_block(cursor))
2660 continue;
2661 if (lxml_xlate_element(cursor->name) != SC_VALUE) {
2662 uu_die(gettext("\"%s\" is an illegal element in the "
2663 "%s element of %s\n"), (char *)cursor->name,
2664 (char *)values->name, service->sc_name);
2665 }
2666 r = lxml_get_tm_value_element(service, pg, cursor, &name_value);
2667 if (r == 0) {
2668 /*
2669 * There is no need to free the memory
2670 * associated with name_value, because the
2671 * property value will end up pointing to
2672 * the memory.
2673 */
2674 astring_prop_value(&name_prop,
2675 SCF_PROPERTY_TM_VALUES_NAME, name_value,
2676 B_TRUE);
2677 }
2678 }
2679
2680 /* Attach the name property if we created one. */
2681 if ((r == 0) && (name_prop != NULL)) {
2682 r = internal_attach_property(pg, name_prop);
2683 }
2684 if ((r != 0) && (name_prop != NULL)) {
2685 internal_property_free(name_prop);
2686 }
2687
2688 return (r);
2689 }
2690
2691 /*
2692 * This function processes a prop_pattern element within a pg_pattern XML
2693 * element. First it creates a property group to hold the prop_pattern
2694 * information. The name of this property group is the concatenation of:
2695 * - SCF_PG_TM_PROP_PATTERN_PREFIX
2696 * - The unique part of the property group name of the enclosing
2697 * pg_pattern. The property group name of the enclosing pg_pattern
2698 * is passed to us in pgpat_name. The unique part, is the part
2699 * following SCF_PG_TM_PG_PATTERN_PREFIX.
2700 * - The name of this prop_pattern element.
2701 *
2702 * After creating the property group, the prop_pattern attributes are saved
2703 * as properties in the PG. Finally, the prop_pattern elements are
2704 * processed and added to the PG.
2705 */
2706 static int
2707 lxml_get_tm_prop_pattern(entity_t *service, xmlNodePtr prop_pattern,
2708 const char *pgpat_name)
2709 {
2710 xmlNodePtr cursor;
2711 int extra;
2712 pgroup_t *pg;
2713 property_t *p;
2714 char *pg_name;
2715 size_t prefix_len;
2716 xmlChar *prop_pattern_name;
2717 int r;
2718 const char *unique;
2719 value_t *v;
2720
2721 /* Find the unique part of the pg_pattern property group name. */
2722 prefix_len = strlen(SCF_PG_TM_PG_PAT_BASE);
2723 assert(strncmp(pgpat_name, SCF_PG_TM_PG_PAT_BASE, prefix_len) == 0);
2724 unique = pgpat_name + prefix_len;
2725
2726 /*
2727 * We need to get the value of the name attribute first. The
2728 * prop_pattern name as well as the name of the enclosing
2729 * pg_pattern both constitute part of the name of the property
2730 * group that we will create.
2731 */
2732 prop_pattern_name = xmlGetProp(prop_pattern, (xmlChar *)name_attr);
2733 if ((prop_pattern_name == NULL) || (*prop_pattern_name == 0)) {
2734 semerr(gettext("prop_pattern name is missing for %s\n"),
2735 service->sc_name);
2736 return (-1);
2737 }
2738 if (uu_check_name((const char *)prop_pattern_name,
2739 UU_NAME_DOMAIN) != 0) {
2740 semerr(gettext("prop_pattern name, \"%s\", for %s is not "
2741 "valid.\n"), prop_pattern_name, service->sc_name);
2742 xmlFree(prop_pattern_name);
2743 return (-1);
2744 }
2745 pg_name = safe_malloc(max_scf_name_len + 1);
2746 if ((extra = snprintf(pg_name, max_scf_name_len + 1, "%s%s_%s",
2747 SCF_PG_TM_PROP_PATTERN_PREFIX, unique,
2748 (char *)prop_pattern_name)) >= max_scf_name_len + 1) {
2749 uu_die(gettext("prop_pattern name, \"%s\", for %s is %d "
2750 "characters too long\n"), (char *)prop_pattern_name,
2751 service->sc_name, extra - max_scf_name_len);
2752 }
2753
2754 /*
2755 * Create the property group, the property referencing the pg_pattern
2756 * name, and add the prop_pattern attributes to the property group.
2757 */
2758 pg = internal_pgroup_create_strict(service, pg_name,
2759 SCF_GROUP_TEMPLATE_PROP_PATTERN);
2760 if (pg == NULL) {
2761 uu_die(gettext("Property group for prop_pattern, \"%s\", "
2762 "already exists in %s\n"), prop_pattern_name,
2763 service->sc_name);
2764 }
2765
2766 p = internal_property_create(SCF_PROPERTY_TM_PG_PATTERN,
2767 SCF_TYPE_ASTRING, 1, safe_strdup(pgpat_name));
2768 /*
2769 * Unfortunately, internal_property_create() does not set the free
2770 * function for the value, so we'll set it now.
2771 */
2772 v = uu_list_first(p->sc_property_values);
2773 v->sc_free = lxml_free_str;
2774 if (internal_attach_property(pg, p) != 0)
2775 internal_property_free(p);
2776
2777
2778 r = lxml_get_prop_pattern_attributes(pg, prop_pattern);
2779 if (r != 0)
2780 goto out;
2781
2782 /*
2783 * Now process the elements of prop_pattern
2784 */
2785 for (cursor = prop_pattern->xmlChildrenNode;
2786 cursor != NULL;
2787 cursor = cursor->next) {
2788 if (lxml_ignorable_block(cursor))
2789 continue;
2790
2791 switch (lxml_xlate_element(cursor->name)) {
2792 case SC_CARDINALITY:
2793 r = lxml_get_tm_cardinality(service, pg, cursor);
2794 if (r != 0)
2795 goto out;
2796 break;
2797 case SC_CHOICES:
2798 r = lxml_get_tm_choices(service, pg, cursor);
2799 if (r != 0)
2800 goto out;
2801 break;
2802 case SC_COMMON_NAME:
2803 (void) lxml_get_all_loctext(service, pg, cursor,
2804 COMMON_NAME_FMT, (const char *)cursor->name);
2805 break;
2806 case SC_CONSTRAINTS:
2807 r = lxml_get_tm_constraints(service, pg, cursor);
2808 if (r != 0)
2809 goto out;
2810 break;
2811 case SC_DESCRIPTION:
2812 (void) lxml_get_all_loctext(service, pg, cursor,
2813 DESCRIPTION_FMT, (const char *)cursor->name);
2814 break;
2815 case SC_INTERNAL_SEPARATORS:
2816 r = lxml_get_tm_internal_seps(service, pg, cursor);
2817 if (r != 0)
2818 goto out;
2819 break;
2820 case SC_UNITS:
2821 (void) lxml_get_all_loctext(service, pg, cursor,
2822 UNITS_FMT, "units");
2823 break;
2824 case SC_VALUES:
2825 (void) lxml_get_tm_values(service, pg, cursor);
2826 break;
2827 case SC_VISIBILITY:
2828 /*
2829 * The visibility element is empty, so we only need
2830 * to proccess the value attribute.
2831 */
2832 (void) new_str_prop_from_attr(pg,
2833 SCF_PROPERTY_TM_VISIBILITY, SCF_TYPE_ASTRING,
2834 cursor, value_attr);
2835 break;
2836 default:
2837 uu_die(gettext("illegal element \"%s\" in prop_pattern "
2838 "for service \"%s\"\n"), cursor->name,
2839 service->sc_name);
2840 }
2841 }
2842
2843 out:
2844 xmlFree(prop_pattern_name);
2845 free(pg_name);
2846 return (r);
2847 }
2848
2849 /*
2850 * Get the pg_pattern attributes and save them as properties in the
2851 * property group at pg. The pg_pattern element accepts four attributes --
2852 * name, type, required and target.
2853 */
2854 static int
2855 lxml_get_pg_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
2856 {
2857 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
2858 SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
2859 return (-1);
2860 }
2861 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
2862 SCF_TYPE_ASTRING, cursor, type_attr, NULL) != 0) {
2863 return (-1);
2864 }
2865 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TARGET,
2866 SCF_TYPE_ASTRING, cursor, target_attr, NULL) != 0) {
2867 return (-1);
2868 }
2869 if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
2870 required_attr) != 0)
2871 return (-1);
2872 return (0);
2873 }
2874
2875 /*
2876 * There are several restrictions on the pg_pattern attributes that cannot
2877 * be specifed in the service bundle DTD. This function verifies that
2878 * those restrictions have been satisfied. The restrictions are:
2879 *
2880 * - The target attribute may have a value of "instance" only when the
2881 * template block is in a service declaration.
2882 *
2883 * - The target attribute may have a value of "delegate" only when the
2884 * template block applies to a restarter.
2885 *
2886 * - The target attribute may have a value of "all" only when the
2887 * template block applies to the master restarter.
2888 *
2889 * The function returns 0 on success and -1 on failure.
2890 */
2891 static int
2892 verify_pg_pattern_attributes(entity_t *s, pgroup_t *pg)
2893 {
2894 int is_restarter;
2895 property_t *target;
2896 value_t *v;
2897
2898 /* Find the value of the target property. */
2899 target = internal_property_find(pg, SCF_PROPERTY_TM_TARGET);
2900 if (target == NULL) {
2901 uu_die(gettext("pg_pattern is missing the %s attribute "
2902 "in %s\n"), target_attr, s->sc_name);
2903 return (-1);
2904 }
2905 v = uu_list_first(target->sc_property_values);
2906 assert(v != NULL);
2907 assert(v->sc_type == SCF_TYPE_ASTRING);
2908
2909 /*
2910 * If target has a value of instance, the template must be in a
2911 * service object.
2912 */
2913 if (strcmp(v->sc_u.sc_string, "instance") == 0) {
2914 if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
2915 uu_warn(gettext("pg_pattern %s attribute may only "
2916 "have a value of \"instance\" when it is in a "
2917 "service declaration.\n"), target_attr);
2918 return (-1);
2919 }
2920 }
2921
2922 /*
2923 * If target has a value of "delegate", the template must be in a
2924 * restarter.
2925 */
2926 if (strcmp(v->sc_u.sc_string, "delegate") == 0) {
2927 is_restarter = 0;
2928 if ((s->sc_etype == SVCCFG_SERVICE_OBJECT) &&
2929 (s->sc_u.sc_service.sc_service_type == SVCCFG_RESTARTER)) {
2930 is_restarter = 1;
2931 }
2932 if ((s->sc_etype == SVCCFG_INSTANCE_OBJECT) &&
2933 (s->sc_parent->sc_u.sc_service.sc_service_type ==
2934 SVCCFG_RESTARTER)) {
2935 is_restarter = 1;
2936 }
2937 if (is_restarter == 0) {
2938 uu_warn(gettext("pg_pattern %s attribute has a "
2939 "value of \"delegate\" but is not in a "
2940 "restarter service\n"), target_attr);
2941 return (-1);
2942 }
2943 }
2944
2945 /*
2946 * If target has a value of "all", the template must be in the
2947 * global (SCF_SERVICE_GLOBAL) service.
2948 */
2949 if (strcmp(v->sc_u.sc_string, all_value) == 0) {
2950 if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
2951 uu_warn(gettext("pg_pattern %s attribute has a "
2952 "value of \"%s\" but is not in a "
2953 "service entity.\n"), target_attr, all_value);
2954 return (-1);
2955 }
2956 if (strcmp(s->sc_fmri, SCF_SERVICE_GLOBAL) != 0) {
2957 uu_warn(gettext("pg_pattern %s attribute has a "
2958 "value of \"%s\" but is in the \"%s\" service. "
2959 "pg_patterns with target \"%s\" are only allowed "
2960 "in the global service.\n"),
2961 target_attr, all_value, s->sc_fmri, all_value);
2962 return (-1);
2963 }
2964 }
2965
2966 return (0);
2967 }
2968
2969 static int
2970 lxml_get_tm_pg_pattern(entity_t *service, xmlNodePtr pg_pattern)
2971 {
2972 xmlNodePtr cursor;
2973 int out_len;
2974 xmlChar *name;
2975 pgroup_t *pg = NULL;
2976 char *pg_name;
2977 int r = -1;
2978 xmlChar *type;
2979
2980 pg_name = safe_malloc(max_scf_name_len + 1);
2981
2982 /*
2983 * Get the name and type attributes. Their presence or absence
2984 * determines whcih prefix we will use for the property group name.
2985 * There are four cases -- neither attribute is present, both are
2986 * present, only name is present or only type is present.
2987 */
2988 name = xmlGetProp(pg_pattern, (xmlChar *)name_attr);
2989 type = xmlGetProp(pg_pattern, (xmlChar *)type_attr);
2990 if ((name == NULL) || (*name == 0)) {
2991 if ((type == NULL) || (*type == 0)) {
2992 /* PG name contains only the prefix in this case */
2993 if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
2994 max_scf_name_len + 1) >= max_scf_name_len + 1) {
2995 uu_die(gettext("Unable to create pg_pattern "
2996 "property for %s\n"), service->sc_name);
2997 }
2998 } else {
2999 /*
3000 * If we have a type and no name, the type becomes
3001 * part of the pg_pattern property group name.
3002 */
3003 if ((out_len = snprintf(pg_name, max_scf_name_len + 1,
3004 "%s%s", SCF_PG_TM_PG_PATTERN_T_PREFIX, type)) >=
3005 max_scf_name_len + 1) {
3006 uu_die(gettext("pg_pattern type is for %s is "
3007 "%d bytes too long\n"), service->sc_name,
3008 out_len - max_scf_name_len);
3009 }
3010 }
3011 } else {
3012 const char *prefix;
3013
3014 /* Make sure that the name is valid. */
3015 if (uu_check_name((const char *)name, UU_NAME_DOMAIN) != 0) {
3016 semerr(gettext("pg_pattern name attribute, \"%s\", "
3017 "for %s is invalid\n"), name, service->sc_name);
3018 goto out;
3019 }
3020
3021 /*
3022 * As long as the pg_pattern has a name, it becomes part of
3023 * the name of the pg_pattern property group name. We
3024 * merely need to pick the appropriate prefix.
3025 */
3026 if ((type == NULL) || (*type == 0)) {
3027 prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX;
3028 } else {
3029 prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX;
3030 }
3031 if ((out_len = snprintf(pg_name, max_scf_name_len + 1, "%s%s",
3032 prefix, name)) >= max_scf_name_len + 1) {
3033 uu_die(gettext("pg_pattern property group name "
3034 "for %s is %d bytes too long\n"), service->sc_name,
3035 out_len - max_scf_name_len);
3036 }
3037 }
3038
3039 /*
3040 * Create the property group for holding this pg_pattern
3041 * information, and capture the pg_pattern attributes.
3042 */
3043 pg = internal_pgroup_create_strict(service, pg_name,
3044 SCF_GROUP_TEMPLATE_PG_PATTERN);
3045 if (pg == NULL) {
3046 if ((name == NULL) || (*name == 0)) {
3047 if ((type == NULL) ||(*type == 0)) {
3048 semerr(gettext("pg_pattern with empty name and "
3049 "type is not unique in %s\n"),
3050 service->sc_name);
3051 } else {
3052 semerr(gettext("pg_pattern with empty name and "
3053 "type \"%s\" is not unique in %s\n"),
3054 type, service->sc_name);
3055 }
3056 } else {
3057 if ((type == NULL) || (*type == 0)) {
3058 semerr(gettext("pg_pattern with name \"%s\" "
3059 "and empty type is not unique in %s\n"),
3060 name, service->sc_name);
3061 } else {
3062 semerr(gettext("pg_pattern with name \"%s\" "
3063 "and type \"%s\" is not unique in %s\n"),
3064 name, type, service->sc_name);
3065 }
3066 }
3067 goto out;
3068 }
3069
3070 /*
3071 * Get the pg_pattern attributes from the manifest and verify
3072 * that they satisfy our restrictions.
3073 */
3074 r = lxml_get_pg_pattern_attributes(pg, pg_pattern);
3075 if (r != 0)
3076 goto out;
3077 if (verify_pg_pattern_attributes(service, pg) != 0) {
3078 semerr(gettext("Invalid pg_pattern attributes in %s\n"),
3079 service->sc_name);
3080 r = -1;
3081 goto out;
3082 }
3083
3084 /*
3085 * Now process all of the elements of pg_pattern.
3086 */
3087 for (cursor = pg_pattern->xmlChildrenNode;
3088 cursor != NULL;
3089 cursor = cursor->next) {
3090 if (lxml_ignorable_block(cursor))
3091 continue;
3092
3093 switch (lxml_xlate_element(cursor->name)) {
3094 case SC_COMMON_NAME:
3095 (void) lxml_get_all_loctext(service, pg, cursor,
3096 COMMON_NAME_FMT, (const char *)cursor->name);
3097 break;
3098 case SC_DESCRIPTION:
3099 (void) lxml_get_all_loctext(service, pg, cursor,
3100 DESCRIPTION_FMT, (const char *)cursor->name);
3101 break;
3102 case SC_PROP_PATTERN:
3103 r = lxml_get_tm_prop_pattern(service, cursor,
3104 pg_name);
3105 if (r != 0)
3106 goto out;
3107 break;
3108 default:
3109 uu_die(gettext("illegal element \"%s\" in pg_pattern "
3110 "for service \"%s\"\n"), cursor->name,
3111 service->sc_name);
3112 }
3113 }
3114
3115 out:
3116 if ((r != 0) && (pg != NULL)) {
3117 internal_detach_pgroup(service, pg);
3118 internal_pgroup_free(pg);
3119 }
3120 free(pg_name);
3121 xmlFree(name);
3122 xmlFree(type);
3123
3124 return (r);
3125 }
3126
3127 static int
3128 lxml_get_template(entity_t *service, xmlNodePtr templ)
3129 {
3130 xmlNodePtr cursor;
3131
3132 for (cursor = templ->xmlChildrenNode; cursor != NULL;
3133 cursor = cursor->next) {
3134 if (lxml_ignorable_block(cursor))
3135 continue;
3136
3137 switch (lxml_xlate_element(cursor->name)) {
3138 case SC_COMMON_NAME:
3139 (void) lxml_get_tm_common_name(service, cursor);
3140 break;
3141 case SC_DESCRIPTION:
3142 (void) lxml_get_tm_description(service, cursor);
3143 break;
3144 case SC_DOCUMENTATION:
3145 (void) lxml_get_tm_documentation(service, cursor);
3146 break;
3147 case SC_PG_PATTERN:
3148 if (lxml_get_tm_pg_pattern(service, cursor) != 0)
3149 return (-1);
3150 break;
3151 default:
3152 uu_die(gettext("illegal element \"%s\" on template "
3153 "for service \"%s\"\n"),
3154 cursor->name, service->sc_name);
3155 }
3156 }
3157
3158 return (0);
3159 }
3160
3161 static int
3162 lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
3163 {
3164 entity_t *i;
3165 xmlChar *enabled;
3166 pgroup_t *pg;
3167 property_t *p;
3168 char *package;
3169 uint64_t enabled_val = 0;
3170
3171 i = internal_instance_new("default");
3172
3173 if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) {
3174 enabled_val = (strcmp(true, (const char *)enabled) == 0) ?
3175 1 : 0;
3176 xmlFree(enabled);
3177 }
3178
3179 /*
3180 * New general property group with enabled boolean property set.
3181 */
3182
3183 i->sc_op = service->sc_op;
3184 pg = internal_pgroup_new();
3185 (void) internal_attach_pgroup(i, pg);
3186
3187 pg->sc_pgroup_name = (char *)scf_pg_general;
3188 pg->sc_pgroup_type = (char *)scf_group_framework;
3189 pg->sc_pgroup_flags = 0;
3190
3191 p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
3192 enabled_val);
3193
3194 (void) internal_attach_property(pg, p);
3195
3196 /*
3197 * Add general/package property if PKGINST is set.
3198 */
3199 if ((package = getenv("PKGINST")) != NULL) {
3200 p = internal_property_create(SCF_PROPERTY_PACKAGE,
3201 SCF_TYPE_ASTRING, 1, package);
3202
3203 (void) internal_attach_property(pg, p);
3204 }
3205
3206 return (internal_attach_entity(service, i));
3207 }
3208
3209 /*
3210 * Translate an instance element into an internal property tree, added to
3211 * service. If op is SVCCFG_OP_APPLY (i.e., apply a profile), set the
3212 * enabled property to override.
3213 *
3214 * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3215 * modification of template data.
3216 */
3217 static int
3218 lxml_get_instance(entity_t *service, xmlNodePtr inst, bundle_type_t bt,
3219 svccfg_op_t op)
3220 {
3221 entity_t *i;
3222 pgroup_t *pg;
3223 property_t *p;
3224 xmlNodePtr cursor;
3225 xmlChar *enabled;
3226 int r, e_val;
3227
3228 /*
3229 * Fetch its attributes, as appropriate.
3230 */
3231 i = internal_instance_new((char *)xmlGetProp(inst,
3232 (xmlChar *)name_attr));
3233
3234 /*
3235 * Note that this must be done before walking the children so that
3236 * sc_fmri is set in case we enter lxml_get_dependent().
3237 */
3238 r = internal_attach_entity(service, i);
3239 if (r != 0)
3240 return (r);
3241
3242 i->sc_op = op;
3243 enabled = xmlGetProp(inst, (xmlChar *)enabled_attr);
3244
3245 if (enabled == NULL) {
3246 if (bt == SVCCFG_MANIFEST) {
3247 semerr(gettext("Instance \"%s\" missing attribute "
3248 "\"%s\".\n"), i->sc_name, enabled_attr);
3249 return (-1);
3250 }
3251 } else { /* enabled != NULL */
3252 if (strcmp(true, (const char *)enabled) != 0 &&
3253 strcmp(false, (const char *)enabled) != 0) {
3254 xmlFree(enabled);
3255 semerr(gettext("Invalid enabled value\n"));
3256 return (-1);
3257 }
3258 pg = internal_pgroup_new();
3259 (void) internal_attach_pgroup(i, pg);
3260
3261 pg->sc_pgroup_name = (char *)scf_pg_general;
3262 pg->sc_pgroup_type = (char *)scf_group_framework;
3263 pg->sc_pgroup_flags = 0;
3264
3265 e_val = (strcmp(true, (const char *)enabled) == 0);
3266 p = internal_property_create(SCF_PROPERTY_ENABLED,
3267 SCF_TYPE_BOOLEAN, 1, (uint64_t)e_val);
3268
3269 p->sc_property_override = (op == SVCCFG_OP_APPLY);
3270
3271 (void) internal_attach_property(pg, p);
3272
3273 xmlFree(enabled);
3274 }
3275
3276 /*
3277 * Walk its child elements, as appropriate.
3278 */
3279 for (cursor = inst->xmlChildrenNode; cursor != NULL;
3280 cursor = cursor->next) {
3281 if (lxml_ignorable_block(cursor))
3282 continue;
3283
3284 switch (lxml_xlate_element(cursor->name)) {
3285 case SC_RESTARTER:
3286 (void) lxml_get_restarter(i, cursor);
3287 break;
3288 case SC_DEPENDENCY:
3289 (void) lxml_get_dependency(i, cursor);
3290 break;
3291 case SC_DEPENDENT:
3292 (void) lxml_get_dependent(i, cursor);
3293 break;
3294 case SC_METHOD_CONTEXT:
3295 (void) lxml_get_entity_method_context(i, cursor);
3296 break;
3297 case SC_EXEC_METHOD:
3298 (void) lxml_get_exec_method(i, cursor);
3299 break;
3300 case SC_PROPERTY_GROUP:
3301 (void) lxml_get_pgroup(i, cursor);
3302 break;
3303 case SC_TEMPLATE:
3304 if (op == SVCCFG_OP_APPLY) {
3305 semerr(gettext("Template data for \"%s\" may "
3306 "not be modified in a profile.\n"),
3307 i->sc_name);
3308
3309 return (-1);
3310 }
3311
3312 if (lxml_get_template(i, cursor) != 0)
3313 return (-1);
3314 break;
3315 case SC_NOTIFICATION_PARAMETERS:
3316 if (lxml_get_notification_parameters(i, cursor) != 0)
3317 return (-1);
3318 break;
3319 default:
3320 uu_die(gettext(
3321 "illegal element \"%s\" on instance \"%s\"\n"),
3322 cursor->name, i->sc_name);
3323 break;
3324 }
3325 }
3326
3327 return (0);
3328 }
3329
3330 /* ARGSUSED1 */
3331 static int
3332 lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
3333 {
3334 pgroup_t *pg;
3335 property_t *p;
3336 int r;
3337
3338 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
3339 (char *)scf_group_framework);
3340
3341 p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE,
3342 SCF_TYPE_BOOLEAN, 1, (uint64_t)1);
3343
3344 r = internal_attach_property(pg, p);
3345 if (r != 0) {
3346 internal_property_free(p);
3347 return (-1);
3348 }
3349
3350 return (0);
3351 }
3352
3353 /*
3354 * Check to see if the service should allow the upgrade
3355 * process to handle adding of the manifestfiles linkage.
3356 *
3357 * If the service exists and does not have a manifestfiles
3358 * property group then the upgrade process should handle
3359 * the service.
3360 *
3361 * If the service doesn't exist or the service exists
3362 * and has a manifestfiles property group then the import
3363 * process can handle the manifestfiles property group
3364 * work.
3365 *
3366 * This prevents potential cleanup of unaccounted for instances
3367 * in early manifest import due to upgrade process needing
3368 * information that has not yet been supplied by manifests
3369 * that are still located in the /var/svc manifests directory.
3370 */
3371 static int
3372 lxml_check_upgrade(const char *service)
3373 {
3374 scf_handle_t *h = NULL;
3375 scf_scope_t *sc = NULL;
3376 scf_service_t *svc = NULL;
3377 scf_propertygroup_t *pg = NULL;
3378 int rc = SCF_FAILED;
3379
3380 if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
3381 (sc = scf_scope_create(h)) == NULL ||
3382 (svc = scf_service_create(h)) == NULL ||
3383 (pg = scf_pg_create(h)) == NULL)
3384 goto out;
3385
3386 if (scf_handle_bind(h) != 0)
3387 goto out;
3388
3389 if (scf_handle_get_scope(h, SCF_FMRI_LOCAL_SCOPE, sc) == -1)
3390 goto out;
3391
3392 if (scf_scope_get_service(sc, service, svc) != SCF_SUCCESS) {
3393 if (scf_error() == SCF_ERROR_NOT_FOUND)
3394 rc = SCF_SUCCESS;
3395
3396 goto out;
3397 }
3398
3399 if (scf_service_get_pg(svc, SCF_PG_MANIFESTFILES, pg) != SCF_SUCCESS)
3400 goto out;
3401
3402 rc = SCF_SUCCESS;
3403 out:
3404 scf_pg_destroy(pg);
3405 scf_service_destroy(svc);
3406 scf_scope_destroy(sc);
3407 scf_handle_destroy(h);
3408
3409 return (rc);
3410 }
3411
3412 /*
3413 * Translate a service element into an internal instance/property tree, added
3414 * to bundle.
3415 *
3416 * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3417 * modification of template data.
3418 */
3419 static int
3420 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
3421 {
3422 pgroup_t *pg;
3423 property_t *p;
3424 entity_t *s;
3425 xmlNodePtr cursor;
3426 xmlChar *type;
3427 xmlChar *version;
3428 int e;
3429
3430 /*
3431 * Fetch attributes, as appropriate.
3432 */
3433 s = internal_service_new((char *)xmlGetProp(svc,
3434 (xmlChar *)name_attr));
3435
3436 version = xmlGetProp(svc, (xmlChar *)version_attr);
3437 s->sc_u.sc_service.sc_service_version = atol((const char *)version);
3438 xmlFree(version);
3439
3440 type = xmlGetProp(svc, (xmlChar *)type_attr);
3441 s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type);
3442 xmlFree(type);
3443
3444 /*
3445 * Set the global missing type to false before processing the service
3446 */
3447 est->sc_miss_type = B_FALSE;
3448 s->sc_op = op;
3449
3450 /*
3451 * Now that the service is created create the manifest
3452 * property group and add the property value of the service.
3453 */
3454 if (lxml_check_upgrade(s->sc_name) == SCF_SUCCESS &&
3455 svc->doc->name != NULL &&
3456 bundle->sc_bundle_type == SVCCFG_MANIFEST) {
3457 char *buf, *base, *fname, *bname;
3458 size_t base_sz = 0;
3459
3460 /*
3461 * Must remove the PKG_INSTALL_ROOT, point to the correct
3462 * directory after install
3463 */
3464 bname = uu_zalloc(PATH_MAX + 1);
3465 if (realpath(svc->doc->name, bname) == NULL) {
3466 uu_die(gettext("Unable to create the real path of the "
3467 "manifest file \"%s\" : %d\n"), svc->doc->name,
3468 errno);
3469 }
3470
3471 base = getenv("PKG_INSTALL_ROOT");
3472 if (base != NULL && strncmp(bname, base, strlen(base)) == 0) {
3473 base_sz = strlen(base);
3474 }
3475 fname = safe_strdup(bname + base_sz);
3476
3477 uu_free(bname);
3478 buf = mhash_filename_to_propname(svc->doc->name, B_FALSE);
3479
3480 pg = internal_pgroup_create_strict(s, SCF_PG_MANIFESTFILES,
3481 SCF_GROUP_FRAMEWORK);
3482
3483 if (pg == NULL) {
3484 uu_die(gettext("Property group for prop_pattern, "
3485 "\"%s\", already exists in %s\n"),
3486 SCF_PG_MANIFESTFILES, s->sc_name);
3487 }
3488
3489 p = internal_property_create(buf, SCF_TYPE_ASTRING, 1, fname);
3490
3491 (void) internal_attach_property(pg, p);
3492 }
3493
3494 /*
3495 * Walk its child elements, as appropriate.
3496 */
3497 for (cursor = svc->xmlChildrenNode; cursor != NULL;
3498 cursor = cursor->next) {
3499 if (lxml_ignorable_block(cursor))
3500 continue;
3501
3502 e = lxml_xlate_element(cursor->name);
3503
3504 switch (e) {
3505 case SC_INSTANCE:
3506 if (lxml_get_instance(s, cursor,
3507 bundle->sc_bundle_type, op) != 0)
3508 return (-1);
3509 break;
3510 case SC_TEMPLATE:
3511 if (op == SVCCFG_OP_APPLY) {
3512 semerr(gettext("Template data for \"%s\" may "
3513 "not be modified in a profile.\n"),
3514 s->sc_name);
3515
3516 return (-1);
3517 }
3518
3519 if (lxml_get_template(s, cursor) != 0)
3520 return (-1);
3521 break;
3522 case SC_NOTIFICATION_PARAMETERS:
3523 if (lxml_get_notification_parameters(s, cursor) != 0)
3524 return (-1);
3525 break;
3526 case SC_STABILITY:
3527 (void) lxml_get_entity_stability(s, cursor);
3528 break;
3529 case SC_DEPENDENCY:
3530 (void) lxml_get_dependency(s, cursor);
3531 break;
3532 case SC_DEPENDENT:
3533 (void) lxml_get_dependent(s, cursor);
3534 break;
3535 case SC_RESTARTER:
3536 (void) lxml_get_restarter(s, cursor);
3537 break;
3538 case SC_EXEC_METHOD:
3539 (void) lxml_get_exec_method(s, cursor);
3540 break;
3541 case SC_METHOD_CONTEXT:
3542 (void) lxml_get_entity_method_context(s, cursor);
3543 break;
3544 case SC_PROPERTY_GROUP:
3545 (void) lxml_get_pgroup(s, cursor);
3546 break;
3547 case SC_INSTANCE_CREATE_DEFAULT:
3548 (void) lxml_get_default_instance(s, cursor);
3549 break;
3550 case SC_INSTANCE_SINGLE:
3551 (void) lxml_get_single_instance(s, cursor);
3552 break;
3553 default:
3554 uu_die(gettext(
3555 "illegal element \"%s\" on service \"%s\"\n"),
3556 cursor->name, s->sc_name);
3557 break;
3558 }
3559 }
3560
3561 /*
3562 * Now that the service has been processed set the missing type
3563 * for the service. So that only the services with missing
3564 * types are processed.
3565 */
3566 s->sc_miss_type = est->sc_miss_type;
3567 if (est->sc_miss_type)
3568 est->sc_miss_type = B_FALSE;
3569
3570 return (internal_attach_service(bundle, s));
3571 }
3572
3573 #ifdef DEBUG
3574 void
3575 lxml_dump(int g, xmlNodePtr p)
3576 {
3577 if (p && p->name) {
3578 (void) printf("%d %s\n", g, p->name);
3579
3580 for (p = p->xmlChildrenNode; p != NULL; p = p->next)
3581 lxml_dump(g + 1, p);
3582 }
3583 }
3584 #endif /* DEBUG */
3585
3586 static int
3587 lxml_is_known_dtd(const xmlChar *dtdname)
3588 {
3589 if (dtdname == NULL ||
3590 strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0)
3591 return (0);
3592
3593 return (1);
3594 }
3595
3596 static int
3597 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
3598 xmlNodePtr subbundle, svccfg_op_t op)
3599 {
3600 xmlNodePtr cursor;
3601 xmlChar *type;
3602 int e;
3603
3604 /*
3605 * 1. Get bundle attributes.
3606 */
3607 type = xmlGetProp(subbundle, (xmlChar *)type_attr);
3608 bundle->sc_bundle_type = lxml_xlate_bundle_type(type);
3609 if (bundle->sc_bundle_type != bundle_type &&
3610 bundle_type != SVCCFG_UNKNOWN_BUNDLE) {
3611 semerr(gettext("included bundle of different type.\n"));
3612 return (-1);
3613 }
3614
3615 xmlFree(type);
3616
3617 switch (op) {
3618 case SVCCFG_OP_IMPORT:
3619 if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
3620 semerr(gettext("document is not a manifest.\n"));
3621 return (-1);
3622 }
3623 break;
3624 case SVCCFG_OP_APPLY:
3625 if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
3626 semerr(gettext("document is not a profile.\n"));
3627 return (-1);
3628 }
3629 break;
3630 case SVCCFG_OP_RESTORE:
3631 if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) {
3632 semerr(gettext("document is not an archive.\n"));
3633 return (-1);
3634 }
3635 break;
3636 }
3637
3638 if (((bundle->sc_bundle_name = xmlGetProp(subbundle,
3639 (xmlChar *)name_attr)) == NULL) || (*bundle->sc_bundle_name == 0)) {
3640 semerr(gettext("service bundle lacks name attribute\n"));
3641 return (-1);
3642 }
3643
3644 /*
3645 * 2. Get services, descend into each one and build state.
3646 */
3647 for (cursor = subbundle->xmlChildrenNode; cursor != NULL;
3648 cursor = cursor->next) {
3649 if (lxml_ignorable_block(cursor))
3650 continue;
3651
3652 e = lxml_xlate_element(cursor->name);
3653
3654 switch (e) {
3655 case SC_XI_INCLUDE:
3656 continue;
3657
3658 case SC_SERVICE_BUNDLE:
3659 if (lxml_get_bundle(bundle, bundle_type, cursor, op))
3660 return (-1);
3661 break;
3662 case SC_SERVICE:
3663 if (lxml_get_service(bundle, cursor, op) != 0)
3664 return (-1);
3665 break;
3666 }
3667 }
3668
3669 return (0);
3670 }
3671
3672 /*
3673 * Load an XML tree from filename and translate it into an internal service
3674 * tree bundle. Require that the bundle be of appropriate type for the
3675 * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY.
3676 */
3677 int
3678 lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op)
3679 {
3680 xmlDocPtr document;
3681 xmlNodePtr cursor;
3682 xmlDtdPtr dtd = NULL;
3683 xmlValidCtxtPtr vcp;
3684 boolean_t do_validate;
3685 char *dtdpath = NULL;
3686 int r;
3687
3688 /*
3689 * Verify we can read the file before we try to parse it.
3690 */
3691 if (access(filename, R_OK | F_OK) == -1) {
3692 semerr(gettext("unable to open file: %s\n"), strerror(errno));
3693 return (-1);
3694 }
3695
3696 /*
3697 * Until libxml2 addresses DTD-based validation with XInclude, we don't
3698 * validate service profiles (i.e. the apply path).
3699 */
3700 do_validate = (op != SVCCFG_OP_APPLY) &&
3701 (getenv("SVCCFG_NOVALIDATE") == NULL);
3702 if (do_validate)
3703 dtdpath = getenv("SVCCFG_DTD");
3704
3705 if (dtdpath != NULL)
3706 xmlLoadExtDtdDefaultValue = 0;
3707
3708 if ((document = xmlReadFile(filename, NULL, 0)) == NULL) {
3709 semerr(gettext("couldn't parse document\n"));
3710 return (-1);
3711 }
3712
3713 document->name = safe_strdup(filename);
3714
3715 /*
3716 * Verify that this is a document type we understand.
3717 */
3718 if ((dtd = xmlGetIntSubset(document)) == NULL) {
3719 semerr(gettext("document has no DTD\n"));
3720 return (-1);
3721 } else if (dtdpath == NULL && !do_validate) {
3722 /*
3723 * If apply then setup so that some validation
3724 * for specific elements can be done.
3725 */
3726 dtdpath = (char *)document->intSubset->SystemID;
3727 }
3728
3729 if (!lxml_is_known_dtd(dtd->SystemID)) {
3730 semerr(gettext("document DTD unknown; not service bundle?\n"));
3731 return (-1);
3732 }
3733
3734 if ((cursor = xmlDocGetRootElement(document)) == NULL) {
3735 semerr(gettext("document is empty\n"));
3736 xmlFreeDoc(document);
3737 return (-1);
3738 }
3739
3740 if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) {
3741 semerr(gettext("document is not a service bundle\n"));
3742 xmlFreeDoc(document);
3743 return (-1);
3744 }
3745
3746
3747 if (dtdpath != NULL) {
3748 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
3749 if (dtd == NULL) {
3750 semerr(gettext("Could not parse DTD \"%s\".\n"),
3751 dtdpath);
3752 return (-1);
3753 }
3754
3755 if (document->extSubset != NULL)
3756 xmlFreeDtd(document->extSubset);
3757
3758 document->extSubset = dtd;
3759 }
3760
3761 if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
3762 semerr(gettext("couldn't handle XInclude statements "
3763 "in document\n"));
3764 return (-1);
3765 }
3766
3767 if (do_validate) {
3768 vcp = xmlNewValidCtxt();
3769 if (vcp == NULL)
3770 uu_die(gettext("could not allocate memory"));
3771 vcp->warning = xmlParserValidityWarning;
3772 vcp->error = xmlParserValidityError;
3773
3774 r = xmlValidateDocument(vcp, document);
3775
3776 xmlFreeValidCtxt(vcp);
3777
3778 if (r == 0) {
3779 semerr(gettext("Document is not valid.\n"));
3780 xmlFreeDoc(document);
3781 return (-1);
3782 }
3783 }
3784
3785 #ifdef DEBUG
3786 lxml_dump(0, cursor);
3787 #endif /* DEBUG */
3788
3789 r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op);
3790
3791 xmlFreeDoc(document);
3792
3793 return (r);
3794 }
3795
3796 int
3797 lxml_inventory(const char *filename)
3798 {
3799 bundle_t *b;
3800 uu_list_walk_t *svcs, *insts;
3801 entity_t *svc, *inst;
3802
3803 b = internal_bundle_new();
3804
3805 if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) {
3806 internal_bundle_free(b);
3807 return (-1);
3808 }
3809
3810 svcs = uu_list_walk_start(b->sc_bundle_services, 0);
3811 if (svcs == NULL)
3812 uu_die(gettext("Couldn't walk services"));
3813
3814 while ((svc = uu_list_walk_next(svcs)) != NULL) {
3815 uu_list_t *inst_list;
3816
3817 inst_list = svc->sc_u.sc_service.sc_service_instances;
3818 insts = uu_list_walk_start(inst_list, 0);
3819 if (insts == NULL)
3820 uu_die(gettext("Couldn't walk instances"));
3821
3822 while ((inst = uu_list_walk_next(insts)) != NULL)
3823 (void) printf("svc:/%s:%s\n", svc->sc_name,
3824 inst->sc_name);
3825
3826 uu_list_walk_end(insts);
3827 }
3828
3829 uu_list_walk_end(svcs);
3830
3831 svcs = uu_list_walk_start(b->sc_bundle_services, 0);
3832 while ((svc = uu_list_walk_next(svcs)) != NULL) {
3833 (void) fputs("svc:/", stdout);
3834 (void) puts(svc->sc_name);
3835 }
3836 uu_list_walk_end(svcs);
3837
3838 internal_bundle_free(b);
3839
3840 return (0);
3841 }