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