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