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