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