summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/configd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/svc/configd')
-rw-r--r--usr/src/cmd/svc/configd/backend.c215
-rw-r--r--usr/src/cmd/svc/configd/client.c78
-rw-r--r--usr/src/cmd/svc/configd/configd.c42
-rw-r--r--usr/src/cmd/svc/configd/configd.h14
-rw-r--r--usr/src/cmd/svc/configd/file_object.c276
-rw-r--r--usr/src/cmd/svc/configd/restore_repository.sh55
6 files changed, 548 insertions, 132 deletions
diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c
index 141df0c168..8589e82b7d 100644
--- a/usr/src/cmd/svc/configd/backend.c
+++ b/usr/src/cmd/svc/configd/backend.c
@@ -20,12 +20,20 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * sqlite is not compatible with _FILE_OFFSET_BITS=64, but we need to
+ * be able to statvfs(2) possibly large systems. This define gives us
+ * access to the transitional interfaces. See lfcompile64(5) for how
+ * _LARGEFILE64_SOURCE works.
+ */
+#define _LARGEFILE64_SOURCE
+
#include <assert.h>
#include <door.h>
#include <dirent.h>
@@ -37,6 +45,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <unistd.h>
#include <zone.h>
@@ -72,9 +82,10 @@ typedef struct sqlite_backend {
pthread_t be_thread; /* thread holding lock */
struct sqlite *be_db;
const char *be_path; /* path to db */
- int be_readonly; /* backend is read-only */
+ int be_readonly; /* readonly at start, and still is */
int be_writing; /* held for writing */
backend_type_t be_type; /* type of db */
+ hrtime_t be_lastcheck; /* time of last read-only check */
backend_totals_t be_totals[2]; /* one for reading, one for writing */
} sqlite_backend_t;
@@ -119,6 +130,9 @@ int backend_do_trace = 0; /* invoke tracing callback */
int backend_print_trace = 0; /* tracing callback prints SQL */
int backend_panic_abort = 0; /* abort when panicking */
+/* interval between read-only checks while starting up */
+#define BACKEND_READONLY_CHECK_INTERVAL (2 * (hrtime_t)NANOSEC)
+
/*
* Any change to the below schema should bump the version number
*/
@@ -319,18 +333,49 @@ backend_fail_if_seen(void *arg, int columns, char **vals, char **names)
return (BACKEND_CALLBACK_ABORT);
}
+/*
+ * check to see if we can successfully start a transaction; if not, the
+ * filesystem is mounted read-only.
+ */
static int
-backend_is_readonly(struct sqlite *db, char **errp)
+backend_is_readonly(struct sqlite *db, const char *path)
{
- int r = sqlite_exec(db,
+ int r;
+ statvfs64_t stat;
+
+ if (statvfs64(path, &stat) == 0 && (stat.f_flag & ST_RDONLY))
+ return (SQLITE_READONLY);
+
+ r = sqlite_exec(db,
"BEGIN TRANSACTION; "
"UPDATE schema_version SET schema_version = schema_version; ",
- NULL, NULL, errp);
-
+ NULL, NULL, NULL);
(void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
return (r);
}
+/*
+ * Check to see if the administrator has removed the writable bits on the
+ * repository file. If they have, we don't allow modifications.
+ *
+ * Since we normally run with PRIV_FILE_DAC_WRITE, we have to use a separate
+ * check.
+ */
+static int
+backend_check_perm(const char *path)
+{
+ struct stat64 stat;
+
+ if (access(path, W_OK) < 0)
+ return (SQLITE_READONLY);
+
+ if (stat64(path, &stat) == 0 &&
+ !(stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ return (SQLITE_READONLY);
+
+ return (SQLITE_OK);
+}
+
static void
backend_trace_sql(void *arg, const char *sql)
{
@@ -655,6 +700,68 @@ backend_backup_base(sqlite_backend_t *be, const char *name,
}
/*
+ * See if a backup is needed. We do a backup unless both files are
+ * byte-for-byte identical.
+ */
+static int
+backend_check_backup_needed(const char *rep_name, const char *backup_name)
+{
+ int repfd = open(rep_name, O_RDONLY);
+ int fd = open(backup_name, O_RDONLY);
+ struct stat s_rep, s_backup;
+ int c1, c2;
+
+ FILE *f_rep = NULL;
+ FILE *f_backup = NULL;
+
+ if (repfd < 0 || fd < 0)
+ goto fail;
+
+ if (fstat(repfd, &s_rep) < 0 || fstat(fd, &s_backup) < 0)
+ goto fail;
+
+ /*
+ * if they are the same file, we need to do a backup to break the
+ * hard link or symlink involved.
+ */
+ if (s_rep.st_ino == s_backup.st_ino && s_rep.st_dev == s_backup.st_dev)
+ goto fail;
+
+ if (s_rep.st_size != s_backup.st_size)
+ goto fail;
+
+ if ((f_rep = fdopen(repfd, "r")) == NULL ||
+ (f_backup = fdopen(fd, "r")) == NULL)
+ goto fail;
+
+ do {
+ c1 = getc(f_rep);
+ c2 = getc(f_backup);
+ if (c1 != c2)
+ goto fail;
+ } while (c1 != EOF);
+
+ if (!ferror(f_rep) && !ferror(f_backup)) {
+ (void) fclose(f_rep);
+ (void) fclose(f_backup);
+ (void) close(repfd);
+ (void) close(fd);
+ return (0);
+ }
+
+fail:
+ if (f_rep != NULL)
+ (void) fclose(f_rep);
+ if (f_backup != NULL)
+ (void) fclose(f_backup);
+ if (repfd >= 0)
+ (void) close(repfd);
+ if (fd >= 0)
+ (void) close(fd);
+ return (1);
+}
+
+/*
* Can return:
* _BAD_REQUEST name is not valid
* _TRUNCATED name is too long for current repository path
@@ -693,6 +800,10 @@ backend_create_backup_locked(sqlite_backend_t *be, const char *name)
if (result != REP_PROTOCOL_SUCCESS)
return (result);
+ if (!backend_check_backup_needed(be->be_path, finalpath)) {
+ return (REP_PROTOCOL_SUCCESS);
+ }
+
/*
* remember the original length, and the basename location
*/
@@ -819,6 +930,58 @@ fail:
return (result);
}
+static int
+backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t)
+{
+ char *errp;
+ struct sqlite *new;
+ int r;
+
+ assert(be->be_readonly);
+ assert(be == bes[BACKEND_TYPE_NORMAL]);
+
+ /*
+ * If we don't *need* to be writable, only check every once in a
+ * while.
+ */
+ if (!writing) {
+ if ((uint64_t)(t - be->be_lastcheck) <
+ BACKEND_READONLY_CHECK_INTERVAL)
+ return (REP_PROTOCOL_SUCCESS);
+ be->be_lastcheck = t;
+ }
+
+ new = sqlite_open(be->be_path, 0600, &errp);
+ if (new == NULL) {
+ backend_panic("reopening %s: %s\n", be->be_path, errp);
+ /*NOTREACHED*/
+ }
+ r = backend_is_readonly(new, be->be_path);
+
+ if (r != SQLITE_OK) {
+ sqlite_close(new);
+ if (writing)
+ return (REP_PROTOCOL_FAIL_BACKEND_READONLY);
+ return (REP_PROTOCOL_SUCCESS);
+ }
+
+ /*
+ * We can write! Swap the db handles, mark ourself writable,
+ * and make a backup.
+ */
+ sqlite_close(be->be_db);
+ be->be_db = new;
+ be->be_readonly = 0;
+
+ if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) !=
+ REP_PROTOCOL_SUCCESS) {
+ configd_critical(
+ "unable to create \"%s\" backup of \"%s\"\n",
+ REPOSITORY_BOOT_BACKUP, be->be_path);
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
/*
* If t is not BACKEND_TYPE_NORMAL, can fail with
@@ -859,41 +1022,23 @@ backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep)
}
be->be_thread = pthread_self();
- if (writing && be->be_readonly) {
- char *errp;
- struct sqlite *new;
+ if (be->be_readonly) {
int r;
-
assert(t == BACKEND_TYPE_NORMAL);
- new = sqlite_open(be->be_path, 0600, &errp);
- if (new == NULL) {
- backend_panic("reopening %s: %s\n", be->be_path, errp);
- /*NOTREACHED*/
- }
- r = backend_is_readonly(new, &errp);
- if (r != SQLITE_OK) {
- free(errp);
- sqlite_close(new);
+ r = backend_check_readonly(be, writing, ts);
+ if (r != REP_PROTOCOL_SUCCESS) {
be->be_thread = 0;
(void) pthread_mutex_unlock(&be->be_lock);
- return (REP_PROTOCOL_FAIL_BACKEND_READONLY);
+ return (r);
}
+ }
- /*
- * We can write! Swap our db handles, mark ourself writable,
- * and make a backup.
- */
- sqlite_close(be->be_db);
- be->be_db = new;
- be->be_readonly = 0;
-
- if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) !=
- REP_PROTOCOL_SUCCESS) {
- configd_critical(
- "unable to create \"%s\" backup of \"%s\"\n",
- REPOSITORY_BOOT_BACKUP, be->be_path);
- }
+ if (writing && t == BACKEND_TYPE_NORMAL &&
+ backend_check_perm(be->be_path) != SQLITE_OK) {
+ be->be_thread = 0;
+ (void) pthread_mutex_unlock(&be->be_lock);
+ return (REP_PROTOCOL_FAIL_BACKEND_READONLY);
}
if (backend_do_trace)
@@ -1192,7 +1337,7 @@ integrity_fail:
/*
* check if we are writable
*/
- r = backend_is_readonly(be->be_db, &errp);
+ r = backend_is_readonly(be->be_db, be->be_path);
if (r == SQLITE_BUSY || r == SQLITE_LOCKED) {
free(errp);
diff --git a/usr/src/cmd/svc/configd/client.c b/usr/src/cmd/svc/configd/client.c
index 49aed805d1..72aa30d12e 100644
--- a/usr/src/cmd/svc/configd/client.c
+++ b/usr/src/cmd/svc/configd/client.c
@@ -72,8 +72,8 @@
#pragma align 64(client_hash)
static client_bucket_t client_hash[CLIENT_HASH_SIZE];
-static uu_list_pool_t *entity_pool;
-static uu_list_pool_t *iter_pool;
+static uu_avl_pool_t *entity_pool;
+static uu_avl_pool_t *iter_pool;
static uu_list_pool_t *client_pool;
#define CLIENT_HASH(id) (&client_hash[((id) & (CLIENT_HASH_SIZE - 1))])
@@ -228,14 +228,14 @@ client_hash_init(void)
int x;
assert_nolint(offsetof(repcache_entity_t, re_id) == 0);
- entity_pool = uu_list_pool_create("repcache_entitys",
+ entity_pool = uu_avl_pool_create("repcache_entitys",
sizeof (repcache_entity_t), offsetof(repcache_entity_t, re_link),
- entity_compare, UU_LIST_POOL_DEBUG);
+ entity_compare, UU_AVL_POOL_DEBUG);
assert_nolint(offsetof(repcache_iter_t, ri_id) == 0);
- iter_pool = uu_list_pool_create("repcache_iters",
+ iter_pool = uu_avl_pool_create("repcache_iters",
sizeof (repcache_iter_t), offsetof(repcache_iter_t, ri_link),
- iter_compare, UU_LIST_POOL_DEBUG);
+ iter_compare, UU_AVL_POOL_DEBUG);
assert_nolint(offsetof(repcache_client_t, rc_id) == 0);
client_pool = uu_list_pool_create("repcache_clients",
@@ -266,12 +266,12 @@ client_alloc(void)
if (cp == NULL)
return (NULL);
- cp->rc_entity_list = uu_list_create(entity_pool, cp, UU_LIST_SORTED);
- if (cp->rc_entity_list == NULL)
+ cp->rc_entities = uu_avl_create(entity_pool, cp, 0);
+ if (cp->rc_entities == NULL)
goto fail;
- cp->rc_iter_list = uu_list_create(iter_pool, cp, UU_LIST_SORTED);
- if (cp->rc_iter_list == NULL)
+ cp->rc_iters = uu_avl_create(iter_pool, cp, 0);
+ if (cp->rc_iters == NULL)
goto fail;
uu_list_node_init(cp, &cp->rc_link, client_pool);
@@ -286,10 +286,10 @@ client_alloc(void)
return (cp);
fail:
- if (cp->rc_iter_list != NULL)
- uu_list_destroy(cp->rc_iter_list);
- if (cp->rc_entity_list != NULL)
- uu_list_destroy(cp->rc_entity_list);
+ if (cp->rc_iters != NULL)
+ uu_avl_destroy(cp->rc_iters);
+ if (cp->rc_entities != NULL)
+ uu_avl_destroy(cp->rc_entities);
uu_free(cp);
return (NULL);
}
@@ -301,10 +301,10 @@ client_free(repcache_client_t *cp)
assert(cp->rc_refcnt == 0);
assert(cp->rc_doorfd == -1);
assert(cp->rc_doorid == INVALID_DOORID);
- assert(uu_list_first(cp->rc_entity_list) == NULL);
- assert(uu_list_first(cp->rc_iter_list) == NULL);
- uu_list_destroy(cp->rc_entity_list);
- uu_list_destroy(cp->rc_iter_list);
+ assert(uu_avl_first(cp->rc_entities) == NULL);
+ assert(uu_avl_first(cp->rc_iters) == NULL);
+ uu_avl_destroy(cp->rc_entities);
+ uu_avl_destroy(cp->rc_iters);
uu_list_node_fini(cp, &cp->rc_link, client_pool);
(void) pthread_mutex_destroy(&cp->rc_lock);
uu_free(cp);
@@ -398,7 +398,7 @@ entity_alloc(repcache_client_t *cp)
{
repcache_entity_t *ep = uu_zalloc(sizeof (repcache_entity_t));
if (ep != NULL) {
- uu_list_node_init(ep, &ep->re_link, entity_pool);
+ uu_avl_node_init(ep, &ep->re_link, entity_pool);
}
return (ep);
}
@@ -406,13 +406,13 @@ entity_alloc(repcache_client_t *cp)
static void
entity_add(repcache_client_t *cp, repcache_entity_t *ep)
{
- uu_list_index_t idx;
+ uu_avl_index_t idx;
(void) pthread_mutex_lock(&cp->rc_lock);
assert(cp->rc_insert_thr == pthread_self());
- (void) uu_list_find(cp->rc_entity_list, ep, NULL, &idx);
- uu_list_insert(cp->rc_entity_list, ep, idx);
+ (void) uu_avl_find(cp->rc_entities, ep, NULL, &idx);
+ uu_avl_insert(cp->rc_entities, ep, idx);
(void) pthread_mutex_unlock(&cp->rc_lock);
}
@@ -423,7 +423,7 @@ entity_find(repcache_client_t *cp, uint32_t id)
repcache_entity_t *ep;
(void) pthread_mutex_lock(&cp->rc_lock);
- ep = uu_list_find(cp->rc_entity_list, &id, NULL, NULL);
+ ep = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
if (ep != NULL) {
add_log_ptr(get_log(), RC_PTR_TYPE_ENTITY, id, ep);
(void) pthread_mutex_lock(&ep->re_lock);
@@ -449,8 +449,8 @@ entity_find2(repcache_client_t *cp, uint32_t id1, repcache_entity_t **out1,
return (REP_PROTOCOL_FAIL_DUPLICATE_ID);
(void) pthread_mutex_lock(&cp->rc_lock);
- e1 = uu_list_find(cp->rc_entity_list, &id1, NULL, NULL);
- e2 = uu_list_find(cp->rc_entity_list, &id2, NULL, NULL);
+ e1 = uu_avl_find(cp->rc_entities, &id1, NULL, NULL);
+ e2 = uu_avl_find(cp->rc_entities, &id2, NULL, NULL);
if (e1 == NULL || e2 == NULL) {
(void) pthread_mutex_unlock(&cp->rc_lock);
return (REP_PROTOCOL_FAIL_UNKNOWN_ID);
@@ -496,7 +496,7 @@ entity_destroy(repcache_entity_t *entity)
rc_node_clear(&entity->re_node, 0);
(void) pthread_mutex_unlock(&entity->re_lock);
- uu_list_node_fini(entity, &entity->re_link, entity_pool);
+ uu_avl_node_fini(entity, &entity->re_link, entity_pool);
(void) pthread_mutex_destroy(&entity->re_lock);
uu_free(entity);
}
@@ -507,9 +507,9 @@ entity_remove(repcache_client_t *cp, uint32_t id)
repcache_entity_t *entity;
(void) pthread_mutex_lock(&cp->rc_lock);
- entity = uu_list_find(cp->rc_entity_list, &id, NULL, NULL);
+ entity = uu_avl_find(cp->rc_entities, &id, NULL, NULL);
if (entity != NULL)
- uu_list_remove(cp->rc_entity_list, entity);
+ uu_avl_remove(cp->rc_entities, entity);
(void) pthread_mutex_unlock(&cp->rc_lock);
if (entity != NULL)
@@ -523,7 +523,7 @@ entity_cleanup(repcache_client_t *cp)
void *cookie = NULL;
(void) pthread_mutex_lock(&cp->rc_lock);
- while ((ep = uu_list_teardown(cp->rc_entity_list, &cookie)) != NULL) {
+ while ((ep = uu_avl_teardown(cp->rc_entities, &cookie)) != NULL) {
(void) pthread_mutex_unlock(&cp->rc_lock);
entity_destroy(ep);
(void) pthread_mutex_lock(&cp->rc_lock);
@@ -538,7 +538,7 @@ iter_alloc(repcache_client_t *cp)
repcache_iter_t *iter;
iter = uu_zalloc(sizeof (repcache_iter_t));
if (iter != NULL)
- uu_list_node_init(iter, &iter->ri_link, iter_pool);
+ uu_avl_node_init(iter, &iter->ri_link, iter_pool);
return (iter);
}
@@ -550,8 +550,8 @@ iter_add(repcache_client_t *cp, repcache_iter_t *iter)
(void) pthread_mutex_lock(&cp->rc_lock);
assert(cp->rc_insert_thr == pthread_self());
- (void) uu_list_find(cp->rc_iter_list, iter, NULL, &idx);
- uu_list_insert(cp->rc_iter_list, iter, idx);
+ (void) uu_avl_find(cp->rc_iters, iter, NULL, &idx);
+ uu_avl_insert(cp->rc_iters, iter, idx);
(void) pthread_mutex_unlock(&cp->rc_lock);
}
@@ -563,7 +563,7 @@ iter_find(repcache_client_t *cp, uint32_t id)
(void) pthread_mutex_lock(&cp->rc_lock);
- iter = uu_list_find(cp->rc_iter_list, &id, NULL, NULL);
+ iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
if (iter != NULL) {
add_log_ptr(get_log(), RC_PTR_TYPE_ITER, id, iter);
(void) pthread_mutex_lock(&iter->ri_lock);
@@ -586,8 +586,8 @@ iter_find_w_entity(repcache_client_t *cp, uint32_t iter_id,
request_log_entry_t *rlp;
(void) pthread_mutex_lock(&cp->rc_lock);
- iter = uu_list_find(cp->rc_iter_list, &iter_id, NULL, NULL);
- ep = uu_list_find(cp->rc_entity_list, &entity_id, NULL, NULL);
+ iter = uu_avl_find(cp->rc_iters, &iter_id, NULL, NULL);
+ ep = uu_avl_find(cp->rc_entities, &entity_id, NULL, NULL);
assert(iter == NULL || !MUTEX_HELD(&iter->ri_lock));
assert(ep == NULL || !MUTEX_HELD(&ep->re_lock));
@@ -626,7 +626,7 @@ iter_destroy(repcache_iter_t *iter)
rc_iter_destroy(&iter->ri_iter);
(void) pthread_mutex_unlock(&iter->ri_lock);
- uu_list_node_fini(iter, &iter->ri_link, iter_pool);
+ uu_avl_node_fini(iter, &iter->ri_link, iter_pool);
(void) pthread_mutex_destroy(&iter->ri_lock);
uu_free(iter);
}
@@ -637,9 +637,9 @@ iter_remove(repcache_client_t *cp, uint32_t id)
repcache_iter_t *iter;
(void) pthread_mutex_lock(&cp->rc_lock);
- iter = uu_list_find(cp->rc_iter_list, &id, NULL, NULL);
+ iter = uu_avl_find(cp->rc_iters, &id, NULL, NULL);
if (iter != NULL)
- uu_list_remove(cp->rc_iter_list, iter);
+ uu_avl_remove(cp->rc_iters, iter);
(void) pthread_mutex_unlock(&cp->rc_lock);
if (iter != NULL)
@@ -653,7 +653,7 @@ iter_cleanup(repcache_client_t *cp)
void *cookie = NULL;
(void) pthread_mutex_lock(&cp->rc_lock);
- while ((iter = uu_list_teardown(cp->rc_iter_list, &cookie)) != NULL) {
+ while ((iter = uu_avl_teardown(cp->rc_iters, &cookie)) != NULL) {
(void) pthread_mutex_unlock(&cp->rc_lock);
iter_destroy(iter);
(void) pthread_mutex_lock(&cp->rc_lock);
diff --git a/usr/src/cmd/svc/configd/configd.c b/usr/src/cmd/svc/configd/configd.c
index 32f6f318c6..5632735834 100644
--- a/usr/src/cmd/svc/configd/configd.c
+++ b/usr/src/cmd/svc/configd/configd.c
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,6 +30,7 @@
#include <door.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <priv.h>
#include <procfs.h>
#include <pthread.h>
@@ -162,7 +163,8 @@ thread_exiting(void *arg)
{
thread_info_t *ti = arg;
- log_enter(&ti->ti_log);
+ if (ti != NULL)
+ log_enter(&ti->ti_log);
(void) pthread_mutex_lock(&thread_lock);
if (ti != NULL) {
@@ -471,6 +473,23 @@ daemonize_ready(void)
(void) close(pipe_fd);
}
+const char *
+regularize_path(const char *dir, const char *base, char *tmpbuf)
+{
+ if (base == NULL)
+ return (NULL);
+ if (base[0] == '/')
+ return (base);
+
+ if (snprintf(tmpbuf, PATH_MAX, "%s/%s", dir, base) >= PATH_MAX) {
+ (void) fprintf(stderr, "svc.configd: %s/%s: path too long\n",
+ dir, base);
+ exit(CONFIGD_EXIT_BAD_ARGS);
+ }
+
+ return (tmpbuf);
+}
+
int
main(int argc, char *argv[])
{
@@ -485,6 +504,12 @@ main(int argc, char *argv[])
int c;
int ret;
int fd;
+
+ char curdir[PATH_MAX];
+ char dbtmp[PATH_MAX];
+ char npdbtmp[PATH_MAX];
+ char doortmp[PATH_MAX];
+
const char *dbpath = NULL;
const char *npdbpath = NULL;
const char *doorpath = REPOSITORY_DOOR_NAME;
@@ -495,13 +520,20 @@ main(int argc, char *argv[])
closefrom(3); /* get rid of extraneous fds */
+ if (getcwd(curdir, sizeof (curdir)) == NULL) {
+ (void) fprintf(stderr,
+ "%s: unable to get current directory: %s\n",
+ argv[0], strerror(errno));
+ exit(CONFIGD_EXIT_INIT_FAILED);
+ }
+
while ((c = getopt(argc, argv, "Dnpd:r:t:")) != -1) {
switch (c) {
case 'n':
daemonize = 0;
break;
case 'd':
- doorpath = optarg;
+ doorpath = regularize_path(curdir, optarg, doortmp);
is_main_repository = 0;
have_npdb = 0; /* default to no non-persist */
break;
@@ -527,11 +559,11 @@ main(int argc, char *argv[])
privileged_psinfo_fd = fd;
break;
case 'r':
- dbpath = optarg;
+ dbpath = regularize_path(curdir, optarg, dbtmp);
is_main_repository = 0;
break;
case 't':
- npdbpath = optarg;
+ npdbpath = regularize_path(curdir, optarg, npdbtmp);
is_main_repository = 0;
break;
default:
diff --git a/usr/src/cmd/svc/configd/configd.h b/usr/src/cmd/svc/configd/configd.h
index f00bb6467b..7d01a9005e 100644
--- a/usr/src/cmd/svc/configd/configd.h
+++ b/usr/src/cmd/svc/configd/configd.h
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -404,7 +404,7 @@ enum repcache_txstate {
typedef struct repcache_entity {
uint32_t re_id;
- uu_list_node_t re_link;
+ uu_avl_node_t re_link;
uint32_t re_changeid;
pthread_mutex_t re_lock;
@@ -415,7 +415,7 @@ typedef struct repcache_entity {
typedef struct repcache_iter {
uint32_t ri_id;
- uu_list_node_t ri_link;
+ uu_avl_node_t ri_link;
uint32_t ri_type; /* result type */
@@ -452,10 +452,10 @@ typedef struct repcache_client {
rc_node_ptr_t rc_notify_ptr;
/*
- * register lists, protected by rc_lock
+ * register sets, protected by rc_lock
*/
- uu_list_t *rc_entity_list; /* entities */
- uu_list_t *rc_iter_list; /* iters */
+ uu_avl_t *rc_entities;
+ uu_avl_t *rc_iters;
/*
* Variables, protected by rc_lock
@@ -660,7 +660,7 @@ int object_create_pg(rc_node_t *, uint32_t, const char *, const char *,
int object_delete(rc_node_t *);
void object_free_values(const char *, uint32_t, size_t, size_t);
-int object_fill_snapshot(rc_snapshot_t *sp);
+int object_fill_snapshot(rc_snapshot_t *);
int object_snapshot_take_new(rc_node_t *, const char *, const char *,
const char *, rc_node_t **);
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)
diff --git a/usr/src/cmd/svc/configd/restore_repository.sh b/usr/src/cmd/svc/configd/restore_repository.sh
index 08e916b0f9..8ba38ce70f 100644
--- a/usr/src/cmd/svc/configd/restore_repository.sh
+++ b/usr/src/cmd/svc/configd/restore_repository.sh
@@ -21,28 +21,24 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
PATH=/sbin:/usr/bin:/usr/sbin
+LC_ALL=C
+export PATH LC_ALL
. /lib/svc/share/smf_include.sh
. /lib/svc/share/fs_include.sh
-echo >&2 "
-Repository Restore utility
-
-See http://sun.com/msg/SMF-8000-MY for more information on the use of
-this script to restore backup copies of the smf(5) repository.
-
-If there are any problems which need human intervention, this script will
-give instructions and then exit back to your shell."
-
usage()
{
echo "usage: $0 [-r rootdir]" >&2
+ echo "
+See http://sun.com/msg/SMF-8000-MY for more information on the use of
+this script."
exit 2;
}
@@ -79,6 +75,23 @@ if [ $OPTIND -le $# ]; then
usage
fi
+#
+# Note that the below test is carefully constructed to fail *open*; if
+# anything goes wrong, it will drive onward.
+#
+if [ -x /usr/bin/id -a -x /usr/bin/grep ] &&
+ /usr/bin/id 2>/dev/null | /usr/bin/grep -v '^[^=]*=0(' >/dev/null 2>&1; then
+ echo "$0: may only be invoked by root" >&2
+ exit 2
+fi
+
+echo >&2 "
+See http://sun.com/msg/SMF-8000-MY for more information on the use of
+this script to restore backup copies of the smf(5) repository.
+
+If there are any problems which need human intervention, this script will
+give instructions and then exit back to your shell."
+
if [ "$myroot" -eq / ]; then
system="system"
[ "`/sbin/zonename`" != global ] && system="zone"
@@ -99,6 +112,7 @@ rootro=false
if [ ! -x /usr/bin/pgrep ]; then
nouser=true
fi
+
if [ ! -w "$myroot" ]; then
rootro=true
fi
@@ -142,7 +156,8 @@ if [ -z "$oldreps" ]; then
cat >&2 <<EOF
There are no available backups of $myroot$repositorydir/$repository.db.
The only available repository is "-seed-". Note that restoring the seed
-will lose all customizations, and XXX other issues?
+will lose all customizations, including those made by the system during
+the installation and/or upgrade process.
EOF
prompt="Enter -seed- to restore from the seed, or -quit- to exit: \c"
@@ -160,13 +175,17 @@ the repository after system boot. Backups beginning with "manifest_import"
are made after svc:/system/manifest-import:default finishes its processing.
The time of backup is given in YYYYMMDD_HHMMSS format.
-Please enter one of:
- 1) boot, for the most recent post-boot backup
- 2) manifest_import, for the most recent manifest_import backup.
- 3) a specific backup repository from the above list
- 4) -seed-, the initial starting repository. (All customizations
- will be lost.)
- 5) -quit-, to cancel.
+Please enter either a specific backup repository from the above list to
+restore it, or one of the following choices:
+
+ CHOICE ACTION
+ ---------------- ----------------------------------------------
+ boot restore the most recent post-boot backup
+ manifest_import restore the most recent manifest_import backup
+ -seed- restore the initial starting repository (All
+ customizations will be lost, including those
+ made by the install/upgrade process.)
+ -quit- cancel script and quit
EOF
prompt="Enter response [boot]: \c"