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