1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * scf_tmpl.c
27 *
28 * This file implements the bulk of the libscf templates interfaces.
29 * Templates describe metadata about a service or instance in general,
30 * and individual configuration properties on those services and instances.
31 * Human-consumable descriptions can be provided, along with definitions
32 * of valid configuration. See service_bundle.dtd.1 for XML definitions
33 * of templates, and the svccfg code for information on how those definitions
34 * are translated into the repository.
35 *
36 * The main data structures are scf_pg_tmpl and scf_prop_tmpl. These
37 * are allocated by the callers through scf_tmpl_[pg|prop]_create(), and
38 * destroyed with scf_tmpl_[pg|prop]_destroy(). They are populated by
39 * scf_tmpl_get_by_pg_name(), scf_tmpl_get_by_pg(), and
40 * scf_tmpl_get_by_prop(). They also store the iterator state for
41 * scf_tmpl_iter_pgs() and scf_tmpl_iter_props().
42 *
43 * These data structures are then consumed by other functions to
44 * gather information about the template (e.g. name, description,
45 * choices, constraints, etc.).
46 *
47 * scf_tmpl_validate_fmri() does instance validation against template
48 * data, and populates a set of template errors which can be explored using
49 * the scf_tmpl_next_error() and the scf_tmpl_error*() suite of functions.
50 *
51 * The main data structures for template errors are scf_tmpl_errors,
52 * defined in this file, and scf_tmpl_error, defined in libscf_priv.h.
53 * scf_tmpl_error is shared with svccfg to offer common printing
54 * of error messages between libscf and svccfg.
55 *
56 * General convenience functions are towards the top of this file,
57 * followed by pg and prop template discovery functions, followed
58 * by functions which gather information about the discovered
59 * template. Validation and error functions are at the end of this file.
60 */
61
62 #include "lowlevel_impl.h"
63 #include "libscf_impl.h"
64 #include <assert.h>
65 #include <errno.h>
66 #include <libintl.h>
67 #include <stdlib.h>
68 #include <stdio.h>
69 #include <strings.h>
70 #include <locale.h>
71 #include <ctype.h>
72 #include <inttypes.h>
73
74 #define SCF_TMPL_PG_COMMON_NAME_C "common_name_C"
75
76 #define SCF__TMPL_ITER_NONE 0
77 #define SCF__TMPL_ITER_INST 1
78 #define SCF__TMPL_ITER_RESTARTER 2
79 #define SCF__TMPL_ITER_GLOBAL 3
80
81 #define SCF_TMPL_PG_NT 0
82 #define SCF_TMPL_PG_N 1
83 #define SCF_TMPL_PG_T 2
84 #define SCF_TMPL_PG_WILD 3
85
86 struct scf_pg_tmpl {
87 int pt_populated;
88 scf_handle_t *pt_h;
89 scf_propertygroup_t *pt_pg;
90 scf_service_t *pt_orig_svc;
91 scf_service_t *pt_svc;
92 scf_instance_t *pt_orig_inst;
93 scf_instance_t *pt_inst;
94 scf_snapshot_t *pt_snap;
95 int pt_is_iter;
96 scf_iter_t *pt_iter;
97 int pt_iter_last;
98 };
99
100 #define SCF_WALK_ERROR -1
101 #define SCF_WALK_NEXT 0
102 #define SCF_WALK_DONE 1
103
104 struct pg_tmpl_walk {
105 const char *pw_snapname;
106 const char *pw_pgname;
107 const char *pw_pgtype;
108 scf_instance_t *pw_inst;
109 scf_service_t *pw_svc;
110 scf_snapshot_t *pw_snap;
111 scf_propertygroup_t *pw_pg;
112 const char *pw_target;
113 char *pw_tmpl_pgname;
114 };
115
116 typedef struct pg_tmpl_walk pg_tmpl_walk_t;
117
118 typedef int walk_template_inst_func_t(scf_service_t *_svc,
119 scf_instance_t *_inst, pg_tmpl_walk_t *p);
120
121 struct scf_prop_tmpl {
122 int prt_populated;
123 scf_handle_t *prt_h;
124 scf_pg_tmpl_t *prt_t;
125 scf_propertygroup_t *prt_pg;
126 char *prt_pg_name;
127 scf_iter_t *prt_iter;
128 };
129
130 /*
131 * Common server errors are usually passed back to the caller. This
132 * array defines them centrally so that they don't need to be enumerated
133 * in every libscf call.
134 */
135 static const scf_error_t errors_server[] = {
136 SCF_ERROR_BACKEND_ACCESS,
137 SCF_ERROR_CONNECTION_BROKEN,
138 SCF_ERROR_DELETED,
139 SCF_ERROR_HANDLE_DESTROYED,
140 SCF_ERROR_INTERNAL,
141 SCF_ERROR_NO_MEMORY,
142 SCF_ERROR_NO_RESOURCES,
143 SCF_ERROR_NOT_BOUND,
144 SCF_ERROR_PERMISSION_DENIED,
145 0
146 };
147
148 /*
149 * int ismember()
150 *
151 * Returns 1 if the supplied error is a member of the error array, 0
152 * if it is not.
153 */
154 int
ismember(const scf_error_t error,const scf_error_t error_array[])155 ismember(const scf_error_t error, const scf_error_t error_array[])
156 {
157 int i;
158
159 for (i = 0; error_array[i] != 0; ++i) {
160 if (error == error_array[i])
161 return (1);
162 }
163
164 return (0);
165 }
166
167 /*
168 * char *_scf_tmpl_get_fmri()
169 *
170 * Given a pg_tmpl, returns the FMRI of the service or instance that
171 * template describes. The allocated string must be freed with free().
172 *
173 * On failure, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
174 * _DELETED, or _NO_MEMORY.
175 */
176 static char *
_scf_tmpl_get_fmri(const scf_pg_tmpl_t * t)177 _scf_tmpl_get_fmri(const scf_pg_tmpl_t *t)
178 {
179 ssize_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
180 int r;
181 char *buf = malloc(sz);
182
183 assert(t->pt_svc != NULL || t->pt_inst != NULL);
184 assert(t->pt_svc == NULL || t->pt_inst == NULL);
185
186 if (buf == NULL) {
187 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
188 return (buf);
189 }
190
191 if (t->pt_inst != NULL)
192 r = scf_instance_to_fmri(t->pt_inst, buf, sz);
193 else
194 r = scf_service_to_fmri(t->pt_svc, buf, sz);
195
196 if (r == -1) {
197 if (ismember(scf_error(), errors_server)) {
198 free(buf);
199 buf = NULL;
200 } else {
201 assert(0);
202 abort();
203 }
204 }
205
206 return (buf);
207 }
208
209 /*
210 * char *_scf_get_pg_type()
211 *
212 * Given a propertygroup, returns an allocated string containing the
213 * type. The string must be freed with free().
214 *
215 * On failure, returns NULL and sets scf_error() to: _CONNECTION_BROKEN,
216 * _DELETED, or _NO_MEMORY.
217 */
218 static char *
_scf_get_pg_type(scf_propertygroup_t * pg)219 _scf_get_pg_type(scf_propertygroup_t *pg)
220 {
221 ssize_t sz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
222 char *buf = malloc(sz);
223
224 if (buf == NULL) {
225 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
226 } else if (scf_pg_get_type(pg, buf, sz) == -1) {
227 if (ismember(scf_error(), errors_server)) {
228 free(buf);
229 buf = NULL;
230 } else {
231 assert(0);
232 abort();
233 }
234 }
235
236 return (buf);
237 }
238
239 /*
240 * char *_scf_get_prop_name()
241 *
242 * Given a property, returns the name in an allocated string. The string must
243 * be freed with free().
244 *
245 * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
246 * _DELETED, or _NO_MEMORY.
247 */
248 static char *
_scf_get_prop_name(scf_property_t * prop)249 _scf_get_prop_name(scf_property_t *prop)
250 {
251 ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
252 char *buf = malloc(sz);
253
254 if (buf == NULL) {
255 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
256 } else if (scf_property_get_name(prop, buf, sz) == -1) {
257 if (ismember(scf_error(), errors_server)) {
258 free(buf);
259 buf = NULL;
260 } else {
261 assert(0);
262 abort();
263 }
264 }
265
266 return (buf);
267 }
268
269 /*
270 * char *_scf_get_prop_type()
271 *
272 * Given a property, returns the type in an allocated string. The string must
273 * be freed with free().
274 *
275 * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
276 * _DELETED, or _NO_MEMORY.
277 */
278 static char *
_scf_get_prop_type(scf_property_t * prop)279 _scf_get_prop_type(scf_property_t *prop)
280 {
281 scf_type_t type;
282 char *ret;
283
284 if (scf_property_type(prop, &type) == -1) {
285 if (ismember(scf_error(), errors_server)) {
286 return (NULL);
287 } else {
288 assert(0);
289 abort();
290 }
291 }
292
293 ret = strdup(scf_type_to_string(type));
294 if (ret == NULL)
295 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
296
297 return (ret);
298 }
299
300 /*
301 * int _read_single_value_from_pg()
302 *
303 * Reads a single value from the pg and property name specified. On success,
304 * returns an allocated value that must be freed.
305 *
306 * Returns -1 on failure, sets scf_error() to:
307 * SCF_ERROR_BACKEND_ACCESS
308 * SCF_ERROR_CONNECTION_BROKEN
309 * SCF_ERROR_CONSTRAINT_VIOLATED
310 * Property has more than one value associated with it.
311 * SCF_ERROR_DELETED
312 * SCF_ERROR_HANDLE_DESTROYED
313 * SCF_ERROR_INTERNAL
314 * SCF_ERROR_INVALID_ARGUMENT
315 * prop_name not a valid property name.
316 * SCF_ERROR_NO_MEMORY
317 * SCF_ERROR_NO_RESOURCES
318 * SCF_ERROR_NOT_BOUND
319 * SCF_ERROR_NOT_FOUND
320 * Property doesn't exist or exists and has no value.
321 * SCF_ERROR_NOT_SET
322 * Property group specified by pg is not set.
323 * SCF_ERROR_PERMISSION_DENIED
324 */
325 static int
_read_single_value_from_pg(scf_propertygroup_t * pg,const char * prop_name,scf_value_t ** val)326 _read_single_value_from_pg(scf_propertygroup_t *pg, const char *prop_name,
327 scf_value_t **val)
328 {
329 scf_handle_t *h;
330 scf_property_t *prop;
331 int ret = 0;
332
333 assert(val != NULL);
334 if ((h = scf_pg_handle(pg)) == NULL) {
335 assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
336 return (-1);
337 }
338
339 prop = scf_property_create(h);
340 *val = scf_value_create(h);
341
342 if (prop == NULL || *val == NULL) {
343 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
344 goto read_single_value_from_pg_fail;
345 }
346
347 if (scf_pg_get_property(pg, prop_name, prop) != 0) {
348 assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
349 goto read_single_value_from_pg_fail;
350 }
351
352 if (scf_property_get_value(prop, *val) == -1) {
353 assert(scf_error() != SCF_ERROR_NOT_SET);
354 assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
355 goto read_single_value_from_pg_fail;
356 }
357
358 goto read_single_value_from_pg_done;
359
360 read_single_value_from_pg_fail:
361 scf_value_destroy(*val);
362 *val = NULL;
363 ret = -1;
364
365 read_single_value_from_pg_done:
366 scf_property_destroy(prop);
367 return (ret);
368 }
369
370 /*
371 * char *_scf_read_single_astring_from_pg()
372 *
373 * Reads an astring from the pg and property name specified. On success,
374 * returns an allocated string. The string must be freed with free().
375 *
376 * Returns NULL on failure, sets scf_error() to:
377 * SCF_ERROR_BACKEND_ACCESS
378 * SCF_ERROR_CONNECTION_BROKEN
379 * SCF_ERROR_CONSTRAINT_VIOLATED
380 * Property has more than one value associated with it.
381 * SCF_ERROR_DELETED
382 * SCF_ERROR_HANDLE_DESTROYED
383 * SCF_ERROR_INTERNAL
384 * SCF_ERROR_INVALID_ARGUMENT
385 * prop_name not a valid property name.
386 * SCF_ERROR_NO_MEMORY
387 * SCF_ERROR_NO_RESOURCES
388 * SCF_ERROR_NOT_BOUND
389 * SCF_ERROR_NOT_FOUND
390 * Property doesn't exist or exists and has no value.
391 * SCF_ERROR_NOT_SET
392 * The property group specified by pg is not set.
393 * SCF_ERROR_PERMISSION_DENIED
394 * SCF_ERROR_TYPE_MISMATCH
395 */
396 char *
_scf_read_single_astring_from_pg(scf_propertygroup_t * pg,const char * prop_name)397 _scf_read_single_astring_from_pg(scf_propertygroup_t *pg, const char *prop_name)
398 {
399 scf_value_t *val;
400 char *ret = NULL;
401 ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
402
403 assert(rsize != 0);
404 if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
405 return (NULL);
406
407 ret = malloc(rsize);
408 if (ret == NULL) {
409 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
410 goto cleanup;
411 }
412
413 if (scf_value_get_astring(val, ret, rsize) < 0) {
414 assert(scf_error() != SCF_ERROR_NOT_SET);
415 free(ret);
416 ret = NULL;
417 }
418
419 cleanup:
420 scf_value_destroy(val);
421 return (ret);
422 }
423
424 /*
425 * char *_scf_read_tmpl_prop_type_as_string()
426 *
427 * Reads the property type and returns it as an allocated string. The string
428 * must be freed with free().
429 *
430 * Returns NULL on failure, sets scf_error() to _BACKEND_ACCESS,
431 * _CONNECTION_BROKEN, _DELETED, _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY,
432 * _NO_RESOURCES, _NOT_BOUND, _PERMISSION_DENIED, or _TEMPLATE_INVALID.
433 */
434 char *
_scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t * pt)435 _scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t *pt)
436 {
437 char *type;
438
439 type = _scf_read_single_astring_from_pg(pt->prt_pg,
440 SCF_PROPERTY_TM_TYPE);
441 if (type == NULL) {
442 if (ismember(scf_error(), errors_server)) {
443 return (NULL);
444 } else switch (scf_error()) {
445 case SCF_ERROR_CONSTRAINT_VIOLATED:
446 case SCF_ERROR_NOT_FOUND:
447 case SCF_ERROR_TYPE_MISMATCH:
448 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
449 return (NULL);
450
451 case SCF_ERROR_INVALID_ARGUMENT:
452 case SCF_ERROR_NOT_SET:
453 default:
454 assert(0);
455 abort();
456 }
457 }
458
459 return (type);
460 }
461
462 /*
463 * int _read_single_boolean_from_pg()
464 *
465 * Reads a boolean from the pg and property name specified.
466 *
467 * Returns -1 on failure, sets scf_error() to:
468 * SCF_ERROR_BACKEND_ACCESS
469 * SCF_ERROR_CONNECTION_BROKEN
470 * SCF_ERROR_CONSTRAINT_VIOLATED
471 * Property has more than one value associated with it.
472 * SCF_ERROR_DELETED
473 * SCF_ERROR_HANDLE_DESTROYED
474 * SCF_ERROR_INTERNAL
475 * SCF_ERROR_INVALID_ARGUMENT
476 * prop_name is not a valid property name.
477 * SCF_ERROR_NO_MEMORY
478 * SCF_ERROR_NO_RESOURCES
479 * SCF_ERROR_NOT_BOUND
480 * SCF_ERROR_NOT_FOUND
481 * Property doesn't exist or exists and has no value.
482 * SCF_ERROR_NOT_SET
483 * The property group specified by pg is not set.
484 * SCF_ERROR_PERMISSION_DENIED
485 * SCF_ERROR_TYPE_MISMATCH
486 */
487 static int
_read_single_boolean_from_pg(scf_propertygroup_t * pg,const char * prop_name,uint8_t * bool)488 _read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
489 uint8_t *bool)
490 {
491 scf_value_t *val;
492 int ret = 0;
493
494 if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
495 return (-1);
496
497 if (scf_value_get_boolean(val, bool) < 0) {
498 assert(scf_error() != SCF_ERROR_NOT_SET);
499 ret = -1;
500 }
501
502 scf_value_destroy(val);
503 return (ret);
504 }
505
506 /*
507 * static char ** _append_astrings_values()
508 *
509 * This function reads the values from the property prop_name in pg and
510 * appends to an existing scf_values_t *vals. vals may be empty, but
511 * must exist. The function skips over zero-length and duplicate values.
512 *
513 * Returns NULL on failure, sets scf_error() to:
514 * SCF_ERROR_BACKEND_ACCESS
515 * SCF_ERROR_CONNECTION_BROKEN
516 * SCF_ERROR_DELETED
517 * SCF_ERROR_HANDLE_DESTROYED
518 * SCF_ERROR_INTERNAL
519 * SCF_ERROR_INVALID_ARGUMENT
520 * prop_name is not a valid property name.
521 * SCF_ERROR_NO_MEMORY
522 * SCF_ERROR_NO_RESOURCES
523 * SCF_ERROR_NOT_BOUND
524 * SCF_ERROR_NOT_FOUND
525 * SCF_ERROR_NOT_SET
526 * SCF_ERROR_PERMISSION_DENIED
527 * SCF_ERROR_TYPE_MISMATCH
528 */
529 static char **
_append_astrings_values(scf_propertygroup_t * pg,const char * prop_name,scf_values_t * vals)530 _append_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
531 scf_values_t *vals)
532 {
533 scf_handle_t *h;
534 scf_property_t *prop;
535 scf_value_t *val;
536 scf_iter_t *iter;
537 ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
538 int err, count, cursz, i;
539
540 assert(vals != NULL);
541 assert(vals->value_type == SCF_TYPE_ASTRING);
542 assert(vals->reserved == NULL);
543 count = vals->value_count;
544 if (count == 0) {
545 cursz = 8;
546 vals->values.v_astring = calloc(cursz, sizeof (char *));
547 if (vals->values.v_astring == NULL) {
548 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
549 return (NULL);
550 }
551 } else {
552 /*
553 * The array may be bigger, but it is irrelevant since
554 * we will always re-allocate a new one.
555 */
556 cursz = count;
557 }
558
559 if ((h = scf_pg_handle(pg)) == NULL) {
560 assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
561 return (NULL);
562 }
563
564 prop = scf_property_create(h);
565 val = scf_value_create(h);
566 iter = scf_iter_create(h);
567
568 if (prop == NULL || val == NULL || iter == NULL) {
569 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
570 goto append_single_astring_from_pg_fail;
571 }
572
573 if (scf_pg_get_property(pg, prop_name, prop) != 0) {
574 assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
575 goto append_single_astring_from_pg_fail;
576 }
577
578 if (scf_iter_property_values(iter, prop) != 0) {
579 assert(scf_error() != SCF_ERROR_NOT_SET);
580 assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
581 goto append_single_astring_from_pg_fail;
582 }
583
584 while ((err = scf_iter_next_value(iter, val)) == 1) {
585 int flag;
586 int r;
587
588 if (count + 1 >= cursz) {
589 void *aux;
590
591 cursz *= 2;
592 if ((aux = calloc(cursz, sizeof (char *))) == NULL) {
593 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
594 goto append_single_astring_from_pg_fail;
595 }
596 (void) memcpy(aux, vals->values.v_astring,
597 count * sizeof (char *));
598 free(vals->values.v_astring);
599 vals->values.v_astring = aux;
600 }
601
602 vals->values.v_astring[count] = malloc(rsize);
603 if (vals->values.v_astring[count] == NULL) {
604 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
605 goto append_single_astring_from_pg_fail;
606 }
607
608 if ((r = scf_value_get_astring(val,
609 vals->values.v_astring[count], rsize)) <= 0) {
610 /* discard zero length strings */
611 if (r == 0) {
612 free(vals->values.v_astring[count]);
613 continue;
614 }
615 assert(scf_error() != SCF_ERROR_NOT_SET);
616 goto append_single_astring_from_pg_fail;
617 }
618 for (i = 0, flag = 0; i < count; ++i) {
619 /* find and discard duplicates */
620 if (strncmp(vals->values.v_astring[i],
621 vals->values.v_astring[count], rsize) == 0) {
622 free(vals->values.v_astring[count]);
623 flag = 1;
624 break;
625 }
626 }
627 if (flag == 1)
628 continue;
629
630 count++;
631 }
632
633 vals->value_count = count;
634
635 if (err != 0) {
636 assert(scf_error() != SCF_ERROR_NOT_SET);
637 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
638 assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
639 goto append_single_astring_from_pg_fail;
640 } else {
641 vals->values_as_strings = vals->values.v_astring;
642 }
643
644 goto append_single_astring_from_pg_done;
645
646 append_single_astring_from_pg_fail:
647 for (i = 0; i <= count; ++i) {
648 if (vals->values.v_astring[i] != NULL)
649 free(vals->values.v_astring[i]);
650 vals->values.v_astring[i] = NULL;
651 }
652 free(vals->values.v_astring);
653 vals->values.v_astring = NULL;
654 vals->value_count = 0;
655
656 append_single_astring_from_pg_done:
657 scf_iter_destroy(iter);
658 scf_property_destroy(prop);
659 scf_value_destroy(val);
660 return (vals->values.v_astring);
661 }
662
663 /*
664 * Returns NULL on failure, sets scf_error() to:
665 * SCF_ERROR_BACKEND_ACCESS
666 * SCF_ERROR_CONNECTION_BROKEN
667 * SCF_ERROR_DELETED
668 * SCF_ERROR_HANDLE_DESTROYED
669 * SCF_ERROR_INTERNAL
670 * SCF_ERROR_INVALID_ARGUMENT
671 * prop_name is not a valid property name.
672 * SCF_ERROR_NO_MEMORY
673 * SCF_ERROR_NO_RESOURCES
674 * SCF_ERROR_NOT_BOUND
675 * SCF_ERROR_NOT_FOUND
676 * SCF_ERROR_NOT_SET
677 * SCF_ERROR_PERMISSION_DENIED
678 * SCF_ERROR_TYPE_MISMATCH
679 */
680 static char **
_read_astrings_values(scf_propertygroup_t * pg,const char * prop_name,scf_values_t * vals)681 _read_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
682 scf_values_t *vals)
683 {
684 assert(vals != NULL);
685 vals->value_count = 0;
686 vals->value_type = SCF_TYPE_ASTRING;
687 vals->reserved = NULL;
688 return (_append_astrings_values(pg, prop_name, vals));
689 }
690
691 void
_scf_sanitize_locale(char * locale)692 _scf_sanitize_locale(char *locale)
693 {
694 for (; *locale != '\0'; locale++)
695 if (!isalnum(*locale) && *locale != '_')
696 *locale = '_';
697 }
698
699 /*
700 * The returned string needs to be freed by the caller
701 * Returns NULL on failure. Sets scf_error() to:
702 * SCF_ERROR_NO_MEMORY
703 * SCF_ERROR_INVALID_ARGUMENT
704 * Name isn't short enough to add the locale to.
705 */
706 static char *
_add_locale_to_name(const char * name,const char * locale)707 _add_locale_to_name(const char *name, const char *locale)
708 {
709 char *lname = NULL;
710 ssize_t lsz;
711 char *loc;
712
713 if (locale == NULL)
714 locale = setlocale(LC_MESSAGES, NULL);
715 loc = strdup(locale);
716 if (loc == NULL) {
717 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
718 return (NULL);
719 } else {
720 _scf_sanitize_locale(loc);
721 }
722
723 lsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
724 lname = malloc(lsz);
725 if (lname == NULL) {
726 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
727 goto cleanup;
728 }
729
730 (void) strlcpy(lname, name, lsz);
731 if (strlcat(lname, loc, lsz) >= lsz) {
732 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
733 free(lname);
734 lname = NULL;
735 }
736 cleanup:
737 free(loc);
738
739 return (lname);
740 }
741
742 /*
743 * char *_tmpl_pg_name(pg, type, use_type)
744 *
745 * pg and type can both be NULL. Returns the name of the most specific
746 * template property group name based on the inputs.
747 * If use_type is set and pg is not NULL, a property group name for a
748 * property group template that has type defined is returned, even if no
749 * type is provided.
750 *
751 * Returns NULL on failure and sets scf_error() to:
752 * SCF_ERROR_INVALID_ARGUMENT
753 * can't combine the arguments and get a reasonable length name
754 * SCF_ERROR_NO_MEMORY
755 *
756 */
757 static char *
_tmpl_pg_name(const char * pg,const char * type,int use_type)758 _tmpl_pg_name(const char *pg, const char *type, int use_type)
759 {
760 char *name;
761 ssize_t limit, size = 0;
762
763 limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
764 name = malloc(limit);
765 if (name == NULL) {
766 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
767 return (NULL);
768 }
769
770 if (pg == NULL && type == NULL) {
771 if (strlcpy(name, SCF_PG_TM_PG_PATTERN_PREFIX, limit) >=
772 limit) {
773 assert(0);
774 abort();
775 }
776 return (name);
777 } else if (pg != NULL && type != NULL) {
778 size = snprintf(name, limit, "%s%s",
779 SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
780 } else if (pg != NULL && type == NULL && use_type == 1) {
781 size = snprintf(name, limit, "%s%s",
782 SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
783 } else if (pg != NULL && type == NULL) {
784 size = snprintf(name, limit, "%s%s",
785 SCF_PG_TM_PG_PATTERN_N_PREFIX, pg);
786 } else if (type != NULL && pg == NULL) {
787 size = snprintf(name, limit, "%s%s",
788 SCF_PG_TM_PG_PATTERN_T_PREFIX, type);
789 } else {
790 assert(0);
791 abort();
792 }
793
794 if (size >= limit) {
795 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
796 free(name);
797 return (NULL);
798 } else {
799 return (name);
800 }
801 }
802
803 /*
804 * _scf_get_pg_name()
805 * Gets the name of the supplied property group. On success, returns an
806 * allocated string. The string must be freed by free().
807 *
808 * Returns NULL on failure and sets scf_error() to _CONNECTION_BROKEN,
809 * _DELETED, or _NO_MEMORY.
810 */
811 static char *
_scf_get_pg_name(scf_propertygroup_t * pg)812 _scf_get_pg_name(scf_propertygroup_t *pg)
813 {
814 ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
815 char *buf = malloc(sz);
816
817 if (buf == NULL) {
818 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
819 } else if (scf_pg_get_name(pg, buf, sz) == -1) {
820 if (ismember(scf_error(), errors_server)) {
821 free(buf);
822 buf = NULL;
823 } else {
824 assert(0);
825 abort();
826 }
827 }
828
829 return (buf);
830 }
831
832 /*
833 * char *_tmpl_prop_name()
834 *
835 * Returns the name of the property template prop (which is the name of
836 * the property template property group) in the property group
837 * template t. Returns NULL on failure and sets scf_error() to:
838 * SCF_ERROR_CONNECTION_BROKEN
839 * SCF_ERROR_DELETED
840 * SCF_ERROR_INVALID_ARGUMENT
841 * can't combine the arguments and get a reasonable length name
842 * SCF_ERROR_NO_MEMORY
843 */
844 static char *
_tmpl_prop_name(const char * prop,scf_pg_tmpl_t * t)845 _tmpl_prop_name(const char *prop, scf_pg_tmpl_t *t)
846 {
847 char *name = NULL, *pg_name = NULL;
848 size_t prefix_size;
849 ssize_t limit, size = 0;
850
851 assert(prop != NULL);
852 assert(t->pt_pg != NULL);
853
854 limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
855 name = malloc(limit);
856 if (name == NULL) {
857 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
858 return (NULL);
859 }
860
861 if ((pg_name = _scf_get_pg_name(t->pt_pg)) == NULL) {
862 free(name);
863 return (NULL);
864 }
865
866 prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
867 if (strncmp(pg_name, SCF_PG_TM_PG_PAT_BASE, prefix_size) != 0) {
868 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
869 free(name);
870 free(pg_name);
871 return (NULL);
872 }
873
874 size = snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
875 pg_name + prefix_size, prop);
876
877 if (size >= limit) {
878 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
879 free(name);
880 free(pg_name);
881 return (NULL);
882 } else {
883 free(pg_name);
884 return (name);
885 }
886 }
887
888 /*
889 * int _get_snapshot()
890 *
891 * Gets the specified snapshot. If "snapshot" isn't defined, use the
892 * running snapshot. If the snapshot isn't found, that may or may
893 * not be an error depending on the caller. Return 0 in that case,
894 * but leave scf_error() set to SCF_ERROR_NOT_FOUND. On all other
895 * errors, set scf_error() to:
896 * SCF_ERROR_BACKEND_ACCESS
897 * SCF_ERROR_CONNECTION_BROKEN
898 * SCF_ERROR_DELETED
899 * SCF_ERR_HANDLE_DESTROYED
900 * SCF_ERROR_INTERNAL
901 * SCF_ERROR_INVALID_ARGUMENT
902 * The handle argument is NULL, or snaphot is not a valid snapshot name
903 * SCF_ERROR_NO_MEMORY
904 * SCF_ERROR_NO_RESOURCES
905 * SCF_ERROR_NOT_BOUND
906 * SCF_ERROR_NOT_FOUND
907 */
908 static int
_get_snapshot(scf_instance_t * inst,const char * snapshot,scf_snapshot_t ** snap)909 _get_snapshot(scf_instance_t *inst, const char *snapshot,
910 scf_snapshot_t **snap)
911 {
912 int err;
913 scf_handle_t *h;
914
915 h = scf_instance_handle(inst);
916 if (h == NULL)
917 return (-1);
918
919 if ((*snap = scf_snapshot_create(h)) == NULL) {
920 return (-1);
921 }
922
923 /* Use running snapshot by default. */
924 if (snapshot == NULL)
925 err = scf_instance_get_snapshot(inst, "running", *snap);
926 else
927 err = scf_instance_get_snapshot(inst, snapshot, *snap);
928
929 if (err != 0) {
930 if (ismember(scf_error(), errors_server)) {
931 scf_snapshot_destroy(*snap);
932 *snap = NULL;
933 return (-1);
934 } else switch (scf_error()) {
935 case SCF_ERROR_INVALID_ARGUMENT:
936 scf_snapshot_destroy(*snap);
937 *snap = NULL;
938 return (-1);
939
940 case SCF_ERROR_NOT_FOUND:
941 scf_snapshot_destroy(*snap);
942 *snap = NULL;
943 return (0);
944
945 case SCF_ERROR_NOT_SET:
946 case SCF_ERROR_HANDLE_MISMATCH:
947 default:
948 assert(0);
949 abort();
950 }
951 }
952
953 /*
954 * Explicitly set SCF_ERROR_NONE so that the SCF_ERROR_NOT_FOUND
955 * return above is explicitly guaranteed to be from
956 * scf_instance_get_snapshot().
957 */
958 (void) scf_set_error(SCF_ERROR_NONE);
959 return (0);
960 }
961
962 /*
963 * Returns NULL on error, sets scf_error() to:
964 * SCF_ERROR_BACKEND_ACCESS
965 * SCF_ERROR_CONNECTION_BROKEN
966 * SCF_ERROR_CONSTRAINT_VIOLATED
967 * The restarter's FMRI does not match an existing instance.
968 * SCF_ERROR_DELETED
969 * SCF_ERROR_HANDLE_DESTROYED
970 * SCF_ERROR_INTERNAL
971 * SCF_ERROR_INVALID_ARGUMENT
972 * The restarter's FMRI is not a valid FMRI.
973 * SCF_ERROR_NO_MEMORY
974 * SCF_ERROR_NO_RESOURCES
975 * SCF_ERROR_NOT_BOUND
976 * SCF_ERROR_NOT_FOUND
977 * Property doesn't exist or exists and has no value.
978 * SCF_ERROR_TEMPLATE_INVALID
979 * restarter property is not SCF_TYPE_ASTRING or has more than one value
980 */
981 static scf_instance_t *
_get_restarter_inst(scf_handle_t * h,scf_service_t * svc,scf_instance_t * inst,scf_snapshot_t * s)982 _get_restarter_inst(scf_handle_t *h, scf_service_t *svc,
983 scf_instance_t *inst, scf_snapshot_t *s)
984 {
985 char *restarter = NULL;
986 scf_instance_t *ri = NULL;
987 scf_propertygroup_t *pg = NULL;
988 int ret = 0;
989
990 assert(svc != NULL || inst != NULL);
991 assert(svc == NULL || inst == NULL);
992
993 if ((ri = scf_instance_create(h)) == NULL ||
994 (pg = scf_pg_create(h)) == NULL) {
995 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
996 goto _get_restarter_inst_fail;
997 }
998
999 if (inst != NULL)
1000 ret = scf_instance_get_pg_composed(inst, s, SCF_PG_GENERAL,
1001 pg);
1002 else
1003 ret = scf_service_get_pg(svc, SCF_PG_GENERAL, pg);
1004
1005 if (ret != 0) {
1006 if (ismember(scf_error(), errors_server)) {
1007 goto _get_restarter_inst_fail;
1008 } else switch (scf_error()) {
1009 case SCF_ERROR_NOT_FOUND:
1010 /* Assume default restarter. */
1011 break;
1012
1013 case SCF_ERROR_NOT_SET:
1014 case SCF_ERROR_HANDLE_MISMATCH:
1015 /*
1016 * If the arguments to the above functions
1017 * aren't derived from the same handle, there's
1018 * something wrong with the internal implementation,
1019 * not the public caller further up the chain.
1020 */
1021 case SCF_ERROR_INVALID_ARGUMENT:
1022 default:
1023 assert(0);
1024 abort();
1025 }
1026 } else {
1027 restarter = _scf_read_single_astring_from_pg(pg,
1028 SCF_PROPERTY_RESTARTER);
1029 /* zero length string is NOT a valid restarter */
1030 if (restarter != NULL && restarter[0] == '\0') {
1031 free(restarter);
1032 restarter = NULL;
1033 } else if (restarter == NULL) {
1034 if (ismember(scf_error(), errors_server)) {
1035 goto _get_restarter_inst_fail;
1036 } else switch (scf_error()) {
1037 case SCF_ERROR_NOT_FOUND:
1038 break;
1039
1040 case SCF_ERROR_CONSTRAINT_VIOLATED:
1041 case SCF_ERROR_TYPE_MISMATCH:
1042 (void) scf_set_error(
1043 SCF_ERROR_TEMPLATE_INVALID);
1044 goto _get_restarter_inst_fail;
1045
1046 case SCF_ERROR_NOT_SET:
1047 case SCF_ERROR_INVALID_ARGUMENT:
1048 default:
1049 assert(0);
1050 abort();
1051 }
1052 }
1053 }
1054
1055 if (restarter == NULL) {
1056 /* Use default restarter */
1057 restarter = strdup(SCF_SERVICE_STARTD);
1058 if (restarter == NULL) {
1059 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
1060 goto _get_restarter_inst_fail;
1061 }
1062 }
1063
1064 if (scf_handle_decode_fmri(h, restarter, NULL, NULL, ri, NULL, NULL,
1065 SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1066 if (ismember(scf_error(), errors_server)) {
1067 goto _get_restarter_inst_fail;
1068 } else switch (scf_error()) {
1069 case SCF_ERROR_CONSTRAINT_VIOLATED:
1070 case SCF_ERROR_INVALID_ARGUMENT:
1071 case SCF_ERROR_NOT_FOUND:
1072 goto _get_restarter_inst_fail;
1073
1074 case SCF_ERROR_HANDLE_MISMATCH:
1075 case SCF_ERROR_NOT_SET:
1076 default:
1077 assert(0);
1078 abort();
1079 }
1080 }
1081 free(restarter);
1082 scf_pg_destroy(pg);
1083
1084 return (ri);
1085
1086 _get_restarter_inst_fail:
1087 free(restarter);
1088 scf_instance_destroy(ri);
1089 scf_pg_destroy(pg);
1090 return (NULL);
1091 }
1092
1093 /*
1094 * Returns NULL on error, sets scf_error() to:
1095 * SCF_ERROR_BACKEND_ACCESS
1096 * SCF_ERROR_CONNECTION_BROKEN
1097 * SCF_ERROR_CONSTRAINT_VIOLATED
1098 * Restarter property has more than one value associated with it,
1099 * or FMRI does not meet restrictions in scf_handle_decode_fmri() flags.
1100 * SCF_ERROR_DELETED
1101 * SCF_ERROR_HANDLE_DESTROYED
1102 * SCF_ERROR_INTERNAL
1103 * SCF_ERROR_INVALID_ARGUMENT
1104 * The fmri argument in scf_handle_decode_fmri() is not a valid FMRI.
1105 * SCF_ERROR_NO_MEMORY
1106 * SCF_ERROR_NO_RESOURCES
1107 * SCF_ERROR_NOT_BOUND
1108 * SCF_ERROR_NOT_FOUND
1109 * Property doesn't exist or exists and has no value.
1110 */
1111 static scf_instance_t *
_get_global_inst(scf_handle_t * h)1112 _get_global_inst(scf_handle_t *h)
1113 {
1114 scf_instance_t *ri;
1115
1116 if ((ri = scf_instance_create(h)) == NULL) {
1117 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
1118 (void) scf_set_error(SCF_ERROR_NO_RESOURCES);
1119 return (NULL);
1120 }
1121
1122 if (scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, ri,
1123 NULL, NULL,
1124 SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1125 if (ismember(scf_error(), errors_server)) {
1126 scf_instance_destroy(ri);
1127 return (NULL);
1128 } else switch (scf_error()) {
1129 case SCF_ERROR_CONSTRAINT_VIOLATED:
1130 case SCF_ERROR_INVALID_ARGUMENT:
1131 case SCF_ERROR_NOT_FOUND:
1132 scf_instance_destroy(ri);
1133 return (NULL);
1134
1135 case SCF_ERROR_HANDLE_MISMATCH:
1136 case SCF_ERROR_NOT_SET:
1137 default:
1138 assert(0);
1139 abort();
1140 }
1141 }
1142
1143 return (ri);
1144 }
1145
1146 /*
1147 * Call the supplied function for each of the service or instance, the
1148 * service's restarter, and the globally defined template instance.
1149 * If the function returns SCF_WALK_ERROR, the walk is ended. If
1150 * the function returns SCF_WALK_NEXT, the next entity is tried.
1151 *
1152 * The function is only expected to return SCF_WALK_DONE if it has
1153 * found a property group match in the current entity, and has
1154 * populated p->pw_pg with the matching property group.
1155 *
1156 * The caller of _walk_template_instances() MUST check if the passed parameters
1157 * inst and svc match the fields pw_inst and pw_svc in the resulting
1158 * pg_tmpl_walk_t and call the destructor for the unmatching objects. The walker
1159 * may silently drop them if the template definition is in the restarter or in
1160 * the global instance.
1161 */
1162 static void
_walk_template_instances(scf_service_t * svc,scf_instance_t * inst,scf_snapshot_t * snap,walk_template_inst_func_t * func,pg_tmpl_walk_t * p,int flag)1163 _walk_template_instances(scf_service_t *svc, scf_instance_t *inst,
1164 scf_snapshot_t *snap, walk_template_inst_func_t *func,
1165 pg_tmpl_walk_t *p, int flag)
1166 {
1167 scf_instance_t *tmpl_inst = NULL;
1168 scf_handle_t *h;
1169 int ret;
1170 char *tg = NULL;
1171
1172 assert(svc != NULL || inst != NULL);
1173 assert(svc == NULL || inst == NULL);
1174
1175 if (inst != NULL)
1176 h = scf_instance_handle(inst);
1177 else
1178 h = scf_service_handle(svc);
1179 if (h == NULL)
1180 goto done;
1181
1182 /* First, use supplied service or instance */
1183 p->pw_target = SCF_TM_TARGET_THIS;
1184 ret = func(svc, inst, p);
1185 switch (ret) {
1186 case SCF_WALK_NEXT:
1187 break;
1188 case SCF_WALK_DONE:
1189 /*
1190 * Check that the template scoping matches and if not,
1191 * continue.
1192 */
1193 assert(p->pw_pg != NULL);
1194 tg = _scf_read_single_astring_from_pg(p->pw_pg,
1195 SCF_PROPERTY_TM_TARGET);
1196 if (tg == NULL || /* scf_error() was set */
1197 (strcmp(tg, SCF_TM_TARGET_INSTANCE) != 0 &&
1198 strcmp(tg, SCF_TM_TARGET_THIS) != 0 &&
1199 (flag & SCF_PG_TMPL_FLAG_EXACT) !=
1200 SCF_PG_TMPL_FLAG_EXACT)) {
1201 scf_pg_destroy(p->pw_pg);
1202 p->pw_pg = NULL;
1203 if (tg != NULL) {
1204 free(tg);
1205 tg = NULL;
1206 break;
1207 }
1208 }
1209 /*FALLTHROUGH*/
1210 case SCF_WALK_ERROR:
1211 goto done;
1212 /*NOTREACHED*/
1213 default:
1214 assert(0);
1215 abort();
1216 }
1217
1218 /* Next the restarter. */
1219 p->pw_target = SCF_TM_TARGET_DELEGATE;
1220 tmpl_inst = _get_restarter_inst(h, svc, inst, snap);
1221 if (tmpl_inst != NULL) {
1222 ret = func(NULL, tmpl_inst, p);
1223 switch (ret) {
1224 case SCF_WALK_NEXT:
1225 break;
1226 case SCF_WALK_DONE:
1227 /*
1228 * Check that the template scoping matches and if not,
1229 * continue.
1230 */
1231 assert(p->pw_pg != NULL);
1232 tg = _scf_read_single_astring_from_pg(p->pw_pg,
1233 SCF_PROPERTY_TM_TARGET);
1234 if (tg == NULL || /* scf_error() was set */
1235 strcmp(tg, SCF_TM_TARGET_DELEGATE) != 0) {
1236 scf_pg_destroy(p->pw_pg);
1237 p->pw_pg = NULL;
1238 if (tg != NULL) {
1239 free(tg);
1240 tg = NULL;
1241 break;
1242 }
1243 }
1244 /*FALLTHROUGH*/
1245 case SCF_WALK_ERROR:
1246 goto done;
1247 /*NOTREACHED*/
1248 default:
1249 assert(0);
1250 abort();
1251 }
1252 }
1253
1254 p->pw_target = SCF_TM_TARGET_ALL;
1255 scf_instance_destroy(tmpl_inst);
1256 tmpl_inst = _get_global_inst(h);
1257 if (tmpl_inst != NULL) {
1258 ret = func(NULL, tmpl_inst, p);
1259 switch (ret) {
1260 case SCF_WALK_NEXT:
1261 break;
1262 case SCF_WALK_DONE:
1263 /*
1264 * Check that the template scoping matches and if not,
1265 * continue.
1266 */
1267 assert(p->pw_pg != NULL);
1268 tg = _scf_read_single_astring_from_pg(p->pw_pg,
1269 SCF_PROPERTY_TM_TARGET);
1270 if (tg == NULL || /* scf_error() was set */
1271 strcmp(tg, SCF_TM_TARGET_ALL) != 0) {
1272 scf_pg_destroy(p->pw_pg);
1273 p->pw_pg = NULL;
1274 if (tg != NULL) {
1275 free(tg);
1276 tg = NULL;
1277 break;
1278 }
1279 }
1280 /*FALLTHROUGH*/
1281 case SCF_WALK_ERROR:
1282 goto done;
1283 /*NOTREACHED*/
1284 default:
1285 assert(0);
1286 abort();
1287 }
1288 }
1289
1290 done:
1291 free(tg);
1292 if (ret != SCF_WALK_DONE)
1293 scf_instance_destroy(tmpl_inst);
1294 p->pw_target = NULL;
1295 }
1296
1297 /*
1298 * _get_pg() returns 0 on success and -1 on failure. Sets scf_error()
1299 * on failure.
1300 * SCF_ERROR_BACKEND_ACCESS
1301 * SCF_ERROR_CONNECTION_BROKEN
1302 * SCF_ERROR_DELETED
1303 * SCF_ERROR_HANDLE_MISMATCH
1304 * SCF_ERROR_INTERNAL
1305 * SCF_ERROR_INVALID_ARGUMENT
1306 * name is not a valid property group.
1307 * SCF_ERROR_NO_RESOURCES
1308 * SCF_ERROR_NOT_BOUND
1309 * SCF_ERROR_NOT_FOUND
1310 * SCF_ERROR_NOT_SET
1311 */
1312 static int
_get_pg(scf_service_t * svc,scf_instance_t * inst,const scf_snapshot_t * snap,const char * name,scf_propertygroup_t * pg)1313 _get_pg(scf_service_t *svc, scf_instance_t *inst,
1314 const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
1315 {
1316 int ret;
1317
1318 assert(svc != NULL || inst != NULL);
1319 assert(svc == NULL || inst == NULL);
1320 assert(pg != NULL);
1321
1322 if (inst != NULL)
1323 ret = scf_instance_get_pg_composed(inst, snap, name, pg);
1324 else
1325 ret = scf_service_get_pg(svc, name, pg);
1326
1327 return (ret);
1328 }
1329
1330 /*
1331 * Returns SCF_WALK_NEXT for not found, SCF_WALK_ERROR for error,
1332 * and SCF_WALK_DONE for found.
1333 * On error, destroy pg and set it to NULL.
1334 *
1335 * Sets scf_error() if SCF_WALK_ERROR is returned to _BACKEND_ACCESS,
1336 * _CONNECTION_BROKEN, _INTERNAL, _INVALID_ARGUMENT (name is not a
1337 * valid property group), _NO_RESOURCES, or _NOT_BOUND.
1338 */
1339 static int
_lookup_pg(scf_service_t * svc,scf_instance_t * inst,const scf_snapshot_t * snap,const char * name,scf_propertygroup_t * pg)1340 _lookup_pg(scf_service_t *svc, scf_instance_t *inst,
1341 const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
1342 {
1343 int ret;
1344
1345 ret = _get_pg(svc, inst, snap, name, pg);
1346
1347 if (ret == 0) {
1348 return (SCF_WALK_DONE);
1349 } else {
1350 switch (scf_error()) {
1351 case SCF_ERROR_NOT_FOUND:
1352 case SCF_ERROR_DELETED:
1353 return (SCF_WALK_NEXT);
1354
1355 case SCF_ERROR_BACKEND_ACCESS:
1356 case SCF_ERROR_CONNECTION_BROKEN:
1357 case SCF_ERROR_INTERNAL:
1358 case SCF_ERROR_INVALID_ARGUMENT:
1359 case SCF_ERROR_NOT_BOUND:
1360 case SCF_ERROR_NO_RESOURCES:
1361 scf_pg_destroy(pg);
1362 pg = NULL;
1363 return (SCF_WALK_ERROR);
1364
1365 case SCF_ERROR_NOT_SET:
1366 case SCF_ERROR_HANDLE_MISMATCH:
1367 default:
1368 assert(0);
1369 abort();
1370 }
1371 }
1372
1373 /*NOTREACHED*/
1374 }
1375
1376 /*
1377 * If match, return 0. If no match, return 1. If error, return -1.
1378 * On error set scf_error() to _BACKEND_ACCESS, _CONNECTION_BROKEN,
1379 * _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY, _NO_RESOURCES, _NOT_BOUND,
1380 * _NOT_SET (property group specified by pg is not set), _PERMISSION_DENIED,
1381 * or _TEMPLATE_INVALID (target property is not SCF_TYPE_ASTRING or has
1382 * more than one value).
1383 */
1384 static int
check_target_match(scf_propertygroup_t * pg,const char * target)1385 check_target_match(scf_propertygroup_t *pg, const char *target)
1386 {
1387 char *pg_target;
1388 int ret = 0;
1389
1390 pg_target = _scf_read_single_astring_from_pg(pg,
1391 SCF_PROPERTY_TM_TARGET);
1392 if (pg_target == NULL) {
1393 switch (scf_error()) {
1394 case SCF_ERROR_DELETED:
1395 case SCF_ERROR_NOT_FOUND:
1396 return (1);
1397
1398 case SCF_ERROR_CONSTRAINT_VIOLATED:
1399 case SCF_ERROR_TYPE_MISMATCH:
1400 (void) scf_set_error(
1401 SCF_ERROR_TEMPLATE_INVALID);
1402 /*FALLTHROUGH*/
1403
1404 case SCF_ERROR_BACKEND_ACCESS:
1405 case SCF_ERROR_CONNECTION_BROKEN:
1406 case SCF_ERROR_HANDLE_DESTROYED:
1407 case SCF_ERROR_INTERNAL:
1408 case SCF_ERROR_NO_RESOURCES:
1409 case SCF_ERROR_NOT_BOUND:
1410 case SCF_ERROR_PERMISSION_DENIED:
1411 return (-1);
1412
1413 case SCF_ERROR_NOT_SET:
1414 case SCF_ERROR_INVALID_ARGUMENT:
1415 default:
1416 assert(0);
1417 abort();
1418 }
1419 /*NOTREACHED*/
1420 }
1421
1422 /* For a desired target of 'this', check for 'this' and 'instance'. */
1423 if ((strcmp(target, SCF_TM_TARGET_INSTANCE) == 0 ||
1424 strcmp(target, SCF_TM_TARGET_THIS) == 0) &&
1425 (strcmp(pg_target, SCF_TM_TARGET_INSTANCE) == 0 ||
1426 strcmp(pg_target, SCF_TM_TARGET_THIS) == 0)) {
1427 goto cleanup;
1428 }
1429
1430 if (strcmp(target, SCF_TM_TARGET_DELEGATE) == 0 &&
1431 strcmp(pg_target, SCF_TM_TARGET_DELEGATE) == 0) {
1432 goto cleanup;
1433 }
1434
1435 if (strcmp(target, SCF_TM_TARGET_ALL) == 0 &&
1436 strcmp(pg_target, SCF_TM_TARGET_ALL) == 0) {
1437 goto cleanup;
1438 }
1439
1440 ret = 1;
1441 cleanup:
1442 free(pg_target);
1443 return (ret);
1444 }
1445
1446 /*
1447 * Check if a matching template property group exists for each of:
1448 * name and type, name only, type only, and completely wildcarded
1449 * template.
1450 *
1451 * Both pg_name and pg_type are optional.
1452 *
1453 * Returns NULL on failure, sets scf_error():
1454 * SCF_ERROR_BACKEND_ACCESS
1455 * SCF_ERROR_CONNECTION_BROKEN
1456 * SCF_ERROR_DELETED
1457 * SCF_ERROR_HANDLE_DESTROYED
1458 * SCF_ERROR_INTERNAL
1459 * SCF_ERROR_INVALID_ARGUMENT
1460 * can't combine the _tmpl_pg_name arguments and get a reasonable
1461 * length name, or pg_name is not a valid property group.
1462 * SCF_ERROR_NO_MEMORY
1463 * SCF_ERROR_NO_RESOURCES
1464 * SCF_ERROR_NOT_BOUND
1465 * SCF_ERROR_NOT_FOUND
1466 * Property doesn't exist or exists and has no value.
1467 * SCF_ERROR_PERMISSION_DENIED
1468 * SCF_ERROR_TEMPLATE_INVALID
1469 * target property is not SCF_TYPE_ASTRING or has more than one value.
1470 */
1471 static scf_propertygroup_t *
_find_template_pg_match(scf_service_t * svc,scf_instance_t * inst,const scf_snapshot_t * snap,const char * pg_name,const char * pg_type,const char * target,char ** tmpl_pg_name)1472 _find_template_pg_match(scf_service_t *svc, scf_instance_t *inst,
1473 const scf_snapshot_t *snap, const char *pg_name, const char *pg_type,
1474 const char *target, char **tmpl_pg_name)
1475 {
1476 int ret, r;
1477 scf_propertygroup_t *pg = NULL;
1478 scf_handle_t *h;
1479 scf_iter_t *iter;
1480 char *name, *type;
1481
1482 assert(inst != NULL || svc != NULL);
1483 assert(inst == NULL || svc == NULL);
1484
1485 if (inst != NULL)
1486 h = scf_instance_handle(inst);
1487 else
1488 h = scf_service_handle(svc);
1489 if (h == NULL) {
1490 return (NULL);
1491 }
1492
1493 if ((pg = scf_pg_create(h)) == NULL ||
1494 (iter = scf_iter_create(h)) == NULL) {
1495 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
1496 scf_pg_destroy(pg);
1497 return (NULL);
1498 }
1499
1500 /*
1501 * We're going to walk through the possible pg templates that
1502 * could match the supplied name and type. We do this
1503 * by explicit name lookups when possible to avoid having to
1504 * keep track of a most-explicit-match during iteration.
1505 */
1506
1507 /* First look for a template with name and type set and matching. */
1508 *tmpl_pg_name = _tmpl_pg_name(pg_name, pg_type, 1);
1509 if (*tmpl_pg_name == NULL)
1510 goto fail;
1511 ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1512 if (ret != SCF_WALK_NEXT) {
1513 if (pg != NULL) {
1514 if ((r = check_target_match(pg, target)) == 0)
1515 goto done;
1516 else if (r == -1)
1517 goto fail;
1518 } else {
1519 goto done;
1520 }
1521 }
1522 free(*tmpl_pg_name);
1523
1524 /*
1525 * Need to search on a name-only match before searching on
1526 * type matches.
1527 */
1528
1529 *tmpl_pg_name = _tmpl_pg_name(pg_name, NULL, 0);
1530 if (*tmpl_pg_name == NULL)
1531 goto fail;
1532 ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1533 if (ret != SCF_WALK_NEXT) {
1534 if (pg != NULL) {
1535 if ((r = check_target_match(pg, target)) == 0)
1536 goto done;
1537 else if (r == -1)
1538 goto fail;
1539 } else {
1540 goto done;
1541 }
1542 }
1543 free(*tmpl_pg_name);
1544
1545 /* Next, see if there's an "nt" template where the type matches. */
1546 if (pg_type != NULL && pg_name == NULL) {
1547 if (inst != NULL)
1548 ret = scf_iter_instance_pgs_typed_composed(iter, inst,
1549 snap, SCF_GROUP_TEMPLATE_PG_PATTERN);
1550 else
1551 ret = scf_iter_service_pgs_typed(iter, svc,
1552 SCF_GROUP_TEMPLATE_PG_PATTERN);
1553
1554 if (ret != 0) {
1555 if (ismember(scf_error(), errors_server)) {
1556 goto fail;
1557 } else {
1558 assert(0);
1559 abort();
1560 }
1561 }
1562
1563 while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
1564 /* Make sure this is a name and type specified pg. */
1565 name = _scf_read_single_astring_from_pg(pg,
1566 SCF_PROPERTY_TM_NAME);
1567 if (name == NULL)
1568 continue;
1569 type = _scf_read_single_astring_from_pg(pg,
1570 SCF_PROPERTY_TM_TYPE);
1571 if (type == NULL) {
1572 free(name);
1573 continue;
1574 }
1575 if (strcmp(pg_type, type) == 0 &&
1576 check_target_match(pg, target) == 0) {
1577 *tmpl_pg_name = name;
1578 free(type);
1579 goto done;
1580 }
1581 free(type);
1582 free(name);
1583 }
1584 if (ret == -1) {
1585 if (ismember(scf_error(), errors_server)) {
1586 goto fail;
1587 } else {
1588 assert(0);
1589 abort();
1590 }
1591 }
1592 }
1593
1594 *tmpl_pg_name = _tmpl_pg_name(NULL, pg_type, 0);
1595 if (*tmpl_pg_name == NULL)
1596 goto fail;
1597 ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1598 if (ret != SCF_WALK_NEXT) {
1599 if (pg != NULL) {
1600 if ((r = check_target_match(pg, target)) == 0)
1601 goto done;
1602 else if (r == -1)
1603 goto fail;
1604 } else {
1605 goto done;
1606 }
1607 }
1608 free(*tmpl_pg_name);
1609
1610 *tmpl_pg_name = _tmpl_pg_name(NULL, NULL, 0);
1611 if (*tmpl_pg_name == NULL)
1612 goto fail;
1613 ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1614 if (ret != SCF_WALK_NEXT) {
1615 if (pg != NULL) {
1616 if ((r = check_target_match(pg, target)) == 0)
1617 goto done;
1618 else if (r == -1)
1619 goto fail;
1620 } else {
1621 goto done;
1622 }
1623 }
1624
1625 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
1626 fail:
1627 scf_pg_destroy(pg);
1628 if (*tmpl_pg_name != NULL)
1629 free(*tmpl_pg_name);
1630 *tmpl_pg_name = NULL;
1631 pg = NULL;
1632 done:
1633 if (ret == SCF_WALK_ERROR)
1634 free(*tmpl_pg_name);
1635 scf_iter_destroy(iter);
1636 return (pg);
1637 }
1638
1639 /*
1640 * Finds the pg match in either the supplied service or instance.
1641 * Returns SCF_WALK_ERROR, SCF_WALK_NEXT, or SCF_WALK_DONE.
1642 * If returning SCF_WALK_ERROR, sets scf_error():
1643 * SCF_ERROR_BACKEND_ACCESS
1644 * SCF_ERROR_CONNECTION_BROKEN
1645 * SCF_ERROR_DELETED
1646 * SCF_ERROR_HANDLE_DESTROYED
1647 * SCF_ERROR_INTERNAL
1648 * SCF_ERROR_INVALID_ARGUMENT
1649 * The snaphot is not a valid snapshot name,
1650 * or can't create a reasonable property group template name.
1651 * SCF_ERROR_NO_MEMORY
1652 * SCF_ERROR_NO_RESOURCES
1653 * SCF_ERROR_NOT_BOUND
1654 * SCF_ERROR_NOT_FOUND
1655 * Property doesn't exist or exists and has no value.
1656 * SCF_ERROR_PERMISSION_DENIED
1657 * SCF_ERROR_TEMPLATE_INVALID
1658 * target property is not SCF_TYPE_ASTRING or has more than one value.
1659 */
1660 static int
find_pg_match(scf_service_t * svc,scf_instance_t * inst,pg_tmpl_walk_t * p)1661 find_pg_match(scf_service_t *svc, scf_instance_t *inst, pg_tmpl_walk_t *p)
1662 {
1663 scf_snapshot_t *tmpl_snap = NULL;
1664 scf_propertygroup_t *pg;
1665 scf_handle_t *h;
1666 char *tmpl_pg_name;
1667
1668 assert(svc != NULL || inst != NULL);
1669 assert(svc == NULL || inst == NULL);
1670
1671 if (inst != NULL)
1672 h = scf_instance_handle(inst);
1673 else
1674 h = scf_service_handle(svc);
1675 if (h == NULL)
1676 return (SCF_WALK_ERROR);
1677
1678 if (p->pw_snapname != NULL) {
1679 if (_get_snapshot(inst, p->pw_snapname, &tmpl_snap) == -1)
1680 return (SCF_WALK_ERROR);
1681 }
1682 pg = _find_template_pg_match(svc, inst, tmpl_snap, p->pw_pgname,
1683 p->pw_pgtype, p->pw_target, &tmpl_pg_name);
1684
1685 if (pg != NULL) {
1686 p->pw_snap = tmpl_snap;
1687 p->pw_pg = pg;
1688 p->pw_tmpl_pgname = tmpl_pg_name;
1689 p->pw_inst = inst;
1690 p->pw_svc = svc;
1691 return (SCF_WALK_DONE);
1692 }
1693
1694 scf_snapshot_destroy(tmpl_snap);
1695 return (SCF_WALK_NEXT);
1696 }
1697
1698 /*
1699 * return 0 on success and -1 on failure.
1700 * SCF_ERROR_CONNECTION_BROKEN
1701 * SCF_ERROR_DELETED
1702 * SCF_ERROR_HANDLE_DESTROYED
1703 * SCF_ERROR_HANDLE_MISMATCH
1704 * SCF_ERROR_INTERNAL
1705 * SCF_ERROR_INVALID_ARGUMENT
1706 * FMRI argument, snapshot name, pg_name, or pg is invalid.
1707 * SCF_ERROR_NO_MEMORY
1708 * SCF_ERROR_NO_RESOURCES
1709 * SCF_ERROR_NOT_BOUND
1710 * SCF_ERROR_NOT_FOUND
1711 * SCF_ERROR_NOT_SET
1712 */
1713 int
scf_tmpl_get_by_pg(scf_propertygroup_t * pg,scf_pg_tmpl_t * pg_tmpl,int flags)1714 scf_tmpl_get_by_pg(scf_propertygroup_t *pg, scf_pg_tmpl_t *pg_tmpl, int flags)
1715 {
1716 char *fmribuf = NULL, *snapbuf = NULL, *pg_name = NULL, *pg_type = NULL;
1717 int ret;
1718 ssize_t fbufsz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
1719 ssize_t nbufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
1720 ssize_t tbufsz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
1721 scf_instance_t *inst = NULL;
1722 scf_snaplevel_t *snaplvl = NULL;
1723 scf_service_t *svc = NULL;
1724 scf_handle_t *h;
1725 scf_snapshot_t *snap = NULL;
1726 pg_tmpl_walk_t *p = NULL;
1727
1728 assert(fbufsz != 0 && nbufsz != 0 && tbufsz != 0);
1729
1730 scf_tmpl_pg_reset(pg_tmpl);
1731
1732 if ((h = scf_pg_handle(pg)) == NULL)
1733 return (-1);
1734
1735 if ((inst = scf_instance_create(h)) == NULL ||
1736 (svc = scf_service_create(h)) == NULL ||
1737 (snaplvl = scf_snaplevel_create(h)) == NULL) {
1738 goto fail;
1739 }
1740
1741 if ((fmribuf = malloc(fbufsz)) == NULL ||
1742 (pg_name = malloc(nbufsz)) == NULL ||
1743 (pg_type = malloc(tbufsz)) == NULL ||
1744 (p = calloc(1, sizeof (pg_tmpl_walk_t))) == NULL) {
1745 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
1746 goto fail;
1747 }
1748
1749 if (scf_pg_get_name(pg, pg_name, nbufsz) < 0) {
1750 goto fail;
1751 }
1752
1753 if (scf_pg_get_type(pg, pg_type, tbufsz) < 0) {
1754 goto fail;
1755 }
1756 p->pw_pgname = pg_name;
1757 p->pw_pgtype = pg_type;
1758
1759 ret = scf_pg_get_parent_snaplevel(pg, snaplvl);
1760 if (ret == -1) {
1761 switch (scf_error()) {
1762 case SCF_ERROR_CONSTRAINT_VIOLATED:
1763 /* Parent type doesn't match. Keep looking. */
1764 break;
1765
1766 case SCF_ERROR_DELETED:
1767 case SCF_ERROR_NOT_BOUND:
1768 case SCF_ERROR_NOT_SET:
1769 /* Pass these back to the caller. */
1770 goto fail;
1771
1772 case SCF_ERROR_HANDLE_MISMATCH:
1773 default:
1774 assert(0);
1775 abort();
1776 }
1777
1778 /*
1779 * No snapshot. We'll use 'editing' by default since
1780 * snap and snapbuf are NULL.
1781 */
1782 p->pw_snapname = NULL;
1783
1784 } else {
1785 if ((snap = scf_snapshot_create(h)) == NULL) {
1786 goto fail;
1787 }
1788
1789 ret = scf_snaplevel_get_parent(snaplvl, snap);
1790 if (ret == -1) {
1791 if (ismember(scf_error(), errors_server)) {
1792 goto fail;
1793 } else {
1794 assert(0);
1795 abort();
1796 }
1797 }
1798
1799 /* Grab snapshot name while we're here. */
1800 if ((snapbuf = malloc(nbufsz)) == NULL) {
1801 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
1802 goto fail;
1803 }
1804 if (scf_snapshot_get_name(snap, snapbuf, nbufsz) < 0) {
1805 if (ismember(scf_error(), errors_server)) {
1806 goto fail;
1807 } else {
1808 assert(0);
1809 abort();
1810 }
1811 }
1812 p->pw_snapname = snapbuf;
1813
1814 ret = scf_snapshot_get_parent(snap, inst);
1815 if (ret == -1) {
1816 if (ismember(scf_error(), errors_server)) {
1817 goto fail;
1818 } else {
1819 assert(0);
1820 abort();
1821 }
1822 }
1823
1824 _walk_template_instances(NULL, inst, snap,
1825 (walk_template_inst_func_t *)find_pg_match, p, flags);
1826 }
1827
1828 /* No snapshot parent. Go looking for instance parent. */
1829 if (snapbuf == NULL) {
1830 /* First look for instance parent. */
1831 ret = scf_pg_get_parent_instance(pg, inst);
1832 if (ret == 0) {
1833 _walk_template_instances(NULL, inst, snap,
1834 (walk_template_inst_func_t *)find_pg_match,
1835 p, flags);
1836 /* OK, check for service parent */
1837 } else if (ret == -1 &&
1838 scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
1839 ret = scf_pg_get_parent_service(pg, svc);
1840 if (ret == 0) {
1841 _walk_template_instances(svc, NULL, snap,
1842 (walk_template_inst_func_t *)find_pg_match,
1843 p, flags);
1844 } else {
1845 switch (scf_error()) {
1846 case SCF_ERROR_CONSTRAINT_VIOLATED:
1847 (void) scf_set_error(
1848 SCF_ERROR_NOT_FOUND);
1849 /*FALLTHROUGH*/
1850
1851 case SCF_ERROR_CONNECTION_BROKEN:
1852 case SCF_ERROR_DELETED:
1853 case SCF_ERROR_HANDLE_MISMATCH:
1854 case SCF_ERROR_NOT_BOUND:
1855 case SCF_ERROR_NOT_SET:
1856 goto fail;
1857
1858 default:
1859 assert(0);
1860 abort();
1861 }
1862 }
1863 } else {
1864 goto fail;
1865 }
1866 }
1867
1868 if (p->pw_pg != NULL) {
1869 pg_tmpl->pt_h = h;
1870 pg_tmpl->pt_pg = p->pw_pg;
1871 pg_tmpl->pt_inst = p->pw_inst;
1872 /* we may get a different instance back */
1873 if (p->pw_inst != inst)
1874 scf_instance_destroy(inst);
1875 pg_tmpl->pt_snap = p->pw_snap;
1876 pg_tmpl->pt_svc = p->pw_svc;
1877 /* we may get a different service back */
1878 if (p->pw_svc != svc)
1879 scf_service_destroy(svc);
1880 pg_tmpl->pt_populated = 1;
1881 free(p->pw_tmpl_pgname);
1882 ret = 0;
1883 goto done;
1884 }
1885
1886 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
1887
1888 fail:
1889 ret = -1;
1890 scf_instance_destroy(inst);
1891 scf_service_destroy(svc);
1892 done:
1893 scf_snapshot_destroy(snap);
1894 free(snapbuf);
1895 free(fmribuf);
1896 free(pg_name);
1897 free(pg_type);
1898 free(p);
1899 scf_snaplevel_destroy(snaplvl);
1900 return (ret);
1901 }
1902
1903 /*
1904 * int scf_tmpl_get_by_pg_name()
1905 *
1906 * Get a template by a combination of the name and type. Either name
1907 * or type can be null, which indicates a wildcard. flags may be
1908 * SCF_PG_TMPL_FLAG_CURRENT (use current properties rather than
1909 * the defined or running snapshot), and SCF_PG_TMPL_FLAG_EXACT (match
1910 * only templates defined by the FMRI in question, not by its restarter
1911 * or globally). Returns 0 on success and -1 on error, and sets
1912 * scf_error() to:
1913 * SCF_ERROR_BACKEND_ACCESS
1914 * SCF_ERROR_CONNECTION_BROKEN
1915 * The connection to the repository was lost.
1916 * SCF_ERROR_DELETED
1917 * The instance has been deleted.
1918 * SCF_ERROR_HANDLE_DESTROYED
1919 * SCF_ERROR_INTERNAL
1920 * SCF_ERROR_INVALID_ARGUMENT
1921 * FMRI isn't valid, pg_name is too long to look for a template, or
1922 * snapshot specified isn't a valid name
1923 * SCF_ERROR_NO_MEMORY
1924 * SCF_ERROR_NO_RESOURCES
1925 * The server does not have adequate resources to complete the request.
1926 * SCF_ERROR_NOT_BOUND
1927 * The handle is not currently bound.
1928 * SCF_ERROR_NOT_FOUND
1929 * Object matching FMRI doesn't exist in the repository, or snapshot
1930 * doesn't exist.
1931 */
1932 int
scf_tmpl_get_by_pg_name(const char * fmri,const char * snapshot,const char * pg_name,const char * pg_type,scf_pg_tmpl_t * pg_tmpl,int flags)1933 scf_tmpl_get_by_pg_name(const char *fmri, const char *snapshot,
1934 const char *pg_name, const char *pg_type, scf_pg_tmpl_t *pg_tmpl, int flags)
1935 {
1936 scf_instance_t *inst = NULL;
1937 scf_service_t *svc = NULL;
1938 scf_snapshot_t *snap = NULL;
1939 pg_tmpl_walk_t *p = NULL;
1940 scf_handle_t *h;
1941 int ret;
1942
1943 assert(pg_tmpl != NULL);
1944 h = pg_tmpl->pt_h;
1945 assert(h != NULL);
1946
1947 scf_tmpl_pg_reset(pg_tmpl);
1948
1949 if ((inst = scf_instance_create(h)) == NULL ||
1950 (svc = scf_service_create(h)) == NULL) {
1951 goto fail;
1952 }
1953
1954 p = calloc(1, sizeof (pg_tmpl_walk_t));
1955 if (p == NULL) {
1956 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
1957 goto fail;
1958 }
1959
1960 ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
1961 NULL, SCF_DECODE_FMRI_EXACT);
1962 if (ret == 0) {
1963 scf_service_destroy(svc);
1964 svc = NULL;
1965 } else if (ret != 0 &&
1966 scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
1967 ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
1968 NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
1969 if (ret == 0) {
1970 scf_instance_destroy(inst);
1971 inst = NULL;
1972 }
1973 }
1974 if (ret != 0) {
1975 if (ismember(scf_error(), errors_server)) {
1976 goto fail;
1977 } else switch (scf_error()) {
1978 case SCF_ERROR_CONSTRAINT_VIOLATED:
1979 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1980 goto fail;
1981
1982 case SCF_ERROR_INVALID_ARGUMENT:
1983 case SCF_ERROR_NOT_FOUND:
1984 goto fail;
1985
1986 case SCF_ERROR_HANDLE_MISMATCH:
1987 case SCF_ERROR_NOT_SET:
1988 default:
1989 assert(0);
1990 abort();
1991 }
1992 }
1993
1994 assert(svc == NULL || inst == NULL);
1995 assert(svc != NULL || inst != NULL);
1996
1997 /* If we have a service fmri, snapshot is ignored. */
1998 if (inst != NULL) {
1999 if (snapshot == NULL || strcmp(snapshot, "running") == 0 ||
2000 (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2001 SCF_PG_TMPL_FLAG_CURRENT) {
2002 if (_get_snapshot(inst, NULL, &snap) == -1)
2003 goto fail;
2004 } else {
2005 if (_get_snapshot(inst, snapshot, &snap) == -1) {
2006 goto fail;
2007 } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2008 goto fail;
2009 }
2010 }
2011 }
2012
2013 p->pw_snapname = snapshot;
2014 p->pw_pgname = pg_name;
2015 p->pw_pgtype = pg_type;
2016
2017 /*
2018 * For each of instance, restarter, global
2019 * - check for a tm_pg_pattern_nt_<name> matching type
2020 * - check for a tm_pg_pattern_t_<type> matching type
2021 * - check for any tm_pg_pattern_
2022 * Currently plan to return the most specific match only.
2023 */
2024 _walk_template_instances(svc, inst, snap,
2025 (walk_template_inst_func_t *)find_pg_match, p, flags);
2026
2027 if (p->pw_pg != NULL) {
2028 pg_tmpl->pt_h = h;
2029 pg_tmpl->pt_pg = p->pw_pg;
2030 pg_tmpl->pt_inst = p->pw_inst;
2031 /* we may get a different instance back */
2032 if (p->pw_inst != inst)
2033 scf_instance_destroy(inst);
2034 pg_tmpl->pt_snap = p->pw_snap;
2035 pg_tmpl->pt_svc = p->pw_svc;
2036 /* we may get a different service back */
2037 if (p->pw_svc != svc)
2038 scf_service_destroy(svc);
2039 pg_tmpl->pt_populated = 1;
2040 scf_snapshot_destroy(snap);
2041 free(p->pw_tmpl_pgname);
2042 free(p);
2043 return (0);
2044 }
2045
2046 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2047 fail:
2048 free(p);
2049 scf_instance_destroy(inst);
2050 scf_service_destroy(svc);
2051 scf_snapshot_destroy(snap);
2052 return (-1);
2053 }
2054
2055 /*
2056 * Returns NULL on failure, sets scf_error() to _CONNECTION_BROKEN,
2057 * _DELETED, _NO_RESOURCES, or _NOT_BOUND.
2058 */
2059 static scf_iter_t *
_get_svc_or_inst_iter(scf_handle_t * h,scf_pg_tmpl_t * t)2060 _get_svc_or_inst_iter(scf_handle_t *h, scf_pg_tmpl_t *t)
2061 {
2062 scf_iter_t *iter;
2063 int ret;
2064
2065 assert(t->pt_svc != NULL || t->pt_inst != NULL);
2066 assert(t->pt_svc == NULL || t->pt_inst == NULL);
2067
2068 if ((iter = scf_iter_create(h)) == NULL) {
2069 return (NULL);
2070 }
2071
2072 /* Iterate on property groups of type template_pg_pattern */
2073
2074 if (t->pt_inst != NULL)
2075 ret = scf_iter_instance_pgs_typed_composed(iter,
2076 t->pt_inst, t->pt_snap,
2077 SCF_GROUP_TEMPLATE_PG_PATTERN);
2078 if (t->pt_svc != NULL)
2079 ret = scf_iter_service_pgs_typed(iter, t->pt_svc,
2080 SCF_GROUP_TEMPLATE_PG_PATTERN);
2081
2082 if (ret != 0) {
2083 if (ismember(scf_error(), errors_server)) {
2084 scf_iter_destroy(iter);
2085 return (NULL);
2086 } else {
2087 assert(0);
2088 abort();
2089 }
2090 }
2091
2092 return (iter);
2093 }
2094
2095 /*
2096 * Returns NULL on failure, sets scf_error() to:
2097 * SCF_ERROR_BACKEND_ACCESS
2098 * SCF_ERROR_CONNECTION_BROKEN
2099 * SCF_ERROR_DELETED
2100 * SCF_HANDLE_DESTROYED
2101 * SCF_ERROR_INTERNAL
2102 * SCF_ERROR_INVALID_ARGUMENT
2103 * Handle argument is NULL, or snaphot is not a valid snapshot name.
2104 * SCF_ERROR_NO_MEMORY
2105 * SCF_ERROR_NO_RESOURCES
2106 * SCF_ERROR_NOT_BOUND
2107 * SCF_ERROR_NOT_FOUND
2108 */
2109 static scf_iter_t *
_get_next_iterator(scf_handle_t * h,scf_pg_tmpl_t * t,const char * snapshot,int exact)2110 _get_next_iterator(scf_handle_t *h, scf_pg_tmpl_t *t, const char *snapshot,
2111 int exact)
2112 {
2113 scf_iter_t *iter = NULL;
2114 ssize_t limit;
2115
2116 limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2117 assert(limit != 0);
2118
2119 /*
2120 * Check what level we last iterated on: none, service,
2121 * restarter, or global. Make sure that if one in the middle
2122 * doesn't exist, we move on to the next entity.
2123 *
2124 * Before we drop any references to pt_inst or pt_svc we must
2125 * destroy them so we don't leak them.
2126 */
2127 do {
2128 switch (t->pt_iter_last) {
2129 case SCF__TMPL_ITER_NONE:
2130 t->pt_iter_last = SCF__TMPL_ITER_INST;
2131 if (t->pt_inst != t->pt_orig_inst)
2132 scf_instance_destroy(t->pt_inst);
2133 t->pt_inst = t->pt_orig_inst;
2134 if (t->pt_svc != t->pt_orig_svc)
2135 scf_service_destroy(t->pt_svc);
2136 t->pt_svc = t->pt_orig_svc;
2137 break;
2138
2139 case SCF__TMPL_ITER_INST:
2140 /*
2141 * Don't go any further than the specified instance
2142 * if exact was set.
2143 */
2144 if (exact == 1) {
2145 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2146 goto fail;
2147 }
2148 t->pt_iter_last = SCF__TMPL_ITER_RESTARTER;
2149 if (t->pt_inst != t->pt_orig_inst)
2150 scf_instance_destroy(t->pt_inst);
2151 t->pt_inst = _get_restarter_inst(h, t->pt_orig_svc,
2152 t->pt_orig_inst, t->pt_snap);
2153 scf_service_destroy(t->pt_svc);
2154 t->pt_svc = NULL;
2155 break;
2156
2157 case SCF__TMPL_ITER_RESTARTER:
2158 t->pt_iter_last = SCF__TMPL_ITER_GLOBAL;
2159 if (t->pt_inst != t->pt_orig_inst)
2160 scf_instance_destroy(t->pt_inst);
2161 t->pt_inst = _get_global_inst(h);
2162 scf_service_destroy(t->pt_svc);
2163 t->pt_svc = NULL;
2164 break;
2165
2166 case SCF__TMPL_ITER_GLOBAL:
2167 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2168 return (NULL);
2169
2170 default:
2171 assert(0);
2172 abort();
2173 }
2174 } while (t->pt_inst == NULL && t->pt_svc == NULL);
2175
2176 /* Set pt_snap to the snapshot for this instance */
2177 if (t->pt_inst != NULL) {
2178 scf_snapshot_destroy(t->pt_snap);
2179 if (_get_snapshot(t->pt_inst, snapshot,
2180 &t->pt_snap) == -1)
2181 goto fail;
2182 }
2183
2184
2185 iter = _get_svc_or_inst_iter(h, t);
2186 fail:
2187 return (iter);
2188 }
2189
2190 /*
2191 * scf_pg_tmpl_t *scf_tmpl_pg_create(scf_handle_t *)
2192 *
2193 * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT
2194 * or _NO_MEMORY.
2195 */
2196 scf_pg_tmpl_t *
scf_tmpl_pg_create(scf_handle_t * handle)2197 scf_tmpl_pg_create(scf_handle_t *handle)
2198 {
2199 scf_pg_tmpl_t *pg_tmpl = NULL;
2200
2201 if (handle == NULL) {
2202 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2203 return (NULL);
2204 }
2205 pg_tmpl = calloc(1, sizeof (scf_pg_tmpl_t));
2206 if (pg_tmpl == NULL)
2207 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2208 else
2209 pg_tmpl->pt_h = handle;
2210
2211 return (pg_tmpl);
2212 }
2213
2214 /*
2215 * Retrieves name or type of a template pg.
2216 * Returns -1 on failure. Sets scf_error():
2217 * SCF_ERROR_BACKEND_ACCESS
2218 * SCF_ERROR_CONNECTION_BROKEN
2219 * SCF_ERROR_DELETED
2220 * SCF_ERROR_HANDLE_DESTROYED
2221 * SCF_ERROR_INTERNAL
2222 * SCF_ERROR_NO_MEMORY
2223 * SCF_ERROR_NO_RESOURCES
2224 * SCF_ERROR_NOT_BOUND
2225 * SCF_ERROR_PERMISSION_DENIED
2226 * SCF_ERROR_TEMPLATE_INVALID
2227 * pname property is not SCF_TYPE_ASTRING or has more than one value.
2228 */
2229 static ssize_t
_scf_tmpl_prop_value(scf_propertygroup_t * pg,const char * pname,char ** out)2230 _scf_tmpl_prop_value(scf_propertygroup_t *pg, const char *pname, char **out)
2231 {
2232 assert(strcmp(pname, SCF_PROPERTY_TM_NAME) == 0 ||
2233 strcmp(pname, SCF_PROPERTY_TM_TYPE) == 0);
2234
2235 *out = _scf_read_single_astring_from_pg(pg, pname);
2236
2237 if (*out != NULL && *out[0] == '\0') {
2238 (void) scf_set_error(SCF_ERROR_NONE);
2239 free(*out);
2240 *out = strdup(SCF_TMPL_WILDCARD);
2241 if (*out == NULL)
2242 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2243 }
2244 if (*out == NULL) {
2245 if (ismember(scf_error(), errors_server)) {
2246 return (-1);
2247 } else switch (scf_error()) {
2248 case SCF_ERROR_CONSTRAINT_VIOLATED:
2249 case SCF_ERROR_NOT_FOUND:
2250 case SCF_ERROR_TYPE_MISMATCH:
2251 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2252 return (-1);
2253
2254 case SCF_ERROR_INVALID_ARGUMENT:
2255 case SCF_ERROR_NOT_SET:
2256 default:
2257 assert(0);
2258 abort();
2259 }
2260 }
2261
2262 return (strlen(*out));
2263 }
2264
2265 /*
2266 * int scf_tmpl_iter_pgs()
2267 *
2268 * Iterates through the property group templates for the fmri given.
2269 * When t is uninitialized or reset, sets t to the first property group
2270 * template in fmri. On subsequent calls, sets t to the next property group
2271 * template in frmi.
2272 * Returns 1 on success, 0 when no property group templates are left to
2273 * iterate, -1 on error.
2274 * The flags argument may include SCF_PG_TMPL_FLAG_REQUIRED,
2275 * SCF_PG_TMPL_FLAG_CURRENT, and/or SCF_PG_TMPL_FLAG_EXACT.
2276 *
2277 * Returns -1 on error and sets scf_error() to:
2278 * SCF_ERROR_BACKEND_ACCESS
2279 * SCF_ERROR_CONNECTION_BROKEN
2280 * SCF_ERROR_DELETED
2281 * SCF_ERROR_HANDLE_DESTROYED
2282 * SCF_ERROR_INTERNAL
2283 * SCF_ERROR_INVALID_ARGUMENT
2284 * The handle argument is NULL, fmri is invalid, or snapshot is invalid.
2285 * SCF_ERROR_NO_MEMORY
2286 * SCF_ERROR_NO_RESOURCES
2287 * SCF_ERROR_NOT_BOUND
2288 * SCF_ERROR_NOT_FOUND
2289 * SCF_ERROR_PERMISSION_DENIED
2290 */
2291 int
scf_tmpl_iter_pgs(scf_pg_tmpl_t * t,const char * fmri,const char * snapshot,const char * type,int flags)2292 scf_tmpl_iter_pgs(scf_pg_tmpl_t *t, const char *fmri, const char *snapshot,
2293 const char *type, int flags)
2294 {
2295 scf_handle_t *h;
2296 scf_service_t *svc = NULL;
2297 scf_instance_t *inst = NULL;
2298 scf_propertygroup_t *pg = NULL;
2299 scf_snapshot_t *snap = NULL;
2300 scf_pg_tmpl_t *pg_tmpl = NULL;
2301 int err;
2302 int found = 0;
2303 char *tmpl_type;
2304 uint8_t required;
2305 int ret;
2306
2307 if (t == NULL) {
2308 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2309 return (-1);
2310 }
2311
2312 h = t->pt_h;
2313
2314 if (t->pt_populated == 0) {
2315 if ((svc = scf_service_create(h)) == NULL ||
2316 (inst = scf_instance_create(h)) == NULL ||
2317 (pg = scf_pg_create(h)) == NULL) {
2318 goto fail_non_populated;
2319 }
2320
2321 ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2322 NULL, SCF_DECODE_FMRI_EXACT);
2323 if (ret == 0) {
2324 scf_service_destroy(svc);
2325 svc = NULL;
2326 } else if (ret != 0 &&
2327 scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
2328 ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
2329 NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
2330 if (ret == 0) {
2331 scf_instance_destroy(inst);
2332 inst = NULL;
2333 }
2334 }
2335
2336 if (ret != 0) {
2337 if (ismember(scf_error(), errors_server)) {
2338 goto fail_non_populated;
2339 } else switch (scf_error()) {
2340 case SCF_ERROR_CONSTRAINT_VIOLATED:
2341 (void) scf_set_error(
2342 SCF_ERROR_INVALID_ARGUMENT);
2343 goto fail_non_populated;
2344
2345 case SCF_ERROR_INVALID_ARGUMENT:
2346 case SCF_ERROR_NOT_FOUND:
2347 goto fail_non_populated;
2348
2349 case SCF_ERROR_HANDLE_MISMATCH:
2350 case SCF_ERROR_NOT_SET:
2351 default:
2352 assert(0);
2353 abort();
2354 }
2355 }
2356
2357 assert(svc == NULL || inst == NULL);
2358 assert(svc != NULL || inst != NULL);
2359
2360 if (inst != NULL) {
2361 if (snapshot == NULL ||
2362 strcmp(snapshot, "running") == 0 ||
2363 (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2364 SCF_PG_TMPL_FLAG_CURRENT) {
2365 if (_get_snapshot(inst, NULL, &snap) == -1)
2366 goto fail_non_populated;
2367 } else {
2368 (void) scf_set_error(SCF_ERROR_NONE);
2369 if (_get_snapshot(inst, snapshot,
2370 &snap) == -1) {
2371 goto fail_non_populated;
2372 } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2373 goto fail_non_populated;
2374 }
2375 }
2376 } else {
2377 scf_snapshot_destroy(snap);
2378 snap = NULL;
2379 }
2380
2381 pg_tmpl = t;
2382 pg_tmpl->pt_orig_inst = inst;
2383 pg_tmpl->pt_orig_svc = svc;
2384 pg_tmpl->pt_snap = snap;
2385 pg_tmpl->pt_is_iter = 1;
2386 pg_tmpl->pt_iter_last = SCF__TMPL_ITER_NONE;
2387 pg_tmpl->pt_pg = pg;
2388 pg_tmpl->pt_populated = 1;
2389 } else {
2390 if (t->pt_is_iter != 1) {
2391 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2392 return (-1);
2393 }
2394 pg_tmpl = t;
2395 assert(pg_tmpl->pt_pg != NULL);
2396 }
2397
2398 if (pg_tmpl->pt_iter == NULL) {
2399 pg_tmpl->pt_iter = _get_next_iterator(h, pg_tmpl, snapshot,
2400 (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2401 if (pg_tmpl->pt_iter == NULL) {
2402 if (scf_error() == SCF_ERROR_NOT_FOUND)
2403 return (0);
2404 else
2405 return (-1);
2406 }
2407 }
2408
2409 while (found == 0) {
2410 while ((err = scf_iter_next_pg(pg_tmpl->pt_iter,
2411 pg_tmpl->pt_pg)) != 1) {
2412 if (err == -1) {
2413 if (ismember(scf_error(), errors_server)) {
2414 return (-1);
2415 } else switch (scf_error()) {
2416 case SCF_ERROR_HANDLE_MISMATCH:
2417 return (-1);
2418
2419 case SCF_ERROR_NOT_SET:
2420 case SCF_ERROR_INVALID_ARGUMENT:
2421 default:
2422 assert(0);
2423 abort();
2424 }
2425 } else if (err == 0) {
2426 /* This iteration is done. Get the next one */
2427 scf_iter_destroy(pg_tmpl->pt_iter);
2428 pg_tmpl->pt_iter = _get_next_iterator(h,
2429 pg_tmpl, snapshot,
2430 (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2431 if (pg_tmpl->pt_iter == NULL) {
2432 if (scf_error() == SCF_ERROR_NOT_FOUND)
2433 return (0);
2434 else
2435 return (-1);
2436 }
2437 continue;
2438 } else {
2439 assert(0);
2440 abort();
2441 }
2442 }
2443
2444 /*
2445 * Discard pgs which don't exist at the right scoping. This
2446 * check also makes sure that if we're looking at, for
2447 * example, svc:/system/svc/restarter:default, that we
2448 * don't hand back the same property groups twice.
2449 */
2450 switch (t->pt_iter_last) {
2451 case SCF__TMPL_ITER_INST:
2452 ret = check_target_match(pg_tmpl->pt_pg,
2453 SCF_TM_TARGET_THIS);
2454 break;
2455 case SCF__TMPL_ITER_RESTARTER:
2456 ret = check_target_match(pg_tmpl->pt_pg,
2457 SCF_TM_TARGET_DELEGATE);
2458 break;
2459 case SCF__TMPL_ITER_GLOBAL:
2460 ret = check_target_match(pg_tmpl->pt_pg,
2461 SCF_TM_TARGET_ALL);
2462 break;
2463 case SCF__TMPL_ITER_NONE:
2464 default:
2465 assert(0);
2466 abort();
2467 }
2468
2469 if (ret != 0)
2470 continue;
2471
2472 /*
2473 * If walking only required property groups, check if
2474 * the retrieved group is required.
2475 */
2476 if (flags & SCF_PG_TMPL_FLAG_REQUIRED) {
2477 if (scf_tmpl_pg_required(pg_tmpl, &required) == 0) {
2478 if (required == 0)
2479 continue;
2480 } else {
2481 return (-1);
2482 }
2483 }
2484
2485 /*
2486 * If type != NULL, check if type property matches. If no
2487 * type property exists, consider it a match.
2488 */
2489 if (type != NULL) {
2490 if (scf_tmpl_pg_type(pg_tmpl, &tmpl_type) != -1) {
2491 if (strcmp(tmpl_type, SCF_TMPL_WILDCARD)
2492 == 0 || strcmp(type, tmpl_type) == 0) {
2493 free(tmpl_type);
2494 break;
2495 }
2496 free(tmpl_type);
2497 } else if (scf_error() == SCF_ERROR_NOT_FOUND ||
2498 scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED ||
2499 scf_error() == SCF_ERROR_TYPE_MISMATCH) {
2500 break;
2501 } else {
2502 return (-1);
2503 }
2504 } else {
2505 break;
2506 }
2507 }
2508
2509 return (1);
2510
2511 fail_non_populated:
2512 scf_service_destroy(svc);
2513 scf_instance_destroy(inst);
2514 scf_pg_destroy(pg);
2515 scf_snapshot_destroy(snap);
2516 return (-1);
2517 }
2518
2519 void
scf_tmpl_pg_destroy(scf_pg_tmpl_t * t)2520 scf_tmpl_pg_destroy(scf_pg_tmpl_t *t)
2521 {
2522 if (t == NULL)
2523 return;
2524
2525 scf_pg_destroy(t->pt_pg);
2526 scf_instance_destroy(t->pt_inst);
2527 if (t->pt_inst != t->pt_orig_inst)
2528 scf_instance_destroy(t->pt_orig_inst);
2529 scf_snapshot_destroy(t->pt_snap);
2530 scf_service_destroy(t->pt_orig_svc);
2531 if (t->pt_svc != t->pt_orig_svc)
2532 scf_service_destroy(t->pt_svc);
2533 scf_iter_destroy(t->pt_iter);
2534 free(t);
2535 }
2536
2537 void
scf_tmpl_pg_reset(scf_pg_tmpl_t * t)2538 scf_tmpl_pg_reset(scf_pg_tmpl_t *t)
2539 {
2540 scf_pg_destroy(t->pt_pg);
2541 t->pt_pg = NULL;
2542
2543 scf_instance_destroy(t->pt_inst);
2544 if (t->pt_inst != t->pt_orig_inst)
2545 scf_instance_destroy(t->pt_orig_inst);
2546 t->pt_inst = NULL;
2547 t->pt_orig_inst = NULL;
2548
2549 scf_snapshot_destroy(t->pt_snap);
2550 t->pt_snap = NULL;
2551
2552 scf_service_destroy(t->pt_orig_svc);
2553 if (t->pt_svc != t->pt_orig_svc)
2554 scf_service_destroy(t->pt_svc);
2555 t->pt_orig_svc = NULL;
2556 t->pt_svc = NULL;
2557
2558 scf_iter_destroy(t->pt_iter);
2559 t->pt_iter = NULL;
2560
2561 t->pt_populated = 0;
2562 t->pt_is_iter = 0;
2563 t->pt_iter_last = 0;
2564
2565 /* Do not reset t->pt_h. */
2566 }
2567
2568 /*
2569 * int scf_tmpl_get_by_prop()
2570 *
2571 * Get the property template given a property group template and property
2572 * name. No flags are currently defined for this function.
2573 *
2574 * Returns NULL on failure, and sets scf_error():
2575 * SCF_ERROR_BACKEND_ACCESS
2576 * SCF_ERROR_CONNECTION_BROKEN
2577 * SCF_ERROR_DELETED
2578 * SCF_ERROR_HANDLE_DESTROYED
2579 * SCF_ERROR_INTERNAL
2580 * SCF_ERROR_INVALID_ARGUMENT
2581 * SCF_ERROR_NO_MEMORY
2582 * SCF_ERROR_NO_RESOURCES
2583 * SCF_ERROR_NOT_BOUND
2584 * SCF_ERROR_NOT_FOUND
2585 * Template object matching property doesn't exist in the repository.
2586 * SCF_ERROR_TYPE_MISMATCH
2587 * Matching template object is the wrong type in the repository.
2588 */
2589 int
scf_tmpl_get_by_prop(scf_pg_tmpl_t * t,const char * prop,scf_prop_tmpl_t * prop_tmpl,int flags)2590 scf_tmpl_get_by_prop(scf_pg_tmpl_t *t, const char *prop,
2591 scf_prop_tmpl_t *prop_tmpl, int flags)
2592 {
2593 char *tmpl_prop_name;
2594 scf_propertygroup_t *pg = NULL;
2595 char *pg_type;
2596 int found = 0;
2597
2598 if (flags != 0) {
2599 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2600 return (-1);
2601 }
2602
2603 scf_tmpl_prop_reset(prop_tmpl);
2604 if ((pg = scf_pg_create(scf_pg_handle(t->pt_pg))) == NULL)
2605 return (-1);
2606
2607 tmpl_prop_name = _tmpl_prop_name(prop, t);
2608 if (tmpl_prop_name == NULL) {
2609 assert(scf_error() != SCF_ERROR_NOT_SET);
2610 return (-1);
2611 }
2612
2613 if (_get_pg(t->pt_svc, t->pt_inst, t->pt_snap,
2614 tmpl_prop_name, pg) != 0) {
2615 if (!ismember(scf_error(), errors_server)) {
2616 switch (scf_error()) {
2617 case SCF_ERROR_NOT_FOUND:
2618 case SCF_ERROR_INVALID_ARGUMENT:
2619 break;
2620
2621 case SCF_ERROR_NOT_SET:
2622 case SCF_ERROR_HANDLE_MISMATCH:
2623 default:
2624 assert(0);
2625 abort();
2626 }
2627 }
2628 } else {
2629 /*
2630 * We've only found a template property group if the type
2631 * is correct.
2632 */
2633 if ((pg_type = _scf_get_pg_type(pg)) != NULL &&
2634 strcmp(pg_type, SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0)
2635 found++;
2636 else
2637 (void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
2638
2639
2640 free(pg_type);
2641 }
2642
2643 if (found == 0) {
2644 scf_pg_destroy(pg);
2645 free(tmpl_prop_name);
2646 return (-1);
2647 }
2648
2649 prop_tmpl->prt_h = scf_pg_handle(t->pt_pg);
2650 prop_tmpl->prt_t = t;
2651 prop_tmpl->prt_pg = pg;
2652 prop_tmpl->prt_pg_name = tmpl_prop_name;
2653 prop_tmpl->prt_populated = 1;
2654
2655 return (0);
2656 }
2657
2658 /*
2659 * scf_prop_tmpl_t *scf_tmpl_prop_create(scf_handle_t *);
2660 *
2661 * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT, or
2662 * _NO_MEMORY.
2663 */
2664 scf_prop_tmpl_t *
scf_tmpl_prop_create(scf_handle_t * handle)2665 scf_tmpl_prop_create(scf_handle_t *handle)
2666 {
2667 scf_prop_tmpl_t *pt;
2668
2669 if (handle == NULL) {
2670 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2671 return (NULL);
2672 }
2673 pt = calloc(1, sizeof (scf_prop_tmpl_t));
2674 if (pt == NULL)
2675 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2676 else
2677 pt->prt_h = handle;
2678
2679 return (pt);
2680 }
2681
2682 /*
2683 * int scf_tmpl_iter_props()
2684 *
2685 * Iterates over all property templates defined in the specified property
2686 * group template. The iterator state is stored on the property template
2687 * data structure, and the data structure should be allocated with
2688 * scf_tmpl_prop_create(). To continue the iteration, the previously
2689 * returned structure should be passed in as an argument to this function.
2690 * flags can include SCF_PROP_TMPL_FLAG_REQUIRED. The function returns
2691 * 1 when a result was found, and 0 when the iteration is complete.
2692 *
2693 * Returns -1 on failure, and sets scf_error():
2694 * SCF_ERROR_BACKEND_ACCESS
2695 * SCF_ERROR_CONNECTION_BROKEN
2696 * SCF_ERROR_DELETED
2697 * SCF_ERROR_HANDLE_DESTROYED
2698 * SCF_ERROR_INTERNAL
2699 * SCF_ERROR_INVALID_ARGUMENT
2700 * SCF_ERROR_NO_MEMORY
2701 * SCF_ERROR_NO_RESOURCES
2702 * SCF_ERROR_NOT_BOUND
2703 * SCF_ERROR_PERMISSION_DENIED
2704 * SCF_ERROR_TEMPLATE_INVALID
2705 * Template data is invalid. One of the property templates in this
2706 * pg_tmpl is malformed.
2707 */
2708 int
scf_tmpl_iter_props(scf_pg_tmpl_t * t,scf_prop_tmpl_t * pt,int flags)2709 scf_tmpl_iter_props(scf_pg_tmpl_t *t, scf_prop_tmpl_t *pt, int flags)
2710 {
2711 scf_prop_tmpl_t *prop_tmpl;
2712 char *pg_pat;
2713 char *pg_name = NULL;
2714 int err;
2715 int ret;
2716 ssize_t size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2717 uint8_t required;
2718 scf_handle_t *h;
2719 scf_propertygroup_t *pg = NULL;
2720 scf_iter_t *iter = NULL;
2721
2722 assert(size != 0);
2723 if (t == NULL || pt == NULL) {
2724 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2725 return (-1);
2726 }
2727
2728 assert(t->pt_inst == NULL || t->pt_svc == NULL);
2729 assert(t->pt_inst != NULL || t->pt_svc != NULL);
2730
2731 if ((pg_name = malloc(size)) == NULL) {
2732 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2733 return (-1);
2734 }
2735
2736 if (pt->prt_populated == 0) {
2737 if ((h = scf_pg_handle(t->pt_pg)) == NULL)
2738 goto fail_non_populated;
2739
2740 if ((pg = scf_pg_create(h)) == NULL ||
2741 (iter = scf_iter_create(h)) == NULL)
2742 goto fail_non_populated;
2743
2744 if (t->pt_inst != NULL)
2745 err = scf_iter_instance_pgs_typed_composed(iter,
2746 t->pt_inst, t->pt_snap,
2747 SCF_GROUP_TEMPLATE_PROP_PATTERN);
2748 else if (t->pt_svc != NULL)
2749 err = scf_iter_service_pgs_typed(iter, t->pt_svc,
2750 SCF_GROUP_TEMPLATE_PROP_PATTERN);
2751
2752 if (err != 0) {
2753 if (ismember(scf_error(), errors_server)) {
2754 goto fail_non_populated;
2755 } else switch (scf_error()) {
2756 case SCF_ERROR_INVALID_ARGUMENT:
2757 goto fail_non_populated;
2758
2759 case SCF_ERROR_NOT_SET:
2760 case SCF_ERROR_HANDLE_MISMATCH:
2761 default:
2762 assert(0);
2763 abort();
2764 }
2765
2766 }
2767 prop_tmpl = pt;
2768 prop_tmpl->prt_t = t;
2769 prop_tmpl->prt_populated = 1;
2770 prop_tmpl->prt_pg = pg;
2771 prop_tmpl->prt_iter = iter;
2772 } else {
2773 prop_tmpl = pt;
2774 }
2775
2776 while ((err = scf_iter_next_pg(prop_tmpl->prt_iter,
2777 prop_tmpl->prt_pg)) > 0) {
2778 /*
2779 * Check if the name matches the appropriate property
2780 * group template name.
2781 */
2782 pg_pat = _scf_read_single_astring_from_pg(prop_tmpl->prt_pg,
2783 SCF_PROPERTY_TM_PG_PATTERN);
2784 if (pg_pat == NULL) {
2785 if (ismember(scf_error(), errors_server)) {
2786 uu_free(pg_name);
2787 return (-1);
2788 } else switch (scf_error()) {
2789 case SCF_ERROR_NOT_FOUND:
2790 continue;
2791
2792 case SCF_ERROR_CONSTRAINT_VIOLATED:
2793 case SCF_ERROR_TYPE_MISMATCH:
2794 (void) scf_set_error(
2795 SCF_ERROR_TEMPLATE_INVALID);
2796 free(pg_name);
2797 return (-1);
2798
2799 case SCF_ERROR_INVALID_ARGUMENT:
2800 case SCF_ERROR_NOT_SET:
2801 default:
2802 assert(0);
2803 abort();
2804 }
2805 }
2806 if ((ret = scf_pg_get_name(t->pt_pg, pg_name, size)) <= 0) {
2807 free(pg_pat);
2808 if (ret == 0)
2809 continue;
2810
2811 if (ismember(scf_error(), errors_server)) {
2812 free(pg_name);
2813 return (-1);
2814 } else {
2815 assert(0);
2816 abort();
2817 }
2818 }
2819 if (strcmp(pg_pat, pg_name) != 0) {
2820 free(pg_pat);
2821 continue;
2822 }
2823 free(pg_pat);
2824
2825 /*
2826 * If walking only required properties, check if
2827 * the retrieved property is required.
2828 */
2829 if (flags & SCF_PROP_TMPL_FLAG_REQUIRED) {
2830 if (scf_tmpl_prop_required(prop_tmpl, &required) == 0) {
2831 if (required == 0)
2832 continue;
2833 } else {
2834 free(pg_name);
2835 return (-1);
2836 }
2837 }
2838 free(pg_name);
2839 return (0);
2840 }
2841
2842 if (err == -1) {
2843 if (ismember(scf_error(), errors_server)) {
2844 free(pg_name);
2845 return (-1);
2846 } else {
2847 assert(0);
2848 abort();
2849 }
2850 } else if (err == 0) {
2851 scf_iter_destroy(prop_tmpl->prt_iter);
2852 prop_tmpl->prt_iter = NULL;
2853 prop_tmpl->prt_populated = 0;
2854 }
2855 free(pg_name);
2856
2857 return (1);
2858
2859 fail_non_populated:
2860 free(pg_name);
2861 scf_pg_destroy(pg);
2862 scf_iter_destroy(iter);
2863 return (-1);
2864 }
2865
2866 void
scf_tmpl_prop_destroy(scf_prop_tmpl_t * t)2867 scf_tmpl_prop_destroy(scf_prop_tmpl_t *t)
2868 {
2869 if (t == NULL)
2870 return;
2871
2872 scf_pg_destroy(t->prt_pg);
2873 free(t->prt_pg_name);
2874 free(t->prt_iter);
2875 free(t);
2876 }
2877
2878 void
scf_tmpl_prop_reset(scf_prop_tmpl_t * t)2879 scf_tmpl_prop_reset(scf_prop_tmpl_t *t)
2880 {
2881 scf_pg_destroy(t->prt_pg);
2882 t->prt_pg = NULL;
2883
2884 free(t->prt_pg_name);
2885 t->prt_pg_name = NULL;
2886
2887 free(t->prt_iter);
2888 t->prt_iter = NULL;
2889
2890 t->prt_populated = 0;
2891 t->prt_h = NULL;
2892 t->prt_t = NULL;
2893 }
2894
2895 /*
2896 * Returns -1 on failure. Sets scf_error():
2897 * SCF_ERROR_BACKEND_ACCESS
2898 * SCF_ERROR_CONNECTION_BROKEN
2899 * SCF_ERROR_DELETED
2900 * SCF_ERROR_HANDLE_DESTROYED
2901 * SCF_ERROR_INTERNAL
2902 * SCF_ERROR_NO_MEMORY
2903 * SCF_ERROR_NO_RESOURCES
2904 * SCF_ERROR_NOT_BOUND
2905 * SCF_ERROR_PERMISSION_DENIED
2906 * SCF_ERROR_TEMPLATE_INVALID
2907 * The name of the template property group (the pname property) has
2908 * an improper repository format and is not type astring or has
2909 * more than one value.
2910 */
2911 ssize_t
scf_tmpl_pg_name(const scf_pg_tmpl_t * t,char ** out)2912 scf_tmpl_pg_name(const scf_pg_tmpl_t *t, char **out)
2913 {
2914 return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_NAME, out));
2915 }
2916
2917 /*
2918 * returns an allocated string that must be freed
2919 *
2920 * Returns NULL on failure, sets scf_error() to:
2921 * SCF_ERROR_BACKEND_ACCESS
2922 * SCF_ERROR_CONNECTION_BROKEN
2923 * SCF_ERROR_DELETED
2924 * SCF_ERROR_HANDLE_DESTROYED
2925 * SCF_ERROR_INTERNAL
2926 * SCF_ERROR_INVALID_ARGUMENT
2927 * name not a valid property name
2928 * name and locale are too long to make a property name
2929 * SCF_ERROR_NO_MEMORY
2930 * SCF_ERROR_NO_RESOURCES
2931 * SCF_ERROR_NOT_BOUND
2932 * SCF_ERROR_NOT_FOUND
2933 * Property doesn't exist or exists and has no value.
2934 * SCF_ERROR_PERMISSION_DENIED
2935 * SCF_ERROR_TEMPLATE_INVALID
2936 */
2937 static char *
_read_localized_astring_from_pg(scf_propertygroup_t * pg,const char * name,const char * locale)2938 _read_localized_astring_from_pg(scf_propertygroup_t *pg, const char *name,
2939 const char *locale)
2940 {
2941 char *str;
2942 char *lname_prop;
2943
2944 str = _add_locale_to_name(name, locale);
2945 if (str == NULL)
2946 return (NULL);
2947 lname_prop = _scf_read_single_astring_from_pg(pg, str);
2948 if (lname_prop == NULL) {
2949 free(str);
2950 if (scf_error() != SCF_ERROR_NOT_FOUND)
2951 return (NULL);
2952 str = _add_locale_to_name(name, "C");
2953 if (str == NULL)
2954 return (NULL);
2955 lname_prop = _scf_read_single_astring_from_pg(pg, str);
2956 }
2957 free(str);
2958 if (lname_prop == NULL) {
2959 if (scf_error() == SCF_ERROR_TYPE_MISMATCH ||
2960 scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
2961 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2962 }
2963 return (lname_prop);
2964 }
2965
2966 /*
2967 * returns an allocated string that must be freed
2968 *
2969 * Returns -1 on failure, sets scf_error() to:
2970 * SCF_ERROR_BACKEND_ACCESS
2971 * SCF_ERROR_CONNECTION_BROKEN
2972 * SCF_ERROR_DELETED
2973 * SCF_ERROR_HANDLE_DESTROYED
2974 * SCF_ERROR_INTERNAL
2975 * SCF_ERROR_INVALID_ARGUMENT
2976 * locale is too long to make a valid property name
2977 * SCF_ERROR_NO_MEMORY
2978 * SCF_ERROR_NO_RESOURCES
2979 * SCF_ERROR_NOT_BOUND
2980 * SCF_ERROR_NOT_FOUND
2981 * Property doesn't exist or exists and has no value.
2982 * SCF_ERROR_PERMISSION_DENIED
2983 * SCF_ERROR_TEMPLATE_INVALID
2984 */
2985 ssize_t
scf_tmpl_pg_common_name(const scf_pg_tmpl_t * t,const char * locale,char ** out)2986 scf_tmpl_pg_common_name(const scf_pg_tmpl_t *t, const char *locale, char **out)
2987 {
2988 assert(out != NULL);
2989 if ((*out = _read_localized_astring_from_pg(t->pt_pg,
2990 SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale)) == NULL)
2991 return (-1);
2992
2993 return (strlen(*out));
2994 }
2995
2996 /*
2997 * returns an allocated string that must be freed
2998 *
2999 * Returns -1 on failure, sets scf_error() to:
3000 * SCF_ERROR_BACKEND_ACCESS
3001 * SCF_ERROR_CONNECTION_BROKEN
3002 * SCF_ERROR_DELETED
3003 * SCF_ERROR_HANDLE_DESTROYED
3004 * SCF_ERROR_INTERNAL
3005 * SCF_ERROR_INVALID_ARGUMENT
3006 * locale is too long to make a valid property name
3007 * SCF_ERROR_NO_MEMORY
3008 * SCF_ERROR_NO_RESOURCES
3009 * SCF_ERROR_NOT_BOUND
3010 * SCF_ERROR_NOT_FOUND
3011 * Property doesn't exist or exists and has no value.
3012 * SCF_ERROR_PERMISSION_DENIED
3013 * SCF_ERROR_TEMPLATE_INVALID
3014 */
3015 ssize_t
scf_tmpl_pg_description(const scf_pg_tmpl_t * t,const char * locale,char ** out)3016 scf_tmpl_pg_description(const scf_pg_tmpl_t *t, const char *locale, char **out)
3017 {
3018 assert(out != NULL);
3019 if ((*out = _read_localized_astring_from_pg(t->pt_pg,
3020 SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale)) == NULL)
3021 return (-1);
3022
3023 return (strlen(*out));
3024 }
3025
3026 /*
3027 * Returns -1 on failure. Sets scf_error():
3028 * SCF_ERROR_BACKEND_ACCESS
3029 * SCF_ERROR_CONNECTION_BROKEN
3030 * SCF_ERROR_DELETED
3031 * SCF_ERROR_HANDLE_DESTROYED
3032 * SCF_ERROR_INTERNAL
3033 * SCF_ERROR_NO_MEMORY
3034 * SCF_ERROR_NO_RESOURCES
3035 * SCF_ERROR_NOT_BOUND
3036 * SCF_ERROR_NOT_FOUND
3037 * 'type' property doesn't exist or exists and has no value.
3038 * SCF_ERROR_PERMISSION_DENIED
3039 * SCF_ERROR_TEMPLATE_INVALID
3040 * 'type' property is not SCF_TYPE_ASTRING or has more than one value.
3041 */
3042 ssize_t
scf_tmpl_pg_type(const scf_pg_tmpl_t * t,char ** out)3043 scf_tmpl_pg_type(const scf_pg_tmpl_t *t, char **out)
3044 {
3045 return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_TYPE, out));
3046 }
3047
3048 /*
3049 * Returns -1 on failure, sets scf_error() to:
3050 * SCF_ERROR_BACKEND_ACCESS
3051 * SCF_ERROR_CONNECTION_BROKEN
3052 * SCF_ERROR_DELETED
3053 * SCF_ERROR_HANDLE_DESTROYED
3054 * SCF_ERROR_INTERNAL
3055 * SCF_ERROR_NO_MEMORY
3056 * SCF_ERROR_NO_RESOURCES
3057 * SCF_ERROR_NOT_BOUND
3058 * SCF_ERROR_PERMISSION_DENIED
3059 * SCF_ERROR_TEMPLATE_INVALID
3060 * required property is not SCF_TYPE_BOOLEAN or has more than one value.
3061 */
3062 int
scf_tmpl_pg_required(const scf_pg_tmpl_t * t,uint8_t * out)3063 scf_tmpl_pg_required(const scf_pg_tmpl_t *t, uint8_t *out)
3064 {
3065
3066 if (_read_single_boolean_from_pg(t->pt_pg, SCF_PROPERTY_TM_REQUIRED,
3067 out) == -1) {
3068 if (ismember(scf_error(), errors_server)) {
3069 return (-1);
3070 } else switch (scf_error()) {
3071 case SCF_ERROR_CONSTRAINT_VIOLATED:
3072 case SCF_ERROR_TYPE_MISMATCH:
3073 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3074 return (-1);
3075
3076 case SCF_ERROR_NOT_FOUND:
3077 *out = 0;
3078 return (0);
3079
3080 case SCF_ERROR_INVALID_ARGUMENT:
3081 default:
3082 assert(0);
3083 abort();
3084 }
3085 }
3086
3087 return (0);
3088 }
3089
3090 /*
3091 * Returns -1 on failure. Sets scf_error():
3092 * SCF_ERROR_BACKEND_ACCESS
3093 * SCF_ERROR_CONNECTION_BROKEN
3094 * SCF_ERROR_DELETED
3095 * SCF_ERROR_HANDLE_DESTROYED
3096 * SCF_ERROR_INTERNAL
3097 * SCF_ERROR_NO_MEMORY
3098 * SCF_ERROR_NO_RESOURCES
3099 * SCF_ERROR_NOT_BOUND
3100 * SCF_ERROR_PERMISSION_DENIED
3101 * SCF_ERROR_TEMPLATE_INVALID
3102 * target property is not SCF_TYPE_ASTRING or has more than one value.
3103 */
3104 ssize_t
scf_tmpl_pg_target(const scf_pg_tmpl_t * t,char ** out)3105 scf_tmpl_pg_target(const scf_pg_tmpl_t *t, char **out)
3106 {
3107 *out = _scf_read_single_astring_from_pg(t->pt_pg,
3108 SCF_PROPERTY_TM_TARGET);
3109
3110 if (*out == NULL) {
3111 if (ismember(scf_error(), errors_server)) {
3112 return (-1);
3113 } else switch (scf_error()) {
3114 case SCF_ERROR_CONSTRAINT_VIOLATED:
3115 case SCF_ERROR_NOT_FOUND:
3116 case SCF_ERROR_TYPE_MISMATCH:
3117 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3118 return (-1);
3119
3120 case SCF_ERROR_INVALID_ARGUMENT:
3121 case SCF_ERROR_NOT_SET:
3122 default:
3123 assert(0);
3124 abort();
3125 }
3126 }
3127
3128 return (strlen(*out));
3129 }
3130
3131 /*
3132 * Returns -1 on failure. Sets scf_error():
3133 * SCF_ERROR_BACKEND_ACCESS
3134 * SCF_ERROR_CONNECTION_BROKEN
3135 * SCF_ERROR_DELETED
3136 * SCF_ERROR_HANDLE_DESTROYED
3137 * SCF_ERROR_INTERNAL
3138 * SCF_ERROR_NO_MEMORY
3139 * SCF_ERROR_NO_RESOURCES
3140 * SCF_ERROR_NOT_BOUND
3141 * SCF_ERROR_PERMISSION_DENIED
3142 * SCF_ERROR_TEMPLATE_INVALID
3143 */
3144 ssize_t
scf_tmpl_prop_name(const scf_prop_tmpl_t * t,char ** out)3145 scf_tmpl_prop_name(const scf_prop_tmpl_t *t, char **out)
3146 {
3147 *out = _scf_read_single_astring_from_pg(t->prt_pg,
3148 SCF_PROPERTY_TM_NAME);
3149
3150 if (*out != NULL && *out[0] == '\0') {
3151 free(*out);
3152 *out = NULL;
3153 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3154 }
3155 if (*out == NULL) {
3156 if (ismember(scf_error(), errors_server)) {
3157 return (-1);
3158 } else switch (scf_error()) {
3159 case SCF_ERROR_CONSTRAINT_VIOLATED:
3160 case SCF_ERROR_NOT_FOUND:
3161 case SCF_ERROR_TEMPLATE_INVALID:
3162 case SCF_ERROR_TYPE_MISMATCH:
3163 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3164 return (-1);
3165
3166 case SCF_ERROR_INVALID_ARGUMENT:
3167 case SCF_ERROR_NOT_SET:
3168 default:
3169 assert(0);
3170 abort();
3171 }
3172 }
3173
3174 return (strlen(*out));
3175 }
3176
3177 /*
3178 * Returns -1 on failure. Sets scf_error():
3179 * SCF_ERROR_BACKEND_ACCESS
3180 * SCF_ERROR_CONNECTION_BROKEN
3181 * SCF_ERROR_DELETED
3182 * SCF_ERROR_HANDLE_DESTROYED
3183 * SCF_ERROR_INTERNAL
3184 * SCF_ERROR_NO_MEMORY
3185 * SCF_ERROR_NO_RESOURCES
3186 * SCF_ERROR_NOT_BOUND
3187 * SCF_ERROR_NOT_FOUND
3188 * 'type' property doesn't exist or exists and has no value.
3189 * SCF_ERROR_PERMISSION_DENIED
3190 * SCF_ERROR_TEMPLATE_INVALID
3191 * 'type' property is not SCF_TYPE_ASTRING, has more than one value,
3192 * is SCF_TYPE_INVALID, or is the empty string.
3193 */
3194 int
scf_tmpl_prop_type(const scf_prop_tmpl_t * t,scf_type_t * out)3195 scf_tmpl_prop_type(const scf_prop_tmpl_t *t, scf_type_t *out)
3196 {
3197 char *type;
3198
3199 type = _scf_read_single_astring_from_pg(t->prt_pg,
3200 SCF_PROPERTY_TM_TYPE);
3201 if (type != NULL && type[0] == '\0') {
3202 free(type);
3203 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
3204 return (-1);
3205 }
3206 if (type == NULL) {
3207 if (ismember(scf_error(), errors_server)) {
3208 return (-1);
3209 } else switch (scf_error()) {
3210 case SCF_ERROR_CONSTRAINT_VIOLATED:
3211 case SCF_ERROR_TYPE_MISMATCH:
3212 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3213 /*FALLTHROUGH*/
3214
3215 case SCF_ERROR_NOT_FOUND:
3216 return (-1);
3217
3218 case SCF_ERROR_INVALID_ARGUMENT:
3219 case SCF_ERROR_NOT_SET:
3220 default:
3221 assert(0);
3222 abort();
3223 }
3224 }
3225
3226 *out = scf_string_to_type(type);
3227 free(type);
3228
3229 if (*out == SCF_TYPE_INVALID) {
3230 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3231 return (-1);
3232 }
3233
3234 return (0);
3235 }
3236
3237 /*
3238 * Returns -1 on failure, sets scf_error() to:
3239 * SCF_ERROR_BACKEND_ACCESS
3240 * SCF_ERROR_CONNECTION_BROKEN
3241 * SCF_ERROR_DELETED
3242 * Property group which represents t was deleted.
3243 * SCF_ERROR_HANDLE_DESTROYED
3244 * SCF_ERROR_INTERNAL
3245 * SCF_ERROR_NO_MEMORY
3246 * SCF_ERROR_NO_RESOURCES
3247 * SCF_ERROR_NOT_BOUND
3248 * SCF_ERROR_PERMISSION_DENIED
3249 * SCF_ERROR_TEMPLATE_INVALID
3250 * required property is not SCF_TYPE_ASTRING has more than one value.
3251 */
3252 int
scf_tmpl_prop_required(const scf_prop_tmpl_t * t,uint8_t * out)3253 scf_tmpl_prop_required(const scf_prop_tmpl_t *t, uint8_t *out)
3254 {
3255 if (_read_single_boolean_from_pg(t->prt_pg, SCF_PROPERTY_TM_REQUIRED,
3256 out) == -1) {
3257 if (ismember(scf_error(), errors_server)) {
3258 return (-1);
3259 } else switch (scf_error()) {
3260 case SCF_ERROR_CONSTRAINT_VIOLATED:
3261 case SCF_ERROR_TYPE_MISMATCH:
3262 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3263 return (-1);
3264
3265 case SCF_ERROR_NOT_FOUND:
3266 *out = 0;
3267 return (0);
3268
3269 case SCF_ERROR_INVALID_ARGUMENT:
3270 case SCF_ERROR_NOT_SET:
3271 default:
3272 assert(0);
3273 abort();
3274 }
3275 }
3276
3277 return (0);
3278 }
3279
3280 /*
3281 * Returns -1 on failure. Sets scf_error():
3282 * SCF_ERROR_BACKEND_ACCESS
3283 * SCF_ERROR_CONNECTION_BROKEN
3284 * SCF_ERROR_DELETED
3285 * SCF_ERROR_HANDLE_DESTROYED
3286 * SCF_ERROR_INTERNAL
3287 * SCF_ERROR_NO_MEMORY
3288 * SCF_ERROR_NO_RESOURCES
3289 * SCF_ERROR_NOT_BOUND
3290 * SCF_ERROR_NOT_FOUND
3291 * Property doesn't exist or exists and has no value.
3292 * SCF_ERROR_INVALID_ARGUMENT
3293 * locale is too long to make a property name
3294 * SCF_ERROR_PERMISSION_DENIED
3295 * SCF_ERROR_TEMPLATE_INVALID
3296 * common_name property is not SCF_TYPE_ASTRING has more than one value.
3297 */
3298 ssize_t
scf_tmpl_prop_common_name(const scf_prop_tmpl_t * t,const char * locale,char ** out)3299 scf_tmpl_prop_common_name(const scf_prop_tmpl_t *t, const char *locale,
3300 char **out)
3301 {
3302 assert(out != NULL);
3303 if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3304 SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale)) == NULL)
3305 return (-1);
3306
3307 return (strlen(*out));
3308 }
3309
3310 /*
3311 * Returns -1 on failure. Sets scf_error():
3312 * SCF_ERROR_BACKEND_ACCESS
3313 * SCF_ERROR_CONNECTION_BROKEN
3314 * SCF_ERROR_DELETED
3315 * SCF_ERROR_HANDLE_DESTROYED
3316 * SCF_ERROR_INTERNAL
3317 * SCF_ERROR_NO_MEMORY
3318 * SCF_ERROR_NO_RESOURCES
3319 * SCF_ERROR_NOT_BOUND
3320 * SCF_ERROR_NOT_FOUND
3321 * Property doesn't exist or exists and has no value.
3322 * SCF_ERROR_INVALID_ARGUMENT
3323 * locale is too long to make a property name
3324 * SCF_ERROR_PERMISSION_DENIED
3325 * SCF_ERROR_TEMPLATE_INVALID
3326 * description property is not SCF_TYPE_ASTRING has more than one value.
3327 */
3328 ssize_t
scf_tmpl_prop_description(const scf_prop_tmpl_t * t,const char * locale,char ** out)3329 scf_tmpl_prop_description(const scf_prop_tmpl_t *t, const char *locale,
3330 char **out)
3331 {
3332 assert(out != NULL);
3333 if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3334 SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale)) == NULL)
3335 return (-1);
3336
3337 return (strlen(*out));
3338 }
3339
3340 /*
3341 * Returns -1 on failure. Sets scf_error():
3342 * SCF_ERROR_BACKEND_ACCESS
3343 * SCF_ERROR_CONNECTION_BROKEN
3344 * SCF_ERROR_DELETED
3345 * SCF_ERROR_HANDLE_DESTROYED
3346 * SCF_ERROR_INTERNAL
3347 * SCF_ERROR_NO_MEMORY
3348 * SCF_ERROR_NO_RESOURCES
3349 * SCF_ERROR_NOT_BOUND
3350 * SCF_ERROR_NOT_FOUND
3351 * Property doesn't exist or exists and has no value.
3352 * SCF_ERROR_INVALID_ARGUMENT
3353 * locale is too long to make a property name
3354 * SCF_ERROR_PERMISSION_DENIED
3355 * SCF_ERROR_TEMPLATE_INVALID
3356 * units property is not SCF_TYPE_ASTRING has more than one value.
3357 */
3358 ssize_t
scf_tmpl_prop_units(const scf_prop_tmpl_t * t,const char * locale,char ** out)3359 scf_tmpl_prop_units(const scf_prop_tmpl_t *t, const char *locale, char **out)
3360 {
3361 assert(out != NULL);
3362 if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3363 SCF_PROPERTY_TM_UNITS_PREFIX, locale)) == NULL)
3364 return (-1);
3365
3366 return (strlen(*out));
3367 }
3368
3369 /*
3370 * Returns -1 on failure. Sets scf_error():
3371 * SCF_ERROR_BACKEND_ACCESS
3372 * SCF_ERROR_CONNECTION_BROKEN
3373 * SCF_ERROR_DELETED
3374 * SCF_ERROR_HANDLE_DESTROYED
3375 * SCF_ERROR_INTERNAL
3376 * SCF_ERROR_NO_MEMORY
3377 * SCF_ERROR_NO_RESOURCES
3378 * SCF_ERROR_NOT_BOUND
3379 * SCF_ERROR_PERMISSION_DENIED
3380 * SCF_ERROR_TEMPLATE_INVALID
3381 * visibility property is not SCF_TYPE_ASTRING has more than one value.
3382 */
3383 int
scf_tmpl_prop_visibility(const scf_prop_tmpl_t * t,uint8_t * out)3384 scf_tmpl_prop_visibility(const scf_prop_tmpl_t *t, uint8_t *out)
3385 {
3386 char *visibility;
3387
3388 visibility = _scf_read_single_astring_from_pg(t->prt_pg,
3389 SCF_PROPERTY_TM_VISIBILITY);
3390 if (visibility == NULL) {
3391 if (ismember(scf_error(), errors_server)) {
3392 return (-1);
3393 } else switch (scf_error()) {
3394 /* prop doesn't exist we take the default value */
3395 case SCF_ERROR_NOT_FOUND:
3396 *out = SCF_TMPL_VISIBILITY_READWRITE;
3397 return (0);
3398
3399 case SCF_ERROR_CONSTRAINT_VIOLATED:
3400 case SCF_ERROR_TYPE_MISMATCH:
3401 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3402 return (-1);
3403
3404 case SCF_ERROR_INVALID_ARGUMENT:
3405 case SCF_ERROR_NOT_SET:
3406 default:
3407 assert(0);
3408 abort();
3409 }
3410 } else if (strcmp(visibility, SCF_TM_VISIBILITY_READWRITE) == 0) {
3411 *out = SCF_TMPL_VISIBILITY_READWRITE;
3412 } else if (strcmp(visibility, SCF_TM_VISIBILITY_HIDDEN) == 0) {
3413 *out = SCF_TMPL_VISIBILITY_HIDDEN;
3414 } else if (strcmp(visibility, SCF_TM_VISIBILITY_READONLY) == 0) {
3415 *out = SCF_TMPL_VISIBILITY_READONLY;
3416 } else {
3417 free(visibility);
3418 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3419 return (-1);
3420 }
3421
3422 free(visibility);
3423 return (0);
3424 }
3425
3426 /*
3427 * Return an allocated string containing the value that must be freed
3428 * with free().
3429 *
3430 * On error set scf_error() _NO_MEMORY, or _NOT_SET (val has not been set
3431 * to a value).
3432 */
3433 static char *
_scf_value_get_as_string(scf_value_t * val)3434 _scf_value_get_as_string(scf_value_t *val)
3435 {
3436 ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
3437 char *buf = malloc(sz);
3438
3439 if (buf == NULL) {
3440 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3441 } else if (scf_value_get_as_string(val, buf, sz) == -1) {
3442 free(buf);
3443 buf = NULL;
3444 }
3445
3446 return (buf);
3447 }
3448
3449 /*
3450 * Returns -1 on failure, sets scf_error() to:
3451 * SCF_ERROR_BACKEND_ACCESS
3452 * SCF_ERROR_CONNECTION_BROKEN
3453 * SCF_ERROR_DELETED
3454 * SCF_ERROR_HANDLE_DESTROYED
3455 * SCF_ERROR_INTERNAL
3456 * SCF_ERROR_NO_MEMORY
3457 * SCF_ERROR_NO_RESOURCES
3458 * SCF_ERROR_NOT_BOUND
3459 * SCF_ERROR_NOT_FOUND
3460 * SCF_ERROR_PERMISSION_DENIED
3461 * SCF_ERROR_TEMPLATE_INVALID
3462 */
3463 int
scf_tmpl_prop_cardinality(const scf_prop_tmpl_t * t,uint64_t * min,uint64_t * max)3464 scf_tmpl_prop_cardinality(const scf_prop_tmpl_t *t, uint64_t *min,
3465 uint64_t *max)
3466 {
3467 scf_value_t *val_min = NULL;
3468 scf_value_t *val_max = NULL;
3469 int ret = 0;
3470
3471 if (_read_single_value_from_pg(t->prt_pg,
3472 SCF_PROPERTY_TM_CARDINALITY_MIN, &val_min) == 0) {
3473 if (scf_value_get_count(val_min, min) < 0)
3474 goto error;
3475 } else {
3476 if (scf_error() == SCF_ERROR_NOT_FOUND)
3477 *min = 0;
3478 else
3479 goto error;
3480 }
3481
3482 if (_read_single_value_from_pg(t->prt_pg,
3483 SCF_PROPERTY_TM_CARDINALITY_MAX, &val_max) == 0) {
3484 if (scf_value_get_count(val_max, max) < 0)
3485 goto error;
3486 } else {
3487 if (scf_error() == SCF_ERROR_NOT_FOUND)
3488 *max = UINT64_MAX;
3489 else
3490 goto error;
3491 }
3492 goto cleanup;
3493
3494 error:
3495 if (ismember(scf_error(), errors_server)) {
3496 ret = -1;
3497 } else switch (scf_error()) {
3498 case SCF_ERROR_TYPE_MISMATCH:
3499 case SCF_ERROR_CONSTRAINT_VIOLATED:
3500 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3501 /*FALLTHROUGH*/
3502
3503 case SCF_ERROR_NOT_FOUND:
3504 case SCF_ERROR_TEMPLATE_INVALID:
3505 ret = -1;
3506 break;
3507
3508 case SCF_ERROR_NOT_SET:
3509 case SCF_ERROR_INVALID_ARGUMENT:
3510 default:
3511 assert(0);
3512 abort();
3513 }
3514
3515 cleanup:
3516 scf_value_destroy(val_min);
3517 scf_value_destroy(val_max);
3518
3519 return (ret);
3520 }
3521
3522 /*
3523 * Returns -1 on failure. Sets scf_error():
3524 * SCF_ERROR_BACKEND_ACCESS
3525 * SCF_ERROR_CONNECTION_BROKEN
3526 * SCF_ERROR_DELETED
3527 * SCF_ERROR_HANDLE_DESTROYED
3528 * SCF_ERROR_INTERNAL
3529 * SCF_ERROR_NO_MEMORY
3530 * SCF_ERROR_NO_RESOURCES
3531 * SCF_ERROR_NOT_BOUND
3532 * SCF_ERROR_NOT_FOUND
3533 * Property doesn't exist or exists and has no value.
3534 * SCF_ERROR_PERMISSION_DENIED
3535 * SCF_ERROR_TEMPLATE_INVALID
3536 */
3537 int
scf_tmpl_prop_internal_seps(const scf_prop_tmpl_t * t,scf_values_t * vals)3538 scf_tmpl_prop_internal_seps(const scf_prop_tmpl_t *t, scf_values_t *vals)
3539 {
3540 if (_read_astrings_values(t->prt_pg,
3541 SCF_PROPERTY_INTERNAL_SEPARATORS, vals) == NULL) {
3542 if (ismember(scf_error(), errors_server)) {
3543 return (-1);
3544 } else switch (scf_error()) {
3545 case SCF_ERROR_CONSTRAINT_VIOLATED:
3546 case SCF_ERROR_TYPE_MISMATCH:
3547 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3548 /*FALLTHROUGH*/
3549
3550 case SCF_ERROR_NOT_FOUND:
3551 return (-1);
3552
3553 case SCF_ERROR_INVALID_ARGUMENT:
3554 case SCF_ERROR_NOT_SET:
3555 default:
3556 assert(0);
3557 abort();
3558 }
3559 } else if (vals->value_count == 0) {
3560 /* property has no value */
3561 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
3562 scf_values_destroy(vals);
3563 return (-1);
3564 }
3565
3566 return (0);
3567 }
3568
3569 /*
3570 * Returns -1 on failure. Sets scf_error():
3571 * SCF_ERROR_BACKEND_ACCESS
3572 * SCF_ERROR_CONNECTION_BROKEN
3573 * SCF_ERROR_DELETED
3574 * SCF_ERROR_HANDLE_DESTROYED
3575 * SCF_ERROR_INTERNAL
3576 * SCF_ERROR_NO_MEMORY
3577 * SCF_ERROR_NO_RESOURCES
3578 * SCF_ERROR_NOT_BOUND
3579 * SCF_ERROR_NOT_FOUND
3580 * Property doesn't exist or exists and has no value.
3581 * SCF_ERROR_PERMISSION_DENIED
3582 * SCF_ERROR_TEMPLATE_INVALID
3583 */
3584 int
scf_tmpl_value_name_constraints(const scf_prop_tmpl_t * t,scf_values_t * vals)3585 scf_tmpl_value_name_constraints(const scf_prop_tmpl_t *t,
3586 scf_values_t *vals)
3587 {
3588 char **ret;
3589
3590 ret = _read_astrings_values(t->prt_pg,
3591 SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
3592
3593 if (ret == NULL) {
3594 if (ismember(scf_error(), errors_server)) {
3595 return (-1);
3596 } else switch (scf_error()) {
3597 case SCF_ERROR_CONSTRAINT_VIOLATED:
3598 case SCF_ERROR_TYPE_MISMATCH:
3599 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3600 /*FALLTHROUGH*/
3601
3602 case SCF_ERROR_NOT_FOUND:
3603 return (-1);
3604
3605 case SCF_ERROR_INVALID_ARGUMENT:
3606 case SCF_ERROR_NOT_SET:
3607 default:
3608 assert(0);
3609 abort();
3610 }
3611 } else if (vals->value_count == 0) {
3612 /* property has no value */
3613 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
3614 scf_values_destroy(vals);
3615 return (-1);
3616 }
3617
3618 return (0);
3619 }
3620
3621 /*
3622 * Returns NULL on failure. Sets scf_error():
3623 * Caller is responsible for freeing returned pointer after use.
3624 * SCF_ERROR_CONSTRAINT_VIOLATED
3625 * More tokens than the array size supplied.
3626 * SCF_ERROR_NO_MEMORY
3627 */
3628 static void *
_separate_by_separator(char * string,const char * sep,char ** array,int size)3629 _separate_by_separator(char *string, const char *sep, char **array, int size)
3630 {
3631 char *str, *token;
3632 char *lasts;
3633 int n = 0;
3634
3635 assert(array != NULL);
3636 assert(string != NULL);
3637 assert(sep != NULL);
3638 assert(size > 0);
3639
3640 str = strdup(string);
3641 if (str == NULL) {
3642 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3643 return (NULL);
3644 }
3645
3646 if ((array[n] = strtok_r(str, sep, &lasts)) == NULL) {
3647 assert(0);
3648 abort();
3649 }
3650
3651 n++;
3652 while ((token = strtok_r(NULL, sep, &lasts)) != NULL) {
3653 if (n >= size) {
3654 goto error;
3655 }
3656 array[n] = token;
3657 n++;
3658 }
3659 if (n < size) {
3660 goto error;
3661 }
3662
3663 return (str);
3664 error:
3665 free(str);
3666 (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3667 return (NULL);
3668 }
3669
3670 /*
3671 * check if name is among values of CHOICES_INCLUDE_VALUES
3672 * return 0 if name is present, 1 name is not present, -1 on failure
3673 * SCF_ERROR_BACKEND_ACCESS
3674 * SCF_ERROR_CONNECTION_BROKEN
3675 * SCF_ERROR_DELETED
3676 * SCF_ERROR_HANDLE_DESTROYED
3677 * SCF_ERROR_INTERNAL
3678 * SCF_ERROR_NO_MEMORY
3679 * SCF_ERROR_NO_RESOURCES
3680 * SCF_ERROR_NOT_BOUND
3681 * SCF_ERROR_PERMISSION_DENIED
3682 * SCF_ERROR_TEMPLATE_INVALID
3683 */
3684 static int
_check_choices_include_values(scf_propertygroup_t * pg,const char * name)3685 _check_choices_include_values(scf_propertygroup_t *pg, const char *name)
3686 {
3687 int n = 0, r = 1;
3688 char **ret;
3689 scf_values_t vals;
3690
3691 if ((ret = _read_astrings_values(pg,
3692 SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES, &vals)) == NULL) {
3693 if (ismember(scf_error(), errors_server)) {
3694 return (-1);
3695 } else switch (scf_error()) {
3696 case SCF_ERROR_NOT_FOUND:
3697 return (1);
3698
3699 case SCF_ERROR_TYPE_MISMATCH:
3700 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3701 return (-1);
3702
3703 case SCF_ERROR_INVALID_ARGUMENT:
3704 case SCF_ERROR_NOT_SET:
3705 default:
3706 assert(0);
3707 abort();
3708 }
3709 }
3710
3711 for (n = 0; n < vals.value_count; ++n) {
3712 if (strcmp(name, ret[n]) == 0) {
3713 r = 0;
3714 break;
3715 }
3716 }
3717 scf_values_destroy(&vals);
3718 return (r);
3719 }
3720
3721 void
scf_count_ranges_destroy(scf_count_ranges_t * ranges)3722 scf_count_ranges_destroy(scf_count_ranges_t *ranges)
3723 {
3724 if (ranges == NULL)
3725 return;
3726
3727 ranges->scr_num_ranges = 0;
3728 free(ranges->scr_min);
3729 free(ranges->scr_max);
3730 ranges->scr_min = NULL;
3731 ranges->scr_max = NULL;
3732 }
3733
3734 void
scf_int_ranges_destroy(scf_int_ranges_t * ranges)3735 scf_int_ranges_destroy(scf_int_ranges_t *ranges)
3736 {
3737 if (ranges == NULL)
3738 return;
3739
3740 ranges->sir_num_ranges = 0;
3741 free(ranges->sir_min);
3742 free(ranges->sir_max);
3743 ranges->sir_min = NULL;
3744 ranges->sir_max = NULL;
3745 }
3746
3747 /*
3748 * Returns -1 on failure. Sets scf_error():
3749 * SCF_ERROR_BACKEND_ACCESS
3750 * SCF_ERROR_CONNECTION_BROKEN
3751 * SCF_ERROR_CONSTRAINT_VIOLATED
3752 * SCF_ERROR_DELETED
3753 * SCF_ERROR_HANDLE_DESTROYED
3754 * SCF_ERROR_INTERNAL
3755 * SCF_ERROR_NO_MEMORY
3756 * SCF_ERROR_NO_RESOURCES
3757 * SCF_ERROR_NOT_BOUND
3758 * SCF_ERROR_NOT_FOUND
3759 * Property doesn't exist or exists and has no value.
3760 * SCF_ERROR_PERMISSION_DENIED
3761 * SCF_ERROR_TEMPLATE_INVALID
3762 */
3763 static int
_scf_tmpl_get_count_ranges(const scf_prop_tmpl_t * t,const char * prop,scf_count_ranges_t * ranges)3764 _scf_tmpl_get_count_ranges(const scf_prop_tmpl_t *t, const char *prop,
3765 scf_count_ranges_t *ranges)
3766 {
3767 scf_values_t vals;
3768 int i = 0;
3769 char **ret;
3770 char *one_range[2];
3771 char *endptr;
3772 char *str = NULL;
3773 uint64_t *min = NULL;
3774 uint64_t *max = NULL;
3775
3776 assert(ranges != NULL);
3777 if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
3778 goto error;
3779 if (vals.value_count == 0) {
3780 /* range values are empty */
3781 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
3782 goto cleanup;
3783 }
3784
3785 min = malloc(vals.value_count * sizeof (uint64_t));
3786 max = malloc(vals.value_count * sizeof (uint64_t));
3787 if (min == NULL || max == NULL) {
3788 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3789 goto cleanup;
3790 }
3791 for (i = 0; i < vals.value_count; ++i) {
3792 /* min and max should be separated by a "," */
3793 if ((str = _separate_by_separator(ret[i], ",", one_range,
3794 2)) == NULL)
3795 goto cleanup;
3796 errno = 0;
3797 min[i] = strtoull(one_range[0], &endptr, 10);
3798 if (errno != 0 || endptr == one_range[0] || *endptr) {
3799 (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3800 goto cleanup;
3801 }
3802 errno = 0;
3803 max[i] = strtoull(one_range[1], &endptr, 10);
3804 if (errno != 0 || endptr == one_range[1] || *endptr) {
3805 (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3806 goto cleanup;
3807 }
3808 if (min[i] > max[i]) {
3809 (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3810 goto cleanup;
3811 }
3812 free(str);
3813 str = NULL;
3814 }
3815 ranges->scr_num_ranges = vals.value_count;
3816 ranges->scr_min = min;
3817 ranges->scr_max = max;
3818 scf_values_destroy(&vals);
3819 return (0);
3820 cleanup:
3821 free(str);
3822 free(min);
3823 free(max);
3824 scf_values_destroy(&vals);
3825 error:
3826 if (ismember(scf_error(), errors_server)) {
3827 return (-1);
3828 } else switch (scf_error()) {
3829 case SCF_ERROR_TYPE_MISMATCH:
3830 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3831 /*FALLTHROUGH*/
3832
3833 case SCF_ERROR_CONSTRAINT_VIOLATED:
3834 case SCF_ERROR_NOT_FOUND:
3835 return (-1);
3836
3837 case SCF_ERROR_INVALID_ARGUMENT:
3838 case SCF_ERROR_NOT_SET:
3839 default:
3840 assert(0);
3841 abort();
3842 }
3843 /*NOTREACHED*/
3844 }
3845
3846 /*
3847 * Returns -1 on failure. Sets scf_error():
3848 * SCF_ERROR_BACKEND_ACCESS
3849 * SCF_ERROR_CONNECTION_BROKEN
3850 * SCF_ERROR_CONSTRAINT_VIOLATED
3851 * SCF_ERROR_DELETED
3852 * SCF_ERROR_HANDLE_DESTROYED
3853 * SCF_ERROR_INTERNAL
3854 * SCF_ERROR_NO_MEMORY
3855 * SCF_ERROR_NO_RESOURCES
3856 * SCF_ERROR_NOT_BOUND
3857 * SCF_ERROR_NOT_FOUND
3858 * Property doesn't exist or exists and has no value.
3859 * SCF_ERROR_PERMISSION_DENIED
3860 * SCF_ERROR_TEMPLATE_INVALID
3861 */
3862 static int
_scf_tmpl_get_int_ranges(const scf_prop_tmpl_t * t,const char * prop,scf_int_ranges_t * ranges)3863 _scf_tmpl_get_int_ranges(const scf_prop_tmpl_t *t, const char *prop,
3864 scf_int_ranges_t *ranges)
3865 {
3866 scf_values_t vals;
3867 int n = 0;
3868 char **ret;
3869 char *one_range[2];
3870 char *endptr;
3871 char *str = NULL;
3872 int64_t *min = NULL;
3873 int64_t *max = NULL;
3874
3875 assert(ranges != NULL);
3876 if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
3877 goto error;
3878 if (vals.value_count == 0) {
3879 /* range values are empty */
3880 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
3881 goto cleanup;
3882 }
3883
3884 min = malloc(vals.value_count * sizeof (int64_t));
3885 max = malloc(vals.value_count * sizeof (int64_t));
3886 if (min == NULL || max == NULL) {
3887 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3888 goto cleanup;
3889 }
3890 while (n < vals.value_count) {
3891 /* min and max should be separated by a "," */
3892 if ((str = _separate_by_separator(ret[n], ",", one_range, 2))
3893 == NULL)
3894 goto cleanup;
3895 errno = 0;
3896 min[n] = strtoll(one_range[0], &endptr, 10);
3897 if (errno != 0 || endptr == one_range[0] || *endptr) {
3898 (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3899 goto cleanup;
3900 }
3901 errno = 0;
3902 max[n] = strtoll(one_range[1], &endptr, 10);
3903 if (errno != 0 || endptr == one_range[1] || *endptr) {
3904 (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3905 goto cleanup;
3906 }
3907 if (min[n] > max[n]) {
3908 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3909 goto cleanup;
3910 }
3911 ++n;
3912 free(str);
3913 str = NULL;
3914 }
3915 ranges->sir_num_ranges = vals.value_count;
3916 ranges->sir_min = min;
3917 ranges->sir_max = max;
3918 scf_values_destroy(&vals);
3919 return (0);
3920 cleanup:
3921 free(str);
3922 free(min);
3923 free(max);
3924 scf_values_destroy(&vals);
3925 error:
3926 if (ismember(scf_error(), errors_server)) {
3927 return (-1);
3928 } else switch (scf_error()) {
3929 case SCF_ERROR_TYPE_MISMATCH:
3930 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3931 /*FALLTHROUGH*/
3932
3933 case SCF_ERROR_CONSTRAINT_VIOLATED:
3934 case SCF_ERROR_NOT_FOUND:
3935 case SCF_ERROR_TEMPLATE_INVALID:
3936 return (-1);
3937
3938 case SCF_ERROR_INVALID_ARGUMENT:
3939 case SCF_ERROR_NOT_SET:
3940 default:
3941 assert(0);
3942 abort();
3943 }
3944 /*NOTREACHED*/
3945 }
3946
3947 /*
3948 * Returns -1 on failure. Sets scf_error():
3949 * SCF_ERROR_BACKEND_ACCESS
3950 * SCF_ERROR_CONNECTION_BROKEN
3951 * SCF_ERROR_CONSTRAINT_VIOLATED
3952 * SCF_ERROR_DELETED
3953 * SCF_ERROR_HANDLE_DESTROYED
3954 * SCF_ERROR_INTERNAL
3955 * SCF_ERROR_NO_MEMORY
3956 * SCF_ERROR_NO_RESOURCES
3957 * SCF_ERROR_NOT_BOUND
3958 * SCF_ERROR_NOT_FOUND
3959 * Property doesn't exist or exists and has no value.
3960 * SCF_ERROR_PERMISSION_DENIED
3961 * SCF_ERROR_TEMPLATE_INVALID
3962 */
3963 int
scf_tmpl_value_count_range_constraints(const scf_prop_tmpl_t * t,scf_count_ranges_t * ranges)3964 scf_tmpl_value_count_range_constraints(const scf_prop_tmpl_t *t,
3965 scf_count_ranges_t *ranges)
3966 {
3967 return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
3968 ranges));
3969 }
3970
3971 int
scf_tmpl_value_int_range_constraints(const scf_prop_tmpl_t * t,scf_int_ranges_t * ranges)3972 scf_tmpl_value_int_range_constraints(const scf_prop_tmpl_t *t,
3973 scf_int_ranges_t *ranges)
3974 {
3975 return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
3976 ranges));
3977 }
3978
3979 int
scf_tmpl_value_count_range_choices(const scf_prop_tmpl_t * t,scf_count_ranges_t * ranges)3980 scf_tmpl_value_count_range_choices(const scf_prop_tmpl_t *t,
3981 scf_count_ranges_t *ranges)
3982 {
3983 return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
3984 ranges));
3985 }
3986
3987 int
scf_tmpl_value_int_range_choices(const scf_prop_tmpl_t * t,scf_int_ranges_t * ranges)3988 scf_tmpl_value_int_range_choices(const scf_prop_tmpl_t *t,
3989 scf_int_ranges_t *ranges)
3990 {
3991 return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
3992 ranges));
3993 }
3994
3995 /*
3996 * Returns -1 on failure. Sets scf_error():
3997 * SCF_ERROR_BACKEND_ACCESS
3998 * SCF_ERROR_CONNECTION_BROKEN
3999 * SCF_ERROR_DELETED
4000 * SCF_ERROR_HANDLE_DESTROYED
4001 * SCF_ERROR_INTERNAL
4002 * SCF_ERROR_NO_MEMORY
4003 * SCF_ERROR_NO_RESOURCES
4004 * SCF_ERROR_NOT_BOUND
4005 * SCF_ERROR_NOT_FOUND
4006 * Property doesn't exist or exists and has no value.
4007 * SCF_ERROR_PERMISSION_DENIED
4008 * SCF_ERROR_TEMPLATE_INVALID
4009 */
4010 int
scf_tmpl_value_name_choices(const scf_prop_tmpl_t * t,scf_values_t * vals)4011 scf_tmpl_value_name_choices(const scf_prop_tmpl_t *t, scf_values_t *vals)
4012 {
4013 int c_flag = 0; /* have not read any value yet */
4014 int r;
4015 char **ret;
4016
4017 /* First, look for explicitly declared choices. */
4018 if ((ret = _read_astrings_values(t->prt_pg,
4019 SCF_PROPERTY_TM_CHOICES_NAME, vals)) != NULL) {
4020 c_flag = 1;
4021 } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
4022 goto error;
4023 }
4024
4025 /* Next, check for choices included by 'values'. */
4026 if ((r = _check_choices_include_values(t->prt_pg, "values")) == 0) {
4027 /* read values_name */
4028 if (c_flag == 1)
4029 /* append values */
4030 ret = _append_astrings_values(t->prt_pg,
4031 SCF_PROPERTY_TM_VALUES_NAME, vals);
4032 else
4033 /* read values */
4034 ret = _read_astrings_values(t->prt_pg,
4035 SCF_PROPERTY_TM_VALUES_NAME, vals);
4036 if (ret != NULL) {
4037 c_flag = 1;
4038 } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
4039 goto error;
4040 }
4041 } else if (r == -1) {
4042 goto error;
4043 }
4044
4045 /* Finally check for choices included by 'constraints'. */
4046 if ((r = _check_choices_include_values(t->prt_pg, "constraints")) ==
4047 0) {
4048 /* read constraint_name */
4049 if (c_flag == 1)
4050 /* append values */
4051 ret = _append_astrings_values(t->prt_pg,
4052 SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
4053 else
4054 /* read values */
4055 ret = _read_astrings_values(t->prt_pg,
4056 SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
4057 if (ret != NULL) {
4058 c_flag = 1;
4059 } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
4060 goto error;
4061 }
4062 } else if (r == -1) {
4063 goto error;
4064 }
4065
4066 if (c_flag == 0 || vals->value_count == 0) {
4067 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
4068 return (-1);
4069 }
4070
4071 return (0);
4072
4073 error:
4074 if (ismember(scf_error(), errors_server)) {
4075 return (-1);
4076 } else switch (scf_error()) {
4077 case SCF_ERROR_TYPE_MISMATCH:
4078 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
4079 return (-1);
4080
4081 case SCF_ERROR_NOT_SET:
4082 case SCF_ERROR_INVALID_ARGUMENT:
4083 default:
4084 assert(0);
4085 abort();
4086 }
4087 /*NOTREACHED*/
4088 }
4089
4090 void
scf_values_destroy(scf_values_t * vals)4091 scf_values_destroy(scf_values_t *vals)
4092 {
4093 int i;
4094 char **items = NULL;
4095 char **str = NULL;
4096
4097 if (vals == NULL)
4098 return;
4099
4100 str = vals->values_as_strings;
4101
4102 /* free values */
4103 switch (vals->value_type) {
4104 case SCF_TYPE_BOOLEAN:
4105 free(vals->values.v_boolean);
4106 break;
4107 case SCF_TYPE_COUNT:
4108 free(vals->values.v_count);
4109 break;
4110 case SCF_TYPE_INTEGER:
4111 free(vals->values.v_integer);
4112 break;
4113 case SCF_TYPE_ASTRING:
4114 items = vals->values.v_astring;
4115 str = NULL;
4116 break;
4117 case SCF_TYPE_USTRING:
4118 items = vals->values.v_ustring;
4119 str = NULL;
4120 break;
4121 case SCF_TYPE_OPAQUE:
4122 items = vals->values.v_opaque;
4123 str = NULL;
4124 break;
4125 case SCF_TYPE_TIME:
4126 free(vals->values.v_time);
4127 break;
4128 default:
4129 assert(0);
4130 abort();
4131 }
4132 for (i = 0; i < vals->value_count; ++i) {
4133 if (items != NULL)
4134 free(items[i]);
4135 if (str != NULL)
4136 free(str[i]);
4137 }
4138 vals->value_count = 0;
4139 free(items);
4140 free(str);
4141 }
4142
4143 /*
4144 * char *_make_value_name()
4145 *
4146 * Construct the prefix for a value common name or value description property.
4147 * It takes the form:
4148 * value_<BASE32 name>_<common_name|description>_
4149 * This is then combined with a localized suffix by the caller to look
4150 * up the property in the repository:
4151 * value_<BASE32 name>_<common_name|description>_<lang>
4152 *
4153 * Returns NULL on failure. Sets scf_error():
4154 * SCF_ERROR_INVALID_ARGUMENT
4155 * Name isn't short enough make a value name with.
4156 * SCF_ERROR_NO_MEMORY
4157 */
4158 static char *
_make_value_name(char * desc_name,const char * value)4159 _make_value_name(char *desc_name, const char *value)
4160 {
4161 char *name = NULL;
4162 char *encoded = NULL;
4163 ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
4164
4165 name = malloc(sz);
4166 encoded = malloc(sz);
4167 if (name == NULL || encoded == NULL) {
4168 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4169 free(name);
4170 free(encoded);
4171 return (NULL);
4172 }
4173
4174 if (scf_encode32(value, strlen(value), encoded, sz, NULL,
4175 SCF_ENCODE32_PAD) != 0) {
4176 /* Shouldn't happen. */
4177 assert(0);
4178 }
4179
4180 (void) strlcpy(name, SCF_PROPERTY_TM_VALUE_PREFIX, sz);
4181
4182 if (strlcat(name, encoded, sz) >= sz) {
4183 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4184 free(name);
4185 free(encoded);
4186 return (NULL);
4187 }
4188
4189 if (strlcat(name, "_", sz) >= sz) {
4190 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4191 free(name);
4192 free(encoded);
4193 return (NULL);
4194 }
4195
4196 if (strlcat(name, desc_name, sz) >= sz) {
4197 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4198 free(name);
4199 free(encoded);
4200 return (NULL);
4201 }
4202
4203 if (strlcat(name, "_", sz) >= sz) {
4204 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4205 free(name);
4206 free(encoded);
4207 return (NULL);
4208 }
4209
4210 free(encoded);
4211 return (name);
4212 }
4213
4214 /*
4215 * ssize_t scf_tmpl_value_common_name()
4216 *
4217 * Populates "out" with an allocated string containing the value's
4218 * common name. Returns the size of the string on successful return.
4219 * out must be freed with free() on successful return.
4220 *
4221 * Returns -1 on failure, sets scf_error() to:
4222 * SCF_ERROR_BACKEND_ACCESS
4223 * SCF_ERROR_CONNECTION_BROKEN
4224 * SCF_ERROR_DELETED
4225 * Property group was deleted.
4226 * SCF_ERROR_HANDLE_DESTROYED
4227 * SCF_ERROR_INTERNAL
4228 * SCF_ERROR_INVALID_ARGUMENT
4229 * name not a valid property name
4230 * name and locale are too long to make a property name
4231 * SCF_ERROR_NO_MEMORY
4232 * SCF_ERROR_NO_RESOURCES
4233 * SCF_ERROR_NOT_BOUND
4234 * SCF_ERROR_NOT_FOUND
4235 * Property doesn't exist or exists and has no value.
4236 * SCF_ERROR_PERMISSION_DENIED
4237 * SCF_ERROR_TEMPLATE_INVALID
4238 * property is not SCF_TYPE_ASTRING has more than one value.
4239 */
4240 ssize_t
scf_tmpl_value_common_name(const scf_prop_tmpl_t * t,const char * locale,const char * value,char ** out)4241 scf_tmpl_value_common_name(const scf_prop_tmpl_t *t, const char *locale,
4242 const char *value, char **out)
4243 {
4244 char *value_name = NULL;
4245
4246 value_name = _make_value_name("common_name", value);
4247 if (value_name == NULL)
4248 return (-1);
4249
4250 *out = _read_localized_astring_from_pg(t->prt_pg, value_name, locale);
4251
4252 free(value_name);
4253
4254 if (*out == NULL)
4255 return (-1);
4256
4257 return (strlen(*out));
4258 }
4259
4260 /*
4261 * ssize_t scf_tmpl_value_description()
4262 *
4263 * Populates "out" with an allocated string containing the value's
4264 * description. Returns the size of the string on successful return.
4265 * out must be freed with free() on successful return.
4266 *
4267 * Returns -1 on failure, sets scf_error() to:
4268 * SCF_ERROR_BACKEND_ACCESS
4269 * SCF_ERROR_CONNECTION_BROKEN
4270 * SCF_ERROR_DELETED
4271 * Property group was deleted.
4272 * SCF_ERROR_HANDLE_DESTROYED
4273 * SCF_ERROR_INTERNAL
4274 * SCF_ERROR_INVALID_ARGUMENT
4275 * name not a valid property name
4276 * name and locale are too long to make a property name
4277 * SCF_ERROR_NO_MEMORY
4278 * SCF_ERROR_NO_RESOURCES
4279 * SCF_ERROR_NOT_BOUND
4280 * SCF_ERROR_NOT_FOUND
4281 * Property doesn't exist or exists and has no value.
4282 * SCF_ERROR_PERMISSION_DENIED
4283 * SCF_ERROR_TEMPLATE_INVALID
4284 * property is not SCF_TYPE_ASTRING has more than one value.
4285 */
4286 ssize_t
scf_tmpl_value_description(const scf_prop_tmpl_t * t,const char * locale,const char * value,char ** out)4287 scf_tmpl_value_description(const scf_prop_tmpl_t *t, const char *locale,
4288 const char *value, char **out)
4289 {
4290 char *value_name = NULL;
4291
4292 value_name = _make_value_name("description", value);
4293 if (value_name == NULL)
4294 return (-1);
4295
4296
4297 *out = _read_localized_astring_from_pg(t->prt_pg, value_name, locale);
4298
4299 free(value_name);
4300
4301 if (*out == NULL)
4302 return (-1);
4303
4304 return (strlen(*out));
4305 }
4306
4307 /*
4308 * Templates error messages format, in human readable form.
4309 * Each line is one error item:
4310 *
4311 * prefix error message
4312 * FMRI="err->te_errs->tes_fmri"
4313 * Property group="err->te_pg_name"
4314 * Property name="err->te_prop_name"
4315 * expected value 1="err->te_ev1"
4316 * expected value 2="err->te_ev2"
4317 * actual value="err->te_actual"
4318 * Tempalte source="err->te_tmpl_fmri"
4319 * pg_pattern name="err->tmpl_pg_name"
4320 * pg_pattern type="err->tmpl_pg_type"
4321 * prop_pattern name="err->tmpl_prop_name"
4322 * prop_pattern type="err->tmpl_prop_type"
4323 *
4324 * To add a new error type, include scf_tmpl_error_type_t in libscf.h
4325 * add one entry in em_desc[], and update the functions pointed by the
4326 * _tmpl_error_access array with the new error code. Also, update the
4327 * scf_tmpl_error_* functions to provide access to desired
4328 * scf_tmpl_error_t fields.
4329 *
4330 * To add a new error item, add a new field to scf_tmpl_error_t, a new field
4331 * in _scf_tmpl_error_desc or a new non-error-dependent string, add a new entry
4332 * in _tmpl_error_access array and create the appropriate get_val, get_desc
4333 * functions.
4334 *
4335 * Changes to both the validation logic and the error types and items must
4336 * be coordinated with the code in svccfg to ensure both libscf and svccfg's
4337 * manifest validation validate the same things.
4338 */
4339
4340 /*
4341 * Container for all template errors on a validated object.
4342 */
4343 struct scf_tmpl_errors {
4344 int tes_index;
4345 int tes_num_errs;
4346 scf_tmpl_error_t **tes_errs;
4347 int tes_errs_size;
4348 const char *tes_fmri;
4349 const char *tes_prefix;
4350 int tes_flag; /* if set, scf_tmpl_error_destroy */
4351 /* will free strings in tes_errs */
4352 };
4353
4354 /*
4355 * Templates error-dependent labels
4356 */
4357 struct _scf_tmpl_error_desc {
4358 const char *em_msg;
4359 const char *em_ev1;
4360 const char *em_ev2;
4361 const char *em_actual;
4362 };
4363
4364 /*
4365 * This array MUST be kept in synch with the template error definition of
4366 * scf_tmpl_error_type_t in libscf.h
4367 */
4368 static struct _scf_tmpl_error_desc em_desc[] = {
4369 /* SCF_TERR_MISSING_PG */
4370 { "Required property group missing", "Name of missing property group",
4371 "Type of missing property group", NULL },
4372 /* SCF_TERR_WRONG_PG_TYPE */
4373 { "Property group has bad type", "Specified type", NULL,
4374 "Actual type" },
4375 /* SCF_TERR_MISSING_PROP */
4376 { "Required property missing", "Name of missing property", NULL, NULL },
4377 /* SCF_TERR_WRONG_PROP_TYPE */
4378 { "Property has bad type", "Specified property type", NULL,
4379 "Actual property type" },
4380 /* SCF_TERR_CARDINALITY_VIOLATION */
4381 { "Number of property values violates cardinality restriction",
4382 "Cardinality minimum", "Cardinality maximum",
4383 "Actual number of values" },
4384 /* SCF_TERR_VALUE_CONSTRAINT_VIOLATED */
4385 { "Property has illegal value", NULL, NULL, "Illegal value" },
4386 /* SCF_TERR_RANGE_VIOLATION */
4387 { "Property value is out of range", NULL, NULL, "Actual value" },
4388 /* SCF_TERR_PG_REDEFINE */
4389 { "Instance redefines pg_pattern", "Instance pg_pattern name",
4390 "Instance pg_pattern type", NULL },
4391 /* SCF_TERR_PROP_TYPE_MISMATCH */
4392 { "Property type and value type mismatch", NULL, NULL, "Value type" },
4393 /* SCF_TERR_VALUE_OUT_OF_RANGE */
4394 { "Value is out of range", NULL, NULL, "Value" },
4395 /* SCF_TERR_INVALID_VALUE */
4396 { "Value is not valid", NULL, NULL, "Value" },
4397 /* SCF_TERR_PG_PATTERN_CONFLICT */
4398 { "Conflicting pg_pattern specifications", "Template source",
4399 "pg_pattern name", "pg_pattern type" },
4400 /* SCF_TERR_PROP_PATTERN_CONFLICT */
4401 { "Conflicting prop_pattern specifications", "Template source",
4402 "prop_pattern name", "prop_pattern type" },
4403 /* SCF_TERR_GENERAL_REDEFINE */
4404 { "Service or instance pg_pattern redefines a global or restarter "
4405 "pg_pattern", "Template source", "pg_pattern name",
4406 "pg_pattern type" },
4407 /* SCF_TERR_INCLUDE_VALUES */
4408 { "Missing constraints or values for include_values element",
4409 "include_values type", NULL, NULL },
4410 /* SCF_TERR_PG_PATTERN_INCOMPLETE */
4411 { "Required pg_pattern is missing a name or type attribute",
4412 NULL, NULL, NULL },
4413 /* SCF_TERR_PROP_PATTERN_INCOMPLETE */
4414 { "Required prop_pattern is missing a type attribute",
4415 NULL, NULL, NULL }
4416 };
4417
4418 /*
4419 * Templates non error-dependent labels
4420 */
4421 static const char *em_fmri = "FMRI";
4422 static const char *em_pg_name = "Property group";
4423 static const char *em_prop_name = "Property name";
4424 static const char *em_tmpl_fmri = "Template source";
4425 static const char *em_tmpl_pg_name = "pg_pattern name";
4426 static const char *em_tmpl_pg_type = "pg_pattern type";
4427 static const char *em_tmpl_prop_name = "prop_pattern name";
4428 static const char *em_tmpl_prop_type = "prop_pattern type";
4429
4430 static const char *
_get_fmri_desc(scf_tmpl_error_t * err)4431 _get_fmri_desc(scf_tmpl_error_t *err)
4432 {
4433 switch (err->te_type) {
4434 case SCF_TERR_MISSING_PG:
4435 case SCF_TERR_WRONG_PG_TYPE:
4436 case SCF_TERR_MISSING_PROP:
4437 case SCF_TERR_WRONG_PROP_TYPE:
4438 case SCF_TERR_CARDINALITY_VIOLATION:
4439 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4440 case SCF_TERR_RANGE_VIOLATION:
4441 case SCF_TERR_PG_REDEFINE:
4442 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4443 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4444 case SCF_TERR_INCLUDE_VALUES:
4445 return (dgettext(TEXT_DOMAIN, em_fmri));
4446 case SCF_TERR_PROP_TYPE_MISMATCH:
4447 case SCF_TERR_VALUE_OUT_OF_RANGE:
4448 case SCF_TERR_INVALID_VALUE:
4449 case SCF_TERR_PG_PATTERN_CONFLICT:
4450 case SCF_TERR_PROP_PATTERN_CONFLICT:
4451 case SCF_TERR_GENERAL_REDEFINE:
4452 default:
4453 return (NULL);
4454 }
4455 }
4456
4457 static const char *
_get_pg_name_desc(scf_tmpl_error_t * err)4458 _get_pg_name_desc(scf_tmpl_error_t *err)
4459 {
4460 switch (err->te_type) {
4461 case SCF_TERR_WRONG_PG_TYPE:
4462 case SCF_TERR_MISSING_PROP:
4463 case SCF_TERR_WRONG_PROP_TYPE:
4464 case SCF_TERR_CARDINALITY_VIOLATION:
4465 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4466 case SCF_TERR_RANGE_VIOLATION:
4467 return (dgettext(TEXT_DOMAIN, em_pg_name));
4468 case SCF_TERR_MISSING_PG:
4469 case SCF_TERR_PG_REDEFINE:
4470 case SCF_TERR_PROP_TYPE_MISMATCH:
4471 case SCF_TERR_VALUE_OUT_OF_RANGE:
4472 case SCF_TERR_INVALID_VALUE:
4473 case SCF_TERR_PG_PATTERN_CONFLICT:
4474 case SCF_TERR_PROP_PATTERN_CONFLICT:
4475 case SCF_TERR_GENERAL_REDEFINE:
4476 case SCF_TERR_INCLUDE_VALUES:
4477 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4478 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4479 default:
4480 return (NULL);
4481 }
4482 }
4483
4484 static const char *
_get_prop_name_desc(scf_tmpl_error_t * err)4485 _get_prop_name_desc(scf_tmpl_error_t *err)
4486 {
4487 switch (err->te_type) {
4488 case SCF_TERR_WRONG_PROP_TYPE:
4489 case SCF_TERR_CARDINALITY_VIOLATION:
4490 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4491 case SCF_TERR_RANGE_VIOLATION:
4492 return (dgettext(TEXT_DOMAIN, em_prop_name));
4493 case SCF_TERR_MISSING_PG:
4494 case SCF_TERR_WRONG_PG_TYPE:
4495 case SCF_TERR_MISSING_PROP:
4496 case SCF_TERR_PG_REDEFINE:
4497 case SCF_TERR_PROP_TYPE_MISMATCH:
4498 case SCF_TERR_VALUE_OUT_OF_RANGE:
4499 case SCF_TERR_INVALID_VALUE:
4500 case SCF_TERR_PG_PATTERN_CONFLICT:
4501 case SCF_TERR_PROP_PATTERN_CONFLICT:
4502 case SCF_TERR_GENERAL_REDEFINE:
4503 case SCF_TERR_INCLUDE_VALUES:
4504 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4505 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4506 default:
4507 return (NULL);
4508 }
4509 }
4510
4511 static const char *
_get_ev1_desc(scf_tmpl_error_t * err)4512 _get_ev1_desc(scf_tmpl_error_t *err)
4513 {
4514 switch (err->te_type) {
4515 case SCF_TERR_MISSING_PG:
4516 case SCF_TERR_WRONG_PG_TYPE:
4517 case SCF_TERR_MISSING_PROP:
4518 case SCF_TERR_WRONG_PROP_TYPE:
4519 case SCF_TERR_CARDINALITY_VIOLATION:
4520 case SCF_TERR_RANGE_VIOLATION:
4521 case SCF_TERR_PG_REDEFINE:
4522 case SCF_TERR_PG_PATTERN_CONFLICT:
4523 case SCF_TERR_PROP_PATTERN_CONFLICT:
4524 case SCF_TERR_GENERAL_REDEFINE:
4525 case SCF_TERR_INCLUDE_VALUES:
4526 return (dgettext(TEXT_DOMAIN, em_desc[err->te_type].em_ev1));
4527 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4528 case SCF_TERR_PROP_TYPE_MISMATCH:
4529 case SCF_TERR_VALUE_OUT_OF_RANGE:
4530 case SCF_TERR_INVALID_VALUE:
4531 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4532 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4533 default:
4534 return (NULL);
4535 }
4536 }
4537
4538 static const char *
_get_ev2_desc(scf_tmpl_error_t * err)4539 _get_ev2_desc(scf_tmpl_error_t *err)
4540 {
4541 switch (err->te_type) {
4542 case SCF_TERR_MISSING_PG:
4543 case SCF_TERR_CARDINALITY_VIOLATION:
4544 case SCF_TERR_RANGE_VIOLATION:
4545 case SCF_TERR_PG_REDEFINE:
4546 case SCF_TERR_PG_PATTERN_CONFLICT:
4547 case SCF_TERR_PROP_PATTERN_CONFLICT:
4548 case SCF_TERR_GENERAL_REDEFINE:
4549 return (dgettext(TEXT_DOMAIN, em_desc[err->te_type].em_ev2));
4550 case SCF_TERR_WRONG_PG_TYPE:
4551 case SCF_TERR_MISSING_PROP:
4552 case SCF_TERR_WRONG_PROP_TYPE:
4553 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4554 case SCF_TERR_PROP_TYPE_MISMATCH:
4555 case SCF_TERR_VALUE_OUT_OF_RANGE:
4556 case SCF_TERR_INVALID_VALUE:
4557 case SCF_TERR_INCLUDE_VALUES:
4558 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4559 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4560 default:
4561 return (NULL);
4562 }
4563 }
4564
4565 static const char *
_get_actual_desc(scf_tmpl_error_t * err)4566 _get_actual_desc(scf_tmpl_error_t *err)
4567 {
4568 switch (err->te_type) {
4569 case SCF_TERR_MISSING_PG:
4570 case SCF_TERR_WRONG_PG_TYPE:
4571 case SCF_TERR_WRONG_PROP_TYPE:
4572 case SCF_TERR_CARDINALITY_VIOLATION:
4573 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4574 case SCF_TERR_RANGE_VIOLATION:
4575 case SCF_TERR_PROP_TYPE_MISMATCH:
4576 case SCF_TERR_VALUE_OUT_OF_RANGE:
4577 case SCF_TERR_INVALID_VALUE:
4578 case SCF_TERR_PG_PATTERN_CONFLICT:
4579 case SCF_TERR_PROP_PATTERN_CONFLICT:
4580 case SCF_TERR_GENERAL_REDEFINE:
4581 case SCF_TERR_INCLUDE_VALUES:
4582 return (dgettext(TEXT_DOMAIN,
4583 em_desc[err->te_type].em_actual));
4584 case SCF_TERR_MISSING_PROP:
4585 case SCF_TERR_PG_REDEFINE:
4586 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4587 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4588 default:
4589 return (NULL);
4590 }
4591 }
4592
4593 static const char *
_get_tmpl_fmri_desc(scf_tmpl_error_t * err)4594 _get_tmpl_fmri_desc(scf_tmpl_error_t *err)
4595 {
4596 switch (err->te_type) {
4597 case SCF_TERR_MISSING_PG:
4598 case SCF_TERR_WRONG_PG_TYPE:
4599 case SCF_TERR_MISSING_PROP:
4600 case SCF_TERR_WRONG_PROP_TYPE:
4601 case SCF_TERR_CARDINALITY_VIOLATION:
4602 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4603 case SCF_TERR_RANGE_VIOLATION:
4604 case SCF_TERR_PG_REDEFINE:
4605 case SCF_TERR_PROP_TYPE_MISMATCH:
4606 case SCF_TERR_VALUE_OUT_OF_RANGE:
4607 case SCF_TERR_INVALID_VALUE:
4608 case SCF_TERR_PG_PATTERN_CONFLICT:
4609 case SCF_TERR_PROP_PATTERN_CONFLICT:
4610 case SCF_TERR_GENERAL_REDEFINE:
4611 case SCF_TERR_INCLUDE_VALUES:
4612 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4613 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4614 return (dgettext(TEXT_DOMAIN, em_tmpl_fmri));
4615 default:
4616 return (NULL);
4617 }
4618 }
4619
4620 static const char *
_get_tmpl_pg_name_desc(scf_tmpl_error_t * err)4621 _get_tmpl_pg_name_desc(scf_tmpl_error_t *err)
4622 {
4623 switch (err->te_type) {
4624 case SCF_TERR_MISSING_PG:
4625 case SCF_TERR_WRONG_PG_TYPE:
4626 case SCF_TERR_MISSING_PROP:
4627 case SCF_TERR_WRONG_PROP_TYPE:
4628 case SCF_TERR_CARDINALITY_VIOLATION:
4629 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4630 case SCF_TERR_RANGE_VIOLATION:
4631 case SCF_TERR_PG_REDEFINE:
4632 case SCF_TERR_PROP_TYPE_MISMATCH:
4633 case SCF_TERR_VALUE_OUT_OF_RANGE:
4634 case SCF_TERR_INVALID_VALUE:
4635 case SCF_TERR_PG_PATTERN_CONFLICT:
4636 case SCF_TERR_PROP_PATTERN_CONFLICT:
4637 case SCF_TERR_GENERAL_REDEFINE:
4638 case SCF_TERR_INCLUDE_VALUES:
4639 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4640 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4641 return (dgettext(TEXT_DOMAIN, em_tmpl_pg_name));
4642 default:
4643 return (NULL);
4644 }
4645 }
4646
4647 static const char *
_get_tmpl_pg_type_desc(scf_tmpl_error_t * err)4648 _get_tmpl_pg_type_desc(scf_tmpl_error_t *err)
4649 {
4650 switch (err->te_type) {
4651 case SCF_TERR_MISSING_PG:
4652 case SCF_TERR_WRONG_PG_TYPE:
4653 case SCF_TERR_MISSING_PROP:
4654 case SCF_TERR_WRONG_PROP_TYPE:
4655 case SCF_TERR_CARDINALITY_VIOLATION:
4656 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4657 case SCF_TERR_RANGE_VIOLATION:
4658 case SCF_TERR_PG_REDEFINE:
4659 case SCF_TERR_PROP_TYPE_MISMATCH:
4660 case SCF_TERR_VALUE_OUT_OF_RANGE:
4661 case SCF_TERR_INVALID_VALUE:
4662 case SCF_TERR_PG_PATTERN_CONFLICT:
4663 case SCF_TERR_PROP_PATTERN_CONFLICT:
4664 case SCF_TERR_GENERAL_REDEFINE:
4665 case SCF_TERR_INCLUDE_VALUES:
4666 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4667 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4668 return (dgettext(TEXT_DOMAIN, em_tmpl_pg_type));
4669 default:
4670 return (NULL);
4671 }
4672 }
4673
4674 static const char *
_get_tmpl_prop_name_desc(scf_tmpl_error_t * err)4675 _get_tmpl_prop_name_desc(scf_tmpl_error_t *err)
4676 {
4677 switch (err->te_type) {
4678 case SCF_TERR_MISSING_PROP:
4679 case SCF_TERR_WRONG_PROP_TYPE:
4680 case SCF_TERR_CARDINALITY_VIOLATION:
4681 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4682 case SCF_TERR_RANGE_VIOLATION:
4683 case SCF_TERR_PROP_TYPE_MISMATCH:
4684 case SCF_TERR_VALUE_OUT_OF_RANGE:
4685 case SCF_TERR_INVALID_VALUE:
4686 case SCF_TERR_PROP_PATTERN_CONFLICT:
4687 case SCF_TERR_INCLUDE_VALUES:
4688 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4689 return (dgettext(TEXT_DOMAIN, em_tmpl_prop_name));
4690 case SCF_TERR_MISSING_PG:
4691 case SCF_TERR_WRONG_PG_TYPE:
4692 case SCF_TERR_PG_REDEFINE:
4693 case SCF_TERR_PG_PATTERN_CONFLICT:
4694 case SCF_TERR_GENERAL_REDEFINE:
4695 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4696 default:
4697 return (NULL);
4698 }
4699 }
4700
4701 static const char *
_get_tmpl_prop_type_desc(scf_tmpl_error_t * err)4702 _get_tmpl_prop_type_desc(scf_tmpl_error_t *err)
4703 {
4704 switch (err->te_type) {
4705 case SCF_TERR_MISSING_PROP:
4706 case SCF_TERR_WRONG_PROP_TYPE:
4707 case SCF_TERR_CARDINALITY_VIOLATION:
4708 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4709 case SCF_TERR_RANGE_VIOLATION:
4710 case SCF_TERR_PROP_TYPE_MISMATCH:
4711 case SCF_TERR_VALUE_OUT_OF_RANGE:
4712 case SCF_TERR_INVALID_VALUE:
4713 case SCF_TERR_PROP_PATTERN_CONFLICT:
4714 case SCF_TERR_INCLUDE_VALUES:
4715 return (dgettext(TEXT_DOMAIN, em_tmpl_prop_type));
4716 case SCF_TERR_MISSING_PG:
4717 case SCF_TERR_WRONG_PG_TYPE:
4718 case SCF_TERR_PG_REDEFINE:
4719 case SCF_TERR_PG_PATTERN_CONFLICT:
4720 case SCF_TERR_GENERAL_REDEFINE:
4721 case SCF_TERR_PG_PATTERN_INCOMPLETE:
4722 case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4723 default:
4724 return (NULL);
4725 }
4726 }
4727
4728 static const char *
_get_fmri_val(scf_tmpl_error_t * err)4729 _get_fmri_val(scf_tmpl_error_t *err)
4730 {
4731 assert(err != NULL && err->te_errs != NULL &&
4732 err->te_errs->tes_fmri != NULL);
4733 return (err->te_errs->tes_fmri);
4734 }
4735
4736 static const char *
_get_pg_name_val(scf_tmpl_error_t * err)4737 _get_pg_name_val(scf_tmpl_error_t *err)
4738 {
4739 assert(err != NULL);
4740 return (err->te_pg_name);
4741 }
4742
4743 static const char *
_get_prop_name_val(scf_tmpl_error_t * err)4744 _get_prop_name_val(scf_tmpl_error_t *err)
4745 {
4746 assert(err != NULL);
4747 return (err->te_prop_name);
4748 }
4749
4750 static const char *
_get_ev1_val(scf_tmpl_error_t * err)4751 _get_ev1_val(scf_tmpl_error_t *err)
4752 {
4753 assert(err != NULL);
4754 return (err->te_ev1);
4755 }
4756
4757 static const char *
_get_ev2_val(scf_tmpl_error_t * err)4758 _get_ev2_val(scf_tmpl_error_t *err)
4759 {
4760 assert(err != NULL);
4761 return (err->te_ev2);
4762 }
4763
4764 static const char *
_get_actual_val(scf_tmpl_error_t * err)4765 _get_actual_val(scf_tmpl_error_t *err)
4766 {
4767 assert(err != NULL);
4768 return (err->te_actual);
4769 }
4770
4771 static const char *
_get_tmpl_fmri_val(scf_tmpl_error_t * err)4772 _get_tmpl_fmri_val(scf_tmpl_error_t *err)
4773 {
4774 assert(err != NULL);
4775 return (err->te_tmpl_fmri);
4776 }
4777
4778 static const char *
_get_tmpl_pg_name_val(scf_tmpl_error_t * err)4779 _get_tmpl_pg_name_val(scf_tmpl_error_t *err)
4780 {
4781 assert(err != NULL);
4782 return (err->te_tmpl_pg_name);
4783 }
4784
4785 static const char *
_get_tmpl_pg_type_val(scf_tmpl_error_t * err)4786 _get_tmpl_pg_type_val(scf_tmpl_error_t *err)
4787 {
4788 assert(err != NULL);
4789 return (err->te_tmpl_pg_type);
4790 }
4791
4792 static const char *
_get_tmpl_prop_name_val(scf_tmpl_error_t * err)4793 _get_tmpl_prop_name_val(scf_tmpl_error_t *err)
4794 {
4795 assert(err != NULL);
4796 return (err->te_tmpl_prop_name);
4797 }
4798
4799 static const char *
_get_tmpl_prop_type_val(scf_tmpl_error_t * err)4800 _get_tmpl_prop_type_val(scf_tmpl_error_t *err)
4801 {
4802 assert(err != NULL);
4803 return (err->te_tmpl_prop_type);
4804 }
4805
4806 /*
4807 * Templates error item retrival functions
4808 */
4809 typedef const char *(*get_em)(scf_tmpl_error_t *);
4810
4811 /*
4812 * if new items (lines) are added to the templates error messages,
4813 * new entries in this array (and new fuctions) will be required.
4814 */
4815 static struct _tmpl_error_access {
4816 get_em get_desc;
4817 get_em get_val;
4818 } _tmpl_error_items[] = {
4819 { (get_em)_get_fmri_desc, (get_em)_get_fmri_val },
4820 { (get_em)_get_pg_name_desc, (get_em)_get_pg_name_val },
4821 { (get_em)_get_prop_name_desc, (get_em)_get_prop_name_val },
4822 { (get_em)_get_ev1_desc, (get_em)_get_ev1_val },
4823 { (get_em)_get_ev2_desc, (get_em)_get_ev2_val },
4824 { (get_em)_get_actual_desc, (get_em)_get_actual_val },
4825 { (get_em)_get_tmpl_fmri_desc, (get_em)_get_tmpl_fmri_val },
4826 { (get_em)_get_tmpl_pg_name_desc, (get_em)_get_tmpl_pg_name_val },
4827 { (get_em)_get_tmpl_pg_type_desc, (get_em)_get_tmpl_pg_type_val },
4828 { (get_em)_get_tmpl_prop_name_desc, (get_em)_get_tmpl_prop_name_val },
4829 { (get_em)_get_tmpl_prop_type_desc, (get_em)_get_tmpl_prop_type_val },
4830 { NULL }
4831 };
4832
4833 /*
4834 * Allocate a new scf_tmpl_error_t and add it to the errs list provided.
4835 * Returns NULL on failure. Sets scf_error():
4836 * SCF_ERROR_NO_MEMORY
4837 */
4838 static scf_tmpl_error_t *
_create_error(scf_tmpl_errors_t * errs)4839 _create_error(scf_tmpl_errors_t *errs)
4840 {
4841 scf_tmpl_error_t *ret;
4842 scf_tmpl_error_t **saved_errs;
4843
4844 assert(errs != NULL);
4845 ret = calloc(1, sizeof (scf_tmpl_error_t));
4846 if (ret == NULL) {
4847 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4848 return (NULL);
4849 }
4850
4851 ret->te_errs = errs;
4852
4853 assert(errs->tes_num_errs <= errs->tes_errs_size);
4854 if (errs->tes_num_errs == errs->tes_errs_size) {
4855 /* Time to grow the pointer array. */
4856 saved_errs = errs->tes_errs;
4857 errs->tes_errs = calloc(2 * errs->tes_errs_size,
4858 sizeof (scf_tmpl_error_t *));
4859 if (errs->tes_errs == NULL) {
4860 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4861 errs->tes_errs = saved_errs;
4862 free(ret);
4863 return (NULL);
4864 }
4865 (void) memcpy(errs->tes_errs, saved_errs, errs->tes_errs_size *
4866 sizeof (scf_tmpl_error_t *));
4867 errs->tes_errs_size = 2 * errs->tes_errs_size;
4868 free(saved_errs);
4869 }
4870
4871 errs->tes_errs[errs->tes_num_errs] = ret;
4872 errs->tes_num_errs++;
4873
4874 return (ret);
4875 }
4876
4877 /*
4878 *
4879 * If destroy_strings is set, scf_tmpl_errors_destroy will free the
4880 * strings in scf_tmpl_error_t entries.
4881 *
4882 * Returns NULL on failure. Sets scf_error():
4883 * SCF_ERROR_NO_MEMORY
4884 */
4885 scf_tmpl_errors_t *
_scf_create_errors(const char * fmri,int destroy_strings)4886 _scf_create_errors(const char *fmri, int destroy_strings)
4887 {
4888 scf_tmpl_errors_t *ret;
4889 int errs_size = 20;
4890
4891 assert(fmri != NULL);
4892
4893 ret = calloc(1, sizeof (scf_tmpl_errors_t));
4894 if (ret == NULL) {
4895 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4896 return (NULL);
4897 }
4898
4899 ret->tes_index = 0;
4900 ret->tes_num_errs = 0;
4901 if ((ret->tes_fmri = strdup(fmri)) == NULL) {
4902 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4903 free(ret);
4904 return (NULL);
4905 }
4906
4907 ret->tes_prefix = strdup("");
4908 if (ret->tes_prefix == NULL) {
4909 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4910 free((char *)ret->tes_fmri);
4911 free(ret);
4912 return (NULL);
4913 }
4914 ret->tes_flag = destroy_strings;
4915
4916 /* Make space for a few errors. */
4917 ret->tes_errs = calloc(errs_size, sizeof (scf_tmpl_error_t *));
4918 if (ret->tes_errs == NULL) {
4919 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4920 free((char *)ret->tes_fmri);
4921 free((char *)ret->tes_prefix);
4922 free(ret);
4923 return (NULL);
4924 }
4925 ret->tes_errs_size = errs_size;
4926
4927 return (ret);
4928 }
4929
4930 /*
4931 * return 0 on success, if fails set scf_error() to:
4932 *
4933 * SCF_ERROR_NO_MEMORY
4934 */
4935 int
_scf_tmpl_error_set_prefix(scf_tmpl_errors_t * errs,const char * prefix)4936 _scf_tmpl_error_set_prefix(scf_tmpl_errors_t *errs, const char *prefix)
4937 {
4938 free((void *) errs->tes_prefix);
4939 if (prefix == NULL)
4940 errs->tes_prefix = strdup("");
4941 else
4942 errs->tes_prefix = strdup(prefix);
4943 if (errs->tes_prefix == NULL) {
4944 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4945 return (-1);
4946 }
4947 return (0);
4948 }
4949
4950 /*
4951 *
4952 * Returns -1 on failure. Sets scf_error():
4953 * SCF_ERROR_NO_MEMORY
4954 */
4955 int
_scf_tmpl_add_error(scf_tmpl_errors_t * errs,scf_tmpl_error_type_t type,const char * pg_name,const char * prop_name,const char * ev1,const char * ev2,const char * actual,const char * tmpl_fmri,const char * tmpl_pg_name,const char * tmpl_pg_type,const char * tmpl_prop_name,const char * tmpl_prop_type)4956 _scf_tmpl_add_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
4957 const char *pg_name, const char *prop_name,
4958 const char *ev1, const char *ev2, const char *actual,
4959 const char *tmpl_fmri, const char *tmpl_pg_name, const char *tmpl_pg_type,
4960 const char *tmpl_prop_name, const char *tmpl_prop_type)
4961 {
4962 scf_tmpl_error_t *err;
4963
4964 assert(errs != NULL);
4965 assert(tmpl_fmri != NULL);
4966
4967 err = _create_error(errs);
4968 if (err == NULL)
4969 return (-1);
4970
4971 err->te_type = type;
4972 err->te_pg_name = pg_name;
4973 err->te_prop_name = prop_name;
4974 err->te_ev1 = ev1;
4975 err->te_ev2 = ev2;
4976 err->te_actual = actual;
4977 err->te_tmpl_fmri = tmpl_fmri;
4978 err->te_tmpl_pg_name = tmpl_pg_name;
4979 err->te_tmpl_pg_type = tmpl_pg_type;
4980 err->te_tmpl_prop_name = tmpl_prop_name;
4981 err->te_tmpl_prop_type = tmpl_prop_type;
4982
4983 return (0);
4984 }
4985
4986 /*
4987 * returns an allocated string that must be freed with free()
4988 * string contains converted 64-bit integer value
4989 * flag set for signed values
4990 * if fails return NULL and set scf_error() to:
4991 * SCF_ERROR_NO_MEMORY
4992 */
4993 static char *
_val_to_string(uint64_t val,int flag)4994 _val_to_string(uint64_t val, int flag)
4995 {
4996 ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
4997 char *buf;
4998
4999 buf = malloc(sz);
5000 if (buf == NULL) {
5001 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5002 return (NULL);
5003 }
5004
5005 if (flag == 0)
5006 (void) snprintf(buf, sz, "%" PRIu64, val);
5007 else
5008 (void) snprintf(buf, sz, "%" PRIi64, (int64_t)val);
5009
5010 return (buf);
5011 }
5012
5013 /*
5014 * return 0 on success, -1 on failure.
5015 * set scf_error() to:
5016 * SCF_ERROR_BACKEND_ACCESS
5017 * SCF_ERROR_CONNECTION_BROKEN
5018 * SCF_ERROR_DELETED
5019 * SCF_ERROR_HANDLE_DESTROYED
5020 * SCF_ERROR_INTERNAL
5021 * SCF_ERROR_NO_MEMORY
5022 * SCF_ERROR_NO_RESOURCES
5023 * SCF_ERROR_NOT_BOUND
5024 * SCF_ERROR_PERMISSION_DENIED
5025 * SCF_ERROR_TEMPLATE_INVALID
5026 */
5027 static int
_add_tmpl_missing_pg_error(scf_tmpl_errors_t * errs,scf_pg_tmpl_t * t)5028 _add_tmpl_missing_pg_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t)
5029 {
5030 char *ev1 = NULL;
5031 char *ev2 = NULL;
5032 char *t_fmri = NULL;
5033 char *t_pg_name = NULL;
5034 char *t_pg_type = NULL;
5035
5036 if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
5037 return (-1);
5038 if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
5039 goto cleanup;
5040 }
5041 if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
5042 goto cleanup;
5043 }
5044 if ((ev1 = strdup(t_pg_name)) == NULL) {
5045 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5046 goto cleanup;
5047 }
5048 if ((ev2 = strdup(t_pg_type)) == NULL) {
5049 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5050 goto cleanup;
5051 }
5052
5053 return (_scf_tmpl_add_error(errs, SCF_TERR_MISSING_PG, NULL, NULL, ev1,
5054 ev2, NULL, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
5055 cleanup:
5056 free(ev1);
5057 free(ev2);
5058 free(t_fmri);
5059 free(t_pg_name);
5060 free(t_pg_type);
5061 return (-1);
5062 }
5063
5064 /*
5065 * return 0 on success, -1 on failure.
5066 * set scf_error() to:
5067 * SCF_ERROR_BACKEND_ACCESS
5068 * SCF_ERROR_CONNECTION_BROKEN
5069 * SCF_ERROR_DELETED
5070 * SCF_ERROR_HANDLE_DESTROYED
5071 * SCF_ERROR_INTERNAL
5072 * SCF_ERROR_NO_MEMORY
5073 * SCF_ERROR_NO_RESOURCES
5074 * SCF_ERROR_NOT_BOUND
5075 * SCF_ERROR_PERMISSION_DENIED
5076 * SCF_ERROR_TEMPLATE_INVALID
5077 */
5078 static int
_add_tmpl_wrong_pg_type_error(scf_tmpl_errors_t * errs,scf_pg_tmpl_t * t,scf_propertygroup_t * pg)5079 _add_tmpl_wrong_pg_type_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
5080 scf_propertygroup_t *pg)
5081 {
5082 char *pg_name = NULL;
5083 char *ev1 = NULL;
5084 char *actual = NULL;
5085 char *t_fmri = NULL;
5086 char *t_pg_name = NULL;
5087 char *t_pg_type = NULL;
5088
5089 if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
5090 return (-1);
5091 if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5092 goto cleanup;
5093 if ((actual = _scf_get_pg_type(pg)) == NULL)
5094 goto cleanup;
5095 if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
5096 goto cleanup;
5097 }
5098 if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
5099 goto cleanup;
5100 }
5101 if ((ev1 = strdup(t_pg_type)) == NULL) {
5102 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5103 goto cleanup;
5104 }
5105
5106 return (_scf_tmpl_add_error(errs, SCF_TERR_WRONG_PG_TYPE, pg_name, NULL,
5107 ev1, NULL, actual, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
5108 cleanup:
5109 free(pg_name);
5110 free(ev1);
5111 free(actual);
5112 free(t_fmri);
5113 free(t_pg_name);
5114 free(t_pg_type);
5115 return (-1);
5116 }
5117
5118 /*
5119 * return 0 on success, -1 on failure.
5120 * set scf_error() to:
5121 * SCF_ERROR_BACKEND_ACCESS
5122 * SCF_ERROR_CONNECTION_BROKEN
5123 * SCF_ERROR_DELETED
5124 * SCF_ERROR_HANDLE_DESTROYED
5125 * SCF_ERROR_INTERNAL
5126 * SCF_ERROR_NO_MEMORY
5127 * SCF_ERROR_NO_RESOURCES
5128 * SCF_ERROR_NOT_BOUND
5129 * SCF_ERROR_PERMISSION_DENIED
5130 * SCF_ERROR_TEMPLATE_INVALID
5131 */
5132 static int
_add_tmpl_missing_prop_error(scf_tmpl_errors_t * errs,scf_pg_tmpl_t * t,scf_propertygroup_t * pg,const scf_prop_tmpl_t * pt)5133 _add_tmpl_missing_prop_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
5134 scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt)
5135 {
5136 char *pg_name = NULL;
5137 char *ev1 = NULL;
5138 char *t_fmri = NULL;
5139 char *t_pg_name = NULL;
5140 char *t_pg_type = NULL;
5141 char *t_prop_name = NULL;
5142 char *t_prop_type = NULL;
5143
5144 if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
5145 return (-1);
5146 if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5147 goto cleanup;
5148 if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
5149 goto cleanup;
5150 }
5151 if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
5152 goto cleanup;
5153 }
5154 if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5155 goto cleanup;
5156 }
5157 t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5158 if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5159 free(t_prop_type);
5160 t_prop_type = NULL;
5161 } else if (t_prop_type == NULL) {
5162 goto cleanup;
5163 }
5164 if (t_prop_type == NULL)
5165 if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5166 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5167 goto cleanup;
5168 }
5169 if ((ev1 = strdup(t_prop_name)) == NULL) {
5170 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5171 goto cleanup;
5172 }
5173
5174 return (_scf_tmpl_add_error(errs, SCF_TERR_MISSING_PROP, pg_name, NULL,
5175 ev1, NULL, NULL, t_fmri, t_pg_name, t_pg_type, t_prop_name,
5176 t_prop_type));
5177 cleanup:
5178 free(pg_name);
5179 free(ev1);
5180 free(t_fmri);
5181 free(t_pg_name);
5182 free(t_pg_type);
5183 free(t_prop_name);
5184 free(t_prop_type);
5185 return (-1);
5186 }
5187
5188 /*
5189 * return 0 on success, -1 on failure.
5190 * set scf_error() to:
5191 * SCF_ERROR_BACKEND_ACCESS
5192 * SCF_ERROR_CONNECTION_BROKEN
5193 * SCF_ERROR_DELETED
5194 * SCF_ERROR_HANDLE_DESTROYED
5195 * SCF_ERROR_INTERNAL
5196 * SCF_ERROR_NO_MEMORY
5197 * SCF_ERROR_NO_RESOURCES
5198 * SCF_ERROR_NOT_BOUND
5199 * SCF_ERROR_PERMISSION_DENIED
5200 * SCF_ERROR_TEMPLATE_INVALID
5201 */
5202 static int
_add_tmpl_wrong_prop_type_error(scf_tmpl_errors_t * errs,scf_propertygroup_t * pg,const scf_prop_tmpl_t * pt,scf_property_t * prop)5203 _add_tmpl_wrong_prop_type_error(scf_tmpl_errors_t *errs,
5204 scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop)
5205 {
5206 char *pg_name = NULL;
5207 char *prop_name = NULL;
5208 char *ev1 = NULL;
5209 char *actual = NULL;
5210 char *t_fmri = NULL;
5211 char *t_pg_name = NULL;
5212 char *t_pg_type = NULL;
5213 char *t_prop_name = NULL;
5214 char *t_prop_type = NULL;
5215
5216 if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5217 return (-1);
5218 if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5219 goto cleanup;
5220 if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5221 goto cleanup;
5222 if ((actual = _scf_get_prop_type(prop)) == NULL)
5223 goto cleanup;
5224 if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5225 goto cleanup;
5226 }
5227 if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5228 goto cleanup;
5229 }
5230 if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5231 goto cleanup;
5232 }
5233 t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5234 if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5235 free(t_prop_type);
5236 t_prop_type = NULL;
5237 } else if (t_prop_type == NULL) {
5238 goto cleanup;
5239 }
5240 if (t_prop_type == NULL)
5241 if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5242 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5243 goto cleanup;
5244 }
5245 if ((ev1 = strdup(t_prop_type)) == NULL) {
5246 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5247 goto cleanup;
5248 }
5249
5250 return (_scf_tmpl_add_error(errs, SCF_TERR_WRONG_PROP_TYPE, pg_name,
5251 prop_name, ev1, NULL, actual, t_fmri, t_pg_name, t_pg_type,
5252 t_prop_name, t_prop_type));
5253 cleanup:
5254 free(pg_name);
5255 free(prop_name);
5256 free(ev1);
5257 free(actual);
5258 free(t_fmri);
5259 free(t_pg_name);
5260 free(t_pg_type);
5261 free(t_prop_name);
5262 free(t_prop_type);
5263 return (-1);
5264 }
5265
5266 /*
5267 * return 0 on success, -1 on failure.
5268 * set scf_error() to:
5269 * SCF_ERROR_BACKEND_ACCESS
5270 * SCF_ERROR_CONNECTION_BROKEN
5271 * SCF_ERROR_DELETED
5272 * SCF_ERROR_HANDLE_DESTROYED
5273 * SCF_ERROR_INTERNAL
5274 * SCF_ERROR_NO_MEMORY
5275 * SCF_ERROR_NO_RESOURCES
5276 * SCF_ERROR_NOT_BOUND
5277 * SCF_ERROR_PERMISSION_DENIED
5278 * SCF_ERROR_TEMPLATE_INVALID
5279 */
5280 static int
_add_tmpl_count_error(scf_tmpl_errors_t * errs,scf_tmpl_error_type_t type,scf_propertygroup_t * pg,const scf_prop_tmpl_t * pt,scf_property_t * prop,uint64_t count,uint64_t * min,uint64_t * max)5281 _add_tmpl_count_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
5282 scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
5283 uint64_t count, uint64_t *min, uint64_t *max)
5284 {
5285 char *pg_name = NULL;
5286 char *prop_name = NULL;
5287 char *s_min = NULL;
5288 char *s_max = NULL;
5289 char *num = NULL;
5290 char *t_fmri = NULL;
5291 char *t_pg_name = NULL;
5292 char *t_pg_type = NULL;
5293 char *t_prop_name = NULL;
5294 char *t_prop_type = NULL;
5295
5296 if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5297 return (-1);
5298 switch (type) {
5299 case SCF_TERR_RANGE_VIOLATION:
5300 case SCF_TERR_CARDINALITY_VIOLATION:
5301 if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5302 goto cleanup;
5303 if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5304 goto cleanup;
5305 break;
5306 case SCF_TERR_VALUE_OUT_OF_RANGE:
5307 /* keep pg_name = NULL and prop_name = NULL */
5308 break;
5309 }
5310 if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5311 goto cleanup;
5312 }
5313 if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5314 goto cleanup;
5315 }
5316 if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5317 goto cleanup;
5318 }
5319 t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5320 if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5321 free(t_prop_type);
5322 t_prop_type = NULL;
5323 } else if (t_prop_type == NULL) {
5324 goto cleanup;
5325 }
5326 if (t_prop_type == NULL)
5327 if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5328 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5329 goto cleanup;
5330 }
5331 if (min == NULL) {
5332 if ((s_min = strdup("")) == NULL) {
5333 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5334 goto cleanup;
5335 }
5336 } else {
5337 if ((s_min = _val_to_string(*min, 0)) == NULL) {
5338 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5339 goto cleanup;
5340 }
5341 }
5342 if (max == NULL) {
5343 if ((s_max = strdup("")) == NULL) {
5344 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5345 goto cleanup;
5346 }
5347 } else {
5348 if ((s_max = _val_to_string(*max, 0)) == NULL) {
5349 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5350 goto cleanup;
5351 }
5352 }
5353 if ((num = _val_to_string(count, 0)) == NULL) {
5354 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5355 goto cleanup;
5356 }
5357
5358 return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, s_min,
5359 s_max, num, t_fmri, t_pg_name, t_pg_type, t_prop_name,
5360 t_prop_type));
5361 cleanup:
5362 free(pg_name);
5363 free(prop_name);
5364 free(s_min);
5365 free(s_max);
5366 free(num);
5367 free(t_fmri);
5368 free(t_pg_name);
5369 free(t_pg_type);
5370 free(t_prop_name);
5371 free(t_prop_type);
5372 return (-1);
5373 }
5374
5375 /*
5376 * return 0 on success, -1 on failure.
5377 * set scf_error() to:
5378 * SCF_ERROR_BACKEND_ACCESS
5379 * SCF_ERROR_CONNECTION_BROKEN
5380 * SCF_ERROR_DELETED
5381 * SCF_ERROR_HANDLE_DESTROYED
5382 * SCF_ERROR_INTERNAL
5383 * SCF_ERROR_NO_MEMORY
5384 * SCF_ERROR_NO_RESOURCES
5385 * SCF_ERROR_NOT_BOUND
5386 * SCF_ERROR_PERMISSION_DENIED
5387 * SCF_ERROR_TEMPLATE_INVALID
5388 */
5389 static int
_add_tmpl_constraint_error(scf_tmpl_errors_t * errs,scf_tmpl_error_type_t type,scf_propertygroup_t * pg,const scf_prop_tmpl_t * pt,scf_property_t * prop,scf_value_t * val)5390 _add_tmpl_constraint_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
5391 scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
5392 scf_value_t *val)
5393 {
5394 scf_type_t val_type;
5395 char *pg_name = NULL;
5396 char *prop_name = NULL;
5397 char *value = NULL;
5398 char *t_fmri = NULL;
5399 char *t_pg_name = NULL;
5400 char *t_pg_type = NULL;
5401 char *t_prop_name = NULL;
5402 char *t_prop_type = NULL;
5403
5404 if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5405 return (-1);
5406 switch (type) {
5407 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
5408 if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5409 goto cleanup;
5410 if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5411 goto cleanup;
5412 /*FALLTHROUGH*/
5413 case SCF_TERR_INVALID_VALUE:
5414 /* keep pg_name = NULL and prop_name = NULL */
5415 if ((value = _scf_value_get_as_string(val)) == NULL)
5416 goto cleanup;
5417 break;
5418 case SCF_TERR_PROP_TYPE_MISMATCH:
5419 /* keep pg_name = NULL and prop_name = NULL */
5420 /* use value for value type */
5421 val_type = scf_value_type(val);
5422 if ((value = strdup(scf_type_to_string(val_type))) ==
5423 NULL) {
5424 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5425 goto cleanup;
5426 }
5427 break;
5428 }
5429 if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5430 goto cleanup;
5431 }
5432 if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5433 goto cleanup;
5434 }
5435 if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5436 goto cleanup;
5437 }
5438 t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5439 if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5440 free(t_prop_type);
5441 t_prop_type = NULL;
5442 } else if (t_prop_type == NULL) {
5443 goto cleanup;
5444 }
5445 if (t_prop_type == NULL)
5446 if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5447 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5448 goto cleanup;
5449 }
5450
5451 return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, NULL, NULL,
5452 value, t_fmri, t_pg_name, t_pg_type, t_prop_name, t_prop_type));
5453 cleanup:
5454 assert(scf_error() != SCF_ERROR_NOT_SET);
5455 free(pg_name);
5456 free(prop_name);
5457 free(value);
5458 free(t_fmri);
5459 free(t_pg_name);
5460 free(t_pg_type);
5461 free(t_prop_name);
5462 free(t_prop_type);
5463 return (-1);
5464 }
5465
5466 /*
5467 * return 0 on success, -1 on failure.
5468 * set scf_error() to:
5469 * SCF_ERROR_BACKEND_ACCESS
5470 * SCF_ERROR_CONNECTION_BROKEN
5471 * SCF_ERROR_DELETED
5472 * SCF_ERROR_HANDLE_DESTROYED
5473 * SCF_ERROR_INTERNAL
5474 * SCF_ERROR_NO_MEMORY
5475 * SCF_ERROR_NO_RESOURCES
5476 * SCF_ERROR_NOT_BOUND
5477 * SCF_ERROR_PERMISSION_DENIED
5478 * SCF_ERROR_TEMPLATE_INVALID
5479 */
5480 static int
_add_tmpl_int_error(scf_tmpl_errors_t * errs,scf_tmpl_error_type_t type,scf_propertygroup_t * pg,const scf_prop_tmpl_t * pt,scf_property_t * prop,int64_t val,int64_t * min,int64_t * max)5481 _add_tmpl_int_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
5482 scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
5483 int64_t val, int64_t *min, int64_t *max)
5484 {
5485 char *pg_name = NULL;
5486 char *prop_name = NULL;
5487 char *s_min = NULL;
5488 char *s_max = NULL;
5489 char *value = NULL;
5490 char *t_fmri = NULL;
5491 char *t_pg_name = NULL;
5492 char *t_pg_type = NULL;
5493 char *t_prop_name = NULL;
5494 char *t_prop_type = NULL;
5495
5496 if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5497 return (-1);
5498
5499 switch (type) {
5500 case SCF_TERR_RANGE_VIOLATION:
5501 if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5502 goto cleanup;
5503 if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5504 goto cleanup;
5505 break;
5506 case SCF_TERR_VALUE_OUT_OF_RANGE:
5507 /* keep pg_name = NULL and prop_name = NULL */
5508 break;
5509 }
5510 if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5511 goto cleanup;
5512 }
5513 if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5514 goto cleanup;
5515 }
5516 if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5517 goto cleanup;
5518 }
5519 t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5520 if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5521 free(t_prop_type);
5522 t_prop_type = NULL;
5523 } else if (t_prop_type == NULL) {
5524 goto cleanup;
5525 }
5526 if (t_prop_type == NULL)
5527 if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5528 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5529 goto cleanup;
5530 }
5531 if (min == NULL) {
5532 if ((s_min = strdup("")) == NULL) {
5533 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5534 goto cleanup;
5535 }
5536 } else {
5537 if ((s_min = _val_to_string(*((uint64_t *)min), 1)) == NULL) {
5538 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5539 goto cleanup;
5540 }
5541 }
5542 if (max == NULL) {
5543 if ((s_max = strdup("")) == NULL) {
5544 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5545 goto cleanup;
5546 }
5547 } else {
5548 if ((s_max = _val_to_string(*((uint64_t *)max), 1)) == NULL) {
5549 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5550 goto cleanup;
5551 }
5552 }
5553 if ((value = _val_to_string((uint64_t)val, 1)) == NULL) {
5554 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5555 goto cleanup;
5556 }
5557
5558 return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, s_min,
5559 s_max, value, t_fmri, t_pg_name, t_pg_type, t_prop_name,
5560 t_prop_type));
5561 cleanup:
5562 free(pg_name);
5563 free(prop_name);
5564 free(s_min);
5565 free(s_max);
5566 free(value);
5567 free(t_fmri);
5568 free(t_pg_name);
5569 free(t_pg_type);
5570 free(t_prop_name);
5571 free(t_prop_type);
5572 return (-1);
5573 }
5574
5575 /*
5576 * return 0 on success, -1 on failure.
5577 * set scf_error() to:
5578 * SCF_ERROR_BACKEND_ACCESS
5579 * SCF_ERROR_CONNECTION_BROKEN
5580 * SCF_ERROR_DELETED
5581 * SCF_ERROR_HANDLE_DESTROYED
5582 * SCF_ERROR_INTERNAL
5583 * SCF_ERROR_NO_MEMORY
5584 * SCF_ERROR_NO_RESOURCES
5585 * SCF_ERROR_NOT_BOUND
5586 * SCF_ERROR_PERMISSION_DENIED
5587 * SCF_ERROR_TEMPLATE_INVALID
5588 */
5589 static int
_add_tmpl_pg_redefine_error(scf_tmpl_errors_t * errs,scf_pg_tmpl_t * t,scf_pg_tmpl_t * r)5590 _add_tmpl_pg_redefine_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
5591 scf_pg_tmpl_t *r)
5592 {
5593 char *ev1 = NULL;
5594 char *ev2 = NULL;
5595 char *t_fmri = NULL;
5596 char *t_pg_name = NULL;
5597 char *t_pg_type = NULL;
5598
5599 if ((t_fmri = _scf_tmpl_get_fmri(r)) == NULL)
5600 return (-1);
5601 if (scf_tmpl_pg_name(r, &t_pg_name) == -1) {
5602 goto cleanup;
5603 }
5604 if (scf_tmpl_pg_type(r, &t_pg_type) == -1) {
5605 goto cleanup;
5606 }
5607 if (scf_tmpl_pg_name(t, &ev1) == -1) {
5608 goto cleanup;
5609 }
5610 if (scf_tmpl_pg_type(t, &ev2) == -1) {
5611 goto cleanup;
5612 }
5613
5614 return (_scf_tmpl_add_error(errs, SCF_TERR_PG_REDEFINE, NULL, NULL,
5615 ev1, ev2, NULL, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
5616 cleanup:
5617 free(ev1);
5618 free(ev2);
5619 free(t_fmri);
5620 free(t_pg_name);
5621 free(t_pg_type);
5622 return (-1);
5623 }
5624
5625 /*
5626 * return 0 if value is within count ranges constraint.
5627 * return -1 otherwise
5628 */
5629 static int
_check_count_ranges(scf_count_ranges_t * cr,uint64_t v)5630 _check_count_ranges(scf_count_ranges_t *cr, uint64_t v)
5631 {
5632 int i;
5633
5634 for (i = 0; i < cr->scr_num_ranges; ++i) {
5635 if (v >= cr->scr_min[i] &&
5636 v <= cr->scr_max[i]) {
5637 /* value is within ranges constraint */
5638 return (0);
5639 }
5640 }
5641 return (-1);
5642 }
5643
5644 /*
5645 * return 0 if value is within count ranges constraint.
5646 * return -1 otherwise
5647 */
5648 static int
_check_int_ranges(scf_int_ranges_t * ir,int64_t v)5649 _check_int_ranges(scf_int_ranges_t *ir, int64_t v)
5650 {
5651 int i;
5652
5653 for (i = 0; i < ir->sir_num_ranges; ++i) {
5654 if (v >= ir->sir_min[i] &&
5655 v <= ir->sir_max[i]) {
5656 /* value is within integer ranges constraint */
5657 return (0);
5658 }
5659 }
5660 return (-1);
5661 }
5662
5663 /*
5664 * int _value_in_constraint()
5665 *
5666 * Checks whether the supplied value violates any of the constraints
5667 * specified in the supplied property template. If it does, an appropriate
5668 * error is appended to "errs". pg and prop, if supplied, are used to
5669 * augment the information in the error. Returns 0 on success.
5670 *
5671 * Returns -1 on failure. Sets scf_error():
5672 * SCF_ERROR_BACKEND_ACCESS
5673 * SCF_ERROR_CONNECTION_BROKEN
5674 * SCF_ERROR_DELETED
5675 * SCF_ERROR_HANDLE_DESTROYED
5676 * SCF_ERROR_INTERNAL
5677 * SCF_ERROR_INVALID_ARGUMENT
5678 * SCF_ERROR_NO_MEMORY
5679 * SCF_ERROR_NO_RESOURCES
5680 * SCF_ERROR_NOT_BOUND
5681 * SCF_ERROR_PERMISSION_DENIED
5682 * SCF_ERROR_TEMPLATE_INVALID
5683 */
5684 static int
_value_in_constraint(scf_propertygroup_t * pg,scf_property_t * prop,const scf_prop_tmpl_t * pt,scf_value_t * value,scf_tmpl_errors_t * errs)5685 _value_in_constraint(scf_propertygroup_t *pg, scf_property_t *prop,
5686 const scf_prop_tmpl_t *pt, scf_value_t *value, scf_tmpl_errors_t *errs)
5687 {
5688 scf_type_t type, tmpl_type;
5689 scf_values_t vals;
5690 scf_tmpl_error_type_t terr_type;
5691 uint64_t v_count;
5692 int64_t v_int;
5693 char *vstr;
5694 ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
5695 ssize_t ret = 0;
5696 char **constraints;
5697 int n = 0;
5698 int r;
5699 int err_flag = 0;
5700 scf_count_ranges_t cr;
5701 scf_int_ranges_t ir;
5702
5703 type = scf_value_type(value);
5704 if (type == SCF_TYPE_INVALID) {
5705 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
5706 return (-1);
5707 }
5708
5709 /* Check if template type matches value type. */
5710 if (scf_tmpl_prop_type(pt, &tmpl_type) == -1) {
5711 if (scf_error() != SCF_ERROR_NOT_FOUND)
5712 /* type is not wildcarded */
5713 return (-1);
5714 } else if (tmpl_type != type) {
5715 if (errs != NULL) {
5716 if (pg == NULL && prop == NULL) {
5717 if (_add_tmpl_constraint_error(errs,
5718 SCF_TERR_PROP_TYPE_MISMATCH, NULL, pt,
5719 NULL, value) == -1)
5720 return (-1);
5721 }
5722 }
5723 return (1);
5724 }
5725
5726 /* Numeric values should be checked against any range constraints. */
5727 switch (type) {
5728 case SCF_TYPE_COUNT:
5729 r = scf_value_get_count(value, &v_count);
5730 assert(r == 0);
5731
5732 if (scf_tmpl_value_count_range_constraints(pt, &cr) != 0) {
5733 if (scf_error() == SCF_ERROR_NOT_FOUND)
5734 break;
5735 if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
5736 (void) scf_set_error(
5737 SCF_ERROR_TEMPLATE_INVALID);
5738 return (-1);
5739 } else {
5740 if (_check_count_ranges(&cr, v_count) == 0) {
5741 /* value is within ranges constraint */
5742 scf_count_ranges_destroy(&cr);
5743 return (0);
5744 }
5745 scf_count_ranges_destroy(&cr);
5746 }
5747
5748 /*
5749 * If we get here, we have a possible constraint
5750 * violation.
5751 */
5752 err_flag |= 0x1; /* RANGE_VIOLATION, count */
5753 break;
5754 case SCF_TYPE_INTEGER:
5755 if (scf_value_get_integer(value, &v_int) != 0)
5756 assert(0);
5757 if (scf_tmpl_value_int_range_constraints(pt, &ir) != 0) {
5758 if (scf_error() == SCF_ERROR_NOT_FOUND)
5759 break;
5760 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
5761 (void) scf_set_error(
5762 SCF_ERROR_TEMPLATE_INVALID);
5763 return (-1);
5764 } else {
5765 if (_check_int_ranges(&ir, v_int) == 0) {
5766 /* value is within ranges constraint */
5767 scf_int_ranges_destroy(&ir);
5768 return (0);
5769 }
5770 scf_int_ranges_destroy(&ir);
5771 }
5772 /*
5773 * If we get here, we have a possible constraint
5774 * violation.
5775 */
5776 err_flag |= 0x2; /* RANGE_VIOLATION, integer */
5777 break;
5778 default:
5779 break;
5780 }
5781
5782 vstr = malloc(sz);
5783 if (vstr == NULL) {
5784 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
5785 return (-1);
5786 }
5787
5788 /*
5789 * If a set of names is provided, confirm value has one of
5790 * those names.
5791 */
5792 if (scf_tmpl_value_name_constraints(pt, &vals) != 0) {
5793 free(vstr);
5794 if (scf_error() != SCF_ERROR_NOT_FOUND) {
5795 return (-1);
5796 }
5797 } else {
5798 r = scf_value_get_as_string_typed(value, type, vstr, sz);
5799
5800 /*
5801 * All errors (INVALID_ARGUMENT, NOT_SET, TYPE_MISMATCH)
5802 * should be impossible or already caught above.
5803 */
5804 assert(r > 0);
5805
5806 constraints = vals.values.v_astring;
5807 for (n = 0; constraints[n] != NULL; ++n) {
5808 if (strcmp(constraints[n], vstr) == 0) {
5809 /* value is within constraint */
5810 scf_values_destroy(&vals);
5811 free(vstr);
5812 return (0);
5813 }
5814 }
5815 /* if we get here, we have a constraint violation */
5816 err_flag |= 0x4; /* CONSTRAINT_VIOLATED */
5817 scf_values_destroy(&vals);
5818 free(vstr);
5819 }
5820 if (err_flag != 0)
5821 ret = 1;
5822 /* register the errors found */
5823 if (ret == 1 && errs != NULL) {
5824 if ((err_flag & 0x1) == 0x1) {
5825 /*
5826 * Help make the error more human-friendly. If
5827 * pg and prop are provided, we know we're
5828 * validating repository data. If they're not,
5829 * we're validating a potentially hypothetical
5830 * value.
5831 */
5832 if (pg == NULL && prop == NULL)
5833 terr_type = SCF_TERR_VALUE_OUT_OF_RANGE;
5834 else
5835 terr_type = SCF_TERR_RANGE_VIOLATION;
5836 if (_add_tmpl_count_error(errs, terr_type, pg, pt,
5837 prop, v_count, 0, 0) == -1)
5838 ret = -1;
5839 }
5840 if ((err_flag & 0x2) == 0x2) {
5841 if (pg == NULL && prop == NULL)
5842 terr_type = SCF_TERR_VALUE_OUT_OF_RANGE;
5843 else
5844 terr_type = SCF_TERR_RANGE_VIOLATION;
5845 if (_add_tmpl_int_error(errs, terr_type, pg, pt, prop,
5846 v_int, 0, 0) == -1)
5847 ret = -1;
5848 }
5849 if ((err_flag & 0x4) == 0x4) {
5850 if (pg == NULL && prop == NULL)
5851 terr_type = SCF_TERR_INVALID_VALUE;
5852 else
5853 terr_type = SCF_TERR_VALUE_CONSTRAINT_VIOLATED;
5854 if (_add_tmpl_constraint_error(errs, terr_type, pg,
5855 pt, prop, value) == -1)
5856 ret = -1;
5857 }
5858 }
5859 return (ret);
5860 }
5861
5862 /*
5863 * Returns -1 on failure. Sets scf_error():
5864 * SCF_ERROR_BACKEND_ACCESS
5865 * SCF_ERROR_CONNECTION_BROKEN
5866 * SCF_ERROR_DELETED
5867 * SCF_ERROR_HANDLE_DESTROYED
5868 * SCF_ERROR_INTERNAL
5869 * SCF_ERROR_INVALID_ARGUMENT
5870 * SCF_ERROR_NO_MEMORY
5871 * SCF_ERROR_NO_RESOURCES
5872 * SCF_ERROR_NOT_BOUND
5873 * SCF_ERROR_PERMISSION_DENIED
5874 * SCF_ERROR_TEMPLATE_INVALID
5875 */
5876 int
scf_tmpl_value_in_constraint(const scf_prop_tmpl_t * pt,scf_value_t * value,scf_tmpl_errors_t ** errs)5877 scf_tmpl_value_in_constraint(const scf_prop_tmpl_t *pt, scf_value_t *value,
5878 scf_tmpl_errors_t **errs)
5879 {
5880 scf_tmpl_errors_t *e = NULL;
5881
5882 if (errs != NULL) {
5883 char *fmri;
5884
5885 if ((fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5886 return (-1);
5887 *errs = _scf_create_errors(fmri, 1);
5888 free(fmri);
5889 if (*errs == NULL)
5890 return (-1);
5891 e = *errs;
5892 }
5893
5894 return (_value_in_constraint(NULL, NULL, pt, value, e));
5895 }
5896
5897 scf_tmpl_error_t *
scf_tmpl_next_error(scf_tmpl_errors_t * errs)5898 scf_tmpl_next_error(scf_tmpl_errors_t *errs)
5899 {
5900 if (errs->tes_index < errs->tes_num_errs) {
5901 assert(errs->tes_errs[errs->tes_index] != NULL);
5902 return (errs->tes_errs[errs->tes_index++]);
5903 } else {
5904 return (NULL);
5905 }
5906 }
5907
5908 void
scf_tmpl_reset_errors(scf_tmpl_errors_t * errs)5909 scf_tmpl_reset_errors(scf_tmpl_errors_t *errs)
5910 {
5911 errs->tes_index = 0;
5912 }
5913
5914 int
scf_tmpl_strerror(scf_tmpl_error_t * err,char * s,size_t n,int flag)5915 scf_tmpl_strerror(scf_tmpl_error_t *err, char *s, size_t n, int flag)
5916 {
5917 const char *str;
5918 int i;
5919 int ret = -1;
5920 int nsz = 0; /* err msg length */
5921 int sz = n; /* available buffer size */
5922 char *buf = s; /* where to append in buffer */
5923 char *s0 = (flag == SCF_TMPL_STRERROR_HUMAN) ? ":\n\t" : ": ";
5924 char *s1 = (flag == SCF_TMPL_STRERROR_HUMAN) ? "\n\t" : "; ";
5925 char *sep = s0;
5926 const char *val;
5927
5928 /* prefix */
5929 if (err->te_errs->tes_prefix != NULL) {
5930 ret = snprintf(buf, sz, "%s", dgettext(TEXT_DOMAIN,
5931 err->te_errs->tes_prefix));
5932 nsz += ret;
5933 sz = (sz - ret) > 0 ? sz - ret : 0;
5934 buf = (sz > 0) ? s + nsz : NULL;
5935 }
5936 /* error message */
5937 ret = snprintf(buf, sz, "%s", dgettext(TEXT_DOMAIN,
5938 em_desc[err->te_type].em_msg));
5939 nsz += ret;
5940 sz = (sz - ret) > 0 ? sz - ret : 0;
5941 buf = (sz > 0) ? s + nsz : NULL;
5942
5943 for (i = 0; _tmpl_error_items[i].get_desc != NULL; ++i) {
5944 if ((str = _tmpl_error_items[i].get_desc(err)) == NULL)
5945 /* no item to print */
5946 continue;
5947 val = _tmpl_error_items[i].get_val(err);
5948 ret = snprintf(buf, sz, "%s%s=\"%s\"", sep, str,
5949 (val == NULL) ? "" : val);
5950 nsz += ret;
5951 sz = (sz - ret) > 0 ? sz - ret : 0;
5952 buf = (sz > 0) ? s + nsz : NULL;
5953 sep = s1;
5954 }
5955 return (nsz);
5956 }
5957
5958 /*
5959 * return 0 on success, -1 on failure.
5960 * set scf_error() to:
5961 * SCF_ERROR_BACKEND_ACCESS
5962 * SCF_ERROR_CONNECTION_BROKEN
5963 * SCF_ERROR_DELETED
5964 * SCF_ERROR_HANDLE_DESTROYED
5965 * SCF_ERROR_INTERNAL
5966 * SCF_ERROR_NO_MEMORY
5967 * SCF_ERROR_NO_RESOURCES
5968 * SCF_ERROR_NOT_BOUND
5969 * SCF_ERROR_PERMISSION_DENIED
5970 * SCF_ERROR_TEMPLATE_INVALID
5971 */
5972 static int
_validate_cardinality(scf_propertygroup_t * pg,scf_prop_tmpl_t * pt,scf_property_t * prop,scf_tmpl_errors_t * errs)5973 _validate_cardinality(scf_propertygroup_t *pg, scf_prop_tmpl_t *pt,
5974 scf_property_t *prop, scf_tmpl_errors_t *errs)
5975 {
5976 uint64_t min, max;
5977 scf_handle_t *h;
5978 scf_iter_t *iter = NULL;
5979 scf_value_t *val = NULL;
5980 int count = 0;
5981 int ret = -1;
5982 int r;
5983
5984 if (scf_tmpl_prop_cardinality(pt, &min, &max) != 0) {
5985 if (scf_error() == SCF_ERROR_NOT_FOUND)
5986 return (0);
5987 else
5988 return (-1);
5989 }
5990
5991 /* Any number of values permitted. Just return success. */
5992 if (min == 0 && max == UINT64_MAX) {
5993 return (0);
5994 }
5995
5996 h = scf_property_handle(prop);
5997 if (h == NULL) {
5998 assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
5999 goto cleanup;
6000 }
6001
6002 iter = scf_iter_create(h);
6003 val = scf_value_create(h);
6004 if (iter == NULL || val == NULL) {
6005 if (ismember(scf_error(), errors_server)) {
6006 goto cleanup;
6007 } else {
6008 assert(0);
6009 abort();
6010 }
6011 }
6012
6013 if (scf_iter_property_values(iter, prop) != 0) {
6014 if (ismember(scf_error(), errors_server)) {
6015 goto cleanup;
6016 } else {
6017 assert(0);
6018 abort();
6019 }
6020 }
6021
6022 while ((r = scf_iter_next_value(iter, val)) == 1)
6023 count++;
6024
6025 if (r < 0) {
6026 if (ismember(scf_error(), errors_server)) {
6027 goto cleanup;
6028 } else {
6029 assert(0);
6030 abort();
6031 }
6032 }
6033
6034 if (count < min || count > max)
6035 if (_add_tmpl_count_error(errs, SCF_TERR_CARDINALITY_VIOLATION,
6036 pg, pt, prop, (uint64_t)count, &min, &max) == -1)
6037 goto cleanup;
6038
6039 ret = 0;
6040
6041 cleanup:
6042 scf_iter_destroy(iter);
6043 scf_value_destroy(val);
6044 return (ret);
6045 }
6046
6047 /*
6048 * Returns -1 on error. Sets scf_error():
6049 * SCF_ERROR_BACKEND_ACCESS
6050 * SCF_ERROR_CONNECTION_BROKEN
6051 * SCF_ERROR_DELETED
6052 * SCF_ERROR_HANDLE_DESTROYED
6053 * SCF_ERROR_INTERNAL
6054 * SCF_ERROR_NO_MEMORY
6055 * SCF_ERROR_NO_RESOURCES
6056 * SCF_ERROR_NOT_BOUND
6057 * SCF_ERROR_PERMISSION_DENIED
6058 * SCF_ERROR_TEMPLATE_INVALID
6059 */
6060 static int
_check_property(scf_prop_tmpl_t * pt,scf_propertygroup_t * pg,scf_property_t * prop,scf_tmpl_errors_t * errs)6061 _check_property(scf_prop_tmpl_t *pt, scf_propertygroup_t *pg,
6062 scf_property_t *prop, scf_tmpl_errors_t *errs)
6063 {
6064 scf_type_t tmpl_type;
6065 uint8_t required;
6066 scf_handle_t *h;
6067 scf_iter_t *iter = NULL;
6068 scf_value_t *val = NULL;
6069 int r;
6070 int ret = -1;
6071
6072 h = scf_pg_handle(pg);
6073 if (h == NULL) {
6074 assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
6075 return (-1);
6076 }
6077
6078 iter = scf_iter_create(h);
6079 val = scf_value_create(h);
6080 if (iter == NULL || val == NULL) {
6081 if (ismember(scf_error(), errors_server)) {
6082 scf_iter_destroy(iter);
6083 scf_value_destroy(val);
6084 return (-1);
6085 } else {
6086 assert(0);
6087 abort();
6088 }
6089 }
6090
6091 if (scf_tmpl_prop_required(pt, &required) != 0)
6092 goto cleanup;
6093
6094 /* Check type */
6095 if (scf_tmpl_prop_type(pt, &tmpl_type) == -1) {
6096 if (scf_error() != SCF_ERROR_NOT_FOUND) {
6097 goto cleanup;
6098 } else if (required) {
6099 /* If required, type must be specified. */
6100 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6101 goto cleanup;
6102 }
6103 } else if (scf_property_is_type(prop, tmpl_type) != 0) {
6104 if (ismember(scf_error(), errors_server)) {
6105 goto cleanup;
6106 } else switch (scf_error()) {
6107 case SCF_ERROR_TYPE_MISMATCH:
6108 if (_add_tmpl_wrong_prop_type_error(errs, pg, pt,
6109 prop) == -1)
6110 goto cleanup;
6111 break;
6112
6113 case SCF_ERROR_INVALID_ARGUMENT:
6114 /*
6115 * tmpl_prop_type shouldn't have handed back
6116 * an invalid property type.
6117 */
6118 case SCF_ERROR_NOT_SET:
6119 default:
6120 assert(0);
6121 abort();
6122 }
6123 }
6124
6125
6126 /* Cardinality */
6127 if (_validate_cardinality(pg, pt, prop, errs) == -1)
6128 goto cleanup;
6129
6130 /* Value constraints */
6131 /*
6132 * Iterate through each value, and confirm it is defined as
6133 * constrained.
6134 */
6135 if (scf_iter_property_values(iter, prop) != 0) {
6136 assert(scf_error() != SCF_ERROR_NOT_SET &&
6137 scf_error() != SCF_ERROR_HANDLE_MISMATCH);
6138 goto cleanup;
6139 }
6140
6141 while ((r = scf_iter_next_value(iter, val)) == 1) {
6142 if (_value_in_constraint(pg, prop, pt, val, errs) == -1) {
6143 if (ismember(scf_error(), errors_server)) {
6144 goto cleanup;
6145 } else switch (scf_error()) {
6146 case SCF_ERROR_TEMPLATE_INVALID:
6147 goto cleanup;
6148
6149 case SCF_ERROR_INVALID_ARGUMENT:
6150 default:
6151 assert(0);
6152 abort();
6153 }
6154 }
6155 }
6156
6157 if (r < 0) {
6158 if (ismember(scf_error(), errors_server)) {
6159 goto cleanup;
6160 } else {
6161 assert(0);
6162 abort();
6163 }
6164 }
6165
6166 ret = 0;
6167
6168 cleanup:
6169 scf_iter_destroy(iter);
6170 scf_value_destroy(val);
6171 return (ret);
6172 }
6173
6174 /*
6175 * Returns -1 on failure, sets scf_error() to:
6176 * SCF_ERROR_BACKEND_ACCESS
6177 * SCF_ERROR_CONNECTION_BROKEN
6178 * SCF_ERROR_DELETED
6179 * SCF_ERROR_HANDLE_DESTROYED
6180 * SCF_ERROR_INTERNAL
6181 * SCF_ERROR_NO_MEMORY
6182 * SCF_ERROR_NO_RESOURCES
6183 * SCF_ERROR_NOT_BOUND
6184 * SCF_ERROR_PERMISSION_DENIED
6185 * SCF_ERROR_TEMPLATE_INVALID
6186 */
6187 static int
_check_pg(scf_pg_tmpl_t * t,scf_propertygroup_t * pg,char * pg_name,char * type,scf_tmpl_errors_t * errs)6188 _check_pg(scf_pg_tmpl_t *t, scf_propertygroup_t *pg, char *pg_name,
6189 char *type, scf_tmpl_errors_t *errs)
6190 {
6191 scf_prop_tmpl_t *pt = NULL;
6192 char *pg_type = NULL;
6193 scf_iter_t *iter = NULL;
6194 uint8_t pg_required;
6195 scf_property_t *prop = NULL;
6196 scf_handle_t *h;
6197 int r;
6198 char *prop_name = NULL;
6199 ssize_t nsize = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
6200 int ret = -1;
6201
6202 assert(pg_name != NULL);
6203 assert(t != NULL);
6204 assert(pg != NULL);
6205 assert(type != NULL);
6206 assert(nsize != 0);
6207
6208 if ((h = scf_pg_handle(pg)) == NULL) {
6209 assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
6210 return (-1);
6211 }
6212 if ((pt = scf_tmpl_prop_create(h)) == NULL) {
6213 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6214 return (-1);
6215 }
6216
6217 if ((prop = scf_property_create(h)) == NULL) {
6218 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6219 goto cleanup;
6220 }
6221
6222 if ((iter = scf_iter_create(h)) == NULL) {
6223 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6224 goto cleanup;
6225 }
6226 if ((prop_name = malloc(nsize)) == NULL) {
6227 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
6228 goto cleanup;
6229 }
6230
6231 if (scf_tmpl_pg_required(t, &pg_required) != 0)
6232 goto cleanup;
6233
6234 if (scf_tmpl_pg_type(t, &pg_type) == -1) {
6235 goto cleanup;
6236 } else if (pg_required != 0 &&
6237 strcmp(SCF_TMPL_WILDCARD, pg_type) == 0) {
6238 /* Type must be specified for required pgs. */
6239 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6240 goto cleanup;
6241 }
6242
6243 if (pg_type != NULL) {
6244 if (strcmp(pg_type, type) != 0 &&
6245 strcmp(pg_type, SCF_TMPL_WILDCARD) != 0) {
6246 if (_add_tmpl_wrong_pg_type_error(errs, t, pg) == -1)
6247 goto cleanup;
6248 }
6249 }
6250
6251
6252 /* Iterate through properties in the repository and check them. */
6253 if (scf_iter_pg_properties(iter, pg) != 0) {
6254 if (ismember(scf_error(), errors_server)) {
6255 goto cleanup;
6256 } else {
6257 assert(0);
6258 abort();
6259 }
6260 }
6261
6262 while ((r = scf_iter_next_property(iter, prop)) == 1) {
6263 if (scf_property_get_name(prop, prop_name, nsize) == -1) {
6264 assert(scf_error() != SCF_ERROR_NOT_SET);
6265 goto cleanup;
6266 }
6267 if (scf_tmpl_get_by_prop(t, prop_name, pt, 0) != 0) {
6268 if (ismember(scf_error(), errors_server)) {
6269 goto cleanup;
6270 } else switch (scf_error()) {
6271 case SCF_ERROR_NOT_FOUND:
6272 /* No template. Continue. */
6273 continue;
6274
6275 case SCF_ERROR_INVALID_ARGUMENT:
6276 default:
6277 assert(0);
6278 abort();
6279 }
6280 }
6281
6282 if (_check_property(pt, pg, prop, errs) != 0)
6283 goto cleanup;
6284 }
6285
6286 if (r < 0) {
6287 if (ismember(scf_error(), errors_server)) {
6288 goto cleanup;
6289 } else {
6290 assert(0);
6291 abort();
6292 }
6293 }
6294
6295 scf_tmpl_prop_reset(pt);
6296 free(prop_name);
6297 prop_name = NULL;
6298 /*
6299 * Confirm required properties are present.
6300 */
6301 while ((r = scf_tmpl_iter_props(t, pt,
6302 SCF_PROP_TMPL_FLAG_REQUIRED)) == 0) {
6303 scf_type_t prop_type;
6304
6305 if (scf_tmpl_prop_name(pt, &prop_name) == -1)
6306 goto cleanup;
6307
6308 /* required properties cannot have type wildcarded */
6309 if (scf_tmpl_prop_type(pt, &prop_type) == -1) {
6310 if (scf_error() == SCF_ERROR_NOT_FOUND)
6311 (void) scf_set_error(
6312 SCF_ERROR_TEMPLATE_INVALID);
6313 goto cleanup;
6314 }
6315
6316 if (scf_pg_get_property(pg, prop_name, prop) != 0) {
6317 if (ismember(scf_error(), errors_server)) {
6318 goto cleanup;
6319 } else switch (scf_error()) {
6320 case SCF_ERROR_NOT_FOUND:
6321 if (_add_tmpl_missing_prop_error(errs, t, pg,
6322 pt) == -1)
6323 goto cleanup;
6324 break;
6325
6326 case SCF_ERROR_INVALID_ARGUMENT:
6327 (void) scf_set_error(
6328 SCF_ERROR_TEMPLATE_INVALID);
6329 goto cleanup;
6330
6331 case SCF_ERROR_HANDLE_MISMATCH:
6332 case SCF_ERROR_NOT_SET:
6333 default:
6334 assert(0);
6335 abort();
6336 }
6337 }
6338 free(prop_name);
6339 prop_name = NULL;
6340 }
6341 if (r < 0) {
6342 if (ismember(scf_error(), errors_server)) {
6343 goto cleanup;
6344 } else switch (scf_error()) {
6345 case SCF_ERROR_NOT_FOUND:
6346 break;
6347
6348 case SCF_ERROR_TEMPLATE_INVALID:
6349 goto cleanup;
6350
6351 case SCF_ERROR_INVALID_ARGUMENT:
6352 default:
6353 assert(0);
6354 abort();
6355 }
6356 }
6357
6358 ret = 0;
6359 cleanup:
6360 scf_tmpl_prop_destroy(pt);
6361 scf_iter_destroy(iter);
6362 scf_property_destroy(prop);
6363 free(prop_name);
6364 free(pg_type);
6365 return (ret);
6366 }
6367
6368 /*
6369 * Checks if instance fmri redefines any pgs defined in restarter or global
6370 * Return -1 on failure, sets scf_error() to:
6371 * SCF_ERROR_BACKEND_ACCESS
6372 * SCF_ERROR_CONNECTION_BROKEN
6373 * SCF_ERROR_DELETED
6374 * SCF_ERROR_HANDLE_DESTROYED
6375 * SCF_ERROR_INTERNAL
6376 * SCF_ERROR_INVALID_ARGUMENT
6377 * SCF_ERROR_NO_MEMORY
6378 * SCF_ERROR_NO_RESOURCES
6379 * SCF_ERROR_NOT_BOUND
6380 * SCF_ERROR_NOT_FOUND
6381 * SCF_ERROR_PERMISSION_DENIED
6382 * SCF_ERROR_TEMPLATE_INVALID
6383 */
6384 static int
_scf_tmpl_check_pg_redef(scf_handle_t * h,const char * fmri,const char * snapname,scf_tmpl_errors_t * errs)6385 _scf_tmpl_check_pg_redef(scf_handle_t *h, const char *fmri,
6386 const char *snapname, scf_tmpl_errors_t *errs)
6387 {
6388 scf_pg_tmpl_t *t = NULL;
6389 scf_pg_tmpl_t *r = NULL;
6390 char *pg_name = NULL;
6391 char *pg_name_r = NULL;
6392 char *pg_type = NULL;
6393 char *pg_type_r = NULL;
6394 char *target = NULL;
6395 int ret_val = -1;
6396 int ret;
6397
6398 t = scf_tmpl_pg_create(h);
6399 r = scf_tmpl_pg_create(h);
6400 if (t == NULL || r == NULL)
6401 goto cleanup;
6402
6403 while ((ret = scf_tmpl_iter_pgs(t, fmri, snapname, NULL,
6404 SCF_PG_TMPL_FLAG_EXACT)) == 1) {
6405 if (scf_tmpl_pg_name(t, &pg_name) == -1) {
6406 goto cleanup;
6407 }
6408 if (scf_tmpl_pg_type(t, &pg_type) == -1) {
6409 goto cleanup;
6410 }
6411 /* look for a redefinition of a global/restarter pg_pattern */
6412 while ((ret = scf_tmpl_iter_pgs(r, fmri, snapname, pg_type,
6413 0)) == 1) {
6414 if (scf_tmpl_pg_name(r, &pg_name_r) == -1) {
6415 goto cleanup;
6416 } else if (strcmp(pg_name_r, SCF_TMPL_WILDCARD) != 0 &&
6417 strcmp(pg_name, SCF_TMPL_WILDCARD) != 0 &&
6418 strcmp(pg_name, pg_name_r) != 0) {
6419 /* not a match */
6420 free(pg_name_r);
6421 pg_name_r = NULL;
6422 continue;
6423 }
6424 if (scf_tmpl_pg_type(r, &pg_type_r) == -1) {
6425 goto cleanup;
6426 } else if (strcmp(pg_type_r, SCF_TMPL_WILDCARD) != 0 &&
6427 strcmp(pg_type, SCF_TMPL_WILDCARD) != 0 &&
6428 strcmp(pg_type, pg_type_r) != 0) {
6429 /* not a match */
6430 free(pg_name_r);
6431 pg_name_r = NULL;
6432 free(pg_type_r);
6433 pg_type_r = NULL;
6434 continue;
6435 }
6436 if (scf_tmpl_pg_target(r, &target) == -1) {
6437 target = NULL;
6438 goto cleanup;
6439 }
6440 if (strcmp(target, SCF_TM_TARGET_ALL) == 0 ||
6441 strcmp(target, SCF_TM_TARGET_DELEGATE) == 0) {
6442 /* found a pg_pattern redefinition */
6443 if (_add_tmpl_pg_redefine_error(errs, t,
6444 r) == -1)
6445 goto cleanup;
6446 free(pg_name_r);
6447 pg_name_r = NULL;
6448 free(pg_type_r);
6449 pg_type_r = NULL;
6450 free(target);
6451 target = NULL;
6452 break;
6453 }
6454 free(pg_name_r);
6455 pg_name_r = NULL;
6456 free(pg_type_r);
6457 pg_type_r = NULL;
6458 free(target);
6459 target = NULL;
6460 }
6461 if (ret == -1)
6462 goto cleanup;
6463 scf_tmpl_pg_reset(r);
6464
6465 free(pg_name);
6466 free(pg_type);
6467 pg_name = NULL;
6468 pg_type = NULL;
6469 }
6470 if (ret == -1)
6471 goto cleanup;
6472
6473 ret_val = 0;
6474
6475 cleanup:
6476 scf_tmpl_pg_destroy(t);
6477 scf_tmpl_pg_destroy(r);
6478 free(pg_name);
6479 free(pg_type);
6480 free(pg_name_r);
6481 free(pg_type_r);
6482 free(target);
6483
6484 if (ret_val == -1) {
6485 if (!ismember(scf_error(), errors_server)) {
6486 switch (scf_error()) {
6487 case SCF_ERROR_TYPE_MISMATCH:
6488 (void) scf_set_error(
6489 SCF_ERROR_TEMPLATE_INVALID);
6490 /*FALLTHROUGH*/
6491
6492 case SCF_ERROR_CONSTRAINT_VIOLATED:
6493 case SCF_ERROR_INVALID_ARGUMENT:
6494 case SCF_ERROR_NOT_FOUND:
6495 case SCF_ERROR_TEMPLATE_INVALID:
6496 break;
6497
6498 case SCF_ERROR_HANDLE_MISMATCH:
6499 case SCF_ERROR_NOT_SET:
6500 default:
6501 assert(0);
6502 abort();
6503 }
6504 }
6505 }
6506 return (ret_val);
6507 }
6508
6509 /*
6510 * Returns -1 on failure, sets scf_error() to:
6511 * SCF_ERROR_BACKEND_ACCESS
6512 * SCF_ERROR_CONNECTION_BROKEN
6513 * SCF_ERROR_DELETED
6514 * SCF_ERROR_HANDLE_DESTROYED
6515 * SCF_ERROR_INTERNAL
6516 * SCF_ERROR_INVALID_ARGUMENT
6517 * SCF_ERROR_NO_MEMORY
6518 * SCF_ERROR_NO_RESOURCES
6519 * SCF_ERROR_NOT_BOUND
6520 * SCF_ERROR_NOT_FOUND
6521 * SCF_ERROR_PERMISSION_DENIED
6522 * SCF_ERROR_TEMPLATE_INVALID
6523 */
6524 int
scf_tmpl_validate_fmri(scf_handle_t * h,const char * fmri,const char * snapshot,scf_tmpl_errors_t ** errs,int flags)6525 scf_tmpl_validate_fmri(scf_handle_t *h, const char *fmri, const char *snapshot,
6526 scf_tmpl_errors_t **errs, int flags)
6527 {
6528 scf_pg_tmpl_t *t = NULL;
6529 scf_iter_t *iter = NULL;
6530 scf_propertygroup_t *pg = NULL;
6531 scf_instance_t *inst = NULL;
6532 scf_snapshot_t *snap = NULL;
6533 char *type = NULL;
6534 char *pg_name = NULL;
6535 ssize_t rsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
6536 ssize_t nsize = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
6537 int ret = -1;
6538 int r;
6539
6540 assert(errs != NULL);
6541
6542 if ((*errs = _scf_create_errors(fmri, 1)) == NULL)
6543 return (-1);
6544
6545 if ((pg = scf_pg_create(h)) == NULL ||
6546 (iter = scf_iter_create(h)) == NULL ||
6547 (inst = scf_instance_create(h)) == NULL ||
6548 (t = scf_tmpl_pg_create(h)) == NULL) {
6549 /*
6550 * Sets SCF_ERROR_INVALID_ARGUMENT, SCF_ERROR_NO_MEMORY,
6551 * SCF_ERROR_NO_RESOURCES, SCF_ERROR_INTERNAL or
6552 * SCF_ERROR_HANDLE_DESTROYED.
6553 */
6554 goto cleanup;
6555 }
6556
6557 if ((type = malloc(rsize)) == NULL ||
6558 (pg_name = malloc(nsize)) == NULL) {
6559 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
6560 goto cleanup;
6561 }
6562
6563 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, NULL,
6564 SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
6565 if (ismember(scf_error(), errors_server)) {
6566 goto cleanup;
6567 } else switch (scf_error()) {
6568 case SCF_ERROR_CONSTRAINT_VIOLATED:
6569 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6570 /*FALLTHROUGH*/
6571
6572 case SCF_ERROR_INVALID_ARGUMENT:
6573 case SCF_ERROR_NOT_FOUND:
6574 goto cleanup;
6575
6576 case SCF_ERROR_HANDLE_MISMATCH:
6577 case SCF_ERROR_NOT_SET:
6578 default:
6579 assert(0);
6580 abort();
6581 }
6582 }
6583
6584 if (snapshot == NULL || strcmp(snapshot, "running") == 0 ||
6585 (flags & SCF_TMPL_VALIDATE_FLAG_CURRENT)) {
6586 if (_get_snapshot(inst, NULL, &snap) == -1)
6587 goto cleanup;
6588 } else {
6589 (void) scf_set_error(SCF_ERROR_NONE);
6590 if (_get_snapshot(inst, snapshot, &snap) == -1) {
6591 goto cleanup;
6592 } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
6593 goto cleanup;
6594 }
6595 }
6596 if (_scf_tmpl_check_pg_redef(h, fmri, snapshot, *errs) != 0) {
6597 goto cleanup;
6598 }
6599
6600 /*
6601 * Check that property groups on this instance conform to the template.
6602 */
6603 if (scf_iter_instance_pgs_composed(iter, inst, snap) != 0) {
6604 if (ismember(scf_error(), errors_server)) {
6605 goto cleanup;
6606 } else {
6607 assert(0);
6608 abort();
6609 }
6610 }
6611
6612 while ((r = scf_iter_next_pg(iter, pg)) == 1) {
6613 if (scf_pg_get_name(pg, pg_name, nsize) == -1) {
6614 if (ismember(scf_error(), errors_server)) {
6615 goto cleanup;
6616 } else {
6617 assert(0);
6618 abort();
6619 }
6620 }
6621
6622 if (scf_pg_get_type(pg, type, rsize) == -1) {
6623 if (ismember(scf_error(), errors_server)) {
6624 goto cleanup;
6625 } else {
6626 assert(0);
6627 abort();
6628 }
6629 }
6630
6631 if (scf_tmpl_get_by_pg_name(fmri, snapshot, pg_name, type, t,
6632 0) != 0) {
6633 if (ismember(scf_error(), errors_server)) {
6634 goto cleanup;
6635 } else switch (scf_error()) {
6636 case SCF_ERROR_NOT_FOUND:
6637 continue;
6638
6639 case SCF_ERROR_INVALID_ARGUMENT:
6640 goto cleanup;
6641
6642 default:
6643 assert(0);
6644 abort();
6645 }
6646 }
6647
6648 if (_check_pg(t, pg, pg_name, type, *errs) != 0)
6649 goto cleanup;
6650 }
6651 if (r < 0) {
6652 if (ismember(scf_error(), errors_server)) {
6653 goto cleanup;
6654 } else {
6655 assert(0);
6656 abort();
6657 }
6658 }
6659
6660 scf_tmpl_pg_reset(t);
6661
6662 /*
6663 * Confirm required property groups are present.
6664 */
6665 while ((r = scf_tmpl_iter_pgs(t, fmri, snapshot, NULL,
6666 SCF_PG_TMPL_FLAG_REQUIRED)) == 1) {
6667 free(pg_name);
6668 free(type);
6669
6670 if (scf_tmpl_pg_name(t, &pg_name) == -1)
6671 goto cleanup;
6672 if (scf_tmpl_pg_type(t, &type) == -1)
6673 goto cleanup;
6674 /*
6675 * required property group templates should not have
6676 * wildcarded name or type
6677 */
6678 if (strcmp(pg_name, SCF_TMPL_WILDCARD) == 0 ||
6679 strcmp(type, SCF_TMPL_WILDCARD) == 0) {
6680 (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6681 goto cleanup;
6682 }
6683
6684 if (_get_pg(NULL, inst, snap, pg_name, pg) != 0) {
6685 if (ismember(scf_error(), errors_server)) {
6686 goto cleanup;
6687 } else switch (scf_error()) {
6688 case SCF_ERROR_NOT_FOUND:
6689 if (_add_tmpl_missing_pg_error(*errs, t) == -1)
6690 goto cleanup;
6691 continue;
6692
6693 case SCF_ERROR_INVALID_ARGUMENT:
6694 case SCF_ERROR_HANDLE_MISMATCH:
6695 case SCF_ERROR_NOT_SET:
6696 default:
6697 assert(0);
6698 abort();
6699 }
6700 }
6701 }
6702 if (r < 0) {
6703 if (ismember(scf_error(), errors_server)) {
6704 goto cleanup;
6705 } else switch (scf_error()) {
6706 case SCF_ERROR_NOT_FOUND:
6707 break;
6708
6709 case SCF_ERROR_INVALID_ARGUMENT:
6710 goto cleanup;
6711
6712 default:
6713 assert(0);
6714 abort();
6715 }
6716 }
6717
6718 ret = 0;
6719 if ((*errs)->tes_num_errs > 0)
6720 ret = 1;
6721 cleanup:
6722 if (ret != 1) {
6723 /* there are no errors to report */
6724 scf_tmpl_errors_destroy(*errs);
6725 *errs = NULL;
6726 }
6727 scf_tmpl_pg_destroy(t);
6728 free(type);
6729 free(pg_name);
6730
6731 scf_iter_destroy(iter);
6732 scf_pg_destroy(pg);
6733 scf_instance_destroy(inst);
6734 scf_snapshot_destroy(snap);
6735
6736 return (ret);
6737 }
6738
6739 void
scf_tmpl_errors_destroy(scf_tmpl_errors_t * errs)6740 scf_tmpl_errors_destroy(scf_tmpl_errors_t *errs)
6741 {
6742 int i;
6743 scf_tmpl_error_t *e;
6744
6745 if (errs == NULL)
6746 return;
6747
6748 for (i = 0; i < errs->tes_num_errs; ++i) {
6749 e = errs->tes_errs[i];
6750 if (errs->tes_flag != 0) {
6751 free((char *)e->te_pg_name);
6752 free((char *)e->te_prop_name);
6753 free((char *)e->te_ev1);
6754 free((char *)e->te_ev2);
6755 free((char *)e->te_actual);
6756 free((char *)e->te_tmpl_fmri);
6757 free((char *)e->te_tmpl_pg_name);
6758 free((char *)e->te_tmpl_pg_type);
6759 free((char *)e->te_tmpl_prop_name);
6760 free((char *)e->te_tmpl_prop_type);
6761 }
6762 free(e);
6763 }
6764 free((char *)errs->tes_fmri);
6765 free((char *)errs->tes_prefix);
6766 free(errs->tes_errs);
6767 free(errs);
6768 }
6769
6770 int
scf_tmpl_error_source_fmri(const scf_tmpl_error_t * err,char ** fmri)6771 scf_tmpl_error_source_fmri(const scf_tmpl_error_t *err, char **fmri)
6772 {
6773 assert(err != NULL);
6774 switch (err->te_type) {
6775 case SCF_TERR_MISSING_PG:
6776 case SCF_TERR_WRONG_PG_TYPE:
6777 case SCF_TERR_MISSING_PROP:
6778 case SCF_TERR_WRONG_PROP_TYPE:
6779 case SCF_TERR_CARDINALITY_VIOLATION:
6780 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6781 case SCF_TERR_RANGE_VIOLATION:
6782 case SCF_TERR_PROP_TYPE_MISMATCH:
6783 case SCF_TERR_VALUE_OUT_OF_RANGE:
6784 case SCF_TERR_INVALID_VALUE:
6785 case SCF_TERR_PG_REDEFINE:
6786 *fmri = (char *)err->te_tmpl_fmri;
6787 return (0);
6788 /*NOTREACHED*/
6789 default:
6790 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6791 }
6792 return (-1);
6793 }
6794
6795 int
scf_tmpl_error_type(const scf_tmpl_error_t * err,scf_tmpl_error_type_t * type)6796 scf_tmpl_error_type(const scf_tmpl_error_t *err, scf_tmpl_error_type_t *type)
6797 {
6798 assert(err != NULL);
6799 switch (err->te_type) {
6800 case SCF_TERR_MISSING_PG:
6801 case SCF_TERR_WRONG_PG_TYPE:
6802 case SCF_TERR_MISSING_PROP:
6803 case SCF_TERR_WRONG_PROP_TYPE:
6804 case SCF_TERR_CARDINALITY_VIOLATION:
6805 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6806 case SCF_TERR_RANGE_VIOLATION:
6807 case SCF_TERR_PROP_TYPE_MISMATCH:
6808 case SCF_TERR_VALUE_OUT_OF_RANGE:
6809 case SCF_TERR_INVALID_VALUE:
6810 case SCF_TERR_PG_REDEFINE:
6811 *type = err->te_type;
6812 return (0);
6813 /*NOTREACHED*/
6814 default:
6815 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6816 }
6817 return (-1);
6818 }
6819
6820 int
scf_tmpl_error_pg_tmpl(const scf_tmpl_error_t * err,char ** name,char ** type)6821 scf_tmpl_error_pg_tmpl(const scf_tmpl_error_t *err, char **name, char **type)
6822 {
6823 assert(err != NULL);
6824 switch (err->te_type) {
6825 case SCF_TERR_MISSING_PG:
6826 case SCF_TERR_WRONG_PG_TYPE:
6827 case SCF_TERR_MISSING_PROP:
6828 case SCF_TERR_WRONG_PROP_TYPE:
6829 case SCF_TERR_CARDINALITY_VIOLATION:
6830 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6831 case SCF_TERR_RANGE_VIOLATION:
6832 case SCF_TERR_PROP_TYPE_MISMATCH:
6833 case SCF_TERR_VALUE_OUT_OF_RANGE:
6834 case SCF_TERR_INVALID_VALUE:
6835 case SCF_TERR_PG_REDEFINE:
6836 if (err->te_tmpl_pg_name != NULL &&
6837 err->te_tmpl_pg_type != NULL) {
6838 if (name != NULL)
6839 *name = (char *)err->te_tmpl_pg_name;
6840 if (type != NULL)
6841 *type = (char *)err->te_tmpl_pg_type;
6842 return (0);
6843 }
6844 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6845 break;
6846 default:
6847 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6848 }
6849 return (-1);
6850 }
6851
6852 int
scf_tmpl_error_pg(const scf_tmpl_error_t * err,char ** name,char ** type)6853 scf_tmpl_error_pg(const scf_tmpl_error_t *err, char **name, char **type)
6854 {
6855 assert(err != NULL);
6856 switch (err->te_type) {
6857 case SCF_TERR_WRONG_PG_TYPE:
6858 if (err->te_pg_name != NULL &&
6859 err->te_actual != NULL) {
6860 if (name != NULL)
6861 *name = (char *)err->te_pg_name;
6862 if (type != NULL)
6863 *type = (char *)err->te_actual;
6864 return (0);
6865 }
6866 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6867 break;
6868 case SCF_TERR_WRONG_PROP_TYPE:
6869 case SCF_TERR_CARDINALITY_VIOLATION:
6870 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6871 case SCF_TERR_RANGE_VIOLATION:
6872 if (err->te_pg_name != NULL &&
6873 err->te_tmpl_pg_type != NULL) {
6874 if (name != NULL)
6875 *name = (char *)err->te_pg_name;
6876 if (type != NULL)
6877 *type = (char *)err->te_tmpl_pg_type;
6878 return (0);
6879 }
6880 /*FALLTHROUGH*/
6881 case SCF_TERR_MISSING_PROP:
6882 case SCF_TERR_MISSING_PG:
6883 case SCF_TERR_PROP_TYPE_MISMATCH:
6884 case SCF_TERR_VALUE_OUT_OF_RANGE:
6885 case SCF_TERR_INVALID_VALUE:
6886 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6887 break;
6888 case SCF_TERR_PG_REDEFINE:
6889 if (err->te_ev1 != NULL && err->te_ev2 != NULL) {
6890 if (name != NULL)
6891 *name = (char *)err->te_ev1;
6892 if (type != NULL)
6893 *type = (char *)err->te_ev2;
6894 return (0);
6895 }
6896 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6897 break;
6898 default:
6899 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6900 }
6901 return (-1);
6902 }
6903
6904 int
scf_tmpl_error_prop_tmpl(const scf_tmpl_error_t * err,char ** name,char ** type)6905 scf_tmpl_error_prop_tmpl(const scf_tmpl_error_t *err, char **name, char **type)
6906 {
6907 assert(err != NULL);
6908 switch (err->te_type) {
6909 case SCF_TERR_MISSING_PROP:
6910 case SCF_TERR_WRONG_PROP_TYPE:
6911 case SCF_TERR_CARDINALITY_VIOLATION:
6912 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6913 case SCF_TERR_RANGE_VIOLATION:
6914 case SCF_TERR_PROP_TYPE_MISMATCH:
6915 case SCF_TERR_VALUE_OUT_OF_RANGE:
6916 case SCF_TERR_INVALID_VALUE:
6917 if (err->te_tmpl_prop_name != NULL &&
6918 err->te_tmpl_prop_type != NULL) {
6919 if (name != NULL)
6920 *name = (char *)err->te_tmpl_prop_name;
6921 if (type != NULL)
6922 *type = (char *)err->te_tmpl_prop_type;
6923 return (0);
6924 }
6925 /*FALLTHROUGH*/
6926 case SCF_TERR_MISSING_PG:
6927 case SCF_TERR_WRONG_PG_TYPE:
6928 case SCF_TERR_PG_REDEFINE:
6929 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6930 break;
6931 default:
6932 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6933 }
6934 return (-1);
6935 }
6936
6937 int
scf_tmpl_error_prop(const scf_tmpl_error_t * err,char ** name,char ** type)6938 scf_tmpl_error_prop(const scf_tmpl_error_t *err, char **name, char **type)
6939 {
6940 assert(err != NULL);
6941 switch (err->te_type) {
6942 case SCF_TERR_WRONG_PROP_TYPE:
6943 case SCF_TERR_CARDINALITY_VIOLATION:
6944 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6945 case SCF_TERR_RANGE_VIOLATION:
6946 if (err->te_prop_name != NULL &&
6947 err->te_tmpl_prop_type != NULL) {
6948 if (name != NULL)
6949 *name = (char *)err->te_prop_name;
6950 if (type != NULL)
6951 *type = (char *)err->te_tmpl_prop_type;
6952 return (0);
6953 }
6954 /*FALLTHROUGH*/
6955 case SCF_TERR_MISSING_PG:
6956 case SCF_TERR_WRONG_PG_TYPE:
6957 case SCF_TERR_MISSING_PROP:
6958 case SCF_TERR_PROP_TYPE_MISMATCH:
6959 case SCF_TERR_VALUE_OUT_OF_RANGE:
6960 case SCF_TERR_INVALID_VALUE:
6961 case SCF_TERR_PG_REDEFINE:
6962 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6963 break;
6964 default:
6965 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6966 }
6967 return (-1);
6968 }
6969
6970 int
scf_tmpl_error_value(const scf_tmpl_error_t * err,char ** val)6971 scf_tmpl_error_value(const scf_tmpl_error_t *err, char **val)
6972 {
6973 assert(err != NULL);
6974 switch (err->te_type) {
6975 case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6976 case SCF_TERR_RANGE_VIOLATION:
6977 case SCF_TERR_VALUE_OUT_OF_RANGE:
6978 case SCF_TERR_INVALID_VALUE:
6979 if (err->te_actual != NULL) {
6980 if (val != NULL)
6981 *val = (char *)err->te_actual;
6982 return (0);
6983 }
6984 /*FALLTHROUGH*/
6985 case SCF_TERR_MISSING_PG:
6986 case SCF_TERR_WRONG_PG_TYPE:
6987 case SCF_TERR_MISSING_PROP:
6988 case SCF_TERR_WRONG_PROP_TYPE:
6989 case SCF_TERR_CARDINALITY_VIOLATION:
6990 case SCF_TERR_PROP_TYPE_MISMATCH:
6991 case SCF_TERR_PG_REDEFINE:
6992 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
6993 break;
6994 default:
6995 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6996 }
6997 return (-1);
6998 }
6999
7000 const char *
scf_tmpl_visibility_to_string(uint8_t vis)7001 scf_tmpl_visibility_to_string(uint8_t vis)
7002 {
7003 if (vis == SCF_TMPL_VISIBILITY_READONLY)
7004 return (SCF_TM_VISIBILITY_READONLY);
7005 else if (vis == SCF_TMPL_VISIBILITY_HIDDEN)
7006 return (SCF_TM_VISIBILITY_HIDDEN);
7007 else if (vis == SCF_TMPL_VISIBILITY_READWRITE)
7008 return (SCF_TM_VISIBILITY_READWRITE);
7009 else
7010 return ("unknown");
7011 }
7012