summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstevep <none@none>2008-02-19 10:33:55 -0800
committerstevep <none@none>2008-02-19 10:33:55 -0800
commitc0889d7a91fa87e1cb7ef4457629b0cb51d47b50 (patch)
tree6d61969fd57b6c78ff2948c9d41b3c23f674ca34
parent81d97edfb9c61e08bc3e3776a29b9d7d0e87e574 (diff)
downloadillumos-joyent-c0889d7a91fa87e1cb7ef4457629b0cb51d47b50.tar.gz
6351623 Initial manifest-import is slow
-rw-r--r--usr/src/cmd/svc/configd/backend.c418
-rw-r--r--usr/src/cmd/svc/configd/client.c43
-rw-r--r--usr/src/cmd/svc/configd/configd.h7
-rw-r--r--usr/src/cmd/svc/milestone/manifest-import24
-rw-r--r--usr/src/cmd/svc/svcadm/svcadm.c58
-rw-r--r--usr/src/common/svc/repcache_protocol.h16
-rw-r--r--usr/src/lib/libscf/common/lowlevel.c49
-rw-r--r--usr/src/lib/libscf/common/mapfile-vers1
-rw-r--r--usr/src/lib/libscf/inc/libscf_priv.h13
9 files changed, 561 insertions, 68 deletions
diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c
index 3f39f068f8..3354b2faee 100644
--- a/usr/src/cmd/svc/configd/backend.c
+++ b/usr/src/cmd/svc/configd/backend.c
@@ -49,6 +49,7 @@
#include <sys/statvfs.h>
#include <unistd.h>
#include <zone.h>
+#include <libscf_priv.h>
#include "configd.h"
#include "repcache_protocol.h"
@@ -740,13 +741,72 @@ fail:
}
/*
+ * This interface is called to perform the actual copy
+ *
+ * Return:
+ * _FAIL_UNKNOWN read/write fails
+ * _FAIL_NO_RESOURCES out of memory
+ * _SUCCESS copy succeeds
+ */
+static rep_protocol_responseid_t
+backend_do_copy(const char *src, int srcfd, const char *dst,
+ int dstfd, size_t *sz)
+{
+ char *buf;
+ off_t nrd, nwr, n, r_off = 0, w_off = 0;
+
+ if ((buf = malloc(8192)) == NULL)
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+
+ while ((nrd = read(srcfd, buf, 8192)) != 0) {
+ if (nrd < 0) {
+ if (errno == EINTR)
+ continue;
+
+ configd_critical(
+ "Backend copy failed: fails to read from %s "
+ "at offset %d: %s\n", src, r_off, strerror(errno));
+ free(buf);
+ return (REP_PROTOCOL_FAIL_UNKNOWN);
+ }
+
+ r_off += nrd;
+
+ nwr = 0;
+ do {
+ if ((n = write(dstfd, &buf[nwr], nrd - nwr)) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ configd_critical(
+ "Backend copy failed: fails to write to %s "
+ "at offset %d: %s\n", dst, w_off,
+ strerror(errno));
+ free(buf);
+ return (REP_PROTOCOL_FAIL_UNKNOWN);
+ }
+
+ nwr += n;
+ w_off += n;
+
+ } while (nwr < nrd);
+ }
+
+ if (sz)
+ *sz = w_off;
+
+ free(buf);
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
* Can return:
* _BAD_REQUEST name is not valid
* _TRUNCATED name is too long for current repository path
* _UNKNOWN failed for unknown reason (details written to
* console)
* _BACKEND_READONLY backend is not writable
- *
+ * _NO_RESOURCES out of memory
* _SUCCESS Backup completed successfully.
*/
static rep_protocol_responseid_t
@@ -756,30 +816,35 @@ backend_create_backup_locked(sqlite_backend_t *be, const char *name)
ssize_t old_sz;
ssize_t old_max = max_repository_backups;
ssize_t cur;
-
char *finalname;
-
- char finalpath[PATH_MAX];
- char tmppath[PATH_MAX];
- char buf[8192];
+ char *finalpath;
+ char *tmppath;
int infd, outfd;
size_t len;
- off_t inlen, outlen, offset;
-
time_t now;
struct tm now_tm;
-
rep_protocol_responseid_t result;
- if (be->be_readonly)
- return (REP_PROTOCOL_FAIL_BACKEND_READONLY);
+ if ((finalpath = malloc(PATH_MAX)) == NULL)
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+
+ if ((tmppath = malloc(PATH_MAX)) == NULL) {
+ free(finalpath);
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
+
+ if (be->be_readonly) {
+ result = REP_PROTOCOL_FAIL_BACKEND_READONLY;
+ goto out;
+ }
- result = backend_backup_base(be, name, finalpath, sizeof (finalpath));
+ result = backend_backup_base(be, name, finalpath, PATH_MAX);
if (result != REP_PROTOCOL_SUCCESS)
- return (result);
+ goto out;
if (!backend_check_backup_needed(be->be_path, finalpath)) {
- return (REP_PROTOCOL_SUCCESS);
+ result = REP_PROTOCOL_SUCCESS;
+ goto out;
}
/*
@@ -792,30 +857,33 @@ backend_create_backup_locked(sqlite_backend_t *be, const char *name)
else
finalname = finalpath;
- (void) strlcpy(tmppath, finalpath, sizeof (tmppath));
- if (strlcat(tmppath, "-tmpXXXXXX", sizeof (tmppath)) >=
- sizeof (tmppath))
- return (REP_PROTOCOL_FAIL_TRUNCATED);
+ (void) strlcpy(tmppath, finalpath, PATH_MAX);
+ if (strlcat(tmppath, "-tmpXXXXXX", PATH_MAX) >= PATH_MAX) {
+ result = REP_PROTOCOL_FAIL_TRUNCATED;
+ goto out;
+ }
now = time(NULL);
if (localtime_r(&now, &now_tm) == NULL) {
configd_critical(
"\"%s\" backup failed: localtime(3C) failed: %s\n", name,
be->be_path, strerror(errno));
- return (REP_PROTOCOL_FAIL_UNKNOWN);
+ result = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto out;
}
- if (strftime(finalpath + len, sizeof (finalpath) - len,
- "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >=
- sizeof (finalpath) - len) {
- return (REP_PROTOCOL_FAIL_TRUNCATED);
+ if (strftime(finalpath + len, PATH_MAX - len,
+ "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >= PATH_MAX - len) {
+ result = REP_PROTOCOL_FAIL_TRUNCATED;
+ goto out;
}
infd = open(be->be_path, O_RDONLY);
if (infd < 0) {
configd_critical("\"%s\" backup failed: opening %s: %s\n", name,
be->be_path, strerror(errno));
- return (REP_PROTOCOL_FAIL_UNKNOWN);
+ result = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto out;
}
outfd = mkstemp(tmppath);
@@ -823,40 +891,13 @@ backend_create_backup_locked(sqlite_backend_t *be, const char *name)
configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n",
name, tmppath, strerror(errno));
(void) close(infd);
- return (REP_PROTOCOL_FAIL_UNKNOWN);
- }
-
- for (;;) {
- do {
- inlen = read(infd, buf, sizeof (buf));
- } while (inlen < 0 && errno == EINTR);
-
- if (inlen <= 0)
- break;
-
- for (offset = 0; offset < inlen; offset += outlen) {
- do {
- outlen = write(outfd, buf + offset,
- inlen - offset);
- } while (outlen < 0 && errno == EINTR);
-
- if (outlen >= 0)
- continue;
-
- configd_critical(
- "\"%s\" backup failed: write to %s: %s\n",
- name, tmppath, strerror(errno));
- result = REP_PROTOCOL_FAIL_UNKNOWN;
- goto fail;
- }
+ result = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto out;
}
- if (inlen < 0) {
- configd_critical(
- "\"%s\" backup failed: read from %s: %s\n",
- name, be->be_path, strerror(errno));
+ if ((result = backend_do_copy((const char *)be->be_path, infd,
+ (const char *)tmppath, outfd, NULL)) != REP_PROTOCOL_SUCCESS)
goto fail;
- }
/*
* grab the old list before doing our re-name.
@@ -886,7 +927,7 @@ backend_create_backup_locked(sqlite_backend_t *be, const char *name)
/* unlink all but the first (old_max - 1) files */
for (cur = old_max - 1; cur < old_sz; cur++) {
(void) strlcpy(finalname, old_list[cur],
- sizeof (finalpath) - (finalname - finalpath));
+ PATH_MAX - (finalname - finalpath));
if (unlink(finalpath) < 0)
configd_critical(
"\"%s\" backup completed, but removing old "
@@ -905,6 +946,10 @@ fail:
if (result != REP_PROTOCOL_SUCCESS)
(void) unlink(tmppath);
+out:
+ free(finalpath);
+ free(tmppath);
+
return (result);
}
@@ -1074,7 +1119,7 @@ backend_fd_write(int fd, const char *mess)
* _UNKNOWN failed for unknown reason (details written to
* console)
* _BACKEND_READONLY backend is not writable
- *
+ * _NO_RESOURCES out of memory
* _SUCCESS Backup completed successfully.
*/
rep_protocol_responseid_t
@@ -1084,8 +1129,7 @@ backend_create_backup(const char *name)
sqlite_backend_t *be;
result = backend_lock(BACKEND_TYPE_NORMAL, 0, &be);
- if (result != REP_PROTOCOL_SUCCESS)
- return (result);
+ assert(result == REP_PROTOCOL_SUCCESS);
result = backend_create_backup_locked(be, name);
backend_unlock(be);
@@ -1093,6 +1137,254 @@ backend_create_backup(const char *name)
return (result);
}
+/*
+ * Copy the repository. If the sw_back flag is not set, we are
+ * copying the repository from the default location under /etc/svc to
+ * the tmpfs /etc/svc/volatile location. If the flag is set, we are
+ * copying back to the /etc/svc location from the volatile location
+ * after manifest-import is completed.
+ *
+ * Can return:
+ *
+ * REP_PROTOCOL_SUCCESS successful copy and rename
+ * REP_PROTOCOL_FAIL_UNKNOWN file operation error
+ * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory
+ */
+static rep_protocol_responseid_t
+backend_switch_copy(const char *src, const char *dst, int sw_back)
+{
+ int srcfd, dstfd;
+ char *tmppath = malloc(PATH_MAX);
+ rep_protocol_responseid_t res = REP_PROTOCOL_SUCCESS;
+ struct stat s_buf;
+ size_t cpsz, sz;
+
+ if (tmppath == NULL) {
+ res = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto out;
+ }
+
+ /*
+ * Create and open the related db files
+ */
+ (void) strlcpy(tmppath, dst, PATH_MAX);
+ sz = strlcat(tmppath, "-XXXXXX", PATH_MAX);
+ assert(sz < PATH_MAX);
+ if (sz >= PATH_MAX) {
+ configd_critical(
+ "Backend copy failed: strlcat %s: overflow\n", tmppath);
+ abort();
+ }
+
+ if ((dstfd = mkstemp(tmppath)) < 0) {
+ configd_critical("Backend copy failed: mkstemp %s: %s\n",
+ tmppath, strerror(errno));
+ res = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto out;
+ }
+
+ if ((srcfd = open(src, O_RDONLY)) < 0) {
+ configd_critical("Backend copy failed: opening %s: %s\n",
+ src, strerror(errno));
+ res = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto errexit;
+ }
+
+ /*
+ * fstat the backend before copy for sanity check.
+ */
+ if (fstat(srcfd, &s_buf) < 0) {
+ configd_critical("Backend copy failed: fstat %s: %s\n",
+ src, strerror(errno));
+ res = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto errexit;
+ }
+
+ if ((res = backend_do_copy(src, srcfd, dst, dstfd, &cpsz)) !=
+ REP_PROTOCOL_SUCCESS)
+ goto errexit;
+
+ if (cpsz != s_buf.st_size) {
+ configd_critical("Backend copy failed: incomplete copy\n");
+ res = REP_PROTOCOL_FAIL_UNKNOWN;
+ goto errexit;
+ }
+
+ /*
+ * Rename tmppath to dst
+ */
+ if (rename(tmppath, dst) < 0) {
+ configd_critical(
+ "Backend copy failed: rename %s to %s: %s\n",
+ tmppath, dst, strerror(errno));
+ res = REP_PROTOCOL_FAIL_UNKNOWN;
+ }
+
+errexit:
+ if (res != REP_PROTOCOL_SUCCESS && unlink(tmppath) < 0)
+ configd_critical(
+ "Backend copy failed: remove %s: %s\n",
+ tmppath, strerror(errno));
+
+ (void) close(srcfd);
+ (void) close(dstfd);
+
+out:
+ free(tmppath);
+ if (sw_back) {
+ if (unlink(src) < 0)
+ configd_critical(
+ "Backend copy failed: remove %s: %s\n",
+ src, strerror(errno));
+ }
+
+ return (res);
+}
+
+/*
+ * Perform sanity check on the repository.
+ * Return 0 if check succeeds or -1 if fails.
+ */
+static int
+backend_switch_check(struct sqlite *be_db, char **errp)
+{
+ struct run_single_int_info info;
+ uint32_t val = -1UL;
+ int r;
+
+ info.rs_out = &val;
+ info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND;
+
+ r = sqlite_exec(be_db,
+ "SELECT schema_version FROM schema_version;",
+ run_single_int_callback, &info, errp);
+
+ if (r == SQLITE_OK &&
+ info.rs_result != REP_PROTOCOL_FAIL_NOT_FOUND &&
+ val == BACKEND_SCHEMA_VERSION)
+ return (0);
+ else
+ return (-1);
+}
+
+/*
+ * Backend switch entry point. It is called to perform the backend copy and
+ * switch from src to dst. First, it blocks all other clients from accessing
+ * the repository by calling backend_lock to lock the repository. Upon
+ * successful lock, copying and switching of the repository are performed.
+ *
+ * Can return:
+ * REP_PROTOCOL_SUCCESS successful switch
+ * REP_PROTOCOL_FAIL_BACKEND_ACCESS backen access fails
+ * REP_PROTOCOL_FAIL_BACKEND_READONLY backend is not writable
+ * REP_PROTOCOL_FAIL_UNKNOWN file operation error
+ * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory
+ */
+rep_protocol_responseid_t
+backend_switch(int sw_back)
+{
+ rep_protocol_responseid_t result;
+ sqlite_backend_t *be;
+ struct sqlite *new;
+ char *errp;
+ const char *dst;
+
+ result = backend_lock(BACKEND_TYPE_NORMAL, 1, &be);
+ if (result != REP_PROTOCOL_SUCCESS)
+ return (result);
+
+ if (sw_back) {
+ dst = REPOSITORY_DB;
+ } else {
+ dst = FAST_REPOSITORY_DB;
+ }
+
+ /*
+ * Do the actual copy and rename
+ */
+ result = backend_switch_copy(be->be_path, dst, sw_back);
+ if (result != REP_PROTOCOL_SUCCESS) {
+ goto errout;
+ }
+
+ /*
+ * Do the backend sanity check and switch
+ */
+ new = sqlite_open(dst, 0600, &errp);
+ if (new != NULL) {
+ /*
+ * Sanity check
+ */
+ if (backend_switch_check(new, &errp) == 0) {
+ free((char *)be->be_path);
+ be->be_path = strdup(dst);
+ if (be->be_path == NULL) {
+ configd_critical(
+ "Backend switch failed: strdup %s: %s\n",
+ dst, strerror(errno));
+ result = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ sqlite_close(new);
+ } else {
+ sqlite_close(be->be_db);
+ be->be_db = new;
+ }
+ } else {
+ configd_critical(
+ "Backend switch failed: integrity check %s: %s\n",
+ dst, errp);
+ result = REP_PROTOCOL_FAIL_BACKEND_ACCESS;
+ }
+ } else {
+ configd_critical("Backend switch failed: sqlite_open %s: %s\n",
+ dst, errp);
+ result = REP_PROTOCOL_FAIL_BACKEND_ACCESS;
+ }
+
+errout:
+ backend_unlock(be);
+ return (result);
+}
+
+/*
+ * This routine is called to attempt the recovery of
+ * the most recent valid repository if possible when configd
+ * is restarted for some reasons or when system crashes
+ * during the switch operation. The repository databases
+ * referenced here are indicators of successful switch
+ * operations.
+ */
+static void
+backend_switch_recovery(void)
+{
+ const char *fast_db = FAST_REPOSITORY_DB;
+ char *errp;
+ struct stat s_buf;
+ struct sqlite *be_db;
+
+
+ /*
+ * A good transient db containing most recent data can
+ * exist if system or svc.configd crashes during the
+ * switch operation. If that is the case, check its
+ * integrity and use it.
+ */
+ if (stat(fast_db, &s_buf) < 0) {
+ return;
+ }
+
+ /*
+ * Do sanity check on the db
+ */
+ be_db = sqlite_open(fast_db, 0600, &errp);
+
+ if (be_db != NULL) {
+ if (backend_switch_check(be_db, &errp) == 0)
+ (void) backend_switch_copy(fast_db, REPOSITORY_DB, 1);
+ }
+
+ (void) unlink(fast_db);
+}
+
/*ARGSUSED*/
static int
backend_integrity_callback(void *private, int narg, char **vals, char **cols)
@@ -1890,6 +2182,14 @@ backend_init(const char *db_file, const char *npdb_file, int have_np)
sqlite_version, SQLITE_VERSION);
return (CONFIGD_EXIT_DATABASE_INIT_FAILED);
}
+
+ /*
+ * If the system crashed during a backend switch, there might
+ * be a leftover transient database which contains useful
+ * information which can be used for recovery.
+ */
+ backend_switch_recovery();
+
if (db_file == NULL)
db_file = REPOSITORY_DB;
if (strcmp(db_file, REPOSITORY_DB) != 0) {
diff --git a/usr/src/cmd/svc/configd/client.c b/usr/src/cmd/svc/configd/client.c
index 2a0b49242d..47a8aefa06 100644
--- a/usr/src/cmd/svc/configd/client.c
+++ b/usr/src/cmd/svc/configd/client.c
@@ -1762,7 +1762,7 @@ client_wait(repcache_client_t *cp, const void *in, size_t insz,
* _UNKNOWN failed for unknown reason (details written to
* console)
* _BACKEND_READONLY backend is not writable
- *
+ * _NO_RESOURCES out of memory
* _SUCCESS Backup completed successfully.
*/
static rep_protocol_responseid_t
@@ -1994,6 +1994,44 @@ start_audit_session(repcache_client_t *cp)
ucred_free(cred);
}
+/*
+ * Handle switch client request
+ *
+ * This routine can return:
+ *
+ * _PERMISSION_DENIED not enough privileges to do request.
+ * _UNKNOWN file operation error (details written to
+ * the console).
+ * _SUCCESS switch operation is completed.
+ * _BACKEND_ACCESS backend access fails.
+ * _NO_RESOURCES out of memory.
+ * _BACKEND_READONLY backend is not writable.
+ */
+static rep_protocol_responseid_t
+repository_switch(repcache_client_t *cp,
+ struct rep_protocol_switch_request *rpr)
+{
+ rep_protocol_responseid_t result;
+ ucred_t *uc = get_ucred();
+
+ if (!client_is_privileged() && (uc == NULL ||
+ ucred_geteuid(uc) != 0)) {
+ return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
+ }
+
+ (void) pthread_mutex_lock(&cp->rc_lock);
+ if (rpr->rpr_changeid != cp->rc_changeid) {
+ if ((result = backend_switch(rpr->rpr_flag)) ==
+ REP_PROTOCOL_SUCCESS)
+ cp->rc_changeid = rpr->rpr_changeid;
+ } else {
+ result = REP_PROTOCOL_SUCCESS;
+ }
+ (void) pthread_mutex_unlock(&cp->rc_lock);
+
+ return (result);
+}
+
typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
const void *rpr);
@@ -2168,6 +2206,9 @@ static struct protocol_entry {
PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION, set_annotation,
struct rep_protocol_annotation),
+ PROTO(REP_PROTOCOL_SWITCH, repository_switch,
+ struct rep_protocol_switch_request),
+
PROTO_END()
};
#undef PROTO
diff --git a/usr/src/cmd/svc/configd/configd.h b/usr/src/cmd/svc/configd/configd.h
index 8d53c15c79..a1fdee0d1d 100644
--- a/usr/src/cmd/svc/configd/configd.h
+++ b/usr/src/cmd/svc/configd/configd.h
@@ -83,12 +83,6 @@ extern int _mutex_held(struct _lwp_mutex_t *);
*/
#define COMPOSITION_DEPTH 2
-/*
- * The default locations of the repository dbs
- */
-#define REPOSITORY_DB "/etc/svc/repository.db"
-#define NONPERSIST_DB "/etc/svc/volatile/svc_nonpersist.db"
-
#define CONFIGD_CORE "core.%f.%t.%p"
#ifndef NDEBUG
@@ -761,6 +755,7 @@ int backend_init(const char *, const char *, int);
void backend_fini(void);
rep_protocol_responseid_t backend_create_backup(const char *);
+rep_protocol_responseid_t backend_switch(int);
/*
* call on any database inconsistency -- cleans up state as best it can,
diff --git a/usr/src/cmd/svc/milestone/manifest-import b/usr/src/cmd/svc/milestone/manifest-import
index 363e410179..07f6ba0328 100644
--- a/usr/src/cmd/svc/milestone/manifest-import
+++ b/usr/src/cmd/svc/milestone/manifest-import
@@ -29,6 +29,8 @@
[ -f /lib/svc/share/smf_include.sh ] || exit 1
+. /lib/svc/share/smf_include.sh
+
activity=false
X=
@@ -445,6 +447,13 @@ if [ -n "$nonsite_manifests" -o -n "$site_manifests" ]; then
echo "Loading smf(5) service descriptions: \c" > /dev/msglog
+ #
+ # Attempt of moving the repository to tmpfs. If that doesn't
+ # work, reset doswitch so we don't attempt switching back.
+ #
+ /usr/sbin/svcadm _smf_repository_switch fast
+ doswitch=$?
+
i=1; n=$#
while [ $# -gt 0 ]; do
printf "%${fwidth}s/%${fwidth}s" $i $n > /dev/msglog
@@ -454,6 +463,21 @@ if [ -n "$nonsite_manifests" -o -n "$site_manifests" ]; then
echo "$backup\c" > /dev/msglog
done
+ #
+ # If switch back fails, exit with the fatal error code.
+ # Normally the failure indicates that there is a serious
+ # problem on the root file system such as file operation
+ # failure or repository access failure.
+ #
+ if [ $doswitch -eq 0 ]; then
+ /usr/sbin/svcadm _smf_repository_switch perm || { \
+ echo "Repository switch back operation failed, \c"
+ echo "please check the system log for the"
+ echo "possible fatal error messages."
+ exit $SMF_EXIT_ERR_FATAL
+ }
+ fi
+
echo > /dev/msglog
echo "Loaded $n smf(5) service descriptions"
activity=true
diff --git a/usr/src/cmd/svc/svcadm/svcadm.c b/usr/src/cmd/svc/svcadm/svcadm.c
index 3e1ccab42c..e9d77475d3 100644
--- a/usr/src/cmd/svc/svcadm/svcadm.c
+++ b/usr/src/cmd/svc/svcadm/svcadm.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -2107,6 +2107,7 @@ main(int argc, char *argv[])
{
int o;
int err;
+ int sw_back;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
@@ -2388,9 +2389,64 @@ main(int argc, char *argv[])
"unknown error (see console for details)";
break;
}
+
uu_warn("failed to backup repository: %s\n", reason);
exit_status = UU_EXIT_FATAL;
}
+ } else if (strcmp(argv[optind], "_smf_repository_switch") == 0) {
+ const char *reason = NULL;
+
+ ++optind;
+
+ /*
+ * Check argument and setup scf_switch structure
+ */
+ if (optind != argc - 1)
+ exit(1);
+
+ if (strcmp(argv[optind], "fast") == 0) {
+ sw_back = 0;
+ } else if (strcmp(argv[optind], "perm") == 0) {
+ sw_back = 1;
+ } else {
+ exit(UU_EXIT_USAGE);
+ }
+
+ /*
+ * Call into switch primitive
+ */
+ if ((err = _scf_repository_switch(h, sw_back)) !=
+ SCF_SUCCESS) {
+ /*
+ * Retrieve per thread SCF error code
+ */
+ switch (scf_error()) {
+ case SCF_ERROR_NOT_BOUND:
+ abort();
+ /* NOTREACHED */
+
+ case SCF_ERROR_CONNECTION_BROKEN:
+ case SCF_ERROR_BACKEND_READONLY:
+ scfdie();
+ /* NOTREACHED */
+
+ case SCF_ERROR_PERMISSION_DENIED:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ reason = scf_strerror(scf_error());
+ break;
+
+ case SCF_ERROR_INTERNAL:
+ reason = "File operation error: (see console)";
+ break;
+
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+
+ uu_warn("failed to switch repository: %s\n", reason);
+ exit_status = UU_EXIT_FATAL;
+ }
} else {
usage();
}
diff --git a/usr/src/common/svc/repcache_protocol.h b/usr/src/common/svc/repcache_protocol.h
index 9f76adb816..58bc356c90 100644
--- a/usr/src/common/svc/repcache_protocol.h
+++ b/usr/src/common/svc/repcache_protocol.h
@@ -359,6 +359,12 @@
* the operation that is being annotated, and file is the file being
* processed. This will be used to mark operations which comprise
* multiple primitive operations such as svccfg import.
+ *
+ * SWITCH(flag) -> result
+ * The flag is used to indicate the direction of the switch operation.
+ * When the flag is set to 'fast', move the main repository from the
+ * default location (/etc/svc) to the tmpfs locationa (/etc/svc/volatile).
+ * When it is set to 'perm', the switch is reversed.
*/
#include <door.h>
@@ -382,7 +388,7 @@ extern "C" {
* This value should be incremented any time the protocol changes. When in
* doubt, bump it.
*/
-#define REPOSITORY_DOOR_VERSION (20 + REPOSITORY_DOOR_BASEVER)
+#define REPOSITORY_DOOR_VERSION (21 + REPOSITORY_DOOR_BASEVER)
/*
* flags for rdr_flags
@@ -478,6 +484,8 @@ enum rep_protocol_requestid {
REP_PROTOCOL_SET_AUDIT_ANNOTATION,
+ REP_PROTOCOL_SWITCH,
+
REP_PROTOCOL_MAX_REQUEST
};
@@ -822,6 +830,12 @@ struct rep_protocol_annotation {
char rpr_file[MAXPATHLEN];
};
+struct rep_protocol_switch_request {
+ enum rep_protocol_requestid rpr_request; /* SWITCH */
+ uint32_t rpr_changeid;
+ int rpr_flag;
+};
+
/*
* Response structures
*/
diff --git a/usr/src/lib/libscf/common/lowlevel.c b/usr/src/lib/libscf/common/lowlevel.c
index a9265f9c1c..4c25197400 100644
--- a/usr/src/lib/libscf/common/lowlevel.c
+++ b/usr/src/lib/libscf/common/lowlevel.c
@@ -6860,6 +6860,55 @@ _scf_request_backup(scf_handle_t *h, const char *name)
return (SCF_SUCCESS);
}
+/*
+ * Request svc.configd daemon to switch repository database.
+ *
+ * Can fail:
+ *
+ * _NOT_BOUND handle is not bound
+ * _CONNECTION_BROKEN server is not reachable
+ * _INTERNAL file operation error
+ * the server response is too big
+ * _PERMISSION_DENIED not enough privileges to do request
+ * _BACKEND_READONLY backend is not writable
+ * _BACKEND_ACCESS backend access fails
+ * _NO_RESOURCES svc.configd is out of memory
+ */
+int
+_scf_repository_switch(scf_handle_t *h, int scf_sw)
+{
+ struct rep_protocol_switch_request request;
+ struct rep_protocol_response response;
+ int r;
+
+ /*
+ * Setup request protocol and make door call
+ * Hold rh_lock lock before handle_next_changeid call
+ */
+ (void) pthread_mutex_lock(&h->rh_lock);
+
+ request.rpr_flag = scf_sw;
+ request.rpr_request = REP_PROTOCOL_SWITCH;
+ request.rpr_changeid = handle_next_changeid(h);
+
+ r = make_door_call(h, &request, sizeof (request),
+ &response, sizeof (response));
+
+ (void) pthread_mutex_unlock(&h->rh_lock);
+
+ if (r < 0) {
+ DOOR_ERRORS_BLOCK(r);
+ }
+
+ /*
+ * Pass protocol error up
+ */
+ if (response.rpr_response != REP_PROTOCOL_SUCCESS)
+ return (scf_set_error(proto_error(response.rpr_response)));
+
+ return (SCF_SUCCESS);
+}
+
int
_scf_pg_is_read_protected(const scf_propertygroup_t *pg, boolean_t *out)
{
diff --git a/usr/src/lib/libscf/common/mapfile-vers b/usr/src/lib/libscf/common/mapfile-vers
index 898e8307ac..315e24d9e8 100644
--- a/usr/src/lib/libscf/common/mapfile-vers
+++ b/usr/src/lib/libscf/common/mapfile-vers
@@ -234,6 +234,7 @@ SUNWprivate_1.1 {
scf_simple_handle_destroy;
gen_filenms_from_fmri;
_scf_pg_is_read_protected;
+ _scf_repository_switch;
local:
*;
};
diff --git a/usr/src/lib/libscf/inc/libscf_priv.h b/usr/src/lib/libscf/inc/libscf_priv.h
index 39c92d20b7..67c966715f 100644
--- a/usr/src/lib/libscf/inc/libscf_priv.h
+++ b/usr/src/lib/libscf/inc/libscf_priv.h
@@ -281,6 +281,14 @@ int scf_set_count_property(scf_transaction_t *, char *, uint64_t, boolean_t);
#define SCF_WALK_NOINSTANCE 0x10
#define SCF_WALK_EXPLICIT 0x20
+/*
+ * The default locations of the repository dbs
+ */
+#define REPOSITORY_DB "/etc/svc/repository.db"
+#define NONPERSIST_DB "/etc/svc/volatile/svc_nonpersist.db"
+#define FAST_REPOSITORY_DB "/etc/svc/volatile/fast_repository.db"
+
+
typedef struct scf_walkinfo {
const char *fmri;
scf_scope_t *scope;
@@ -308,6 +316,11 @@ scf_error_t scf_walk_fmri(scf_handle_t *, int, char **, int,
int _scf_request_backup(scf_handle_t *, const char *);
/*
+ * Repository switch client
+ */
+int _scf_repository_switch(scf_handle_t *, int);
+
+/*
* Determines whether a property group requires authorization to read; this
* does not in any way reflect whether the caller has that authorization.
* To determine that, the caller must attempt to read the value of one of the