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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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 *
 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
 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
 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
 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
 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
 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
 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
 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 *
 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
 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
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         if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SECFLAGS,
1018             SCF_TYPE_ASTRING, ctx, "security_flags", NULL) != 0)
1019                 return (-1);
1020 
1021         for (cursor = ctx->xmlChildrenNode; cursor != NULL;
1022             cursor = cursor->next) {
1023                 if (lxml_ignorable_block(cursor))
1024                         continue;
1025 
1026                 switch (lxml_xlate_element(cursor->name)) {
1027                 case SC_METHOD_CREDENTIAL:
1028                         (void) lxml_get_method_credential(pg, cursor);
1029                         break;
1030                 case SC_METHOD_PROFILE:
1031                         (void) lxml_get_method_profile(pg, cursor);
1032                         break;
1033                 case SC_METHOD_ENVIRONMENT:
1034                         (void) lxml_get_method_environment(pg, cursor);
1035                         break;
1036                 default:
1037                         semerr(gettext("illegal element \'%s\' in method "
1038                             "context\n"), (char *)cursor);
1039                         break;
1040                 }
1041         }
1042 
1043         return (0);
1044 }
1045 
1046 static int
1047 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx)
1048 {
1049         pgroup_t *pg;
1050 
1051         pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT,
1052             (char *)scf_group_framework);
1053 
1054         return (lxml_get_method_context(pg, ctx));
1055 }
1056 
1057 static int
1058 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
1059 {
1060         pgroup_t *pg;
1061         property_t *p;
1062         xmlChar *name, *timeout, *delete;
1063         xmlNodePtr cursor;
1064         int r = 0;
1065 
1066         if (entity->sc_op == SVCCFG_OP_APPLY)
1067                 lxml_validate_element(emeth);
1068 
1069         name = xmlGetProp(emeth, (xmlChar *)name_attr);
1070         pg = internal_pgroup_find_or_create(entity, (char *)name,
1071             (char *)SCF_GROUP_METHOD);
1072         xmlFree(name);
1073 
1074         if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
1075             emeth, type_attr) != 0 ||
1076             new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING,
1077             emeth, "exec") != 0)
1078                 return (-1);
1079 
1080         timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr);
1081         if (timeout != NULL) {
1082                 uint64_t u_timeout;
1083                 char *endptr;
1084                 /*
1085                  * Although an SC_COUNT represents a uint64_t the use
1086                  * of a negative value is acceptable due to the usage
1087                  * established by inetd(1M).
1088                  */
1089                 errno = 0;
1090                 u_timeout = strtoull((char *)timeout, &endptr, 10);
1091                 if (errno != 0 || endptr == (char *)timeout || *endptr)
1092                         uu_die(gettext("illegal value \"%s\" for "
1093                             "timeout_seconds (%s)\n"),
1094                             (char *)timeout, (errno) ? strerror(errno):
1095                             gettext("Illegal character"));
1096                 p = internal_property_create(SCF_PROPERTY_TIMEOUT,
1097                     SCF_TYPE_COUNT, 1, u_timeout);
1098                 r = internal_attach_property(pg, p);
1099                 xmlFree(timeout);
1100         }
1101         if (r != 0)
1102                 return (-1);
1103 
1104         /*
1105          * There is a possibility that a method context also exists, in which
1106          * case the following attributes are defined: project, resource_pool,
1107          * working_directory, profile, user, group, privileges,
1108          * limit_privileges, security_flags
1109          */
1110         for (cursor = emeth->xmlChildrenNode; cursor != NULL;
1111             cursor = cursor->next) {
1112                 if (lxml_ignorable_block(cursor))
1113                         continue;
1114 
1115                 switch (lxml_xlate_element(cursor->name)) {
1116                 case SC_STABILITY:
1117                         if (lxml_get_pgroup_stability(pg, cursor) != 0)
1118                                 return (-1);
1119                         break;
1120 
1121                 case SC_METHOD_CONTEXT:
1122                         (void) lxml_get_method_context(pg, cursor);
1123                         break;
1124 
1125                 case SC_PROPVAL:
1126                         (void) lxml_get_propval(pg, cursor);
1127                         break;
1128 
1129                 case SC_PROPERTY:
1130                         (void) lxml_get_property(pg, cursor);
1131                         break;
1132 
1133                 default:
1134                         uu_die(gettext("illegal element \"%s\" on "
1135                             "execution method \"%s\"\n"), cursor->name,
1136                             pg->sc_pgroup_name);
1137                         break;
1138                 }
1139         }
1140 
1141         delete = xmlGetProp(emeth, (xmlChar *)delete_attr);
1142         pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1143         xmlFree(delete);
1144 
1145         return (0);
1146 }
1147 
1148 static int
1149 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency)
1150 {
1151         pgroup_t *pg;
1152         property_t *p;
1153         xmlNodePtr cursor;
1154         xmlChar *name;
1155         xmlChar *delete;
1156 
1157         /*
1158          * dependency attributes:
1159          * name: string
1160          * grouping: require_all | require_any | exclude_all | optional_all
1161          * reset_on: string (error | restart | refresh | none)
1162          * type:  service / path /host
1163          */
1164 
1165         if (entity->sc_op == SVCCFG_OP_APPLY)
1166                 lxml_validate_element(dependency);
1167 
1168         name = xmlGetProp(dependency, (xmlChar *)name_attr);
1169         pg = internal_pgroup_find_or_create(entity, (char *)name,
1170             (char *)SCF_GROUP_DEPENDENCY);
1171         xmlFree(name);
1172 
1173         if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
1174             dependency, type_attr) != 0)
1175                 return (-1);
1176 
1177         if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1178             SCF_TYPE_ASTRING, dependency, "restart_on") != 0)
1179                 return (-1);
1180 
1181         if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1182             dependency, "grouping") != 0)
1183                 return (-1);
1184 
1185         p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0);
1186         if (internal_attach_property(pg, p) != 0)
1187                 return (-1);
1188 
1189         for (cursor = dependency->xmlChildrenNode; cursor != NULL;
1190             cursor = cursor->next) {
1191                 xmlChar *value;
1192                 value_t *v;
1193 
1194                 if (lxml_ignorable_block(cursor))
1195                         continue;
1196 
1197                 switch (lxml_xlate_element(cursor->name)) {
1198                 case SC_STABILITY:
1199                         if (lxml_get_pgroup_stability(pg, cursor) != 0)
1200                                 return (-1);
1201                         break;
1202 
1203                 case SC_SERVICE_FMRI:
1204                         value = xmlGetProp(cursor, (xmlChar *)value_attr);
1205                         if (value != NULL) {
1206                                 if (lxml_validate_string_value(SCF_TYPE_FMRI,
1207                                     (char *)value) != 0)
1208                                         uu_die(gettext("illegal value \"%s\" "
1209                                             "for %s (%s)\n"), (char *)value,
1210                                             lxml_prop_types[SC_FMRI],
1211                                             (scf_error()) ?
1212                                             scf_strerror(scf_error()) :
1213                                             gettext("Illegal format"));
1214                                 v = internal_value_new();
1215                                 v->sc_type = SCF_TYPE_FMRI;
1216                                 v->sc_u.sc_string = (char *)value;
1217                                 internal_attach_value(p, v);
1218                         }
1219 
1220                         break;
1221 
1222                 case SC_PROPVAL:
1223                         (void) lxml_get_propval(pg, cursor);
1224                         break;
1225 
1226                 case SC_PROPERTY:
1227                         (void) lxml_get_property(pg, cursor);
1228                         break;
1229 
1230                 default:
1231                         uu_die(gettext("illegal element \"%s\" on "
1232                             "dependency group \"%s\"\n"), cursor->name, name);
1233                         break;
1234                 }
1235         }
1236 
1237         delete = xmlGetProp(dependency, (xmlChar *)delete_attr);
1238         pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1239         xmlFree(delete);
1240 
1241         return (0);
1242 }
1243 
1244 /*
1245  * Dependents are hairy.  They should cause a dependency pg to be created in
1246  * another service, but we can't do that here; we'll have to wait until the
1247  * import routines.  So for now we'll add the dependency group that should go
1248  * in the other service to the entity's dependent list.
1249  */
1250 static int
1251 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent)
1252 {
1253         xmlChar *name, *or;
1254         xmlNodePtr sf;
1255         xmlChar *fmri, *delete;
1256         pgroup_t *pg;
1257         property_t *p;
1258         xmlNodePtr n;
1259         char *myfmri;
1260 
1261         if (entity->sc_op == SVCCFG_OP_APPLY)
1262                 lxml_validate_element(dependent);
1263 
1264         name = xmlGetProp(dependent, (xmlChar *)name_attr);
1265 
1266         if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) {
1267                 semerr(gettext("Property group and dependent of entity %s "
1268                     "have same name \"%s\".\n"), entity->sc_name, name);
1269                 xmlFree(name);
1270                 return (-1);
1271         }
1272 
1273         or = xmlGetProp(dependent, (xmlChar *)override_attr);
1274 
1275         pg = internal_pgroup_new();
1276         pg->sc_pgroup_name = (char *)name;
1277         pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY;
1278         pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0);
1279         xmlFree(or);
1280         if (internal_attach_dependent(entity, pg) != 0) {
1281                 xmlFree(name);
1282                 internal_pgroup_free(pg);
1283                 return (-1);
1284         }
1285 
1286         for (sf = dependent->children; sf != NULL; sf = sf->next)
1287                 if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0)
1288                         break;
1289         assert(sf != NULL);
1290         fmri = xmlGetProp(sf, (xmlChar *)value_attr);
1291         pg->sc_pgroup_fmri = (char *)fmri;
1292 
1293         if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1294             SCF_TYPE_ASTRING, dependent, "restart_on") != 0)
1295                 return (-1);
1296 
1297         if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1298             dependent, "grouping") != 0)
1299                 return (-1);
1300 
1301         myfmri = safe_malloc(max_scf_fmri_len + 1);
1302         if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) {
1303                 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s",
1304                     entity->sc_name) < 0)
1305                         bad_error("snprintf", errno);
1306         } else {
1307                 assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT);
1308                 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s",
1309                     entity->sc_parent->sc_name, entity->sc_name) < 0)
1310                         bad_error("snprintf", errno);
1311         }
1312 
1313         p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1,
1314             myfmri);
1315         if (internal_attach_property(pg, p) != 0)
1316                 return (-1);
1317 
1318         /* Create a property to serve as a do-not-export flag. */
1319         p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1,
1320             (uint64_t)1);
1321         if (internal_attach_property(pg, p) != 0)
1322                 return (-1);
1323 
1324         for (n = sf->next; n != NULL; n = n->next) {
1325                 if (lxml_ignorable_block(n))
1326                         continue;
1327 
1328                 switch (lxml_xlate_element(n->name)) {
1329                 case SC_STABILITY:
1330                         if (new_str_prop_from_attr(pg,
1331                             SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n,
1332                             value_attr) != 0)
1333                                 return (-1);
1334                         break;
1335 
1336                 case SC_PROPVAL:
1337                         (void) lxml_get_propval(pg, n);
1338                         break;
1339 
1340                 case SC_PROPERTY:
1341                         (void) lxml_get_property(pg, n);
1342                         break;
1343 
1344                 default:
1345                         uu_die(gettext("unexpected element %s.\n"), n->name);
1346                 }
1347         }
1348 
1349         /* Go back and fill in defaults. */
1350         if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) {
1351                 p = internal_property_create(SCF_PROPERTY_TYPE,
1352                     SCF_TYPE_ASTRING, 1, "service");
1353                 if (internal_attach_property(pg, p) != 0)
1354                         return (-1);
1355         }
1356 
1357         delete = xmlGetProp(dependent, (xmlChar *)delete_attr);
1358         pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1359         xmlFree(delete);
1360 
1361         pg = internal_pgroup_find_or_create(entity, "dependents",
1362             (char *)scf_group_framework);
1363         p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri);
1364         if (internal_attach_property(pg, p) != 0)
1365                 return (-1);
1366 
1367         return (0);
1368 }
1369 
1370 static int
1371 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
1372 {
1373         pgroup_t *pg;
1374         property_t *p;
1375         xmlChar *stabval;
1376 
1377         if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) ||
1378             (*stabval == 0)) {
1379                 uu_warn(gettext("no stability value found\n"));
1380                 stabval = (xmlChar *)safe_strdup("External");
1381         }
1382 
1383         pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1384             (char *)scf_group_framework);
1385 
1386         p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY,
1387             SCF_TYPE_ASTRING, 1, stabval);
1388 
1389         return (internal_attach_property(pg, p));
1390 }
1391 
1392 static int
1393 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
1394 {
1395         pgroup_t *pg;
1396         property_t *p;
1397         xmlChar *restarter;
1398         xmlNode *cursor;
1399         int r;
1400 
1401         /*
1402          * Go find child.  Child is a service_fmri element.  value attribute
1403          * contains restarter FMRI.
1404          */
1405 
1406         pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1407             (char *)scf_group_framework);
1408 
1409         /*
1410          * Walk its child elements, as appropriate.
1411          */
1412         for (cursor = rstr->xmlChildrenNode; cursor != NULL;
1413             cursor = cursor->next) {
1414                 if (lxml_ignorable_block(cursor))
1415                         continue;
1416 
1417                 switch (lxml_xlate_element(cursor->name)) {
1418                 case SC_SERVICE_FMRI:
1419                         restarter = xmlGetProp(cursor, (xmlChar *)value_attr);
1420                         break;
1421                 default:
1422                         uu_die(gettext("illegal element \"%s\" on restarter "
1423                             "element for \"%s\"\n"), cursor->name,
1424                             entity->sc_name);
1425                         break;
1426                 }
1427         }
1428 
1429         p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1,
1430             restarter);
1431 
1432         r = internal_attach_property(pg, p);
1433         if (r != 0) {
1434                 internal_property_free(p);
1435                 return (-1);
1436         }
1437 
1438         return (0);
1439 }
1440 
1441 static void
1442 lxml_get_paramval(pgroup_t *pgrp, const char *propname, xmlNodePtr pval)
1443 {
1444         property_t *p;
1445         char *value;
1446         char *prop;
1447 
1448         prop = safe_strdup(propname);
1449 
1450         value = (char *)xmlGetProp(pval, (xmlChar *)value_attr);
1451         if (value == NULL || *value == '\0')
1452                 uu_die(gettext("property value missing for property '%s/%s'\n"),
1453                     pgrp->sc_pgroup_name, propname);
1454         p = internal_property_create(prop, SCF_TYPE_ASTRING, 1, value);
1455 
1456         (void) internal_attach_property(pgrp, p);
1457 }
1458 
1459 static void
1460 lxml_get_parameter(pgroup_t *pgrp, const char *propname, xmlNodePtr param)
1461 {
1462         property_t *p = internal_property_new();
1463 
1464         p->sc_property_name = safe_strdup(propname);
1465         p->sc_value_type = SCF_TYPE_ASTRING;
1466 
1467         (void) lxml_get_value(p, SC_ASTRING, param);
1468 
1469         (void) internal_attach_property(pgrp, p);
1470 }
1471 
1472 static void
1473 lxml_get_type(pgroup_t *pgrp, xmlNodePtr type)
1474 {
1475         property_t *p;
1476         xmlChar *name;
1477         xmlChar *active;
1478         xmlNodePtr cursor;
1479         uint64_t active_val;
1480         size_t sz = max_scf_name_len + 1;
1481         char *propname = safe_malloc(sz);
1482 
1483         if (pgrp->sc_parent->sc_op == SVCCFG_OP_APPLY)
1484                 lxml_validate_element(type);
1485 
1486         name = xmlGetProp(type, (xmlChar *)name_attr);
1487         if (name == NULL || *name == '\0')
1488                 uu_die(gettext("attribute name missing in element 'type'\n"));
1489 
1490         for (cursor = type->xmlChildrenNode; cursor != NULL;
1491             cursor = cursor->next) {
1492                 xmlChar *pname;
1493 
1494                 if (lxml_ignorable_block(cursor))
1495                         continue;
1496 
1497                 pname = xmlGetProp(cursor, (xmlChar *)name_attr);
1498                 if (pname == NULL || *pname == '\0')
1499                         uu_die(gettext(
1500                             "attribute name missing in sub-element of type\n"));
1501 
1502                 if (snprintf(propname, sz, "%s,%s", (char *)name,
1503                     (char *)pname) >= sz)
1504                         uu_die(gettext("name '%s,%s' is too long\n"),
1505                             (char *)name, (char *)pname);
1506                 xmlFree(pname);
1507 
1508                 switch (lxml_xlate_element(cursor->name)) {
1509                 case SC_PARAMETER:
1510                         lxml_get_parameter(pgrp, propname, cursor);
1511                         break;
1512 
1513                 case SC_PARAMVAL:
1514                         lxml_get_paramval(pgrp, propname, cursor);
1515                         break;
1516 
1517                 default:
1518                         uu_die(gettext("unknown element %s\n"), cursor->name);
1519                 }
1520         }
1521 
1522         active = xmlGetProp(type, (xmlChar *)active_attr);
1523         if (active == NULL || strcmp(true, (const char *)active) == 0)
1524                 active_val = 1;
1525         else
1526                 active_val = 0;
1527         xmlFree(active);
1528 
1529         if (snprintf(propname, sz, "%s,%s", (char *)name,
1530             SCF_PROPERTY_ACTIVE_POSTFIX) >= sz)
1531                 uu_die(gettext("name '%s,%s' is too long\n"),
1532                     (char *)name, SCF_PROPERTY_ACTIVE_POSTFIX);
1533 
1534         p = internal_property_create(propname, SCF_TYPE_BOOLEAN, 1, active_val);
1535 
1536         (void) internal_attach_property(pgrp, p);
1537 
1538         xmlFree(name);
1539 }
1540 
1541 static void
1542 lxml_get_event(entity_t *entity, const char *pgname, xmlNodePtr np)
1543 {
1544         xmlNodePtr cursor;
1545         pgroup_t *pgrp;
1546 
1547         pgrp = internal_pgroup_find_or_create(entity, pgname,
1548             SCF_NOTIFY_PARAMS_PG_TYPE);
1549         for (cursor = np->xmlChildrenNode; cursor != NULL;
1550             cursor = cursor->next) {
1551                 if (lxml_ignorable_block(cursor))
1552                         continue;
1553 
1554                 switch (lxml_xlate_element(cursor->name)) {
1555                 case SC_EVENT:
1556                         continue;
1557 
1558                 case SC_TYPE:
1559                         lxml_get_type(pgrp, cursor);
1560                         break;
1561 
1562                 default:
1563                         uu_warn(gettext("illegal element '%s' on "
1564                             "notification parameters\n"), cursor->name);
1565                 }
1566         }
1567 }
1568 
1569 static int
1570 lxml_get_notification_parameters(entity_t *entity, xmlNodePtr np)
1571 {
1572         char *event = NULL;
1573         char **pgs = NULL;
1574         char **p;
1575         char *pgname = NULL;
1576         xmlNodePtr cursor;
1577         int32_t tset, t;
1578         size_t sz = max_scf_name_len + 1;
1579         int count;
1580         int r = -1;
1581 
1582         for (count = 0, cursor = np->xmlChildrenNode; cursor != NULL;
1583             cursor = cursor->next) {
1584                 if (lxml_ignorable_block(cursor))
1585                         continue;
1586 
1587                 if (lxml_xlate_element(cursor->name) == SC_EVENT) {
1588                         xmlChar *s;
1589 
1590                         count++;
1591                         if (count > 1)
1592                                 uu_die(gettext("Can't have more than 1 element "
1593                                     "event in a notification parameter\n"));
1594                         s = xmlGetProp(cursor, (xmlChar *)value_attr);
1595                         if (s == NULL || (event = strdup((char *)s)) == NULL)
1596                                 uu_die(gettext("couldn't allocate memory"));
1597                         xmlFree(s);
1598                 }
1599         }
1600 
1601         pgs = tokenize(event, ",");
1602 
1603         switch (tset = check_tokens(pgs)) {
1604         case INVALID_TOKENS:
1605                 uu_die(gettext("Invalid input.\n"));
1606                 /*NOTREACHED*/
1607         case MIXED_TOKENS:
1608                 semerr(gettext("Can't mix SMF and FMA event definitions\n"));
1609                 goto out;
1610         case FMA_TOKENS:
1611                 /* make sure this is SCF_NOTIFY_PARAMS_INST */
1612                 if (entity->sc_etype != SVCCFG_INSTANCE_OBJECT ||
1613                     strcmp(entity->sc_fmri, SCF_NOTIFY_PARAMS_INST) != 0) {
1614                         semerr(gettext(
1615                             "Non-SMF transition events must go to %s\n"),
1616                             SCF_NOTIFY_PARAMS_INST);
1617                         goto out;
1618                 }
1619                 pgname = safe_malloc(sz);
1620                 for (p = pgs; *p; ++p) {
1621                         if (snprintf(pgname, sz, "%s,%s", de_tag(*p),
1622                             SCF_NOTIFY_PG_POSTFIX) >= sz)
1623                                 uu_die(gettext("event name too long: %s\n"),
1624                                     *p);
1625 
1626                         lxml_get_event(entity, pgname, np);
1627                 }
1628                 break;
1629 
1630         default:        /* smf state transition tokens */
1631                 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT &&
1632                     strcmp(entity->sc_fmri, SCF_SERVICE_GLOBAL) == 0) {
1633                         semerr(gettext(
1634                             "Can't set events for global service\n"));
1635                         goto out;
1636                 }
1637                 for (t = 0x1; t < SCF_STATE_ALL; t <<= 1) {
1638                         if (t & tset) {
1639                                 lxml_get_event(entity, tset_to_string(t), np);
1640                         }
1641                         if ((t << 16) & tset) {
1642                                 lxml_get_event(entity, tset_to_string(t << 16),
1643                                     np);
1644                         }
1645                 }
1646         }
1647 
1648         r = 0;
1649 out:
1650         free(pgname);
1651         free(pgs);
1652         free(event);
1653 
1654         return (r);
1655 }
1656 
1657 /*
1658  * Add a property containing the localized text from the manifest.  The
1659  * property is added to the property group at pg.  The name of the created
1660  * property is based on the format at pn_format.  This is an snprintf(3C)
1661  * format containing a single %s conversion specification.  At conversion
1662  * time, the %s is replaced by the locale designation.
1663  *
1664  * source is the source element and it is only used for error messages.
1665  */
1666 static int
1667 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext,
1668     const char *pn_format, const char *source)
1669 {
1670         int extra;
1671         xmlNodePtr cursor;
1672         xmlChar *val;
1673         char *stripped, *cp;
1674         property_t *p;
1675         char *prop_name;
1676         int r;
1677 
1678         if (((val = xmlGetProp(loctext, (xmlChar *)xml_lang_attr)) == NULL) ||
1679             (*val == 0)) {
1680                 if (((val = xmlGetProp(loctext,
1681                     (xmlChar *)lang_attr)) == NULL) || (*val == 0)) {
1682                         val = (xmlChar *)"unknown";
1683                 }
1684         }
1685 
1686         _scf_sanitize_locale((char *)val);
1687         prop_name = safe_malloc(max_scf_name_len + 1);
1688         if ((extra = snprintf(prop_name, max_scf_name_len + 1, pn_format,
1689             val)) >= max_scf_name_len + 1) {
1690                 extra -= max_scf_name_len;
1691                 uu_die(gettext("%s attribute is %d characters too long for "
1692                     "%s in %s\n"),
1693                     xml_lang_attr, extra, source, service->sc_name);
1694         }
1695         xmlFree(val);
1696 
1697         for (cursor = loctext->xmlChildrenNode; cursor != NULL;
1698             cursor = cursor->next) {
1699                 if (strcmp("text", (const char *)cursor->name) == 0) {
1700                         break;
1701                 } else if (strcmp("comment", (const char *)cursor->name) != 0) {
1702                         uu_die(gettext("illegal element \"%s\" on loctext "
1703                             "element for \"%s\"\n"), cursor->name,
1704                             service->sc_name);
1705                 }
1706         }
1707 
1708         if (cursor == NULL) {
1709                 uu_die(gettext("loctext element has no content for \"%s\"\n"),
1710                     service->sc_name);
1711         }
1712 
1713         /*
1714          * Remove leading and trailing whitespace.
1715          */
1716         stripped = safe_strdup((const char *)cursor->content);
1717 
1718         for (; isspace(*stripped); stripped++)
1719                 ;
1720         for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--)
1721                 ;
1722         *(cp + 1) = '\0';
1723 
1724         p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1,
1725             stripped);
1726 
1727         r = internal_attach_property(pg, p);
1728         if (r != 0) {
1729                 internal_property_free(p);
1730                 free(prop_name);
1731         }
1732 
1733         return (r);
1734 }
1735 
1736 /*
1737  * This function processes all loctext elements in the current XML element
1738  * designated by container.  A property is created for each loctext element
1739  * and added to the property group at pg.  The name of the property is
1740  * derived from the loctext language designation using the format at
1741  * pn_format.  pn_format should be an snprintf format string containing one
1742  * %s which is replaced by the language designation.
1743  *
1744  * The function returns 0 on success and -1 if it is unable to attach the
1745  * newly created property to pg.
1746  */
1747 static int
1748 lxml_get_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container,
1749     const char *pn_format, const char *source)
1750 {
1751         xmlNodePtr cursor;
1752 
1753         /*
1754          * Iterate through one or more loctext elements.  The locale is
1755          * used to generate the property name; the contents are the ustring
1756          * value for the property.
1757          */
1758         for (cursor = container->xmlChildrenNode; cursor != NULL;
1759             cursor = cursor->next) {
1760                 if (lxml_ignorable_block(cursor))
1761                         continue;
1762 
1763                 switch (lxml_xlate_element(cursor->name)) {
1764                 case SC_LOCTEXT:
1765                         if (lxml_get_loctext(service, pg, cursor, pn_format,
1766                             source))
1767                                 return (-1);
1768                         break;
1769                 default:
1770                         uu_die(gettext("illegal element \"%s\" on %s element "
1771                             "for \"%s\"\n"), cursor->name, container->name,
1772                             service->sc_name);
1773                         break;
1774                 }
1775         }
1776 
1777         return (0);
1778 }
1779 
1780 /*
1781  * Obtain the specified cardinality attribute and place it in a property
1782  * named prop_name.  The converted attribute is placed at *value, and the
1783  * newly created property is returned to propp.  NULL is returned to propp
1784  * if the attribute is not provided in the manifest.
1785  *
1786  * 0 is returned upon success, and -1 indicates that the manifest contained
1787  * an invalid cardinality value.
1788  */
1789 static int
1790 lxml_get_cardinality_attribute(entity_t *service, xmlNodePtr cursor,
1791     const char *attr_name, const char *prop_name, uint64_t *value,
1792     property_t **propp)
1793 {
1794         char *c;
1795         property_t *p;
1796         xmlChar *val;
1797         uint64_t count;
1798         char *endptr;
1799 
1800         *propp = NULL;
1801         val = xmlGetProp(cursor, (xmlChar *)attr_name);
1802         if (val == NULL)
1803                 return (0);
1804         if (*val == 0) {
1805                 xmlFree(val);
1806                 return (0);
1807         }
1808 
1809         /*
1810          * Make sure that the string at val doesn't have a leading minus
1811          * sign.  The strtoull() call below does not catch this problem.
1812          */
1813         for (c = (char *)val; *c != 0; c++) {
1814                 if (isspace(*c))
1815                         continue;
1816                 if (isdigit(*c))
1817                         break;
1818                 semerr(gettext("\"%c\" is not a legal character in the %s "
1819                     "attribute of the %s element in %s.\n"), *c,
1820                     attr_name, prop_name, service->sc_name);
1821                 xmlFree(val);
1822                 return (-1);
1823         }
1824         errno = 0;
1825         count = strtoull((char *)val, &endptr, 10);
1826         if (errno != 0 || endptr == (char *)val || *endptr) {
1827                 semerr(gettext("\"%s\" is not a legal number for the %s "
1828                     "attribute of the %s element in %s.\n"), (char *)val,
1829                     attr_name, prop_name, service->sc_name);
1830                 xmlFree(val);
1831                 return (-1);
1832         }
1833 
1834         xmlFree(val);
1835 
1836         /* Value is valid.  Create the property. */
1837         p = internal_property_create(prop_name, SCF_TYPE_COUNT, 1, count);
1838         *value = count;
1839         *propp = p;
1840         return (0);
1841 }
1842 
1843 /*
1844  * The cardinality is specified by two attributes max and min at cursor.
1845  * Both are optional, but if present they must be unsigned integers.
1846  */
1847 static int
1848 lxml_get_tm_cardinality(entity_t *service, pgroup_t *pg, xmlNodePtr cursor)
1849 {
1850         int min_attached = 0;
1851         int compare = 1;
1852         property_t *min_prop;
1853         property_t *max_prop;
1854         uint64_t max;
1855         uint64_t min;
1856         int r;
1857 
1858         r = lxml_get_cardinality_attribute(service, cursor, min_attr,
1859             SCF_PROPERTY_TM_CARDINALITY_MIN, &min, &min_prop);
1860         if (r != 0)
1861                 return (r);
1862         if (min_prop == NULL)
1863                 compare = 0;
1864         r = lxml_get_cardinality_attribute(service, cursor, max_attr,
1865             SCF_PROPERTY_TM_CARDINALITY_MAX, &max, &max_prop);
1866         if (r != 0)
1867                 goto errout;
1868         if ((max_prop != NULL) && (compare == 1)) {
1869                 if (max < min) {
1870                         semerr(gettext("Cardinality max is less than min for "
1871                             "the %s element in %s.\n"), pg->sc_pgroup_name,
1872                             service->sc_fmri);
1873                         goto errout;
1874                 }
1875         }
1876 
1877         /* Attach the properties to the property group. */
1878         if (min_prop) {
1879                 if (internal_attach_property(pg, min_prop) == 0) {
1880                         min_attached = 1;
1881                 } else {
1882                         goto errout;
1883                 }
1884         }
1885         if (max_prop) {
1886                 if (internal_attach_property(pg, max_prop) != 0) {
1887                         if (min_attached)
1888                                 internal_detach_property(pg, min_prop);
1889                         goto errout;
1890                 }
1891         }
1892         return (0);
1893 
1894 errout:
1895         if (min_prop)
1896                 internal_property_free(min_prop);
1897         if (max_prop)
1898                 internal_property_free(max_prop);
1899         return (-1);
1900 }
1901 
1902 /*
1903  * Get the common_name which is present as localized text at common_name in
1904  * the manifest.  The common_name is stored as the value of a property in
1905  * the property group whose name is SCF_PG_TM_COMMON_NAME and type is
1906  * SCF_GROUP_TEMPLATE.  This property group will be created in service if
1907  * it is not already there.
1908  */
1909 static int
1910 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
1911 {
1912         pgroup_t *pg;
1913 
1914         /*
1915          * Create the property group, if absent.
1916          */
1917         pg = internal_pgroup_find_or_create(service, SCF_PG_TM_COMMON_NAME,
1918             SCF_GROUP_TEMPLATE);
1919 
1920         return (lxml_get_all_loctext(service, pg, common_name, LOCALE_ONLY_FMT,
1921             "common_name"));
1922 }
1923 
1924 /*
1925  * Get the description which is present as localized text at description in
1926  * the manifest.  The description is stored as the value of a property in
1927  * the property group whose name is SCF_PG_TM_DESCRIPTION and type is
1928  * SCF_GROUP_TEMPLATE.  This property group will be created in service if
1929  * it is not already there.
1930  */
1931 static int
1932 lxml_get_tm_description(entity_t *service, xmlNodePtr description)
1933 {
1934         pgroup_t *pg;
1935 
1936         /*
1937          * Create the property group, if absent.
1938          */
1939         pg = internal_pgroup_find_or_create(service, SCF_PG_TM_DESCRIPTION,
1940             SCF_GROUP_TEMPLATE);
1941 
1942         return (lxml_get_all_loctext(service, pg, description,
1943             LOCALE_ONLY_FMT, "description"));
1944 }
1945 
1946 static char *
1947 lxml_label_to_groupname(const char *prefix, const char *in)
1948 {
1949         char *out, *cp;
1950         size_t len, piece_len;
1951 
1952         out = uu_zalloc(2 * max_scf_name_len + 1);
1953         if (out == NULL)
1954                 return (NULL);
1955 
1956         (void) strlcpy(out, prefix, 2 * max_scf_name_len + 1);
1957 
1958         len = strlcat(out, in, 2 * max_scf_name_len + 1);
1959         if (len > max_scf_name_len) {
1960                 /* Use the first half and the second half. */
1961                 piece_len = (max_scf_name_len - 2) / 2;
1962 
1963                 (void) strncpy(out + piece_len, "..", 2);
1964 
1965                 (void) strcpy(out + piece_len + 2, out + (len - piece_len));
1966         }
1967 
1968         /*
1969          * Translate non-property characters to '_'.
1970          */
1971         for (cp = out; *cp != '\0'; ++cp) {
1972                 if (!(isalnum(*cp) || *cp == '_' || *cp == '-'))
1973                         *cp = '_';
1974         }
1975 
1976         return (out);
1977 }
1978 
1979 /*
1980  * If *p is NULL, astring_prop_value() first creates a property with the
1981  * name specified in prop_name.  The address of the newly created property
1982  * is placed in *p.
1983  *
1984  * In either case, newly created property or existing property, a new
1985  * SCF_TYPE_ASTRING value will created and attached to the property at *p.
1986  * The value of the newly created property is prop_value.
1987  *
1988  * free_flag is used to indicate whether or not the memory at prop_value
1989  * should be freed when the property is freed by a call to
1990  * internal_property_free().
1991  */
1992 static void
1993 astring_prop_value(property_t **p, const char *prop_name, char *prop_value,
1994     boolean_t free_flag)
1995 {
1996         value_t *v;
1997 
1998         if (*p == NULL) {
1999                 /* Create the property */
2000                 *p = internal_property_new();
2001                 (*p)->sc_property_name = (char *)prop_name;
2002                 (*p)->sc_value_type = SCF_TYPE_ASTRING;
2003         }
2004 
2005         /* Add the property value to the property's list of values. */
2006         v = internal_value_new();
2007         v->sc_type = SCF_TYPE_ASTRING;
2008         if (free_flag == B_TRUE)
2009                 v->sc_free = lxml_free_str;
2010         v->sc_u.sc_string = prop_value;
2011         internal_attach_value(*p, v);
2012 }
2013 
2014 /*
2015  * If p points to a null pointer, create an internal_separators property
2016  * saving the address at p.  For each character at seps create a property
2017  * value and attach it to the property at p.
2018  */
2019 static void
2020 seps_to_prop_values(property_t **p, xmlChar *seps)
2021 {
2022         value_t *v;
2023         char val_str[2];
2024 
2025         if (*p == NULL) {
2026                 *p = internal_property_new();
2027                 (*p)->sc_property_name =
2028                     (char *)SCF_PROPERTY_INTERNAL_SEPARATORS;
2029                 (*p)->sc_value_type = SCF_TYPE_ASTRING;
2030         }
2031 
2032         /* Add the values to the property's list. */
2033         val_str[1] = 0;         /* Terminate the string. */
2034         for (; *seps != 0; seps++) {
2035                 v = internal_value_new();
2036                 v->sc_type = (*p)->sc_value_type;
2037                 v->sc_free = lxml_free_str;
2038                 val_str[0] = *seps;
2039                 v->sc_u.sc_string = safe_strdup(val_str);
2040                 internal_attach_value(*p, v);
2041         }
2042 }
2043 
2044 /*
2045  * Create an internal_separators property and attach it to the property
2046  * group at pg.  The separator characters are provided in the text nodes
2047  * that are the children of seps.  Each separator character is stored as a
2048  * property value in the internal_separators property.
2049  */
2050 static int
2051 lxml_get_tm_internal_seps(entity_t *service, pgroup_t *pg, xmlNodePtr seps)
2052 {
2053         xmlNodePtr cursor;
2054         property_t *prop = NULL;
2055         int r;
2056 
2057         for (cursor = seps->xmlChildrenNode; cursor != NULL;
2058             cursor = cursor->next) {
2059                 if (strcmp("text", (const char *)cursor->name) == 0) {
2060                         seps_to_prop_values(&prop, cursor->content);
2061                 } else if (strcmp("comment", (const char *)cursor->name) != 0) {
2062                         uu_die(gettext("illegal element \"%s\" on %s element "
2063                             "for \"%s\"\n"), cursor->name, seps->name,
2064                             service->sc_name);
2065                 }
2066         }
2067         if (prop == NULL) {
2068                 semerr(gettext("The %s element in %s had an empty list of "
2069                     "separators.\n"), (const char *)seps->name,
2070                     service->sc_name);
2071                 return (-1);
2072         }
2073         r = internal_attach_property(pg, prop);
2074         if (r != 0)
2075                 internal_property_free(prop);
2076         return (r);
2077 }
2078 
2079 static int
2080 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
2081 {
2082         pgroup_t *pg;
2083         char *pgname;
2084         char *name;
2085         xmlChar *title;
2086         xmlChar *section;
2087 
2088         /*
2089          * Fetch title and section attributes, convert to something sanitized,
2090          * and create property group.
2091          */
2092         title = xmlGetProp(manpage, (xmlChar *)title_attr);
2093         if (title == NULL)
2094                 return (-1);
2095         section = xmlGetProp(manpage, (xmlChar *)section_attr);
2096         if (section == NULL) {
2097                 xmlFree(title);
2098                 return (-1);
2099         }
2100 
2101         name = safe_malloc(max_scf_name_len + 1);
2102 
2103         /* Find existing property group with underscore separators */
2104         (void) snprintf(name, max_scf_name_len + 1, "%s_%s", title, section);
2105         pgname = lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, name);
2106         pg = internal_pgroup_find(service, pgname, SCF_GROUP_TEMPLATE);
2107 
2108         uu_free(pgname);
2109         (void) snprintf(name, max_scf_name_len + 1, "%s%s", title, section);
2110         pgname = lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, name);
2111 
2112         if (pg == NULL) {
2113                 pg = internal_pgroup_find_or_create(service, pgname,
2114                     SCF_GROUP_TEMPLATE);
2115         } else {
2116                 /* Rename property group */
2117                 free((char *)pg->sc_pgroup_name);
2118                 pg->sc_pgroup_name = safe_strdup(pgname);
2119         }
2120 
2121         uu_free(pgname);
2122         free(name);
2123         xmlFree(section);
2124         xmlFree(title);
2125 
2126 
2127         /*
2128          * Each attribute is an astring property within the group.
2129          */
2130         if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_TITLE,
2131             SCF_TYPE_ASTRING, manpage, title_attr) != 0 ||
2132             new_str_prop_from_attr(pg, SCF_PROPERTY_TM_SECTION,
2133             SCF_TYPE_ASTRING, manpage, section_attr) != 0 ||
2134             new_str_prop_from_attr(pg, SCF_PROPERTY_TM_MANPATH,
2135             SCF_TYPE_ASTRING, manpage, manpath_attr) != 0)
2136                 return (-1);
2137 
2138         return (0);
2139 }
2140 
2141 static int
2142 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
2143 {
2144         pgroup_t *pg;
2145         char *pgname;
2146         xmlChar *name;
2147 
2148         /*
2149          * Fetch name attribute, convert name to something sanitized, and create
2150          * property group.
2151          */
2152         name = xmlGetProp(doc_link, (xmlChar *)name_attr);
2153         if (name == NULL)
2154                 return (-1);
2155 
2156         pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX,
2157             (const char *)name);
2158 
2159         pg = internal_pgroup_find_or_create(service, pgname,
2160             (char *)SCF_GROUP_TEMPLATE);
2161 
2162         uu_free(pgname);
2163         xmlFree(name);
2164 
2165         /*
2166          * Each attribute is an astring property within the group.
2167          */
2168         if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, SCF_TYPE_ASTRING,
2169             doc_link, name_attr) != 0 ||
2170             new_str_prop_from_attr(pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING,
2171             doc_link, uri_attr) != 0)
2172                 return (-1);
2173 
2174         return (0);
2175 }
2176 
2177 static int
2178 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
2179 {
2180         xmlNodePtr cursor;
2181 
2182         for (cursor = documentation->xmlChildrenNode; cursor != NULL;
2183             cursor = cursor->next) {
2184                 if (lxml_ignorable_block(cursor))
2185                         continue;
2186 
2187                 switch (lxml_xlate_element(cursor->name)) {
2188                 case SC_MANPAGE:
2189                         (void) lxml_get_tm_manpage(service, cursor);
2190                         break;
2191                 case SC_DOC_LINK:
2192                         (void) lxml_get_tm_doclink(service, cursor);
2193                         break;
2194                 default:
2195                         uu_die(gettext("illegal element \"%s\" on template "
2196                             "for service \"%s\"\n"),
2197                             cursor->name, service->sc_name);
2198                 }
2199         }
2200 
2201         return (0);
2202 }
2203 
2204 static int
2205 lxml_get_prop_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
2206 {
2207         if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
2208             SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
2209                 return (-1);
2210         }
2211         if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
2212             SCF_TYPE_ASTRING, cursor, type_attr, "") != 0) {
2213                 return (-1);
2214         }
2215         if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
2216             required_attr) != 0)
2217                 return (-1);
2218         return (0);
2219 }
2220 
2221 static int
2222 lxml_get_tm_include_values(entity_t *service, pgroup_t *pg,
2223     xmlNodePtr include_values, const char *prop_name)
2224 {
2225         boolean_t attach_to_pg = B_FALSE;
2226         property_t *p;
2227         int r = 0;
2228         char *type;
2229 
2230         /* Get the type attribute of the include_values element. */
2231         type = (char *)xmlGetProp(include_values, (const xmlChar *)type_attr);
2232         if ((type == NULL) || (*type == 0)) {
2233                 uu_die(gettext("%s element requires a %s attribute in the %s "
2234                     "service.\n"), include_values->name, type_attr,
2235                     service->sc_name);
2236         }
2237 
2238         /* Add the type to the values of the prop_name property. */
2239         p = internal_property_find(pg, prop_name);
2240         if (p == NULL)
2241                 attach_to_pg = B_TRUE;
2242         astring_prop_value(&p, prop_name, type, B_FALSE);
2243         if (attach_to_pg == B_TRUE) {
2244                 r = internal_attach_property(pg, p);
2245                 if (r != 0)
2246                         internal_property_free(p);
2247         }
2248         return (r);
2249 }
2250 
2251 #define RC_MIN          0
2252 #define RC_MAX          1
2253 #define RC_COUNT        2
2254 
2255 /*
2256  * Verify that the strings at min and max are valid numeric strings.  Also
2257  * verify that max is numerically >= min.
2258  *
2259  * 0 is returned if the range is valid, and -1 is returned if it is not.
2260  */
2261 static int
2262 verify_range(entity_t *service, xmlNodePtr range, char *min, char *max)
2263 {
2264         char *c;
2265         int i;
2266         int is_signed = 0;
2267         int inverted = 0;
2268         const char *limit[RC_COUNT];
2269         char *strings[RC_COUNT];
2270         uint64_t urange[RC_COUNT];      /* unsigned range. */
2271         int64_t srange[RC_COUNT];       /* signed range. */
2272 
2273         strings[RC_MIN] = min;
2274         strings[RC_MAX] = max;
2275         limit[RC_MIN] = min_attr;
2276         limit[RC_MAX] = max_attr;
2277 
2278         /* See if the range is signed. */
2279         for (i = 0; (i < RC_COUNT) && (is_signed == 0); i++) {
2280                 c = strings[i];
2281                 while (isspace(*c)) {
2282                         c++;
2283                 }
2284                 if (*c == '-')
2285                         is_signed = 1;
2286         }
2287 
2288         /* Attempt to convert the strings. */
2289         for (i = 0; i < RC_COUNT; i++) {
2290                 errno = 0;
2291                 if (is_signed) {
2292                         srange[i] = strtoll(strings[i], &c, 0);
2293                 } else {
2294                         urange[i] = strtoull(strings[i], &c, 0);
2295                 }
2296                 if ((errno != 0) || (c == strings[i]) || (*c != 0)) {
2297                         /* Conversion failed. */
2298                         uu_die(gettext("Unable to convert %s for the %s "
2299                             "element in service %s.\n"), limit[i],
2300                             (char *)range->name, service->sc_name);
2301                 }
2302         }
2303 
2304         /* Make sure that min is <= max */
2305         if (is_signed) {
2306                 if (srange[RC_MAX] < srange[RC_MIN])
2307                         inverted = 1;
2308         } else {
2309                 if (urange[RC_MAX] < urange[RC_MIN])
2310                         inverted = 1;
2311         }
2312         if (inverted != 0) {
2313                 semerr(gettext("Maximum less than minimum for the %s element "
2314                     "in service %s.\n"), (char *)range->name,
2315                     service->sc_name);
2316                 return (-1);
2317         }
2318 
2319         return (0);
2320 }
2321 
2322 /*
2323  * This, function creates a property named prop_name.  The range element
2324  * should have two attributes -- min and max.  The property value then
2325  * becomes the concatenation of their value separated by a comma.  The
2326  * property is then attached to the property group at pg.
2327  *
2328  * If pg already contains a property with a name of prop_name, it is only
2329  * necessary to create a new value and attach it to the existing property.
2330  */
2331 static int
2332 lxml_get_tm_range(entity_t *service, pgroup_t *pg, xmlNodePtr range,
2333     const char *prop_name)
2334 {
2335         boolean_t attach_to_pg = B_FALSE;
2336         char *max;
2337         char *min;
2338         property_t *p;
2339         char *prop_value;
2340         int r = 0;
2341 
2342         /* Get max and min from the XML description. */
2343         max = (char *)xmlGetProp(range, (xmlChar *)max_attr);
2344         if ((max == NULL) || (*max == 0)) {
2345                 uu_die(gettext("%s element is missing the %s attribute in "
2346                     "service %s.\n"), (char *)range->name, max_attr,
2347                     service->sc_name);
2348         }
2349         min = (char *)xmlGetProp(range, (xmlChar *)min_attr);
2350         if ((min == NULL) || (*min == 0)) {
2351                 uu_die(gettext("%s element is missing the %s attribute in "
2352                     "service %s.\n"), (char *)range->name, min_attr,
2353                     service->sc_name);
2354         }
2355         if (verify_range(service, range, min, max) != 0) {
2356                 xmlFree(min);
2357                 xmlFree(max);
2358                 return (-1);
2359         }
2360 
2361         /* Property value is concatenation of min and max. */
2362         prop_value = safe_malloc(max_scf_value_len + 1);
2363         if (snprintf(prop_value, max_scf_value_len + 1, "%s,%s", min, max) >=
2364             max_scf_value_len + 1) {
2365                 uu_die(gettext("min and max are too long for the %s element "
2366                     "of %s.\n"), (char *)range->name, service->sc_name);
2367         }
2368         xmlFree(min);
2369         xmlFree(max);
2370 
2371         /*
2372          * If necessary create the property and attach it to the property
2373          * group.
2374          */
2375         p = internal_property_find(pg, prop_name);
2376         if (p == NULL)
2377                 attach_to_pg = B_TRUE;
2378         astring_prop_value(&p, prop_name, prop_value, B_TRUE);
2379         if (attach_to_pg == B_TRUE) {
2380                 r = internal_attach_property(pg, p);
2381                 if (r != 0) {
2382                         internal_property_free(p);
2383                 }
2384         }
2385         return (r);
2386 }
2387 
2388 /*
2389  * Determine how many plain characters are represented by count Base32
2390  * encoded characters.  5 plain text characters are converted to 8 Base32
2391  * characters.
2392  */
2393 static size_t
2394 encoded_count_to_plain(size_t count)
2395 {
2396         return (5 * ((count + 7) / 8));
2397 }
2398 
2399 /*
2400  * The value element contains 0 or 1 common_name element followed by 0 or 1
2401  * description element.  It also has a required attribute called "name".
2402  * The common_name and description are stored as property values in pg.
2403  * The property names are:
2404  *      value_<name>_common_name_<lang>
2405  *      value_<name>_description_<lang>
2406  *
2407  * The <name> portion of the preceeding proper names requires more
2408  * explanation.  Ideally it would just the name attribute of this value
2409  * element.  Unfortunately, the name attribute can contain characters that
2410  * are not legal in a property name.  Thus, we base 32 encode the name
2411  * attribute and use that for <name>.
2412  *
2413  * There are cases where the caller needs to know the name, so it is
2414  * returned through the name_value pointer if it is not NULL.
2415  *
2416  * Parameters:
2417  *      service -       Information about the service that is being
2418  *                      processed.  This function only uses this parameter
2419  *                      for producing error messages.
2420  *
2421  *      pg -            The property group to receive the newly created
2422  *                      properties.
2423  *
2424  *      value -         Pointer to the value element in the XML tree.
2425  *
2426  *      name_value -    Address to receive the value of the name attribute.
2427  *                      The caller must free the memory.
2428  */
2429 static int
2430 lxml_get_tm_value_element(entity_t *service, pgroup_t *pg, xmlNodePtr value,
2431     char **name_value)
2432 {
2433         char *common_name_fmt;
2434         xmlNodePtr cursor;
2435         char *description_fmt;
2436         char *encoded_value = NULL;
2437         size_t extra;
2438         char *value_name;
2439         int r = 0;
2440 
2441         common_name_fmt = safe_malloc(max_scf_name_len + 1);
2442         description_fmt = safe_malloc(max_scf_name_len + 1);
2443 
2444         /*
2445          * Get the value of our name attribute, so that we can use it to
2446          * construct property names.
2447          */
2448         value_name = (char *)xmlGetProp(value, (xmlChar *)name_attr);
2449         /* The value name must be present, but it can be empty. */
2450         if (value_name == NULL) {
2451                 uu_die(gettext("%s element requires a %s attribute in the %s "
2452                     "service.\n"), (char *)value->name, name_attr,
2453                     service->sc_name);
2454         }
2455 
2456         /*
2457          * The value_name may contain characters that are not valid in in a
2458          * property name.  So we will encode value_name and then use the
2459          * encoded value in the property name.
2460          */
2461         encoded_value = safe_malloc(max_scf_name_len + 1);
2462         if (scf_encode32(value_name, strlen(value_name), encoded_value,
2463             max_scf_name_len + 1, &extra, SCF_ENCODE32_PAD) != 0) {
2464                 extra = encoded_count_to_plain(extra - max_scf_name_len);
2465                 uu_die(gettext("Constructed property name is %u characters "
2466                     "too long for value \"%s\" in the %s service.\n"),
2467                     extra, value_name, service->sc_name);
2468         }
2469         if ((extra = snprintf(common_name_fmt, max_scf_name_len + 1,
2470             VALUE_COMMON_NAME_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
2471             encoded_value)) >= max_scf_name_len + 1) {
2472                 extra = encoded_count_to_plain(extra - max_scf_name_len);
2473                 uu_die(gettext("Name attribute is "
2474                     "%u characters too long for %s in service %s\n"),
2475                     extra, (char *)value->name, service->sc_name);
2476         }
2477         if ((extra = snprintf(description_fmt, max_scf_name_len + 1,
2478             VALUE_DESCRIPTION_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
2479             encoded_value)) >= max_scf_name_len + 1) {
2480                 extra = encoded_count_to_plain(extra - max_scf_name_len);
2481                 uu_die(gettext("Name attribute is "
2482                     "%u characters too long for %s in service %s\n"),
2483                     extra, (char *)value->name, service->sc_name);
2484         }
2485 
2486         for (cursor = value->xmlChildrenNode;
2487             cursor != NULL;
2488             cursor = cursor->next) {
2489                 if (lxml_ignorable_block(cursor))
2490                         continue;
2491                 switch (lxml_xlate_element(cursor->name)) {
2492                 case SC_COMMON_NAME:
2493                         r = lxml_get_all_loctext(service, pg, cursor,
2494                             common_name_fmt, (const char *)cursor->name);
2495                         break;
2496                 case SC_DESCRIPTION:
2497                         r = lxml_get_all_loctext(service, pg, cursor,
2498                             description_fmt, (const char *)cursor->name);
2499                         break;
2500                 default:
2501                         uu_die(gettext("\"%s\" is an illegal element in %s "
2502                             "of service %s\n"), (char *)cursor->name,
2503                             (char *)value->name, service->sc_name);
2504                 }
2505                 if (r != 0)
2506                         break;
2507         }
2508 
2509         free(description_fmt);
2510         free(common_name_fmt);
2511         if (r == 0) {
2512                 *name_value = safe_strdup(value_name);
2513         }
2514         xmlFree(value_name);
2515         free(encoded_value);
2516         return (r);
2517 }
2518 
2519 static int
2520 lxml_get_tm_choices(entity_t *service, pgroup_t *pg, xmlNodePtr choices)
2521 {
2522         xmlNodePtr cursor;
2523         char *name_value;
2524         property_t *name_prop = NULL;
2525         int r = 0;
2526 
2527         for (cursor = choices->xmlChildrenNode;
2528             (cursor != NULL) && (r == 0);
2529             cursor = cursor->next) {
2530                 if (lxml_ignorable_block(cursor))
2531                         continue;
2532                 switch (lxml_xlate_element(cursor->name)) {
2533                 case SC_INCLUDE_VALUES:
2534                         (void) lxml_get_tm_include_values(service, pg, cursor,
2535                             SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES);
2536                         break;
2537                 case SC_RANGE:
2538                         r = lxml_get_tm_range(service, pg, cursor,
2539                             SCF_PROPERTY_TM_CHOICES_RANGE);
2540                         if (r != 0)
2541                                 goto out;
2542                         break;
2543                 case SC_VALUE:
2544                         r = lxml_get_tm_value_element(service, pg, cursor,
2545                             &name_value);
2546                         if (r == 0) {
2547                                 /*
2548                                  * There is no need to free the memory
2549                                  * associated with name_value, because the
2550                                  * property value will end up pointing to
2551                                  * the memory.
2552                                  */
2553                                 astring_prop_value(&name_prop,
2554                                     SCF_PROPERTY_TM_CHOICES_NAME, name_value,
2555                                     B_TRUE);
2556                         } else {
2557                                 goto out;
2558                         }
2559                         break;
2560                 default:
2561                         uu_die(gettext("%s is an invalid element of "
2562                             "choices for service %s.\n"),  cursor->name,
2563                             service->sc_name);
2564                 }
2565         }
2566 
2567 out:
2568         /* Attach the name property if we created one. */
2569         if ((r == 0) && (name_prop != NULL)) {
2570                 r = internal_attach_property(pg, name_prop);
2571         }
2572         if ((r != 0) && (name_prop != NULL)) {
2573                 internal_property_free(name_prop);
2574         }
2575 
2576         return (r);
2577 }
2578 
2579 static int
2580 lxml_get_tm_constraints(entity_t *service, pgroup_t *pg, xmlNodePtr constraints)
2581 {
2582         xmlNodePtr cursor;
2583         char *name_value;
2584         property_t *name_prop = NULL;
2585         int r = 0;
2586 
2587         for (cursor = constraints->xmlChildrenNode;
2588             (cursor != NULL) && (r == 0);
2589             cursor = cursor->next) {
2590                 if (lxml_ignorable_block(cursor))
2591                         continue;
2592                 switch (lxml_xlate_element(cursor->name)) {
2593                 case SC_RANGE:
2594                         r = lxml_get_tm_range(service, pg, cursor,
2595                             SCF_PROPERTY_TM_CONSTRAINT_RANGE);
2596                         if (r != 0)
2597                                 goto out;
2598                         break;
2599                 case SC_VALUE:
2600                         r = lxml_get_tm_value_element(service, pg, cursor,
2601                             &name_value);
2602                         if (r == 0) {
2603                                 /*
2604                                  * There is no need to free the memory
2605                                  * associated with name_value, because the
2606                                  * property value will end up pointing to
2607                                  * the memory.
2608                                  */
2609                                 astring_prop_value(&name_prop,
2610                                     SCF_PROPERTY_TM_CONSTRAINT_NAME, name_value,
2611                                     B_TRUE);
2612                         } else {
2613                                 goto out;
2614                         }
2615                         break;
2616                 default:
2617                         uu_die(gettext("%s is an invalid element of "
2618                             "constraints for service %s.\n"),  cursor->name,
2619                             service->sc_name);
2620                 }
2621         }
2622 
2623 out:
2624         /* Attach the name property if we created one. */
2625         if ((r == 0) && (name_prop != NULL)) {
2626                 r = internal_attach_property(pg, name_prop);
2627         }
2628         if ((r != 0) && (name_prop != NULL)) {
2629                 internal_property_free(name_prop);
2630         }
2631 
2632         return (r);
2633 }
2634 
2635 /*
2636  * The values element contains one or more value elements.
2637  */
2638 static int
2639 lxml_get_tm_values(entity_t *service, pgroup_t *pg, xmlNodePtr values)
2640 {
2641         xmlNodePtr cursor;
2642         char *name_value;
2643         property_t *name_prop = NULL;
2644         int r = 0;
2645 
2646         for (cursor = values->xmlChildrenNode;
2647             (cursor != NULL) && (r == 0);
2648             cursor = cursor->next) {
2649                 if (lxml_ignorable_block(cursor))
2650                         continue;
2651                 if (lxml_xlate_element(cursor->name) != SC_VALUE) {
2652                         uu_die(gettext("\"%s\" is an illegal element in the "
2653                             "%s element of %s\n"), (char *)cursor->name,
2654                             (char *)values->name, service->sc_name);
2655                 }
2656                 r = lxml_get_tm_value_element(service, pg, cursor, &name_value);
2657                 if (r == 0) {
2658                         /*
2659                          * There is no need to free the memory
2660                          * associated with name_value, because the
2661                          * property value will end up pointing to
2662                          * the memory.
2663                          */
2664                         astring_prop_value(&name_prop,
2665                             SCF_PROPERTY_TM_VALUES_NAME, name_value,
2666                             B_TRUE);
2667                 }
2668         }
2669 
2670         /* Attach the name property if we created one. */
2671         if ((r == 0) && (name_prop != NULL)) {
2672                 r = internal_attach_property(pg, name_prop);
2673         }
2674         if ((r != 0) && (name_prop != NULL)) {
2675                 internal_property_free(name_prop);
2676         }
2677 
2678         return (r);
2679 }
2680 
2681 /*
2682  * This function processes a prop_pattern element within a pg_pattern XML
2683  * element.  First it creates a property group to hold the prop_pattern
2684  * information.  The name of this property group is the concatenation of:
2685  *      - SCF_PG_TM_PROP_PATTERN_PREFIX
2686  *      - The unique part of the property group name of the enclosing
2687  *        pg_pattern.  The property group name of the enclosing pg_pattern
2688  *        is passed to us in pgpat_name.  The unique part, is the part
2689  *        following SCF_PG_TM_PG_PATTERN_PREFIX.
2690  *      - The name of this prop_pattern element.
2691  *
2692  * After creating the property group, the prop_pattern attributes are saved
2693  * as properties in the PG.  Finally, the prop_pattern elements are
2694  * processed and added to the PG.
2695  */
2696 static int
2697 lxml_get_tm_prop_pattern(entity_t *service, xmlNodePtr prop_pattern,
2698     const char *pgpat_name)
2699 {
2700         xmlNodePtr cursor;
2701         int extra;
2702         pgroup_t *pg;
2703         property_t *p;
2704         char *pg_name;
2705         size_t prefix_len;
2706         xmlChar *prop_pattern_name;
2707         int r;
2708         const char *unique;
2709         value_t *v;
2710 
2711         /* Find the unique part of the pg_pattern property group name. */
2712         prefix_len = strlen(SCF_PG_TM_PG_PAT_BASE);
2713         assert(strncmp(pgpat_name, SCF_PG_TM_PG_PAT_BASE, prefix_len) == 0);
2714         unique = pgpat_name + prefix_len;
2715 
2716         /*
2717          * We need to get the value of the name attribute first.  The
2718          * prop_pattern name as well as the name of the enclosing
2719          * pg_pattern both constitute part of the name of the property
2720          * group that we will create.
2721          */
2722         prop_pattern_name = xmlGetProp(prop_pattern, (xmlChar *)name_attr);
2723         if ((prop_pattern_name == NULL) || (*prop_pattern_name == 0)) {
2724                 semerr(gettext("prop_pattern name is missing for %s\n"),
2725                     service->sc_name);
2726                 return (-1);
2727         }
2728         if (uu_check_name((const char *)prop_pattern_name,
2729             UU_NAME_DOMAIN) != 0) {
2730                 semerr(gettext("prop_pattern name, \"%s\", for %s is not "
2731                     "valid.\n"), prop_pattern_name, service->sc_name);
2732                 xmlFree(prop_pattern_name);
2733                 return (-1);
2734         }
2735         pg_name = safe_malloc(max_scf_name_len + 1);
2736         if ((extra = snprintf(pg_name, max_scf_name_len + 1, "%s%s_%s",
2737             SCF_PG_TM_PROP_PATTERN_PREFIX, unique,
2738             (char *)prop_pattern_name)) >= max_scf_name_len + 1) {
2739                 uu_die(gettext("prop_pattern name, \"%s\", for %s is %d "
2740                     "characters too long\n"), (char *)prop_pattern_name,
2741                     service->sc_name, extra - max_scf_name_len);
2742         }
2743 
2744         /*
2745          * Create the property group, the property referencing the pg_pattern
2746          * name, and add the prop_pattern attributes to the property group.
2747          */
2748         pg = internal_pgroup_create_strict(service, pg_name,
2749             SCF_GROUP_TEMPLATE_PROP_PATTERN);
2750         if (pg == NULL) {
2751                 uu_die(gettext("Property group for prop_pattern, \"%s\", "
2752                     "already exists in %s\n"), prop_pattern_name,
2753                     service->sc_name);
2754         }
2755 
2756         p = internal_property_create(SCF_PROPERTY_TM_PG_PATTERN,
2757             SCF_TYPE_ASTRING, 1, safe_strdup(pgpat_name));
2758         /*
2759          * Unfortunately, internal_property_create() does not set the free
2760          * function for the value, so we'll set it now.
2761          */
2762         v = uu_list_first(p->sc_property_values);
2763         v->sc_free = lxml_free_str;
2764         if (internal_attach_property(pg, p) != 0)
2765                 internal_property_free(p);
2766 
2767 
2768         r = lxml_get_prop_pattern_attributes(pg, prop_pattern);
2769         if (r != 0)
2770                 goto out;
2771 
2772         /*
2773          * Now process the elements of prop_pattern
2774          */
2775         for (cursor = prop_pattern->xmlChildrenNode;
2776             cursor != NULL;
2777             cursor = cursor->next) {
2778                 if (lxml_ignorable_block(cursor))
2779                         continue;
2780 
2781                 switch (lxml_xlate_element(cursor->name)) {
2782                 case SC_CARDINALITY:
2783                         r = lxml_get_tm_cardinality(service, pg, cursor);
2784                         if (r != 0)
2785                                 goto out;
2786                         break;
2787                 case SC_CHOICES:
2788                         r = lxml_get_tm_choices(service, pg, cursor);
2789                         if (r != 0)
2790                                 goto out;
2791                         break;
2792                 case SC_COMMON_NAME:
2793                         (void) lxml_get_all_loctext(service, pg, cursor,
2794                             COMMON_NAME_FMT, (const char *)cursor->name);
2795                         break;
2796                 case SC_CONSTRAINTS:
2797                         r = lxml_get_tm_constraints(service, pg, cursor);
2798                         if (r != 0)
2799                                 goto out;
2800                         break;
2801                 case SC_DESCRIPTION:
2802                         (void) lxml_get_all_loctext(service, pg, cursor,
2803                             DESCRIPTION_FMT, (const char *)cursor->name);
2804                         break;
2805                 case SC_INTERNAL_SEPARATORS:
2806                         r = lxml_get_tm_internal_seps(service, pg, cursor);
2807                         if (r != 0)
2808                                 goto out;
2809                         break;
2810                 case SC_UNITS:
2811                         (void) lxml_get_all_loctext(service, pg, cursor,
2812                             UNITS_FMT, "units");
2813                         break;
2814                 case SC_VALUES:
2815                         (void) lxml_get_tm_values(service, pg, cursor);
2816                         break;
2817                 case SC_VISIBILITY:
2818                         /*
2819                          * The visibility element is empty, so we only need
2820                          * to proccess the value attribute.
2821                          */
2822                         (void) new_str_prop_from_attr(pg,
2823                             SCF_PROPERTY_TM_VISIBILITY, SCF_TYPE_ASTRING,
2824                             cursor, value_attr);
2825                         break;
2826                 default:
2827                         uu_die(gettext("illegal element \"%s\" in prop_pattern "
2828                             "for service \"%s\"\n"), cursor->name,
2829                             service->sc_name);
2830                 }
2831         }
2832 
2833 out:
2834         xmlFree(prop_pattern_name);
2835         free(pg_name);
2836         return (r);
2837 }
2838 
2839 /*
2840  * Get the pg_pattern attributes and save them as properties in the
2841  * property group at pg.  The pg_pattern element accepts four attributes --
2842  * name, type, required and target.
2843  */
2844 static int
2845 lxml_get_pg_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
2846 {
2847         if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
2848             SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
2849                 return (-1);
2850         }
2851         if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
2852             SCF_TYPE_ASTRING, cursor, type_attr, NULL) != 0) {
2853                 return (-1);
2854         }
2855         if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TARGET,
2856             SCF_TYPE_ASTRING, cursor, target_attr, NULL) != 0) {
2857                 return (-1);
2858         }
2859         if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
2860             required_attr) != 0)
2861                 return (-1);
2862         return (0);
2863 }
2864 
2865 /*
2866  * There are several restrictions on the pg_pattern attributes that cannot
2867  * be specifed in the service bundle DTD.  This function verifies that
2868  * those restrictions have been satisfied.  The restrictions are:
2869  *
2870  *      - The target attribute may have a value of "instance" only when the
2871  *        template block is in a service declaration.
2872  *
2873  *      - The target attribute may have a value of "delegate" only when the
2874  *        template block applies to a restarter.
2875  *
2876  *      - The target attribute may have a value of "all" only when the
2877  *        template block applies to the master restarter.
2878  *
2879  * The function returns 0 on success and -1 on failure.
2880  */
2881 static int
2882 verify_pg_pattern_attributes(entity_t *s, pgroup_t *pg)
2883 {
2884         int is_restarter;
2885         property_t *target;
2886         value_t *v;
2887 
2888         /* Find the value of the target property. */
2889         target = internal_property_find(pg, SCF_PROPERTY_TM_TARGET);
2890         if (target == NULL) {
2891                 uu_die(gettext("pg_pattern is missing the %s attribute "
2892                     "in %s\n"), target_attr, s->sc_name);
2893                 return (-1);
2894         }
2895         v = uu_list_first(target->sc_property_values);
2896         assert(v != NULL);
2897         assert(v->sc_type == SCF_TYPE_ASTRING);
2898 
2899         /*
2900          * If target has a value of instance, the template must be in a
2901          * service object.
2902          */
2903         if (strcmp(v->sc_u.sc_string, "instance") == 0) {
2904                 if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
2905                         uu_warn(gettext("pg_pattern %s attribute may only "
2906                             "have a value of \"instance\" when it is in a "
2907                             "service declaration.\n"), target_attr);
2908                         return (-1);
2909                 }
2910         }
2911 
2912         /*
2913          * If target has a value of "delegate", the template must be in a
2914          * restarter.
2915          */
2916         if (strcmp(v->sc_u.sc_string, "delegate") == 0) {
2917                 is_restarter = 0;
2918                 if ((s->sc_etype == SVCCFG_SERVICE_OBJECT) &&
2919                     (s->sc_u.sc_service.sc_service_type == SVCCFG_RESTARTER)) {
2920                         is_restarter = 1;
2921                 }
2922                 if ((s->sc_etype == SVCCFG_INSTANCE_OBJECT) &&
2923                     (s->sc_parent->sc_u.sc_service.sc_service_type ==
2924                     SVCCFG_RESTARTER)) {
2925                         is_restarter = 1;
2926                 }
2927                 if (is_restarter == 0) {
2928                         uu_warn(gettext("pg_pattern %s attribute has a "
2929                             "value of \"delegate\" but is not in a "
2930                             "restarter service\n"), target_attr);
2931                         return (-1);
2932                 }
2933         }
2934 
2935         /*
2936          * If target has a value of "all", the template must be in the
2937          * global (SCF_SERVICE_GLOBAL) service.
2938          */
2939         if (strcmp(v->sc_u.sc_string, all_value) == 0) {
2940                 if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
2941                         uu_warn(gettext("pg_pattern %s attribute has a "
2942                             "value of \"%s\" but is not in a "
2943                             "service entity.\n"), target_attr, all_value);
2944                         return (-1);
2945                 }
2946                 if (strcmp(s->sc_fmri, SCF_SERVICE_GLOBAL) != 0) {
2947                         uu_warn(gettext("pg_pattern %s attribute has a "
2948                             "value of \"%s\" but is in the \"%s\" service.  "
2949                             "pg_patterns with target \"%s\" are only allowed "
2950                             "in the global service.\n"),
2951                             target_attr, all_value, s->sc_fmri, all_value);
2952                         return (-1);
2953                 }
2954         }
2955 
2956         return (0);
2957 }
2958 
2959 static int
2960 lxml_get_tm_pg_pattern(entity_t *service, xmlNodePtr pg_pattern)
2961 {
2962         xmlNodePtr cursor;
2963         int out_len;
2964         xmlChar *name;
2965         pgroup_t *pg = NULL;
2966         char *pg_name;
2967         int r = -1;
2968         xmlChar *type;
2969 
2970         pg_name = safe_malloc(max_scf_name_len + 1);
2971 
2972         /*
2973          * Get the name and type attributes.  Their presence or absence
2974          * determines whcih prefix we will use for the property group name.
2975          * There are four cases -- neither attribute is present, both are
2976          * present, only name is present or only type is present.
2977          */
2978         name = xmlGetProp(pg_pattern, (xmlChar *)name_attr);
2979         type = xmlGetProp(pg_pattern, (xmlChar *)type_attr);
2980         if ((name == NULL) || (*name == 0)) {
2981                 if ((type == NULL) || (*type == 0)) {
2982                         /* PG name contains only the prefix in this case */
2983                         if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
2984                             max_scf_name_len + 1) >= max_scf_name_len + 1) {
2985                                 uu_die(gettext("Unable to create pg_pattern "
2986                                     "property for %s\n"), service->sc_name);
2987                         }
2988                 } else {
2989                         /*
2990                          * If we have a type and no name, the type becomes
2991                          * part of the pg_pattern property group name.
2992                          */
2993                         if ((out_len = snprintf(pg_name, max_scf_name_len + 1,
2994                             "%s%s", SCF_PG_TM_PG_PATTERN_T_PREFIX, type)) >=
2995                             max_scf_name_len + 1) {
2996                                 uu_die(gettext("pg_pattern type is for %s is "
2997                                     "%d bytes too long\n"), service->sc_name,
2998                                     out_len - max_scf_name_len);
2999                         }
3000                 }
3001         } else {
3002                 const char *prefix;
3003 
3004                 /* Make sure that the name is valid. */
3005                 if (uu_check_name((const char *)name, UU_NAME_DOMAIN) != 0) {
3006                         semerr(gettext("pg_pattern name attribute, \"%s\", "
3007                             "for %s is invalid\n"), name, service->sc_name);
3008                         goto out;
3009                 }
3010 
3011                 /*
3012                  * As long as the pg_pattern has a name, it becomes part of
3013                  * the name of the pg_pattern property group name.  We
3014                  * merely need to pick the appropriate prefix.
3015                  */
3016                 if ((type == NULL) || (*type == 0)) {
3017                         prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX;
3018                 } else {
3019                         prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX;
3020                 }
3021                 if ((out_len = snprintf(pg_name, max_scf_name_len + 1, "%s%s",
3022                     prefix, name)) >= max_scf_name_len + 1) {
3023                         uu_die(gettext("pg_pattern property group name "
3024                             "for %s is %d bytes too long\n"), service->sc_name,
3025                             out_len - max_scf_name_len);
3026                 }
3027         }
3028 
3029         /*
3030          * Create the property group for holding this pg_pattern
3031          * information, and capture the pg_pattern attributes.
3032          */
3033         pg = internal_pgroup_create_strict(service, pg_name,
3034             SCF_GROUP_TEMPLATE_PG_PATTERN);
3035         if (pg == NULL) {
3036                 if ((name == NULL) || (*name == 0)) {
3037                         if ((type == NULL) ||(*type == 0)) {
3038                                 semerr(gettext("pg_pattern with empty name and "
3039                                     "type is not unique in %s\n"),
3040                                     service->sc_name);
3041                         } else {
3042                                 semerr(gettext("pg_pattern with empty name and "
3043                                     "type \"%s\" is not unique in %s\n"),
3044                                     type, service->sc_name);
3045                         }
3046                 } else {
3047                         if ((type == NULL) || (*type == 0)) {
3048                                 semerr(gettext("pg_pattern with name \"%s\" "
3049                                     "and empty type is not unique in %s\n"),
3050                                     name, service->sc_name);
3051                         } else {
3052                                 semerr(gettext("pg_pattern with name \"%s\" "
3053                                     "and type \"%s\" is not unique in %s\n"),
3054                                     name, type, service->sc_name);
3055                         }
3056                 }
3057                 goto out;
3058         }
3059 
3060         /*
3061          * Get the pg_pattern attributes from the manifest and verify
3062          * that they satisfy our restrictions.
3063          */
3064         r = lxml_get_pg_pattern_attributes(pg, pg_pattern);
3065         if (r != 0)
3066                 goto out;
3067         if (verify_pg_pattern_attributes(service, pg) != 0) {
3068                 semerr(gettext("Invalid pg_pattern attributes in %s\n"),
3069                     service->sc_name);
3070                 r = -1;
3071                 goto out;
3072         }
3073 
3074         /*
3075          * Now process all of the elements of pg_pattern.
3076          */
3077         for (cursor = pg_pattern->xmlChildrenNode;
3078             cursor != NULL;
3079             cursor = cursor->next) {
3080                 if (lxml_ignorable_block(cursor))
3081                         continue;
3082 
3083                 switch (lxml_xlate_element(cursor->name)) {
3084                 case SC_COMMON_NAME:
3085                         (void) lxml_get_all_loctext(service, pg, cursor,
3086                             COMMON_NAME_FMT, (const char *)cursor->name);
3087                         break;
3088                 case SC_DESCRIPTION:
3089                         (void) lxml_get_all_loctext(service, pg, cursor,
3090                             DESCRIPTION_FMT, (const char *)cursor->name);
3091                         break;
3092                 case SC_PROP_PATTERN:
3093                         r = lxml_get_tm_prop_pattern(service, cursor,
3094                             pg_name);
3095                         if (r != 0)
3096                                 goto out;
3097                         break;
3098                 default:
3099                         uu_die(gettext("illegal element \"%s\" in pg_pattern "
3100                             "for service \"%s\"\n"), cursor->name,
3101                             service->sc_name);
3102                 }
3103         }
3104 
3105 out:
3106         if ((r != 0) && (pg != NULL)) {
3107                 internal_detach_pgroup(service, pg);
3108                 internal_pgroup_free(pg);
3109         }
3110         free(pg_name);
3111         xmlFree(name);
3112         xmlFree(type);
3113 
3114         return (r);
3115 }
3116 
3117 static int
3118 lxml_get_template(entity_t *service, xmlNodePtr templ)
3119 {
3120         xmlNodePtr cursor;
3121 
3122         for (cursor = templ->xmlChildrenNode; cursor != NULL;
3123             cursor = cursor->next) {
3124                 if (lxml_ignorable_block(cursor))
3125                         continue;
3126 
3127                 switch (lxml_xlate_element(cursor->name)) {
3128                 case SC_COMMON_NAME:
3129                         (void) lxml_get_tm_common_name(service, cursor);
3130                         break;
3131                 case SC_DESCRIPTION:
3132                         (void) lxml_get_tm_description(service, cursor);
3133                         break;
3134                 case SC_DOCUMENTATION:
3135                         (void) lxml_get_tm_documentation(service, cursor);
3136                         break;
3137                 case SC_PG_PATTERN:
3138                         if (lxml_get_tm_pg_pattern(service, cursor) != 0)
3139                                 return (-1);
3140                         break;
3141                 default:
3142                         uu_die(gettext("illegal element \"%s\" on template "
3143                             "for service \"%s\"\n"),
3144                             cursor->name, service->sc_name);
3145                 }
3146         }
3147 
3148         return (0);
3149 }
3150 
3151 static int
3152 lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
3153 {
3154         entity_t *i;
3155         xmlChar *enabled;
3156         pgroup_t *pg;
3157         property_t *p;
3158         char *package;
3159         uint64_t enabled_val = 0;
3160 
3161         i = internal_instance_new("default");
3162 
3163         if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) {
3164                 enabled_val = (strcmp(true, (const char *)enabled) == 0) ?
3165                     1 : 0;
3166                 xmlFree(enabled);
3167         }
3168 
3169         /*
3170          * New general property group with enabled boolean property set.
3171          */
3172 
3173         i->sc_op = service->sc_op;
3174         pg = internal_pgroup_new();
3175         (void) internal_attach_pgroup(i, pg);
3176 
3177         pg->sc_pgroup_name = (char *)scf_pg_general;
3178         pg->sc_pgroup_type = (char *)scf_group_framework;
3179         pg->sc_pgroup_flags = 0;
3180 
3181         p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
3182             enabled_val);
3183 
3184         (void) internal_attach_property(pg, p);
3185 
3186         /*
3187          * Add general/package property if PKGINST is set.
3188          */
3189         if ((package = getenv("PKGINST")) != NULL) {
3190                 p = internal_property_create(SCF_PROPERTY_PACKAGE,
3191                     SCF_TYPE_ASTRING, 1, package);
3192 
3193                 (void) internal_attach_property(pg, p);
3194         }
3195 
3196         return (internal_attach_entity(service, i));
3197 }
3198 
3199 /*
3200  * Translate an instance element into an internal property tree, added to
3201  * service.  If op is SVCCFG_OP_APPLY (i.e., apply a profile), set the
3202  * enabled property to override.
3203  *
3204  * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3205  * modification of template data.
3206  */
3207 static int
3208 lxml_get_instance(entity_t *service, xmlNodePtr inst, bundle_type_t bt,
3209     svccfg_op_t op)
3210 {
3211         entity_t *i;
3212         pgroup_t *pg;
3213         property_t *p;
3214         xmlNodePtr cursor;
3215         xmlChar *enabled;
3216         int r, e_val;
3217 
3218         /*
3219          * Fetch its attributes, as appropriate.
3220          */
3221         i = internal_instance_new((char *)xmlGetProp(inst,
3222             (xmlChar *)name_attr));
3223 
3224         /*
3225          * Note that this must be done before walking the children so that
3226          * sc_fmri is set in case we enter lxml_get_dependent().
3227          */
3228         r = internal_attach_entity(service, i);
3229         if (r != 0)
3230                 return (r);
3231 
3232         i->sc_op = op;
3233         enabled = xmlGetProp(inst, (xmlChar *)enabled_attr);
3234 
3235         if (enabled == NULL) {
3236                 if (bt == SVCCFG_MANIFEST) {
3237                         semerr(gettext("Instance \"%s\" missing attribute "
3238                             "\"%s\".\n"), i->sc_name, enabled_attr);
3239                         return (-1);
3240                 }
3241         } else {        /* enabled != NULL */
3242                 if (strcmp(true, (const char *)enabled) != 0 &&
3243                     strcmp(false, (const char *)enabled) != 0) {
3244                         xmlFree(enabled);
3245                         semerr(gettext("Invalid enabled value\n"));
3246                         return (-1);
3247                 }
3248                 pg = internal_pgroup_new();
3249                 (void) internal_attach_pgroup(i, pg);
3250 
3251                 pg->sc_pgroup_name = (char *)scf_pg_general;
3252                 pg->sc_pgroup_type = (char *)scf_group_framework;
3253                 pg->sc_pgroup_flags = 0;
3254 
3255                 e_val = (strcmp(true, (const char *)enabled) == 0);
3256                 p = internal_property_create(SCF_PROPERTY_ENABLED,
3257                     SCF_TYPE_BOOLEAN, 1, (uint64_t)e_val);
3258 
3259                 p->sc_property_override = (op == SVCCFG_OP_APPLY);
3260 
3261                 (void) internal_attach_property(pg, p);
3262 
3263                 xmlFree(enabled);
3264         }
3265 
3266         /*
3267          * Walk its child elements, as appropriate.
3268          */
3269         for (cursor = inst->xmlChildrenNode; cursor != NULL;
3270             cursor = cursor->next) {
3271                 if (lxml_ignorable_block(cursor))
3272                         continue;
3273 
3274                 switch (lxml_xlate_element(cursor->name)) {
3275                 case SC_RESTARTER:
3276                         (void) lxml_get_restarter(i, cursor);
3277                         break;
3278                 case SC_DEPENDENCY:
3279                         (void) lxml_get_dependency(i, cursor);
3280                         break;
3281                 case SC_DEPENDENT:
3282                         (void) lxml_get_dependent(i, cursor);
3283                         break;
3284                 case SC_METHOD_CONTEXT:
3285                         (void) lxml_get_entity_method_context(i, cursor);
3286                         break;
3287                 case SC_EXEC_METHOD:
3288                         (void) lxml_get_exec_method(i, cursor);
3289                         break;
3290                 case SC_PROPERTY_GROUP:
3291                         (void) lxml_get_pgroup(i, cursor);
3292                         break;
3293                 case SC_TEMPLATE:
3294                         if (op == SVCCFG_OP_APPLY) {
3295                                 semerr(gettext("Template data for \"%s\" may "
3296                                     "not be modified in a profile.\n"),
3297                                     i->sc_name);
3298 
3299                                 return (-1);
3300                         }
3301 
3302                         if (lxml_get_template(i, cursor) != 0)
3303                                 return (-1);
3304                         break;
3305                 case SC_NOTIFICATION_PARAMETERS:
3306                         if (lxml_get_notification_parameters(i, cursor) != 0)
3307                                 return (-1);
3308                         break;
3309                 default:
3310                         uu_die(gettext(
3311                             "illegal element \"%s\" on instance \"%s\"\n"),
3312                             cursor->name, i->sc_name);
3313                         break;
3314                 }
3315         }
3316 
3317         return (0);
3318 }
3319 
3320 /* ARGSUSED1 */
3321 static int
3322 lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
3323 {
3324         pgroup_t *pg;
3325         property_t *p;
3326         int r;
3327 
3328         pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
3329             (char *)scf_group_framework);
3330 
3331         p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE,
3332             SCF_TYPE_BOOLEAN, 1, (uint64_t)1);
3333 
3334         r = internal_attach_property(pg, p);
3335         if (r != 0) {
3336                 internal_property_free(p);
3337                 return (-1);
3338         }
3339 
3340         return (0);
3341 }
3342 
3343 /*
3344  * Check to see if the service should allow the upgrade
3345  * process to handle adding of the manifestfiles linkage.
3346  *
3347  * If the service exists and does not have a manifestfiles
3348  * property group then the upgrade process should handle
3349  * the service.
3350  *
3351  * If the service doesn't exist or the service exists
3352  * and has a manifestfiles property group then the import
3353  * process can handle the manifestfiles property group
3354  * work.
3355  *
3356  * This prevents potential cleanup of unaccounted for instances
3357  * in early manifest import due to upgrade process needing
3358  * information that has not yet been supplied by manifests
3359  * that are still located in the /var/svc manifests directory.
3360  */
3361 static int
3362 lxml_check_upgrade(const char *service)
3363 {
3364         scf_handle_t    *h = NULL;
3365         scf_scope_t     *sc = NULL;
3366         scf_service_t   *svc = NULL;
3367         scf_propertygroup_t     *pg = NULL;
3368         int rc = SCF_FAILED;
3369 
3370         if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
3371             (sc = scf_scope_create(h)) == NULL ||
3372             (svc = scf_service_create(h)) == NULL ||
3373             (pg = scf_pg_create(h)) == NULL)
3374                 goto out;
3375 
3376         if (scf_handle_bind(h) != 0)
3377                 goto out;
3378 
3379         if (scf_handle_get_scope(h, SCF_FMRI_LOCAL_SCOPE, sc) == -1)
3380                 goto out;
3381 
3382         if (scf_scope_get_service(sc, service, svc) != SCF_SUCCESS) {
3383                 if (scf_error() == SCF_ERROR_NOT_FOUND)
3384                         rc = SCF_SUCCESS;
3385 
3386                 goto out;
3387         }
3388 
3389         if (scf_service_get_pg(svc, SCF_PG_MANIFESTFILES, pg) != SCF_SUCCESS)
3390                 goto out;
3391 
3392         rc = SCF_SUCCESS;
3393 out:
3394         scf_pg_destroy(pg);
3395         scf_service_destroy(svc);
3396         scf_scope_destroy(sc);
3397         scf_handle_destroy(h);
3398 
3399         return (rc);
3400 }
3401 
3402 /*
3403  * Translate a service element into an internal instance/property tree, added
3404  * to bundle.
3405  *
3406  * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3407  * modification of template data.
3408  */
3409 static int
3410 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
3411 {
3412         pgroup_t *pg;
3413         property_t *p;
3414         entity_t *s;
3415         xmlNodePtr cursor;
3416         xmlChar *type;
3417         xmlChar *version;
3418         int e;
3419 
3420         /*
3421          * Fetch attributes, as appropriate.
3422          */
3423         s = internal_service_new((char *)xmlGetProp(svc,
3424             (xmlChar *)name_attr));
3425 
3426         version = xmlGetProp(svc, (xmlChar *)version_attr);
3427         s->sc_u.sc_service.sc_service_version = atol((const char *)version);
3428         xmlFree(version);
3429 
3430         type = xmlGetProp(svc, (xmlChar *)type_attr);
3431         s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type);
3432         xmlFree(type);
3433 
3434         /*
3435          * Set the global missing type to false before processing the service
3436          */
3437         est->sc_miss_type = B_FALSE;
3438         s->sc_op = op;
3439 
3440         /*
3441          * Now that the service is created create the manifest
3442          * property group and add the property value of the service.
3443          */
3444         if (lxml_check_upgrade(s->sc_name) == SCF_SUCCESS &&
3445             svc->doc->name != NULL &&
3446             bundle->sc_bundle_type == SVCCFG_MANIFEST) {
3447                 char *buf, *base, *fname, *bname;
3448                 size_t  base_sz = 0;
3449 
3450                 /*
3451                  * Must remove the PKG_INSTALL_ROOT, point to the correct
3452                  * directory after install
3453                  */
3454                 bname = uu_zalloc(PATH_MAX + 1);
3455                 if (realpath(svc->doc->name, bname) == NULL) {
3456                         uu_die(gettext("Unable to create the real path of the "
3457                             "manifest file \"%s\" : %d\n"), svc->doc->name,
3458                             errno);
3459                 }
3460 
3461                 base = getenv("PKG_INSTALL_ROOT");
3462                 if (base != NULL && strncmp(bname, base, strlen(base)) == 0) {
3463                         base_sz = strlen(base);
3464                 }
3465                 fname = safe_strdup(bname + base_sz);
3466 
3467                 uu_free(bname);
3468                 buf = mhash_filename_to_propname(svc->doc->name, B_FALSE);
3469 
3470                 pg = internal_pgroup_create_strict(s, SCF_PG_MANIFESTFILES,
3471                     SCF_GROUP_FRAMEWORK);
3472 
3473                 if (pg == NULL) {
3474                         uu_die(gettext("Property group for prop_pattern, "
3475                             "\"%s\", already exists in %s\n"),
3476                             SCF_PG_MANIFESTFILES, s->sc_name);
3477                 }
3478 
3479                 p = internal_property_create(buf, SCF_TYPE_ASTRING, 1, fname);
3480 
3481                 (void) internal_attach_property(pg, p);
3482         }
3483 
3484         /*
3485          * Walk its child elements, as appropriate.
3486          */
3487         for (cursor = svc->xmlChildrenNode; cursor != NULL;
3488             cursor = cursor->next) {
3489                 if (lxml_ignorable_block(cursor))
3490                         continue;
3491 
3492                 e = lxml_xlate_element(cursor->name);
3493 
3494                 switch (e) {
3495                 case SC_INSTANCE:
3496                         if (lxml_get_instance(s, cursor,
3497                             bundle->sc_bundle_type, op) != 0)
3498                                 return (-1);
3499                         break;
3500                 case SC_TEMPLATE:
3501                         if (op == SVCCFG_OP_APPLY) {
3502                                 semerr(gettext("Template data for \"%s\" may "
3503                                     "not be modified in a profile.\n"),
3504                                     s->sc_name);
3505 
3506                                 return (-1);
3507                         }
3508 
3509                         if (lxml_get_template(s, cursor) != 0)
3510                                 return (-1);
3511                         break;
3512                 case SC_NOTIFICATION_PARAMETERS:
3513                         if (lxml_get_notification_parameters(s, cursor) != 0)
3514                                 return (-1);
3515                         break;
3516                 case SC_STABILITY:
3517                         (void) lxml_get_entity_stability(s, cursor);
3518                         break;
3519                 case SC_DEPENDENCY:
3520                         (void) lxml_get_dependency(s, cursor);
3521                         break;
3522                 case SC_DEPENDENT:
3523                         (void) lxml_get_dependent(s, cursor);
3524                         break;
3525                 case SC_RESTARTER:
3526                         (void) lxml_get_restarter(s, cursor);
3527                         break;
3528                 case SC_EXEC_METHOD:
3529                         (void) lxml_get_exec_method(s, cursor);
3530                         break;
3531                 case SC_METHOD_CONTEXT:
3532                         (void) lxml_get_entity_method_context(s, cursor);
3533                         break;
3534                 case SC_PROPERTY_GROUP:
3535                         (void) lxml_get_pgroup(s, cursor);
3536                         break;
3537                 case SC_INSTANCE_CREATE_DEFAULT:
3538                         (void) lxml_get_default_instance(s, cursor);
3539                         break;
3540                 case SC_INSTANCE_SINGLE:
3541                         (void) lxml_get_single_instance(s, cursor);
3542                         break;
3543                 default:
3544                         uu_die(gettext(
3545                             "illegal element \"%s\" on service \"%s\"\n"),
3546                             cursor->name, s->sc_name);
3547                         break;
3548                 }
3549         }
3550 
3551         /*
3552          * Now that the service has been processed set the missing type
3553          * for the service.  So that only the services with missing
3554          * types are processed.
3555          */
3556         s->sc_miss_type = est->sc_miss_type;
3557         if (est->sc_miss_type)
3558                 est->sc_miss_type = B_FALSE;
3559 
3560         return (internal_attach_service(bundle, s));
3561 }
3562 
3563 #ifdef DEBUG
3564 void
3565 lxml_dump(int g, xmlNodePtr p)
3566 {
3567         if (p && p->name) {
3568                 (void) printf("%d %s\n", g, p->name);
3569 
3570                 for (p = p->xmlChildrenNode; p != NULL; p = p->next)
3571                         lxml_dump(g + 1, p);
3572         }
3573 }
3574 #endif /* DEBUG */
3575 
3576 static int
3577 lxml_is_known_dtd(const xmlChar *dtdname)
3578 {
3579         if (dtdname == NULL ||
3580             strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0)
3581                 return (0);
3582 
3583         return (1);
3584 }
3585 
3586 static int
3587 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
3588     xmlNodePtr subbundle, svccfg_op_t op)
3589 {
3590         xmlNodePtr cursor;
3591         xmlChar *type;
3592         int e;
3593 
3594         /*
3595          * 1.  Get bundle attributes.
3596          */
3597         type = xmlGetProp(subbundle, (xmlChar *)type_attr);
3598         bundle->sc_bundle_type = lxml_xlate_bundle_type(type);
3599         if (bundle->sc_bundle_type != bundle_type &&
3600             bundle_type != SVCCFG_UNKNOWN_BUNDLE) {
3601                 semerr(gettext("included bundle of different type.\n"));
3602                 return (-1);
3603         }
3604 
3605         xmlFree(type);
3606 
3607         switch (op) {
3608         case SVCCFG_OP_IMPORT:
3609                 if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
3610                         semerr(gettext("document is not a manifest.\n"));
3611                         return (-1);
3612                 }
3613                 break;
3614         case SVCCFG_OP_APPLY:
3615                 if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
3616                         semerr(gettext("document is not a profile.\n"));
3617                         return (-1);
3618                 }
3619                 break;
3620         case SVCCFG_OP_RESTORE:
3621                 if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) {
3622                         semerr(gettext("document is not an archive.\n"));
3623                         return (-1);
3624                 }
3625                 break;
3626         }
3627 
3628         if (((bundle->sc_bundle_name = xmlGetProp(subbundle,
3629             (xmlChar *)name_attr)) == NULL) || (*bundle->sc_bundle_name == 0)) {
3630                 semerr(gettext("service bundle lacks name attribute\n"));
3631                 return (-1);
3632         }
3633 
3634         /*
3635          * 2.  Get services, descend into each one and build state.
3636          */
3637         for (cursor = subbundle->xmlChildrenNode; cursor != NULL;
3638             cursor = cursor->next) {
3639                 if (lxml_ignorable_block(cursor))
3640                         continue;
3641 
3642                 e = lxml_xlate_element(cursor->name);
3643 
3644                 switch (e) {
3645                 case SC_XI_INCLUDE:
3646                         continue;
3647 
3648                 case SC_SERVICE_BUNDLE:
3649                         if (lxml_get_bundle(bundle, bundle_type, cursor, op))
3650                                 return (-1);
3651                         break;
3652                 case SC_SERVICE:
3653                         if (lxml_get_service(bundle, cursor, op) != 0)
3654                                 return (-1);
3655                         break;
3656                 }
3657         }
3658 
3659         return (0);
3660 }
3661 
3662 /*
3663  * Load an XML tree from filename and translate it into an internal service
3664  * tree bundle.  Require that the bundle be of appropriate type for the
3665  * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY.
3666  */
3667 int
3668 lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op)
3669 {
3670         xmlDocPtr document;
3671         xmlNodePtr cursor;
3672         xmlDtdPtr dtd = NULL;
3673         xmlValidCtxtPtr vcp;
3674         boolean_t do_validate;
3675         char *dtdpath = NULL;
3676         int r;
3677 
3678         /*
3679          * Verify we can read the file before we try to parse it.
3680          */
3681         if (access(filename, R_OK | F_OK) == -1) {
3682                 semerr(gettext("unable to open file: %s\n"), strerror(errno));
3683                 return (-1);
3684         }
3685 
3686         /*
3687          * Until libxml2 addresses DTD-based validation with XInclude, we don't
3688          * validate service profiles (i.e. the apply path).
3689          */
3690         do_validate = (op != SVCCFG_OP_APPLY) &&
3691             (getenv("SVCCFG_NOVALIDATE") == NULL);
3692         if (do_validate)
3693                 dtdpath = getenv("SVCCFG_DTD");
3694 
3695         if (dtdpath != NULL)
3696                 xmlLoadExtDtdDefaultValue = 0;
3697 
3698         if ((document = xmlReadFile(filename, NULL, 0)) == NULL) {
3699                 semerr(gettext("couldn't parse document\n"));
3700                 return (-1);
3701         }
3702 
3703         document->name = safe_strdup(filename);
3704 
3705         /*
3706          * Verify that this is a document type we understand.
3707          */
3708         if ((dtd = xmlGetIntSubset(document)) == NULL) {
3709                 semerr(gettext("document has no DTD\n"));
3710                 return (-1);
3711         } else if (dtdpath == NULL && !do_validate) {
3712                 /*
3713                  * If apply then setup so that some validation
3714                  * for specific elements can be done.
3715                  */
3716                 dtdpath = (char *)document->intSubset->SystemID;
3717         }
3718 
3719         if (!lxml_is_known_dtd(dtd->SystemID)) {
3720                 semerr(gettext("document DTD unknown; not service bundle?\n"));
3721                 return (-1);
3722         }
3723 
3724         if ((cursor = xmlDocGetRootElement(document)) == NULL) {
3725                 semerr(gettext("document is empty\n"));
3726                 xmlFreeDoc(document);
3727                 return (-1);
3728         }
3729 
3730         if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) {
3731                 semerr(gettext("document is not a service bundle\n"));
3732                 xmlFreeDoc(document);
3733                 return (-1);
3734         }
3735 
3736 
3737         if (dtdpath != NULL) {
3738                 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
3739                 if (dtd == NULL) {
3740                         semerr(gettext("Could not parse DTD \"%s\".\n"),
3741                             dtdpath);
3742                         return (-1);
3743                 }
3744 
3745                 if (document->extSubset != NULL)
3746                         xmlFreeDtd(document->extSubset);
3747 
3748                 document->extSubset = dtd;
3749         }
3750 
3751         if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
3752                 semerr(gettext("couldn't handle XInclude statements "
3753                     "in document\n"));
3754                 return (-1);
3755         }
3756 
3757         if (do_validate) {
3758                 vcp = xmlNewValidCtxt();
3759                 if (vcp == NULL)
3760                         uu_die(gettext("could not allocate memory"));
3761                 vcp->warning = xmlParserValidityWarning;
3762                 vcp->error = xmlParserValidityError;
3763 
3764                 r = xmlValidateDocument(vcp, document);
3765 
3766                 xmlFreeValidCtxt(vcp);
3767 
3768                 if (r == 0) {
3769                         semerr(gettext("Document is not valid.\n"));
3770                         xmlFreeDoc(document);
3771                         return (-1);
3772                 }
3773         }
3774 
3775 #ifdef DEBUG
3776         lxml_dump(0, cursor);
3777 #endif /* DEBUG */
3778 
3779         r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op);
3780 
3781         xmlFreeDoc(document);
3782 
3783         return (r);
3784 }
3785 
3786 int
3787 lxml_inventory(const char *filename)
3788 {
3789         bundle_t *b;
3790         uu_list_walk_t *svcs, *insts;
3791         entity_t *svc, *inst;
3792 
3793         b = internal_bundle_new();
3794 
3795         if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) {
3796                 internal_bundle_free(b);
3797                 return (-1);
3798         }
3799 
3800         svcs = uu_list_walk_start(b->sc_bundle_services, 0);
3801         if (svcs == NULL)
3802                 uu_die(gettext("Couldn't walk services"));
3803 
3804         while ((svc = uu_list_walk_next(svcs)) != NULL) {
3805                 uu_list_t *inst_list;
3806 
3807                 inst_list = svc->sc_u.sc_service.sc_service_instances;
3808                 insts = uu_list_walk_start(inst_list, 0);
3809                 if (insts == NULL)
3810                         uu_die(gettext("Couldn't walk instances"));
3811 
3812                 while ((inst = uu_list_walk_next(insts)) != NULL)
3813                         (void) printf("svc:/%s:%s\n", svc->sc_name,
3814                             inst->sc_name);
3815 
3816                 uu_list_walk_end(insts);
3817         }
3818 
3819         uu_list_walk_end(svcs);
3820 
3821         svcs = uu_list_walk_start(b->sc_bundle_services, 0);
3822         while ((svc = uu_list_walk_next(svcs)) != NULL) {
3823                 (void) fputs("svc:/", stdout);
3824                 (void) puts(svc->sc_name);
3825         }
3826         uu_list_walk_end(svcs);
3827 
3828         internal_bundle_free(b);
3829 
3830         return (0);
3831 }