backend.c revision c0889d7a91fa87e1cb7ef4457629b0cb51d47b50
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2008 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>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zone.h>
#include <libscf_priv.h>
#include "configd.h"
#include "repcache_protocol.h"
#include <sqlite.h>
#include <sqlite-misc.h>
/*
* This file has two purposes:
*
* 1. It contains the database schema, and the code for setting up our backend
* databases, including installing said schema.
*
* 2. It provides a simplified interface to the SQL database library, and
* synchronizes MT access to the database.
*/
typedef struct backend_spent {
typedef struct backend_totals {
typedef struct sqlite_backend {
const char *be_path; /* path to db */
int be_readonly; /* readonly at start, and still is */
int be_writing; /* held for writing */
struct backend_tx {
int bt_readonly;
int bt_type;
int bt_full; /* SQLITE_FULL during tx */
};
}
struct backend_query {
char *bq_buf;
};
struct backend_tbl_info {
const char *bti_name;
const char *bti_cols;
};
struct backend_idx_info {
const char *bxi_tbl;
const char *bxi_idx;
const char *bxi_cols;
};
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 */
/*
* Any change to the below schema should bump the version number
*/
#define BACKEND_SCHEMA_VERSION 5
/*
* service_tbl holds all services. svc_id is the identifier of the
* service.
*/
{
"service_tbl",
"svc_id INTEGER PRIMARY KEY,"
"svc_name CHAR(256) NOT NULL"
},
/*
* instance_tbl holds all of the instances. The parent service id
* is instance_svc.
*/
{
"instance_tbl",
"instance_id INTEGER PRIMARY KEY,"
"instance_name CHAR(256) NOT NULL,"
"instance_svc INTEGER NOT NULL"
},
/*
* snapshot_lnk_tbl links (instance, snapshot name) with snapshots.
*/
{
"snapshot_lnk_tbl",
"lnk_id INTEGER PRIMARY KEY,"
"lnk_inst_id INTEGER NOT NULL,"
"lnk_snap_name CHAR(256) NOT NULL,"
"lnk_snap_id INTEGER NOT NULL"
},
/*
* snaplevel_tbl maps a snapshot id to a set of named, ordered
* snaplevels.
*/
{
"snaplevel_tbl",
"snap_id INTEGER NOT NULL,"
"snap_level_num INTEGER NOT NULL,"
"snap_level_id INTEGER NOT NULL,"
"snap_level_service_id INTEGER NOT NULL,"
"snap_level_service CHAR(256) NOT NULL,"
"snap_level_instance_id INTEGER NULL,"
"snap_level_instance CHAR(256) NULL"
},
/*
* snaplevel_lnk_tbl links snaplevels to property groups.
* snaplvl_pg_* is identical to the original property group,
* and snaplvl_gen_id overrides the generation number.
*/
{
"snaplevel_lnk_tbl",
"snaplvl_level_id INTEGER NOT NULL,"
"snaplvl_pg_id INTEGER NOT NULL,"
"snaplvl_pg_name CHAR(256) NOT NULL,"
"snaplvl_pg_type CHAR(256) NOT NULL,"
"snaplvl_pg_flags INTEGER NOT NULL,"
"snaplvl_gen_id INTEGER NOT NULL"
},
};
{ "service_tbl", "name", "svc_name" },
{ "instance_tbl", "name", "instance_svc, instance_name" },
{ "snapshot_lnk_tbl", "name", "lnk_inst_id, lnk_snap_name" },
{ "snapshot_lnk_tbl", "snapid", "lnk_snap_id" },
{ "snaplevel_tbl", "id", "snap_id" },
{ "snaplevel_lnk_tbl", "id", "snaplvl_pg_id" },
{ "snaplevel_lnk_tbl", "level", "snaplvl_level_id" },
};
};
};
/*
* pg_tbl defines property groups. They are associated with a single
* service or instance. The pg_gen_id links them with the latest
* "edited" version of its properties.
*/
{
"pg_tbl",
"pg_id INTEGER PRIMARY KEY,"
"pg_parent_id INTEGER NOT NULL,"
"pg_name CHAR(256) NOT NULL,"
"pg_type CHAR(256) NOT NULL,"
"pg_flags INTEGER NOT NULL,"
"pg_gen_id INTEGER NOT NULL"
},
/*
* prop_lnk_tbl links a particular pg_id and gen_id to a set of
* (prop_name, prop_type, val_id) trios.
*/
{
"prop_lnk_tbl",
"lnk_prop_id INTEGER PRIMARY KEY,"
"lnk_pg_id INTEGER NOT NULL,"
"lnk_gen_id INTEGER NOT NULL,"
"lnk_prop_name CHAR(256) NOT NULL,"
"lnk_prop_type CHAR(2) NOT NULL,"
"lnk_val_id INTEGER"
},
/*
* value_tbl maps a value_id to a set of values. For any given
* value_id, value_type is constant.
*/
{
"value_tbl",
"value_id INTEGER NOT NULL,"
"value_type CHAR(1) NOT NULL,"
"value_value VARCHAR NOT NULL"
},
/*
* id_tbl has one row per id space
*/
{
"id_tbl",
"id_name STRING NOT NULL,"
"id_next INTEGER NOT NULL"
},
/*
* schema_version has a single row, which contains
* BACKEND_SCHEMA_VERSION at the time of creation.
*/
{
"schema_version",
"schema_version INTEGER"
},
};
{ "pg_tbl", "parent", "pg_parent_id" },
{ "pg_tbl", "name", "pg_parent_id, pg_name" },
{ "pg_tbl", "type", "pg_parent_id, pg_type" },
{ "prop_lnk_tbl", "base", "lnk_pg_id, lnk_gen_id" },
{ "prop_lnk_tbl", "val", "lnk_val_id" },
{ "value_tbl", "id", "value_id" },
{ "id_tbl", "id", "id_name" },
};
struct run_single_int_info {
int rs_result;
};
/*ARGSUSED*/
static int
{
return (BACKEND_CALLBACK_CONTINUE);
errno = 0;
return (BACKEND_CALLBACK_CONTINUE);
}
/*ARGSUSED*/
int
{
return (BACKEND_CALLBACK_ABORT);
}
/*
* check to see if we can successfully start a transaction; if not, the
* filesystem is mounted read-only.
*/
static int
{
int r;
return (SQLITE_READONLY);
r = sqlite_exec(db,
"BEGIN TRANSACTION; "
"UPDATE schema_version SET schema_version = schema_version; ",
return (r);
}
static void
{
if (backend_print_trace) {
}
}
/*
* backend_panic() -- some kind of database problem or corruption has been hit.
* We attempt to quiesce the other database users -- all of the backend sql
* entry points will call backend_panic(NULL) if a panic is in progress, as
* will any attempt to start a transaction.
*
* We give threads holding a backend lock 50ms (BACKEND_PANIC_TIMEOUT) to
* either drop the lock or call backend_panic(). If they don't respond in
* time, we'll just exit anyway.
*/
void
backend_panic(const char *format, ...)
{
int i;
int failed = 0;
(void) pthread_mutex_lock(&backend_panic_lock);
if (backend_panic_thread != 0) {
(void) pthread_mutex_unlock(&backend_panic_lock);
/*
* first, drop any backend locks we're holding, then
* sleep forever on the panic_cv.
*/
for (i = 0; i < BACKEND_TYPE_TOTAL; i++) {
}
(void) pthread_mutex_lock(&backend_panic_lock);
for (;;)
(void) pthread_cond_wait(&backend_panic_cv,
}
(void) pthread_mutex_unlock(&backend_panic_lock);
for (i = 0; i < BACKEND_TYPE_TOTAL; i++) {
}
for (i = 0; i < BACKEND_TYPE_TOTAL; i++) {
&rel) != 0)
failed++;
}
}
if (failed) {
configd_critical("unable to quiesce database\n");
}
if (backend_panic_abort)
abort();
}
/*
* Returns
* _SUCCESS
* _DONE - callback aborted query
* _NO_RESOURCES - out of memory (_FULL & _TOOBIG?)
*/
static int
{
return (REP_PROTOCOL_SUCCESS);
switch (error) {
case SQLITE_ABORT:
return (REP_PROTOCOL_DONE);
case SQLITE_NOMEM:
case SQLITE_FULL:
case SQLITE_TOOBIG:
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
default:
/*NOTREACHED*/
}
}
static void
{
while (out_sz-- > 0)
}
/*
* builds a inverse-time-sorted array of backup files. The path is a
* a single buffer, and the pointers look like:
*
* ^pathname ^ ^(pathname+pathlen)
* basename
*
* dirname will either be pathname, or ".".
*
* Returns the number of elements in the array, 0 if there are no previous
* backups, or -1 on error.
*/
static ssize_t
{
char *name, *p;
char *pathend;
/*
* year, month, day, hour, min, sec, plus an '_'.
*/
*pathend = '\0';
basename++;
} else {
dirname = ".";
}
/*
* munge the string temporarily for the opendir(), then restore it.
*/
basename[0] = '\0';
goto fail;
/*
* Must match:
* basename-YYYYMMDD_HHMMSS
* or we ignore it.
*/
continue;
continue;
char c = p[idx];
break;
break;
}
continue;
/*
* We have a match. insertion-sort it into our list.
*/
goto fail_closedir;
if (cmp == 0)
if (cmp == 0) {
break;
} else if (cmp > 0) {
p = tp;
}
}
goto fail_closedir;
}
} else {
}
}
return (count);
fail:
return (-1);
}
/*
* Copies the repository path into out, a buffer of out_len bytes,
* removes the ".db" (or whatever) extension, and, if name is non-NULL,
* appends "-name" to it. If name is non-NULL, it can fail with:
*
* _TRUNCATED will not fit in buffer.
* _BAD_REQUEST name is not a valid identifier
*/
static rep_protocol_responseid_t
{
char *p, *q;
/*
* '.'.
*/
*q = 0;
/*
* verify that the name tag is entirely alphabetic,
* non-empty, and not too long.
*/
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_FAIL_TRUNCATED);
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* See if a backup is needed. We do a backup unless both files are
* byte-for-byte identical.
*/
static int
{
goto fail;
goto fail;
/*
* if they are the same file, we need to do a backup to break the
* hard link or symlink involved.
*/
goto fail;
goto fail;
goto fail;
do {
goto fail;
return (0);
}
fail:
if (repfd >= 0)
if (fd >= 0)
return (1);
}
/*
* This interface is called to perform the actual copy
*
* Return:
* _FAIL_NO_RESOURCES out of memory
* _SUCCESS copy succeeds
*/
static rep_protocol_responseid_t
{
char *buf;
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
if (nrd < 0) {
continue;
"Backend copy failed: fails to read from %s "
return (REP_PROTOCOL_FAIL_UNKNOWN);
}
nwr = 0;
do {
continue;
"Backend copy failed: fails to write to %s "
return (REP_PROTOCOL_FAIL_UNKNOWN);
}
nwr += n;
w_off += n;
}
if (sz)
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
{
const char **old_list;
char *finalname;
char *finalpath;
char *tmppath;
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
if (be->be_readonly) {
goto out;
}
if (result != REP_PROTOCOL_SUCCESS)
goto out;
goto out;
}
/*
* remember the original length, and the basename location
*/
finalname++;
else
goto out;
}
"\"%s\" backup failed: localtime(3C) failed: %s\n", name,
goto out;
}
goto out;
}
if (infd < 0) {
goto out;
}
if (outfd < 0) {
configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n",
goto out;
}
goto fail;
/*
* grab the old list before doing our re-name.
*/
if (old_max > 0)
"\"%s\" backup failed: rename(%s, %s): %s\n",
goto fail;
}
"\"%s\" backup completed, but updating "
"\"%s\" symlink to \"%s\" failed: %s\n",
}
/* unlink all but the first (old_max - 1) files */
"\"%s\" backup completed, but removing old "
"file \"%s\" failed: %s\n",
}
}
fail:
if (result != REP_PROTOCOL_SUCCESS)
out:
return (result);
}
static int
{
char *errp;
int r;
/*
* If we don't *need* to be writable, only check every once in a
* while.
*/
if (!writing) {
return (REP_PROTOCOL_SUCCESS);
be->be_lastcheck = t;
}
/*NOTREACHED*/
}
if (r != SQLITE_OK) {
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.
*/
be->be_readonly = 0;
"unable to create \"%s\" backup of \"%s\"\n",
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* If t is not BACKEND_TYPE_NORMAL, can fail with
* _BACKEND_ACCESS - backend does not exist
*
* If writing is nonzero, can also fail with
* _BACKEND_READONLY - backend is read-only
*/
static int
{
assert(t == BACKEND_TYPE_NORMAL ||
t == BACKEND_TYPE_NONPERSIST);
if (t == BACKEND_TYPE_NORMAL)
return (REP_PROTOCOL_FAIL_BACKEND_ACCESS);
if (backend_panic_thread != 0)
vts = gethrvtime();
if (backend_panic_thread != 0) {
}
if (be->be_readonly) {
int r;
assert(t == BACKEND_TYPE_NORMAL);
if (r != REP_PROTOCOL_SUCCESS) {
return (r);
}
}
if (backend_do_trace)
else
return (REP_PROTOCOL_SUCCESS);
}
static void
{
be->be_writing = 0;
}
static void
{
}
}
static void
{
}
static int
{
int written;
while (len > 0) {
return (-1);
}
return (0);
}
/*
* 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.
*/
backend_create_backup(const char *name)
{
return (result);
}
/*
* Copy the repository. If the sw_back flag is not set, we are
* 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
{
goto out;
}
/*
* Create and open the related db files
*/
"Backend copy failed: strlcat %s: overflow\n", tmppath);
abort();
}
configd_critical("Backend copy failed: mkstemp %s: %s\n",
goto out;
}
configd_critical("Backend copy failed: opening %s: %s\n",
goto errexit;
}
/*
* fstat the backend before copy for sanity check.
*/
configd_critical("Backend copy failed: fstat %s: %s\n",
goto errexit;
}
goto errexit;
configd_critical("Backend copy failed: incomplete copy\n");
goto errexit;
}
/*
* Rename tmppath to dst
*/
"Backend copy failed: rename %s to %s: %s\n",
}
"Backend copy failed: remove %s: %s\n",
out:
if (sw_back) {
"Backend copy failed: remove %s: %s\n",
}
return (res);
}
/*
* Perform sanity check on the repository.
* Return 0 if check succeeds or -1 if fails.
*/
static int
{
struct run_single_int_info info;
int r;
r = sqlite_exec(be_db,
"SELECT schema_version FROM schema_version;",
if (r == SQLITE_OK &&
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
*/
backend_switch(int sw_back)
{
char *errp;
const char *dst;
if (result != REP_PROTOCOL_SUCCESS)
return (result);
if (sw_back) {
dst = REPOSITORY_DB;
} else {
}
/*
* Do the actual copy and rename
*/
if (result != REP_PROTOCOL_SUCCESS) {
goto errout;
}
/*
* Do the backend sanity check and switch
*/
/*
* Sanity check
*/
"Backend switch failed: strdup %s: %s\n",
} else {
}
} else {
"Backend switch failed: integrity check %s: %s\n",
}
} else {
configd_critical("Backend switch failed: sqlite_open %s: %s\n",
}
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;
/*
* 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.
*/
return;
}
/*
* Do sanity check on the db
*/
}
}
/*ARGSUSED*/
static int
{
char *new;
const char *info;
int x;
for (x = 0; x < narg; x++) {
return (BACKEND_CALLBACK_ABORT);
new[0] = 0;
}
}
return (BACKEND_CALLBACK_CONTINUE);
}
#define BACKEND_CREATE_LOCKED -2
#define BACKEND_CREATE_FAIL -1
#define BACKEND_CREATE_SUCCESS 0
#define BACKEND_CREATE_READONLY 1
#define BACKEND_CREATE_NEED_INIT 2
static int
{
char *errp;
char *integrity_results = NULL;
int r;
struct run_single_int_info info;
int fd;
perror("malloc");
goto fail;
}
goto fail;
}
/* report it as an integrity failure */
goto integrity_fail;
}
/*
* check if we are inited and of the correct schema version
*
* Eventually, we'll support schema upgrade here.
*/
if (r == SQLITE_ERROR &&
/*
* Could be an empty repository, could be pre-schema_version
* schema. Check for id_tbl, which has always been there.
*/
if (r == SQLITE_ERROR &&
return (BACKEND_CREATE_NEED_INIT);
}
goto fail;
}
if (r == SQLITE_BUSY || r == SQLITE_LOCKED) {
return (BACKEND_CREATE_LOCKED);
}
if (r == SQLITE_OK) {
val != BACKEND_SCHEMA_VERSION) {
configd_critical("%s: schema version mismatch\n",
db_file);
goto fail;
}
}
/*
* pull in the whole database sequentially.
*/
;
}
}
/*
* run an integrity check
*/
if (r == SQLITE_BUSY || r == SQLITE_LOCKED) {
return (BACKEND_CREATE_LOCKED);
}
if (r == SQLITE_ABORT) {
integrity_results = "out of memory running integrity check\n";
}
if (integrity_results != NULL) {
} else {
": PRAGMA integrity_check; failed. Results:\n") <
}
}
if (!is_main_repository ||
"%s: integrity check failed. Details in "
else
"%s: integrity check failed.\n",
db_file);
} else {
"\n"
"svc.configd: smf(5) database integrity check of:\n"
"\n"
" %s\n"
"\n"
" failed. The database might be damaged or a media error might have\n"
" prevented it from being verified. Additional information useful to\n"
" your service provider%s%s\n"
"\n"
" The system will not be able to boot until you have restored a working\n"
" database. svc.startd(1M) will provide a sulogin(1M) prompt for recovery\n"
" purposes. The command:\n"
"\n"
" /lib/svc/bin/restore_repository\n"
"\n"
" can be run to restore a backup version of your repository. See\n"
" http://sun.com/msg/SMF-8000-MY for more information.\n"
"\n",
}
goto fail;
}
/*
* check if we are writable
*/
if (r == SQLITE_BUSY || r == SQLITE_LOCKED) {
return (BACKEND_CREATE_LOCKED);
}
if (r != SQLITE_OK && r != SQLITE_FULL) {
return (BACKEND_CREATE_READONLY);
}
return (BACKEND_CREATE_SUCCESS);
fail:
return (BACKEND_CREATE_FAIL);
}
/*
* (arg & -arg) is, through the magic of twos-complement arithmetic, the
* lowest set bit in arg.
*/
static size_t
{
/*
* Don't allow a zero result.
*/
return (arg);
}
/*
* Returns
* _NO_RESOURCES - out of memory
* _BACKEND_ACCESS - backend type t (other than _NORMAL) doesn't exist
* _DONE - callback aborted query
* _SUCCESS
*/
int
{
int ret;
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
return (ret);
vts = gethrvtime();
return (ret);
}
/*
* Starts a "read-only" transaction -- i.e., locks out writers as long
* as it is active.
*
* Fails with
* _NO_RESOURCES - out of memory
*
* If t is not _NORMAL, can also fail with
* _BACKEND_ACCESS - backend does not exist
*
* If writable is true, can also fail with
* _BACKEND_READONLY
*/
static int
{
int r;
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
return (r);
}
return (REP_PROTOCOL_SUCCESS);
}
int
{
return (backend_tx_begin_common(t, txp, 0));
}
static void
{
/*
* sqlite tends to be sticky with SQLITE_FULL, so we try
* to get a fresh database handle if we got a FULL warning
* along the way. If that fails, no harm done.
*/
}
}
}
void
{
}
/*
* Fails with
* _NO_RESOURCES - out of memory
* _BACKEND_ACCESS
* _BACKEND_READONLY
*/
int
{
int r;
char *errmsg;
if (r != REP_PROTOCOL_SUCCESS)
return (r);
vts = gethrvtime();
&errmsg);
if (r == SQLITE_FULL)
if (r != REP_PROTOCOL_SUCCESS) {
assert(r != REP_PROTOCOL_DONE);
return (r);
}
(*txp)->bt_readonly = 0;
return (REP_PROTOCOL_SUCCESS);
}
void
{
int r;
char *errmsg;
vts = gethrvtime();
&errmsg);
if (r == SQLITE_FULL)
}
/*
* Fails with
* _NO_RESOURCES - out of memory
*/
int
{
int r, r2;
char *errmsg;
vts = gethrvtime();
&errmsg);
if (r == SQLITE_FULL)
assert(r != REP_PROTOCOL_DONE);
if (r != REP_PROTOCOL_SUCCESS) {
&errmsg);
if (r2 != REP_PROTOCOL_SUCCESS)
backend_panic("cannot rollback failed commit");
return (r);
}
return (REP_PROTOCOL_SUCCESS);
}
static const char *
{
switch (id) {
return ("SI");
case BACKEND_ID_PROPERTYGRP:
return ("PG");
case BACKEND_ID_GENERATION:
return ("GEN");
case BACKEND_ID_PROPERTY:
return ("PROP");
case BACKEND_ID_VALUE:
return ("VAL");
case BACKEND_ID_SNAPNAME:
return ("SNAME");
case BACKEND_ID_SNAPSHOT:
return ("SHOT");
case BACKEND_ID_SNAPLEVEL:
return ("SLVL");
default:
abort();
/*NOTREACHED*/
}
}
/*
* Returns a new id or 0 if the id argument is invalid or the query fails.
*/
{
struct run_single_int_info info;
char *errmsg;
int ret;
vts = gethrvtime();
"SELECT id_next FROM id_tbl WHERE (id_name = '%q');"
"UPDATE id_tbl SET id_next = id_next + 1 WHERE (id_name = '%q');",
if (ret == SQLITE_FULL)
if (ret != REP_PROTOCOL_SUCCESS) {
return (0);
}
return (new_id);
}
/*
* Returns
* _NO_RESOURCES - out of memory
* _DONE - callback aborted query
* _SUCCESS
*/
int
{
int ret;
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
vts = gethrvtime();
if (ret == SQLITE_FULL)
return (ret);
}
/*
* Returns
* _NO_RESOURCES - out of memory
* _NOT_FOUND - the query returned no results
* _SUCCESS - the query returned a single integer
*/
int
{
struct run_single_int_info info;
int ret;
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
}
/*
* Fails with
* _NO_RESOURCES - out of memory
*/
int
{
va_list a;
char *errmsg;
int ret;
vts = gethrvtime();
if (ret == SQLITE_FULL)
va_end(a);
return (ret);
}
/*
* returns REP_PROTOCOL_FAIL_NOT_FOUND if no changes occured
*/
int
{
va_list a;
char *errmsg;
int ret;
vts = gethrvtime();
if (ret == SQLITE_FULL)
va_end(a);
return (ret);
}
static int
{
int i;
char *errmsg;
int ret;
/*
* Create the tables.
*/
for (i = 0; i < tbl_count; i++) {
break;
}
"CREATE TABLE %s (%s);\n",
"%s: %s table creation fails: %s\n", file,
return (-1);
}
}
/*
* Make indices on key tables and columns.
*/
for (i = 0; i < idx_count; i++) {
break;
}
"CREATE INDEX %s_%s ON %s (%s);\n",
"%s: %s_%s index creation fails: %s\n", file,
return (-1);
}
}
return (0);
}
static int
{
int i;
char *errmsg;
int ret;
if (t == BACKEND_TYPE_NORMAL) {
} else if (t == BACKEND_TYPE_NONPERSIST) {
} else {
abort(); /* can't happen */
}
if (ret < 0) {
return (ret);
}
if (ret < 0) {
return (ret);
}
/*
* Add the schema version to the table
*/
"INSERT INTO schema_version (schema_version) VALUES (%d)",
"setting schema version fails: %s\n", errmsg);
}
/*
* Populate id_tbl with initial IDs.
*/
for (i = 0; i < BACKEND_ID_INVALID; i++) {
const char *name = id_space_to_name(i);
"INSERT INTO id_tbl (id_name, id_next) "
return (-1);
}
}
/*
* Set the persistance of the database. The normal database is marked
* "synchronous", so that all writes are synchronized to stable storage
* before proceeding.
*/
"PRAGMA default_synchronous = %s; PRAGMA synchronous = %s;",
return (-1);
}
return (0);
}
int
{
int r;
int writable_persist = 1;
/* set up our temporary directory */
sqlite_temp_directory = "/etc/svc/volatile";
configd_critical("Mismatched link! (%s should be %s)\n",
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.
*/
is_main_repository = 0;
}
switch (r) {
case BACKEND_CREATE_FAIL:
return (CONFIGD_EXIT_DATABASE_INIT_FAILED);
case BACKEND_CREATE_LOCKED:
return (CONFIGD_EXIT_DATABASE_LOCKED);
case BACKEND_CREATE_SUCCESS:
break; /* success */
case BACKEND_CREATE_READONLY:
writable_persist = 0;
break;
case BACKEND_CREATE_NEED_INIT:
return (CONFIGD_EXIT_DATABASE_INIT_FAILED);
}
break;
default:
abort();
/*NOTREACHED*/
}
if (have_np) {
switch (r) {
case BACKEND_CREATE_SUCCESS:
break; /* success */
case BACKEND_CREATE_FAIL:
return (CONFIGD_EXIT_DATABASE_INIT_FAILED);
case BACKEND_CREATE_LOCKED:
return (CONFIGD_EXIT_DATABASE_LOCKED);
case BACKEND_CREATE_READONLY:
return (CONFIGD_EXIT_DATABASE_INIT_FAILED);
case BACKEND_CREATE_NEED_INIT:
return (CONFIGD_EXIT_DATABASE_INIT_FAILED);
}
break;
default:
abort();
/*NOTREACHED*/
}
/*
* 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.
*/
if (r == BACKEND_CREATE_NEED_INIT && writable_persist &&
"unable to create \"%s\" backup of "
"\"%s\"\n", REPOSITORY_BOOT_BACKUP,
}
}
}
return (CONFIGD_EXIT_OKAY);
}
/*
* quiesce all database activity prior to exiting
*/
void
backend_fini(void)
{
}
#define QUERY_BASE 128
backend_query_alloc(void)
{
backend_query_t *q;
if (q != NULL) {
q->bq_size = QUERY_BASE;
q->bq_size = 0;
}
}
return (q);
}
void
{
char *alloc;
int count;
if (q == NULL) {
/* We'll discover the error when we try to run the query. */
return;
}
break; /* success */
break; /* can't grow */
}
}
}
void
{
char *new;
return;
return;
}
backend_query_append(q, new);
}
void
{
if (q != NULL) {
}
free(q);
}
}