1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include <assert.h> 30*7c478bd9Sstevel@tonic-gate #include <door.h> 31*7c478bd9Sstevel@tonic-gate #include <dirent.h> 32*7c478bd9Sstevel@tonic-gate #include <errno.h> 33*7c478bd9Sstevel@tonic-gate #include <fcntl.h> 34*7c478bd9Sstevel@tonic-gate #include <limits.h> 35*7c478bd9Sstevel@tonic-gate #include <pthread.h> 36*7c478bd9Sstevel@tonic-gate #include <stdarg.h> 37*7c478bd9Sstevel@tonic-gate #include <stdio.h> 38*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 39*7c478bd9Sstevel@tonic-gate #include <string.h> 40*7c478bd9Sstevel@tonic-gate #include <unistd.h> 41*7c478bd9Sstevel@tonic-gate #include <zone.h> 42*7c478bd9Sstevel@tonic-gate 43*7c478bd9Sstevel@tonic-gate #include "configd.h" 44*7c478bd9Sstevel@tonic-gate #include "repcache_protocol.h" 45*7c478bd9Sstevel@tonic-gate 46*7c478bd9Sstevel@tonic-gate #include "sqlite/sqlite.h" 47*7c478bd9Sstevel@tonic-gate #include "sqlite/sqlite-misc.h" 48*7c478bd9Sstevel@tonic-gate 49*7c478bd9Sstevel@tonic-gate /* 50*7c478bd9Sstevel@tonic-gate * This file has two purposes: 51*7c478bd9Sstevel@tonic-gate * 52*7c478bd9Sstevel@tonic-gate * 1. It contains the database schema, and the code for setting up our backend 53*7c478bd9Sstevel@tonic-gate * databases, including installing said schema. 54*7c478bd9Sstevel@tonic-gate * 55*7c478bd9Sstevel@tonic-gate * 2. It provides a simplified interface to the SQL database library, and 56*7c478bd9Sstevel@tonic-gate * synchronizes MT access to the database. 57*7c478bd9Sstevel@tonic-gate */ 58*7c478bd9Sstevel@tonic-gate 59*7c478bd9Sstevel@tonic-gate typedef struct backend_spent { 60*7c478bd9Sstevel@tonic-gate uint64_t bs_count; 61*7c478bd9Sstevel@tonic-gate hrtime_t bs_time; 62*7c478bd9Sstevel@tonic-gate hrtime_t bs_vtime; 63*7c478bd9Sstevel@tonic-gate } backend_spent_t; 64*7c478bd9Sstevel@tonic-gate 65*7c478bd9Sstevel@tonic-gate typedef struct backend_totals { 66*7c478bd9Sstevel@tonic-gate backend_spent_t bt_lock; /* waiting for lock */ 67*7c478bd9Sstevel@tonic-gate backend_spent_t bt_exec; /* time spent executing SQL */ 68*7c478bd9Sstevel@tonic-gate } backend_totals_t; 69*7c478bd9Sstevel@tonic-gate 70*7c478bd9Sstevel@tonic-gate typedef struct sqlite_backend { 71*7c478bd9Sstevel@tonic-gate pthread_mutex_t be_lock; 72*7c478bd9Sstevel@tonic-gate pthread_t be_thread; /* thread holding lock */ 73*7c478bd9Sstevel@tonic-gate struct sqlite *be_db; 74*7c478bd9Sstevel@tonic-gate const char *be_path; /* path to db */ 75*7c478bd9Sstevel@tonic-gate int be_readonly; /* backend is read-only */ 76*7c478bd9Sstevel@tonic-gate int be_writing; /* held for writing */ 77*7c478bd9Sstevel@tonic-gate backend_type_t be_type; /* type of db */ 78*7c478bd9Sstevel@tonic-gate backend_totals_t be_totals[2]; /* one for reading, one for writing */ 79*7c478bd9Sstevel@tonic-gate } sqlite_backend_t; 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate struct backend_tx { 82*7c478bd9Sstevel@tonic-gate sqlite_backend_t *bt_be; 83*7c478bd9Sstevel@tonic-gate int bt_readonly; 84*7c478bd9Sstevel@tonic-gate int bt_type; 85*7c478bd9Sstevel@tonic-gate int bt_full; /* SQLITE_FULL during tx */ 86*7c478bd9Sstevel@tonic-gate }; 87*7c478bd9Sstevel@tonic-gate 88*7c478bd9Sstevel@tonic-gate #define UPDATE_TOTALS_WR(sb, writing, field, ts, vts) { \ 89*7c478bd9Sstevel@tonic-gate backend_spent_t *__bsp = &(sb)->be_totals[!!(writing)].field; \ 90*7c478bd9Sstevel@tonic-gate __bsp->bs_count++; \ 91*7c478bd9Sstevel@tonic-gate __bsp->bs_time += (gethrtime() - ts); \ 92*7c478bd9Sstevel@tonic-gate __bsp->bs_vtime += (gethrvtime() - vts); \ 93*7c478bd9Sstevel@tonic-gate } 94*7c478bd9Sstevel@tonic-gate 95*7c478bd9Sstevel@tonic-gate #define UPDATE_TOTALS(sb, field, ts, vts) \ 96*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS_WR(sb, (sb)->be_writing, field, ts, vts) 97*7c478bd9Sstevel@tonic-gate 98*7c478bd9Sstevel@tonic-gate struct backend_query { 99*7c478bd9Sstevel@tonic-gate char *bq_buf; 100*7c478bd9Sstevel@tonic-gate size_t bq_size; 101*7c478bd9Sstevel@tonic-gate }; 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate struct backend_tbl_info { 104*7c478bd9Sstevel@tonic-gate const char *bti_name; 105*7c478bd9Sstevel@tonic-gate const char *bti_cols; 106*7c478bd9Sstevel@tonic-gate }; 107*7c478bd9Sstevel@tonic-gate 108*7c478bd9Sstevel@tonic-gate struct backend_idx_info { 109*7c478bd9Sstevel@tonic-gate const char *bxi_tbl; 110*7c478bd9Sstevel@tonic-gate const char *bxi_idx; 111*7c478bd9Sstevel@tonic-gate const char *bxi_cols; 112*7c478bd9Sstevel@tonic-gate }; 113*7c478bd9Sstevel@tonic-gate 114*7c478bd9Sstevel@tonic-gate static pthread_mutex_t backend_panic_lock = PTHREAD_MUTEX_INITIALIZER; 115*7c478bd9Sstevel@tonic-gate static pthread_cond_t backend_panic_cv = PTHREAD_COND_INITIALIZER; 116*7c478bd9Sstevel@tonic-gate pthread_t backend_panic_thread = 0; 117*7c478bd9Sstevel@tonic-gate 118*7c478bd9Sstevel@tonic-gate int backend_do_trace = 0; /* invoke tracing callback */ 119*7c478bd9Sstevel@tonic-gate int backend_print_trace = 0; /* tracing callback prints SQL */ 120*7c478bd9Sstevel@tonic-gate int backend_panic_abort = 0; /* abort when panicking */ 121*7c478bd9Sstevel@tonic-gate 122*7c478bd9Sstevel@tonic-gate /* 123*7c478bd9Sstevel@tonic-gate * Any change to the below schema should bump the version number 124*7c478bd9Sstevel@tonic-gate */ 125*7c478bd9Sstevel@tonic-gate #define BACKEND_SCHEMA_VERSION 5 126*7c478bd9Sstevel@tonic-gate 127*7c478bd9Sstevel@tonic-gate static struct backend_tbl_info tbls_normal[] = { /* BACKEND_TYPE_NORMAL */ 128*7c478bd9Sstevel@tonic-gate /* 129*7c478bd9Sstevel@tonic-gate * service_tbl holds all services. svc_id is the identifier of the 130*7c478bd9Sstevel@tonic-gate * service. 131*7c478bd9Sstevel@tonic-gate */ 132*7c478bd9Sstevel@tonic-gate { 133*7c478bd9Sstevel@tonic-gate "service_tbl", 134*7c478bd9Sstevel@tonic-gate "svc_id INTEGER PRIMARY KEY," 135*7c478bd9Sstevel@tonic-gate "svc_name CHAR(256) NOT NULL" 136*7c478bd9Sstevel@tonic-gate }, 137*7c478bd9Sstevel@tonic-gate 138*7c478bd9Sstevel@tonic-gate /* 139*7c478bd9Sstevel@tonic-gate * instance_tbl holds all of the instances. The parent service id 140*7c478bd9Sstevel@tonic-gate * is instance_svc. 141*7c478bd9Sstevel@tonic-gate */ 142*7c478bd9Sstevel@tonic-gate { 143*7c478bd9Sstevel@tonic-gate "instance_tbl", 144*7c478bd9Sstevel@tonic-gate "instance_id INTEGER PRIMARY KEY," 145*7c478bd9Sstevel@tonic-gate "instance_name CHAR(256) NOT NULL," 146*7c478bd9Sstevel@tonic-gate "instance_svc INTEGER NOT NULL" 147*7c478bd9Sstevel@tonic-gate }, 148*7c478bd9Sstevel@tonic-gate 149*7c478bd9Sstevel@tonic-gate /* 150*7c478bd9Sstevel@tonic-gate * snapshot_lnk_tbl links (instance, snapshot name) with snapshots. 151*7c478bd9Sstevel@tonic-gate */ 152*7c478bd9Sstevel@tonic-gate { 153*7c478bd9Sstevel@tonic-gate "snapshot_lnk_tbl", 154*7c478bd9Sstevel@tonic-gate "lnk_id INTEGER PRIMARY KEY," 155*7c478bd9Sstevel@tonic-gate "lnk_inst_id INTEGER NOT NULL," 156*7c478bd9Sstevel@tonic-gate "lnk_snap_name CHAR(256) NOT NULL," 157*7c478bd9Sstevel@tonic-gate "lnk_snap_id INTEGER NOT NULL" 158*7c478bd9Sstevel@tonic-gate }, 159*7c478bd9Sstevel@tonic-gate 160*7c478bd9Sstevel@tonic-gate /* 161*7c478bd9Sstevel@tonic-gate * snaplevel_tbl maps a snapshot id to a set of named, ordered 162*7c478bd9Sstevel@tonic-gate * snaplevels. 163*7c478bd9Sstevel@tonic-gate */ 164*7c478bd9Sstevel@tonic-gate { 165*7c478bd9Sstevel@tonic-gate "snaplevel_tbl", 166*7c478bd9Sstevel@tonic-gate "snap_id INTEGER NOT NULL," 167*7c478bd9Sstevel@tonic-gate "snap_level_num INTEGER NOT NULL," 168*7c478bd9Sstevel@tonic-gate "snap_level_id INTEGER NOT NULL," 169*7c478bd9Sstevel@tonic-gate "snap_level_service_id INTEGER NOT NULL," 170*7c478bd9Sstevel@tonic-gate "snap_level_service CHAR(256) NOT NULL," 171*7c478bd9Sstevel@tonic-gate "snap_level_instance_id INTEGER NULL," 172*7c478bd9Sstevel@tonic-gate "snap_level_instance CHAR(256) NULL" 173*7c478bd9Sstevel@tonic-gate }, 174*7c478bd9Sstevel@tonic-gate 175*7c478bd9Sstevel@tonic-gate /* 176*7c478bd9Sstevel@tonic-gate * snaplevel_lnk_tbl links snaplevels to property groups. 177*7c478bd9Sstevel@tonic-gate * snaplvl_pg_* is identical to the original property group, 178*7c478bd9Sstevel@tonic-gate * and snaplvl_gen_id overrides the generation number. 179*7c478bd9Sstevel@tonic-gate * The service/instance ids are as in the snaplevel. 180*7c478bd9Sstevel@tonic-gate */ 181*7c478bd9Sstevel@tonic-gate { 182*7c478bd9Sstevel@tonic-gate "snaplevel_lnk_tbl", 183*7c478bd9Sstevel@tonic-gate "snaplvl_level_id INTEGER NOT NULL," 184*7c478bd9Sstevel@tonic-gate "snaplvl_pg_id INTEGER NOT NULL," 185*7c478bd9Sstevel@tonic-gate "snaplvl_pg_name CHAR(256) NOT NULL," 186*7c478bd9Sstevel@tonic-gate "snaplvl_pg_type CHAR(256) NOT NULL," 187*7c478bd9Sstevel@tonic-gate "snaplvl_pg_flags INTEGER NOT NULL," 188*7c478bd9Sstevel@tonic-gate "snaplvl_gen_id INTEGER NOT NULL" 189*7c478bd9Sstevel@tonic-gate }, 190*7c478bd9Sstevel@tonic-gate 191*7c478bd9Sstevel@tonic-gate { NULL, NULL } 192*7c478bd9Sstevel@tonic-gate }; 193*7c478bd9Sstevel@tonic-gate 194*7c478bd9Sstevel@tonic-gate static struct backend_idx_info idxs_normal[] = { /* BACKEND_TYPE_NORMAL */ 195*7c478bd9Sstevel@tonic-gate { "service_tbl", "name", "svc_name" }, 196*7c478bd9Sstevel@tonic-gate { "instance_tbl", "name", "instance_svc, instance_name" }, 197*7c478bd9Sstevel@tonic-gate { "snapshot_lnk_tbl", "name", "lnk_inst_id, lnk_snap_name" }, 198*7c478bd9Sstevel@tonic-gate { "snapshot_lnk_tbl", "snapid", "lnk_snap_id" }, 199*7c478bd9Sstevel@tonic-gate { "snaplevel_tbl", "id", "snap_id" }, 200*7c478bd9Sstevel@tonic-gate { "snaplevel_lnk_tbl", "id", "snaplvl_pg_id" }, 201*7c478bd9Sstevel@tonic-gate { "snaplevel_lnk_tbl", "level", "snaplvl_level_id" }, 202*7c478bd9Sstevel@tonic-gate { NULL, NULL, NULL } 203*7c478bd9Sstevel@tonic-gate }; 204*7c478bd9Sstevel@tonic-gate 205*7c478bd9Sstevel@tonic-gate static struct backend_tbl_info tbls_np[] = { /* BACKEND_TYPE_NONPERSIST */ 206*7c478bd9Sstevel@tonic-gate { NULL, NULL } 207*7c478bd9Sstevel@tonic-gate }; 208*7c478bd9Sstevel@tonic-gate 209*7c478bd9Sstevel@tonic-gate static struct backend_idx_info idxs_np[] = { /* BACKEND_TYPE_NONPERSIST */ 210*7c478bd9Sstevel@tonic-gate { NULL, NULL, NULL } 211*7c478bd9Sstevel@tonic-gate }; 212*7c478bd9Sstevel@tonic-gate 213*7c478bd9Sstevel@tonic-gate static struct backend_tbl_info tbls_common[] = { /* all backend types */ 214*7c478bd9Sstevel@tonic-gate /* 215*7c478bd9Sstevel@tonic-gate * pg_tbl defines property groups. They are associated with a single 216*7c478bd9Sstevel@tonic-gate * service or instance. The pg_gen_id links them with the latest 217*7c478bd9Sstevel@tonic-gate * "edited" version of its properties. 218*7c478bd9Sstevel@tonic-gate */ 219*7c478bd9Sstevel@tonic-gate { 220*7c478bd9Sstevel@tonic-gate "pg_tbl", 221*7c478bd9Sstevel@tonic-gate "pg_id INTEGER PRIMARY KEY," 222*7c478bd9Sstevel@tonic-gate "pg_parent_id INTEGER NOT NULL," 223*7c478bd9Sstevel@tonic-gate "pg_name CHAR(256) NOT NULL," 224*7c478bd9Sstevel@tonic-gate "pg_type CHAR(256) NOT NULL," 225*7c478bd9Sstevel@tonic-gate "pg_flags INTEGER NOT NULL," 226*7c478bd9Sstevel@tonic-gate "pg_gen_id INTEGER NOT NULL" 227*7c478bd9Sstevel@tonic-gate }, 228*7c478bd9Sstevel@tonic-gate 229*7c478bd9Sstevel@tonic-gate /* 230*7c478bd9Sstevel@tonic-gate * prop_lnk_tbl links a particular pg_id and gen_id to a set of 231*7c478bd9Sstevel@tonic-gate * (prop_name, prop_type, val_id) trios. 232*7c478bd9Sstevel@tonic-gate */ 233*7c478bd9Sstevel@tonic-gate { 234*7c478bd9Sstevel@tonic-gate "prop_lnk_tbl", 235*7c478bd9Sstevel@tonic-gate "lnk_prop_id INTEGER PRIMARY KEY," 236*7c478bd9Sstevel@tonic-gate "lnk_pg_id INTEGER NOT NULL," 237*7c478bd9Sstevel@tonic-gate "lnk_gen_id INTEGER NOT NULL," 238*7c478bd9Sstevel@tonic-gate "lnk_prop_name CHAR(256) NOT NULL," 239*7c478bd9Sstevel@tonic-gate "lnk_prop_type CHAR(2) NOT NULL," 240*7c478bd9Sstevel@tonic-gate "lnk_val_id INTEGER" 241*7c478bd9Sstevel@tonic-gate }, 242*7c478bd9Sstevel@tonic-gate 243*7c478bd9Sstevel@tonic-gate /* 244*7c478bd9Sstevel@tonic-gate * value_tbl maps a value_id to a set of values. For any given 245*7c478bd9Sstevel@tonic-gate * value_id, value_type is constant. 246*7c478bd9Sstevel@tonic-gate */ 247*7c478bd9Sstevel@tonic-gate { 248*7c478bd9Sstevel@tonic-gate "value_tbl", 249*7c478bd9Sstevel@tonic-gate "value_id INTEGER NOT NULL," 250*7c478bd9Sstevel@tonic-gate "value_type CHAR(1) NOT NULL," 251*7c478bd9Sstevel@tonic-gate "value_value VARCHAR NOT NULL" 252*7c478bd9Sstevel@tonic-gate }, 253*7c478bd9Sstevel@tonic-gate 254*7c478bd9Sstevel@tonic-gate /* 255*7c478bd9Sstevel@tonic-gate * id_tbl has one row per id space 256*7c478bd9Sstevel@tonic-gate */ 257*7c478bd9Sstevel@tonic-gate { 258*7c478bd9Sstevel@tonic-gate "id_tbl", 259*7c478bd9Sstevel@tonic-gate "id_name STRING NOT NULL," 260*7c478bd9Sstevel@tonic-gate "id_next INTEGER NOT NULL" 261*7c478bd9Sstevel@tonic-gate }, 262*7c478bd9Sstevel@tonic-gate 263*7c478bd9Sstevel@tonic-gate /* 264*7c478bd9Sstevel@tonic-gate * schema_version has a single row, which contains 265*7c478bd9Sstevel@tonic-gate * BACKEND_SCHEMA_VERSION at the time of creation. 266*7c478bd9Sstevel@tonic-gate */ 267*7c478bd9Sstevel@tonic-gate { 268*7c478bd9Sstevel@tonic-gate "schema_version", 269*7c478bd9Sstevel@tonic-gate "schema_version INTEGER" 270*7c478bd9Sstevel@tonic-gate }, 271*7c478bd9Sstevel@tonic-gate { NULL, NULL } 272*7c478bd9Sstevel@tonic-gate }; 273*7c478bd9Sstevel@tonic-gate 274*7c478bd9Sstevel@tonic-gate static struct backend_idx_info idxs_common[] = { /* all backend types */ 275*7c478bd9Sstevel@tonic-gate { "pg_tbl", "parent", "pg_parent_id" }, 276*7c478bd9Sstevel@tonic-gate { "pg_tbl", "name", "pg_parent_id, pg_name" }, 277*7c478bd9Sstevel@tonic-gate { "pg_tbl", "type", "pg_parent_id, pg_type" }, 278*7c478bd9Sstevel@tonic-gate { "prop_lnk_tbl", "base", "lnk_pg_id, lnk_gen_id" }, 279*7c478bd9Sstevel@tonic-gate { "prop_lnk_tbl", "val", "lnk_val_id" }, 280*7c478bd9Sstevel@tonic-gate { "value_tbl", "id", "value_id" }, 281*7c478bd9Sstevel@tonic-gate { "id_tbl", "id", "id_name" }, 282*7c478bd9Sstevel@tonic-gate { NULL, NULL, NULL } 283*7c478bd9Sstevel@tonic-gate }; 284*7c478bd9Sstevel@tonic-gate 285*7c478bd9Sstevel@tonic-gate struct run_single_int_info { 286*7c478bd9Sstevel@tonic-gate uint32_t *rs_out; 287*7c478bd9Sstevel@tonic-gate int rs_result; 288*7c478bd9Sstevel@tonic-gate }; 289*7c478bd9Sstevel@tonic-gate 290*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 291*7c478bd9Sstevel@tonic-gate static int 292*7c478bd9Sstevel@tonic-gate run_single_int_callback(void *arg, int columns, char **vals, char **names) 293*7c478bd9Sstevel@tonic-gate { 294*7c478bd9Sstevel@tonic-gate struct run_single_int_info *info = arg; 295*7c478bd9Sstevel@tonic-gate uint32_t val; 296*7c478bd9Sstevel@tonic-gate 297*7c478bd9Sstevel@tonic-gate char *endptr = vals[0]; 298*7c478bd9Sstevel@tonic-gate 299*7c478bd9Sstevel@tonic-gate assert(info->rs_result != REP_PROTOCOL_SUCCESS); 300*7c478bd9Sstevel@tonic-gate assert(columns == 1); 301*7c478bd9Sstevel@tonic-gate 302*7c478bd9Sstevel@tonic-gate if (vals[0] == NULL) 303*7c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 304*7c478bd9Sstevel@tonic-gate 305*7c478bd9Sstevel@tonic-gate errno = 0; 306*7c478bd9Sstevel@tonic-gate val = strtoul(vals[0], &endptr, 10); 307*7c478bd9Sstevel@tonic-gate if ((val == 0 && endptr == vals[0]) || *endptr != 0 || errno != 0) 308*7c478bd9Sstevel@tonic-gate backend_panic("malformed integer \"%20s\"", vals[0]); 309*7c478bd9Sstevel@tonic-gate 310*7c478bd9Sstevel@tonic-gate *info->rs_out = val; 311*7c478bd9Sstevel@tonic-gate info->rs_result = REP_PROTOCOL_SUCCESS; 312*7c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 313*7c478bd9Sstevel@tonic-gate } 314*7c478bd9Sstevel@tonic-gate 315*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 316*7c478bd9Sstevel@tonic-gate int 317*7c478bd9Sstevel@tonic-gate backend_fail_if_seen(void *arg, int columns, char **vals, char **names) 318*7c478bd9Sstevel@tonic-gate { 319*7c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 320*7c478bd9Sstevel@tonic-gate } 321*7c478bd9Sstevel@tonic-gate 322*7c478bd9Sstevel@tonic-gate static int 323*7c478bd9Sstevel@tonic-gate backend_is_readonly(struct sqlite *db, char **errp) 324*7c478bd9Sstevel@tonic-gate { 325*7c478bd9Sstevel@tonic-gate int r = sqlite_exec(db, 326*7c478bd9Sstevel@tonic-gate "BEGIN TRANSACTION; " 327*7c478bd9Sstevel@tonic-gate "UPDATE schema_version SET schema_version = schema_version; ", 328*7c478bd9Sstevel@tonic-gate NULL, NULL, errp); 329*7c478bd9Sstevel@tonic-gate 330*7c478bd9Sstevel@tonic-gate (void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); 331*7c478bd9Sstevel@tonic-gate return (r); 332*7c478bd9Sstevel@tonic-gate } 333*7c478bd9Sstevel@tonic-gate 334*7c478bd9Sstevel@tonic-gate static void 335*7c478bd9Sstevel@tonic-gate backend_trace_sql(void *arg, const char *sql) 336*7c478bd9Sstevel@tonic-gate { 337*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be = arg; 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate if (backend_print_trace) { 340*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d: %s\n", be->be_type, sql); 341*7c478bd9Sstevel@tonic-gate } 342*7c478bd9Sstevel@tonic-gate } 343*7c478bd9Sstevel@tonic-gate 344*7c478bd9Sstevel@tonic-gate static sqlite_backend_t be_info[BACKEND_TYPE_TOTAL]; 345*7c478bd9Sstevel@tonic-gate static sqlite_backend_t *bes[BACKEND_TYPE_TOTAL]; 346*7c478bd9Sstevel@tonic-gate 347*7c478bd9Sstevel@tonic-gate #define BACKEND_PANIC_TIMEOUT (50 * MILLISEC) 348*7c478bd9Sstevel@tonic-gate /* 349*7c478bd9Sstevel@tonic-gate * backend_panic() -- some kind of database problem or corruption has been hit. 350*7c478bd9Sstevel@tonic-gate * We attempt to quiesce the other database users -- all of the backend sql 351*7c478bd9Sstevel@tonic-gate * entry points will call backend_panic(NULL) if a panic is in progress, as 352*7c478bd9Sstevel@tonic-gate * will any attempt to start a transaction. 353*7c478bd9Sstevel@tonic-gate * 354*7c478bd9Sstevel@tonic-gate * We give threads holding a backend lock 50ms (BACKEND_PANIC_TIMEOUT) to 355*7c478bd9Sstevel@tonic-gate * either drop the lock or call backend_panic(). If they don't respond in 356*7c478bd9Sstevel@tonic-gate * time, we'll just exit anyway. 357*7c478bd9Sstevel@tonic-gate */ 358*7c478bd9Sstevel@tonic-gate void 359*7c478bd9Sstevel@tonic-gate backend_panic(const char *format, ...) 360*7c478bd9Sstevel@tonic-gate { 361*7c478bd9Sstevel@tonic-gate int i; 362*7c478bd9Sstevel@tonic-gate va_list args; 363*7c478bd9Sstevel@tonic-gate int failed = 0; 364*7c478bd9Sstevel@tonic-gate 365*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 366*7c478bd9Sstevel@tonic-gate if (backend_panic_thread != 0) { 367*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 368*7c478bd9Sstevel@tonic-gate /* 369*7c478bd9Sstevel@tonic-gate * first, drop any backend locks we're holding, then 370*7c478bd9Sstevel@tonic-gate * sleep forever on the panic_cv. 371*7c478bd9Sstevel@tonic-gate */ 372*7c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 373*7c478bd9Sstevel@tonic-gate if (bes[i] != NULL && 374*7c478bd9Sstevel@tonic-gate bes[i]->be_thread == pthread_self()) 375*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 376*7c478bd9Sstevel@tonic-gate } 377*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 378*7c478bd9Sstevel@tonic-gate for (;;) 379*7c478bd9Sstevel@tonic-gate (void) pthread_cond_wait(&backend_panic_cv, 380*7c478bd9Sstevel@tonic-gate &backend_panic_lock); 381*7c478bd9Sstevel@tonic-gate } 382*7c478bd9Sstevel@tonic-gate backend_panic_thread = pthread_self(); 383*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 384*7c478bd9Sstevel@tonic-gate 385*7c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 386*7c478bd9Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread == pthread_self()) 387*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 388*7c478bd9Sstevel@tonic-gate } 389*7c478bd9Sstevel@tonic-gate 390*7c478bd9Sstevel@tonic-gate va_start(args, format); 391*7c478bd9Sstevel@tonic-gate configd_vcritical(format, args); 392*7c478bd9Sstevel@tonic-gate va_end(args); 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 395*7c478bd9Sstevel@tonic-gate timespec_t rel; 396*7c478bd9Sstevel@tonic-gate 397*7c478bd9Sstevel@tonic-gate rel.tv_sec = 0; 398*7c478bd9Sstevel@tonic-gate rel.tv_nsec = BACKEND_PANIC_TIMEOUT; 399*7c478bd9Sstevel@tonic-gate 400*7c478bd9Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread != pthread_self()) { 401*7c478bd9Sstevel@tonic-gate if (pthread_mutex_reltimedlock_np(&bes[i]->be_lock, 402*7c478bd9Sstevel@tonic-gate &rel) != 0) 403*7c478bd9Sstevel@tonic-gate failed++; 404*7c478bd9Sstevel@tonic-gate } 405*7c478bd9Sstevel@tonic-gate } 406*7c478bd9Sstevel@tonic-gate if (failed) { 407*7c478bd9Sstevel@tonic-gate configd_critical("unable to quiesce database\n"); 408*7c478bd9Sstevel@tonic-gate } 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate if (backend_panic_abort) 411*7c478bd9Sstevel@tonic-gate abort(); 412*7c478bd9Sstevel@tonic-gate 413*7c478bd9Sstevel@tonic-gate exit(CONFIGD_EXIT_DATABASE_BAD); 414*7c478bd9Sstevel@tonic-gate } 415*7c478bd9Sstevel@tonic-gate 416*7c478bd9Sstevel@tonic-gate /* 417*7c478bd9Sstevel@tonic-gate * Returns 418*7c478bd9Sstevel@tonic-gate * _SUCCESS 419*7c478bd9Sstevel@tonic-gate * _DONE - callback aborted query 420*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory (_FULL & _TOOBIG?) 421*7c478bd9Sstevel@tonic-gate */ 422*7c478bd9Sstevel@tonic-gate static int 423*7c478bd9Sstevel@tonic-gate backend_error(sqlite_backend_t *be, int error, char *errmsg) 424*7c478bd9Sstevel@tonic-gate { 425*7c478bd9Sstevel@tonic-gate if (error == SQLITE_OK) 426*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 427*7c478bd9Sstevel@tonic-gate 428*7c478bd9Sstevel@tonic-gate switch (error) { 429*7c478bd9Sstevel@tonic-gate case SQLITE_ABORT: 430*7c478bd9Sstevel@tonic-gate free(errmsg); 431*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_DONE); 432*7c478bd9Sstevel@tonic-gate 433*7c478bd9Sstevel@tonic-gate case SQLITE_NOMEM: 434*7c478bd9Sstevel@tonic-gate case SQLITE_FULL: 435*7c478bd9Sstevel@tonic-gate case SQLITE_TOOBIG: 436*7c478bd9Sstevel@tonic-gate free(errmsg); 437*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 438*7c478bd9Sstevel@tonic-gate 439*7c478bd9Sstevel@tonic-gate default: 440*7c478bd9Sstevel@tonic-gate backend_panic("%s: db error: %s", be->be_path, errmsg); 441*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 442*7c478bd9Sstevel@tonic-gate } 443*7c478bd9Sstevel@tonic-gate } 444*7c478bd9Sstevel@tonic-gate 445*7c478bd9Sstevel@tonic-gate static void 446*7c478bd9Sstevel@tonic-gate backend_backup_cleanup(const char **out_arg, ssize_t out_sz) 447*7c478bd9Sstevel@tonic-gate { 448*7c478bd9Sstevel@tonic-gate char **out = (char **)out_arg; 449*7c478bd9Sstevel@tonic-gate 450*7c478bd9Sstevel@tonic-gate while (out_sz-- > 0) 451*7c478bd9Sstevel@tonic-gate free(*out++); 452*7c478bd9Sstevel@tonic-gate free(out_arg); 453*7c478bd9Sstevel@tonic-gate } 454*7c478bd9Sstevel@tonic-gate 455*7c478bd9Sstevel@tonic-gate /* 456*7c478bd9Sstevel@tonic-gate * builds a inverse-time-sorted array of backup files. The path is a 457*7c478bd9Sstevel@tonic-gate * a single buffer, and the pointers look like: 458*7c478bd9Sstevel@tonic-gate * 459*7c478bd9Sstevel@tonic-gate * /this/is/a/full/path/to/repository-name-YYYYMMDDHHMMSS 460*7c478bd9Sstevel@tonic-gate * ^pathname ^ ^(pathname+pathlen) 461*7c478bd9Sstevel@tonic-gate * basename 462*7c478bd9Sstevel@tonic-gate * 463*7c478bd9Sstevel@tonic-gate * dirname will either be pathname, or ".". 464*7c478bd9Sstevel@tonic-gate * 465*7c478bd9Sstevel@tonic-gate * Returns the number of elements in the array, 0 if there are no previous 466*7c478bd9Sstevel@tonic-gate * backups, or -1 on error. 467*7c478bd9Sstevel@tonic-gate */ 468*7c478bd9Sstevel@tonic-gate static ssize_t 469*7c478bd9Sstevel@tonic-gate backend_backup_get_prev(char *pathname, size_t pathlen, const char ***out_arg) 470*7c478bd9Sstevel@tonic-gate { 471*7c478bd9Sstevel@tonic-gate char b_start, b_end; 472*7c478bd9Sstevel@tonic-gate DIR *dir; 473*7c478bd9Sstevel@tonic-gate char **out = NULL; 474*7c478bd9Sstevel@tonic-gate char *name, *p; 475*7c478bd9Sstevel@tonic-gate char *dirname, *basename; 476*7c478bd9Sstevel@tonic-gate char *pathend; 477*7c478bd9Sstevel@tonic-gate struct dirent *ent; 478*7c478bd9Sstevel@tonic-gate 479*7c478bd9Sstevel@tonic-gate size_t count = 0; 480*7c478bd9Sstevel@tonic-gate size_t baselen; 481*7c478bd9Sstevel@tonic-gate 482*7c478bd9Sstevel@tonic-gate /* 483*7c478bd9Sstevel@tonic-gate * year, month, day, hour, min, sec, plus an '_'. 484*7c478bd9Sstevel@tonic-gate */ 485*7c478bd9Sstevel@tonic-gate const size_t ndigits = 4 + 5*2 + 1; 486*7c478bd9Sstevel@tonic-gate const size_t baroffset = 4 + 2*2; 487*7c478bd9Sstevel@tonic-gate 488*7c478bd9Sstevel@tonic-gate size_t idx; 489*7c478bd9Sstevel@tonic-gate 490*7c478bd9Sstevel@tonic-gate pathend = pathname + pathlen; 491*7c478bd9Sstevel@tonic-gate b_end = *pathend; 492*7c478bd9Sstevel@tonic-gate *pathend = '\0'; 493*7c478bd9Sstevel@tonic-gate 494*7c478bd9Sstevel@tonic-gate basename = strrchr(pathname, '/'); 495*7c478bd9Sstevel@tonic-gate 496*7c478bd9Sstevel@tonic-gate if (basename != NULL) { 497*7c478bd9Sstevel@tonic-gate assert(pathend > pathname && basename < pathend); 498*7c478bd9Sstevel@tonic-gate basename++; 499*7c478bd9Sstevel@tonic-gate dirname = pathname; 500*7c478bd9Sstevel@tonic-gate } else { 501*7c478bd9Sstevel@tonic-gate basename = pathname; 502*7c478bd9Sstevel@tonic-gate dirname = "."; 503*7c478bd9Sstevel@tonic-gate } 504*7c478bd9Sstevel@tonic-gate 505*7c478bd9Sstevel@tonic-gate baselen = strlen(basename); 506*7c478bd9Sstevel@tonic-gate 507*7c478bd9Sstevel@tonic-gate /* 508*7c478bd9Sstevel@tonic-gate * munge the string temporarily for the opendir(), then restore it. 509*7c478bd9Sstevel@tonic-gate */ 510*7c478bd9Sstevel@tonic-gate b_start = basename[0]; 511*7c478bd9Sstevel@tonic-gate 512*7c478bd9Sstevel@tonic-gate basename[0] = '\0'; 513*7c478bd9Sstevel@tonic-gate dir = opendir(dirname); 514*7c478bd9Sstevel@tonic-gate basename[0] = b_start; /* restore path */ 515*7c478bd9Sstevel@tonic-gate 516*7c478bd9Sstevel@tonic-gate if (dir == NULL) 517*7c478bd9Sstevel@tonic-gate goto fail; 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate 520*7c478bd9Sstevel@tonic-gate while ((ent = readdir(dir)) != NULL) { 521*7c478bd9Sstevel@tonic-gate /* 522*7c478bd9Sstevel@tonic-gate * Must match: 523*7c478bd9Sstevel@tonic-gate * basename-YYYYMMDD_HHMMSS 524*7c478bd9Sstevel@tonic-gate * or we ignore it. 525*7c478bd9Sstevel@tonic-gate */ 526*7c478bd9Sstevel@tonic-gate if (strncmp(ent->d_name, basename, baselen) != 0) 527*7c478bd9Sstevel@tonic-gate continue; 528*7c478bd9Sstevel@tonic-gate 529*7c478bd9Sstevel@tonic-gate name = ent->d_name; 530*7c478bd9Sstevel@tonic-gate if (name[baselen] != '-') 531*7c478bd9Sstevel@tonic-gate continue; 532*7c478bd9Sstevel@tonic-gate 533*7c478bd9Sstevel@tonic-gate p = name + baselen + 1; 534*7c478bd9Sstevel@tonic-gate 535*7c478bd9Sstevel@tonic-gate for (idx = 0; idx < ndigits; idx++) { 536*7c478bd9Sstevel@tonic-gate char c = p[idx]; 537*7c478bd9Sstevel@tonic-gate if (idx == baroffset && c != '_') 538*7c478bd9Sstevel@tonic-gate break; 539*7c478bd9Sstevel@tonic-gate if (idx != baroffset && (c < '0' || c > '9')) 540*7c478bd9Sstevel@tonic-gate break; 541*7c478bd9Sstevel@tonic-gate } 542*7c478bd9Sstevel@tonic-gate if (idx != ndigits || p[idx] != '\0') 543*7c478bd9Sstevel@tonic-gate continue; 544*7c478bd9Sstevel@tonic-gate 545*7c478bd9Sstevel@tonic-gate /* 546*7c478bd9Sstevel@tonic-gate * We have a match. insertion-sort it into our list. 547*7c478bd9Sstevel@tonic-gate */ 548*7c478bd9Sstevel@tonic-gate name = strdup(name); 549*7c478bd9Sstevel@tonic-gate if (name == NULL) 550*7c478bd9Sstevel@tonic-gate goto fail_closedir; 551*7c478bd9Sstevel@tonic-gate p = strrchr(name, '-'); 552*7c478bd9Sstevel@tonic-gate 553*7c478bd9Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 554*7c478bd9Sstevel@tonic-gate char *tmp = out[idx]; 555*7c478bd9Sstevel@tonic-gate char *tp = strrchr(tmp, '-'); 556*7c478bd9Sstevel@tonic-gate 557*7c478bd9Sstevel@tonic-gate int cmp = strcmp(p, tp); 558*7c478bd9Sstevel@tonic-gate if (cmp == 0) 559*7c478bd9Sstevel@tonic-gate cmp = strcmp(name, tmp); 560*7c478bd9Sstevel@tonic-gate 561*7c478bd9Sstevel@tonic-gate if (cmp == 0) { 562*7c478bd9Sstevel@tonic-gate free(name); 563*7c478bd9Sstevel@tonic-gate name = NULL; 564*7c478bd9Sstevel@tonic-gate break; 565*7c478bd9Sstevel@tonic-gate } else if (cmp > 0) { 566*7c478bd9Sstevel@tonic-gate out[idx] = name; 567*7c478bd9Sstevel@tonic-gate name = tmp; 568*7c478bd9Sstevel@tonic-gate p = tp; 569*7c478bd9Sstevel@tonic-gate } 570*7c478bd9Sstevel@tonic-gate } 571*7c478bd9Sstevel@tonic-gate 572*7c478bd9Sstevel@tonic-gate if (idx == count) { 573*7c478bd9Sstevel@tonic-gate char **new_out = realloc(out, 574*7c478bd9Sstevel@tonic-gate (count + 1) * sizeof (*out)); 575*7c478bd9Sstevel@tonic-gate 576*7c478bd9Sstevel@tonic-gate if (new_out == NULL) { 577*7c478bd9Sstevel@tonic-gate free(name); 578*7c478bd9Sstevel@tonic-gate goto fail_closedir; 579*7c478bd9Sstevel@tonic-gate } 580*7c478bd9Sstevel@tonic-gate 581*7c478bd9Sstevel@tonic-gate out = new_out; 582*7c478bd9Sstevel@tonic-gate out[count++] = name; 583*7c478bd9Sstevel@tonic-gate } else { 584*7c478bd9Sstevel@tonic-gate assert(name == NULL); 585*7c478bd9Sstevel@tonic-gate } 586*7c478bd9Sstevel@tonic-gate } 587*7c478bd9Sstevel@tonic-gate (void) closedir(dir); 588*7c478bd9Sstevel@tonic-gate 589*7c478bd9Sstevel@tonic-gate basename[baselen] = b_end; 590*7c478bd9Sstevel@tonic-gate 591*7c478bd9Sstevel@tonic-gate *out_arg = (const char **)out; 592*7c478bd9Sstevel@tonic-gate return (count); 593*7c478bd9Sstevel@tonic-gate 594*7c478bd9Sstevel@tonic-gate fail_closedir: 595*7c478bd9Sstevel@tonic-gate (void) closedir(dir); 596*7c478bd9Sstevel@tonic-gate fail: 597*7c478bd9Sstevel@tonic-gate basename[0] = b_start; 598*7c478bd9Sstevel@tonic-gate *pathend = b_end; 599*7c478bd9Sstevel@tonic-gate 600*7c478bd9Sstevel@tonic-gate backend_backup_cleanup((const char **)out, count); 601*7c478bd9Sstevel@tonic-gate 602*7c478bd9Sstevel@tonic-gate *out_arg = NULL; 603*7c478bd9Sstevel@tonic-gate return (-1); 604*7c478bd9Sstevel@tonic-gate } 605*7c478bd9Sstevel@tonic-gate 606*7c478bd9Sstevel@tonic-gate /* 607*7c478bd9Sstevel@tonic-gate * Copies the repository path into out, a buffer of out_len bytes, 608*7c478bd9Sstevel@tonic-gate * removes the ".db" (or whatever) extension, and, if name is non-NULL, 609*7c478bd9Sstevel@tonic-gate * appends "-name" to it. If name is non-NULL, it can fail with: 610*7c478bd9Sstevel@tonic-gate * 611*7c478bd9Sstevel@tonic-gate * _TRUNCATED will not fit in buffer. 612*7c478bd9Sstevel@tonic-gate * _BAD_REQUEST name is not a valid identifier 613*7c478bd9Sstevel@tonic-gate */ 614*7c478bd9Sstevel@tonic-gate static rep_protocol_responseid_t 615*7c478bd9Sstevel@tonic-gate backend_backup_base(sqlite_backend_t *be, const char *name, 616*7c478bd9Sstevel@tonic-gate char *out, size_t out_len) 617*7c478bd9Sstevel@tonic-gate { 618*7c478bd9Sstevel@tonic-gate char *p, *q; 619*7c478bd9Sstevel@tonic-gate size_t len; 620*7c478bd9Sstevel@tonic-gate 621*7c478bd9Sstevel@tonic-gate /* 622*7c478bd9Sstevel@tonic-gate * for paths of the form /path/to/foo.db, we truncate at the final 623*7c478bd9Sstevel@tonic-gate * '.'. 624*7c478bd9Sstevel@tonic-gate */ 625*7c478bd9Sstevel@tonic-gate (void) strlcpy(out, be->be_path, out_len); 626*7c478bd9Sstevel@tonic-gate 627*7c478bd9Sstevel@tonic-gate p = strrchr(out, '/'); 628*7c478bd9Sstevel@tonic-gate q = strrchr(out, '.'); 629*7c478bd9Sstevel@tonic-gate 630*7c478bd9Sstevel@tonic-gate if (p != NULL && q != NULL && q > p) 631*7c478bd9Sstevel@tonic-gate *q = 0; 632*7c478bd9Sstevel@tonic-gate 633*7c478bd9Sstevel@tonic-gate if (name != NULL) { 634*7c478bd9Sstevel@tonic-gate len = strlen(out); 635*7c478bd9Sstevel@tonic-gate assert(len < out_len); 636*7c478bd9Sstevel@tonic-gate 637*7c478bd9Sstevel@tonic-gate out += len; 638*7c478bd9Sstevel@tonic-gate out_len -= len; 639*7c478bd9Sstevel@tonic-gate 640*7c478bd9Sstevel@tonic-gate len = strlen(name); 641*7c478bd9Sstevel@tonic-gate 642*7c478bd9Sstevel@tonic-gate /* 643*7c478bd9Sstevel@tonic-gate * verify that the name tag is entirely alphabetic, 644*7c478bd9Sstevel@tonic-gate * non-empty, and not too long. 645*7c478bd9Sstevel@tonic-gate */ 646*7c478bd9Sstevel@tonic-gate if (len == 0 || len >= REP_PROTOCOL_NAME_LEN || 647*7c478bd9Sstevel@tonic-gate uu_check_name(name, UU_NAME_DOMAIN) < 0) 648*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 649*7c478bd9Sstevel@tonic-gate 650*7c478bd9Sstevel@tonic-gate if (snprintf(out, out_len, "-%s", name) >= out_len) 651*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 652*7c478bd9Sstevel@tonic-gate } 653*7c478bd9Sstevel@tonic-gate 654*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 655*7c478bd9Sstevel@tonic-gate } 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate /* 658*7c478bd9Sstevel@tonic-gate * Can return: 659*7c478bd9Sstevel@tonic-gate * _BAD_REQUEST name is not valid 660*7c478bd9Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 661*7c478bd9Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 662*7c478bd9Sstevel@tonic-gate * console) 663*7c478bd9Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 664*7c478bd9Sstevel@tonic-gate * 665*7c478bd9Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 666*7c478bd9Sstevel@tonic-gate */ 667*7c478bd9Sstevel@tonic-gate static rep_protocol_responseid_t 668*7c478bd9Sstevel@tonic-gate backend_create_backup_locked(sqlite_backend_t *be, const char *name) 669*7c478bd9Sstevel@tonic-gate { 670*7c478bd9Sstevel@tonic-gate const char **old_list; 671*7c478bd9Sstevel@tonic-gate ssize_t old_sz; 672*7c478bd9Sstevel@tonic-gate ssize_t old_max = max_repository_backups; 673*7c478bd9Sstevel@tonic-gate ssize_t cur; 674*7c478bd9Sstevel@tonic-gate 675*7c478bd9Sstevel@tonic-gate char *finalname; 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate char finalpath[PATH_MAX]; 678*7c478bd9Sstevel@tonic-gate char tmppath[PATH_MAX]; 679*7c478bd9Sstevel@tonic-gate char buf[8192]; 680*7c478bd9Sstevel@tonic-gate int infd, outfd; 681*7c478bd9Sstevel@tonic-gate size_t len; 682*7c478bd9Sstevel@tonic-gate off_t inlen, outlen, offset; 683*7c478bd9Sstevel@tonic-gate 684*7c478bd9Sstevel@tonic-gate time_t now; 685*7c478bd9Sstevel@tonic-gate struct tm now_tm; 686*7c478bd9Sstevel@tonic-gate 687*7c478bd9Sstevel@tonic-gate rep_protocol_responseid_t result; 688*7c478bd9Sstevel@tonic-gate 689*7c478bd9Sstevel@tonic-gate if (be->be_readonly) 690*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 691*7c478bd9Sstevel@tonic-gate 692*7c478bd9Sstevel@tonic-gate result = backend_backup_base(be, name, finalpath, sizeof (finalpath)); 693*7c478bd9Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 694*7c478bd9Sstevel@tonic-gate return (result); 695*7c478bd9Sstevel@tonic-gate 696*7c478bd9Sstevel@tonic-gate /* 697*7c478bd9Sstevel@tonic-gate * remember the original length, and the basename location 698*7c478bd9Sstevel@tonic-gate */ 699*7c478bd9Sstevel@tonic-gate len = strlen(finalpath); 700*7c478bd9Sstevel@tonic-gate finalname = strrchr(finalpath, '/'); 701*7c478bd9Sstevel@tonic-gate if (finalname != NULL) 702*7c478bd9Sstevel@tonic-gate finalname++; 703*7c478bd9Sstevel@tonic-gate else 704*7c478bd9Sstevel@tonic-gate finalname = finalpath; 705*7c478bd9Sstevel@tonic-gate 706*7c478bd9Sstevel@tonic-gate (void) strlcpy(tmppath, finalpath, sizeof (tmppath)); 707*7c478bd9Sstevel@tonic-gate if (strlcat(tmppath, "-tmpXXXXXX", sizeof (tmppath)) >= 708*7c478bd9Sstevel@tonic-gate sizeof (tmppath)) 709*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 710*7c478bd9Sstevel@tonic-gate 711*7c478bd9Sstevel@tonic-gate now = time(NULL); 712*7c478bd9Sstevel@tonic-gate if (localtime_r(&now, &now_tm) == NULL) { 713*7c478bd9Sstevel@tonic-gate configd_critical( 714*7c478bd9Sstevel@tonic-gate "\"%s\" backup failed: localtime(3C) failed: %s\n", name, 715*7c478bd9Sstevel@tonic-gate be->be_path, strerror(errno)); 716*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 717*7c478bd9Sstevel@tonic-gate } 718*7c478bd9Sstevel@tonic-gate 719*7c478bd9Sstevel@tonic-gate if (strftime(finalpath + len, sizeof (finalpath) - len, 720*7c478bd9Sstevel@tonic-gate "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >= 721*7c478bd9Sstevel@tonic-gate sizeof (finalpath) - len) { 722*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 723*7c478bd9Sstevel@tonic-gate } 724*7c478bd9Sstevel@tonic-gate 725*7c478bd9Sstevel@tonic-gate infd = open(be->be_path, O_RDONLY); 726*7c478bd9Sstevel@tonic-gate if (infd < 0) { 727*7c478bd9Sstevel@tonic-gate configd_critical("\"%s\" backup failed: opening %s: %s\n", name, 728*7c478bd9Sstevel@tonic-gate be->be_path, strerror(errno)); 729*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 730*7c478bd9Sstevel@tonic-gate } 731*7c478bd9Sstevel@tonic-gate 732*7c478bd9Sstevel@tonic-gate outfd = mkstemp(tmppath); 733*7c478bd9Sstevel@tonic-gate if (outfd < 0) { 734*7c478bd9Sstevel@tonic-gate configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n", 735*7c478bd9Sstevel@tonic-gate name, tmppath, strerror(errno)); 736*7c478bd9Sstevel@tonic-gate (void) close(infd); 737*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_UNKNOWN); 738*7c478bd9Sstevel@tonic-gate } 739*7c478bd9Sstevel@tonic-gate 740*7c478bd9Sstevel@tonic-gate for (;;) { 741*7c478bd9Sstevel@tonic-gate do { 742*7c478bd9Sstevel@tonic-gate inlen = read(infd, buf, sizeof (buf)); 743*7c478bd9Sstevel@tonic-gate } while (inlen < 0 && errno == EINTR); 744*7c478bd9Sstevel@tonic-gate 745*7c478bd9Sstevel@tonic-gate if (inlen <= 0) 746*7c478bd9Sstevel@tonic-gate break; 747*7c478bd9Sstevel@tonic-gate 748*7c478bd9Sstevel@tonic-gate for (offset = 0; offset < inlen; offset += outlen) { 749*7c478bd9Sstevel@tonic-gate do { 750*7c478bd9Sstevel@tonic-gate outlen = write(outfd, buf + offset, 751*7c478bd9Sstevel@tonic-gate inlen - offset); 752*7c478bd9Sstevel@tonic-gate } while (outlen < 0 && errno == EINTR); 753*7c478bd9Sstevel@tonic-gate 754*7c478bd9Sstevel@tonic-gate if (outlen >= 0) 755*7c478bd9Sstevel@tonic-gate continue; 756*7c478bd9Sstevel@tonic-gate 757*7c478bd9Sstevel@tonic-gate configd_critical( 758*7c478bd9Sstevel@tonic-gate "\"%s\" backup failed: write to %s: %s\n", 759*7c478bd9Sstevel@tonic-gate name, tmppath, strerror(errno)); 760*7c478bd9Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 761*7c478bd9Sstevel@tonic-gate goto fail; 762*7c478bd9Sstevel@tonic-gate } 763*7c478bd9Sstevel@tonic-gate } 764*7c478bd9Sstevel@tonic-gate 765*7c478bd9Sstevel@tonic-gate if (inlen < 0) { 766*7c478bd9Sstevel@tonic-gate configd_critical( 767*7c478bd9Sstevel@tonic-gate "\"%s\" backup failed: read from %s: %s\n", 768*7c478bd9Sstevel@tonic-gate name, be->be_path, strerror(errno)); 769*7c478bd9Sstevel@tonic-gate goto fail; 770*7c478bd9Sstevel@tonic-gate } 771*7c478bd9Sstevel@tonic-gate 772*7c478bd9Sstevel@tonic-gate /* 773*7c478bd9Sstevel@tonic-gate * grab the old list before doing our re-name. 774*7c478bd9Sstevel@tonic-gate */ 775*7c478bd9Sstevel@tonic-gate if (old_max > 0) 776*7c478bd9Sstevel@tonic-gate old_sz = backend_backup_get_prev(finalpath, len, &old_list); 777*7c478bd9Sstevel@tonic-gate 778*7c478bd9Sstevel@tonic-gate if (rename(tmppath, finalpath) < 0) { 779*7c478bd9Sstevel@tonic-gate configd_critical( 780*7c478bd9Sstevel@tonic-gate "\"%s\" backup failed: rename(%s, %s): %s\n", 781*7c478bd9Sstevel@tonic-gate name, tmppath, finalpath, strerror(errno)); 782*7c478bd9Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 783*7c478bd9Sstevel@tonic-gate goto fail; 784*7c478bd9Sstevel@tonic-gate } 785*7c478bd9Sstevel@tonic-gate 786*7c478bd9Sstevel@tonic-gate tmppath[len] = 0; /* strip -XXXXXX, for reference symlink */ 787*7c478bd9Sstevel@tonic-gate 788*7c478bd9Sstevel@tonic-gate (void) unlink(tmppath); 789*7c478bd9Sstevel@tonic-gate if (symlink(finalname, tmppath) < 0) { 790*7c478bd9Sstevel@tonic-gate configd_critical( 791*7c478bd9Sstevel@tonic-gate "\"%s\" backup completed, but updating " 792*7c478bd9Sstevel@tonic-gate "\"%s\" symlink to \"%s\" failed: %s\n", 793*7c478bd9Sstevel@tonic-gate name, tmppath, finalname, strerror(errno)); 794*7c478bd9Sstevel@tonic-gate } 795*7c478bd9Sstevel@tonic-gate 796*7c478bd9Sstevel@tonic-gate if (old_max > 0 && old_sz > 0) { 797*7c478bd9Sstevel@tonic-gate /* unlink all but the first (old_max - 1) files */ 798*7c478bd9Sstevel@tonic-gate for (cur = old_max - 1; cur < old_sz; cur++) { 799*7c478bd9Sstevel@tonic-gate (void) strlcpy(finalname, old_list[cur], 800*7c478bd9Sstevel@tonic-gate sizeof (finalpath) - (finalname - finalpath)); 801*7c478bd9Sstevel@tonic-gate if (unlink(finalpath) < 0) 802*7c478bd9Sstevel@tonic-gate configd_critical( 803*7c478bd9Sstevel@tonic-gate "\"%s\" backup completed, but removing old " 804*7c478bd9Sstevel@tonic-gate "file \"%s\" failed: %s\n", 805*7c478bd9Sstevel@tonic-gate name, finalpath, strerror(errno)); 806*7c478bd9Sstevel@tonic-gate } 807*7c478bd9Sstevel@tonic-gate 808*7c478bd9Sstevel@tonic-gate backend_backup_cleanup(old_list, old_sz); 809*7c478bd9Sstevel@tonic-gate } 810*7c478bd9Sstevel@tonic-gate 811*7c478bd9Sstevel@tonic-gate result = REP_PROTOCOL_SUCCESS; 812*7c478bd9Sstevel@tonic-gate 813*7c478bd9Sstevel@tonic-gate fail: 814*7c478bd9Sstevel@tonic-gate (void) close(infd); 815*7c478bd9Sstevel@tonic-gate (void) close(outfd); 816*7c478bd9Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 817*7c478bd9Sstevel@tonic-gate (void) unlink(tmppath); 818*7c478bd9Sstevel@tonic-gate 819*7c478bd9Sstevel@tonic-gate return (result); 820*7c478bd9Sstevel@tonic-gate } 821*7c478bd9Sstevel@tonic-gate 822*7c478bd9Sstevel@tonic-gate 823*7c478bd9Sstevel@tonic-gate /* 824*7c478bd9Sstevel@tonic-gate * If t is not BACKEND_TYPE_NORMAL, can fail with 825*7c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 826*7c478bd9Sstevel@tonic-gate * 827*7c478bd9Sstevel@tonic-gate * If writing is nonzero, can also fail with 828*7c478bd9Sstevel@tonic-gate * _BACKEND_READONLY - backend is read-only 829*7c478bd9Sstevel@tonic-gate */ 830*7c478bd9Sstevel@tonic-gate static int 831*7c478bd9Sstevel@tonic-gate backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep) 832*7c478bd9Sstevel@tonic-gate { 833*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be = NULL; 834*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 835*7c478bd9Sstevel@tonic-gate 836*7c478bd9Sstevel@tonic-gate *bep = NULL; 837*7c478bd9Sstevel@tonic-gate 838*7c478bd9Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || 839*7c478bd9Sstevel@tonic-gate t == BACKEND_TYPE_NONPERSIST); 840*7c478bd9Sstevel@tonic-gate 841*7c478bd9Sstevel@tonic-gate be = bes[t]; 842*7c478bd9Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) 843*7c478bd9Sstevel@tonic-gate assert(be != NULL); /* should always be there */ 844*7c478bd9Sstevel@tonic-gate 845*7c478bd9Sstevel@tonic-gate if (be == NULL) 846*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_ACCESS); 847*7c478bd9Sstevel@tonic-gate 848*7c478bd9Sstevel@tonic-gate if (backend_panic_thread != 0) 849*7c478bd9Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 850*7c478bd9Sstevel@tonic-gate 851*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 852*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 853*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 854*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS_WR(be, writing, bt_lock, ts, vts); 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate if (backend_panic_thread != 0) { 857*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 858*7c478bd9Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 859*7c478bd9Sstevel@tonic-gate } 860*7c478bd9Sstevel@tonic-gate be->be_thread = pthread_self(); 861*7c478bd9Sstevel@tonic-gate 862*7c478bd9Sstevel@tonic-gate if (writing && be->be_readonly) { 863*7c478bd9Sstevel@tonic-gate char *errp; 864*7c478bd9Sstevel@tonic-gate struct sqlite *new; 865*7c478bd9Sstevel@tonic-gate int r; 866*7c478bd9Sstevel@tonic-gate 867*7c478bd9Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL); 868*7c478bd9Sstevel@tonic-gate 869*7c478bd9Sstevel@tonic-gate new = sqlite_open(be->be_path, 0600, &errp); 870*7c478bd9Sstevel@tonic-gate if (new == NULL) { 871*7c478bd9Sstevel@tonic-gate backend_panic("reopening %s: %s\n", be->be_path, errp); 872*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 873*7c478bd9Sstevel@tonic-gate } 874*7c478bd9Sstevel@tonic-gate r = backend_is_readonly(new, &errp); 875*7c478bd9Sstevel@tonic-gate if (r != SQLITE_OK) { 876*7c478bd9Sstevel@tonic-gate free(errp); 877*7c478bd9Sstevel@tonic-gate sqlite_close(new); 878*7c478bd9Sstevel@tonic-gate be->be_thread = 0; 879*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 880*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 881*7c478bd9Sstevel@tonic-gate } 882*7c478bd9Sstevel@tonic-gate 883*7c478bd9Sstevel@tonic-gate /* 884*7c478bd9Sstevel@tonic-gate * We can write! Swap our db handles, mark ourself writable, 885*7c478bd9Sstevel@tonic-gate * and make a backup. 886*7c478bd9Sstevel@tonic-gate */ 887*7c478bd9Sstevel@tonic-gate sqlite_close(be->be_db); 888*7c478bd9Sstevel@tonic-gate be->be_db = new; 889*7c478bd9Sstevel@tonic-gate be->be_readonly = 0; 890*7c478bd9Sstevel@tonic-gate 891*7c478bd9Sstevel@tonic-gate if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != 892*7c478bd9Sstevel@tonic-gate REP_PROTOCOL_SUCCESS) { 893*7c478bd9Sstevel@tonic-gate configd_critical( 894*7c478bd9Sstevel@tonic-gate "unable to create \"%s\" backup of \"%s\"\n", 895*7c478bd9Sstevel@tonic-gate REPOSITORY_BOOT_BACKUP, be->be_path); 896*7c478bd9Sstevel@tonic-gate } 897*7c478bd9Sstevel@tonic-gate } 898*7c478bd9Sstevel@tonic-gate 899*7c478bd9Sstevel@tonic-gate if (backend_do_trace) 900*7c478bd9Sstevel@tonic-gate (void) sqlite_trace(be->be_db, backend_trace_sql, be); 901*7c478bd9Sstevel@tonic-gate else 902*7c478bd9Sstevel@tonic-gate (void) sqlite_trace(be->be_db, NULL, NULL); 903*7c478bd9Sstevel@tonic-gate 904*7c478bd9Sstevel@tonic-gate be->be_writing = writing; 905*7c478bd9Sstevel@tonic-gate *bep = be; 906*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 907*7c478bd9Sstevel@tonic-gate } 908*7c478bd9Sstevel@tonic-gate 909*7c478bd9Sstevel@tonic-gate static void 910*7c478bd9Sstevel@tonic-gate backend_unlock(sqlite_backend_t *be) 911*7c478bd9Sstevel@tonic-gate { 912*7c478bd9Sstevel@tonic-gate be->be_writing = 0; 913*7c478bd9Sstevel@tonic-gate be->be_thread = 0; 914*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 915*7c478bd9Sstevel@tonic-gate } 916*7c478bd9Sstevel@tonic-gate 917*7c478bd9Sstevel@tonic-gate static void 918*7c478bd9Sstevel@tonic-gate backend_destroy(sqlite_backend_t *be) 919*7c478bd9Sstevel@tonic-gate { 920*7c478bd9Sstevel@tonic-gate if (be->be_db != NULL) { 921*7c478bd9Sstevel@tonic-gate sqlite_close(be->be_db); 922*7c478bd9Sstevel@tonic-gate be->be_db = NULL; 923*7c478bd9Sstevel@tonic-gate } 924*7c478bd9Sstevel@tonic-gate be->be_thread = 0; 925*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 926*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_destroy(&be->be_lock); 927*7c478bd9Sstevel@tonic-gate } 928*7c478bd9Sstevel@tonic-gate 929*7c478bd9Sstevel@tonic-gate static void 930*7c478bd9Sstevel@tonic-gate backend_create_finish(backend_type_t backend_id, sqlite_backend_t *be) 931*7c478bd9Sstevel@tonic-gate { 932*7c478bd9Sstevel@tonic-gate assert(MUTEX_HELD(&be->be_lock)); 933*7c478bd9Sstevel@tonic-gate assert(be == &be_info[backend_id]); 934*7c478bd9Sstevel@tonic-gate 935*7c478bd9Sstevel@tonic-gate bes[backend_id] = be; 936*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 937*7c478bd9Sstevel@tonic-gate } 938*7c478bd9Sstevel@tonic-gate 939*7c478bd9Sstevel@tonic-gate static int 940*7c478bd9Sstevel@tonic-gate backend_fd_write(int fd, const char *mess) 941*7c478bd9Sstevel@tonic-gate { 942*7c478bd9Sstevel@tonic-gate int len = strlen(mess); 943*7c478bd9Sstevel@tonic-gate int written; 944*7c478bd9Sstevel@tonic-gate 945*7c478bd9Sstevel@tonic-gate while (len > 0) { 946*7c478bd9Sstevel@tonic-gate if ((written = write(fd, mess, len)) < 0) 947*7c478bd9Sstevel@tonic-gate return (-1); 948*7c478bd9Sstevel@tonic-gate mess += written; 949*7c478bd9Sstevel@tonic-gate len -= written; 950*7c478bd9Sstevel@tonic-gate } 951*7c478bd9Sstevel@tonic-gate return (0); 952*7c478bd9Sstevel@tonic-gate } 953*7c478bd9Sstevel@tonic-gate 954*7c478bd9Sstevel@tonic-gate /* 955*7c478bd9Sstevel@tonic-gate * Can return: 956*7c478bd9Sstevel@tonic-gate * _BAD_REQUEST name is not valid 957*7c478bd9Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 958*7c478bd9Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 959*7c478bd9Sstevel@tonic-gate * console) 960*7c478bd9Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 961*7c478bd9Sstevel@tonic-gate * 962*7c478bd9Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 963*7c478bd9Sstevel@tonic-gate */ 964*7c478bd9Sstevel@tonic-gate rep_protocol_responseid_t 965*7c478bd9Sstevel@tonic-gate backend_create_backup(const char *name) 966*7c478bd9Sstevel@tonic-gate { 967*7c478bd9Sstevel@tonic-gate rep_protocol_responseid_t result; 968*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 969*7c478bd9Sstevel@tonic-gate 970*7c478bd9Sstevel@tonic-gate result = backend_lock(BACKEND_TYPE_NORMAL, 0, &be); 971*7c478bd9Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 972*7c478bd9Sstevel@tonic-gate return (result); 973*7c478bd9Sstevel@tonic-gate 974*7c478bd9Sstevel@tonic-gate result = backend_create_backup_locked(be, name); 975*7c478bd9Sstevel@tonic-gate backend_unlock(be); 976*7c478bd9Sstevel@tonic-gate 977*7c478bd9Sstevel@tonic-gate return (result); 978*7c478bd9Sstevel@tonic-gate } 979*7c478bd9Sstevel@tonic-gate 980*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 981*7c478bd9Sstevel@tonic-gate static int 982*7c478bd9Sstevel@tonic-gate backend_integrity_callback(void *private, int narg, char **vals, char **cols) 983*7c478bd9Sstevel@tonic-gate { 984*7c478bd9Sstevel@tonic-gate char **out = private; 985*7c478bd9Sstevel@tonic-gate char *old = *out; 986*7c478bd9Sstevel@tonic-gate char *new; 987*7c478bd9Sstevel@tonic-gate const char *info; 988*7c478bd9Sstevel@tonic-gate size_t len; 989*7c478bd9Sstevel@tonic-gate int x; 990*7c478bd9Sstevel@tonic-gate 991*7c478bd9Sstevel@tonic-gate for (x = 0; x < narg; x++) { 992*7c478bd9Sstevel@tonic-gate if ((info = vals[x]) != NULL && 993*7c478bd9Sstevel@tonic-gate strcmp(info, "ok") != 0) { 994*7c478bd9Sstevel@tonic-gate len = (old == NULL)? 0 : strlen(old); 995*7c478bd9Sstevel@tonic-gate len += strlen(info) + 2; /* '\n' + '\0' */ 996*7c478bd9Sstevel@tonic-gate 997*7c478bd9Sstevel@tonic-gate new = realloc(old, len); 998*7c478bd9Sstevel@tonic-gate if (new == NULL) 999*7c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 1000*7c478bd9Sstevel@tonic-gate if (old == NULL) 1001*7c478bd9Sstevel@tonic-gate new[0] = 0; 1002*7c478bd9Sstevel@tonic-gate old = *out = new; 1003*7c478bd9Sstevel@tonic-gate (void) strlcat(new, info, len); 1004*7c478bd9Sstevel@tonic-gate (void) strlcat(new, "\n", len); 1005*7c478bd9Sstevel@tonic-gate } 1006*7c478bd9Sstevel@tonic-gate } 1007*7c478bd9Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 1008*7c478bd9Sstevel@tonic-gate } 1009*7c478bd9Sstevel@tonic-gate 1010*7c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_LOCKED -2 1011*7c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_FAIL -1 1012*7c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_SUCCESS 0 1013*7c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_READONLY 1 1014*7c478bd9Sstevel@tonic-gate #define BACKEND_CREATE_NEED_INIT 2 1015*7c478bd9Sstevel@tonic-gate static int 1016*7c478bd9Sstevel@tonic-gate backend_create(backend_type_t backend_id, const char *db_file, 1017*7c478bd9Sstevel@tonic-gate sqlite_backend_t **bep) 1018*7c478bd9Sstevel@tonic-gate { 1019*7c478bd9Sstevel@tonic-gate char *errp; 1020*7c478bd9Sstevel@tonic-gate char *integrity_results = NULL; 1021*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1022*7c478bd9Sstevel@tonic-gate int r; 1023*7c478bd9Sstevel@tonic-gate uint32_t val = -1UL; 1024*7c478bd9Sstevel@tonic-gate struct run_single_int_info info; 1025*7c478bd9Sstevel@tonic-gate int fd; 1026*7c478bd9Sstevel@tonic-gate 1027*7c478bd9Sstevel@tonic-gate assert(backend_id >= 0 && backend_id < BACKEND_TYPE_TOTAL); 1028*7c478bd9Sstevel@tonic-gate 1029*7c478bd9Sstevel@tonic-gate be = &be_info[backend_id]; 1030*7c478bd9Sstevel@tonic-gate assert(be->be_db == NULL); 1031*7c478bd9Sstevel@tonic-gate 1032*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_init(&be->be_lock, NULL); 1033*7c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 1034*7c478bd9Sstevel@tonic-gate 1035*7c478bd9Sstevel@tonic-gate be->be_type = backend_id; 1036*7c478bd9Sstevel@tonic-gate be->be_path = strdup(db_file); 1037*7c478bd9Sstevel@tonic-gate if (be->be_path == NULL) { 1038*7c478bd9Sstevel@tonic-gate perror("malloc"); 1039*7c478bd9Sstevel@tonic-gate goto fail; 1040*7c478bd9Sstevel@tonic-gate } 1041*7c478bd9Sstevel@tonic-gate 1042*7c478bd9Sstevel@tonic-gate be->be_db = sqlite_open(be->be_path, 0600, &errp); 1043*7c478bd9Sstevel@tonic-gate 1044*7c478bd9Sstevel@tonic-gate if (be->be_db == NULL) { 1045*7c478bd9Sstevel@tonic-gate if (strstr(errp, "out of memory") != NULL) { 1046*7c478bd9Sstevel@tonic-gate configd_critical("%s: %s\n", db_file, errp); 1047*7c478bd9Sstevel@tonic-gate free(errp); 1048*7c478bd9Sstevel@tonic-gate 1049*7c478bd9Sstevel@tonic-gate goto fail; 1050*7c478bd9Sstevel@tonic-gate } 1051*7c478bd9Sstevel@tonic-gate 1052*7c478bd9Sstevel@tonic-gate /* report it as an integrity failure */ 1053*7c478bd9Sstevel@tonic-gate integrity_results = errp; 1054*7c478bd9Sstevel@tonic-gate errp = NULL; 1055*7c478bd9Sstevel@tonic-gate goto integrity_fail; 1056*7c478bd9Sstevel@tonic-gate } 1057*7c478bd9Sstevel@tonic-gate 1058*7c478bd9Sstevel@tonic-gate /* 1059*7c478bd9Sstevel@tonic-gate * check if we are inited and of the correct schema version 1060*7c478bd9Sstevel@tonic-gate * 1061*7c478bd9Sstevel@tonic-gate * Eventually, we'll support schema upgrade here. 1062*7c478bd9Sstevel@tonic-gate */ 1063*7c478bd9Sstevel@tonic-gate info.rs_out = &val; 1064*7c478bd9Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 1065*7c478bd9Sstevel@tonic-gate 1066*7c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT schema_version FROM schema_version;", 1067*7c478bd9Sstevel@tonic-gate run_single_int_callback, &info, &errp); 1068*7c478bd9Sstevel@tonic-gate if (r == SQLITE_ERROR && 1069*7c478bd9Sstevel@tonic-gate strcmp("no such table: schema_version", errp) == 0) { 1070*7c478bd9Sstevel@tonic-gate free(errp); 1071*7c478bd9Sstevel@tonic-gate /* 1072*7c478bd9Sstevel@tonic-gate * Could be an empty repository, could be pre-schema_version 1073*7c478bd9Sstevel@tonic-gate * schema. Check for id_tbl, which has always been there. 1074*7c478bd9Sstevel@tonic-gate */ 1075*7c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT count() FROM id_tbl;", 1076*7c478bd9Sstevel@tonic-gate NULL, NULL, &errp); 1077*7c478bd9Sstevel@tonic-gate if (r == SQLITE_ERROR && 1078*7c478bd9Sstevel@tonic-gate strcmp("no such table: id_tbl", errp) == 0) { 1079*7c478bd9Sstevel@tonic-gate free(errp); 1080*7c478bd9Sstevel@tonic-gate *bep = be; 1081*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_NEED_INIT); 1082*7c478bd9Sstevel@tonic-gate } 1083*7c478bd9Sstevel@tonic-gate 1084*7c478bd9Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", db_file); 1085*7c478bd9Sstevel@tonic-gate goto fail; 1086*7c478bd9Sstevel@tonic-gate } 1087*7c478bd9Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 1088*7c478bd9Sstevel@tonic-gate free(errp); 1089*7c478bd9Sstevel@tonic-gate *bep = NULL; 1090*7c478bd9Sstevel@tonic-gate backend_destroy(be); 1091*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 1092*7c478bd9Sstevel@tonic-gate } 1093*7c478bd9Sstevel@tonic-gate if (r == SQLITE_OK) { 1094*7c478bd9Sstevel@tonic-gate if (info.rs_result == REP_PROTOCOL_FAIL_NOT_FOUND || 1095*7c478bd9Sstevel@tonic-gate val != BACKEND_SCHEMA_VERSION) { 1096*7c478bd9Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", 1097*7c478bd9Sstevel@tonic-gate db_file); 1098*7c478bd9Sstevel@tonic-gate goto fail; 1099*7c478bd9Sstevel@tonic-gate } 1100*7c478bd9Sstevel@tonic-gate } 1101*7c478bd9Sstevel@tonic-gate 1102*7c478bd9Sstevel@tonic-gate /* 1103*7c478bd9Sstevel@tonic-gate * pull in the whole database sequentially. 1104*7c478bd9Sstevel@tonic-gate */ 1105*7c478bd9Sstevel@tonic-gate if ((fd = open(db_file, O_RDONLY)) >= 0) { 1106*7c478bd9Sstevel@tonic-gate size_t sz = 64 * 1024; 1107*7c478bd9Sstevel@tonic-gate char *buffer = malloc(sz); 1108*7c478bd9Sstevel@tonic-gate if (buffer != NULL) { 1109*7c478bd9Sstevel@tonic-gate while (read(fd, buffer, sz) > 0) 1110*7c478bd9Sstevel@tonic-gate ; 1111*7c478bd9Sstevel@tonic-gate free(buffer); 1112*7c478bd9Sstevel@tonic-gate } 1113*7c478bd9Sstevel@tonic-gate (void) close(fd); 1114*7c478bd9Sstevel@tonic-gate } 1115*7c478bd9Sstevel@tonic-gate 1116*7c478bd9Sstevel@tonic-gate /* 1117*7c478bd9Sstevel@tonic-gate * run an integrity check 1118*7c478bd9Sstevel@tonic-gate */ 1119*7c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "PRAGMA integrity_check;", 1120*7c478bd9Sstevel@tonic-gate backend_integrity_callback, &integrity_results, &errp); 1121*7c478bd9Sstevel@tonic-gate 1122*7c478bd9Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 1123*7c478bd9Sstevel@tonic-gate free(errp); 1124*7c478bd9Sstevel@tonic-gate *bep = NULL; 1125*7c478bd9Sstevel@tonic-gate backend_destroy(be); 1126*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 1127*7c478bd9Sstevel@tonic-gate } 1128*7c478bd9Sstevel@tonic-gate if (r == SQLITE_ABORT) { 1129*7c478bd9Sstevel@tonic-gate free(errp); 1130*7c478bd9Sstevel@tonic-gate errp = NULL; 1131*7c478bd9Sstevel@tonic-gate integrity_results = "out of memory running integrity check\n"; 1132*7c478bd9Sstevel@tonic-gate } else if (r != SQLITE_OK && integrity_results == NULL) { 1133*7c478bd9Sstevel@tonic-gate integrity_results = errp; 1134*7c478bd9Sstevel@tonic-gate errp = NULL; 1135*7c478bd9Sstevel@tonic-gate } 1136*7c478bd9Sstevel@tonic-gate 1137*7c478bd9Sstevel@tonic-gate integrity_fail: 1138*7c478bd9Sstevel@tonic-gate if (integrity_results != NULL) { 1139*7c478bd9Sstevel@tonic-gate const char *fname = "/etc/svc/volatile/db_errors"; 1140*7c478bd9Sstevel@tonic-gate if ((fd = open(fname, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { 1141*7c478bd9Sstevel@tonic-gate fname = NULL; 1142*7c478bd9Sstevel@tonic-gate } else { 1143*7c478bd9Sstevel@tonic-gate if (backend_fd_write(fd, "\n\n") < 0 || 1144*7c478bd9Sstevel@tonic-gate backend_fd_write(fd, db_file) < 0 || 1145*7c478bd9Sstevel@tonic-gate backend_fd_write(fd, 1146*7c478bd9Sstevel@tonic-gate ": PRAGMA integrity_check; failed. Results:\n") < 1147*7c478bd9Sstevel@tonic-gate 0 || backend_fd_write(fd, integrity_results) < 0 || 1148*7c478bd9Sstevel@tonic-gate backend_fd_write(fd, "\n\n") < 0) { 1149*7c478bd9Sstevel@tonic-gate fname = NULL; 1150*7c478bd9Sstevel@tonic-gate } 1151*7c478bd9Sstevel@tonic-gate (void) close(fd); 1152*7c478bd9Sstevel@tonic-gate } 1153*7c478bd9Sstevel@tonic-gate 1154*7c478bd9Sstevel@tonic-gate if (!is_main_repository || 1155*7c478bd9Sstevel@tonic-gate backend_id == BACKEND_TYPE_NONPERSIST) { 1156*7c478bd9Sstevel@tonic-gate if (fname != NULL) 1157*7c478bd9Sstevel@tonic-gate configd_critical( 1158*7c478bd9Sstevel@tonic-gate "%s: integrity check failed. Details in " 1159*7c478bd9Sstevel@tonic-gate "%s\n", db_file, fname); 1160*7c478bd9Sstevel@tonic-gate else 1161*7c478bd9Sstevel@tonic-gate configd_critical( 1162*7c478bd9Sstevel@tonic-gate "%s: integrity check failed: %s\n", 1163*7c478bd9Sstevel@tonic-gate db_file); 1164*7c478bd9Sstevel@tonic-gate } else { 1165*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1166*7c478bd9Sstevel@tonic-gate "\n" 1167*7c478bd9Sstevel@tonic-gate "svc.configd: smf(5) database integrity check of:\n" 1168*7c478bd9Sstevel@tonic-gate "\n" 1169*7c478bd9Sstevel@tonic-gate " %s\n" 1170*7c478bd9Sstevel@tonic-gate "\n" 1171*7c478bd9Sstevel@tonic-gate " failed. The database might be damaged or a media error might have\n" 1172*7c478bd9Sstevel@tonic-gate " prevented it from being verified. Additional information useful to\n" 1173*7c478bd9Sstevel@tonic-gate " your service provider%s%s\n" 1174*7c478bd9Sstevel@tonic-gate "\n" 1175*7c478bd9Sstevel@tonic-gate " The system will not be able to boot until you have restored a working\n" 1176*7c478bd9Sstevel@tonic-gate " database. svc.startd(1M) will provide a sulogin(1M) prompt for recovery\n" 1177*7c478bd9Sstevel@tonic-gate " purposes. The command:\n" 1178*7c478bd9Sstevel@tonic-gate "\n" 1179*7c478bd9Sstevel@tonic-gate " /lib/svc/bin/restore_repository\n" 1180*7c478bd9Sstevel@tonic-gate "\n" 1181*7c478bd9Sstevel@tonic-gate " can be run to restore a backup version of your repository. See\n" 1182*7c478bd9Sstevel@tonic-gate " http://sun.com/msg/SMF-8000-MY for more information.\n" 1183*7c478bd9Sstevel@tonic-gate "\n", 1184*7c478bd9Sstevel@tonic-gate db_file, 1185*7c478bd9Sstevel@tonic-gate (fname == NULL)? ":\n\n" : " is in:\n\n ", 1186*7c478bd9Sstevel@tonic-gate (fname == NULL)? integrity_results : fname); 1187*7c478bd9Sstevel@tonic-gate } 1188*7c478bd9Sstevel@tonic-gate free(errp); 1189*7c478bd9Sstevel@tonic-gate goto fail; 1190*7c478bd9Sstevel@tonic-gate } 1191*7c478bd9Sstevel@tonic-gate 1192*7c478bd9Sstevel@tonic-gate /* 1193*7c478bd9Sstevel@tonic-gate * check if we are writable 1194*7c478bd9Sstevel@tonic-gate */ 1195*7c478bd9Sstevel@tonic-gate r = backend_is_readonly(be->be_db, &errp); 1196*7c478bd9Sstevel@tonic-gate 1197*7c478bd9Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 1198*7c478bd9Sstevel@tonic-gate free(errp); 1199*7c478bd9Sstevel@tonic-gate *bep = NULL; 1200*7c478bd9Sstevel@tonic-gate backend_destroy(be); 1201*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 1202*7c478bd9Sstevel@tonic-gate } 1203*7c478bd9Sstevel@tonic-gate if (r != SQLITE_OK && r != SQLITE_FULL) { 1204*7c478bd9Sstevel@tonic-gate free(errp); 1205*7c478bd9Sstevel@tonic-gate be->be_readonly = 1; 1206*7c478bd9Sstevel@tonic-gate *bep = be; 1207*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_READONLY); 1208*7c478bd9Sstevel@tonic-gate } 1209*7c478bd9Sstevel@tonic-gate *bep = be; 1210*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_SUCCESS); 1211*7c478bd9Sstevel@tonic-gate 1212*7c478bd9Sstevel@tonic-gate fail: 1213*7c478bd9Sstevel@tonic-gate *bep = NULL; 1214*7c478bd9Sstevel@tonic-gate backend_destroy(be); 1215*7c478bd9Sstevel@tonic-gate return (BACKEND_CREATE_FAIL); 1216*7c478bd9Sstevel@tonic-gate } 1217*7c478bd9Sstevel@tonic-gate 1218*7c478bd9Sstevel@tonic-gate /* 1219*7c478bd9Sstevel@tonic-gate * (arg & -arg) is, through the magic of twos-complement arithmetic, the 1220*7c478bd9Sstevel@tonic-gate * lowest set bit in arg. 1221*7c478bd9Sstevel@tonic-gate */ 1222*7c478bd9Sstevel@tonic-gate static size_t 1223*7c478bd9Sstevel@tonic-gate round_up_to_p2(size_t arg) 1224*7c478bd9Sstevel@tonic-gate { 1225*7c478bd9Sstevel@tonic-gate /* 1226*7c478bd9Sstevel@tonic-gate * Don't allow a zero result. 1227*7c478bd9Sstevel@tonic-gate */ 1228*7c478bd9Sstevel@tonic-gate assert(arg > 0 && ((ssize_t)arg > 0)); 1229*7c478bd9Sstevel@tonic-gate 1230*7c478bd9Sstevel@tonic-gate while ((arg & (arg - 1)) != 0) 1231*7c478bd9Sstevel@tonic-gate arg += (arg & -arg); 1232*7c478bd9Sstevel@tonic-gate 1233*7c478bd9Sstevel@tonic-gate return (arg); 1234*7c478bd9Sstevel@tonic-gate } 1235*7c478bd9Sstevel@tonic-gate 1236*7c478bd9Sstevel@tonic-gate /* 1237*7c478bd9Sstevel@tonic-gate * Returns 1238*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1239*7c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS - backend type t (other than _NORMAL) doesn't exist 1240*7c478bd9Sstevel@tonic-gate * _DONE - callback aborted query 1241*7c478bd9Sstevel@tonic-gate * _SUCCESS 1242*7c478bd9Sstevel@tonic-gate */ 1243*7c478bd9Sstevel@tonic-gate int 1244*7c478bd9Sstevel@tonic-gate backend_run(backend_type_t t, backend_query_t *q, 1245*7c478bd9Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 1246*7c478bd9Sstevel@tonic-gate { 1247*7c478bd9Sstevel@tonic-gate char *errmsg = NULL; 1248*7c478bd9Sstevel@tonic-gate int ret; 1249*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1250*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1251*7c478bd9Sstevel@tonic-gate 1252*7c478bd9Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 1253*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1254*7c478bd9Sstevel@tonic-gate 1255*7c478bd9Sstevel@tonic-gate if ((ret = backend_lock(t, 0, &be)) != REP_PROTOCOL_SUCCESS) 1256*7c478bd9Sstevel@tonic-gate return (ret); 1257*7c478bd9Sstevel@tonic-gate 1258*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1259*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1260*7c478bd9Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 1261*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1262*7c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 1263*7c478bd9Sstevel@tonic-gate backend_unlock(be); 1264*7c478bd9Sstevel@tonic-gate 1265*7c478bd9Sstevel@tonic-gate return (ret); 1266*7c478bd9Sstevel@tonic-gate } 1267*7c478bd9Sstevel@tonic-gate 1268*7c478bd9Sstevel@tonic-gate /* 1269*7c478bd9Sstevel@tonic-gate * Starts a "read-only" transaction -- i.e., locks out writers as long 1270*7c478bd9Sstevel@tonic-gate * as it is active. 1271*7c478bd9Sstevel@tonic-gate * 1272*7c478bd9Sstevel@tonic-gate * Fails with 1273*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1274*7c478bd9Sstevel@tonic-gate * 1275*7c478bd9Sstevel@tonic-gate * If t is not _NORMAL, can also fail with 1276*7c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 1277*7c478bd9Sstevel@tonic-gate * 1278*7c478bd9Sstevel@tonic-gate * If writable is true, can also fail with 1279*7c478bd9Sstevel@tonic-gate * _BACKEND_READONLY 1280*7c478bd9Sstevel@tonic-gate */ 1281*7c478bd9Sstevel@tonic-gate static int 1282*7c478bd9Sstevel@tonic-gate backend_tx_begin_common(backend_type_t t, backend_tx_t **txp, int writable) 1283*7c478bd9Sstevel@tonic-gate { 1284*7c478bd9Sstevel@tonic-gate backend_tx_t *ret; 1285*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1286*7c478bd9Sstevel@tonic-gate int r; 1287*7c478bd9Sstevel@tonic-gate 1288*7c478bd9Sstevel@tonic-gate *txp = NULL; 1289*7c478bd9Sstevel@tonic-gate 1290*7c478bd9Sstevel@tonic-gate ret = uu_zalloc(sizeof (*ret)); 1291*7c478bd9Sstevel@tonic-gate if (ret == NULL) 1292*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1293*7c478bd9Sstevel@tonic-gate 1294*7c478bd9Sstevel@tonic-gate if ((r = backend_lock(t, writable, &be)) != REP_PROTOCOL_SUCCESS) { 1295*7c478bd9Sstevel@tonic-gate uu_free(ret); 1296*7c478bd9Sstevel@tonic-gate return (r); 1297*7c478bd9Sstevel@tonic-gate } 1298*7c478bd9Sstevel@tonic-gate 1299*7c478bd9Sstevel@tonic-gate ret->bt_be = be; 1300*7c478bd9Sstevel@tonic-gate ret->bt_readonly = !writable; 1301*7c478bd9Sstevel@tonic-gate ret->bt_type = t; 1302*7c478bd9Sstevel@tonic-gate ret->bt_full = 0; 1303*7c478bd9Sstevel@tonic-gate 1304*7c478bd9Sstevel@tonic-gate *txp = ret; 1305*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 1306*7c478bd9Sstevel@tonic-gate } 1307*7c478bd9Sstevel@tonic-gate 1308*7c478bd9Sstevel@tonic-gate int 1309*7c478bd9Sstevel@tonic-gate backend_tx_begin_ro(backend_type_t t, backend_tx_t **txp) 1310*7c478bd9Sstevel@tonic-gate { 1311*7c478bd9Sstevel@tonic-gate return (backend_tx_begin_common(t, txp, 0)); 1312*7c478bd9Sstevel@tonic-gate } 1313*7c478bd9Sstevel@tonic-gate 1314*7c478bd9Sstevel@tonic-gate static void 1315*7c478bd9Sstevel@tonic-gate backend_tx_end(backend_tx_t *tx) 1316*7c478bd9Sstevel@tonic-gate { 1317*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1318*7c478bd9Sstevel@tonic-gate 1319*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1320*7c478bd9Sstevel@tonic-gate 1321*7c478bd9Sstevel@tonic-gate if (tx->bt_full) { 1322*7c478bd9Sstevel@tonic-gate struct sqlite *new; 1323*7c478bd9Sstevel@tonic-gate 1324*7c478bd9Sstevel@tonic-gate /* 1325*7c478bd9Sstevel@tonic-gate * sqlite tends to be sticky with SQLITE_FULL, so we try 1326*7c478bd9Sstevel@tonic-gate * to get a fresh database handle if we got a FULL warning 1327*7c478bd9Sstevel@tonic-gate * along the way. If that fails, no harm done. 1328*7c478bd9Sstevel@tonic-gate */ 1329*7c478bd9Sstevel@tonic-gate new = sqlite_open(be->be_path, 0600, NULL); 1330*7c478bd9Sstevel@tonic-gate if (new != NULL) { 1331*7c478bd9Sstevel@tonic-gate sqlite_close(be->be_db); 1332*7c478bd9Sstevel@tonic-gate be->be_db = new; 1333*7c478bd9Sstevel@tonic-gate } 1334*7c478bd9Sstevel@tonic-gate } 1335*7c478bd9Sstevel@tonic-gate backend_unlock(be); 1336*7c478bd9Sstevel@tonic-gate tx->bt_be = NULL; 1337*7c478bd9Sstevel@tonic-gate uu_free(tx); 1338*7c478bd9Sstevel@tonic-gate } 1339*7c478bd9Sstevel@tonic-gate 1340*7c478bd9Sstevel@tonic-gate void 1341*7c478bd9Sstevel@tonic-gate backend_tx_end_ro(backend_tx_t *tx) 1342*7c478bd9Sstevel@tonic-gate { 1343*7c478bd9Sstevel@tonic-gate assert(tx->bt_readonly); 1344*7c478bd9Sstevel@tonic-gate backend_tx_end(tx); 1345*7c478bd9Sstevel@tonic-gate } 1346*7c478bd9Sstevel@tonic-gate 1347*7c478bd9Sstevel@tonic-gate /* 1348*7c478bd9Sstevel@tonic-gate * Fails with 1349*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1350*7c478bd9Sstevel@tonic-gate * _BACKEND_ACCESS 1351*7c478bd9Sstevel@tonic-gate * _BACKEND_READONLY 1352*7c478bd9Sstevel@tonic-gate */ 1353*7c478bd9Sstevel@tonic-gate int 1354*7c478bd9Sstevel@tonic-gate backend_tx_begin(backend_type_t t, backend_tx_t **txp) 1355*7c478bd9Sstevel@tonic-gate { 1356*7c478bd9Sstevel@tonic-gate int r; 1357*7c478bd9Sstevel@tonic-gate char *errmsg; 1358*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1359*7c478bd9Sstevel@tonic-gate 1360*7c478bd9Sstevel@tonic-gate r = backend_tx_begin_common(t, txp, 1); 1361*7c478bd9Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) 1362*7c478bd9Sstevel@tonic-gate return (r); 1363*7c478bd9Sstevel@tonic-gate 1364*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1365*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1366*7c478bd9Sstevel@tonic-gate r = sqlite_exec((*txp)->bt_be->be_db, "BEGIN TRANSACTION", NULL, NULL, 1367*7c478bd9Sstevel@tonic-gate &errmsg); 1368*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); 1369*7c478bd9Sstevel@tonic-gate if (r == SQLITE_FULL) 1370*7c478bd9Sstevel@tonic-gate (*txp)->bt_full = 1; 1371*7c478bd9Sstevel@tonic-gate r = backend_error((*txp)->bt_be, r, errmsg); 1372*7c478bd9Sstevel@tonic-gate 1373*7c478bd9Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 1374*7c478bd9Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 1375*7c478bd9Sstevel@tonic-gate (void) sqlite_exec((*txp)->bt_be->be_db, 1376*7c478bd9Sstevel@tonic-gate "ROLLBACK TRANSACTION", NULL, NULL, NULL); 1377*7c478bd9Sstevel@tonic-gate backend_tx_end(*txp); 1378*7c478bd9Sstevel@tonic-gate *txp = NULL; 1379*7c478bd9Sstevel@tonic-gate return (r); 1380*7c478bd9Sstevel@tonic-gate } 1381*7c478bd9Sstevel@tonic-gate 1382*7c478bd9Sstevel@tonic-gate (*txp)->bt_readonly = 0; 1383*7c478bd9Sstevel@tonic-gate 1384*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 1385*7c478bd9Sstevel@tonic-gate } 1386*7c478bd9Sstevel@tonic-gate 1387*7c478bd9Sstevel@tonic-gate void 1388*7c478bd9Sstevel@tonic-gate backend_tx_rollback(backend_tx_t *tx) 1389*7c478bd9Sstevel@tonic-gate { 1390*7c478bd9Sstevel@tonic-gate int r; 1391*7c478bd9Sstevel@tonic-gate char *errmsg; 1392*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1393*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1394*7c478bd9Sstevel@tonic-gate 1395*7c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 1396*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1397*7c478bd9Sstevel@tonic-gate 1398*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1399*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1400*7c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 1401*7c478bd9Sstevel@tonic-gate &errmsg); 1402*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1403*7c478bd9Sstevel@tonic-gate if (r == SQLITE_FULL) 1404*7c478bd9Sstevel@tonic-gate tx->bt_full = 1; 1405*7c478bd9Sstevel@tonic-gate (void) backend_error(be, r, errmsg); 1406*7c478bd9Sstevel@tonic-gate 1407*7c478bd9Sstevel@tonic-gate backend_tx_end(tx); 1408*7c478bd9Sstevel@tonic-gate } 1409*7c478bd9Sstevel@tonic-gate 1410*7c478bd9Sstevel@tonic-gate /* 1411*7c478bd9Sstevel@tonic-gate * Fails with 1412*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1413*7c478bd9Sstevel@tonic-gate */ 1414*7c478bd9Sstevel@tonic-gate int 1415*7c478bd9Sstevel@tonic-gate backend_tx_commit(backend_tx_t *tx) 1416*7c478bd9Sstevel@tonic-gate { 1417*7c478bd9Sstevel@tonic-gate int r, r2; 1418*7c478bd9Sstevel@tonic-gate char *errmsg; 1419*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1420*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1421*7c478bd9Sstevel@tonic-gate 1422*7c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 1423*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1424*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1425*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1426*7c478bd9Sstevel@tonic-gate r = sqlite_exec(be->be_db, "COMMIT TRANSACTION", NULL, NULL, 1427*7c478bd9Sstevel@tonic-gate &errmsg); 1428*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1429*7c478bd9Sstevel@tonic-gate if (r == SQLITE_FULL) 1430*7c478bd9Sstevel@tonic-gate tx->bt_full = 1; 1431*7c478bd9Sstevel@tonic-gate 1432*7c478bd9Sstevel@tonic-gate r = backend_error(be, r, errmsg); 1433*7c478bd9Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 1434*7c478bd9Sstevel@tonic-gate 1435*7c478bd9Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 1436*7c478bd9Sstevel@tonic-gate r2 = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 1437*7c478bd9Sstevel@tonic-gate &errmsg); 1438*7c478bd9Sstevel@tonic-gate r2 = backend_error(be, r2, errmsg); 1439*7c478bd9Sstevel@tonic-gate if (r2 != REP_PROTOCOL_SUCCESS) 1440*7c478bd9Sstevel@tonic-gate backend_panic("cannot rollback failed commit"); 1441*7c478bd9Sstevel@tonic-gate 1442*7c478bd9Sstevel@tonic-gate backend_tx_end(tx); 1443*7c478bd9Sstevel@tonic-gate return (r); 1444*7c478bd9Sstevel@tonic-gate } 1445*7c478bd9Sstevel@tonic-gate backend_tx_end(tx); 1446*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 1447*7c478bd9Sstevel@tonic-gate } 1448*7c478bd9Sstevel@tonic-gate 1449*7c478bd9Sstevel@tonic-gate static const char * 1450*7c478bd9Sstevel@tonic-gate id_space_to_name(enum id_space id) 1451*7c478bd9Sstevel@tonic-gate { 1452*7c478bd9Sstevel@tonic-gate switch (id) { 1453*7c478bd9Sstevel@tonic-gate case BACKEND_ID_SERVICE_INSTANCE: 1454*7c478bd9Sstevel@tonic-gate return ("SI"); 1455*7c478bd9Sstevel@tonic-gate case BACKEND_ID_PROPERTYGRP: 1456*7c478bd9Sstevel@tonic-gate return ("PG"); 1457*7c478bd9Sstevel@tonic-gate case BACKEND_ID_GENERATION: 1458*7c478bd9Sstevel@tonic-gate return ("GEN"); 1459*7c478bd9Sstevel@tonic-gate case BACKEND_ID_PROPERTY: 1460*7c478bd9Sstevel@tonic-gate return ("PROP"); 1461*7c478bd9Sstevel@tonic-gate case BACKEND_ID_VALUE: 1462*7c478bd9Sstevel@tonic-gate return ("VAL"); 1463*7c478bd9Sstevel@tonic-gate case BACKEND_ID_SNAPNAME: 1464*7c478bd9Sstevel@tonic-gate return ("SNAME"); 1465*7c478bd9Sstevel@tonic-gate case BACKEND_ID_SNAPSHOT: 1466*7c478bd9Sstevel@tonic-gate return ("SHOT"); 1467*7c478bd9Sstevel@tonic-gate case BACKEND_ID_SNAPLEVEL: 1468*7c478bd9Sstevel@tonic-gate return ("SLVL"); 1469*7c478bd9Sstevel@tonic-gate default: 1470*7c478bd9Sstevel@tonic-gate abort(); 1471*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 1472*7c478bd9Sstevel@tonic-gate } 1473*7c478bd9Sstevel@tonic-gate } 1474*7c478bd9Sstevel@tonic-gate 1475*7c478bd9Sstevel@tonic-gate /* 1476*7c478bd9Sstevel@tonic-gate * Returns a new id or 0 if the id argument is invalid or the query fails. 1477*7c478bd9Sstevel@tonic-gate */ 1478*7c478bd9Sstevel@tonic-gate uint32_t 1479*7c478bd9Sstevel@tonic-gate backend_new_id(backend_tx_t *tx, enum id_space id) 1480*7c478bd9Sstevel@tonic-gate { 1481*7c478bd9Sstevel@tonic-gate struct run_single_int_info info; 1482*7c478bd9Sstevel@tonic-gate uint32_t new_id = 0; 1483*7c478bd9Sstevel@tonic-gate const char *name = id_space_to_name(id); 1484*7c478bd9Sstevel@tonic-gate char *errmsg; 1485*7c478bd9Sstevel@tonic-gate int ret; 1486*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1487*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1488*7c478bd9Sstevel@tonic-gate 1489*7c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 1490*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1491*7c478bd9Sstevel@tonic-gate 1492*7c478bd9Sstevel@tonic-gate info.rs_out = &new_id; 1493*7c478bd9Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 1494*7c478bd9Sstevel@tonic-gate 1495*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1496*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1497*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 1498*7c478bd9Sstevel@tonic-gate "SELECT id_next FROM id_tbl WHERE (id_name = '%q');" 1499*7c478bd9Sstevel@tonic-gate "UPDATE id_tbl SET id_next = id_next + 1 WHERE (id_name = '%q');", 1500*7c478bd9Sstevel@tonic-gate run_single_int_callback, &info, &errmsg, name, name); 1501*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1502*7c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 1503*7c478bd9Sstevel@tonic-gate tx->bt_full = 1; 1504*7c478bd9Sstevel@tonic-gate 1505*7c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 1506*7c478bd9Sstevel@tonic-gate 1507*7c478bd9Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) { 1508*7c478bd9Sstevel@tonic-gate return (0); 1509*7c478bd9Sstevel@tonic-gate } 1510*7c478bd9Sstevel@tonic-gate 1511*7c478bd9Sstevel@tonic-gate return (new_id); 1512*7c478bd9Sstevel@tonic-gate } 1513*7c478bd9Sstevel@tonic-gate 1514*7c478bd9Sstevel@tonic-gate /* 1515*7c478bd9Sstevel@tonic-gate * Returns 1516*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1517*7c478bd9Sstevel@tonic-gate * _DONE - callback aborted query 1518*7c478bd9Sstevel@tonic-gate * _SUCCESS 1519*7c478bd9Sstevel@tonic-gate */ 1520*7c478bd9Sstevel@tonic-gate int 1521*7c478bd9Sstevel@tonic-gate backend_tx_run(backend_tx_t *tx, backend_query_t *q, 1522*7c478bd9Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 1523*7c478bd9Sstevel@tonic-gate { 1524*7c478bd9Sstevel@tonic-gate char *errmsg = NULL; 1525*7c478bd9Sstevel@tonic-gate int ret; 1526*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1527*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1528*7c478bd9Sstevel@tonic-gate 1529*7c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL); 1530*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1531*7c478bd9Sstevel@tonic-gate 1532*7c478bd9Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 1533*7c478bd9Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 1534*7c478bd9Sstevel@tonic-gate 1535*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1536*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1537*7c478bd9Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 1538*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1539*7c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 1540*7c478bd9Sstevel@tonic-gate tx->bt_full = 1; 1541*7c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 1542*7c478bd9Sstevel@tonic-gate 1543*7c478bd9Sstevel@tonic-gate return (ret); 1544*7c478bd9Sstevel@tonic-gate } 1545*7c478bd9Sstevel@tonic-gate 1546*7c478bd9Sstevel@tonic-gate /* 1547*7c478bd9Sstevel@tonic-gate * Returns 1548*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1549*7c478bd9Sstevel@tonic-gate * _NOT_FOUND - the query returned no results 1550*7c478bd9Sstevel@tonic-gate * _SUCCESS - the query returned a single integer 1551*7c478bd9Sstevel@tonic-gate */ 1552*7c478bd9Sstevel@tonic-gate int 1553*7c478bd9Sstevel@tonic-gate backend_tx_run_single_int(backend_tx_t *tx, backend_query_t *q, uint32_t *buf) 1554*7c478bd9Sstevel@tonic-gate { 1555*7c478bd9Sstevel@tonic-gate struct run_single_int_info info; 1556*7c478bd9Sstevel@tonic-gate int ret; 1557*7c478bd9Sstevel@tonic-gate 1558*7c478bd9Sstevel@tonic-gate info.rs_out = buf; 1559*7c478bd9Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 1560*7c478bd9Sstevel@tonic-gate 1561*7c478bd9Sstevel@tonic-gate ret = backend_tx_run(tx, q, run_single_int_callback, &info); 1562*7c478bd9Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 1563*7c478bd9Sstevel@tonic-gate 1564*7c478bd9Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) 1565*7c478bd9Sstevel@tonic-gate return (ret); 1566*7c478bd9Sstevel@tonic-gate 1567*7c478bd9Sstevel@tonic-gate return (info.rs_result); 1568*7c478bd9Sstevel@tonic-gate } 1569*7c478bd9Sstevel@tonic-gate 1570*7c478bd9Sstevel@tonic-gate /* 1571*7c478bd9Sstevel@tonic-gate * Fails with 1572*7c478bd9Sstevel@tonic-gate * _NO_RESOURCES - out of memory 1573*7c478bd9Sstevel@tonic-gate */ 1574*7c478bd9Sstevel@tonic-gate int 1575*7c478bd9Sstevel@tonic-gate backend_tx_run_update(backend_tx_t *tx, const char *format, ...) 1576*7c478bd9Sstevel@tonic-gate { 1577*7c478bd9Sstevel@tonic-gate va_list a; 1578*7c478bd9Sstevel@tonic-gate char *errmsg; 1579*7c478bd9Sstevel@tonic-gate int ret; 1580*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1581*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1582*7c478bd9Sstevel@tonic-gate 1583*7c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 1584*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1585*7c478bd9Sstevel@tonic-gate 1586*7c478bd9Sstevel@tonic-gate va_start(a, format); 1587*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1588*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1589*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 1590*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1591*7c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 1592*7c478bd9Sstevel@tonic-gate tx->bt_full = 1; 1593*7c478bd9Sstevel@tonic-gate va_end(a); 1594*7c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 1595*7c478bd9Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 1596*7c478bd9Sstevel@tonic-gate 1597*7c478bd9Sstevel@tonic-gate return (ret); 1598*7c478bd9Sstevel@tonic-gate } 1599*7c478bd9Sstevel@tonic-gate 1600*7c478bd9Sstevel@tonic-gate /* 1601*7c478bd9Sstevel@tonic-gate * returns REP_PROTOCOL_FAIL_NOT_FOUND if no changes occured 1602*7c478bd9Sstevel@tonic-gate */ 1603*7c478bd9Sstevel@tonic-gate int 1604*7c478bd9Sstevel@tonic-gate backend_tx_run_update_changed(backend_tx_t *tx, const char *format, ...) 1605*7c478bd9Sstevel@tonic-gate { 1606*7c478bd9Sstevel@tonic-gate va_list a; 1607*7c478bd9Sstevel@tonic-gate char *errmsg; 1608*7c478bd9Sstevel@tonic-gate int ret; 1609*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1610*7c478bd9Sstevel@tonic-gate hrtime_t ts, vts; 1611*7c478bd9Sstevel@tonic-gate 1612*7c478bd9Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 1613*7c478bd9Sstevel@tonic-gate be = tx->bt_be; 1614*7c478bd9Sstevel@tonic-gate 1615*7c478bd9Sstevel@tonic-gate va_start(a, format); 1616*7c478bd9Sstevel@tonic-gate ts = gethrtime(); 1617*7c478bd9Sstevel@tonic-gate vts = gethrvtime(); 1618*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 1619*7c478bd9Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 1620*7c478bd9Sstevel@tonic-gate if (ret == SQLITE_FULL) 1621*7c478bd9Sstevel@tonic-gate tx->bt_full = 1; 1622*7c478bd9Sstevel@tonic-gate va_end(a); 1623*7c478bd9Sstevel@tonic-gate 1624*7c478bd9Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 1625*7c478bd9Sstevel@tonic-gate 1626*7c478bd9Sstevel@tonic-gate return (ret); 1627*7c478bd9Sstevel@tonic-gate } 1628*7c478bd9Sstevel@tonic-gate 1629*7c478bd9Sstevel@tonic-gate #define BACKEND_ADD_SCHEMA(be, file, tbls, idxs) \ 1630*7c478bd9Sstevel@tonic-gate (backend_add_schema((be), (file), \ 1631*7c478bd9Sstevel@tonic-gate (tbls), sizeof (tbls) / sizeof (*(tbls)), \ 1632*7c478bd9Sstevel@tonic-gate (idxs), sizeof (idxs) / sizeof (*(idxs)))) 1633*7c478bd9Sstevel@tonic-gate 1634*7c478bd9Sstevel@tonic-gate static int 1635*7c478bd9Sstevel@tonic-gate backend_add_schema(sqlite_backend_t *be, const char *file, 1636*7c478bd9Sstevel@tonic-gate struct backend_tbl_info *tbls, int tbl_count, 1637*7c478bd9Sstevel@tonic-gate struct backend_idx_info *idxs, int idx_count) 1638*7c478bd9Sstevel@tonic-gate { 1639*7c478bd9Sstevel@tonic-gate int i; 1640*7c478bd9Sstevel@tonic-gate char *errmsg; 1641*7c478bd9Sstevel@tonic-gate int ret; 1642*7c478bd9Sstevel@tonic-gate 1643*7c478bd9Sstevel@tonic-gate /* 1644*7c478bd9Sstevel@tonic-gate * Create the tables. 1645*7c478bd9Sstevel@tonic-gate */ 1646*7c478bd9Sstevel@tonic-gate for (i = 0; i < tbl_count; i++) { 1647*7c478bd9Sstevel@tonic-gate if (tbls[i].bti_name == NULL) { 1648*7c478bd9Sstevel@tonic-gate assert(i + 1 == tbl_count); 1649*7c478bd9Sstevel@tonic-gate break; 1650*7c478bd9Sstevel@tonic-gate } 1651*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 1652*7c478bd9Sstevel@tonic-gate "CREATE TABLE %s (%s);\n", 1653*7c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, tbls[i].bti_name, tbls[i].bti_cols); 1654*7c478bd9Sstevel@tonic-gate 1655*7c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 1656*7c478bd9Sstevel@tonic-gate configd_critical( 1657*7c478bd9Sstevel@tonic-gate "%s: %s table creation fails: %s\n", file, 1658*7c478bd9Sstevel@tonic-gate tbls[i].bti_name, errmsg); 1659*7c478bd9Sstevel@tonic-gate free(errmsg); 1660*7c478bd9Sstevel@tonic-gate return (-1); 1661*7c478bd9Sstevel@tonic-gate } 1662*7c478bd9Sstevel@tonic-gate } 1663*7c478bd9Sstevel@tonic-gate 1664*7c478bd9Sstevel@tonic-gate /* 1665*7c478bd9Sstevel@tonic-gate * Make indices on key tables and columns. 1666*7c478bd9Sstevel@tonic-gate */ 1667*7c478bd9Sstevel@tonic-gate for (i = 0; i < idx_count; i++) { 1668*7c478bd9Sstevel@tonic-gate if (idxs[i].bxi_tbl == NULL) { 1669*7c478bd9Sstevel@tonic-gate assert(i + 1 == idx_count); 1670*7c478bd9Sstevel@tonic-gate break; 1671*7c478bd9Sstevel@tonic-gate } 1672*7c478bd9Sstevel@tonic-gate 1673*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 1674*7c478bd9Sstevel@tonic-gate "CREATE INDEX %s_%s ON %s (%s);\n", 1675*7c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, idxs[i].bxi_tbl, idxs[i].bxi_idx, 1676*7c478bd9Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_cols); 1677*7c478bd9Sstevel@tonic-gate 1678*7c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 1679*7c478bd9Sstevel@tonic-gate configd_critical( 1680*7c478bd9Sstevel@tonic-gate "%s: %s_%s index creation fails: %s\n", file, 1681*7c478bd9Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_idx, errmsg); 1682*7c478bd9Sstevel@tonic-gate free(errmsg); 1683*7c478bd9Sstevel@tonic-gate return (-1); 1684*7c478bd9Sstevel@tonic-gate } 1685*7c478bd9Sstevel@tonic-gate } 1686*7c478bd9Sstevel@tonic-gate return (0); 1687*7c478bd9Sstevel@tonic-gate } 1688*7c478bd9Sstevel@tonic-gate 1689*7c478bd9Sstevel@tonic-gate static int 1690*7c478bd9Sstevel@tonic-gate backend_init_schema(sqlite_backend_t *be, const char *db_file, backend_type_t t) 1691*7c478bd9Sstevel@tonic-gate { 1692*7c478bd9Sstevel@tonic-gate int i; 1693*7c478bd9Sstevel@tonic-gate char *errmsg; 1694*7c478bd9Sstevel@tonic-gate int ret; 1695*7c478bd9Sstevel@tonic-gate 1696*7c478bd9Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || t == BACKEND_TYPE_NONPERSIST); 1697*7c478bd9Sstevel@tonic-gate 1698*7c478bd9Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) { 1699*7c478bd9Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_normal, idxs_normal); 1700*7c478bd9Sstevel@tonic-gate } else if (t == BACKEND_TYPE_NONPERSIST) { 1701*7c478bd9Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_np, idxs_np); 1702*7c478bd9Sstevel@tonic-gate } else { 1703*7c478bd9Sstevel@tonic-gate abort(); /* can't happen */ 1704*7c478bd9Sstevel@tonic-gate } 1705*7c478bd9Sstevel@tonic-gate 1706*7c478bd9Sstevel@tonic-gate if (ret < 0) { 1707*7c478bd9Sstevel@tonic-gate return (ret); 1708*7c478bd9Sstevel@tonic-gate } 1709*7c478bd9Sstevel@tonic-gate 1710*7c478bd9Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_common, idxs_common); 1711*7c478bd9Sstevel@tonic-gate if (ret < 0) { 1712*7c478bd9Sstevel@tonic-gate return (ret); 1713*7c478bd9Sstevel@tonic-gate } 1714*7c478bd9Sstevel@tonic-gate 1715*7c478bd9Sstevel@tonic-gate /* 1716*7c478bd9Sstevel@tonic-gate * Add the schema version to the table 1717*7c478bd9Sstevel@tonic-gate */ 1718*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 1719*7c478bd9Sstevel@tonic-gate "INSERT INTO schema_version (schema_version) VALUES (%d)", 1720*7c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, BACKEND_SCHEMA_VERSION); 1721*7c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 1722*7c478bd9Sstevel@tonic-gate configd_critical( 1723*7c478bd9Sstevel@tonic-gate "setting schema version fails: %s\n", errmsg); 1724*7c478bd9Sstevel@tonic-gate free(errmsg); 1725*7c478bd9Sstevel@tonic-gate } 1726*7c478bd9Sstevel@tonic-gate 1727*7c478bd9Sstevel@tonic-gate /* 1728*7c478bd9Sstevel@tonic-gate * Populate id_tbl with initial IDs. 1729*7c478bd9Sstevel@tonic-gate */ 1730*7c478bd9Sstevel@tonic-gate for (i = 0; i < BACKEND_ID_INVALID; i++) { 1731*7c478bd9Sstevel@tonic-gate const char *name = id_space_to_name(i); 1732*7c478bd9Sstevel@tonic-gate 1733*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 1734*7c478bd9Sstevel@tonic-gate "INSERT INTO id_tbl (id_name, id_next) " 1735*7c478bd9Sstevel@tonic-gate "VALUES ('%q', %d);", NULL, NULL, &errmsg, name, 1); 1736*7c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 1737*7c478bd9Sstevel@tonic-gate configd_critical( 1738*7c478bd9Sstevel@tonic-gate "id insertion for %s fails: %s\n", name, errmsg); 1739*7c478bd9Sstevel@tonic-gate free(errmsg); 1740*7c478bd9Sstevel@tonic-gate return (-1); 1741*7c478bd9Sstevel@tonic-gate } 1742*7c478bd9Sstevel@tonic-gate } 1743*7c478bd9Sstevel@tonic-gate /* 1744*7c478bd9Sstevel@tonic-gate * Set the persistance of the database. The normal database is marked 1745*7c478bd9Sstevel@tonic-gate * "synchronous", so that all writes are synchronized to stable storage 1746*7c478bd9Sstevel@tonic-gate * before proceeding. 1747*7c478bd9Sstevel@tonic-gate */ 1748*7c478bd9Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 1749*7c478bd9Sstevel@tonic-gate "PRAGMA default_synchronous = %s; PRAGMA synchronous = %s;", 1750*7c478bd9Sstevel@tonic-gate NULL, NULL, &errmsg, 1751*7c478bd9Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF", 1752*7c478bd9Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF"); 1753*7c478bd9Sstevel@tonic-gate if (ret != SQLITE_OK) { 1754*7c478bd9Sstevel@tonic-gate configd_critical("pragma setting fails: %s\n", errmsg); 1755*7c478bd9Sstevel@tonic-gate free(errmsg); 1756*7c478bd9Sstevel@tonic-gate return (-1); 1757*7c478bd9Sstevel@tonic-gate } 1758*7c478bd9Sstevel@tonic-gate 1759*7c478bd9Sstevel@tonic-gate return (0); 1760*7c478bd9Sstevel@tonic-gate } 1761*7c478bd9Sstevel@tonic-gate 1762*7c478bd9Sstevel@tonic-gate int 1763*7c478bd9Sstevel@tonic-gate backend_init(const char *db_file, const char *npdb_file, int have_np) 1764*7c478bd9Sstevel@tonic-gate { 1765*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be; 1766*7c478bd9Sstevel@tonic-gate int r; 1767*7c478bd9Sstevel@tonic-gate int writable_persist = 1; 1768*7c478bd9Sstevel@tonic-gate 1769*7c478bd9Sstevel@tonic-gate /* set up our temporary directory */ 1770*7c478bd9Sstevel@tonic-gate sqlite_temp_directory = "/etc/svc/volatile"; 1771*7c478bd9Sstevel@tonic-gate 1772*7c478bd9Sstevel@tonic-gate if (strcmp(SQLITE_VERSION, sqlite_version) != 0) { 1773*7c478bd9Sstevel@tonic-gate configd_critical("Mismatched link! (%s should be %s)\n", 1774*7c478bd9Sstevel@tonic-gate sqlite_version, SQLITE_VERSION); 1775*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 1776*7c478bd9Sstevel@tonic-gate } 1777*7c478bd9Sstevel@tonic-gate if (db_file == NULL) 1778*7c478bd9Sstevel@tonic-gate db_file = REPOSITORY_DB; 1779*7c478bd9Sstevel@tonic-gate 1780*7c478bd9Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NORMAL, db_file, &be); 1781*7c478bd9Sstevel@tonic-gate switch (r) { 1782*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 1783*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 1784*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 1785*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 1786*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 1787*7c478bd9Sstevel@tonic-gate break; /* success */ 1788*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 1789*7c478bd9Sstevel@tonic-gate writable_persist = 0; 1790*7c478bd9Sstevel@tonic-gate break; 1791*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 1792*7c478bd9Sstevel@tonic-gate if (backend_init_schema(be, db_file, BACKEND_TYPE_NORMAL)) { 1793*7c478bd9Sstevel@tonic-gate backend_destroy(be); 1794*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 1795*7c478bd9Sstevel@tonic-gate } 1796*7c478bd9Sstevel@tonic-gate break; 1797*7c478bd9Sstevel@tonic-gate default: 1798*7c478bd9Sstevel@tonic-gate abort(); 1799*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 1800*7c478bd9Sstevel@tonic-gate } 1801*7c478bd9Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NORMAL, be); 1802*7c478bd9Sstevel@tonic-gate 1803*7c478bd9Sstevel@tonic-gate if (have_np) { 1804*7c478bd9Sstevel@tonic-gate if (npdb_file == NULL) 1805*7c478bd9Sstevel@tonic-gate npdb_file = NONPERSIST_DB; 1806*7c478bd9Sstevel@tonic-gate 1807*7c478bd9Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NONPERSIST, npdb_file, &be); 1808*7c478bd9Sstevel@tonic-gate switch (r) { 1809*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 1810*7c478bd9Sstevel@tonic-gate break; /* success */ 1811*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 1812*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 1813*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 1814*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 1815*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 1816*7c478bd9Sstevel@tonic-gate configd_critical("%s: unable to write\n", npdb_file); 1817*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 1818*7c478bd9Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 1819*7c478bd9Sstevel@tonic-gate if (backend_init_schema(be, db_file, 1820*7c478bd9Sstevel@tonic-gate BACKEND_TYPE_NONPERSIST)) { 1821*7c478bd9Sstevel@tonic-gate backend_destroy(be); 1822*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 1823*7c478bd9Sstevel@tonic-gate } 1824*7c478bd9Sstevel@tonic-gate break; 1825*7c478bd9Sstevel@tonic-gate default: 1826*7c478bd9Sstevel@tonic-gate abort(); 1827*7c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 1828*7c478bd9Sstevel@tonic-gate } 1829*7c478bd9Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NONPERSIST, be); 1830*7c478bd9Sstevel@tonic-gate 1831*7c478bd9Sstevel@tonic-gate /* 1832*7c478bd9Sstevel@tonic-gate * If we started up with a writable filesystem, but the 1833*7c478bd9Sstevel@tonic-gate * non-persistent database needed initialization, we 1834*7c478bd9Sstevel@tonic-gate * are booting a non-global zone, so do a backup. 1835*7c478bd9Sstevel@tonic-gate */ 1836*7c478bd9Sstevel@tonic-gate if (r == BACKEND_CREATE_NEED_INIT && writable_persist && 1837*7c478bd9Sstevel@tonic-gate backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 1838*7c478bd9Sstevel@tonic-gate REP_PROTOCOL_SUCCESS) { 1839*7c478bd9Sstevel@tonic-gate if (backend_create_backup_locked(be, 1840*7c478bd9Sstevel@tonic-gate REPOSITORY_BOOT_BACKUP) != REP_PROTOCOL_SUCCESS) { 1841*7c478bd9Sstevel@tonic-gate configd_critical( 1842*7c478bd9Sstevel@tonic-gate "unable to create \"%s\" backup of " 1843*7c478bd9Sstevel@tonic-gate "\"%s\"\n", REPOSITORY_BOOT_BACKUP, 1844*7c478bd9Sstevel@tonic-gate be->be_path); 1845*7c478bd9Sstevel@tonic-gate } 1846*7c478bd9Sstevel@tonic-gate backend_unlock(be); 1847*7c478bd9Sstevel@tonic-gate } 1848*7c478bd9Sstevel@tonic-gate } 1849*7c478bd9Sstevel@tonic-gate return (CONFIGD_EXIT_OKAY); 1850*7c478bd9Sstevel@tonic-gate } 1851*7c478bd9Sstevel@tonic-gate 1852*7c478bd9Sstevel@tonic-gate /* 1853*7c478bd9Sstevel@tonic-gate * quiesce all database activity prior to exiting 1854*7c478bd9Sstevel@tonic-gate */ 1855*7c478bd9Sstevel@tonic-gate void 1856*7c478bd9Sstevel@tonic-gate backend_fini(void) 1857*7c478bd9Sstevel@tonic-gate { 1858*7c478bd9Sstevel@tonic-gate sqlite_backend_t *be_normal, *be_np; 1859*7c478bd9Sstevel@tonic-gate 1860*7c478bd9Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NORMAL, 1, &be_normal); 1861*7c478bd9Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NONPERSIST, 1, &be_np); 1862*7c478bd9Sstevel@tonic-gate } 1863*7c478bd9Sstevel@tonic-gate 1864*7c478bd9Sstevel@tonic-gate #define QUERY_BASE 128 1865*7c478bd9Sstevel@tonic-gate backend_query_t * 1866*7c478bd9Sstevel@tonic-gate backend_query_alloc(void) 1867*7c478bd9Sstevel@tonic-gate { 1868*7c478bd9Sstevel@tonic-gate backend_query_t *q; 1869*7c478bd9Sstevel@tonic-gate q = calloc(1, sizeof (backend_query_t)); 1870*7c478bd9Sstevel@tonic-gate if (q != NULL) { 1871*7c478bd9Sstevel@tonic-gate q->bq_size = QUERY_BASE; 1872*7c478bd9Sstevel@tonic-gate q->bq_buf = calloc(1, q->bq_size); 1873*7c478bd9Sstevel@tonic-gate if (q->bq_buf == NULL) { 1874*7c478bd9Sstevel@tonic-gate q->bq_size = 0; 1875*7c478bd9Sstevel@tonic-gate } 1876*7c478bd9Sstevel@tonic-gate 1877*7c478bd9Sstevel@tonic-gate } 1878*7c478bd9Sstevel@tonic-gate return (q); 1879*7c478bd9Sstevel@tonic-gate } 1880*7c478bd9Sstevel@tonic-gate 1881*7c478bd9Sstevel@tonic-gate void 1882*7c478bd9Sstevel@tonic-gate backend_query_append(backend_query_t *q, const char *value) 1883*7c478bd9Sstevel@tonic-gate { 1884*7c478bd9Sstevel@tonic-gate char *alloc; 1885*7c478bd9Sstevel@tonic-gate int count; 1886*7c478bd9Sstevel@tonic-gate size_t size, old_len; 1887*7c478bd9Sstevel@tonic-gate 1888*7c478bd9Sstevel@tonic-gate if (q == NULL) { 1889*7c478bd9Sstevel@tonic-gate /* We'll discover the error when we try to run the query. */ 1890*7c478bd9Sstevel@tonic-gate return; 1891*7c478bd9Sstevel@tonic-gate } 1892*7c478bd9Sstevel@tonic-gate 1893*7c478bd9Sstevel@tonic-gate while (q->bq_buf != NULL) { 1894*7c478bd9Sstevel@tonic-gate old_len = strlen(q->bq_buf); 1895*7c478bd9Sstevel@tonic-gate size = q->bq_size; 1896*7c478bd9Sstevel@tonic-gate count = strlcat(q->bq_buf, value, size); 1897*7c478bd9Sstevel@tonic-gate 1898*7c478bd9Sstevel@tonic-gate if (count < size) 1899*7c478bd9Sstevel@tonic-gate break; /* success */ 1900*7c478bd9Sstevel@tonic-gate 1901*7c478bd9Sstevel@tonic-gate q->bq_buf[old_len] = 0; 1902*7c478bd9Sstevel@tonic-gate size = round_up_to_p2(count + 1); 1903*7c478bd9Sstevel@tonic-gate 1904*7c478bd9Sstevel@tonic-gate assert(size > q->bq_size); 1905*7c478bd9Sstevel@tonic-gate alloc = realloc(q->bq_buf, size); 1906*7c478bd9Sstevel@tonic-gate if (alloc == NULL) { 1907*7c478bd9Sstevel@tonic-gate free(q->bq_buf); 1908*7c478bd9Sstevel@tonic-gate q->bq_buf = NULL; 1909*7c478bd9Sstevel@tonic-gate break; /* can't grow */ 1910*7c478bd9Sstevel@tonic-gate } 1911*7c478bd9Sstevel@tonic-gate 1912*7c478bd9Sstevel@tonic-gate q->bq_buf = alloc; 1913*7c478bd9Sstevel@tonic-gate q->bq_size = size; 1914*7c478bd9Sstevel@tonic-gate } 1915*7c478bd9Sstevel@tonic-gate } 1916*7c478bd9Sstevel@tonic-gate 1917*7c478bd9Sstevel@tonic-gate void 1918*7c478bd9Sstevel@tonic-gate backend_query_add(backend_query_t *q, const char *format, ...) 1919*7c478bd9Sstevel@tonic-gate { 1920*7c478bd9Sstevel@tonic-gate va_list args; 1921*7c478bd9Sstevel@tonic-gate char *new; 1922*7c478bd9Sstevel@tonic-gate 1923*7c478bd9Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 1924*7c478bd9Sstevel@tonic-gate return; 1925*7c478bd9Sstevel@tonic-gate 1926*7c478bd9Sstevel@tonic-gate va_start(args, format); 1927*7c478bd9Sstevel@tonic-gate new = sqlite_vmprintf(format, args); 1928*7c478bd9Sstevel@tonic-gate va_end(args); 1929*7c478bd9Sstevel@tonic-gate 1930*7c478bd9Sstevel@tonic-gate if (new == NULL) { 1931*7c478bd9Sstevel@tonic-gate free(q->bq_buf); 1932*7c478bd9Sstevel@tonic-gate q->bq_buf = NULL; 1933*7c478bd9Sstevel@tonic-gate return; 1934*7c478bd9Sstevel@tonic-gate } 1935*7c478bd9Sstevel@tonic-gate 1936*7c478bd9Sstevel@tonic-gate backend_query_append(q, new); 1937*7c478bd9Sstevel@tonic-gate 1938*7c478bd9Sstevel@tonic-gate free(new); 1939*7c478bd9Sstevel@tonic-gate } 1940*7c478bd9Sstevel@tonic-gate 1941*7c478bd9Sstevel@tonic-gate void 1942*7c478bd9Sstevel@tonic-gate backend_query_free(backend_query_t *q) 1943*7c478bd9Sstevel@tonic-gate { 1944*7c478bd9Sstevel@tonic-gate if (q != NULL) { 1945*7c478bd9Sstevel@tonic-gate if (q->bq_buf != NULL) { 1946*7c478bd9Sstevel@tonic-gate free(q->bq_buf); 1947*7c478bd9Sstevel@tonic-gate } 1948*7c478bd9Sstevel@tonic-gate free(q); 1949*7c478bd9Sstevel@tonic-gate } 1950*7c478bd9Sstevel@tonic-gate } 1951