summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/configd/file_object.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/svc/configd/file_object.c')
-rw-r--r--usr/src/cmd/svc/configd/file_object.c2174
1 files changed, 2174 insertions, 0 deletions
diff --git a/usr/src/cmd/svc/configd/file_object.c b/usr/src/cmd/svc/configd/file_object.c
new file mode 100644
index 0000000000..52bff4858f
--- /dev/null
+++ b/usr/src/cmd/svc/configd/file_object.c
@@ -0,0 +1,2174 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file_object.c - enter objects into and load them from the backend
+ *
+ * The primary entry points in this layer are object_create(),
+ * object_create_pg(), object_delete(), and object_fill_children(). They each
+ * take an rc_node_t and use the functions in the object_info_t info array for
+ * the node's type.
+ */
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include "configd.h"
+#include "repcache_protocol.h"
+
+typedef struct child_info {
+ rc_node_t *ci_parent;
+ backend_tx_t *ci_tx; /* only for properties */
+ rc_node_lookup_t ci_base_nl;
+} child_info_t;
+
+typedef struct delete_ent delete_ent_t;
+typedef struct delete_stack delete_stack_t;
+typedef struct delete_info delete_info_t;
+
+typedef int delete_cb_func(delete_info_t *, const delete_ent_t *);
+
+struct delete_ent {
+ delete_cb_func *de_cb; /* callback */
+ uint32_t de_backend;
+ uint32_t de_id;
+ uint32_t de_gen; /* only for property groups */
+};
+
+struct delete_stack {
+ struct delete_stack *ds_next;
+ uint32_t ds_size; /* number of elements */
+ uint32_t ds_cur; /* current offset */
+ delete_ent_t ds_buf[1]; /* actually ds_size */
+};
+#define DELETE_STACK_SIZE(x) offsetof(delete_stack_t, ds_buf[(x)])
+
+struct delete_info {
+ backend_tx_t *di_tx;
+ backend_tx_t *di_np_tx;
+ delete_stack_t *di_stack;
+ delete_stack_t *di_free;
+};
+
+typedef struct object_info {
+ uint32_t obj_type;
+ enum id_space obj_id_space;
+
+ int (*obj_fill_children)(rc_node_t *);
+ int (*obj_setup_child_info)(rc_node_t *, uint32_t, child_info_t *);
+ int (*obj_query_child)(backend_query_t *, rc_node_lookup_t *,
+ const char *);
+ int (*obj_insert_child)(backend_tx_t *, rc_node_lookup_t *,
+ const char *);
+ int (*obj_insert_pg_child)(backend_tx_t *, rc_node_lookup_t *,
+ const char *, const char *, uint32_t, uint32_t);
+ int (*obj_delete_start)(rc_node_t *, delete_info_t *);
+} object_info_t;
+
+#define NUM_NEEDED 50
+
+static int
+delete_stack_push(delete_info_t *dip, uint32_t be, delete_cb_func *cb,
+ uint32_t id, uint32_t gen)
+{
+ delete_stack_t *cur = dip->di_stack;
+ delete_ent_t *ent;
+
+ if (cur == NULL || cur->ds_cur == cur->ds_size) {
+ delete_stack_t *new = dip->di_free;
+ dip->di_free = NULL;
+ if (new == NULL) {
+ new = uu_zalloc(DELETE_STACK_SIZE(NUM_NEEDED));
+ if (new == NULL)
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ new->ds_size = NUM_NEEDED;
+ }
+ new->ds_cur = 0;
+ new->ds_next = dip->di_stack;
+ dip->di_stack = new;
+ cur = new;
+ }
+ assert(cur->ds_cur < cur->ds_size);
+ ent = &cur->ds_buf[cur->ds_cur++];
+
+ ent->de_backend = be;
+ ent->de_cb = cb;
+ ent->de_id = id;
+ ent->de_gen = gen;
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+delete_stack_pop(delete_info_t *dip, delete_ent_t *out)
+{
+ delete_stack_t *cur = dip->di_stack;
+ delete_ent_t *ent;
+
+ if (cur == NULL)
+ return (NULL);
+ assert(cur->ds_cur > 0 && cur->ds_cur <= cur->ds_size);
+ ent = &cur->ds_buf[--cur->ds_cur];
+ if (cur->ds_cur == 0) {
+ dip->di_stack = cur->ds_next;
+ cur->ds_next = NULL;
+
+ if (dip->di_free != NULL)
+ uu_free(dip->di_free);
+ dip->di_free = cur;
+ }
+ if (ent == NULL)
+ return (0);
+
+ *out = *ent;
+ return (1);
+}
+
+static void
+delete_stack_cleanup(delete_info_t *dip)
+{
+ delete_stack_t *cur;
+ while ((cur = dip->di_stack) != NULL) {
+ dip->di_stack = cur->ds_next;
+
+ uu_free(cur);
+ }
+
+ if ((cur = dip->di_free) != NULL) {
+ assert(cur->ds_next == NULL); /* should only be one */
+ uu_free(cur);
+ dip->di_free = NULL;
+ }
+}
+
+struct delete_cb_info {
+ delete_info_t *dci_dip;
+ uint32_t dci_be;
+ delete_cb_func *dci_cb;
+ int dci_result;
+};
+
+/*ARGSUSED*/
+static int
+push_delete_callback(void *data, int columns, char **vals, char **names)
+{
+ struct delete_cb_info *info = data;
+
+ const char *id_str = *vals++;
+ const char *gen_str = *vals++;
+
+ uint32_t id;
+ uint32_t gen;
+
+ assert(columns == 2);
+
+ if (uu_strtouint(id_str, &id, sizeof (id), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+ if (uu_strtouint(gen_str, &gen, sizeof (gen), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ info->dci_result = delete_stack_push(info->dci_dip, info->dci_be,
+ info->dci_cb, id, gen);
+
+ if (info->dci_result != REP_PROTOCOL_SUCCESS)
+ return (BACKEND_CALLBACK_ABORT);
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+static int
+value_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ uint32_t be = ent->de_backend;
+ int r;
+
+ backend_query_t *q;
+
+ backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
+ dip->di_np_tx;
+
+ q = backend_query_alloc();
+
+ backend_query_add(q,
+ "SELECT 1 FROM prop_lnk_tbl WHERE (lnk_val_id = %d); "
+ "DELETE FROM value_tbl WHERE (value_id = %d); ",
+ ent->de_id, ent->de_id);
+ r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
+ backend_query_free(q);
+ if (r == REP_PROTOCOL_DONE)
+ return (REP_PROTOCOL_SUCCESS); /* still in use */
+ return (r);
+}
+
+static int
+pg_lnk_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ struct delete_cb_info info;
+ uint32_t be = ent->de_backend;
+ int r;
+
+ backend_query_t *q;
+
+ backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
+ dip->di_np_tx;
+
+ /*
+ * For non-persistent backends, we could only have one parent, and
+ * he's already been deleted.
+ *
+ * For normal backends, we need to check to see if we're in
+ * a snapshot or are the active generation for the property
+ * group. If we are, there's nothing to be done.
+ */
+ if (be == BACKEND_TYPE_NORMAL) {
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT 1 "
+ "FROM pg_tbl "
+ "WHERE (pg_id = %d AND pg_gen_id = %d); "
+ "SELECT 1 "
+ "FROM snaplevel_lnk_tbl "
+ "WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d);",
+ ent->de_id, ent->de_gen,
+ ent->de_id, ent->de_gen);
+ r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE)
+ return (REP_PROTOCOL_SUCCESS); /* still in use */
+ }
+
+ info.dci_dip = dip;
+ info.dci_be = be;
+ info.dci_cb = &value_delete;
+ info.dci_result = REP_PROTOCOL_SUCCESS;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT DISTINCT lnk_val_id, 0 FROM prop_lnk_tbl "
+ "WHERE "
+ " (lnk_pg_id = %d AND lnk_gen_id = %d AND lnk_val_id NOTNULL); "
+ "DELETE FROM prop_lnk_tbl "
+ "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
+ ent->de_id, ent->de_gen, ent->de_id, ent->de_gen);
+
+ r = backend_tx_run(tx, q, push_delete_callback, &info);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE) {
+ assert(info.dci_result != REP_PROTOCOL_SUCCESS);
+ return (info.dci_result);
+ }
+ return (r);
+}
+
+static int
+propertygrp_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ uint32_t be = ent->de_backend;
+ backend_query_t *q;
+ uint32_t gen;
+
+ int r;
+
+ backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
+ dip->di_np_tx;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT pg_gen_id FROM pg_tbl WHERE pg_id = %d; "
+ "DELETE FROM pg_tbl WHERE pg_id = %d",
+ ent->de_id, ent->de_id);
+ r = backend_tx_run_single_int(tx, q, &gen);
+ backend_query_free(q);
+
+ if (r != REP_PROTOCOL_SUCCESS)
+ return (r);
+
+ return (delete_stack_push(dip, be, &pg_lnk_tbl_delete,
+ ent->de_id, gen));
+}
+
+static int
+snaplevel_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ uint32_t be = ent->de_backend;
+ backend_query_t *q;
+ struct delete_cb_info info;
+
+ int r;
+
+ backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
+ dip->di_np_tx;
+
+ info.dci_dip = dip;
+ info.dci_be = be;
+ info.dci_cb = &pg_lnk_tbl_delete;
+ info.dci_result = REP_PROTOCOL_SUCCESS;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT snaplvl_pg_id, snaplvl_gen_id "
+ " FROM snaplevel_lnk_tbl "
+ " WHERE snaplvl_level_id = %d; "
+ "DELETE FROM snaplevel_lnk_tbl WHERE snaplvl_level_id = %d",
+ ent->de_id, ent->de_id);
+ r = backend_tx_run(tx, q, push_delete_callback, &info);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE) {
+ assert(info.dci_result != REP_PROTOCOL_SUCCESS);
+ return (info.dci_result);
+ }
+ return (r);
+}
+
+static int
+snaplevel_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ uint32_t be = ent->de_backend;
+ backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
+ dip->di_np_tx;
+
+ struct delete_cb_info info;
+ backend_query_t *q;
+ int r;
+
+ assert(be == BACKEND_TYPE_NORMAL);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT 1 FROM snapshot_lnk_tbl WHERE lnk_snap_id = %d",
+ ent->de_id);
+ r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE)
+ return (REP_PROTOCOL_SUCCESS); /* still in use */
+
+ info.dci_dip = dip;
+ info.dci_be = be;
+ info.dci_cb = &snaplevel_lnk_delete;
+ info.dci_result = REP_PROTOCOL_SUCCESS;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT snap_level_id, 0 FROM snaplevel_tbl WHERE snap_id = %d;"
+ "DELETE FROM snaplevel_tbl WHERE snap_id = %d",
+ ent->de_id, ent->de_id);
+ r = backend_tx_run(tx, q, push_delete_callback, &info);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE) {
+ assert(info.dci_result != REP_PROTOCOL_SUCCESS);
+ return (info.dci_result);
+ }
+ return (r);
+}
+
+static int
+snapshot_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ uint32_t be = ent->de_backend;
+ backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
+ dip->di_np_tx;
+
+ backend_query_t *q;
+ uint32_t snapid;
+ int r;
+
+ assert(be == BACKEND_TYPE_NORMAL);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
+ "DELETE FROM snapshot_lnk_tbl WHERE lnk_id = %d",
+ ent->de_id, ent->de_id);
+ r = backend_tx_run_single_int(tx, q, &snapid);
+ backend_query_free(q);
+
+ if (r != REP_PROTOCOL_SUCCESS)
+ return (r);
+
+ return (delete_stack_push(dip, be, &snaplevel_tbl_delete, snapid, 0));
+}
+
+static int
+pgparent_delete_add_pgs(delete_info_t *dip, uint32_t parent_id)
+{
+ struct delete_cb_info info;
+ backend_query_t *q;
+ int r;
+
+ info.dci_dip = dip;
+ info.dci_be = BACKEND_TYPE_NORMAL;
+ info.dci_cb = &propertygrp_delete;
+ info.dci_result = REP_PROTOCOL_SUCCESS;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT pg_id, 0 FROM pg_tbl WHERE pg_parent_id = %d",
+ parent_id);
+
+ r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
+
+ if (r == REP_PROTOCOL_DONE) {
+ assert(info.dci_result != REP_PROTOCOL_SUCCESS);
+ backend_query_free(q);
+ return (info.dci_result);
+ }
+ if (r != REP_PROTOCOL_SUCCESS) {
+ backend_query_free(q);
+ return (r);
+ }
+
+ if (dip->di_np_tx != NULL) {
+ info.dci_be = BACKEND_TYPE_NONPERSIST;
+
+ r = backend_tx_run(dip->di_np_tx, q, push_delete_callback,
+ &info);
+
+ if (r == REP_PROTOCOL_DONE) {
+ assert(info.dci_result != REP_PROTOCOL_SUCCESS);
+ backend_query_free(q);
+ return (info.dci_result);
+ }
+ if (r != REP_PROTOCOL_SUCCESS) {
+ backend_query_free(q);
+ return (r);
+ }
+ }
+ backend_query_free(q);
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+service_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ int r;
+
+ r = backend_tx_run_update_changed(dip->di_tx,
+ "DELETE FROM service_tbl WHERE svc_id = %d", ent->de_id);
+ if (r != REP_PROTOCOL_SUCCESS)
+ return (r);
+
+ return (pgparent_delete_add_pgs(dip, ent->de_id));
+}
+
+static int
+instance_delete(delete_info_t *dip, const delete_ent_t *ent)
+{
+ struct delete_cb_info info;
+ int r;
+ backend_query_t *q;
+
+ r = backend_tx_run_update_changed(dip->di_tx,
+ "DELETE FROM instance_tbl WHERE instance_id = %d", ent->de_id);
+ if (r != REP_PROTOCOL_SUCCESS)
+ return (r);
+
+ r = pgparent_delete_add_pgs(dip, ent->de_id);
+ if (r != REP_PROTOCOL_SUCCESS)
+ return (r);
+
+ info.dci_dip = dip;
+ info.dci_be = BACKEND_TYPE_NORMAL;
+ info.dci_cb = &snapshot_lnk_delete;
+ info.dci_result = REP_PROTOCOL_SUCCESS;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT lnk_id, 0 FROM snapshot_lnk_tbl WHERE lnk_inst_id = %d",
+ ent->de_id);
+ r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE) {
+ assert(info.dci_result != REP_PROTOCOL_SUCCESS);
+ return (info.dci_result);
+ }
+ return (r);
+}
+
+/*ARGSUSED*/
+static int
+fill_child_callback(void *data, int columns, char **vals, char **names)
+{
+ child_info_t *cp = data;
+ rc_node_t *np;
+ uint32_t main_id;
+ const char *name;
+ const char *cur;
+ rc_node_lookup_t *lp = &cp->ci_base_nl;
+
+ assert(columns == 2);
+
+ name = *vals++;
+ columns--;
+
+ cur = *vals++;
+ columns--;
+ if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ lp->rl_main_id = main_id;
+
+ if ((np = rc_node_alloc()) == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+
+ np = rc_node_setup(np, lp, name, cp->ci_parent);
+ rc_node_rele(np);
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static int
+fill_snapshot_callback(void *data, int columns, char **vals, char **names)
+{
+ child_info_t *cp = data;
+ rc_node_t *np;
+ uint32_t main_id;
+ uint32_t snap_id;
+ const char *name;
+ const char *cur;
+ const char *snap;
+ rc_node_lookup_t *lp = &cp->ci_base_nl;
+
+ assert(columns == 3);
+
+ name = *vals++;
+ columns--;
+
+ cur = *vals++;
+ columns--;
+ snap = *vals++;
+ columns--;
+ if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1 ||
+ uu_strtouint(snap, &snap_id, sizeof (snap_id), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ lp->rl_main_id = main_id;
+
+ if ((np = rc_node_alloc()) == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+
+ np = rc_node_setup_snapshot(np, lp, name, snap_id, cp->ci_parent);
+ rc_node_rele(np);
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static int
+fill_pg_callback(void *data, int columns, char **vals, char **names)
+{
+ child_info_t *cip = data;
+ const char *name;
+ const char *type;
+ const char *cur;
+ uint32_t main_id;
+ uint32_t flags;
+ uint32_t gen_id;
+
+ rc_node_lookup_t *lp = &cip->ci_base_nl;
+ rc_node_t *newnode, *pg;
+
+ assert(columns == 5);
+
+ name = *vals++; /* pg_name */
+ columns--;
+
+ cur = *vals++; /* pg_id */
+ columns--;
+ if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ lp->rl_main_id = main_id;
+
+ cur = *vals++; /* pg_gen_id */
+ columns--;
+ if (uu_strtouint(cur, &gen_id, sizeof (gen_id), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ type = *vals++; /* pg_type */
+ columns--;
+
+ cur = *vals++; /* pg_flags */
+ columns--;
+ if (uu_strtouint(cur, &flags, sizeof (flags), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ if ((newnode = rc_node_alloc()) == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+
+ pg = rc_node_setup_pg(newnode, lp, name, type, flags, gen_id,
+ cip->ci_parent);
+ if (pg == NULL) {
+ rc_node_destroy(newnode);
+ return (BACKEND_CALLBACK_ABORT);
+ }
+
+ rc_node_rele(pg);
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+struct property_value_info {
+ char *pvi_base;
+ size_t pvi_pos;
+ size_t pvi_size;
+ size_t pvi_count;
+};
+
+/*ARGSUSED*/
+static int
+property_value_size_cb(void *data, int columns, char **vals, char **names)
+{
+ struct property_value_info *info = data;
+ assert(columns == 1);
+
+ info->pvi_size += strlen(vals[0]) + 1; /* count the '\0' */
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static int
+property_value_cb(void *data, int columns, char **vals, char **names)
+{
+ struct property_value_info *info = data;
+ size_t pos, left, len;
+
+ assert(columns == 1);
+ pos = info->pvi_pos;
+ left = info->pvi_size - pos;
+
+ pos = info->pvi_pos;
+ left = info->pvi_size - pos;
+
+ if ((len = strlcpy(&info->pvi_base[pos], vals[0], left)) >= left) {
+ /*
+ * since we preallocated, above, this shouldn't happen
+ */
+ backend_panic("unexpected database change");
+ }
+
+ len += 1; /* count the '\0' */
+
+ info->pvi_pos += len;
+ info->pvi_count++;
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*ARGSUSED*/
+void
+object_free_values(const char *vals, uint32_t type, size_t count, size_t size)
+{
+ if (vals != NULL)
+ uu_free((void *)vals);
+}
+
+/*ARGSUSED*/
+static int
+fill_property_callback(void *data, int columns, char **vals, char **names)
+{
+ child_info_t *cp = data;
+ backend_tx_t *tx = cp->ci_tx;
+ uint32_t main_id;
+ const char *name;
+ const char *cur;
+ rep_protocol_value_type_t type;
+ rc_node_lookup_t *lp = &cp->ci_base_nl;
+ struct property_value_info info;
+ int rc;
+
+ assert(columns == 4);
+ assert(tx != NULL);
+
+ info.pvi_base = NULL;
+ info.pvi_pos = 0;
+ info.pvi_size = 0;
+ info.pvi_count = 0;
+
+ name = *vals++;
+
+ cur = *vals++;
+ if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
+ backend_panic("invalid integer in database");
+
+ cur = *vals++;
+ assert(('a' <= cur[0] && 'z' >= cur[0]) ||
+ ('A' <= cur[0] && 'Z' >= cur[0]) &&
+ (cur[1] == 0 || ('a' <= cur[1] && 'z' >= cur[1]) ||
+ ('A' <= cur[1] && 'Z' >= cur[1])));
+ type = cur[0] | (cur[1] << 8);
+
+ lp->rl_main_id = main_id;
+
+ /*
+ * fill in the values, if any
+ */
+ if ((cur = *vals++) != NULL) {
+ rep_protocol_responseid_t r;
+ backend_query_t *q = backend_query_alloc();
+
+ backend_query_add(q,
+ "SELECT value_value FROM value_tbl "
+ "WHERE (value_id = '%q')", cur);
+
+ switch (r = backend_tx_run(tx, q, property_value_size_cb,
+ &info)) {
+ case REP_PROTOCOL_SUCCESS:
+ break;
+
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ backend_query_free(q);
+ return (BACKEND_CALLBACK_ABORT);
+
+ case REP_PROTOCOL_DONE:
+ default:
+ backend_panic("backend_tx_run() returned %d", r);
+ }
+ if (info.pvi_size > 0) {
+ info.pvi_base = uu_zalloc(info.pvi_size);
+ if (info.pvi_base == NULL) {
+ backend_query_free(q);
+ return (BACKEND_CALLBACK_ABORT);
+ }
+ switch (r = backend_tx_run(tx, q, property_value_cb,
+ &info)) {
+ case REP_PROTOCOL_SUCCESS:
+ break;
+
+ case REP_PROTOCOL_FAIL_NO_RESOURCES:
+ uu_free(info.pvi_base);
+ backend_query_free(q);
+ return (BACKEND_CALLBACK_ABORT);
+
+ case REP_PROTOCOL_DONE:
+ default:
+ backend_panic("backend_tx_run() returned %d",
+ r);
+ }
+ }
+ backend_query_free(q);
+ }
+
+ rc = rc_node_create_property(cp->ci_parent, lp, name, type,
+ info.pvi_base, info.pvi_count, info.pvi_size);
+ if (rc != REP_PROTOCOL_SUCCESS) {
+ assert(rc == REP_PROTOCOL_FAIL_NO_RESOURCES);
+ return (BACKEND_CALLBACK_ABORT);
+ }
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*
+ * The *_setup_child_info() functions fill in a child_info_t structure with the
+ * information for the children of np with type type.
+ *
+ * They fail with
+ * _TYPE_MISMATCH - object cannot have children of type type
+ */
+
+static int
+scope_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
+{
+ if (type != REP_PROTOCOL_ENTITY_SERVICE)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ bzero(cip, sizeof (*cip));
+ cip->ci_parent = np;
+ cip->ci_base_nl.rl_type = type;
+ cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+service_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
+{
+ switch (type) {
+ case REP_PROTOCOL_ENTITY_INSTANCE:
+ case REP_PROTOCOL_ENTITY_PROPERTYGRP:
+ break;
+ default:
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+ }
+
+ bzero(cip, sizeof (*cip));
+ cip->ci_parent = np;
+ cip->ci_base_nl.rl_type = type;
+ cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
+ cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_main_id;
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+instance_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
+{
+ switch (type) {
+ case REP_PROTOCOL_ENTITY_PROPERTYGRP:
+ case REP_PROTOCOL_ENTITY_SNAPSHOT:
+ break;
+ default:
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+ }
+
+ bzero(cip, sizeof (*cip));
+ cip->ci_parent = np;
+ cip->ci_base_nl.rl_type = type;
+ cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
+ cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
+ cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_main_id;
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+snaplevel_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
+{
+ if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ bzero(cip, sizeof (*cip));
+ cip->ci_parent = np;
+ cip->ci_base_nl.rl_type = type;
+ cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
+ cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
+ cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
+ cip->ci_base_nl.rl_ids[ID_NAME] = np->rn_id.rl_ids[ID_NAME];
+ cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = np->rn_id.rl_ids[ID_SNAPSHOT];
+ cip->ci_base_nl.rl_ids[ID_LEVEL] = np->rn_id.rl_main_id;
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+propertygrp_setup_child_info(rc_node_t *pg, uint32_t type, child_info_t *cip)
+{
+ if (type != REP_PROTOCOL_ENTITY_PROPERTY)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ bzero(cip, sizeof (*cip));
+ cip->ci_parent = pg;
+ cip->ci_base_nl.rl_type = type;
+ cip->ci_base_nl.rl_backend = pg->rn_id.rl_backend;
+ cip->ci_base_nl.rl_ids[ID_SERVICE] = pg->rn_id.rl_ids[ID_SERVICE];
+ cip->ci_base_nl.rl_ids[ID_INSTANCE] = pg->rn_id.rl_ids[ID_INSTANCE];
+ cip->ci_base_nl.rl_ids[ID_PG] = pg->rn_id.rl_main_id;
+ cip->ci_base_nl.rl_ids[ID_GEN] = pg->rn_gen_id;
+ cip->ci_base_nl.rl_ids[ID_NAME] = pg->rn_id.rl_ids[ID_NAME];
+ cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = pg->rn_id.rl_ids[ID_SNAPSHOT];
+ cip->ci_base_nl.rl_ids[ID_LEVEL] = pg->rn_id.rl_ids[ID_LEVEL];
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * The *_fill_children() functions populate the children of the given rc_node_t
+ * by querying the database and calling rc_node_setup_*() functions (usually
+ * via a fill_*_callback()).
+ *
+ * They fail with
+ * _NO_RESOURCES
+ */
+
+/*
+ * Returns
+ * _NO_RESOURCES
+ * _SUCCESS
+ */
+static int
+scope_fill_children(rc_node_t *np)
+{
+ backend_query_t *q;
+ child_info_t ci;
+ int res;
+
+ (void) scope_setup_child_info(np, REP_PROTOCOL_ENTITY_SERVICE, &ci);
+
+ q = backend_query_alloc();
+ backend_query_append(q, "SELECT svc_name, svc_id FROM service_tbl");
+ res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
+ backend_query_free(q);
+
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ return (res);
+}
+
+/*
+ * Returns
+ * _NO_RESOURCES
+ * _SUCCESS
+ */
+static int
+service_fill_children(rc_node_t *np)
+{
+ backend_query_t *q;
+ child_info_t ci;
+ int res;
+
+ assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
+
+ (void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_INSTANCE, &ci);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT instance_name, instance_id FROM instance_tbl"
+ " WHERE (instance_svc = %d)",
+ np->rn_id.rl_main_id);
+ res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
+ backend_query_free(q);
+
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ if (res != REP_PROTOCOL_SUCCESS)
+ return (res);
+
+ (void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
+ &ci);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
+ " WHERE (pg_parent_id = %d)",
+ np->rn_id.rl_main_id);
+
+ ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
+ res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
+ if (res == REP_PROTOCOL_SUCCESS) {
+ ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
+ res = backend_run(BACKEND_TYPE_NONPERSIST, q,
+ fill_pg_callback, &ci);
+ /* nonpersistant database may not exist */
+ if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
+ res = REP_PROTOCOL_SUCCESS;
+ }
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+
+ return (res);
+}
+
+/*
+ * Returns
+ * _NO_RESOURCES
+ * _SUCCESS
+ */
+static int
+instance_fill_children(rc_node_t *np)
+{
+ backend_query_t *q;
+ child_info_t ci;
+ int res;
+
+ assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
+
+ /* Get child property groups */
+ (void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
+ &ci);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
+ " WHERE (pg_parent_id = %d)",
+ np->rn_id.rl_main_id);
+ ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
+ res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
+ if (res == REP_PROTOCOL_SUCCESS) {
+ ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
+ res = backend_run(BACKEND_TYPE_NONPERSIST, q,
+ fill_pg_callback, &ci);
+ /* nonpersistant database may not exist */
+ if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
+ res = REP_PROTOCOL_SUCCESS;
+ }
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+
+ if (res != REP_PROTOCOL_SUCCESS)
+ return (res);
+
+ /* Get child snapshots */
+ (void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_SNAPSHOT,
+ &ci);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT lnk_snap_name, lnk_id, lnk_snap_id FROM snapshot_lnk_tbl"
+ " WHERE (lnk_inst_id = %d)",
+ np->rn_id.rl_main_id);
+ res = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_callback, &ci);
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+
+ return (res);
+}
+
+/*
+ * Returns
+ * _NO_RESOURCES
+ * _SUCCESS
+ */
+static int
+snapshot_fill_children(rc_node_t *np)
+{
+ rc_node_t *nnp;
+ rc_snapshot_t *sp, *oldsp;
+ rc_snaplevel_t *lvl;
+ rc_node_lookup_t nl;
+ int r;
+
+ /* Get the rc_snapshot_t (& its rc_snaplevel_t's). */
+ (void) pthread_mutex_lock(&np->rn_lock);
+ sp = np->rn_snapshot;
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ if (sp == NULL) {
+ r = rc_snapshot_get(np->rn_snapshot_id, &sp);
+ if (r != REP_PROTOCOL_SUCCESS) {
+ assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
+ return (r);
+ }
+ (void) pthread_mutex_lock(&np->rn_lock);
+ oldsp = np->rn_snapshot;
+ assert(oldsp == NULL || oldsp == sp);
+ np->rn_snapshot = sp;
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ if (oldsp != NULL)
+ rc_snapshot_rele(oldsp);
+ }
+
+ bzero(&nl, sizeof (nl));
+ nl.rl_type = REP_PROTOCOL_ENTITY_SNAPLEVEL;
+ nl.rl_backend = np->rn_id.rl_backend;
+ nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
+ nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
+ nl.rl_ids[ID_NAME] = np->rn_id.rl_main_id;
+ nl.rl_ids[ID_SNAPSHOT] = np->rn_snapshot_id;
+
+ /* Create rc_node_t's for the snapshot's rc_snaplevel_t's. */
+ for (lvl = sp->rs_levels; lvl != NULL; lvl = lvl->rsl_next) {
+ nnp = rc_node_alloc();
+ assert(nnp != NULL);
+ nl.rl_main_id = lvl->rsl_level_id;
+ nnp = rc_node_setup_snaplevel(nnp, &nl, lvl, np);
+ rc_node_rele(nnp);
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Returns
+ * _NO_RESOURCES
+ * _SUCCESS
+ */
+static int
+snaplevel_fill_children(rc_node_t *np)
+{
+ rc_snaplevel_t *lvl = np->rn_snaplevel;
+ child_info_t ci;
+ int res;
+ backend_query_t *q;
+
+ (void) snaplevel_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
+ &ci);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT snaplvl_pg_name, snaplvl_pg_id, snaplvl_gen_id, "
+ " snaplvl_pg_type, snaplvl_pg_flags "
+ " FROM snaplevel_lnk_tbl "
+ " WHERE (snaplvl_level_id = %d)",
+ lvl->rsl_level_id);
+ res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+
+ return (res);
+}
+
+/*
+ * Returns
+ * _NO_RESOURCES
+ * _SUCCESS
+ */
+static int
+propertygrp_fill_children(rc_node_t *np)
+{
+ backend_query_t *q;
+ child_info_t ci;
+ int res;
+ backend_tx_t *tx;
+
+ backend_type_t backend = np->rn_id.rl_backend;
+
+ (void) propertygrp_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTY,
+ &ci);
+
+ res = backend_tx_begin_ro(backend, &tx);
+ if (res != REP_PROTOCOL_SUCCESS) {
+ /*
+ * If the backend didn't exist, we wouldn't have got this
+ * property group.
+ */
+ assert(res != REP_PROTOCOL_FAIL_BACKEND_ACCESS);
+ return (res);
+ }
+
+ ci.ci_tx = tx;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT lnk_prop_name, lnk_prop_id, lnk_prop_type, lnk_val_id "
+ "FROM prop_lnk_tbl "
+ "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
+ np->rn_id.rl_main_id, np->rn_gen_id);
+ res = backend_tx_run(tx, q, fill_property_callback, &ci);
+ if (res == REP_PROTOCOL_DONE)
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+ backend_tx_end_ro(tx);
+
+ return (res);
+}
+
+/*
+ * Fails with
+ * _TYPE_MISMATCH - lp is not for a service
+ * _INVALID_TYPE - lp has invalid type
+ * _BAD_REQUEST - name is invalid
+ */
+static int
+scope_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
+{
+ uint32_t type = lp->rl_type;
+ int rc;
+
+ if (type != REP_PROTOCOL_ENTITY_SERVICE)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
+ return (rc);
+
+ backend_query_add(q,
+ "SELECT svc_id FROM service_tbl "
+ "WHERE svc_name = '%q'",
+ name);
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Fails with
+ * _NO_RESOURCES - out of memory
+ */
+static int
+scope_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
+{
+ return (backend_tx_run_update(tx,
+ "INSERT INTO service_tbl (svc_id, svc_name) "
+ "VALUES (%d, '%q')",
+ lp->rl_main_id, name));
+}
+
+/*
+ * Fails with
+ * _TYPE_MISMATCH - lp is not for an instance or property group
+ * _INVALID_TYPE - lp has invalid type
+ * _BAD_REQUEST - name is invalid
+ */
+static int
+service_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
+{
+ uint32_t type = lp->rl_type;
+ int rc;
+
+ if (type != REP_PROTOCOL_ENTITY_INSTANCE &&
+ type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
+ return (rc);
+
+ switch (type) {
+ case REP_PROTOCOL_ENTITY_INSTANCE:
+ backend_query_add(q,
+ "SELECT instance_id FROM instance_tbl "
+ "WHERE instance_name = '%q' AND instance_svc = %d",
+ name, lp->rl_ids[ID_SERVICE]);
+ break;
+ case REP_PROTOCOL_ENTITY_PROPERTYGRP:
+ backend_query_add(q,
+ "SELECT pg_id FROM pg_tbl "
+ " WHERE pg_name = '%q' AND pg_parent_id = %d",
+ name, lp->rl_ids[ID_SERVICE]);
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Fails with
+ * _NO_RESOURCES - out of memory
+ */
+static int
+service_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
+{
+ return (backend_tx_run_update(tx,
+ "INSERT INTO instance_tbl "
+ " (instance_id, instance_name, instance_svc) "
+ "VALUES (%d, '%q', %d)",
+ lp->rl_main_id, name, lp->rl_ids[ID_SERVICE]));
+}
+
+/*
+ * Fails with
+ * _NO_RESOURCES - out of memory
+ */
+static int
+instance_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
+{
+ return (backend_tx_run_update(tx,
+ "INSERT INTO snapshot_lnk_tbl "
+ " (lnk_id, lnk_inst_id, lnk_snap_name, lnk_snap_id) "
+ "VALUES (%d, %d, '%q', 0)",
+ lp->rl_main_id, lp->rl_ids[ID_INSTANCE], name));
+}
+
+/*
+ * Fails with
+ * _TYPE_MISMATCH - lp is not for a property group or snapshot
+ * _INVALID_TYPE - lp has invalid type
+ * _BAD_REQUEST - name is invalid
+ */
+static int
+instance_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
+{
+ uint32_t type = lp->rl_type;
+ int rc;
+
+ if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
+ type != REP_PROTOCOL_ENTITY_SNAPSHOT)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
+ return (rc);
+
+ switch (type) {
+ case REP_PROTOCOL_ENTITY_PROPERTYGRP:
+ backend_query_add(q,
+ "SELECT pg_id FROM pg_tbl "
+ " WHERE pg_name = '%q' AND pg_parent_id = %d",
+ name, lp->rl_ids[ID_INSTANCE]);
+ break;
+ case REP_PROTOCOL_ENTITY_SNAPSHOT:
+ backend_query_add(q,
+ "SELECT lnk_id FROM snapshot_lnk_tbl "
+ " WHERE lnk_snap_name = '%q' AND lnk_inst_id = %d",
+ name, lp->rl_ids[ID_INSTANCE]);
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+static int
+generic_insert_pg_child(backend_tx_t *tx, rc_node_lookup_t *lp,
+ const char *name, const char *pgtype, uint32_t flags, uint32_t gen)
+{
+ int parent_id = (lp->rl_ids[ID_INSTANCE] != 0)?
+ lp->rl_ids[ID_INSTANCE] : lp->rl_ids[ID_SERVICE];
+ return (backend_tx_run_update(tx,
+ "INSERT INTO pg_tbl "
+ " (pg_id, pg_name, pg_parent_id, pg_type, pg_flags, pg_gen_id) "
+ "VALUES (%d, '%q', %d, '%q', %d, %d)",
+ lp->rl_main_id, name, parent_id, pgtype, flags, gen));
+}
+
+static int
+service_delete_start(rc_node_t *np, delete_info_t *dip)
+{
+ int r;
+ backend_query_t *q = backend_query_alloc();
+
+ /*
+ * Check for child instances, and refuse to delete if they exist.
+ */
+ backend_query_add(q,
+ "SELECT 1 FROM instance_tbl WHERE instance_svc = %d",
+ np->rn_id.rl_main_id);
+
+ r = backend_tx_run(dip->di_tx, q, backend_fail_if_seen, NULL);
+ backend_query_free(q);
+
+ if (r == REP_PROTOCOL_DONE)
+ return (REP_PROTOCOL_FAIL_EXISTS); /* instances exist */
+
+ return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &service_delete,
+ np->rn_id.rl_main_id, 0));
+}
+
+static int
+instance_delete_start(rc_node_t *np, delete_info_t *dip)
+{
+ return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &instance_delete,
+ np->rn_id.rl_main_id, 0));
+}
+
+static int
+snapshot_delete_start(rc_node_t *np, delete_info_t *dip)
+{
+ return (delete_stack_push(dip, BACKEND_TYPE_NORMAL,
+ &snapshot_lnk_delete, np->rn_id.rl_main_id, 0));
+}
+
+static int
+propertygrp_delete_start(rc_node_t *np, delete_info_t *dip)
+{
+ return (delete_stack_push(dip, np->rn_id.rl_backend,
+ &propertygrp_delete, np->rn_id.rl_main_id, 0));
+}
+
+static object_info_t info[] = {
+ {REP_PROTOCOL_ENTITY_NONE},
+ {REP_PROTOCOL_ENTITY_SCOPE,
+ BACKEND_ID_INVALID,
+ scope_fill_children,
+ scope_setup_child_info,
+ scope_query_child,
+ scope_insert_child,
+ NULL,
+ NULL,
+ },
+ {REP_PROTOCOL_ENTITY_SERVICE,
+ BACKEND_ID_SERVICE_INSTANCE,
+ service_fill_children,
+ service_setup_child_info,
+ service_query_child,
+ service_insert_child,
+ generic_insert_pg_child,
+ service_delete_start,
+ },
+ {REP_PROTOCOL_ENTITY_INSTANCE,
+ BACKEND_ID_SERVICE_INSTANCE,
+ instance_fill_children,
+ instance_setup_child_info,
+ instance_query_child,
+ instance_insert_child,
+ generic_insert_pg_child,
+ instance_delete_start,
+ },
+ {REP_PROTOCOL_ENTITY_SNAPSHOT,
+ BACKEND_ID_SNAPNAME,
+ snapshot_fill_children,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ snapshot_delete_start,
+ },
+ {REP_PROTOCOL_ENTITY_SNAPLEVEL,
+ BACKEND_ID_SNAPLEVEL,
+ snaplevel_fill_children,
+ snaplevel_setup_child_info,
+ },
+ {REP_PROTOCOL_ENTITY_PROPERTYGRP,
+ BACKEND_ID_PROPERTYGRP,
+ propertygrp_fill_children,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ propertygrp_delete_start,
+ },
+ {REP_PROTOCOL_ENTITY_PROPERTY},
+ {-1UL}
+};
+#define NUM_INFO (sizeof (info) / sizeof (*info))
+
+/*
+ * object_fill_children() populates the child list of an rc_node_t by calling
+ * the appropriate <type>_fill_children() which runs backend queries that
+ * call an appropriate fill_*_callback() which takes a row of results,
+ * decodes them, and calls an rc_node_setup*() function in rc_node.c to create
+ * a child.
+ *
+ * Fails with
+ * _NO_RESOURCES
+ */
+int
+object_fill_children(rc_node_t *pp)
+{
+ uint32_t type = pp->rn_id.rl_type;
+ assert(type > 0 && type < NUM_INFO);
+
+ return ((*info[type].obj_fill_children)(pp));
+}
+
+int
+object_delete(rc_node_t *pp)
+{
+ int rc;
+
+ delete_info_t dip;
+ delete_ent_t de;
+
+ uint32_t type = pp->rn_id.rl_type;
+ assert(type > 0 && type < NUM_INFO);
+
+ if (info[type].obj_delete_start == NULL)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ (void) memset(&dip, '\0', sizeof (dip));
+ rc = backend_tx_begin(BACKEND_TYPE_NORMAL, &dip.di_tx);
+ if (rc != REP_PROTOCOL_SUCCESS)
+ return (rc);
+
+ rc = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &dip.di_np_tx);
+ if (rc == REP_PROTOCOL_FAIL_BACKEND_ACCESS ||
+ rc == REP_PROTOCOL_FAIL_BACKEND_READONLY)
+ dip.di_np_tx = NULL;
+ else if (rc != REP_PROTOCOL_SUCCESS) {
+ backend_tx_rollback(dip.di_tx);
+ return (rc);
+ }
+
+ if ((rc = (*info[type].obj_delete_start)(pp, &dip)) !=
+ REP_PROTOCOL_SUCCESS) {
+ goto fail;
+ }
+
+ while (delete_stack_pop(&dip, &de)) {
+ rc = (*de.de_cb)(&dip, &de);
+ if (rc != REP_PROTOCOL_SUCCESS)
+ goto fail;
+ }
+
+ rc = backend_tx_commit(dip.di_tx);
+ if (rc != REP_PROTOCOL_SUCCESS)
+ backend_tx_rollback(dip.di_np_tx);
+ else if (dip.di_np_tx)
+ (void) backend_tx_commit(dip.di_np_tx);
+
+ delete_stack_cleanup(&dip);
+
+ return (rc);
+
+fail:
+ backend_tx_rollback(dip.di_tx);
+ backend_tx_rollback(dip.di_np_tx);
+ delete_stack_cleanup(&dip);
+ return (rc);
+}
+
+int
+object_do_create(backend_tx_t *tx, child_info_t *cip, rc_node_t *pp,
+ uint32_t type, const char *name, rc_node_t **cpp)
+{
+ uint32_t ptype = pp->rn_id.rl_type;
+
+ backend_query_t *q;
+ uint32_t id;
+ rc_node_t *np = NULL;
+ int rc;
+ object_info_t *ip;
+
+ rc_node_lookup_t *lp = &cip->ci_base_nl;
+
+ assert(ptype > 0 && ptype < NUM_INFO);
+
+ ip = &info[ptype];
+
+ if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP)
+ return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
+
+ if (ip->obj_setup_child_info == NULL ||
+ ip->obj_query_child == NULL ||
+ ip->obj_insert_child == NULL)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ if ((rc = (*ip->obj_setup_child_info)(pp, type, cip)) !=
+ REP_PROTOCOL_SUCCESS)
+ return (rc);
+
+ q = backend_query_alloc();
+ if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
+ REP_PROTOCOL_SUCCESS) {
+ assert(rc == REP_PROTOCOL_FAIL_BAD_REQUEST);
+ backend_query_free(q);
+ return (rc);
+ }
+
+ rc = backend_tx_run_single_int(tx, q, &id);
+ backend_query_free(q);
+
+ if (rc == REP_PROTOCOL_SUCCESS)
+ return (REP_PROTOCOL_FAIL_EXISTS);
+ else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND)
+ return (rc);
+
+ if ((lp->rl_main_id = backend_new_id(tx,
+ info[type].obj_id_space)) == 0) {
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
+
+ if ((np = rc_node_alloc()) == NULL)
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+
+ if ((rc = (*ip->obj_insert_child)(tx, lp, name)) !=
+ REP_PROTOCOL_SUCCESS) {
+ rc_node_destroy(np);
+ return (rc);
+ }
+
+ *cpp = np;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Fails with
+ * _NOT_APPLICABLE - type is _PROPERTYGRP
+ * _BAD_REQUEST - cannot create children for this type of node
+ * name is invalid
+ * _TYPE_MISMATCH - object cannot have children of type type
+ * _NO_RESOURCES - out of memory, or could not allocate new id
+ * _BACKEND_READONLY
+ * _BACKEND_ACCESS
+ * _EXISTS - child already exists
+ */
+int
+object_create(rc_node_t *pp, uint32_t type, const char *name, rc_node_t **cpp)
+{
+ backend_tx_t *tx;
+ rc_node_t *np = NULL;
+ child_info_t ci;
+ int rc;
+
+ if ((rc = backend_tx_begin(pp->rn_id.rl_backend, &tx)) !=
+ REP_PROTOCOL_SUCCESS) {
+ return (rc);
+ }
+
+ if ((rc = object_do_create(tx, &ci, pp, type, name, &np)) !=
+ REP_PROTOCOL_SUCCESS) {
+ backend_tx_rollback(tx);
+ return (rc);
+ }
+
+ rc = backend_tx_commit(tx);
+ if (rc != REP_PROTOCOL_SUCCESS) {
+ rc_node_destroy(np);
+ return (rc);
+ }
+
+ *cpp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*ARGSUSED*/
+int
+object_create_pg(rc_node_t *pp, uint32_t type, const char *name,
+ const char *pgtype, uint32_t flags, rc_node_t **cpp)
+{
+ uint32_t ptype = pp->rn_id.rl_type;
+ backend_tx_t *tx_ro, *tx_wr;
+ backend_query_t *q;
+ uint32_t id;
+ uint32_t gen = 0;
+ rc_node_t *np = NULL;
+ int rc;
+ int rc_wr;
+ int rc_ro;
+ object_info_t *ip;
+
+ int nonpersist = (flags & SCF_PG_FLAG_NONPERSISTENT);
+
+ child_info_t ci;
+ rc_node_lookup_t *lp = &ci.ci_base_nl;
+
+ assert(ptype > 0 && ptype < NUM_INFO);
+
+ if (ptype != REP_PROTOCOL_ENTITY_SERVICE &&
+ ptype != REP_PROTOCOL_ENTITY_INSTANCE)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ ip = &info[ptype];
+
+ assert(ip->obj_setup_child_info != NULL &&
+ ip->obj_query_child != NULL &&
+ ip->obj_insert_pg_child != NULL);
+
+ if ((rc = (*ip->obj_setup_child_info)(pp, type, &ci)) !=
+ REP_PROTOCOL_SUCCESS)
+ return (rc);
+
+ q = backend_query_alloc();
+ if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
+ REP_PROTOCOL_SUCCESS) {
+ backend_query_free(q);
+ return (rc);
+ }
+
+ if (!nonpersist) {
+ lp->rl_backend = BACKEND_TYPE_NORMAL;
+ rc_wr = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx_wr);
+ rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NONPERSIST, &tx_ro);
+ } else {
+ lp->rl_backend = BACKEND_TYPE_NONPERSIST;
+ rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NORMAL, &tx_ro);
+ rc_wr = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &tx_wr);
+ }
+
+ if (rc_wr != REP_PROTOCOL_SUCCESS) {
+ rc = rc_wr;
+ goto fail;
+ }
+ if (rc_ro != REP_PROTOCOL_SUCCESS &&
+ rc_ro != REP_PROTOCOL_FAIL_BACKEND_ACCESS) {
+ rc = rc_ro;
+ goto fail;
+ }
+
+ if (tx_ro != NULL) {
+ rc = backend_tx_run_single_int(tx_ro, q, &id);
+
+ if (rc == REP_PROTOCOL_SUCCESS) {
+ backend_query_free(q);
+ rc = REP_PROTOCOL_FAIL_EXISTS;
+ goto fail;
+ } else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
+ backend_query_free(q);
+ goto fail;
+ }
+ }
+
+ rc = backend_tx_run_single_int(tx_wr, q, &id);
+ backend_query_free(q);
+
+ if (rc == REP_PROTOCOL_SUCCESS) {
+ rc = REP_PROTOCOL_FAIL_EXISTS;
+ goto fail;
+ } else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
+ goto fail;
+ }
+
+ if (tx_ro != NULL)
+ backend_tx_end_ro(tx_ro);
+ tx_ro = NULL;
+
+ if ((lp->rl_main_id = backend_new_id(tx_wr,
+ info[type].obj_id_space)) == 0) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto fail;
+ }
+
+ if ((np = rc_node_alloc()) == NULL) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto fail;
+ }
+
+ if ((rc = (*ip->obj_insert_pg_child)(tx_wr, lp, name, pgtype, flags,
+ gen)) != REP_PROTOCOL_SUCCESS) {
+ rc_node_destroy(np);
+ goto fail;
+ }
+
+ rc = backend_tx_commit(tx_wr);
+ if (rc != REP_PROTOCOL_SUCCESS) {
+ rc_node_destroy(np);
+ return (rc);
+ }
+
+ *cpp = rc_node_setup_pg(np, lp, name, pgtype, flags, gen, ci.ci_parent);
+
+ return (REP_PROTOCOL_SUCCESS);
+
+fail:
+ if (tx_ro != NULL)
+ backend_tx_end_ro(tx_ro);
+ if (tx_wr != NULL)
+ backend_tx_rollback(tx_wr);
+ return (rc);
+}
+
+/*
+ * Given a row of snaplevel number, snaplevel id, service id, service name,
+ * instance id, & instance name, create a rc_snaplevel_t & prepend it onto the
+ * rs_levels list of the rc_snapshot_t passed in as data.
+ * Returns _CONTINUE on success or _ABORT if any allocations fail.
+ */
+/*ARGSUSED*/
+static int
+fill_snapshot_cb(void *data, int columns, char **vals, char **names)
+{
+ rc_snapshot_t *sp = data;
+ rc_snaplevel_t *lvl;
+ char *num = vals[0];
+ char *id = vals[1];
+ char *service_id = vals[2];
+ char *service = vals[3];
+ char *instance_id = vals[4];
+ char *instance = vals[5];
+ assert(columns == 6);
+
+ lvl = uu_zalloc(sizeof (*lvl));
+ if (lvl == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+ lvl->rsl_parent = sp;
+ lvl->rsl_next = sp->rs_levels;
+ sp->rs_levels = lvl;
+
+ if (uu_strtouint(num, &lvl->rsl_level_num,
+ sizeof (lvl->rsl_level_num), 0, 0, 0) == -1 ||
+ uu_strtouint(id, &lvl->rsl_level_id,
+ sizeof (lvl->rsl_level_id), 0, 0, 0) == -1 ||
+ uu_strtouint(service_id, &lvl->rsl_service_id,
+ sizeof (lvl->rsl_level_num), 0, 0, 0) == -1 ||
+ (instance_id != NULL &&
+ uu_strtouint(instance_id, &lvl->rsl_instance_id,
+ sizeof (lvl->rsl_instance_id), 0, 0, 0) == -1)) {
+ backend_panic("invalid integer in database");
+ }
+
+ lvl->rsl_scope = (const char *)"localhost";
+ lvl->rsl_service = strdup(service);
+ if (lvl->rsl_service == NULL) {
+ uu_free(lvl);
+ return (BACKEND_CALLBACK_ABORT);
+ }
+ if (instance) {
+ assert(lvl->rsl_instance_id != 0);
+ lvl->rsl_instance = strdup(instance);
+ if (lvl->rsl_instance == NULL) {
+ free((void *)lvl->rsl_instance);
+ uu_free(lvl);
+ return (BACKEND_CALLBACK_ABORT);
+ }
+ } else {
+ assert(lvl->rsl_instance_id == 0);
+ }
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*
+ * Populate sp's rs_levels list from the snaplevel_tbl table.
+ * Fails with
+ * _NO_RESOURCES
+ */
+int
+object_fill_snapshot(rc_snapshot_t *sp)
+{
+ backend_query_t *q;
+ rc_snaplevel_t *sl;
+ int result;
+ int i;
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT snap_level_num, snap_level_id, "
+ " snap_level_service_id, snap_level_service, "
+ " snap_level_instance_id, snap_level_instance "
+ "FROM snaplevel_tbl "
+ "WHERE snap_id = %d "
+ "ORDER BY snap_level_id DESC",
+ sp->rs_snap_id);
+
+ result = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_cb, sp);
+ if (result == REP_PROTOCOL_DONE)
+ result = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+
+ if (result == REP_PROTOCOL_SUCCESS) {
+ i = 0;
+ for (sl = sp->rs_levels; sl != NULL; sl = sl->rsl_next) {
+ if (sl->rsl_level_num != ++i) {
+ backend_panic("snaplevels corrupt; expected "
+ "level %d, got %d", i, sl->rsl_level_num);
+ }
+ }
+ }
+ return (result);
+}
+
+/*ARGSUSED*/
+static int
+object_copy_string(void *data_arg, int columns, char **vals, char **names)
+{
+ char **data = data_arg;
+
+ assert(columns == 1);
+
+ if (*data != NULL)
+ free(*data);
+ *data = NULL;
+
+ if (vals[0] != NULL) {
+ if ((*data = strdup(vals[0])) == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+ }
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+struct snaplevel_add_info {
+ backend_query_t *sai_q;
+ uint32_t sai_level_id;
+ int sai_used; /* sai_q has been used */
+};
+
+/*ARGSUSED*/
+static int
+object_snaplevel_process_pg(void *data_arg, int columns, char **vals,
+ char **names)
+{
+ struct snaplevel_add_info *data = data_arg;
+
+ assert(columns == 5);
+
+ backend_query_add(data->sai_q,
+ "INSERT INTO snaplevel_lnk_tbl "
+ " (snaplvl_level_id, snaplvl_pg_id, snaplvl_pg_name, "
+ " snaplvl_pg_type, snaplvl_pg_flags, snaplvl_gen_id)"
+ "VALUES (%d, %s, '%q', '%q', %s, %s);",
+ data->sai_level_id, vals[0], vals[1], vals[2], vals[3], vals[4]);
+
+ data->sai_used = 1;
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static int
+object_snapshot_add_level(backend_tx_t *tx, uint32_t snap_id,
+ uint32_t snap_level_num, uint32_t svc_id, const char *svc_name,
+ uint32_t inst_id, const char *inst_name)
+{
+ struct snaplevel_add_info data;
+ backend_query_t *q;
+ int result;
+
+ assert((snap_level_num == 1 && inst_name != NULL) ||
+ snap_level_num == 2 && inst_name == NULL);
+
+ data.sai_level_id = backend_new_id(tx, BACKEND_ID_SNAPLEVEL);
+ if (data.sai_level_id == 0) {
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
+
+ result = backend_tx_run_update(tx,
+ "INSERT INTO snaplevel_tbl "
+ " (snap_id, snap_level_num, snap_level_id, "
+ " snap_level_service_id, snap_level_service, "
+ " snap_level_instance_id, snap_level_instance) "
+ "VALUES (%d, %d, %d, %d, %Q, %d, %Q);",
+ snap_id, snap_level_num, data.sai_level_id, svc_id, svc_name,
+ inst_id, inst_name);
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT pg_id, pg_name, pg_type, pg_flags, pg_gen_id FROM pg_tbl "
+ "WHERE (pg_parent_id = %d);",
+ (inst_name != NULL)? inst_id : svc_id);
+
+ data.sai_q = backend_query_alloc();
+ data.sai_used = 0;
+ result = backend_tx_run(tx, q, object_snaplevel_process_pg,
+ &data);
+ backend_query_free(q);
+
+ if (result == REP_PROTOCOL_SUCCESS && data.sai_used != 0)
+ result = backend_tx_run(tx, data.sai_q, NULL, NULL);
+ backend_query_free(data.sai_q);
+
+ return (result);
+}
+
+/*
+ * Fails with:
+ * _NO_RESOURCES - no new id or out of disk space
+ * _BACKEND_READONLY - persistent backend is read-only
+ */
+static int
+object_snapshot_do_take(uint32_t instid, const char *inst_name,
+ uint32_t svcid, const char *svc_name,
+ backend_tx_t **tx_out, uint32_t *snapid_out)
+{
+ backend_tx_t *tx;
+ backend_query_t *q;
+ int result;
+
+ char *svc_name_alloc = NULL;
+ char *inst_name_alloc = NULL;
+ uint32_t snapid;
+
+ result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
+ if (result != REP_PROTOCOL_SUCCESS)
+ return (result);
+
+ snapid = backend_new_id(tx, BACKEND_ID_SNAPSHOT);
+ if (snapid == 0) {
+ result = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto fail;
+ }
+
+ if (svc_name == NULL) {
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT svc_name FROM service_tbl "
+ "WHERE (svc_id = %d)", svcid);
+ result = backend_tx_run(tx, q, object_copy_string,
+ &svc_name_alloc);
+ backend_query_free(q);
+
+ svc_name = svc_name_alloc;
+
+ if (result == REP_PROTOCOL_DONE) {
+ result = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto fail;
+ }
+ if (result == REP_PROTOCOL_SUCCESS && svc_name == NULL)
+ backend_panic("unable to find name for svc id %d\n",
+ svcid);
+
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+ }
+
+ if (inst_name == NULL) {
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT instance_name FROM instance_tbl "
+ "WHERE (instance_id = %d)", instid);
+ result = backend_tx_run(tx, q, object_copy_string,
+ &inst_name_alloc);
+ backend_query_free(q);
+
+ inst_name = inst_name_alloc;
+
+ if (result == REP_PROTOCOL_DONE) {
+ result = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto fail;
+ }
+
+ if (result == REP_PROTOCOL_SUCCESS && inst_name == NULL)
+ backend_panic(
+ "unable to find name for instance id %d\n", instid);
+
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+ }
+
+ result = object_snapshot_add_level(tx, snapid, 1,
+ svcid, svc_name, instid, inst_name);
+
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+
+ result = object_snapshot_add_level(tx, snapid, 2,
+ svcid, svc_name, 0, NULL);
+
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+
+ *snapid_out = snapid;
+ *tx_out = tx;
+
+ free(svc_name_alloc);
+ free(inst_name_alloc);
+
+ return (REP_PROTOCOL_SUCCESS);
+
+fail:
+ backend_tx_rollback(tx);
+ free(svc_name_alloc);
+ free(inst_name_alloc);
+ return (result);
+}
+
+/*
+ * Fails with:
+ * _TYPE_MISMATCH - pp is not an instance
+ * _NO_RESOURCES - no new id or out of disk space
+ * _BACKEND_READONLY - persistent backend is read-only
+ */
+int
+object_snapshot_take_new(rc_node_t *pp,
+ const char *svc_name, const char *inst_name,
+ const char *name, rc_node_t **outp)
+{
+ rc_node_lookup_t *insti = &pp->rn_id;
+
+ uint32_t instid = insti->rl_main_id;
+ uint32_t svcid = insti->rl_ids[ID_SERVICE];
+ uint32_t snapid = 0;
+ backend_tx_t *tx = NULL;
+ child_info_t ci;
+ rc_node_t *np;
+ int result;
+
+ if (insti->rl_type != REP_PROTOCOL_ENTITY_INSTANCE)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ result = object_snapshot_do_take(instid, inst_name, svcid, svc_name,
+ &tx, &snapid);
+ if (result != REP_PROTOCOL_SUCCESS)
+ return (result);
+
+ if ((result = object_do_create(tx, &ci, pp,
+ REP_PROTOCOL_ENTITY_SNAPSHOT, name, &np)) != REP_PROTOCOL_SUCCESS) {
+ backend_tx_rollback(tx);
+ return (result);
+ }
+
+ /*
+ * link the new object to the new snapshot.
+ */
+ np->rn_snapshot_id = snapid;
+
+ result = backend_tx_run_update(tx,
+ "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
+ snapid, ci.ci_base_nl.rl_main_id);
+ if (result != REP_PROTOCOL_SUCCESS) {
+ backend_tx_rollback(tx);
+ rc_node_destroy(np);
+ return (result);
+ }
+ result = backend_tx_commit(tx);
+ if (result != REP_PROTOCOL_SUCCESS) {
+ rc_node_destroy(np);
+ return (result);
+ }
+
+ *outp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Fails with:
+ * _TYPE_MISMATCH - pp is not an instance
+ * _NO_RESOURCES - no new id or out of disk space
+ * _BACKEND_READONLY - persistent backend is read-only
+ */
+int
+object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr,
+ int takesnap)
+{
+ uint32_t svcid = snapi->rl_ids[ID_SERVICE];
+ uint32_t instid = snapi->rl_ids[ID_INSTANCE];
+ uint32_t snapid = *snapid_ptr;
+ uint32_t oldsnapid = 0;
+ backend_tx_t *tx = NULL;
+ backend_query_t *q;
+ int result;
+
+ delete_info_t dip;
+ delete_ent_t de;
+
+ if (snapi->rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
+ return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
+
+ if (takesnap) {
+ result = object_snapshot_do_take(instid, NULL,
+ svcid, NULL, &tx, &snapid);
+ if (result != REP_PROTOCOL_SUCCESS)
+ return (result);
+ } else {
+ result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
+ if (result != REP_PROTOCOL_SUCCESS)
+ return (result);
+ }
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
+ "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
+ snapi->rl_main_id, snapid, snapi->rl_main_id);
+ result = backend_tx_run_single_int(tx, q, &oldsnapid);
+ backend_query_free(q);
+
+ if (result == REP_PROTOCOL_FAIL_NOT_FOUND) {
+ backend_tx_rollback(tx);
+ backend_panic("unable to find snapshot id %d",
+ snapi->rl_main_id);
+ }
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+
+ /*
+ * Now we use the delete stack to handle the possible unreferencing
+ * of oldsnapid.
+ */
+ (void) memset(&dip, 0, sizeof (dip));
+ dip.di_tx = tx;
+ dip.di_np_tx = NULL; /* no need for non-persistant backend */
+
+ if ((result = delete_stack_push(&dip, BACKEND_TYPE_NORMAL,
+ &snaplevel_tbl_delete, oldsnapid, 0)) != REP_PROTOCOL_SUCCESS)
+ goto fail;
+
+ while (delete_stack_pop(&dip, &de)) {
+ result = (*de.de_cb)(&dip, &de);
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+ }
+
+ result = backend_tx_commit(tx);
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+
+ delete_stack_cleanup(&dip);
+ *snapid_ptr = snapid;
+ return (REP_PROTOCOL_SUCCESS);
+
+fail:
+ backend_tx_rollback(tx);
+ delete_stack_cleanup(&dip);
+ return (result);
+}