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