xref: /illumos-gate/usr/src/cmd/svc/configd/file_object.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * file_object.c - enter objects into and load them from the backend
31  *
32  * The primary entry points in this layer are object_create(),
33  * object_create_pg(), object_delete(), and object_fill_children().  They each
34  * take an rc_node_t and use the functions in the object_info_t info array for
35  * the node's type.
36  */
37 
38 #include <assert.h>
39 #include <pthread.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <strings.h>
44 
45 #include "configd.h"
46 #include "repcache_protocol.h"
47 
48 typedef struct child_info {
49 	rc_node_t	*ci_parent;
50 	backend_tx_t	*ci_tx;			/* only for properties */
51 	rc_node_lookup_t ci_base_nl;
52 } child_info_t;
53 
54 typedef struct delete_ent delete_ent_t;
55 typedef struct delete_stack delete_stack_t;
56 typedef struct delete_info delete_info_t;
57 
58 typedef int	delete_cb_func(delete_info_t *, const delete_ent_t *);
59 
60 struct delete_ent {
61 	delete_cb_func	*de_cb;		/* callback */
62 	uint32_t	de_backend;
63 	uint32_t	de_id;
64 	uint32_t	de_gen;		/* only for property groups */
65 };
66 
67 struct delete_stack {
68 	struct delete_stack *ds_next;
69 	uint32_t	ds_size;	/* number of elements */
70 	uint32_t	ds_cur;		/* current offset */
71 	delete_ent_t	ds_buf[1];	/* actually ds_size */
72 };
73 #define	DELETE_STACK_SIZE(x)	offsetof(delete_stack_t, ds_buf[(x)])
74 
75 struct delete_info {
76 	backend_tx_t	*di_tx;
77 	backend_tx_t	*di_np_tx;
78 	delete_stack_t	*di_stack;
79 	delete_stack_t	*di_free;
80 };
81 
82 typedef struct object_info {
83 	uint32_t	obj_type;
84 	enum id_space	obj_id_space;
85 
86 	int (*obj_fill_children)(rc_node_t *);
87 	int (*obj_setup_child_info)(rc_node_t *, uint32_t, child_info_t *);
88 	int (*obj_query_child)(backend_query_t *, rc_node_lookup_t *,
89 	    const char *);
90 	int (*obj_insert_child)(backend_tx_t *, rc_node_lookup_t *,
91 	    const char *);
92 	int (*obj_insert_pg_child)(backend_tx_t *, rc_node_lookup_t *,
93 	    const char *, const char *, uint32_t, uint32_t);
94 	int (*obj_delete_start)(rc_node_t *, delete_info_t *);
95 } object_info_t;
96 
97 #define	NUM_NEEDED	50
98 
99 static int
100 delete_stack_push(delete_info_t *dip, uint32_t be, delete_cb_func *cb,
101     uint32_t id, uint32_t gen)
102 {
103 	delete_stack_t *cur = dip->di_stack;
104 	delete_ent_t *ent;
105 
106 	if (cur == NULL || cur->ds_cur == cur->ds_size) {
107 		delete_stack_t *new = dip->di_free;
108 		dip->di_free = NULL;
109 		if (new == NULL) {
110 			new = uu_zalloc(DELETE_STACK_SIZE(NUM_NEEDED));
111 			if (new == NULL)
112 				return (REP_PROTOCOL_FAIL_NO_RESOURCES);
113 			new->ds_size = NUM_NEEDED;
114 		}
115 		new->ds_cur = 0;
116 		new->ds_next = dip->di_stack;
117 		dip->di_stack = new;
118 		cur = new;
119 	}
120 	assert(cur->ds_cur < cur->ds_size);
121 	ent = &cur->ds_buf[cur->ds_cur++];
122 
123 	ent->de_backend = be;
124 	ent->de_cb = cb;
125 	ent->de_id = id;
126 	ent->de_gen = gen;
127 
128 	return (REP_PROTOCOL_SUCCESS);
129 }
130 
131 static int
132 delete_stack_pop(delete_info_t *dip, delete_ent_t *out)
133 {
134 	delete_stack_t *cur = dip->di_stack;
135 	delete_ent_t *ent;
136 
137 	if (cur == NULL)
138 		return (NULL);
139 	assert(cur->ds_cur > 0 && cur->ds_cur <= cur->ds_size);
140 	ent = &cur->ds_buf[--cur->ds_cur];
141 	if (cur->ds_cur == 0) {
142 		dip->di_stack = cur->ds_next;
143 		cur->ds_next = NULL;
144 
145 		if (dip->di_free != NULL)
146 			uu_free(dip->di_free);
147 		dip->di_free = cur;
148 	}
149 	if (ent == NULL)
150 		return (0);
151 
152 	*out = *ent;
153 	return (1);
154 }
155 
156 static void
157 delete_stack_cleanup(delete_info_t *dip)
158 {
159 	delete_stack_t *cur;
160 	while ((cur = dip->di_stack) != NULL) {
161 		dip->di_stack = cur->ds_next;
162 
163 		uu_free(cur);
164 	}
165 
166 	if ((cur = dip->di_free) != NULL) {
167 		assert(cur->ds_next == NULL);	/* should only be one */
168 		uu_free(cur);
169 		dip->di_free = NULL;
170 	}
171 }
172 
173 struct delete_cb_info {
174 	delete_info_t	*dci_dip;
175 	uint32_t	dci_be;
176 	delete_cb_func	*dci_cb;
177 	int		dci_result;
178 };
179 
180 /*ARGSUSED*/
181 static int
182 push_delete_callback(void *data, int columns, char **vals, char **names)
183 {
184 	struct delete_cb_info *info = data;
185 
186 	const char *id_str = *vals++;
187 	const char *gen_str = *vals++;
188 
189 	uint32_t id;
190 	uint32_t gen;
191 
192 	assert(columns == 2);
193 
194 	if (uu_strtouint(id_str, &id, sizeof (id), 0, 0, 0) == -1)
195 		backend_panic("invalid integer in database");
196 	if (uu_strtouint(gen_str, &gen, sizeof (gen), 0, 0, 0) == -1)
197 		backend_panic("invalid integer in database");
198 
199 	info->dci_result = delete_stack_push(info->dci_dip, info->dci_be,
200 	    info->dci_cb, id, gen);
201 
202 	if (info->dci_result != REP_PROTOCOL_SUCCESS)
203 		return (BACKEND_CALLBACK_ABORT);
204 	return (BACKEND_CALLBACK_CONTINUE);
205 }
206 
207 static int
208 value_delete(delete_info_t *dip, const delete_ent_t *ent)
209 {
210 	uint32_t be = ent->de_backend;
211 	int r;
212 
213 	backend_query_t *q;
214 
215 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
216 	    dip->di_np_tx;
217 
218 	q = backend_query_alloc();
219 
220 	backend_query_add(q,
221 	    "SELECT 1 FROM prop_lnk_tbl WHERE (lnk_val_id = %d); "
222 	    "DELETE FROM value_tbl WHERE (value_id = %d); ",
223 	    ent->de_id, ent->de_id);
224 	r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
225 	backend_query_free(q);
226 	if (r == REP_PROTOCOL_DONE)
227 		return (REP_PROTOCOL_SUCCESS);		/* still in use */
228 	return (r);
229 }
230 
231 static int
232 pg_lnk_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
233 {
234 	struct delete_cb_info info;
235 	uint32_t be = ent->de_backend;
236 	int r;
237 
238 	backend_query_t *q;
239 
240 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
241 	    dip->di_np_tx;
242 
243 	/*
244 	 * For non-persistent backends, we could only have one parent, and
245 	 * he's already been deleted.
246 	 *
247 	 * For normal backends, we need to check to see if we're in
248 	 * a snapshot or are the active generation for the property
249 	 * group.  If we are, there's nothing to be done.
250 	 */
251 	if (be == BACKEND_TYPE_NORMAL) {
252 		q = backend_query_alloc();
253 		backend_query_add(q,
254 		    "SELECT 1 "
255 		    "FROM pg_tbl "
256 		    "WHERE (pg_id = %d AND pg_gen_id = %d); "
257 		    "SELECT 1 "
258 		    "FROM snaplevel_lnk_tbl "
259 		    "WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d);",
260 		    ent->de_id, ent->de_gen,
261 		    ent->de_id, ent->de_gen);
262 		r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
263 		backend_query_free(q);
264 
265 		if (r == REP_PROTOCOL_DONE)
266 			return (REP_PROTOCOL_SUCCESS);	/* still in use */
267 	}
268 
269 	info.dci_dip = dip;
270 	info.dci_be =  be;
271 	info.dci_cb = &value_delete;
272 	info.dci_result = REP_PROTOCOL_SUCCESS;
273 
274 	q = backend_query_alloc();
275 	backend_query_add(q,
276 	    "SELECT DISTINCT lnk_val_id, 0 FROM prop_lnk_tbl "
277 	    "WHERE "
278 	    "    (lnk_pg_id = %d AND lnk_gen_id = %d AND lnk_val_id NOTNULL); "
279 	    "DELETE FROM prop_lnk_tbl "
280 	    "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
281 	    ent->de_id, ent->de_gen, ent->de_id, ent->de_gen);
282 
283 	r = backend_tx_run(tx, q, push_delete_callback, &info);
284 	backend_query_free(q);
285 
286 	if (r == REP_PROTOCOL_DONE) {
287 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
288 		return (info.dci_result);
289 	}
290 	return (r);
291 }
292 
293 static int
294 propertygrp_delete(delete_info_t *dip, const delete_ent_t *ent)
295 {
296 	uint32_t be = ent->de_backend;
297 	backend_query_t *q;
298 	uint32_t gen;
299 
300 	int r;
301 
302 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
303 	    dip->di_np_tx;
304 
305 	q = backend_query_alloc();
306 	backend_query_add(q,
307 	    "SELECT pg_gen_id FROM pg_tbl WHERE pg_id = %d; "
308 	    "DELETE FROM pg_tbl WHERE pg_id = %d",
309 	    ent->de_id, ent->de_id);
310 	r = backend_tx_run_single_int(tx, q, &gen);
311 	backend_query_free(q);
312 
313 	if (r != REP_PROTOCOL_SUCCESS)
314 		return (r);
315 
316 	return (delete_stack_push(dip, be, &pg_lnk_tbl_delete,
317 	    ent->de_id, gen));
318 }
319 
320 static int
321 snaplevel_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
322 {
323 	uint32_t be = ent->de_backend;
324 	backend_query_t *q;
325 	struct delete_cb_info info;
326 
327 	int r;
328 
329 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
330 	    dip->di_np_tx;
331 
332 	info.dci_dip = dip;
333 	info.dci_be = be;
334 	info.dci_cb = &pg_lnk_tbl_delete;
335 	info.dci_result = REP_PROTOCOL_SUCCESS;
336 
337 	q = backend_query_alloc();
338 	backend_query_add(q,
339 	    "SELECT snaplvl_pg_id, snaplvl_gen_id "
340 	    "    FROM snaplevel_lnk_tbl "
341 	    "    WHERE snaplvl_level_id = %d; "
342 	    "DELETE FROM snaplevel_lnk_tbl WHERE snaplvl_level_id = %d",
343 	    ent->de_id, ent->de_id);
344 	r = backend_tx_run(tx, q, push_delete_callback, &info);
345 	backend_query_free(q);
346 
347 	if (r == REP_PROTOCOL_DONE) {
348 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
349 		return (info.dci_result);
350 	}
351 	return (r);
352 }
353 
354 static int
355 snaplevel_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
356 {
357 	uint32_t be = ent->de_backend;
358 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
359 	    dip->di_np_tx;
360 
361 	struct delete_cb_info info;
362 	backend_query_t *q;
363 	int r;
364 
365 	assert(be == BACKEND_TYPE_NORMAL);
366 
367 	q = backend_query_alloc();
368 	backend_query_add(q,
369 	    "SELECT 1 FROM snapshot_lnk_tbl WHERE lnk_snap_id = %d",
370 	    ent->de_id);
371 	r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
372 	backend_query_free(q);
373 
374 	if (r == REP_PROTOCOL_DONE)
375 		return (REP_PROTOCOL_SUCCESS);		/* still in use */
376 
377 	info.dci_dip = dip;
378 	info.dci_be = be;
379 	info.dci_cb = &snaplevel_lnk_delete;
380 	info.dci_result = REP_PROTOCOL_SUCCESS;
381 
382 	q = backend_query_alloc();
383 	backend_query_add(q,
384 	    "SELECT snap_level_id, 0 FROM snaplevel_tbl WHERE snap_id = %d;"
385 	    "DELETE FROM snaplevel_tbl WHERE snap_id = %d",
386 	    ent->de_id, ent->de_id);
387 	r = backend_tx_run(tx, q, push_delete_callback, &info);
388 	backend_query_free(q);
389 
390 	if (r == REP_PROTOCOL_DONE) {
391 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
392 		return (info.dci_result);
393 	}
394 	return (r);
395 }
396 
397 static int
398 snapshot_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
399 {
400 	uint32_t be = ent->de_backend;
401 	backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
402 	    dip->di_np_tx;
403 
404 	backend_query_t *q;
405 	uint32_t snapid;
406 	int r;
407 
408 	assert(be == BACKEND_TYPE_NORMAL);
409 
410 	q = backend_query_alloc();
411 	backend_query_add(q,
412 	    "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
413 	    "DELETE FROM snapshot_lnk_tbl WHERE lnk_id = %d",
414 	    ent->de_id, ent->de_id);
415 	r = backend_tx_run_single_int(tx, q, &snapid);
416 	backend_query_free(q);
417 
418 	if (r != REP_PROTOCOL_SUCCESS)
419 		return (r);
420 
421 	return (delete_stack_push(dip, be, &snaplevel_tbl_delete, snapid, 0));
422 }
423 
424 static int
425 pgparent_delete_add_pgs(delete_info_t *dip, uint32_t parent_id)
426 {
427 	struct delete_cb_info info;
428 	backend_query_t *q;
429 	int r;
430 
431 	info.dci_dip = dip;
432 	info.dci_be = BACKEND_TYPE_NORMAL;
433 	info.dci_cb = &propertygrp_delete;
434 	info.dci_result = REP_PROTOCOL_SUCCESS;
435 
436 	q = backend_query_alloc();
437 	backend_query_add(q,
438 	    "SELECT pg_id, 0 FROM pg_tbl WHERE pg_parent_id = %d",
439 	    parent_id);
440 
441 	r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
442 
443 	if (r == REP_PROTOCOL_DONE) {
444 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
445 		backend_query_free(q);
446 		return (info.dci_result);
447 	}
448 	if (r != REP_PROTOCOL_SUCCESS) {
449 		backend_query_free(q);
450 		return (r);
451 	}
452 
453 	if (dip->di_np_tx != NULL) {
454 		info.dci_be = BACKEND_TYPE_NONPERSIST;
455 
456 		r = backend_tx_run(dip->di_np_tx, q, push_delete_callback,
457 		    &info);
458 
459 		if (r == REP_PROTOCOL_DONE) {
460 			assert(info.dci_result != REP_PROTOCOL_SUCCESS);
461 			backend_query_free(q);
462 			return (info.dci_result);
463 		}
464 		if (r != REP_PROTOCOL_SUCCESS) {
465 			backend_query_free(q);
466 			return (r);
467 		}
468 	}
469 	backend_query_free(q);
470 	return (REP_PROTOCOL_SUCCESS);
471 }
472 
473 static int
474 service_delete(delete_info_t *dip, const delete_ent_t *ent)
475 {
476 	int r;
477 
478 	r = backend_tx_run_update_changed(dip->di_tx,
479 	    "DELETE FROM service_tbl WHERE svc_id = %d", ent->de_id);
480 	if (r != REP_PROTOCOL_SUCCESS)
481 		return (r);
482 
483 	return (pgparent_delete_add_pgs(dip, ent->de_id));
484 }
485 
486 static int
487 instance_delete(delete_info_t *dip, const delete_ent_t *ent)
488 {
489 	struct delete_cb_info info;
490 	int r;
491 	backend_query_t *q;
492 
493 	r = backend_tx_run_update_changed(dip->di_tx,
494 	    "DELETE FROM instance_tbl WHERE instance_id = %d", ent->de_id);
495 	if (r != REP_PROTOCOL_SUCCESS)
496 		return (r);
497 
498 	r = pgparent_delete_add_pgs(dip, ent->de_id);
499 	if (r != REP_PROTOCOL_SUCCESS)
500 		return (r);
501 
502 	info.dci_dip = dip;
503 	info.dci_be = BACKEND_TYPE_NORMAL;
504 	info.dci_cb = &snapshot_lnk_delete;
505 	info.dci_result = REP_PROTOCOL_SUCCESS;
506 
507 	q = backend_query_alloc();
508 	backend_query_add(q,
509 	    "SELECT lnk_id, 0 FROM snapshot_lnk_tbl WHERE lnk_inst_id = %d",
510 	    ent->de_id);
511 	r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
512 	backend_query_free(q);
513 
514 	if (r == REP_PROTOCOL_DONE) {
515 		assert(info.dci_result != REP_PROTOCOL_SUCCESS);
516 		return (info.dci_result);
517 	}
518 	return (r);
519 }
520 
521 /*ARGSUSED*/
522 static int
523 fill_child_callback(void *data, int columns, char **vals, char **names)
524 {
525 	child_info_t *cp = data;
526 	rc_node_t *np;
527 	uint32_t main_id;
528 	const char *name;
529 	const char *cur;
530 	rc_node_lookup_t *lp = &cp->ci_base_nl;
531 
532 	assert(columns == 2);
533 
534 	name = *vals++;
535 	columns--;
536 
537 	cur = *vals++;
538 	columns--;
539 	if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
540 		backend_panic("invalid integer in database");
541 
542 	lp->rl_main_id = main_id;
543 
544 	if ((np = rc_node_alloc()) == NULL)
545 		return (BACKEND_CALLBACK_ABORT);
546 
547 	np = rc_node_setup(np, lp, name, cp->ci_parent);
548 	rc_node_rele(np);
549 
550 	return (BACKEND_CALLBACK_CONTINUE);
551 }
552 
553 /*ARGSUSED*/
554 static int
555 fill_snapshot_callback(void *data, int columns, char **vals, char **names)
556 {
557 	child_info_t *cp = data;
558 	rc_node_t *np;
559 	uint32_t main_id;
560 	uint32_t snap_id;
561 	const char *name;
562 	const char *cur;
563 	const char *snap;
564 	rc_node_lookup_t *lp = &cp->ci_base_nl;
565 
566 	assert(columns == 3);
567 
568 	name = *vals++;
569 	columns--;
570 
571 	cur = *vals++;
572 	columns--;
573 	snap = *vals++;
574 	columns--;
575 	if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1 ||
576 	    uu_strtouint(snap, &snap_id, sizeof (snap_id), 0, 0, 0) == -1)
577 		backend_panic("invalid integer in database");
578 
579 	lp->rl_main_id = main_id;
580 
581 	if ((np = rc_node_alloc()) == NULL)
582 		return (BACKEND_CALLBACK_ABORT);
583 
584 	np = rc_node_setup_snapshot(np, lp, name, snap_id, cp->ci_parent);
585 	rc_node_rele(np);
586 
587 	return (BACKEND_CALLBACK_CONTINUE);
588 }
589 
590 /*ARGSUSED*/
591 static int
592 fill_pg_callback(void *data, int columns, char **vals, char **names)
593 {
594 	child_info_t *cip = data;
595 	const char *name;
596 	const char *type;
597 	const char *cur;
598 	uint32_t main_id;
599 	uint32_t flags;
600 	uint32_t gen_id;
601 
602 	rc_node_lookup_t *lp = &cip->ci_base_nl;
603 	rc_node_t *newnode, *pg;
604 
605 	assert(columns == 5);
606 
607 	name = *vals++;		/* pg_name */
608 	columns--;
609 
610 	cur = *vals++;		/* pg_id */
611 	columns--;
612 	if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
613 		backend_panic("invalid integer in database");
614 
615 	lp->rl_main_id = main_id;
616 
617 	cur = *vals++;		/* pg_gen_id */
618 	columns--;
619 	if (uu_strtouint(cur, &gen_id, sizeof (gen_id), 0, 0, 0) == -1)
620 		backend_panic("invalid integer in database");
621 
622 	type = *vals++;		/* pg_type */
623 	columns--;
624 
625 	cur = *vals++;		/* pg_flags */
626 	columns--;
627 	if (uu_strtouint(cur, &flags, sizeof (flags), 0, 0, 0) == -1)
628 		backend_panic("invalid integer in database");
629 
630 	if ((newnode = rc_node_alloc()) == NULL)
631 		return (BACKEND_CALLBACK_ABORT);
632 
633 	pg = rc_node_setup_pg(newnode, lp, name, type, flags, gen_id,
634 	    cip->ci_parent);
635 	if (pg == NULL) {
636 		rc_node_destroy(newnode);
637 		return (BACKEND_CALLBACK_ABORT);
638 	}
639 
640 	rc_node_rele(pg);
641 
642 	return (BACKEND_CALLBACK_CONTINUE);
643 }
644 
645 struct property_value_info {
646 	char		*pvi_base;
647 	size_t		pvi_pos;
648 	size_t		pvi_size;
649 	size_t		pvi_count;
650 };
651 
652 /*ARGSUSED*/
653 static int
654 property_value_size_cb(void *data, int columns, char **vals, char **names)
655 {
656 	struct property_value_info *info = data;
657 	assert(columns == 1);
658 
659 	info->pvi_size += strlen(vals[0]) + 1;		/* count the '\0' */
660 
661 	return (BACKEND_CALLBACK_CONTINUE);
662 }
663 
664 /*ARGSUSED*/
665 static int
666 property_value_cb(void *data, int columns, char **vals, char **names)
667 {
668 	struct property_value_info *info = data;
669 	size_t pos, left, len;
670 
671 	assert(columns == 1);
672 	pos = info->pvi_pos;
673 	left = info->pvi_size - pos;
674 
675 	pos = info->pvi_pos;
676 	left = info->pvi_size - pos;
677 
678 	if ((len = strlcpy(&info->pvi_base[pos], vals[0], left)) >= left) {
679 		/*
680 		 * since we preallocated, above, this shouldn't happen
681 		 */
682 		backend_panic("unexpected database change");
683 	}
684 
685 	len += 1;	/* count the '\0' */
686 
687 	info->pvi_pos += len;
688 	info->pvi_count++;
689 
690 	return (BACKEND_CALLBACK_CONTINUE);
691 }
692 
693 /*ARGSUSED*/
694 void
695 object_free_values(const char *vals, uint32_t type, size_t count, size_t size)
696 {
697 	if (vals != NULL)
698 		uu_free((void *)vals);
699 }
700 
701 /*ARGSUSED*/
702 static int
703 fill_property_callback(void *data, int columns, char **vals, char **names)
704 {
705 	child_info_t *cp = data;
706 	backend_tx_t *tx = cp->ci_tx;
707 	uint32_t main_id;
708 	const char *name;
709 	const char *cur;
710 	rep_protocol_value_type_t type;
711 	rc_node_lookup_t *lp = &cp->ci_base_nl;
712 	struct property_value_info info;
713 	int rc;
714 
715 	assert(columns == 4);
716 	assert(tx != NULL);
717 
718 	info.pvi_base = NULL;
719 	info.pvi_pos = 0;
720 	info.pvi_size = 0;
721 	info.pvi_count = 0;
722 
723 	name = *vals++;
724 
725 	cur = *vals++;
726 	if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
727 		backend_panic("invalid integer in database");
728 
729 	cur = *vals++;
730 	assert(('a' <= cur[0] && 'z' >= cur[0]) ||
731 	    ('A' <= cur[0] && 'Z' >= cur[0]) &&
732 	    (cur[1] == 0 || ('a' <= cur[1] && 'z' >= cur[1]) ||
733 	    ('A' <= cur[1] && 'Z' >= cur[1])));
734 	type = cur[0] | (cur[1] << 8);
735 
736 	lp->rl_main_id = main_id;
737 
738 	/*
739 	 * fill in the values, if any
740 	 */
741 	if ((cur = *vals++) != NULL) {
742 		rep_protocol_responseid_t r;
743 		backend_query_t *q = backend_query_alloc();
744 
745 		backend_query_add(q,
746 		    "SELECT value_value FROM value_tbl "
747 		    "WHERE (value_id = '%q')", cur);
748 
749 		switch (r = backend_tx_run(tx, q, property_value_size_cb,
750 		    &info)) {
751 		case REP_PROTOCOL_SUCCESS:
752 			break;
753 
754 		case REP_PROTOCOL_FAIL_NO_RESOURCES:
755 			backend_query_free(q);
756 			return (BACKEND_CALLBACK_ABORT);
757 
758 		case REP_PROTOCOL_DONE:
759 		default:
760 			backend_panic("backend_tx_run() returned %d", r);
761 		}
762 		if (info.pvi_size > 0) {
763 			info.pvi_base = uu_zalloc(info.pvi_size);
764 			if (info.pvi_base == NULL) {
765 				backend_query_free(q);
766 				return (BACKEND_CALLBACK_ABORT);
767 			}
768 			switch (r = backend_tx_run(tx, q, property_value_cb,
769 			    &info)) {
770 			case REP_PROTOCOL_SUCCESS:
771 				break;
772 
773 			case REP_PROTOCOL_FAIL_NO_RESOURCES:
774 				uu_free(info.pvi_base);
775 				backend_query_free(q);
776 				return (BACKEND_CALLBACK_ABORT);
777 
778 			case REP_PROTOCOL_DONE:
779 			default:
780 				backend_panic("backend_tx_run() returned %d",
781 				    r);
782 			}
783 		}
784 		backend_query_free(q);
785 	}
786 
787 	rc = rc_node_create_property(cp->ci_parent, lp, name, type,
788 	    info.pvi_base, info.pvi_count, info.pvi_size);
789 	if (rc != REP_PROTOCOL_SUCCESS) {
790 		assert(rc == REP_PROTOCOL_FAIL_NO_RESOURCES);
791 		return (BACKEND_CALLBACK_ABORT);
792 	}
793 
794 	return (BACKEND_CALLBACK_CONTINUE);
795 }
796 
797 /*
798  * The *_setup_child_info() functions fill in a child_info_t structure with the
799  * information for the children of np with type type.
800  *
801  * They fail with
802  *   _TYPE_MISMATCH - object cannot have children of type type
803  */
804 
805 static int
806 scope_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
807 {
808 	if (type != REP_PROTOCOL_ENTITY_SERVICE)
809 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
810 
811 	bzero(cip, sizeof (*cip));
812 	cip->ci_parent = np;
813 	cip->ci_base_nl.rl_type = type;
814 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
815 	return (REP_PROTOCOL_SUCCESS);
816 }
817 
818 static int
819 service_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
820 {
821 	switch (type) {
822 	case REP_PROTOCOL_ENTITY_INSTANCE:
823 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
824 		break;
825 	default:
826 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
827 	}
828 
829 	bzero(cip, sizeof (*cip));
830 	cip->ci_parent = np;
831 	cip->ci_base_nl.rl_type = type;
832 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
833 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_main_id;
834 
835 	return (REP_PROTOCOL_SUCCESS);
836 }
837 
838 static int
839 instance_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
840 {
841 	switch (type) {
842 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
843 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
844 		break;
845 	default:
846 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
847 	}
848 
849 	bzero(cip, sizeof (*cip));
850 	cip->ci_parent = np;
851 	cip->ci_base_nl.rl_type = type;
852 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
853 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
854 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_main_id;
855 
856 	return (REP_PROTOCOL_SUCCESS);
857 }
858 
859 static int
860 snaplevel_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
861 {
862 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
863 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
864 
865 	bzero(cip, sizeof (*cip));
866 	cip->ci_parent = np;
867 	cip->ci_base_nl.rl_type = type;
868 	cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
869 	cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
870 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
871 	cip->ci_base_nl.rl_ids[ID_NAME] = np->rn_id.rl_ids[ID_NAME];
872 	cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = np->rn_id.rl_ids[ID_SNAPSHOT];
873 	cip->ci_base_nl.rl_ids[ID_LEVEL] = np->rn_id.rl_main_id;
874 
875 	return (REP_PROTOCOL_SUCCESS);
876 }
877 
878 static int
879 propertygrp_setup_child_info(rc_node_t *pg, uint32_t type, child_info_t *cip)
880 {
881 	if (type != REP_PROTOCOL_ENTITY_PROPERTY)
882 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
883 
884 	bzero(cip, sizeof (*cip));
885 	cip->ci_parent = pg;
886 	cip->ci_base_nl.rl_type = type;
887 	cip->ci_base_nl.rl_backend = pg->rn_id.rl_backend;
888 	cip->ci_base_nl.rl_ids[ID_SERVICE] = pg->rn_id.rl_ids[ID_SERVICE];
889 	cip->ci_base_nl.rl_ids[ID_INSTANCE] = pg->rn_id.rl_ids[ID_INSTANCE];
890 	cip->ci_base_nl.rl_ids[ID_PG] = pg->rn_id.rl_main_id;
891 	cip->ci_base_nl.rl_ids[ID_GEN] = pg->rn_gen_id;
892 	cip->ci_base_nl.rl_ids[ID_NAME] = pg->rn_id.rl_ids[ID_NAME];
893 	cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = pg->rn_id.rl_ids[ID_SNAPSHOT];
894 	cip->ci_base_nl.rl_ids[ID_LEVEL] = pg->rn_id.rl_ids[ID_LEVEL];
895 
896 	return (REP_PROTOCOL_SUCCESS);
897 }
898 
899 /*
900  * The *_fill_children() functions populate the children of the given rc_node_t
901  * by querying the database and calling rc_node_setup_*() functions (usually
902  * via a fill_*_callback()).
903  *
904  * They fail with
905  *   _NO_RESOURCES
906  */
907 
908 /*
909  * Returns
910  *   _NO_RESOURCES
911  *   _SUCCESS
912  */
913 static int
914 scope_fill_children(rc_node_t *np)
915 {
916 	backend_query_t *q;
917 	child_info_t ci;
918 	int res;
919 
920 	(void) scope_setup_child_info(np, REP_PROTOCOL_ENTITY_SERVICE, &ci);
921 
922 	q = backend_query_alloc();
923 	backend_query_append(q, "SELECT svc_name, svc_id FROM service_tbl");
924 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
925 	backend_query_free(q);
926 
927 	if (res == REP_PROTOCOL_DONE)
928 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
929 	return (res);
930 }
931 
932 /*
933  * Returns
934  *   _NO_RESOURCES
935  *   _SUCCESS
936  */
937 static int
938 service_fill_children(rc_node_t *np)
939 {
940 	backend_query_t *q;
941 	child_info_t ci;
942 	int res;
943 
944 	assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
945 
946 	(void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_INSTANCE, &ci);
947 
948 	q = backend_query_alloc();
949 	backend_query_add(q,
950 	    "SELECT instance_name, instance_id FROM instance_tbl"
951 	    "    WHERE (instance_svc = %d)",
952 	    np->rn_id.rl_main_id);
953 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
954 	backend_query_free(q);
955 
956 	if (res == REP_PROTOCOL_DONE)
957 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
958 	if (res != REP_PROTOCOL_SUCCESS)
959 		return (res);
960 
961 	(void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
962 	    &ci);
963 
964 	q = backend_query_alloc();
965 	backend_query_add(q,
966 	    "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
967 	    "    WHERE (pg_parent_id = %d)",
968 	    np->rn_id.rl_main_id);
969 
970 	ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
971 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
972 	if (res == REP_PROTOCOL_SUCCESS) {
973 		ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
974 		res = backend_run(BACKEND_TYPE_NONPERSIST, q,
975 		    fill_pg_callback, &ci);
976 		/* nonpersistant database may not exist */
977 		if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
978 			res = REP_PROTOCOL_SUCCESS;
979 	}
980 	if (res == REP_PROTOCOL_DONE)
981 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
982 	backend_query_free(q);
983 
984 	return (res);
985 }
986 
987 /*
988  * Returns
989  *   _NO_RESOURCES
990  *   _SUCCESS
991  */
992 static int
993 instance_fill_children(rc_node_t *np)
994 {
995 	backend_query_t *q;
996 	child_info_t ci;
997 	int res;
998 
999 	assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
1000 
1001 	/* Get child property groups */
1002 	(void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1003 	    &ci);
1004 
1005 	q = backend_query_alloc();
1006 	backend_query_add(q,
1007 	    "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
1008 	    "    WHERE (pg_parent_id = %d)",
1009 	    np->rn_id.rl_main_id);
1010 	ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
1011 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1012 	if (res == REP_PROTOCOL_SUCCESS) {
1013 		ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
1014 		res = backend_run(BACKEND_TYPE_NONPERSIST, q,
1015 		    fill_pg_callback, &ci);
1016 		/* nonpersistant database may not exist */
1017 		if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
1018 			res = REP_PROTOCOL_SUCCESS;
1019 	}
1020 	if (res == REP_PROTOCOL_DONE)
1021 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1022 	backend_query_free(q);
1023 
1024 	if (res != REP_PROTOCOL_SUCCESS)
1025 		return (res);
1026 
1027 	/* Get child snapshots */
1028 	(void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_SNAPSHOT,
1029 	    &ci);
1030 
1031 	q = backend_query_alloc();
1032 	backend_query_add(q,
1033 	    "SELECT lnk_snap_name, lnk_id, lnk_snap_id FROM snapshot_lnk_tbl"
1034 	    "    WHERE (lnk_inst_id = %d)",
1035 	    np->rn_id.rl_main_id);
1036 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_callback, &ci);
1037 	if (res == REP_PROTOCOL_DONE)
1038 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1039 	backend_query_free(q);
1040 
1041 	return (res);
1042 }
1043 
1044 /*
1045  * Returns
1046  *   _NO_RESOURCES
1047  *   _SUCCESS
1048  */
1049 static int
1050 snapshot_fill_children(rc_node_t *np)
1051 {
1052 	rc_node_t *nnp;
1053 	rc_snapshot_t *sp, *oldsp;
1054 	rc_snaplevel_t *lvl;
1055 	rc_node_lookup_t nl;
1056 	int r;
1057 
1058 	/* Get the rc_snapshot_t (& its rc_snaplevel_t's). */
1059 	(void) pthread_mutex_lock(&np->rn_lock);
1060 	sp = np->rn_snapshot;
1061 	(void) pthread_mutex_unlock(&np->rn_lock);
1062 	if (sp == NULL) {
1063 		r = rc_snapshot_get(np->rn_snapshot_id, &sp);
1064 		if (r != REP_PROTOCOL_SUCCESS) {
1065 			assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
1066 			return (r);
1067 		}
1068 		(void) pthread_mutex_lock(&np->rn_lock);
1069 		oldsp = np->rn_snapshot;
1070 		assert(oldsp == NULL || oldsp == sp);
1071 		np->rn_snapshot = sp;
1072 		(void) pthread_mutex_unlock(&np->rn_lock);
1073 		if (oldsp != NULL)
1074 			rc_snapshot_rele(oldsp);
1075 	}
1076 
1077 	bzero(&nl, sizeof (nl));
1078 	nl.rl_type = REP_PROTOCOL_ENTITY_SNAPLEVEL;
1079 	nl.rl_backend = np->rn_id.rl_backend;
1080 	nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
1081 	nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
1082 	nl.rl_ids[ID_NAME] = np->rn_id.rl_main_id;
1083 	nl.rl_ids[ID_SNAPSHOT] = np->rn_snapshot_id;
1084 
1085 	/* Create rc_node_t's for the snapshot's rc_snaplevel_t's. */
1086 	for (lvl = sp->rs_levels; lvl != NULL; lvl = lvl->rsl_next) {
1087 		nnp = rc_node_alloc();
1088 		assert(nnp != NULL);
1089 		nl.rl_main_id = lvl->rsl_level_id;
1090 		nnp = rc_node_setup_snaplevel(nnp, &nl, lvl, np);
1091 		rc_node_rele(nnp);
1092 	}
1093 
1094 	return (REP_PROTOCOL_SUCCESS);
1095 }
1096 
1097 /*
1098  * Returns
1099  *   _NO_RESOURCES
1100  *   _SUCCESS
1101  */
1102 static int
1103 snaplevel_fill_children(rc_node_t *np)
1104 {
1105 	rc_snaplevel_t *lvl = np->rn_snaplevel;
1106 	child_info_t ci;
1107 	int res;
1108 	backend_query_t *q;
1109 
1110 	(void) snaplevel_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1111 	    &ci);
1112 
1113 	q = backend_query_alloc();
1114 	backend_query_add(q,
1115 	    "SELECT snaplvl_pg_name, snaplvl_pg_id, snaplvl_gen_id, "
1116 	    "    snaplvl_pg_type, snaplvl_pg_flags "
1117 	    "    FROM snaplevel_lnk_tbl "
1118 	    "    WHERE (snaplvl_level_id = %d)",
1119 	    lvl->rsl_level_id);
1120 	res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1121 	if (res == REP_PROTOCOL_DONE)
1122 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1123 	backend_query_free(q);
1124 
1125 	return (res);
1126 }
1127 
1128 /*
1129  * Returns
1130  *   _NO_RESOURCES
1131  *   _SUCCESS
1132  */
1133 static int
1134 propertygrp_fill_children(rc_node_t *np)
1135 {
1136 	backend_query_t *q;
1137 	child_info_t ci;
1138 	int res;
1139 	backend_tx_t *tx;
1140 
1141 	backend_type_t backend = np->rn_id.rl_backend;
1142 
1143 	(void) propertygrp_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTY,
1144 	    &ci);
1145 
1146 	res = backend_tx_begin_ro(backend, &tx);
1147 	if (res != REP_PROTOCOL_SUCCESS) {
1148 		/*
1149 		 * If the backend didn't exist, we wouldn't have got this
1150 		 * property group.
1151 		 */
1152 		assert(res != REP_PROTOCOL_FAIL_BACKEND_ACCESS);
1153 		return (res);
1154 	}
1155 
1156 	ci.ci_tx = tx;
1157 
1158 	q = backend_query_alloc();
1159 	backend_query_add(q,
1160 	    "SELECT lnk_prop_name, lnk_prop_id, lnk_prop_type, lnk_val_id "
1161 	    "FROM prop_lnk_tbl "
1162 	    "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
1163 	    np->rn_id.rl_main_id, np->rn_gen_id);
1164 	res = backend_tx_run(tx, q, fill_property_callback, &ci);
1165 	if (res == REP_PROTOCOL_DONE)
1166 		res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1167 	backend_query_free(q);
1168 	backend_tx_end_ro(tx);
1169 
1170 	return (res);
1171 }
1172 
1173 /*
1174  * Fails with
1175  *   _TYPE_MISMATCH - lp is not for a service
1176  *   _INVALID_TYPE - lp has invalid type
1177  *   _BAD_REQUEST - name is invalid
1178  */
1179 static int
1180 scope_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1181 {
1182 	uint32_t type = lp->rl_type;
1183 	int rc;
1184 
1185 	if (type != REP_PROTOCOL_ENTITY_SERVICE)
1186 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1187 
1188 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1189 		return (rc);
1190 
1191 	backend_query_add(q,
1192 	    "SELECT svc_id FROM service_tbl "
1193 	    "WHERE svc_name = '%q'",
1194 	    name);
1195 
1196 	return (REP_PROTOCOL_SUCCESS);
1197 }
1198 
1199 /*
1200  * Fails with
1201  *   _NO_RESOURCES - out of memory
1202  */
1203 static int
1204 scope_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1205 {
1206 	return (backend_tx_run_update(tx,
1207 	    "INSERT INTO service_tbl (svc_id, svc_name) "
1208 	    "VALUES (%d, '%q')",
1209 	    lp->rl_main_id, name));
1210 }
1211 
1212 /*
1213  * Fails with
1214  *   _TYPE_MISMATCH - lp is not for an instance or property group
1215  *   _INVALID_TYPE - lp has invalid type
1216  *   _BAD_REQUEST - name is invalid
1217  */
1218 static int
1219 service_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1220 {
1221 	uint32_t type = lp->rl_type;
1222 	int rc;
1223 
1224 	if (type != REP_PROTOCOL_ENTITY_INSTANCE &&
1225 	    type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
1226 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1227 
1228 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1229 		return (rc);
1230 
1231 	switch (type) {
1232 	case REP_PROTOCOL_ENTITY_INSTANCE:
1233 		backend_query_add(q,
1234 		    "SELECT instance_id FROM instance_tbl "
1235 		    "WHERE instance_name = '%q' AND instance_svc = %d",
1236 		    name, lp->rl_ids[ID_SERVICE]);
1237 		break;
1238 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1239 		backend_query_add(q,
1240 		    "SELECT pg_id FROM pg_tbl "
1241 		    "    WHERE pg_name = '%q' AND pg_parent_id = %d",
1242 		    name, lp->rl_ids[ID_SERVICE]);
1243 		break;
1244 	default:
1245 		assert(0);
1246 		abort();
1247 	}
1248 
1249 	return (REP_PROTOCOL_SUCCESS);
1250 }
1251 
1252 /*
1253  * Fails with
1254  *   _NO_RESOURCES - out of memory
1255  */
1256 static int
1257 service_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1258 {
1259 	return (backend_tx_run_update(tx,
1260 	    "INSERT INTO instance_tbl "
1261 	    "    (instance_id, instance_name, instance_svc) "
1262 	    "VALUES (%d, '%q', %d)",
1263 	    lp->rl_main_id, name, lp->rl_ids[ID_SERVICE]));
1264 }
1265 
1266 /*
1267  * Fails with
1268  *   _NO_RESOURCES - out of memory
1269  */
1270 static int
1271 instance_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1272 {
1273 	return (backend_tx_run_update(tx,
1274 	    "INSERT INTO snapshot_lnk_tbl "
1275 	    "    (lnk_id, lnk_inst_id, lnk_snap_name, lnk_snap_id) "
1276 	    "VALUES (%d, %d, '%q', 0)",
1277 	    lp->rl_main_id, lp->rl_ids[ID_INSTANCE], name));
1278 }
1279 
1280 /*
1281  * Fails with
1282  *   _TYPE_MISMATCH - lp is not for a property group or snapshot
1283  *   _INVALID_TYPE - lp has invalid type
1284  *   _BAD_REQUEST - name is invalid
1285  */
1286 static int
1287 instance_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1288 {
1289 	uint32_t type = lp->rl_type;
1290 	int rc;
1291 
1292 	if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
1293 	    type != REP_PROTOCOL_ENTITY_SNAPSHOT)
1294 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1295 
1296 	if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1297 		return (rc);
1298 
1299 	switch (type) {
1300 	case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1301 		backend_query_add(q,
1302 		    "SELECT pg_id FROM pg_tbl "
1303 		    "    WHERE pg_name = '%q' AND pg_parent_id = %d",
1304 		    name, lp->rl_ids[ID_INSTANCE]);
1305 		break;
1306 	case REP_PROTOCOL_ENTITY_SNAPSHOT:
1307 		backend_query_add(q,
1308 		    "SELECT lnk_id FROM snapshot_lnk_tbl "
1309 		    "    WHERE lnk_snap_name = '%q' AND lnk_inst_id = %d",
1310 		    name, lp->rl_ids[ID_INSTANCE]);
1311 		break;
1312 	default:
1313 		assert(0);
1314 		abort();
1315 	}
1316 
1317 	return (REP_PROTOCOL_SUCCESS);
1318 }
1319 
1320 static int
1321 generic_insert_pg_child(backend_tx_t *tx, rc_node_lookup_t *lp,
1322     const char *name, const char *pgtype, uint32_t flags, uint32_t gen)
1323 {
1324 	int parent_id = (lp->rl_ids[ID_INSTANCE] != 0)?
1325 	    lp->rl_ids[ID_INSTANCE] : lp->rl_ids[ID_SERVICE];
1326 	return (backend_tx_run_update(tx,
1327 	    "INSERT INTO pg_tbl "
1328 	    "    (pg_id, pg_name, pg_parent_id, pg_type, pg_flags, pg_gen_id) "
1329 	    "VALUES (%d, '%q', %d, '%q', %d, %d)",
1330 	    lp->rl_main_id, name, parent_id, pgtype, flags, gen));
1331 }
1332 
1333 static int
1334 service_delete_start(rc_node_t *np, delete_info_t *dip)
1335 {
1336 	int r;
1337 	backend_query_t *q = backend_query_alloc();
1338 
1339 	/*
1340 	 * Check for child instances, and refuse to delete if they exist.
1341 	 */
1342 	backend_query_add(q,
1343 	    "SELECT 1 FROM instance_tbl WHERE instance_svc = %d",
1344 	    np->rn_id.rl_main_id);
1345 
1346 	r = backend_tx_run(dip->di_tx, q, backend_fail_if_seen, NULL);
1347 	backend_query_free(q);
1348 
1349 	if (r == REP_PROTOCOL_DONE)
1350 		return (REP_PROTOCOL_FAIL_EXISTS);	/* instances exist */
1351 
1352 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &service_delete,
1353 	    np->rn_id.rl_main_id, 0));
1354 }
1355 
1356 static int
1357 instance_delete_start(rc_node_t *np, delete_info_t *dip)
1358 {
1359 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &instance_delete,
1360 	    np->rn_id.rl_main_id, 0));
1361 }
1362 
1363 static int
1364 snapshot_delete_start(rc_node_t *np, delete_info_t *dip)
1365 {
1366 	return (delete_stack_push(dip, BACKEND_TYPE_NORMAL,
1367 	    &snapshot_lnk_delete, np->rn_id.rl_main_id, 0));
1368 }
1369 
1370 static int
1371 propertygrp_delete_start(rc_node_t *np, delete_info_t *dip)
1372 {
1373 	return (delete_stack_push(dip, np->rn_id.rl_backend,
1374 	    &propertygrp_delete, np->rn_id.rl_main_id, 0));
1375 }
1376 
1377 static object_info_t info[] = {
1378 	{REP_PROTOCOL_ENTITY_NONE},
1379 	{REP_PROTOCOL_ENTITY_SCOPE,
1380 		BACKEND_ID_INVALID,
1381 		scope_fill_children,
1382 		scope_setup_child_info,
1383 		scope_query_child,
1384 		scope_insert_child,
1385 		NULL,
1386 		NULL,
1387 	},
1388 	{REP_PROTOCOL_ENTITY_SERVICE,
1389 		BACKEND_ID_SERVICE_INSTANCE,
1390 		service_fill_children,
1391 		service_setup_child_info,
1392 		service_query_child,
1393 		service_insert_child,
1394 		generic_insert_pg_child,
1395 		service_delete_start,
1396 	},
1397 	{REP_PROTOCOL_ENTITY_INSTANCE,
1398 		BACKEND_ID_SERVICE_INSTANCE,
1399 		instance_fill_children,
1400 		instance_setup_child_info,
1401 		instance_query_child,
1402 		instance_insert_child,
1403 		generic_insert_pg_child,
1404 		instance_delete_start,
1405 	},
1406 	{REP_PROTOCOL_ENTITY_SNAPSHOT,
1407 		BACKEND_ID_SNAPNAME,
1408 		snapshot_fill_children,
1409 		NULL,
1410 		NULL,
1411 		NULL,
1412 		NULL,
1413 		snapshot_delete_start,
1414 	},
1415 	{REP_PROTOCOL_ENTITY_SNAPLEVEL,
1416 		BACKEND_ID_SNAPLEVEL,
1417 		snaplevel_fill_children,
1418 		snaplevel_setup_child_info,
1419 	},
1420 	{REP_PROTOCOL_ENTITY_PROPERTYGRP,
1421 		BACKEND_ID_PROPERTYGRP,
1422 		propertygrp_fill_children,
1423 		NULL,
1424 		NULL,
1425 		NULL,
1426 		NULL,
1427 		propertygrp_delete_start,
1428 	},
1429 	{REP_PROTOCOL_ENTITY_PROPERTY},
1430 	{-1UL}
1431 };
1432 #define	NUM_INFO (sizeof (info) / sizeof (*info))
1433 
1434 /*
1435  * object_fill_children() populates the child list of an rc_node_t by calling
1436  * the appropriate <type>_fill_children() which runs backend queries that
1437  * call an appropriate fill_*_callback() which takes a row of results,
1438  * decodes them, and calls an rc_node_setup*() function in rc_node.c to create
1439  * a child.
1440  *
1441  * Fails with
1442  *   _NO_RESOURCES
1443  */
1444 int
1445 object_fill_children(rc_node_t *pp)
1446 {
1447 	uint32_t type = pp->rn_id.rl_type;
1448 	assert(type > 0 && type < NUM_INFO);
1449 
1450 	return ((*info[type].obj_fill_children)(pp));
1451 }
1452 
1453 int
1454 object_delete(rc_node_t *pp)
1455 {
1456 	int rc;
1457 
1458 	delete_info_t dip;
1459 	delete_ent_t de;
1460 
1461 	uint32_t type = pp->rn_id.rl_type;
1462 	assert(type > 0 && type < NUM_INFO);
1463 
1464 	if (info[type].obj_delete_start == NULL)
1465 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1466 
1467 	(void) memset(&dip, '\0', sizeof (dip));
1468 	rc = backend_tx_begin(BACKEND_TYPE_NORMAL, &dip.di_tx);
1469 	if (rc != REP_PROTOCOL_SUCCESS)
1470 		return (rc);
1471 
1472 	rc = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &dip.di_np_tx);
1473 	if (rc == REP_PROTOCOL_FAIL_BACKEND_ACCESS ||
1474 	    rc == REP_PROTOCOL_FAIL_BACKEND_READONLY)
1475 		dip.di_np_tx = NULL;
1476 	else if (rc != REP_PROTOCOL_SUCCESS) {
1477 		backend_tx_rollback(dip.di_tx);
1478 		return (rc);
1479 	}
1480 
1481 	if ((rc = (*info[type].obj_delete_start)(pp, &dip)) !=
1482 	    REP_PROTOCOL_SUCCESS) {
1483 		goto fail;
1484 	}
1485 
1486 	while (delete_stack_pop(&dip, &de)) {
1487 		rc = (*de.de_cb)(&dip, &de);
1488 		if (rc != REP_PROTOCOL_SUCCESS)
1489 			goto fail;
1490 	}
1491 
1492 	rc = backend_tx_commit(dip.di_tx);
1493 	if (rc != REP_PROTOCOL_SUCCESS)
1494 		backend_tx_rollback(dip.di_np_tx);
1495 	else if (dip.di_np_tx)
1496 		(void) backend_tx_commit(dip.di_np_tx);
1497 
1498 	delete_stack_cleanup(&dip);
1499 
1500 	return (rc);
1501 
1502 fail:
1503 	backend_tx_rollback(dip.di_tx);
1504 	backend_tx_rollback(dip.di_np_tx);
1505 	delete_stack_cleanup(&dip);
1506 	return (rc);
1507 }
1508 
1509 int
1510 object_do_create(backend_tx_t *tx, child_info_t *cip, rc_node_t *pp,
1511     uint32_t type, const char *name, rc_node_t **cpp)
1512 {
1513 	uint32_t ptype = pp->rn_id.rl_type;
1514 
1515 	backend_query_t *q;
1516 	uint32_t id;
1517 	rc_node_t *np = NULL;
1518 	int rc;
1519 	object_info_t *ip;
1520 
1521 	rc_node_lookup_t *lp = &cip->ci_base_nl;
1522 
1523 	assert(ptype > 0 && ptype < NUM_INFO);
1524 
1525 	ip = &info[ptype];
1526 
1527 	if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP)
1528 		return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
1529 
1530 	if (ip->obj_setup_child_info == NULL ||
1531 	    ip->obj_query_child == NULL ||
1532 	    ip->obj_insert_child == NULL)
1533 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1534 
1535 	if ((rc = (*ip->obj_setup_child_info)(pp, type, cip)) !=
1536 	    REP_PROTOCOL_SUCCESS)
1537 		return (rc);
1538 
1539 	q = backend_query_alloc();
1540 	if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1541 	    REP_PROTOCOL_SUCCESS) {
1542 		assert(rc == REP_PROTOCOL_FAIL_BAD_REQUEST);
1543 		backend_query_free(q);
1544 		return (rc);
1545 	}
1546 
1547 	rc = backend_tx_run_single_int(tx, q, &id);
1548 	backend_query_free(q);
1549 
1550 	if (rc == REP_PROTOCOL_SUCCESS)
1551 		return (REP_PROTOCOL_FAIL_EXISTS);
1552 	else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND)
1553 		return (rc);
1554 
1555 	if ((lp->rl_main_id = backend_new_id(tx,
1556 	    info[type].obj_id_space)) == 0) {
1557 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1558 	}
1559 
1560 	if ((np = rc_node_alloc()) == NULL)
1561 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1562 
1563 	if ((rc = (*ip->obj_insert_child)(tx, lp, name)) !=
1564 	    REP_PROTOCOL_SUCCESS) {
1565 		rc_node_destroy(np);
1566 		return (rc);
1567 	}
1568 
1569 	*cpp = np;
1570 	return (REP_PROTOCOL_SUCCESS);
1571 }
1572 
1573 /*
1574  * Fails with
1575  *   _NOT_APPLICABLE - type is _PROPERTYGRP
1576  *   _BAD_REQUEST - cannot create children for this type of node
1577  *		    name is invalid
1578  *   _TYPE_MISMATCH - object cannot have children of type type
1579  *   _NO_RESOURCES - out of memory, or could not allocate new id
1580  *   _BACKEND_READONLY
1581  *   _BACKEND_ACCESS
1582  *   _EXISTS - child already exists
1583  */
1584 int
1585 object_create(rc_node_t *pp, uint32_t type, const char *name, rc_node_t **cpp)
1586 {
1587 	backend_tx_t *tx;
1588 	rc_node_t *np = NULL;
1589 	child_info_t ci;
1590 	int rc;
1591 
1592 	if ((rc = backend_tx_begin(pp->rn_id.rl_backend, &tx)) !=
1593 	    REP_PROTOCOL_SUCCESS) {
1594 		return (rc);
1595 	}
1596 
1597 	if ((rc = object_do_create(tx, &ci, pp, type, name, &np)) !=
1598 	    REP_PROTOCOL_SUCCESS) {
1599 		backend_tx_rollback(tx);
1600 		return (rc);
1601 	}
1602 
1603 	rc = backend_tx_commit(tx);
1604 	if (rc != REP_PROTOCOL_SUCCESS) {
1605 		rc_node_destroy(np);
1606 		return (rc);
1607 	}
1608 
1609 	*cpp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
1610 
1611 	return (REP_PROTOCOL_SUCCESS);
1612 }
1613 
1614 /*ARGSUSED*/
1615 int
1616 object_create_pg(rc_node_t *pp, uint32_t type, const char *name,
1617     const char *pgtype, uint32_t flags, rc_node_t **cpp)
1618 {
1619 	uint32_t ptype = pp->rn_id.rl_type;
1620 	backend_tx_t *tx_ro, *tx_wr;
1621 	backend_query_t *q;
1622 	uint32_t id;
1623 	uint32_t gen = 0;
1624 	rc_node_t *np = NULL;
1625 	int rc;
1626 	int rc_wr;
1627 	int rc_ro;
1628 	object_info_t *ip;
1629 
1630 	int nonpersist = (flags & SCF_PG_FLAG_NONPERSISTENT);
1631 
1632 	child_info_t ci;
1633 	rc_node_lookup_t *lp = &ci.ci_base_nl;
1634 
1635 	assert(ptype > 0 && ptype < NUM_INFO);
1636 
1637 	if (ptype != REP_PROTOCOL_ENTITY_SERVICE &&
1638 	    ptype != REP_PROTOCOL_ENTITY_INSTANCE)
1639 		return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1640 
1641 	ip = &info[ptype];
1642 
1643 	assert(ip->obj_setup_child_info != NULL &&
1644 	    ip->obj_query_child != NULL &&
1645 	    ip->obj_insert_pg_child != NULL);
1646 
1647 	if ((rc = (*ip->obj_setup_child_info)(pp, type, &ci)) !=
1648 	    REP_PROTOCOL_SUCCESS)
1649 		return (rc);
1650 
1651 	q = backend_query_alloc();
1652 	if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1653 	    REP_PROTOCOL_SUCCESS) {
1654 		backend_query_free(q);
1655 		return (rc);
1656 	}
1657 
1658 	if (!nonpersist) {
1659 		lp->rl_backend = BACKEND_TYPE_NORMAL;
1660 		rc_wr = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx_wr);
1661 		rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NONPERSIST, &tx_ro);
1662 	} else {
1663 		lp->rl_backend = BACKEND_TYPE_NONPERSIST;
1664 		rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NORMAL, &tx_ro);
1665 		rc_wr = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &tx_wr);
1666 	}
1667 
1668 	if (rc_wr != REP_PROTOCOL_SUCCESS) {
1669 		rc = rc_wr;
1670 		goto fail;
1671 	}
1672 	if (rc_ro != REP_PROTOCOL_SUCCESS &&
1673 	    rc_ro != REP_PROTOCOL_FAIL_BACKEND_ACCESS) {
1674 		rc = rc_ro;
1675 		goto fail;
1676 	}
1677 
1678 	if (tx_ro != NULL) {
1679 		rc = backend_tx_run_single_int(tx_ro, q, &id);
1680 
1681 		if (rc == REP_PROTOCOL_SUCCESS) {
1682 			backend_query_free(q);
1683 			rc = REP_PROTOCOL_FAIL_EXISTS;
1684 			goto fail;
1685 		} else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1686 			backend_query_free(q);
1687 			goto fail;
1688 		}
1689 	}
1690 
1691 	rc = backend_tx_run_single_int(tx_wr, q, &id);
1692 	backend_query_free(q);
1693 
1694 	if (rc == REP_PROTOCOL_SUCCESS) {
1695 		rc = REP_PROTOCOL_FAIL_EXISTS;
1696 		goto fail;
1697 	} else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1698 		goto fail;
1699 	}
1700 
1701 	if (tx_ro != NULL)
1702 		backend_tx_end_ro(tx_ro);
1703 	tx_ro = NULL;
1704 
1705 	if ((lp->rl_main_id = backend_new_id(tx_wr,
1706 	    info[type].obj_id_space)) == 0) {
1707 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1708 		goto fail;
1709 	}
1710 
1711 	if ((np = rc_node_alloc()) == NULL) {
1712 		rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1713 		goto fail;
1714 	}
1715 
1716 	if ((rc = (*ip->obj_insert_pg_child)(tx_wr, lp, name, pgtype, flags,
1717 	    gen)) != REP_PROTOCOL_SUCCESS) {
1718 		rc_node_destroy(np);
1719 		goto fail;
1720 	}
1721 
1722 	rc = backend_tx_commit(tx_wr);
1723 	if (rc != REP_PROTOCOL_SUCCESS) {
1724 		rc_node_destroy(np);
1725 		return (rc);
1726 	}
1727 
1728 	*cpp = rc_node_setup_pg(np, lp, name, pgtype, flags, gen, ci.ci_parent);
1729 
1730 	return (REP_PROTOCOL_SUCCESS);
1731 
1732 fail:
1733 	if (tx_ro != NULL)
1734 		backend_tx_end_ro(tx_ro);
1735 	if (tx_wr != NULL)
1736 		backend_tx_rollback(tx_wr);
1737 	return (rc);
1738 }
1739 
1740 /*
1741  * Given a row of snaplevel number, snaplevel id, service id, service name,
1742  * instance id, & instance name, create a rc_snaplevel_t & prepend it onto the
1743  * rs_levels list of the rc_snapshot_t passed in as data.
1744  * Returns _CONTINUE on success or _ABORT if any allocations fail.
1745  */
1746 /*ARGSUSED*/
1747 static int
1748 fill_snapshot_cb(void *data, int columns, char **vals, char **names)
1749 {
1750 	rc_snapshot_t *sp = data;
1751 	rc_snaplevel_t *lvl;
1752 	char *num = vals[0];
1753 	char *id = vals[1];
1754 	char *service_id = vals[2];
1755 	char *service = vals[3];
1756 	char *instance_id = vals[4];
1757 	char *instance = vals[5];
1758 	assert(columns == 6);
1759 
1760 	lvl = uu_zalloc(sizeof (*lvl));
1761 	if (lvl == NULL)
1762 		return (BACKEND_CALLBACK_ABORT);
1763 	lvl->rsl_parent = sp;
1764 	lvl->rsl_next = sp->rs_levels;
1765 	sp->rs_levels = lvl;
1766 
1767 	if (uu_strtouint(num, &lvl->rsl_level_num,
1768 	    sizeof (lvl->rsl_level_num), 0, 0, 0) == -1 ||
1769 	    uu_strtouint(id, &lvl->rsl_level_id,
1770 	    sizeof (lvl->rsl_level_id), 0, 0, 0) == -1 ||
1771 	    uu_strtouint(service_id, &lvl->rsl_service_id,
1772 	    sizeof (lvl->rsl_level_num), 0, 0, 0) == -1 ||
1773 	    (instance_id != NULL &&
1774 	    uu_strtouint(instance_id, &lvl->rsl_instance_id,
1775 	    sizeof (lvl->rsl_instance_id), 0, 0, 0) == -1)) {
1776 		backend_panic("invalid integer in database");
1777 	}
1778 
1779 	lvl->rsl_scope = (const char *)"localhost";
1780 	lvl->rsl_service = strdup(service);
1781 	if (lvl->rsl_service == NULL) {
1782 		uu_free(lvl);
1783 		return (BACKEND_CALLBACK_ABORT);
1784 	}
1785 	if (instance) {
1786 		assert(lvl->rsl_instance_id != 0);
1787 		lvl->rsl_instance = strdup(instance);
1788 		if (lvl->rsl_instance == NULL) {
1789 			free((void *)lvl->rsl_instance);
1790 			uu_free(lvl);
1791 			return (BACKEND_CALLBACK_ABORT);
1792 		}
1793 	} else {
1794 		assert(lvl->rsl_instance_id == 0);
1795 	}
1796 
1797 	return (BACKEND_CALLBACK_CONTINUE);
1798 }
1799 
1800 /*
1801  * Populate sp's rs_levels list from the snaplevel_tbl table.
1802  * Fails with
1803  *   _NO_RESOURCES
1804  */
1805 int
1806 object_fill_snapshot(rc_snapshot_t *sp)
1807 {
1808 	backend_query_t *q;
1809 	rc_snaplevel_t *sl;
1810 	int result;
1811 	int i;
1812 
1813 	q = backend_query_alloc();
1814 	backend_query_add(q,
1815 	    "SELECT snap_level_num, snap_level_id, "
1816 	    "    snap_level_service_id, snap_level_service, "
1817 	    "    snap_level_instance_id, snap_level_instance "
1818 	    "FROM snaplevel_tbl "
1819 	    "WHERE snap_id = %d "
1820 	    "ORDER BY snap_level_id DESC",
1821 	    sp->rs_snap_id);
1822 
1823 	result = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_cb, sp);
1824 	if (result == REP_PROTOCOL_DONE)
1825 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1826 	backend_query_free(q);
1827 
1828 	if (result == REP_PROTOCOL_SUCCESS) {
1829 		i = 0;
1830 		for (sl = sp->rs_levels; sl != NULL; sl = sl->rsl_next) {
1831 			if (sl->rsl_level_num != ++i) {
1832 				backend_panic("snaplevels corrupt; expected "
1833 				    "level %d, got %d", i, sl->rsl_level_num);
1834 			}
1835 		}
1836 	}
1837 	return (result);
1838 }
1839 
1840 /*ARGSUSED*/
1841 static int
1842 object_copy_string(void *data_arg, int columns, char **vals, char **names)
1843 {
1844 	char **data = data_arg;
1845 
1846 	assert(columns == 1);
1847 
1848 	if (*data != NULL)
1849 		free(*data);
1850 	*data = NULL;
1851 
1852 	if (vals[0] != NULL) {
1853 		if ((*data = strdup(vals[0])) == NULL)
1854 			return (BACKEND_CALLBACK_ABORT);
1855 	}
1856 
1857 	return (BACKEND_CALLBACK_CONTINUE);
1858 }
1859 
1860 struct snaplevel_add_info {
1861 	backend_query_t *sai_q;
1862 	uint32_t	sai_level_id;
1863 	int		sai_used;		/* sai_q has been used */
1864 };
1865 
1866 /*ARGSUSED*/
1867 static int
1868 object_snaplevel_process_pg(void *data_arg, int columns, char **vals,
1869     char **names)
1870 {
1871 	struct snaplevel_add_info *data = data_arg;
1872 
1873 	assert(columns == 5);
1874 
1875 	backend_query_add(data->sai_q,
1876 	    "INSERT INTO snaplevel_lnk_tbl "
1877 	    "    (snaplvl_level_id, snaplvl_pg_id, snaplvl_pg_name, "
1878 	    "    snaplvl_pg_type, snaplvl_pg_flags, snaplvl_gen_id)"
1879 	    "VALUES (%d, %s, '%q', '%q', %s, %s);",
1880 	    data->sai_level_id, vals[0], vals[1], vals[2], vals[3], vals[4]);
1881 
1882 	data->sai_used = 1;
1883 
1884 	return (BACKEND_CALLBACK_CONTINUE);
1885 }
1886 
1887 /*ARGSUSED*/
1888 static int
1889 object_snapshot_add_level(backend_tx_t *tx, uint32_t snap_id,
1890     uint32_t snap_level_num, uint32_t svc_id, const char *svc_name,
1891     uint32_t inst_id, const char *inst_name)
1892 {
1893 	struct snaplevel_add_info data;
1894 	backend_query_t *q;
1895 	int result;
1896 
1897 	assert((snap_level_num == 1 && inst_name != NULL) ||
1898 	    snap_level_num == 2 && inst_name == NULL);
1899 
1900 	data.sai_level_id = backend_new_id(tx, BACKEND_ID_SNAPLEVEL);
1901 	if (data.sai_level_id == 0) {
1902 		return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1903 	}
1904 
1905 	result = backend_tx_run_update(tx,
1906 	    "INSERT INTO snaplevel_tbl "
1907 	    "    (snap_id, snap_level_num, snap_level_id, "
1908 	    "    snap_level_service_id, snap_level_service, "
1909 	    "    snap_level_instance_id, snap_level_instance) "
1910 	    "VALUES (%d, %d, %d, %d, %Q, %d, %Q);",
1911 	    snap_id, snap_level_num, data.sai_level_id, svc_id, svc_name,
1912 	    inst_id, inst_name);
1913 
1914 	q = backend_query_alloc();
1915 	backend_query_add(q,
1916 	    "SELECT pg_id, pg_name, pg_type, pg_flags, pg_gen_id FROM pg_tbl "
1917 	    "WHERE (pg_parent_id = %d);",
1918 	    (inst_name != NULL)? inst_id : svc_id);
1919 
1920 	data.sai_q = backend_query_alloc();
1921 	data.sai_used = 0;
1922 	result = backend_tx_run(tx, q, object_snaplevel_process_pg,
1923 	    &data);
1924 	backend_query_free(q);
1925 
1926 	if (result == REP_PROTOCOL_SUCCESS && data.sai_used != 0)
1927 		result = backend_tx_run(tx, data.sai_q, NULL, NULL);
1928 	backend_query_free(data.sai_q);
1929 
1930 	return (result);
1931 }
1932 
1933 /*
1934  * Fails with:
1935  *	_NO_RESOURCES - no new id or out of disk space
1936  *	_BACKEND_READONLY - persistent backend is read-only
1937  */
1938 static int
1939 object_snapshot_do_take(uint32_t instid, const char *inst_name,
1940     uint32_t svcid, const char *svc_name,
1941     backend_tx_t **tx_out, uint32_t *snapid_out)
1942 {
1943 	backend_tx_t *tx;
1944 	backend_query_t *q;
1945 	int result;
1946 
1947 	char *svc_name_alloc = NULL;
1948 	char *inst_name_alloc = NULL;
1949 	uint32_t snapid;
1950 
1951 	result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
1952 	if (result != REP_PROTOCOL_SUCCESS)
1953 		return (result);
1954 
1955 	snapid = backend_new_id(tx, BACKEND_ID_SNAPSHOT);
1956 	if (snapid == 0) {
1957 		result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1958 		goto fail;
1959 	}
1960 
1961 	if (svc_name == NULL) {
1962 		q = backend_query_alloc();
1963 		backend_query_add(q,
1964 		    "SELECT svc_name FROM service_tbl "
1965 		    "WHERE (svc_id = %d)", svcid);
1966 		result = backend_tx_run(tx, q, object_copy_string,
1967 		    &svc_name_alloc);
1968 		backend_query_free(q);
1969 
1970 		svc_name = svc_name_alloc;
1971 
1972 		if (result == REP_PROTOCOL_DONE) {
1973 			result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1974 			goto fail;
1975 		}
1976 		if (result == REP_PROTOCOL_SUCCESS && svc_name == NULL)
1977 			backend_panic("unable to find name for svc id %d\n",
1978 			    svcid);
1979 
1980 		if (result != REP_PROTOCOL_SUCCESS)
1981 			goto fail;
1982 	}
1983 
1984 	if (inst_name == NULL) {
1985 		q = backend_query_alloc();
1986 		backend_query_add(q,
1987 		    "SELECT instance_name FROM instance_tbl "
1988 		    "WHERE (instance_id = %d)", instid);
1989 		result = backend_tx_run(tx, q, object_copy_string,
1990 		    &inst_name_alloc);
1991 		backend_query_free(q);
1992 
1993 		inst_name = inst_name_alloc;
1994 
1995 		if (result == REP_PROTOCOL_DONE) {
1996 			result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1997 			goto fail;
1998 		}
1999 
2000 		if (result == REP_PROTOCOL_SUCCESS && inst_name == NULL)
2001 			backend_panic(
2002 			    "unable to find name for instance id %d\n", instid);
2003 
2004 		if (result != REP_PROTOCOL_SUCCESS)
2005 			goto fail;
2006 	}
2007 
2008 	result = object_snapshot_add_level(tx, snapid, 1,
2009 	    svcid, svc_name, instid, inst_name);
2010 
2011 	if (result != REP_PROTOCOL_SUCCESS)
2012 		goto fail;
2013 
2014 	result = object_snapshot_add_level(tx, snapid, 2,
2015 	    svcid, svc_name, 0, NULL);
2016 
2017 	if (result != REP_PROTOCOL_SUCCESS)
2018 		goto fail;
2019 
2020 	*snapid_out = snapid;
2021 	*tx_out = tx;
2022 
2023 	free(svc_name_alloc);
2024 	free(inst_name_alloc);
2025 
2026 	return (REP_PROTOCOL_SUCCESS);
2027 
2028 fail:
2029 	backend_tx_rollback(tx);
2030 	free(svc_name_alloc);
2031 	free(inst_name_alloc);
2032 	return (result);
2033 }
2034 
2035 /*
2036  * Fails with:
2037  *	_TYPE_MISMATCH - pp is not an instance
2038  *	_NO_RESOURCES - no new id or out of disk space
2039  *	_BACKEND_READONLY - persistent backend is read-only
2040  */
2041 int
2042 object_snapshot_take_new(rc_node_t *pp,
2043     const char *svc_name, const char *inst_name,
2044     const char *name, rc_node_t **outp)
2045 {
2046 	rc_node_lookup_t *insti = &pp->rn_id;
2047 
2048 	uint32_t instid = insti->rl_main_id;
2049 	uint32_t svcid = insti->rl_ids[ID_SERVICE];
2050 	uint32_t snapid = 0;
2051 	backend_tx_t *tx = NULL;
2052 	child_info_t ci;
2053 	rc_node_t *np;
2054 	int result;
2055 
2056 	if (insti->rl_type != REP_PROTOCOL_ENTITY_INSTANCE)
2057 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2058 
2059 	result = object_snapshot_do_take(instid, inst_name, svcid, svc_name,
2060 	    &tx, &snapid);
2061 	if (result != REP_PROTOCOL_SUCCESS)
2062 		return (result);
2063 
2064 	if ((result = object_do_create(tx, &ci, pp,
2065 	    REP_PROTOCOL_ENTITY_SNAPSHOT, name, &np)) != REP_PROTOCOL_SUCCESS) {
2066 		backend_tx_rollback(tx);
2067 		return (result);
2068 	}
2069 
2070 	/*
2071 	 * link the new object to the new snapshot.
2072 	 */
2073 	np->rn_snapshot_id = snapid;
2074 
2075 	result = backend_tx_run_update(tx,
2076 	    "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2077 	    snapid, ci.ci_base_nl.rl_main_id);
2078 	if (result != REP_PROTOCOL_SUCCESS) {
2079 		backend_tx_rollback(tx);
2080 		rc_node_destroy(np);
2081 		return (result);
2082 	}
2083 	result = backend_tx_commit(tx);
2084 	if (result != REP_PROTOCOL_SUCCESS) {
2085 		rc_node_destroy(np);
2086 		return (result);
2087 	}
2088 
2089 	*outp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
2090 	return (REP_PROTOCOL_SUCCESS);
2091 }
2092 
2093 /*
2094  * Fails with:
2095  *	_TYPE_MISMATCH - pp is not an instance
2096  *	_NO_RESOURCES - no new id or out of disk space
2097  *	_BACKEND_READONLY - persistent backend is read-only
2098  */
2099 int
2100 object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr,
2101     int takesnap)
2102 {
2103 	uint32_t svcid = snapi->rl_ids[ID_SERVICE];
2104 	uint32_t instid = snapi->rl_ids[ID_INSTANCE];
2105 	uint32_t snapid = *snapid_ptr;
2106 	uint32_t oldsnapid = 0;
2107 	backend_tx_t *tx = NULL;
2108 	backend_query_t *q;
2109 	int result;
2110 
2111 	delete_info_t dip;
2112 	delete_ent_t de;
2113 
2114 	if (snapi->rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
2115 		return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2116 
2117 	if (takesnap) {
2118 		result = object_snapshot_do_take(instid, NULL,
2119 		    svcid, NULL, &tx, &snapid);
2120 		if (result != REP_PROTOCOL_SUCCESS)
2121 			return (result);
2122 	} else {
2123 		result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2124 		if (result != REP_PROTOCOL_SUCCESS)
2125 			return (result);
2126 	}
2127 
2128 	q = backend_query_alloc();
2129 	backend_query_add(q,
2130 	    "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
2131 	    "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2132 	    snapi->rl_main_id, snapid, snapi->rl_main_id);
2133 	result = backend_tx_run_single_int(tx, q, &oldsnapid);
2134 	backend_query_free(q);
2135 
2136 	if (result == REP_PROTOCOL_FAIL_NOT_FOUND) {
2137 		backend_tx_rollback(tx);
2138 		backend_panic("unable to find snapshot id %d",
2139 		    snapi->rl_main_id);
2140 	}
2141 	if (result != REP_PROTOCOL_SUCCESS)
2142 		goto fail;
2143 
2144 	/*
2145 	 * Now we use the delete stack to handle the possible unreferencing
2146 	 * of oldsnapid.
2147 	 */
2148 	(void) memset(&dip, 0, sizeof (dip));
2149 	dip.di_tx = tx;
2150 	dip.di_np_tx = NULL;	/* no need for non-persistant backend */
2151 
2152 	if ((result = delete_stack_push(&dip, BACKEND_TYPE_NORMAL,
2153 	    &snaplevel_tbl_delete, oldsnapid, 0)) != REP_PROTOCOL_SUCCESS)
2154 		goto fail;
2155 
2156 	while (delete_stack_pop(&dip, &de)) {
2157 		result = (*de.de_cb)(&dip, &de);
2158 		if (result != REP_PROTOCOL_SUCCESS)
2159 			goto fail;
2160 	}
2161 
2162 	result = backend_tx_commit(tx);
2163 	if (result != REP_PROTOCOL_SUCCESS)
2164 		goto fail;
2165 
2166 	delete_stack_cleanup(&dip);
2167 	*snapid_ptr = snapid;
2168 	return (REP_PROTOCOL_SUCCESS);
2169 
2170 fail:
2171 	backend_tx_rollback(tx);
2172 	delete_stack_cleanup(&dip);
2173 	return (result);
2174 }
2175