diff options
Diffstat (limited to 'usr/src/cmd/svc/configd/backend.c')
-rw-r--r-- | usr/src/cmd/svc/configd/backend.c | 132 |
1 files changed, 126 insertions, 6 deletions
diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c index 3354b2faee..3c07f41845 100644 --- a/usr/src/cmd/svc/configd/backend.c +++ b/usr/src/cmd/svc/configd/backend.c @@ -135,7 +135,12 @@ int backend_panic_abort = 0; /* abort when panicking */ #define BACKEND_READONLY_CHECK_INTERVAL (2 * (hrtime_t)NANOSEC) /* - * Any change to the below schema should bump the version number + * Any incompatible change to the below schema should bump the version number. + * The schema has been changed to support value ordering, but this change + * is backwards-compatible - i.e. a previous svc.configd can use a + * repository database with the new schema perfectly well. As a result, + * the schema version has not been updated, allowing downgrade of systems + * without losing repository data. */ #define BACKEND_SCHEMA_VERSION 5 @@ -257,13 +262,15 @@ static struct backend_tbl_info tbls_common[] = { /* all backend types */ /* * value_tbl maps a value_id to a set of values. For any given - * value_id, value_type is constant. + * value_id, value_type is constant. The table definition here + * is repeated in backend_check_upgrade(), and must be kept in-sync. */ { "value_tbl", "value_id INTEGER NOT NULL," "value_type CHAR(1) NOT NULL," - "value_value VARCHAR NOT NULL" + "value_value VARCHAR NOT NULL," + "value_order INTEGER DEFAULT 0" }, /* @@ -286,6 +293,10 @@ static struct backend_tbl_info tbls_common[] = { /* all backend types */ { NULL, NULL } }; +/* + * The indexing of value_tbl is repeated in backend_check_upgrade() and + * must be kept in sync with the indexing specification here. + */ static struct backend_idx_info idxs_common[] = { /* all backend types */ { "pg_tbl", "parent", "pg_parent_id" }, { "pg_tbl", "name", "pg_parent_id, pg_name" }, @@ -368,6 +379,29 @@ backend_trace_sql(void *arg, const char *sql) static sqlite_backend_t be_info[BACKEND_TYPE_TOTAL]; static sqlite_backend_t *bes[BACKEND_TYPE_TOTAL]; +/* + * For a native build, repositories are created from scratch, so upgrade + * is not an issue. This variable is implicitly protected by + * bes[BACKEND_TYPE_NORMAL]->be_lock. + */ +#ifdef NATIVE_BUILD +static boolean_t be_normal_upgraded = B_TRUE; +#else +static boolean_t be_normal_upgraded = B_FALSE; +#endif /* NATIVE_BUILD */ + +/* + * Has backend been upgraded? In nonpersistent case, answer is always + * yes. + */ +boolean_t +backend_is_upgraded(backend_tx_t *bt) +{ + if (bt->bt_type == BACKEND_TYPE_NONPERSIST) + return (B_TRUE); + return (be_normal_upgraded); +} + #define BACKEND_PANIC_TIMEOUT (50 * MILLISEC) /* * backend_panic() -- some kind of database problem or corruption has been hit. @@ -953,6 +987,75 @@ out: return (result); } +/* + * Check if value_tbl has been upgraded in the main database, and + * if not (if the value_order column is not present), and do_upgrade is true, + * upgrade value_tbl in repository to contain the additional value_order + * column. The version of sqlite used means ALTER TABLE is not + * available, so we cannot simply use "ALTER TABLE value_tbl ADD COLUMN". + * Rather we need to create a temporary table with the additional column, + * import the value_tbl, drop the original value_tbl, recreate the value_tbl + * with the additional column, import the values from value_tbl_tmp, + * reindex and finally drop value_tbl_tmp. During boot, we wish to check + * if the repository has been upgraded before it is writable, so that + * property value retrieval can use the appropriate form of the SELECT + * statement that retrieves property values. As a result, we need to check + * if the repository has been upgraded prior to the point when we can + * actually carry out the update. + */ +void +backend_check_upgrade(sqlite_backend_t *be, boolean_t do_upgrade) +{ + char *errp; + int r; + + if (be_normal_upgraded) + return; + /* + * Test if upgrade is needed. If value_order column does not exist, + * we need to upgrade the schema. + */ + r = sqlite_exec(be->be_db, "SELECT value_order FROM value_tbl LIMIT 1;", + NULL, NULL, NULL); + if (r == SQLITE_ERROR && do_upgrade) { + /* No value_order column - needs upgrade */ + configd_info("Upgrading SMF repository format..."); + r = sqlite_exec(be->be_db, + "BEGIN TRANSACTION; " + "CREATE TABLE value_tbl_tmp ( " + "value_id INTEGER NOT NULL, " + "value_type CHAR(1) NOT NULL, " + "value_value VARCHAR NOT NULL, " + "value_order INTEGER DEFAULT 0); " + "INSERT INTO value_tbl_tmp " + "(value_id, value_type, value_value) " + "SELECT value_id, value_type, value_value FROM value_tbl; " + "DROP TABLE value_tbl; " + "CREATE TABLE value_tbl( " + "value_id INTEGER NOT NULL, " + "value_type CHAR(1) NOT NULL, " + "value_value VARCHAR NOT NULL, " + "value_order INTEGER DEFAULT 0); " + "INSERT INTO value_tbl SELECT * FROM value_tbl_tmp; " + "CREATE INDEX value_tbl_id ON value_tbl (value_id); " + "DROP TABLE value_tbl_tmp; " + "COMMIT TRANSACTION; " + "VACUUM; ", + NULL, NULL, &errp); + if (r == SQLITE_OK) { + configd_info("SMF repository upgrade is complete."); + } else { + backend_panic("%s: repository upgrade failed: %s", + be->be_path, errp); + /* NOTREACHED */ + } + } + if (r == SQLITE_OK) + be_normal_upgraded = B_TRUE; + else + be_normal_upgraded = B_FALSE; +} + static int backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) { @@ -990,12 +1093,15 @@ backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) /* * We can write! Swap the db handles, mark ourself writable, - * and make a backup. + * upgrade if necessary, and make a backup. */ sqlite_close(be->be_db); be->be_db = new; be->be_readonly = 0; + if (be->be_type == BACKEND_TYPE_NORMAL) + backend_check_upgrade(be, B_TRUE); + if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != REP_PROTOCOL_SUCCESS) { configd_critical( @@ -1435,6 +1541,7 @@ backend_create(backend_type_t backend_id, const char *db_file, assert(backend_id >= 0 && backend_id < BACKEND_TYPE_TOTAL); be = &be_info[backend_id]; + assert(be->be_db == NULL); (void) pthread_mutex_init(&be->be_lock, NULL); @@ -1466,7 +1573,6 @@ backend_create(backend_type_t backend_id, const char *db_file, /* * check if we are inited and of the correct schema version * - * Eventually, we'll support schema upgrade here. */ info.rs_out = &val; info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; @@ -1598,6 +1704,17 @@ integrity_fail: } /* + * Simply do check if backend has been upgraded. We do not wish + * to actually carry out upgrade here - the main repository may + * not be writable at this point. Actual upgrade is carried out + * via backend_check_readonly(). This check is done so that + * we determine repository state - upgraded or not - and then + * the appropriate SELECT statement (value-ordered or not) + * can be used when retrieving property values early in boot. + */ + if (backend_id == BACKEND_TYPE_NORMAL) + backend_check_upgrade(be, B_FALSE); + /* * check if we are writable */ r = backend_is_readonly(be->be_db, be->be_path); @@ -1614,6 +1731,7 @@ integrity_fail: *bep = be; return (BACKEND_CREATE_READONLY); } + *bep = be; return (BACKEND_CREATE_SUCCESS); @@ -2250,7 +2368,8 @@ backend_init(const char *db_file, const char *npdb_file, int have_np) /* * If we started up with a writable filesystem, but the * non-persistent database needed initialization, we - * are booting a non-global zone, so do a backup. + * are booting a non-global zone, so do a backup, and carry + * out upgrade if necessary. */ if (r == BACKEND_CREATE_NEED_INIT && writable_persist && backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == @@ -2262,6 +2381,7 @@ backend_init(const char *db_file, const char *npdb_file, int have_np) "\"%s\"\n", REPOSITORY_BOOT_BACKUP, be->be_path); } + backend_check_upgrade(be, B_TRUE); backend_unlock(be); } } |