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.c276
1 files changed, 248 insertions, 28 deletions
diff --git a/usr/src/cmd/svc/configd/file_object.c b/usr/src/cmd/svc/configd/file_object.c
index 52bff4858f..b4f177f1eb 100644
--- a/usr/src/cmd/svc/configd/file_object.c
+++ b/usr/src/cmd/svc/configd/file_object.c
@@ -94,6 +94,14 @@ typedef struct object_info {
int (*obj_delete_start)(rc_node_t *, delete_info_t *);
} object_info_t;
+static void
+string_to_id(const char *str, uint32_t *output, const char *fieldname)
+{
+ if (uu_strtouint(str, output, sizeof (*output), 0, 0, 0) == -1)
+ backend_panic("invalid integer \"%s\" in field \"%s\"",
+ str, fieldname);
+}
+
#define NUM_NEEDED 50
static int
@@ -191,10 +199,8 @@ push_delete_callback(void *data, int columns, char **vals, char **names)
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");
+ string_to_id(id_str, &id, "id");
+ string_to_id(gen_str, &gen, "gen_id");
info->dci_result = delete_stack_push(info->dci_dip, info->dci_be,
info->dci_cb, id, gen);
@@ -536,8 +542,7 @@ fill_child_callback(void *data, int columns, char **vals, char **names)
cur = *vals++;
columns--;
- if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
- backend_panic("invalid integer in database");
+ string_to_id(cur, &main_id, "id");
lp->rl_main_id = main_id;
@@ -572,9 +577,9 @@ fill_snapshot_callback(void *data, int columns, char **vals, char **names)
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");
+
+ string_to_id(cur, &main_id, "lnk_id");
+ string_to_id(snap, &snap_id, "lnk_snap_id");
lp->rl_main_id = main_id;
@@ -609,23 +614,20 @@ fill_pg_callback(void *data, int columns, char **vals, char **names)
cur = *vals++; /* pg_id */
columns--;
- if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
- backend_panic("invalid integer in database");
+ string_to_id(cur, &main_id, "pg_id");
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");
+ string_to_id(cur, &gen_id, "pg_gen_id");
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");
+ string_to_id(cur, &flags, "pg_flags");
if ((newnode = rc_node_alloc()) == NULL)
return (BACKEND_CALLBACK_ABORT);
@@ -723,8 +725,7 @@ fill_property_callback(void *data, int columns, char **vals, char **names)
name = *vals++;
cur = *vals++;
- if (uu_strtouint(cur, &main_id, sizeof (main_id), 0, 0, 0) == -1)
- backend_panic("invalid integer in database");
+ string_to_id(cur, &main_id, "lnk_prop_id");
cur = *vals++;
assert(('a' <= cur[0] && 'z' >= cur[0]) ||
@@ -1764,17 +1765,12 @@ fill_snapshot_cb(void *data, int columns, char **vals, char **names)
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");
- }
+ string_to_id(num, &lvl->rsl_level_num, "snap_level_num");
+ string_to_id(id, &lvl->rsl_level_id, "snap_level_id");
+ string_to_id(service_id, &lvl->rsl_service_id, "snap_level_service_id");
+ if (instance_id != NULL)
+ string_to_id(instance_id, &lvl->rsl_instance_id,
+ "snap_level_instance_id");
lvl->rsl_scope = (const char *)"localhost";
lvl->rsl_service = strdup(service);
@@ -1837,6 +1833,226 @@ object_fill_snapshot(rc_snapshot_t *sp)
return (result);
}
+/*
+ * This represents a property group in a snapshot.
+ */
+typedef struct check_snapshot_elem {
+ uint32_t cse_parent;
+ uint32_t cse_pg_id;
+ uint32_t cse_pg_gen;
+ char cse_seen;
+} check_snapshot_elem_t;
+
+#define CSI_MAX_PARENTS COMPOSITION_DEPTH
+typedef struct check_snapshot_info {
+ size_t csi_count;
+ size_t csi_array_size;
+ check_snapshot_elem_t *csi_array;
+ size_t csi_nparents;
+ uint32_t csi_parent_ids[CSI_MAX_PARENTS];
+} check_snapshot_info_t;
+
+/*ARGSUSED*/
+static int
+check_snapshot_fill_cb(void *data, int columns, char **vals, char **names)
+{
+ check_snapshot_info_t *csip = data;
+ check_snapshot_elem_t *cur;
+ const char *parent;
+ const char *pg_id;
+ const char *pg_gen_id;
+
+ if (columns == 1) {
+ uint32_t *target;
+
+ if (csip->csi_nparents >= CSI_MAX_PARENTS)
+ backend_panic("snaplevel table has too many elements");
+
+ target = &csip->csi_parent_ids[csip->csi_nparents++];
+ string_to_id(vals[0], target, "snap_level_*_id");
+
+ return (BACKEND_CALLBACK_CONTINUE);
+ }
+
+ assert(columns == 3);
+
+ parent = vals[0];
+ pg_id = vals[1];
+ pg_gen_id = vals[2];
+
+ if (csip->csi_count == csip->csi_array_size) {
+ size_t newsz = (csip->csi_array_size > 0) ?
+ csip->csi_array_size * 2 : 8;
+ check_snapshot_elem_t *new = uu_zalloc(newsz * sizeof (*new));
+
+ if (new == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+
+ (void) memcpy(new, csip->csi_array,
+ sizeof (*new) * csip->csi_array_size);
+ uu_free(csip->csi_array);
+ csip->csi_array = new;
+ csip->csi_array_size = newsz;
+ }
+
+ cur = &csip->csi_array[csip->csi_count++];
+
+ string_to_id(parent, &cur->cse_parent, "snap_level_*_id");
+ string_to_id(pg_id, &cur->cse_pg_id, "snaplvl_pg_id");
+ string_to_id(pg_gen_id, &cur->cse_pg_gen, "snaplvl_gen_id");
+ cur->cse_seen = 0;
+
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+static int
+check_snapshot_elem_cmp(const void *lhs_arg, const void *rhs_arg)
+{
+ const check_snapshot_elem_t *lhs = lhs_arg;
+ const check_snapshot_elem_t *rhs = rhs_arg;
+
+ if (lhs->cse_parent < rhs->cse_parent)
+ return (-1);
+ if (lhs->cse_parent > rhs->cse_parent)
+ return (1);
+
+ if (lhs->cse_pg_id < rhs->cse_pg_id)
+ return (-1);
+ if (lhs->cse_pg_id > rhs->cse_pg_id)
+ return (1);
+
+ if (lhs->cse_pg_gen < rhs->cse_pg_gen)
+ return (-1);
+ if (lhs->cse_pg_gen > rhs->cse_pg_gen)
+ return (1);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+check_snapshot_check_cb(void *data, int columns, char **vals, char **names)
+{
+ check_snapshot_info_t *csip = data;
+ check_snapshot_elem_t elem;
+ check_snapshot_elem_t *cur;
+
+ const char *parent = vals[0];
+ const char *pg_id = vals[1];
+ const char *pg_gen_id = vals[2];
+
+ assert(columns == 3);
+
+ string_to_id(parent, &elem.cse_parent, "snap_level_*_id");
+ string_to_id(pg_id, &elem.cse_pg_id, "snaplvl_pg_id");
+ string_to_id(pg_gen_id, &elem.cse_pg_gen, "snaplvl_gen_id");
+
+ if ((cur = bsearch(&elem, csip->csi_array, csip->csi_count,
+ sizeof (*csip->csi_array), check_snapshot_elem_cmp)) == NULL)
+ return (BACKEND_CALLBACK_ABORT);
+
+ if (cur->cse_seen)
+ backend_panic("duplicate property group reported");
+ cur->cse_seen = 1;
+ return (BACKEND_CALLBACK_CONTINUE);
+}
+
+/*
+ * Check that a snapshot matches up with the latest in the repository.
+ * Returns:
+ * REP_PROTOCOL_SUCCESS if it is up-to-date,
+ * REP_PROTOCOL_DONE if it is out-of-date, or
+ * REP_PROTOCOL_FAIL_NO_RESOURCES if we ran out of memory.
+ */
+static int
+object_check_snapshot(uint32_t snap_id)
+{
+ check_snapshot_info_t csi;
+ backend_query_t *q;
+ int result;
+ size_t idx;
+
+ /* if the snapshot has never been taken, it must be out of date. */
+ if (snap_id == 0)
+ return (REP_PROTOCOL_DONE);
+
+ (void) memset(&csi, '\0', sizeof (csi));
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT\n"
+ " CASE snap_level_instance_id\n"
+ " WHEN 0 THEN snap_level_service_id\n"
+ " ELSE snap_level_instance_id\n"
+ " END\n"
+ "FROM snaplevel_tbl\n"
+ "WHERE snap_id = %d;\n"
+ "\n"
+ "SELECT\n"
+ " CASE snap_level_instance_id\n"
+ " WHEN 0 THEN snap_level_service_id\n"
+ " ELSE snap_level_instance_id\n"
+ " END,\n"
+ " snaplvl_pg_id,\n"
+ " snaplvl_gen_id\n"
+ "FROM snaplevel_tbl, snaplevel_lnk_tbl\n"
+ "WHERE\n"
+ " (snaplvl_level_id = snap_level_id AND\n"
+ " snap_id = %d);",
+ snap_id, snap_id);
+
+ result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_fill_cb,
+ &csi);
+ if (result == REP_PROTOCOL_DONE)
+ result = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ backend_query_free(q);
+
+ if (result != REP_PROTOCOL_SUCCESS)
+ goto fail;
+
+ if (csi.csi_count > 0) {
+ qsort(csi.csi_array, csi.csi_count, sizeof (*csi.csi_array),
+ check_snapshot_elem_cmp);
+ }
+
+#if COMPOSITION_DEPTH == 2
+ if (csi.csi_nparents != COMPOSITION_DEPTH) {
+ result = REP_PROTOCOL_DONE;
+ goto fail;
+ }
+
+ q = backend_query_alloc();
+ backend_query_add(q,
+ "SELECT "
+ " pg_parent_id, pg_id, pg_gen_id "
+ "FROM "
+ " pg_tbl "
+ "WHERE (pg_parent_id = %d OR pg_parent_id = %d)",
+ csi.csi_parent_ids[0], csi.csi_parent_ids[1]);
+
+ result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_check_cb,
+ &csi);
+#else
+#error This code must be updated
+#endif
+ /*
+ * To succeed, the callback must not have aborted, and we must have
+ * found all of the items.
+ */
+ if (result == REP_PROTOCOL_SUCCESS) {
+ for (idx = 0; idx < csi.csi_count; idx++) {
+ if (csi.csi_array[idx].cse_seen == 0) {
+ result = REP_PROTOCOL_DONE;
+ goto fail;
+ }
+ }
+ }
+
+fail:
+ uu_free(csi.csi_array);
+ return (result);
+}
+
/*ARGSUSED*/
static int
object_copy_string(void *data_arg, int columns, char **vals, char **names)
@@ -2115,6 +2331,10 @@ object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr,
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
if (takesnap) {
+ /* first, check that we're actually out of date */
+ if (object_check_snapshot(snapid) == REP_PROTOCOL_SUCCESS)
+ return (REP_PROTOCOL_SUCCESS);
+
result = object_snapshot_do_take(instid, NULL,
svcid, NULL, &tx, &snapid);
if (result != REP_PROTOCOL_SUCCESS)