10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54520Snw141292 * Common Development and Distribution License (the "License"). 64520Snw141292 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 224520Snw141292 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 28407Sjwadams /* 29407Sjwadams * sqlite is not compatible with _FILE_OFFSET_BITS=64, but we need to 30407Sjwadams * be able to statvfs(2) possibly large systems. This define gives us 31407Sjwadams * access to the transitional interfaces. See lfcompile64(5) for how 32407Sjwadams * _LARGEFILE64_SOURCE works. 33407Sjwadams */ 34407Sjwadams #define _LARGEFILE64_SOURCE 35407Sjwadams 360Sstevel@tonic-gate #include <assert.h> 370Sstevel@tonic-gate #include <door.h> 380Sstevel@tonic-gate #include <dirent.h> 390Sstevel@tonic-gate #include <errno.h> 400Sstevel@tonic-gate #include <fcntl.h> 410Sstevel@tonic-gate #include <limits.h> 420Sstevel@tonic-gate #include <pthread.h> 430Sstevel@tonic-gate #include <stdarg.h> 440Sstevel@tonic-gate #include <stdio.h> 450Sstevel@tonic-gate #include <stdlib.h> 460Sstevel@tonic-gate #include <string.h> 47407Sjwadams #include <sys/stat.h> 48407Sjwadams #include <sys/statvfs.h> 490Sstevel@tonic-gate #include <unistd.h> 500Sstevel@tonic-gate #include <zone.h> 510Sstevel@tonic-gate 520Sstevel@tonic-gate #include "configd.h" 530Sstevel@tonic-gate #include "repcache_protocol.h" 540Sstevel@tonic-gate 554520Snw141292 #include <sqlite.h> 564520Snw141292 #include <sqlite-misc.h> 570Sstevel@tonic-gate 580Sstevel@tonic-gate /* 590Sstevel@tonic-gate * This file has two purposes: 600Sstevel@tonic-gate * 610Sstevel@tonic-gate * 1. It contains the database schema, and the code for setting up our backend 620Sstevel@tonic-gate * databases, including installing said schema. 630Sstevel@tonic-gate * 640Sstevel@tonic-gate * 2. It provides a simplified interface to the SQL database library, and 650Sstevel@tonic-gate * synchronizes MT access to the database. 660Sstevel@tonic-gate */ 670Sstevel@tonic-gate 680Sstevel@tonic-gate typedef struct backend_spent { 690Sstevel@tonic-gate uint64_t bs_count; 700Sstevel@tonic-gate hrtime_t bs_time; 710Sstevel@tonic-gate hrtime_t bs_vtime; 720Sstevel@tonic-gate } backend_spent_t; 730Sstevel@tonic-gate 740Sstevel@tonic-gate typedef struct backend_totals { 750Sstevel@tonic-gate backend_spent_t bt_lock; /* waiting for lock */ 760Sstevel@tonic-gate backend_spent_t bt_exec; /* time spent executing SQL */ 770Sstevel@tonic-gate } backend_totals_t; 780Sstevel@tonic-gate 790Sstevel@tonic-gate typedef struct sqlite_backend { 800Sstevel@tonic-gate pthread_mutex_t be_lock; 810Sstevel@tonic-gate pthread_t be_thread; /* thread holding lock */ 820Sstevel@tonic-gate struct sqlite *be_db; 830Sstevel@tonic-gate const char *be_path; /* path to db */ 84407Sjwadams int be_readonly; /* readonly at start, and still is */ 850Sstevel@tonic-gate int be_writing; /* held for writing */ 860Sstevel@tonic-gate backend_type_t be_type; /* type of db */ 87407Sjwadams hrtime_t be_lastcheck; /* time of last read-only check */ 880Sstevel@tonic-gate backend_totals_t be_totals[2]; /* one for reading, one for writing */ 890Sstevel@tonic-gate } sqlite_backend_t; 900Sstevel@tonic-gate 910Sstevel@tonic-gate struct backend_tx { 920Sstevel@tonic-gate sqlite_backend_t *bt_be; 930Sstevel@tonic-gate int bt_readonly; 940Sstevel@tonic-gate int bt_type; 950Sstevel@tonic-gate int bt_full; /* SQLITE_FULL during tx */ 960Sstevel@tonic-gate }; 970Sstevel@tonic-gate 980Sstevel@tonic-gate #define UPDATE_TOTALS_WR(sb, writing, field, ts, vts) { \ 990Sstevel@tonic-gate backend_spent_t *__bsp = &(sb)->be_totals[!!(writing)].field; \ 1000Sstevel@tonic-gate __bsp->bs_count++; \ 1010Sstevel@tonic-gate __bsp->bs_time += (gethrtime() - ts); \ 1020Sstevel@tonic-gate __bsp->bs_vtime += (gethrvtime() - vts); \ 1030Sstevel@tonic-gate } 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate #define UPDATE_TOTALS(sb, field, ts, vts) \ 1060Sstevel@tonic-gate UPDATE_TOTALS_WR(sb, (sb)->be_writing, field, ts, vts) 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate struct backend_query { 1090Sstevel@tonic-gate char *bq_buf; 1100Sstevel@tonic-gate size_t bq_size; 1110Sstevel@tonic-gate }; 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate struct backend_tbl_info { 1140Sstevel@tonic-gate const char *bti_name; 1150Sstevel@tonic-gate const char *bti_cols; 1160Sstevel@tonic-gate }; 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate struct backend_idx_info { 1190Sstevel@tonic-gate const char *bxi_tbl; 1200Sstevel@tonic-gate const char *bxi_idx; 1210Sstevel@tonic-gate const char *bxi_cols; 1220Sstevel@tonic-gate }; 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate static pthread_mutex_t backend_panic_lock = PTHREAD_MUTEX_INITIALIZER; 1250Sstevel@tonic-gate static pthread_cond_t backend_panic_cv = PTHREAD_COND_INITIALIZER; 1260Sstevel@tonic-gate pthread_t backend_panic_thread = 0; 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate int backend_do_trace = 0; /* invoke tracing callback */ 1290Sstevel@tonic-gate int backend_print_trace = 0; /* tracing callback prints SQL */ 1300Sstevel@tonic-gate int backend_panic_abort = 0; /* abort when panicking */ 1310Sstevel@tonic-gate 132407Sjwadams /* interval between read-only checks while starting up */ 133407Sjwadams #define BACKEND_READONLY_CHECK_INTERVAL (2 * (hrtime_t)NANOSEC) 134407Sjwadams 1350Sstevel@tonic-gate /* 1360Sstevel@tonic-gate * Any change to the below schema should bump the version number 1370Sstevel@tonic-gate */ 1380Sstevel@tonic-gate #define BACKEND_SCHEMA_VERSION 5 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate static struct backend_tbl_info tbls_normal[] = { /* BACKEND_TYPE_NORMAL */ 1410Sstevel@tonic-gate /* 1420Sstevel@tonic-gate * service_tbl holds all services. svc_id is the identifier of the 1430Sstevel@tonic-gate * service. 1440Sstevel@tonic-gate */ 1450Sstevel@tonic-gate { 1460Sstevel@tonic-gate "service_tbl", 1470Sstevel@tonic-gate "svc_id INTEGER PRIMARY KEY," 1480Sstevel@tonic-gate "svc_name CHAR(256) NOT NULL" 1490Sstevel@tonic-gate }, 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate /* 1520Sstevel@tonic-gate * instance_tbl holds all of the instances. The parent service id 1530Sstevel@tonic-gate * is instance_svc. 1540Sstevel@tonic-gate */ 1550Sstevel@tonic-gate { 1560Sstevel@tonic-gate "instance_tbl", 1570Sstevel@tonic-gate "instance_id INTEGER PRIMARY KEY," 1580Sstevel@tonic-gate "instance_name CHAR(256) NOT NULL," 1590Sstevel@tonic-gate "instance_svc INTEGER NOT NULL" 1600Sstevel@tonic-gate }, 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* 1630Sstevel@tonic-gate * snapshot_lnk_tbl links (instance, snapshot name) with snapshots. 1640Sstevel@tonic-gate */ 1650Sstevel@tonic-gate { 1660Sstevel@tonic-gate "snapshot_lnk_tbl", 1670Sstevel@tonic-gate "lnk_id INTEGER PRIMARY KEY," 1680Sstevel@tonic-gate "lnk_inst_id INTEGER NOT NULL," 1690Sstevel@tonic-gate "lnk_snap_name CHAR(256) NOT NULL," 1700Sstevel@tonic-gate "lnk_snap_id INTEGER NOT NULL" 1710Sstevel@tonic-gate }, 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1740Sstevel@tonic-gate * snaplevel_tbl maps a snapshot id to a set of named, ordered 1750Sstevel@tonic-gate * snaplevels. 1760Sstevel@tonic-gate */ 1770Sstevel@tonic-gate { 1780Sstevel@tonic-gate "snaplevel_tbl", 1790Sstevel@tonic-gate "snap_id INTEGER NOT NULL," 1800Sstevel@tonic-gate "snap_level_num INTEGER NOT NULL," 1810Sstevel@tonic-gate "snap_level_id INTEGER NOT NULL," 1820Sstevel@tonic-gate "snap_level_service_id INTEGER NOT NULL," 1830Sstevel@tonic-gate "snap_level_service CHAR(256) NOT NULL," 1840Sstevel@tonic-gate "snap_level_instance_id INTEGER NULL," 1850Sstevel@tonic-gate "snap_level_instance CHAR(256) NULL" 1860Sstevel@tonic-gate }, 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate /* 1890Sstevel@tonic-gate * snaplevel_lnk_tbl links snaplevels to property groups. 1900Sstevel@tonic-gate * snaplvl_pg_* is identical to the original property group, 1910Sstevel@tonic-gate * and snaplvl_gen_id overrides the generation number. 1920Sstevel@tonic-gate * The service/instance ids are as in the snaplevel. 1930Sstevel@tonic-gate */ 1940Sstevel@tonic-gate { 1950Sstevel@tonic-gate "snaplevel_lnk_tbl", 1960Sstevel@tonic-gate "snaplvl_level_id INTEGER NOT NULL," 1970Sstevel@tonic-gate "snaplvl_pg_id INTEGER NOT NULL," 1980Sstevel@tonic-gate "snaplvl_pg_name CHAR(256) NOT NULL," 1990Sstevel@tonic-gate "snaplvl_pg_type CHAR(256) NOT NULL," 2000Sstevel@tonic-gate "snaplvl_pg_flags INTEGER NOT NULL," 2010Sstevel@tonic-gate "snaplvl_gen_id INTEGER NOT NULL" 2020Sstevel@tonic-gate }, 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate { NULL, NULL } 2050Sstevel@tonic-gate }; 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate static struct backend_idx_info idxs_normal[] = { /* BACKEND_TYPE_NORMAL */ 2080Sstevel@tonic-gate { "service_tbl", "name", "svc_name" }, 2090Sstevel@tonic-gate { "instance_tbl", "name", "instance_svc, instance_name" }, 2100Sstevel@tonic-gate { "snapshot_lnk_tbl", "name", "lnk_inst_id, lnk_snap_name" }, 2110Sstevel@tonic-gate { "snapshot_lnk_tbl", "snapid", "lnk_snap_id" }, 2120Sstevel@tonic-gate { "snaplevel_tbl", "id", "snap_id" }, 2130Sstevel@tonic-gate { "snaplevel_lnk_tbl", "id", "snaplvl_pg_id" }, 2140Sstevel@tonic-gate { "snaplevel_lnk_tbl", "level", "snaplvl_level_id" }, 2150Sstevel@tonic-gate { NULL, NULL, NULL } 2160Sstevel@tonic-gate }; 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate static struct backend_tbl_info tbls_np[] = { /* BACKEND_TYPE_NONPERSIST */ 2190Sstevel@tonic-gate { NULL, NULL } 2200Sstevel@tonic-gate }; 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate static struct backend_idx_info idxs_np[] = { /* BACKEND_TYPE_NONPERSIST */ 2230Sstevel@tonic-gate { NULL, NULL, NULL } 2240Sstevel@tonic-gate }; 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate static struct backend_tbl_info tbls_common[] = { /* all backend types */ 2270Sstevel@tonic-gate /* 2280Sstevel@tonic-gate * pg_tbl defines property groups. They are associated with a single 2290Sstevel@tonic-gate * service or instance. The pg_gen_id links them with the latest 2300Sstevel@tonic-gate * "edited" version of its properties. 2310Sstevel@tonic-gate */ 2320Sstevel@tonic-gate { 2330Sstevel@tonic-gate "pg_tbl", 2340Sstevel@tonic-gate "pg_id INTEGER PRIMARY KEY," 2350Sstevel@tonic-gate "pg_parent_id INTEGER NOT NULL," 2360Sstevel@tonic-gate "pg_name CHAR(256) NOT NULL," 2370Sstevel@tonic-gate "pg_type CHAR(256) NOT NULL," 2380Sstevel@tonic-gate "pg_flags INTEGER NOT NULL," 2390Sstevel@tonic-gate "pg_gen_id INTEGER NOT NULL" 2400Sstevel@tonic-gate }, 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2430Sstevel@tonic-gate * prop_lnk_tbl links a particular pg_id and gen_id to a set of 2440Sstevel@tonic-gate * (prop_name, prop_type, val_id) trios. 2450Sstevel@tonic-gate */ 2460Sstevel@tonic-gate { 2470Sstevel@tonic-gate "prop_lnk_tbl", 2480Sstevel@tonic-gate "lnk_prop_id INTEGER PRIMARY KEY," 2490Sstevel@tonic-gate "lnk_pg_id INTEGER NOT NULL," 2500Sstevel@tonic-gate "lnk_gen_id INTEGER NOT NULL," 2510Sstevel@tonic-gate "lnk_prop_name CHAR(256) NOT NULL," 2520Sstevel@tonic-gate "lnk_prop_type CHAR(2) NOT NULL," 2530Sstevel@tonic-gate "lnk_val_id INTEGER" 2540Sstevel@tonic-gate }, 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate /* 2570Sstevel@tonic-gate * value_tbl maps a value_id to a set of values. For any given 2580Sstevel@tonic-gate * value_id, value_type is constant. 2590Sstevel@tonic-gate */ 2600Sstevel@tonic-gate { 2610Sstevel@tonic-gate "value_tbl", 2620Sstevel@tonic-gate "value_id INTEGER NOT NULL," 2630Sstevel@tonic-gate "value_type CHAR(1) NOT NULL," 2640Sstevel@tonic-gate "value_value VARCHAR NOT NULL" 2650Sstevel@tonic-gate }, 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate /* 2680Sstevel@tonic-gate * id_tbl has one row per id space 2690Sstevel@tonic-gate */ 2700Sstevel@tonic-gate { 2710Sstevel@tonic-gate "id_tbl", 2720Sstevel@tonic-gate "id_name STRING NOT NULL," 2730Sstevel@tonic-gate "id_next INTEGER NOT NULL" 2740Sstevel@tonic-gate }, 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate /* 2770Sstevel@tonic-gate * schema_version has a single row, which contains 2780Sstevel@tonic-gate * BACKEND_SCHEMA_VERSION at the time of creation. 2790Sstevel@tonic-gate */ 2800Sstevel@tonic-gate { 2810Sstevel@tonic-gate "schema_version", 2820Sstevel@tonic-gate "schema_version INTEGER" 2830Sstevel@tonic-gate }, 2840Sstevel@tonic-gate { NULL, NULL } 2850Sstevel@tonic-gate }; 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate static struct backend_idx_info idxs_common[] = { /* all backend types */ 2880Sstevel@tonic-gate { "pg_tbl", "parent", "pg_parent_id" }, 2890Sstevel@tonic-gate { "pg_tbl", "name", "pg_parent_id, pg_name" }, 2900Sstevel@tonic-gate { "pg_tbl", "type", "pg_parent_id, pg_type" }, 2910Sstevel@tonic-gate { "prop_lnk_tbl", "base", "lnk_pg_id, lnk_gen_id" }, 2920Sstevel@tonic-gate { "prop_lnk_tbl", "val", "lnk_val_id" }, 2930Sstevel@tonic-gate { "value_tbl", "id", "value_id" }, 2940Sstevel@tonic-gate { "id_tbl", "id", "id_name" }, 2950Sstevel@tonic-gate { NULL, NULL, NULL } 2960Sstevel@tonic-gate }; 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate struct run_single_int_info { 2990Sstevel@tonic-gate uint32_t *rs_out; 3000Sstevel@tonic-gate int rs_result; 3010Sstevel@tonic-gate }; 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /*ARGSUSED*/ 3040Sstevel@tonic-gate static int 3050Sstevel@tonic-gate run_single_int_callback(void *arg, int columns, char **vals, char **names) 3060Sstevel@tonic-gate { 3070Sstevel@tonic-gate struct run_single_int_info *info = arg; 3080Sstevel@tonic-gate uint32_t val; 3090Sstevel@tonic-gate 3100Sstevel@tonic-gate char *endptr = vals[0]; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate assert(info->rs_result != REP_PROTOCOL_SUCCESS); 3130Sstevel@tonic-gate assert(columns == 1); 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate if (vals[0] == NULL) 3160Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate errno = 0; 3190Sstevel@tonic-gate val = strtoul(vals[0], &endptr, 10); 3200Sstevel@tonic-gate if ((val == 0 && endptr == vals[0]) || *endptr != 0 || errno != 0) 3210Sstevel@tonic-gate backend_panic("malformed integer \"%20s\"", vals[0]); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate *info->rs_out = val; 3240Sstevel@tonic-gate info->rs_result = REP_PROTOCOL_SUCCESS; 3250Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate /*ARGSUSED*/ 3290Sstevel@tonic-gate int 3300Sstevel@tonic-gate backend_fail_if_seen(void *arg, int columns, char **vals, char **names) 3310Sstevel@tonic-gate { 3320Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 3330Sstevel@tonic-gate } 3340Sstevel@tonic-gate 335407Sjwadams /* 336407Sjwadams * check to see if we can successfully start a transaction; if not, the 337407Sjwadams * filesystem is mounted read-only. 338407Sjwadams */ 3390Sstevel@tonic-gate static int 340407Sjwadams backend_is_readonly(struct sqlite *db, const char *path) 3410Sstevel@tonic-gate { 342407Sjwadams int r; 343407Sjwadams statvfs64_t stat; 344407Sjwadams 345407Sjwadams if (statvfs64(path, &stat) == 0 && (stat.f_flag & ST_RDONLY)) 346407Sjwadams return (SQLITE_READONLY); 347407Sjwadams 348407Sjwadams r = sqlite_exec(db, 3490Sstevel@tonic-gate "BEGIN TRANSACTION; " 3500Sstevel@tonic-gate "UPDATE schema_version SET schema_version = schema_version; ", 351407Sjwadams NULL, NULL, NULL); 3520Sstevel@tonic-gate (void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); 3530Sstevel@tonic-gate return (r); 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate static void 3570Sstevel@tonic-gate backend_trace_sql(void *arg, const char *sql) 3580Sstevel@tonic-gate { 3590Sstevel@tonic-gate sqlite_backend_t *be = arg; 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate if (backend_print_trace) { 3620Sstevel@tonic-gate (void) fprintf(stderr, "%d: %s\n", be->be_type, sql); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate static sqlite_backend_t be_info[BACKEND_TYPE_TOTAL]; 3670Sstevel@tonic-gate static sqlite_backend_t *bes[BACKEND_TYPE_TOTAL]; 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate #define BACKEND_PANIC_TIMEOUT (50 * MILLISEC) 3700Sstevel@tonic-gate /* 3710Sstevel@tonic-gate * backend_panic() -- some kind of database problem or corruption has been hit. 3720Sstevel@tonic-gate * We attempt to quiesce the other database users -- all of the backend sql 3730Sstevel@tonic-gate * entry points will call backend_panic(NULL) if a panic is in progress, as 3740Sstevel@tonic-gate * will any attempt to start a transaction. 3750Sstevel@tonic-gate * 3760Sstevel@tonic-gate * We give threads holding a backend lock 50ms (BACKEND_PANIC_TIMEOUT) to 3770Sstevel@tonic-gate * either drop the lock or call backend_panic(). If they don't respond in 3780Sstevel@tonic-gate * time, we'll just exit anyway. 3790Sstevel@tonic-gate */ 3800Sstevel@tonic-gate void 3810Sstevel@tonic-gate backend_panic(const char *format, ...) 3820Sstevel@tonic-gate { 3830Sstevel@tonic-gate int i; 3840Sstevel@tonic-gate va_list args; 3850Sstevel@tonic-gate int failed = 0; 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 3880Sstevel@tonic-gate if (backend_panic_thread != 0) { 3890Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 3900Sstevel@tonic-gate /* 3910Sstevel@tonic-gate * first, drop any backend locks we're holding, then 3920Sstevel@tonic-gate * sleep forever on the panic_cv. 3930Sstevel@tonic-gate */ 3940Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 3950Sstevel@tonic-gate if (bes[i] != NULL && 3960Sstevel@tonic-gate bes[i]->be_thread == pthread_self()) 3970Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 4000Sstevel@tonic-gate for (;;) 4010Sstevel@tonic-gate (void) pthread_cond_wait(&backend_panic_cv, 4020Sstevel@tonic-gate &backend_panic_lock); 4030Sstevel@tonic-gate } 4040Sstevel@tonic-gate backend_panic_thread = pthread_self(); 4050Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4080Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread == pthread_self()) 4090Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate va_start(args, format); 4130Sstevel@tonic-gate configd_vcritical(format, args); 4140Sstevel@tonic-gate va_end(args); 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4170Sstevel@tonic-gate timespec_t rel; 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate rel.tv_sec = 0; 4200Sstevel@tonic-gate rel.tv_nsec = BACKEND_PANIC_TIMEOUT; 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread != pthread_self()) { 4230Sstevel@tonic-gate if (pthread_mutex_reltimedlock_np(&bes[i]->be_lock, 4240Sstevel@tonic-gate &rel) != 0) 4250Sstevel@tonic-gate failed++; 4260Sstevel@tonic-gate } 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate if (failed) { 4290Sstevel@tonic-gate configd_critical("unable to quiesce database\n"); 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate if (backend_panic_abort) 4330Sstevel@tonic-gate abort(); 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate exit(CONFIGD_EXIT_DATABASE_BAD); 4360Sstevel@tonic-gate } 4370Sstevel@tonic-gate 4380Sstevel@tonic-gate /* 4390Sstevel@tonic-gate * Returns 4400Sstevel@tonic-gate * _SUCCESS 4410Sstevel@tonic-gate * _DONE - callback aborted query 4420Sstevel@tonic-gate * _NO_RESOURCES - out of memory (_FULL & _TOOBIG?) 4430Sstevel@tonic-gate */ 4440Sstevel@tonic-gate static int 4450Sstevel@tonic-gate backend_error(sqlite_backend_t *be, int error, char *errmsg) 4460Sstevel@tonic-gate { 4470Sstevel@tonic-gate if (error == SQLITE_OK) 4480Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate switch (error) { 4510Sstevel@tonic-gate case SQLITE_ABORT: 4520Sstevel@tonic-gate free(errmsg); 4530Sstevel@tonic-gate return (REP_PROTOCOL_DONE); 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate case SQLITE_NOMEM: 4560Sstevel@tonic-gate case SQLITE_FULL: 4570Sstevel@tonic-gate case SQLITE_TOOBIG: 4580Sstevel@tonic-gate free(errmsg); 4590Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate default: 4620Sstevel@tonic-gate backend_panic("%s: db error: %s", be->be_path, errmsg); 4630Sstevel@tonic-gate /*NOTREACHED*/ 4640Sstevel@tonic-gate } 4650Sstevel@tonic-gate } 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate static void 4680Sstevel@tonic-gate backend_backup_cleanup(const char **out_arg, ssize_t out_sz) 4690Sstevel@tonic-gate { 4700Sstevel@tonic-gate char **out = (char **)out_arg; 4710Sstevel@tonic-gate 4720Sstevel@tonic-gate while (out_sz-- > 0) 4730Sstevel@tonic-gate free(*out++); 4740Sstevel@tonic-gate free(out_arg); 4750Sstevel@tonic-gate } 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate /* 4780Sstevel@tonic-gate * builds a inverse-time-sorted array of backup files. The path is a 4790Sstevel@tonic-gate * a single buffer, and the pointers look like: 4800Sstevel@tonic-gate * 4810Sstevel@tonic-gate * /this/is/a/full/path/to/repository-name-YYYYMMDDHHMMSS 4820Sstevel@tonic-gate * ^pathname ^ ^(pathname+pathlen) 4830Sstevel@tonic-gate * basename 4840Sstevel@tonic-gate * 4850Sstevel@tonic-gate * dirname will either be pathname, or ".". 4860Sstevel@tonic-gate * 4870Sstevel@tonic-gate * Returns the number of elements in the array, 0 if there are no previous 4880Sstevel@tonic-gate * backups, or -1 on error. 4890Sstevel@tonic-gate */ 4900Sstevel@tonic-gate static ssize_t 4910Sstevel@tonic-gate backend_backup_get_prev(char *pathname, size_t pathlen, const char ***out_arg) 4920Sstevel@tonic-gate { 4930Sstevel@tonic-gate char b_start, b_end; 4940Sstevel@tonic-gate DIR *dir; 4950Sstevel@tonic-gate char **out = NULL; 4960Sstevel@tonic-gate char *name, *p; 4970Sstevel@tonic-gate char *dirname, *basename; 4980Sstevel@tonic-gate char *pathend; 4990Sstevel@tonic-gate struct dirent *ent; 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate size_t count = 0; 5020Sstevel@tonic-gate size_t baselen; 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate /* 5050Sstevel@tonic-gate * year, month, day, hour, min, sec, plus an '_'. 5060Sstevel@tonic-gate */ 5070Sstevel@tonic-gate const size_t ndigits = 4 + 5*2 + 1; 5080Sstevel@tonic-gate const size_t baroffset = 4 + 2*2; 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate size_t idx; 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate pathend = pathname + pathlen; 5130Sstevel@tonic-gate b_end = *pathend; 5140Sstevel@tonic-gate *pathend = '\0'; 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate basename = strrchr(pathname, '/'); 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate if (basename != NULL) { 5190Sstevel@tonic-gate assert(pathend > pathname && basename < pathend); 5200Sstevel@tonic-gate basename++; 5210Sstevel@tonic-gate dirname = pathname; 5220Sstevel@tonic-gate } else { 5230Sstevel@tonic-gate basename = pathname; 5240Sstevel@tonic-gate dirname = "."; 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate baselen = strlen(basename); 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate /* 5300Sstevel@tonic-gate * munge the string temporarily for the opendir(), then restore it. 5310Sstevel@tonic-gate */ 5320Sstevel@tonic-gate b_start = basename[0]; 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate basename[0] = '\0'; 5350Sstevel@tonic-gate dir = opendir(dirname); 5360Sstevel@tonic-gate basename[0] = b_start; /* restore path */ 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate if (dir == NULL) 5390Sstevel@tonic-gate goto fail; 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate while ((ent = readdir(dir)) != NULL) { 5430Sstevel@tonic-gate /* 5440Sstevel@tonic-gate * Must match: 5450Sstevel@tonic-gate * basename-YYYYMMDD_HHMMSS 5460Sstevel@tonic-gate * or we ignore it. 5470Sstevel@tonic-gate */ 5480Sstevel@tonic-gate if (strncmp(ent->d_name, basename, baselen) != 0) 5490Sstevel@tonic-gate continue; 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate name = ent->d_name; 5520Sstevel@tonic-gate if (name[baselen] != '-') 5530Sstevel@tonic-gate continue; 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate p = name + baselen + 1; 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate for (idx = 0; idx < ndigits; idx++) { 5580Sstevel@tonic-gate char c = p[idx]; 5590Sstevel@tonic-gate if (idx == baroffset && c != '_') 5600Sstevel@tonic-gate break; 5610Sstevel@tonic-gate if (idx != baroffset && (c < '0' || c > '9')) 5620Sstevel@tonic-gate break; 5630Sstevel@tonic-gate } 5640Sstevel@tonic-gate if (idx != ndigits || p[idx] != '\0') 5650Sstevel@tonic-gate continue; 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate /* 5680Sstevel@tonic-gate * We have a match. insertion-sort it into our list. 5690Sstevel@tonic-gate */ 5700Sstevel@tonic-gate name = strdup(name); 5710Sstevel@tonic-gate if (name == NULL) 5720Sstevel@tonic-gate goto fail_closedir; 5730Sstevel@tonic-gate p = strrchr(name, '-'); 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 5760Sstevel@tonic-gate char *tmp = out[idx]; 5770Sstevel@tonic-gate char *tp = strrchr(tmp, '-'); 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate int cmp = strcmp(p, tp); 5800Sstevel@tonic-gate if (cmp == 0) 5810Sstevel@tonic-gate cmp = strcmp(name, tmp); 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate if (cmp == 0) { 5840Sstevel@tonic-gate free(name); 5850Sstevel@tonic-gate name = NULL; 5860Sstevel@tonic-gate break; 5870Sstevel@tonic-gate } else if (cmp > 0) { 5880Sstevel@tonic-gate out[idx] = name; 5890Sstevel@tonic-gate name = tmp; 5900Sstevel@tonic-gate p = tp; 5910Sstevel@tonic-gate } 5920Sstevel@tonic-gate } 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate if (idx == count) { 5950Sstevel@tonic-gate char **new_out = realloc(out, 5960Sstevel@tonic-gate (count + 1) * sizeof (*out)); 5970Sstevel@tonic-gate 5980Sstevel@tonic-gate if (new_out == NULL) { 5990Sstevel@tonic-gate free(name); 6000Sstevel@tonic-gate goto fail_closedir; 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate out = new_out; 6040Sstevel@tonic-gate out[count++] = name; 6050Sstevel@tonic-gate } else { 6060Sstevel@tonic-gate assert(name == NULL); 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate } 6090Sstevel@tonic-gate (void) closedir(dir); 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate basename[baselen] = b_end; 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate *out_arg = (const char **)out; 6140Sstevel@tonic-gate return (count); 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate fail_closedir: 6170Sstevel@tonic-gate (void) closedir(dir); 6180Sstevel@tonic-gate fail: 6190Sstevel@tonic-gate basename[0] = b_start; 6200Sstevel@tonic-gate *pathend = b_end; 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate backend_backup_cleanup((const char **)out, count); 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate *out_arg = NULL; 6250Sstevel@tonic-gate return (-1); 6260Sstevel@tonic-gate } 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate /* 6290Sstevel@tonic-gate * Copies the repository path into out, a buffer of out_len bytes, 6300Sstevel@tonic-gate * removes the ".db" (or whatever) extension, and, if name is non-NULL, 6310Sstevel@tonic-gate * appends "-name" to it. If name is non-NULL, it can fail with: 6320Sstevel@tonic-gate * 6330Sstevel@tonic-gate * _TRUNCATED will not fit in buffer. 6340Sstevel@tonic-gate * _BAD_REQUEST name is not a valid identifier 6350Sstevel@tonic-gate */ 6360Sstevel@tonic-gate static rep_protocol_responseid_t 6370Sstevel@tonic-gate backend_backup_base(sqlite_backend_t *be, const char *name, 6380Sstevel@tonic-gate char *out, size_t out_len) 6390Sstevel@tonic-gate { 6400Sstevel@tonic-gate char *p, *q; 6410Sstevel@tonic-gate size_t len; 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate /* 6440Sstevel@tonic-gate * for paths of the form /path/to/foo.db, we truncate at the final 6450Sstevel@tonic-gate * '.'. 6460Sstevel@tonic-gate */ 6470Sstevel@tonic-gate (void) strlcpy(out, be->be_path, out_len); 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate p = strrchr(out, '/'); 6500Sstevel@tonic-gate q = strrchr(out, '.'); 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate if (p != NULL && q != NULL && q > p) 6530Sstevel@tonic-gate *q = 0; 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate if (name != NULL) { 6560Sstevel@tonic-gate len = strlen(out); 6570Sstevel@tonic-gate assert(len < out_len); 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate out += len; 6600Sstevel@tonic-gate out_len -= len; 6610Sstevel@tonic-gate 6620Sstevel@tonic-gate len = strlen(name); 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate /* 6650Sstevel@tonic-gate * verify that the name tag is entirely alphabetic, 6660Sstevel@tonic-gate * non-empty, and not too long. 6670Sstevel@tonic-gate */ 6680Sstevel@tonic-gate if (len == 0 || len >= REP_PROTOCOL_NAME_LEN || 6690Sstevel@tonic-gate uu_check_name(name, UU_NAME_DOMAIN) < 0) 6700Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 6710Sstevel@tonic-gate 6720Sstevel@tonic-gate if (snprintf(out, out_len, "-%s", name) >= out_len) 6730Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 6740Sstevel@tonic-gate } 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 6770Sstevel@tonic-gate } 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate /* 680407Sjwadams * See if a backup is needed. We do a backup unless both files are 681407Sjwadams * byte-for-byte identical. 682407Sjwadams */ 683407Sjwadams static int 684407Sjwadams backend_check_backup_needed(const char *rep_name, const char *backup_name) 685407Sjwadams { 686407Sjwadams int repfd = open(rep_name, O_RDONLY); 687407Sjwadams int fd = open(backup_name, O_RDONLY); 688407Sjwadams struct stat s_rep, s_backup; 689407Sjwadams int c1, c2; 690407Sjwadams 691407Sjwadams FILE *f_rep = NULL; 692407Sjwadams FILE *f_backup = NULL; 693407Sjwadams 694407Sjwadams if (repfd < 0 || fd < 0) 695407Sjwadams goto fail; 696407Sjwadams 697407Sjwadams if (fstat(repfd, &s_rep) < 0 || fstat(fd, &s_backup) < 0) 698407Sjwadams goto fail; 699407Sjwadams 700407Sjwadams /* 701407Sjwadams * if they are the same file, we need to do a backup to break the 702407Sjwadams * hard link or symlink involved. 703407Sjwadams */ 704407Sjwadams if (s_rep.st_ino == s_backup.st_ino && s_rep.st_dev == s_backup.st_dev) 705407Sjwadams goto fail; 706407Sjwadams 707407Sjwadams if (s_rep.st_size != s_backup.st_size) 708407Sjwadams goto fail; 709407Sjwadams 710407Sjwadams if ((f_rep = fdopen(repfd, "r")) == NULL || 711407Sjwadams (f_backup = fdopen(fd, "r")) == NULL) 712407Sjwadams goto fail; 713407Sjwadams 714407Sjwadams do { 715407Sjwadams c1 = getc(f_rep); 716407Sjwadams c2 = getc(f_backup); 717407Sjwadams if (c1 != c2) 718407Sjwadams goto fail; 719407Sjwadams } while (c1 != EOF); 720407Sjwadams 721407Sjwadams if (!ferror(f_rep) && !ferror(f_backup)) { 722407Sjwadams (void) fclose(f_rep); 723407Sjwadams (void) fclose(f_backup); 724407Sjwadams (void) close(repfd); 725407Sjwadams (void) close(fd); 726407Sjwadams return (0); 727407Sjwadams } 728407Sjwadams 729407Sjwadams fail: 730407Sjwadams if (f_rep != NULL) 731407Sjwadams (void) fclose(f_rep); 732407Sjwadams if (f_backup != NULL) 733407Sjwadams (void) fclose(f_backup); 734407Sjwadams if (repfd >= 0) 735407Sjwadams (void) close(repfd); 736407Sjwadams if (fd >= 0) 737407Sjwadams (void) close(fd); 738407Sjwadams return (1); 739407Sjwadams } 740407Sjwadams 741407Sjwadams /* 7420Sstevel@tonic-gate * Can return: 7430Sstevel@tonic-gate * _BAD_REQUEST name is not valid 7440Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 7450Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 7460Sstevel@tonic-gate * console) 7470Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 7480Sstevel@tonic-gate * 7490Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 7500Sstevel@tonic-gate */ 7510Sstevel@tonic-gate static rep_protocol_responseid_t 7520Sstevel@tonic-gate backend_create_backup_locked(sqlite_backend_t *be, const char *name) 7530Sstevel@tonic-gate { 7540Sstevel@tonic-gate const char **old_list; 7550Sstevel@tonic-gate ssize_t old_sz; 7560Sstevel@tonic-gate ssize_t old_max = max_repository_backups; 7570Sstevel@tonic-gate ssize_t cur; 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate char *finalname; 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate char finalpath[PATH_MAX]; 7620Sstevel@tonic-gate char tmppath[PATH_MAX]; 7630Sstevel@tonic-gate char buf[8192]; 7640Sstevel@tonic-gate int infd, outfd; 7650Sstevel@tonic-gate size_t len; 7660Sstevel@tonic-gate off_t inlen, outlen, offset; 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate time_t now; 7690Sstevel@tonic-gate struct tm now_tm; 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate rep_protocol_responseid_t result; 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate if (be->be_readonly) 7740Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 7750Sstevel@tonic-gate 7760Sstevel@tonic-gate result = backend_backup_base(be, name, finalpath, sizeof (finalpath)); 7770Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 7780Sstevel@tonic-gate return (result); 7790Sstevel@tonic-gate 780407Sjwadams if (!backend_check_backup_needed(be->be_path, finalpath)) { 781407Sjwadams return (REP_PROTOCOL_SUCCESS); 782407Sjwadams } 783407Sjwadams 7840Sstevel@tonic-gate /* 7850Sstevel@tonic-gate * remember the original length, and the basename location 7860Sstevel@tonic-gate */ 7870Sstevel@tonic-gate len = strlen(finalpath); 7880Sstevel@tonic-gate finalname = strrchr(finalpath, '/'); 7890Sstevel@tonic-gate if (finalname != NULL) 7900Sstevel@tonic-gate finalname++; 7910Sstevel@tonic-gate else 7920Sstevel@tonic-gate finalname = finalpath; 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate (void) strlcpy(tmppath, finalpath, sizeof (tmppath)); 7950Sstevel@tonic-gate if (strlcat(tmppath, "-tmpXXXXXX", sizeof (tmppath)) >= 7960Sstevel@tonic-gate sizeof (tmppath)) 7970Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate now = time(NULL); 8000Sstevel@tonic-gate if (localtime_r(&now, &now_tm) == NULL) { 8010Sstevel@tonic-gate configd_critical( 8020Sstevel@tonic-gate "\"%s\" backup failed: localtime(3C) failed: %s\n", name, 8030Sstevel@tonic-gate be->be_path, strerror(errno)); 8040Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 8050Sstevel@tonic-gate } 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate if (strftime(finalpath + len, sizeof (finalpath) - len, 8080Sstevel@tonic-gate "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >= 8090Sstevel@tonic-gate sizeof (finalpath) - len) { 8100Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 8110Sstevel@tonic-gate } 8120Sstevel@tonic-gate 8130Sstevel@tonic-gate infd = open(be->be_path, O_RDONLY); 8140Sstevel@tonic-gate if (infd < 0) { 8150Sstevel@tonic-gate configd_critical("\"%s\" backup failed: opening %s: %s\n", name, 8160Sstevel@tonic-gate be->be_path, strerror(errno)); 8170Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 8180Sstevel@tonic-gate } 8190Sstevel@tonic-gate 8200Sstevel@tonic-gate outfd = mkstemp(tmppath); 8210Sstevel@tonic-gate if (outfd < 0) { 8220Sstevel@tonic-gate configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n", 8230Sstevel@tonic-gate name, tmppath, strerror(errno)); 8240Sstevel@tonic-gate (void) close(infd); 8250Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 8260Sstevel@tonic-gate } 8270Sstevel@tonic-gate 8280Sstevel@tonic-gate for (;;) { 8290Sstevel@tonic-gate do { 8300Sstevel@tonic-gate inlen = read(infd, buf, sizeof (buf)); 8310Sstevel@tonic-gate } while (inlen < 0 && errno == EINTR); 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate if (inlen <= 0) 8340Sstevel@tonic-gate break; 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate for (offset = 0; offset < inlen; offset += outlen) { 8370Sstevel@tonic-gate do { 8380Sstevel@tonic-gate outlen = write(outfd, buf + offset, 8390Sstevel@tonic-gate inlen - offset); 8400Sstevel@tonic-gate } while (outlen < 0 && errno == EINTR); 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate if (outlen >= 0) 8430Sstevel@tonic-gate continue; 8440Sstevel@tonic-gate 8450Sstevel@tonic-gate configd_critical( 8460Sstevel@tonic-gate "\"%s\" backup failed: write to %s: %s\n", 8470Sstevel@tonic-gate name, tmppath, strerror(errno)); 8480Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 8490Sstevel@tonic-gate goto fail; 8500Sstevel@tonic-gate } 8510Sstevel@tonic-gate } 8520Sstevel@tonic-gate 8530Sstevel@tonic-gate if (inlen < 0) { 8540Sstevel@tonic-gate configd_critical( 8550Sstevel@tonic-gate "\"%s\" backup failed: read from %s: %s\n", 8560Sstevel@tonic-gate name, be->be_path, strerror(errno)); 8570Sstevel@tonic-gate goto fail; 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate 8600Sstevel@tonic-gate /* 8610Sstevel@tonic-gate * grab the old list before doing our re-name. 8620Sstevel@tonic-gate */ 8630Sstevel@tonic-gate if (old_max > 0) 8640Sstevel@tonic-gate old_sz = backend_backup_get_prev(finalpath, len, &old_list); 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate if (rename(tmppath, finalpath) < 0) { 8670Sstevel@tonic-gate configd_critical( 8680Sstevel@tonic-gate "\"%s\" backup failed: rename(%s, %s): %s\n", 8690Sstevel@tonic-gate name, tmppath, finalpath, strerror(errno)); 8700Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 8710Sstevel@tonic-gate goto fail; 8720Sstevel@tonic-gate } 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate tmppath[len] = 0; /* strip -XXXXXX, for reference symlink */ 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate (void) unlink(tmppath); 8770Sstevel@tonic-gate if (symlink(finalname, tmppath) < 0) { 8780Sstevel@tonic-gate configd_critical( 8790Sstevel@tonic-gate "\"%s\" backup completed, but updating " 8800Sstevel@tonic-gate "\"%s\" symlink to \"%s\" failed: %s\n", 8810Sstevel@tonic-gate name, tmppath, finalname, strerror(errno)); 8820Sstevel@tonic-gate } 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate if (old_max > 0 && old_sz > 0) { 8850Sstevel@tonic-gate /* unlink all but the first (old_max - 1) files */ 8860Sstevel@tonic-gate for (cur = old_max - 1; cur < old_sz; cur++) { 8870Sstevel@tonic-gate (void) strlcpy(finalname, old_list[cur], 8880Sstevel@tonic-gate sizeof (finalpath) - (finalname - finalpath)); 8890Sstevel@tonic-gate if (unlink(finalpath) < 0) 8900Sstevel@tonic-gate configd_critical( 8910Sstevel@tonic-gate "\"%s\" backup completed, but removing old " 8920Sstevel@tonic-gate "file \"%s\" failed: %s\n", 8930Sstevel@tonic-gate name, finalpath, strerror(errno)); 8940Sstevel@tonic-gate } 8950Sstevel@tonic-gate 8960Sstevel@tonic-gate backend_backup_cleanup(old_list, old_sz); 8970Sstevel@tonic-gate } 8980Sstevel@tonic-gate 8990Sstevel@tonic-gate result = REP_PROTOCOL_SUCCESS; 9000Sstevel@tonic-gate 9010Sstevel@tonic-gate fail: 9020Sstevel@tonic-gate (void) close(infd); 9030Sstevel@tonic-gate (void) close(outfd); 9040Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 9050Sstevel@tonic-gate (void) unlink(tmppath); 9060Sstevel@tonic-gate 9070Sstevel@tonic-gate return (result); 9080Sstevel@tonic-gate } 9090Sstevel@tonic-gate 910407Sjwadams static int 911407Sjwadams backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) 912407Sjwadams { 913407Sjwadams char *errp; 914407Sjwadams struct sqlite *new; 915407Sjwadams int r; 916407Sjwadams 917407Sjwadams assert(be->be_readonly); 918407Sjwadams assert(be == bes[BACKEND_TYPE_NORMAL]); 919407Sjwadams 920407Sjwadams /* 921407Sjwadams * If we don't *need* to be writable, only check every once in a 922407Sjwadams * while. 923407Sjwadams */ 924407Sjwadams if (!writing) { 925407Sjwadams if ((uint64_t)(t - be->be_lastcheck) < 926407Sjwadams BACKEND_READONLY_CHECK_INTERVAL) 927407Sjwadams return (REP_PROTOCOL_SUCCESS); 928407Sjwadams be->be_lastcheck = t; 929407Sjwadams } 930407Sjwadams 931407Sjwadams new = sqlite_open(be->be_path, 0600, &errp); 932407Sjwadams if (new == NULL) { 933407Sjwadams backend_panic("reopening %s: %s\n", be->be_path, errp); 934407Sjwadams /*NOTREACHED*/ 935407Sjwadams } 936407Sjwadams r = backend_is_readonly(new, be->be_path); 937407Sjwadams 938407Sjwadams if (r != SQLITE_OK) { 939407Sjwadams sqlite_close(new); 940407Sjwadams if (writing) 941407Sjwadams return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 942407Sjwadams return (REP_PROTOCOL_SUCCESS); 943407Sjwadams } 944407Sjwadams 945407Sjwadams /* 946407Sjwadams * We can write! Swap the db handles, mark ourself writable, 947407Sjwadams * and make a backup. 948407Sjwadams */ 949407Sjwadams sqlite_close(be->be_db); 950407Sjwadams be->be_db = new; 951407Sjwadams be->be_readonly = 0; 952407Sjwadams 953407Sjwadams if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != 954407Sjwadams REP_PROTOCOL_SUCCESS) { 955407Sjwadams configd_critical( 956407Sjwadams "unable to create \"%s\" backup of \"%s\"\n", 957407Sjwadams REPOSITORY_BOOT_BACKUP, be->be_path); 958407Sjwadams } 959407Sjwadams 960407Sjwadams return (REP_PROTOCOL_SUCCESS); 961407Sjwadams } 9620Sstevel@tonic-gate 9630Sstevel@tonic-gate /* 9640Sstevel@tonic-gate * If t is not BACKEND_TYPE_NORMAL, can fail with 9650Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 9660Sstevel@tonic-gate * 9670Sstevel@tonic-gate * If writing is nonzero, can also fail with 9680Sstevel@tonic-gate * _BACKEND_READONLY - backend is read-only 9690Sstevel@tonic-gate */ 9700Sstevel@tonic-gate static int 9710Sstevel@tonic-gate backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep) 9720Sstevel@tonic-gate { 9730Sstevel@tonic-gate sqlite_backend_t *be = NULL; 9740Sstevel@tonic-gate hrtime_t ts, vts; 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate *bep = NULL; 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || 9790Sstevel@tonic-gate t == BACKEND_TYPE_NONPERSIST); 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate be = bes[t]; 9820Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) 9830Sstevel@tonic-gate assert(be != NULL); /* should always be there */ 9840Sstevel@tonic-gate 9850Sstevel@tonic-gate if (be == NULL) 9860Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_ACCESS); 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate if (backend_panic_thread != 0) 9890Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate ts = gethrtime(); 9920Sstevel@tonic-gate vts = gethrvtime(); 9930Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 9940Sstevel@tonic-gate UPDATE_TOTALS_WR(be, writing, bt_lock, ts, vts); 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate if (backend_panic_thread != 0) { 9970Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 9980Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 9990Sstevel@tonic-gate } 10000Sstevel@tonic-gate be->be_thread = pthread_self(); 10010Sstevel@tonic-gate 1002407Sjwadams if (be->be_readonly) { 10030Sstevel@tonic-gate int r; 10040Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL); 10050Sstevel@tonic-gate 1006407Sjwadams r = backend_check_readonly(be, writing, ts); 1007407Sjwadams if (r != REP_PROTOCOL_SUCCESS) { 10080Sstevel@tonic-gate be->be_thread = 0; 10090Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 1010407Sjwadams return (r); 10110Sstevel@tonic-gate } 1012407Sjwadams } 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate if (backend_do_trace) 10150Sstevel@tonic-gate (void) sqlite_trace(be->be_db, backend_trace_sql, be); 10160Sstevel@tonic-gate else 10170Sstevel@tonic-gate (void) sqlite_trace(be->be_db, NULL, NULL); 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate be->be_writing = writing; 10200Sstevel@tonic-gate *bep = be; 10210Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 10220Sstevel@tonic-gate } 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate static void 10250Sstevel@tonic-gate backend_unlock(sqlite_backend_t *be) 10260Sstevel@tonic-gate { 10270Sstevel@tonic-gate be->be_writing = 0; 10280Sstevel@tonic-gate be->be_thread = 0; 10290Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate static void 10330Sstevel@tonic-gate backend_destroy(sqlite_backend_t *be) 10340Sstevel@tonic-gate { 10350Sstevel@tonic-gate if (be->be_db != NULL) { 10360Sstevel@tonic-gate sqlite_close(be->be_db); 10370Sstevel@tonic-gate be->be_db = NULL; 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate be->be_thread = 0; 10400Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10410Sstevel@tonic-gate (void) pthread_mutex_destroy(&be->be_lock); 10420Sstevel@tonic-gate } 10430Sstevel@tonic-gate 10440Sstevel@tonic-gate static void 10450Sstevel@tonic-gate backend_create_finish(backend_type_t backend_id, sqlite_backend_t *be) 10460Sstevel@tonic-gate { 10470Sstevel@tonic-gate assert(MUTEX_HELD(&be->be_lock)); 10480Sstevel@tonic-gate assert(be == &be_info[backend_id]); 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate bes[backend_id] = be; 10510Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10520Sstevel@tonic-gate } 10530Sstevel@tonic-gate 10540Sstevel@tonic-gate static int 10550Sstevel@tonic-gate backend_fd_write(int fd, const char *mess) 10560Sstevel@tonic-gate { 10570Sstevel@tonic-gate int len = strlen(mess); 10580Sstevel@tonic-gate int written; 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate while (len > 0) { 10610Sstevel@tonic-gate if ((written = write(fd, mess, len)) < 0) 10620Sstevel@tonic-gate return (-1); 10630Sstevel@tonic-gate mess += written; 10640Sstevel@tonic-gate len -= written; 10650Sstevel@tonic-gate } 10660Sstevel@tonic-gate return (0); 10670Sstevel@tonic-gate } 10680Sstevel@tonic-gate 10690Sstevel@tonic-gate /* 10700Sstevel@tonic-gate * Can return: 10710Sstevel@tonic-gate * _BAD_REQUEST name is not valid 10720Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 10730Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 10740Sstevel@tonic-gate * console) 10750Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 10760Sstevel@tonic-gate * 10770Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 10780Sstevel@tonic-gate */ 10790Sstevel@tonic-gate rep_protocol_responseid_t 10800Sstevel@tonic-gate backend_create_backup(const char *name) 10810Sstevel@tonic-gate { 10820Sstevel@tonic-gate rep_protocol_responseid_t result; 10830Sstevel@tonic-gate sqlite_backend_t *be; 10840Sstevel@tonic-gate 10850Sstevel@tonic-gate result = backend_lock(BACKEND_TYPE_NORMAL, 0, &be); 10860Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 10870Sstevel@tonic-gate return (result); 10880Sstevel@tonic-gate 10890Sstevel@tonic-gate result = backend_create_backup_locked(be, name); 10900Sstevel@tonic-gate backend_unlock(be); 10910Sstevel@tonic-gate 10920Sstevel@tonic-gate return (result); 10930Sstevel@tonic-gate } 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate /*ARGSUSED*/ 10960Sstevel@tonic-gate static int 10970Sstevel@tonic-gate backend_integrity_callback(void *private, int narg, char **vals, char **cols) 10980Sstevel@tonic-gate { 10990Sstevel@tonic-gate char **out = private; 11000Sstevel@tonic-gate char *old = *out; 11010Sstevel@tonic-gate char *new; 11020Sstevel@tonic-gate const char *info; 11030Sstevel@tonic-gate size_t len; 11040Sstevel@tonic-gate int x; 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate for (x = 0; x < narg; x++) { 11070Sstevel@tonic-gate if ((info = vals[x]) != NULL && 11080Sstevel@tonic-gate strcmp(info, "ok") != 0) { 11090Sstevel@tonic-gate len = (old == NULL)? 0 : strlen(old); 11100Sstevel@tonic-gate len += strlen(info) + 2; /* '\n' + '\0' */ 11110Sstevel@tonic-gate 11120Sstevel@tonic-gate new = realloc(old, len); 11130Sstevel@tonic-gate if (new == NULL) 11140Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 11150Sstevel@tonic-gate if (old == NULL) 11160Sstevel@tonic-gate new[0] = 0; 11170Sstevel@tonic-gate old = *out = new; 11180Sstevel@tonic-gate (void) strlcat(new, info, len); 11190Sstevel@tonic-gate (void) strlcat(new, "\n", len); 11200Sstevel@tonic-gate } 11210Sstevel@tonic-gate } 11220Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 11230Sstevel@tonic-gate } 11240Sstevel@tonic-gate 11250Sstevel@tonic-gate #define BACKEND_CREATE_LOCKED -2 11260Sstevel@tonic-gate #define BACKEND_CREATE_FAIL -1 11270Sstevel@tonic-gate #define BACKEND_CREATE_SUCCESS 0 11280Sstevel@tonic-gate #define BACKEND_CREATE_READONLY 1 11290Sstevel@tonic-gate #define BACKEND_CREATE_NEED_INIT 2 11300Sstevel@tonic-gate static int 11310Sstevel@tonic-gate backend_create(backend_type_t backend_id, const char *db_file, 11320Sstevel@tonic-gate sqlite_backend_t **bep) 11330Sstevel@tonic-gate { 11340Sstevel@tonic-gate char *errp; 11350Sstevel@tonic-gate char *integrity_results = NULL; 11360Sstevel@tonic-gate sqlite_backend_t *be; 11370Sstevel@tonic-gate int r; 11380Sstevel@tonic-gate uint32_t val = -1UL; 11390Sstevel@tonic-gate struct run_single_int_info info; 11400Sstevel@tonic-gate int fd; 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate assert(backend_id >= 0 && backend_id < BACKEND_TYPE_TOTAL); 11430Sstevel@tonic-gate 11440Sstevel@tonic-gate be = &be_info[backend_id]; 11450Sstevel@tonic-gate assert(be->be_db == NULL); 11460Sstevel@tonic-gate 11470Sstevel@tonic-gate (void) pthread_mutex_init(&be->be_lock, NULL); 11480Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 11490Sstevel@tonic-gate 11500Sstevel@tonic-gate be->be_type = backend_id; 11510Sstevel@tonic-gate be->be_path = strdup(db_file); 11520Sstevel@tonic-gate if (be->be_path == NULL) { 11530Sstevel@tonic-gate perror("malloc"); 11540Sstevel@tonic-gate goto fail; 11550Sstevel@tonic-gate } 11560Sstevel@tonic-gate 11570Sstevel@tonic-gate be->be_db = sqlite_open(be->be_path, 0600, &errp); 11580Sstevel@tonic-gate 11590Sstevel@tonic-gate if (be->be_db == NULL) { 11600Sstevel@tonic-gate if (strstr(errp, "out of memory") != NULL) { 11610Sstevel@tonic-gate configd_critical("%s: %s\n", db_file, errp); 11620Sstevel@tonic-gate free(errp); 11630Sstevel@tonic-gate 11640Sstevel@tonic-gate goto fail; 11650Sstevel@tonic-gate } 11660Sstevel@tonic-gate 11670Sstevel@tonic-gate /* report it as an integrity failure */ 11680Sstevel@tonic-gate integrity_results = errp; 11690Sstevel@tonic-gate errp = NULL; 11700Sstevel@tonic-gate goto integrity_fail; 11710Sstevel@tonic-gate } 11720Sstevel@tonic-gate 11730Sstevel@tonic-gate /* 11740Sstevel@tonic-gate * check if we are inited and of the correct schema version 11750Sstevel@tonic-gate * 11760Sstevel@tonic-gate * Eventually, we'll support schema upgrade here. 11770Sstevel@tonic-gate */ 11780Sstevel@tonic-gate info.rs_out = &val; 11790Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 11800Sstevel@tonic-gate 11810Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT schema_version FROM schema_version;", 11820Sstevel@tonic-gate run_single_int_callback, &info, &errp); 11830Sstevel@tonic-gate if (r == SQLITE_ERROR && 11840Sstevel@tonic-gate strcmp("no such table: schema_version", errp) == 0) { 11850Sstevel@tonic-gate free(errp); 11860Sstevel@tonic-gate /* 11870Sstevel@tonic-gate * Could be an empty repository, could be pre-schema_version 11880Sstevel@tonic-gate * schema. Check for id_tbl, which has always been there. 11890Sstevel@tonic-gate */ 11900Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT count() FROM id_tbl;", 11910Sstevel@tonic-gate NULL, NULL, &errp); 11920Sstevel@tonic-gate if (r == SQLITE_ERROR && 11930Sstevel@tonic-gate strcmp("no such table: id_tbl", errp) == 0) { 11940Sstevel@tonic-gate free(errp); 11950Sstevel@tonic-gate *bep = be; 11960Sstevel@tonic-gate return (BACKEND_CREATE_NEED_INIT); 11970Sstevel@tonic-gate } 11980Sstevel@tonic-gate 11990Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", db_file); 12000Sstevel@tonic-gate goto fail; 12010Sstevel@tonic-gate } 12020Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 12030Sstevel@tonic-gate free(errp); 12040Sstevel@tonic-gate *bep = NULL; 12050Sstevel@tonic-gate backend_destroy(be); 12060Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate if (r == SQLITE_OK) { 12090Sstevel@tonic-gate if (info.rs_result == REP_PROTOCOL_FAIL_NOT_FOUND || 12100Sstevel@tonic-gate val != BACKEND_SCHEMA_VERSION) { 12110Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", 12120Sstevel@tonic-gate db_file); 12130Sstevel@tonic-gate goto fail; 12140Sstevel@tonic-gate } 12150Sstevel@tonic-gate } 12160Sstevel@tonic-gate 12170Sstevel@tonic-gate /* 12180Sstevel@tonic-gate * pull in the whole database sequentially. 12190Sstevel@tonic-gate */ 12200Sstevel@tonic-gate if ((fd = open(db_file, O_RDONLY)) >= 0) { 12210Sstevel@tonic-gate size_t sz = 64 * 1024; 12220Sstevel@tonic-gate char *buffer = malloc(sz); 12230Sstevel@tonic-gate if (buffer != NULL) { 12240Sstevel@tonic-gate while (read(fd, buffer, sz) > 0) 12250Sstevel@tonic-gate ; 12260Sstevel@tonic-gate free(buffer); 12270Sstevel@tonic-gate } 12280Sstevel@tonic-gate (void) close(fd); 12290Sstevel@tonic-gate } 12300Sstevel@tonic-gate 12310Sstevel@tonic-gate /* 12320Sstevel@tonic-gate * run an integrity check 12330Sstevel@tonic-gate */ 12340Sstevel@tonic-gate r = sqlite_exec(be->be_db, "PRAGMA integrity_check;", 12350Sstevel@tonic-gate backend_integrity_callback, &integrity_results, &errp); 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 12380Sstevel@tonic-gate free(errp); 12390Sstevel@tonic-gate *bep = NULL; 12400Sstevel@tonic-gate backend_destroy(be); 12410Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 12420Sstevel@tonic-gate } 12430Sstevel@tonic-gate if (r == SQLITE_ABORT) { 12440Sstevel@tonic-gate free(errp); 12450Sstevel@tonic-gate errp = NULL; 12460Sstevel@tonic-gate integrity_results = "out of memory running integrity check\n"; 12470Sstevel@tonic-gate } else if (r != SQLITE_OK && integrity_results == NULL) { 12480Sstevel@tonic-gate integrity_results = errp; 12490Sstevel@tonic-gate errp = NULL; 12500Sstevel@tonic-gate } 12510Sstevel@tonic-gate 12520Sstevel@tonic-gate integrity_fail: 12530Sstevel@tonic-gate if (integrity_results != NULL) { 12540Sstevel@tonic-gate const char *fname = "/etc/svc/volatile/db_errors"; 12550Sstevel@tonic-gate if ((fd = open(fname, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { 12560Sstevel@tonic-gate fname = NULL; 12570Sstevel@tonic-gate } else { 12580Sstevel@tonic-gate if (backend_fd_write(fd, "\n\n") < 0 || 12590Sstevel@tonic-gate backend_fd_write(fd, db_file) < 0 || 12600Sstevel@tonic-gate backend_fd_write(fd, 12610Sstevel@tonic-gate ": PRAGMA integrity_check; failed. Results:\n") < 12620Sstevel@tonic-gate 0 || backend_fd_write(fd, integrity_results) < 0 || 12630Sstevel@tonic-gate backend_fd_write(fd, "\n\n") < 0) { 12640Sstevel@tonic-gate fname = NULL; 12650Sstevel@tonic-gate } 12660Sstevel@tonic-gate (void) close(fd); 12670Sstevel@tonic-gate } 12680Sstevel@tonic-gate 12690Sstevel@tonic-gate if (!is_main_repository || 12700Sstevel@tonic-gate backend_id == BACKEND_TYPE_NONPERSIST) { 12710Sstevel@tonic-gate if (fname != NULL) 12720Sstevel@tonic-gate configd_critical( 12730Sstevel@tonic-gate "%s: integrity check failed. Details in " 12740Sstevel@tonic-gate "%s\n", db_file, fname); 12750Sstevel@tonic-gate else 12760Sstevel@tonic-gate configd_critical( 1277*4931Spjung "%s: integrity check failed.\n", 12780Sstevel@tonic-gate db_file); 12790Sstevel@tonic-gate } else { 12800Sstevel@tonic-gate (void) fprintf(stderr, 12810Sstevel@tonic-gate "\n" 12820Sstevel@tonic-gate "svc.configd: smf(5) database integrity check of:\n" 12830Sstevel@tonic-gate "\n" 12840Sstevel@tonic-gate " %s\n" 12850Sstevel@tonic-gate "\n" 12860Sstevel@tonic-gate " failed. The database might be damaged or a media error might have\n" 12870Sstevel@tonic-gate " prevented it from being verified. Additional information useful to\n" 12880Sstevel@tonic-gate " your service provider%s%s\n" 12890Sstevel@tonic-gate "\n" 12900Sstevel@tonic-gate " The system will not be able to boot until you have restored a working\n" 12910Sstevel@tonic-gate " database. svc.startd(1M) will provide a sulogin(1M) prompt for recovery\n" 12920Sstevel@tonic-gate " purposes. The command:\n" 12930Sstevel@tonic-gate "\n" 12940Sstevel@tonic-gate " /lib/svc/bin/restore_repository\n" 12950Sstevel@tonic-gate "\n" 12960Sstevel@tonic-gate " can be run to restore a backup version of your repository. See\n" 12970Sstevel@tonic-gate " http://sun.com/msg/SMF-8000-MY for more information.\n" 12980Sstevel@tonic-gate "\n", 12994520Snw141292 db_file, 13004520Snw141292 (fname == NULL)? ":\n\n" : " is in:\n\n ", 13014520Snw141292 (fname == NULL)? integrity_results : fname); 13020Sstevel@tonic-gate } 13030Sstevel@tonic-gate free(errp); 13040Sstevel@tonic-gate goto fail; 13050Sstevel@tonic-gate } 13060Sstevel@tonic-gate 13070Sstevel@tonic-gate /* 13080Sstevel@tonic-gate * check if we are writable 13090Sstevel@tonic-gate */ 1310407Sjwadams r = backend_is_readonly(be->be_db, be->be_path); 13110Sstevel@tonic-gate 13120Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 13130Sstevel@tonic-gate free(errp); 13140Sstevel@tonic-gate *bep = NULL; 13150Sstevel@tonic-gate backend_destroy(be); 13160Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 13170Sstevel@tonic-gate } 13180Sstevel@tonic-gate if (r != SQLITE_OK && r != SQLITE_FULL) { 13190Sstevel@tonic-gate free(errp); 13200Sstevel@tonic-gate be->be_readonly = 1; 13210Sstevel@tonic-gate *bep = be; 13220Sstevel@tonic-gate return (BACKEND_CREATE_READONLY); 13230Sstevel@tonic-gate } 13240Sstevel@tonic-gate *bep = be; 13250Sstevel@tonic-gate return (BACKEND_CREATE_SUCCESS); 13260Sstevel@tonic-gate 13270Sstevel@tonic-gate fail: 13280Sstevel@tonic-gate *bep = NULL; 13290Sstevel@tonic-gate backend_destroy(be); 13300Sstevel@tonic-gate return (BACKEND_CREATE_FAIL); 13310Sstevel@tonic-gate } 13320Sstevel@tonic-gate 13330Sstevel@tonic-gate /* 13340Sstevel@tonic-gate * (arg & -arg) is, through the magic of twos-complement arithmetic, the 13350Sstevel@tonic-gate * lowest set bit in arg. 13360Sstevel@tonic-gate */ 13370Sstevel@tonic-gate static size_t 13380Sstevel@tonic-gate round_up_to_p2(size_t arg) 13390Sstevel@tonic-gate { 13400Sstevel@tonic-gate /* 13410Sstevel@tonic-gate * Don't allow a zero result. 13420Sstevel@tonic-gate */ 13430Sstevel@tonic-gate assert(arg > 0 && ((ssize_t)arg > 0)); 13440Sstevel@tonic-gate 13450Sstevel@tonic-gate while ((arg & (arg - 1)) != 0) 13460Sstevel@tonic-gate arg += (arg & -arg); 13470Sstevel@tonic-gate 13480Sstevel@tonic-gate return (arg); 13490Sstevel@tonic-gate } 13500Sstevel@tonic-gate 13510Sstevel@tonic-gate /* 13520Sstevel@tonic-gate * Returns 13530Sstevel@tonic-gate * _NO_RESOURCES - out of memory 13540Sstevel@tonic-gate * _BACKEND_ACCESS - backend type t (other than _NORMAL) doesn't exist 13550Sstevel@tonic-gate * _DONE - callback aborted query 13560Sstevel@tonic-gate * _SUCCESS 13570Sstevel@tonic-gate */ 13580Sstevel@tonic-gate int 13590Sstevel@tonic-gate backend_run(backend_type_t t, backend_query_t *q, 13600Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 13610Sstevel@tonic-gate { 13620Sstevel@tonic-gate char *errmsg = NULL; 13630Sstevel@tonic-gate int ret; 13640Sstevel@tonic-gate sqlite_backend_t *be; 13650Sstevel@tonic-gate hrtime_t ts, vts; 13660Sstevel@tonic-gate 13670Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 13680Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 13690Sstevel@tonic-gate 13700Sstevel@tonic-gate if ((ret = backend_lock(t, 0, &be)) != REP_PROTOCOL_SUCCESS) 13710Sstevel@tonic-gate return (ret); 13720Sstevel@tonic-gate 13730Sstevel@tonic-gate ts = gethrtime(); 13740Sstevel@tonic-gate vts = gethrvtime(); 13750Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 13760Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 13770Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 13780Sstevel@tonic-gate backend_unlock(be); 13790Sstevel@tonic-gate 13800Sstevel@tonic-gate return (ret); 13810Sstevel@tonic-gate } 13820Sstevel@tonic-gate 13830Sstevel@tonic-gate /* 13840Sstevel@tonic-gate * Starts a "read-only" transaction -- i.e., locks out writers as long 13850Sstevel@tonic-gate * as it is active. 13860Sstevel@tonic-gate * 13870Sstevel@tonic-gate * Fails with 13880Sstevel@tonic-gate * _NO_RESOURCES - out of memory 13890Sstevel@tonic-gate * 13900Sstevel@tonic-gate * If t is not _NORMAL, can also fail with 13910Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 13920Sstevel@tonic-gate * 13930Sstevel@tonic-gate * If writable is true, can also fail with 13940Sstevel@tonic-gate * _BACKEND_READONLY 13950Sstevel@tonic-gate */ 13960Sstevel@tonic-gate static int 13970Sstevel@tonic-gate backend_tx_begin_common(backend_type_t t, backend_tx_t **txp, int writable) 13980Sstevel@tonic-gate { 13990Sstevel@tonic-gate backend_tx_t *ret; 14000Sstevel@tonic-gate sqlite_backend_t *be; 14010Sstevel@tonic-gate int r; 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate *txp = NULL; 14040Sstevel@tonic-gate 14050Sstevel@tonic-gate ret = uu_zalloc(sizeof (*ret)); 14060Sstevel@tonic-gate if (ret == NULL) 14070Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 14080Sstevel@tonic-gate 14090Sstevel@tonic-gate if ((r = backend_lock(t, writable, &be)) != REP_PROTOCOL_SUCCESS) { 14100Sstevel@tonic-gate uu_free(ret); 14110Sstevel@tonic-gate return (r); 14120Sstevel@tonic-gate } 14130Sstevel@tonic-gate 14140Sstevel@tonic-gate ret->bt_be = be; 14150Sstevel@tonic-gate ret->bt_readonly = !writable; 14160Sstevel@tonic-gate ret->bt_type = t; 14170Sstevel@tonic-gate ret->bt_full = 0; 14180Sstevel@tonic-gate 14190Sstevel@tonic-gate *txp = ret; 14200Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 14210Sstevel@tonic-gate } 14220Sstevel@tonic-gate 14230Sstevel@tonic-gate int 14240Sstevel@tonic-gate backend_tx_begin_ro(backend_type_t t, backend_tx_t **txp) 14250Sstevel@tonic-gate { 14260Sstevel@tonic-gate return (backend_tx_begin_common(t, txp, 0)); 14270Sstevel@tonic-gate } 14280Sstevel@tonic-gate 14290Sstevel@tonic-gate static void 14300Sstevel@tonic-gate backend_tx_end(backend_tx_t *tx) 14310Sstevel@tonic-gate { 14320Sstevel@tonic-gate sqlite_backend_t *be; 14330Sstevel@tonic-gate 14340Sstevel@tonic-gate be = tx->bt_be; 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate if (tx->bt_full) { 14370Sstevel@tonic-gate struct sqlite *new; 14380Sstevel@tonic-gate 14390Sstevel@tonic-gate /* 14400Sstevel@tonic-gate * sqlite tends to be sticky with SQLITE_FULL, so we try 14410Sstevel@tonic-gate * to get a fresh database handle if we got a FULL warning 14420Sstevel@tonic-gate * along the way. If that fails, no harm done. 14430Sstevel@tonic-gate */ 14440Sstevel@tonic-gate new = sqlite_open(be->be_path, 0600, NULL); 14450Sstevel@tonic-gate if (new != NULL) { 14460Sstevel@tonic-gate sqlite_close(be->be_db); 14470Sstevel@tonic-gate be->be_db = new; 14480Sstevel@tonic-gate } 14490Sstevel@tonic-gate } 14500Sstevel@tonic-gate backend_unlock(be); 14510Sstevel@tonic-gate tx->bt_be = NULL; 14520Sstevel@tonic-gate uu_free(tx); 14530Sstevel@tonic-gate } 14540Sstevel@tonic-gate 14550Sstevel@tonic-gate void 14560Sstevel@tonic-gate backend_tx_end_ro(backend_tx_t *tx) 14570Sstevel@tonic-gate { 14580Sstevel@tonic-gate assert(tx->bt_readonly); 14590Sstevel@tonic-gate backend_tx_end(tx); 14600Sstevel@tonic-gate } 14610Sstevel@tonic-gate 14620Sstevel@tonic-gate /* 14630Sstevel@tonic-gate * Fails with 14640Sstevel@tonic-gate * _NO_RESOURCES - out of memory 14650Sstevel@tonic-gate * _BACKEND_ACCESS 14660Sstevel@tonic-gate * _BACKEND_READONLY 14670Sstevel@tonic-gate */ 14680Sstevel@tonic-gate int 14690Sstevel@tonic-gate backend_tx_begin(backend_type_t t, backend_tx_t **txp) 14700Sstevel@tonic-gate { 14710Sstevel@tonic-gate int r; 14720Sstevel@tonic-gate char *errmsg; 14730Sstevel@tonic-gate hrtime_t ts, vts; 14740Sstevel@tonic-gate 14750Sstevel@tonic-gate r = backend_tx_begin_common(t, txp, 1); 14760Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) 14770Sstevel@tonic-gate return (r); 14780Sstevel@tonic-gate 14790Sstevel@tonic-gate ts = gethrtime(); 14800Sstevel@tonic-gate vts = gethrvtime(); 14810Sstevel@tonic-gate r = sqlite_exec((*txp)->bt_be->be_db, "BEGIN TRANSACTION", NULL, NULL, 14820Sstevel@tonic-gate &errmsg); 14830Sstevel@tonic-gate UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); 14840Sstevel@tonic-gate if (r == SQLITE_FULL) 14850Sstevel@tonic-gate (*txp)->bt_full = 1; 14860Sstevel@tonic-gate r = backend_error((*txp)->bt_be, r, errmsg); 14870Sstevel@tonic-gate 14880Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 14890Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 14900Sstevel@tonic-gate (void) sqlite_exec((*txp)->bt_be->be_db, 14910Sstevel@tonic-gate "ROLLBACK TRANSACTION", NULL, NULL, NULL); 14920Sstevel@tonic-gate backend_tx_end(*txp); 14930Sstevel@tonic-gate *txp = NULL; 14940Sstevel@tonic-gate return (r); 14950Sstevel@tonic-gate } 14960Sstevel@tonic-gate 14970Sstevel@tonic-gate (*txp)->bt_readonly = 0; 14980Sstevel@tonic-gate 14990Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 15000Sstevel@tonic-gate } 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate void 15030Sstevel@tonic-gate backend_tx_rollback(backend_tx_t *tx) 15040Sstevel@tonic-gate { 15050Sstevel@tonic-gate int r; 15060Sstevel@tonic-gate char *errmsg; 15070Sstevel@tonic-gate sqlite_backend_t *be; 15080Sstevel@tonic-gate hrtime_t ts, vts; 15090Sstevel@tonic-gate 15100Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 15110Sstevel@tonic-gate be = tx->bt_be; 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate ts = gethrtime(); 15140Sstevel@tonic-gate vts = gethrvtime(); 15150Sstevel@tonic-gate r = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 15160Sstevel@tonic-gate &errmsg); 15170Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 15180Sstevel@tonic-gate if (r == SQLITE_FULL) 15190Sstevel@tonic-gate tx->bt_full = 1; 15200Sstevel@tonic-gate (void) backend_error(be, r, errmsg); 15210Sstevel@tonic-gate 15220Sstevel@tonic-gate backend_tx_end(tx); 15230Sstevel@tonic-gate } 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate /* 15260Sstevel@tonic-gate * Fails with 15270Sstevel@tonic-gate * _NO_RESOURCES - out of memory 15280Sstevel@tonic-gate */ 15290Sstevel@tonic-gate int 15300Sstevel@tonic-gate backend_tx_commit(backend_tx_t *tx) 15310Sstevel@tonic-gate { 15320Sstevel@tonic-gate int r, r2; 15330Sstevel@tonic-gate char *errmsg; 15340Sstevel@tonic-gate sqlite_backend_t *be; 15350Sstevel@tonic-gate hrtime_t ts, vts; 15360Sstevel@tonic-gate 15370Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 15380Sstevel@tonic-gate be = tx->bt_be; 15390Sstevel@tonic-gate ts = gethrtime(); 15400Sstevel@tonic-gate vts = gethrvtime(); 15410Sstevel@tonic-gate r = sqlite_exec(be->be_db, "COMMIT TRANSACTION", NULL, NULL, 15420Sstevel@tonic-gate &errmsg); 15430Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 15440Sstevel@tonic-gate if (r == SQLITE_FULL) 15450Sstevel@tonic-gate tx->bt_full = 1; 15460Sstevel@tonic-gate 15470Sstevel@tonic-gate r = backend_error(be, r, errmsg); 15480Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 15490Sstevel@tonic-gate 15500Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 15510Sstevel@tonic-gate r2 = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 15520Sstevel@tonic-gate &errmsg); 15530Sstevel@tonic-gate r2 = backend_error(be, r2, errmsg); 15540Sstevel@tonic-gate if (r2 != REP_PROTOCOL_SUCCESS) 15550Sstevel@tonic-gate backend_panic("cannot rollback failed commit"); 15560Sstevel@tonic-gate 15570Sstevel@tonic-gate backend_tx_end(tx); 15580Sstevel@tonic-gate return (r); 15590Sstevel@tonic-gate } 15600Sstevel@tonic-gate backend_tx_end(tx); 15610Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 15620Sstevel@tonic-gate } 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate static const char * 15650Sstevel@tonic-gate id_space_to_name(enum id_space id) 15660Sstevel@tonic-gate { 15670Sstevel@tonic-gate switch (id) { 15680Sstevel@tonic-gate case BACKEND_ID_SERVICE_INSTANCE: 15690Sstevel@tonic-gate return ("SI"); 15700Sstevel@tonic-gate case BACKEND_ID_PROPERTYGRP: 15710Sstevel@tonic-gate return ("PG"); 15720Sstevel@tonic-gate case BACKEND_ID_GENERATION: 15730Sstevel@tonic-gate return ("GEN"); 15740Sstevel@tonic-gate case BACKEND_ID_PROPERTY: 15750Sstevel@tonic-gate return ("PROP"); 15760Sstevel@tonic-gate case BACKEND_ID_VALUE: 15770Sstevel@tonic-gate return ("VAL"); 15780Sstevel@tonic-gate case BACKEND_ID_SNAPNAME: 15790Sstevel@tonic-gate return ("SNAME"); 15800Sstevel@tonic-gate case BACKEND_ID_SNAPSHOT: 15810Sstevel@tonic-gate return ("SHOT"); 15820Sstevel@tonic-gate case BACKEND_ID_SNAPLEVEL: 15830Sstevel@tonic-gate return ("SLVL"); 15840Sstevel@tonic-gate default: 15850Sstevel@tonic-gate abort(); 15860Sstevel@tonic-gate /*NOTREACHED*/ 15870Sstevel@tonic-gate } 15880Sstevel@tonic-gate } 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate /* 15910Sstevel@tonic-gate * Returns a new id or 0 if the id argument is invalid or the query fails. 15920Sstevel@tonic-gate */ 15930Sstevel@tonic-gate uint32_t 15940Sstevel@tonic-gate backend_new_id(backend_tx_t *tx, enum id_space id) 15950Sstevel@tonic-gate { 15960Sstevel@tonic-gate struct run_single_int_info info; 15970Sstevel@tonic-gate uint32_t new_id = 0; 15980Sstevel@tonic-gate const char *name = id_space_to_name(id); 15990Sstevel@tonic-gate char *errmsg; 16000Sstevel@tonic-gate int ret; 16010Sstevel@tonic-gate sqlite_backend_t *be; 16020Sstevel@tonic-gate hrtime_t ts, vts; 16030Sstevel@tonic-gate 16040Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 16050Sstevel@tonic-gate be = tx->bt_be; 16060Sstevel@tonic-gate 16070Sstevel@tonic-gate info.rs_out = &new_id; 16080Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 16090Sstevel@tonic-gate 16100Sstevel@tonic-gate ts = gethrtime(); 16110Sstevel@tonic-gate vts = gethrvtime(); 16120Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 16130Sstevel@tonic-gate "SELECT id_next FROM id_tbl WHERE (id_name = '%q');" 16140Sstevel@tonic-gate "UPDATE id_tbl SET id_next = id_next + 1 WHERE (id_name = '%q');", 16150Sstevel@tonic-gate run_single_int_callback, &info, &errmsg, name, name); 16160Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 16170Sstevel@tonic-gate if (ret == SQLITE_FULL) 16180Sstevel@tonic-gate tx->bt_full = 1; 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) { 16230Sstevel@tonic-gate return (0); 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate return (new_id); 16270Sstevel@tonic-gate } 16280Sstevel@tonic-gate 16290Sstevel@tonic-gate /* 16300Sstevel@tonic-gate * Returns 16310Sstevel@tonic-gate * _NO_RESOURCES - out of memory 16320Sstevel@tonic-gate * _DONE - callback aborted query 16330Sstevel@tonic-gate * _SUCCESS 16340Sstevel@tonic-gate */ 16350Sstevel@tonic-gate int 16360Sstevel@tonic-gate backend_tx_run(backend_tx_t *tx, backend_query_t *q, 16370Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 16380Sstevel@tonic-gate { 16390Sstevel@tonic-gate char *errmsg = NULL; 16400Sstevel@tonic-gate int ret; 16410Sstevel@tonic-gate sqlite_backend_t *be; 16420Sstevel@tonic-gate hrtime_t ts, vts; 16430Sstevel@tonic-gate 16440Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL); 16450Sstevel@tonic-gate be = tx->bt_be; 16460Sstevel@tonic-gate 16470Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 16480Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 16490Sstevel@tonic-gate 16500Sstevel@tonic-gate ts = gethrtime(); 16510Sstevel@tonic-gate vts = gethrvtime(); 16520Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 16530Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 16540Sstevel@tonic-gate if (ret == SQLITE_FULL) 16550Sstevel@tonic-gate tx->bt_full = 1; 16560Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 16570Sstevel@tonic-gate 16580Sstevel@tonic-gate return (ret); 16590Sstevel@tonic-gate } 16600Sstevel@tonic-gate 16610Sstevel@tonic-gate /* 16620Sstevel@tonic-gate * Returns 16630Sstevel@tonic-gate * _NO_RESOURCES - out of memory 16640Sstevel@tonic-gate * _NOT_FOUND - the query returned no results 16650Sstevel@tonic-gate * _SUCCESS - the query returned a single integer 16660Sstevel@tonic-gate */ 16670Sstevel@tonic-gate int 16680Sstevel@tonic-gate backend_tx_run_single_int(backend_tx_t *tx, backend_query_t *q, uint32_t *buf) 16690Sstevel@tonic-gate { 16700Sstevel@tonic-gate struct run_single_int_info info; 16710Sstevel@tonic-gate int ret; 16720Sstevel@tonic-gate 16730Sstevel@tonic-gate info.rs_out = buf; 16740Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 16750Sstevel@tonic-gate 16760Sstevel@tonic-gate ret = backend_tx_run(tx, q, run_single_int_callback, &info); 16770Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 16780Sstevel@tonic-gate 16790Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) 16800Sstevel@tonic-gate return (ret); 16810Sstevel@tonic-gate 16820Sstevel@tonic-gate return (info.rs_result); 16830Sstevel@tonic-gate } 16840Sstevel@tonic-gate 16850Sstevel@tonic-gate /* 16860Sstevel@tonic-gate * Fails with 16870Sstevel@tonic-gate * _NO_RESOURCES - out of memory 16880Sstevel@tonic-gate */ 16890Sstevel@tonic-gate int 16900Sstevel@tonic-gate backend_tx_run_update(backend_tx_t *tx, const char *format, ...) 16910Sstevel@tonic-gate { 16920Sstevel@tonic-gate va_list a; 16930Sstevel@tonic-gate char *errmsg; 16940Sstevel@tonic-gate int ret; 16950Sstevel@tonic-gate sqlite_backend_t *be; 16960Sstevel@tonic-gate hrtime_t ts, vts; 16970Sstevel@tonic-gate 16980Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 16990Sstevel@tonic-gate be = tx->bt_be; 17000Sstevel@tonic-gate 17010Sstevel@tonic-gate va_start(a, format); 17020Sstevel@tonic-gate ts = gethrtime(); 17030Sstevel@tonic-gate vts = gethrvtime(); 17040Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 17050Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 17060Sstevel@tonic-gate if (ret == SQLITE_FULL) 17070Sstevel@tonic-gate tx->bt_full = 1; 17080Sstevel@tonic-gate va_end(a); 17090Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 17100Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 17110Sstevel@tonic-gate 17120Sstevel@tonic-gate return (ret); 17130Sstevel@tonic-gate } 17140Sstevel@tonic-gate 17150Sstevel@tonic-gate /* 17160Sstevel@tonic-gate * returns REP_PROTOCOL_FAIL_NOT_FOUND if no changes occured 17170Sstevel@tonic-gate */ 17180Sstevel@tonic-gate int 17190Sstevel@tonic-gate backend_tx_run_update_changed(backend_tx_t *tx, const char *format, ...) 17200Sstevel@tonic-gate { 17210Sstevel@tonic-gate va_list a; 17220Sstevel@tonic-gate char *errmsg; 17230Sstevel@tonic-gate int ret; 17240Sstevel@tonic-gate sqlite_backend_t *be; 17250Sstevel@tonic-gate hrtime_t ts, vts; 17260Sstevel@tonic-gate 17270Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 17280Sstevel@tonic-gate be = tx->bt_be; 17290Sstevel@tonic-gate 17300Sstevel@tonic-gate va_start(a, format); 17310Sstevel@tonic-gate ts = gethrtime(); 17320Sstevel@tonic-gate vts = gethrvtime(); 17330Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 17340Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 17350Sstevel@tonic-gate if (ret == SQLITE_FULL) 17360Sstevel@tonic-gate tx->bt_full = 1; 17370Sstevel@tonic-gate va_end(a); 17380Sstevel@tonic-gate 17390Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 17400Sstevel@tonic-gate 17410Sstevel@tonic-gate return (ret); 17420Sstevel@tonic-gate } 17430Sstevel@tonic-gate 17440Sstevel@tonic-gate #define BACKEND_ADD_SCHEMA(be, file, tbls, idxs) \ 17450Sstevel@tonic-gate (backend_add_schema((be), (file), \ 17460Sstevel@tonic-gate (tbls), sizeof (tbls) / sizeof (*(tbls)), \ 17470Sstevel@tonic-gate (idxs), sizeof (idxs) / sizeof (*(idxs)))) 17480Sstevel@tonic-gate 17490Sstevel@tonic-gate static int 17500Sstevel@tonic-gate backend_add_schema(sqlite_backend_t *be, const char *file, 17510Sstevel@tonic-gate struct backend_tbl_info *tbls, int tbl_count, 17520Sstevel@tonic-gate struct backend_idx_info *idxs, int idx_count) 17530Sstevel@tonic-gate { 17540Sstevel@tonic-gate int i; 17550Sstevel@tonic-gate char *errmsg; 17560Sstevel@tonic-gate int ret; 17570Sstevel@tonic-gate 17580Sstevel@tonic-gate /* 17590Sstevel@tonic-gate * Create the tables. 17600Sstevel@tonic-gate */ 17610Sstevel@tonic-gate for (i = 0; i < tbl_count; i++) { 17620Sstevel@tonic-gate if (tbls[i].bti_name == NULL) { 17630Sstevel@tonic-gate assert(i + 1 == tbl_count); 17640Sstevel@tonic-gate break; 17650Sstevel@tonic-gate } 17660Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 17670Sstevel@tonic-gate "CREATE TABLE %s (%s);\n", 17680Sstevel@tonic-gate NULL, NULL, &errmsg, tbls[i].bti_name, tbls[i].bti_cols); 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate if (ret != SQLITE_OK) { 17710Sstevel@tonic-gate configd_critical( 17720Sstevel@tonic-gate "%s: %s table creation fails: %s\n", file, 17730Sstevel@tonic-gate tbls[i].bti_name, errmsg); 17740Sstevel@tonic-gate free(errmsg); 17750Sstevel@tonic-gate return (-1); 17760Sstevel@tonic-gate } 17770Sstevel@tonic-gate } 17780Sstevel@tonic-gate 17790Sstevel@tonic-gate /* 17800Sstevel@tonic-gate * Make indices on key tables and columns. 17810Sstevel@tonic-gate */ 17820Sstevel@tonic-gate for (i = 0; i < idx_count; i++) { 17830Sstevel@tonic-gate if (idxs[i].bxi_tbl == NULL) { 17840Sstevel@tonic-gate assert(i + 1 == idx_count); 17850Sstevel@tonic-gate break; 17860Sstevel@tonic-gate } 17870Sstevel@tonic-gate 17880Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 17890Sstevel@tonic-gate "CREATE INDEX %s_%s ON %s (%s);\n", 17900Sstevel@tonic-gate NULL, NULL, &errmsg, idxs[i].bxi_tbl, idxs[i].bxi_idx, 17910Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_cols); 17920Sstevel@tonic-gate 17930Sstevel@tonic-gate if (ret != SQLITE_OK) { 17940Sstevel@tonic-gate configd_critical( 17950Sstevel@tonic-gate "%s: %s_%s index creation fails: %s\n", file, 17960Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_idx, errmsg); 17970Sstevel@tonic-gate free(errmsg); 17980Sstevel@tonic-gate return (-1); 17990Sstevel@tonic-gate } 18000Sstevel@tonic-gate } 18010Sstevel@tonic-gate return (0); 18020Sstevel@tonic-gate } 18030Sstevel@tonic-gate 18040Sstevel@tonic-gate static int 18050Sstevel@tonic-gate backend_init_schema(sqlite_backend_t *be, const char *db_file, backend_type_t t) 18060Sstevel@tonic-gate { 18070Sstevel@tonic-gate int i; 18080Sstevel@tonic-gate char *errmsg; 18090Sstevel@tonic-gate int ret; 18100Sstevel@tonic-gate 18110Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || t == BACKEND_TYPE_NONPERSIST); 18120Sstevel@tonic-gate 18130Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) { 18140Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_normal, idxs_normal); 18150Sstevel@tonic-gate } else if (t == BACKEND_TYPE_NONPERSIST) { 18160Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_np, idxs_np); 18170Sstevel@tonic-gate } else { 18180Sstevel@tonic-gate abort(); /* can't happen */ 18190Sstevel@tonic-gate } 18200Sstevel@tonic-gate 18210Sstevel@tonic-gate if (ret < 0) { 18220Sstevel@tonic-gate return (ret); 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate 18250Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_common, idxs_common); 18260Sstevel@tonic-gate if (ret < 0) { 18270Sstevel@tonic-gate return (ret); 18280Sstevel@tonic-gate } 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate /* 18310Sstevel@tonic-gate * Add the schema version to the table 18320Sstevel@tonic-gate */ 18330Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 18340Sstevel@tonic-gate "INSERT INTO schema_version (schema_version) VALUES (%d)", 18350Sstevel@tonic-gate NULL, NULL, &errmsg, BACKEND_SCHEMA_VERSION); 18360Sstevel@tonic-gate if (ret != SQLITE_OK) { 18370Sstevel@tonic-gate configd_critical( 18380Sstevel@tonic-gate "setting schema version fails: %s\n", errmsg); 18390Sstevel@tonic-gate free(errmsg); 18400Sstevel@tonic-gate } 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate /* 18430Sstevel@tonic-gate * Populate id_tbl with initial IDs. 18440Sstevel@tonic-gate */ 18450Sstevel@tonic-gate for (i = 0; i < BACKEND_ID_INVALID; i++) { 18460Sstevel@tonic-gate const char *name = id_space_to_name(i); 18470Sstevel@tonic-gate 18480Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 18490Sstevel@tonic-gate "INSERT INTO id_tbl (id_name, id_next) " 18500Sstevel@tonic-gate "VALUES ('%q', %d);", NULL, NULL, &errmsg, name, 1); 18510Sstevel@tonic-gate if (ret != SQLITE_OK) { 18520Sstevel@tonic-gate configd_critical( 18530Sstevel@tonic-gate "id insertion for %s fails: %s\n", name, errmsg); 18540Sstevel@tonic-gate free(errmsg); 18550Sstevel@tonic-gate return (-1); 18560Sstevel@tonic-gate } 18570Sstevel@tonic-gate } 18580Sstevel@tonic-gate /* 18590Sstevel@tonic-gate * Set the persistance of the database. The normal database is marked 18600Sstevel@tonic-gate * "synchronous", so that all writes are synchronized to stable storage 18610Sstevel@tonic-gate * before proceeding. 18620Sstevel@tonic-gate */ 18630Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 18640Sstevel@tonic-gate "PRAGMA default_synchronous = %s; PRAGMA synchronous = %s;", 18650Sstevel@tonic-gate NULL, NULL, &errmsg, 18660Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF", 18670Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF"); 18680Sstevel@tonic-gate if (ret != SQLITE_OK) { 18690Sstevel@tonic-gate configd_critical("pragma setting fails: %s\n", errmsg); 18700Sstevel@tonic-gate free(errmsg); 18710Sstevel@tonic-gate return (-1); 18720Sstevel@tonic-gate } 18730Sstevel@tonic-gate 18740Sstevel@tonic-gate return (0); 18750Sstevel@tonic-gate } 18760Sstevel@tonic-gate 18770Sstevel@tonic-gate int 18780Sstevel@tonic-gate backend_init(const char *db_file, const char *npdb_file, int have_np) 18790Sstevel@tonic-gate { 18800Sstevel@tonic-gate sqlite_backend_t *be; 18810Sstevel@tonic-gate int r; 18820Sstevel@tonic-gate int writable_persist = 1; 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate /* set up our temporary directory */ 18850Sstevel@tonic-gate sqlite_temp_directory = "/etc/svc/volatile"; 18860Sstevel@tonic-gate 18870Sstevel@tonic-gate if (strcmp(SQLITE_VERSION, sqlite_version) != 0) { 18880Sstevel@tonic-gate configd_critical("Mismatched link! (%s should be %s)\n", 18890Sstevel@tonic-gate sqlite_version, SQLITE_VERSION); 18900Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 18910Sstevel@tonic-gate } 18920Sstevel@tonic-gate if (db_file == NULL) 18930Sstevel@tonic-gate db_file = REPOSITORY_DB; 18940Sstevel@tonic-gate 18950Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NORMAL, db_file, &be); 18960Sstevel@tonic-gate switch (r) { 18970Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 18980Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 18990Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 19000Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 19010Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 19020Sstevel@tonic-gate break; /* success */ 19030Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 19040Sstevel@tonic-gate writable_persist = 0; 19050Sstevel@tonic-gate break; 19060Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 19070Sstevel@tonic-gate if (backend_init_schema(be, db_file, BACKEND_TYPE_NORMAL)) { 19080Sstevel@tonic-gate backend_destroy(be); 19090Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 19100Sstevel@tonic-gate } 19110Sstevel@tonic-gate break; 19120Sstevel@tonic-gate default: 19130Sstevel@tonic-gate abort(); 19140Sstevel@tonic-gate /*NOTREACHED*/ 19150Sstevel@tonic-gate } 19160Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NORMAL, be); 19170Sstevel@tonic-gate 19180Sstevel@tonic-gate if (have_np) { 19190Sstevel@tonic-gate if (npdb_file == NULL) 19200Sstevel@tonic-gate npdb_file = NONPERSIST_DB; 19210Sstevel@tonic-gate 19220Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NONPERSIST, npdb_file, &be); 19230Sstevel@tonic-gate switch (r) { 19240Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 19250Sstevel@tonic-gate break; /* success */ 19260Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 19270Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 19280Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 19290Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 19300Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 19310Sstevel@tonic-gate configd_critical("%s: unable to write\n", npdb_file); 19320Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 19330Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 19340Sstevel@tonic-gate if (backend_init_schema(be, db_file, 19350Sstevel@tonic-gate BACKEND_TYPE_NONPERSIST)) { 19360Sstevel@tonic-gate backend_destroy(be); 19370Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 19380Sstevel@tonic-gate } 19390Sstevel@tonic-gate break; 19400Sstevel@tonic-gate default: 19410Sstevel@tonic-gate abort(); 19420Sstevel@tonic-gate /*NOTREACHED*/ 19430Sstevel@tonic-gate } 19440Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NONPERSIST, be); 19450Sstevel@tonic-gate 19460Sstevel@tonic-gate /* 19470Sstevel@tonic-gate * If we started up with a writable filesystem, but the 19480Sstevel@tonic-gate * non-persistent database needed initialization, we 19490Sstevel@tonic-gate * are booting a non-global zone, so do a backup. 19500Sstevel@tonic-gate */ 19510Sstevel@tonic-gate if (r == BACKEND_CREATE_NEED_INIT && writable_persist && 19520Sstevel@tonic-gate backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 19530Sstevel@tonic-gate REP_PROTOCOL_SUCCESS) { 19540Sstevel@tonic-gate if (backend_create_backup_locked(be, 19550Sstevel@tonic-gate REPOSITORY_BOOT_BACKUP) != REP_PROTOCOL_SUCCESS) { 19560Sstevel@tonic-gate configd_critical( 19570Sstevel@tonic-gate "unable to create \"%s\" backup of " 19580Sstevel@tonic-gate "\"%s\"\n", REPOSITORY_BOOT_BACKUP, 19590Sstevel@tonic-gate be->be_path); 19600Sstevel@tonic-gate } 19610Sstevel@tonic-gate backend_unlock(be); 19620Sstevel@tonic-gate } 19630Sstevel@tonic-gate } 19640Sstevel@tonic-gate return (CONFIGD_EXIT_OKAY); 19650Sstevel@tonic-gate } 19660Sstevel@tonic-gate 19670Sstevel@tonic-gate /* 19680Sstevel@tonic-gate * quiesce all database activity prior to exiting 19690Sstevel@tonic-gate */ 19700Sstevel@tonic-gate void 19710Sstevel@tonic-gate backend_fini(void) 19720Sstevel@tonic-gate { 19730Sstevel@tonic-gate sqlite_backend_t *be_normal, *be_np; 19740Sstevel@tonic-gate 19750Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NORMAL, 1, &be_normal); 19760Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NONPERSIST, 1, &be_np); 19770Sstevel@tonic-gate } 19780Sstevel@tonic-gate 19790Sstevel@tonic-gate #define QUERY_BASE 128 19800Sstevel@tonic-gate backend_query_t * 19810Sstevel@tonic-gate backend_query_alloc(void) 19820Sstevel@tonic-gate { 19830Sstevel@tonic-gate backend_query_t *q; 19840Sstevel@tonic-gate q = calloc(1, sizeof (backend_query_t)); 19850Sstevel@tonic-gate if (q != NULL) { 19860Sstevel@tonic-gate q->bq_size = QUERY_BASE; 19870Sstevel@tonic-gate q->bq_buf = calloc(1, q->bq_size); 19880Sstevel@tonic-gate if (q->bq_buf == NULL) { 19890Sstevel@tonic-gate q->bq_size = 0; 19900Sstevel@tonic-gate } 19910Sstevel@tonic-gate 19920Sstevel@tonic-gate } 19930Sstevel@tonic-gate return (q); 19940Sstevel@tonic-gate } 19950Sstevel@tonic-gate 19960Sstevel@tonic-gate void 19970Sstevel@tonic-gate backend_query_append(backend_query_t *q, const char *value) 19980Sstevel@tonic-gate { 19990Sstevel@tonic-gate char *alloc; 20000Sstevel@tonic-gate int count; 20010Sstevel@tonic-gate size_t size, old_len; 20020Sstevel@tonic-gate 20030Sstevel@tonic-gate if (q == NULL) { 20040Sstevel@tonic-gate /* We'll discover the error when we try to run the query. */ 20050Sstevel@tonic-gate return; 20060Sstevel@tonic-gate } 20070Sstevel@tonic-gate 20080Sstevel@tonic-gate while (q->bq_buf != NULL) { 20090Sstevel@tonic-gate old_len = strlen(q->bq_buf); 20100Sstevel@tonic-gate size = q->bq_size; 20110Sstevel@tonic-gate count = strlcat(q->bq_buf, value, size); 20120Sstevel@tonic-gate 20130Sstevel@tonic-gate if (count < size) 20140Sstevel@tonic-gate break; /* success */ 20150Sstevel@tonic-gate 20160Sstevel@tonic-gate q->bq_buf[old_len] = 0; 20170Sstevel@tonic-gate size = round_up_to_p2(count + 1); 20180Sstevel@tonic-gate 20190Sstevel@tonic-gate assert(size > q->bq_size); 20200Sstevel@tonic-gate alloc = realloc(q->bq_buf, size); 20210Sstevel@tonic-gate if (alloc == NULL) { 20220Sstevel@tonic-gate free(q->bq_buf); 20230Sstevel@tonic-gate q->bq_buf = NULL; 20240Sstevel@tonic-gate break; /* can't grow */ 20250Sstevel@tonic-gate } 20260Sstevel@tonic-gate 20270Sstevel@tonic-gate q->bq_buf = alloc; 20280Sstevel@tonic-gate q->bq_size = size; 20290Sstevel@tonic-gate } 20300Sstevel@tonic-gate } 20310Sstevel@tonic-gate 20320Sstevel@tonic-gate void 20330Sstevel@tonic-gate backend_query_add(backend_query_t *q, const char *format, ...) 20340Sstevel@tonic-gate { 20350Sstevel@tonic-gate va_list args; 20360Sstevel@tonic-gate char *new; 20370Sstevel@tonic-gate 20380Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 20390Sstevel@tonic-gate return; 20400Sstevel@tonic-gate 20410Sstevel@tonic-gate va_start(args, format); 20420Sstevel@tonic-gate new = sqlite_vmprintf(format, args); 20430Sstevel@tonic-gate va_end(args); 20440Sstevel@tonic-gate 20450Sstevel@tonic-gate if (new == NULL) { 20460Sstevel@tonic-gate free(q->bq_buf); 20470Sstevel@tonic-gate q->bq_buf = NULL; 20480Sstevel@tonic-gate return; 20490Sstevel@tonic-gate } 20500Sstevel@tonic-gate 20510Sstevel@tonic-gate backend_query_append(q, new); 20520Sstevel@tonic-gate 20530Sstevel@tonic-gate free(new); 20540Sstevel@tonic-gate } 20550Sstevel@tonic-gate 20560Sstevel@tonic-gate void 20570Sstevel@tonic-gate backend_query_free(backend_query_t *q) 20580Sstevel@tonic-gate { 20590Sstevel@tonic-gate if (q != NULL) { 20600Sstevel@tonic-gate if (q->bq_buf != NULL) { 20610Sstevel@tonic-gate free(q->bq_buf); 20620Sstevel@tonic-gate } 20630Sstevel@tonic-gate free(q); 20640Sstevel@tonic-gate } 20650Sstevel@tonic-gate } 2066