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