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