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