10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54520Snw141292 * Common Development and Distribution License (the "License"). 64520Snw141292 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 215777Stw21770 220Sstevel@tonic-gate /* 235777Stw21770 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 29407Sjwadams /* 30407Sjwadams * sqlite is not compatible with _FILE_OFFSET_BITS=64, but we need to 31407Sjwadams * be able to statvfs(2) possibly large systems. This define gives us 32407Sjwadams * access to the transitional interfaces. See lfcompile64(5) for how 33407Sjwadams * _LARGEFILE64_SOURCE works. 34407Sjwadams */ 35407Sjwadams #define _LARGEFILE64_SOURCE 36407Sjwadams 370Sstevel@tonic-gate #include <assert.h> 380Sstevel@tonic-gate #include <door.h> 390Sstevel@tonic-gate #include <dirent.h> 400Sstevel@tonic-gate #include <errno.h> 410Sstevel@tonic-gate #include <fcntl.h> 420Sstevel@tonic-gate #include <limits.h> 430Sstevel@tonic-gate #include <pthread.h> 440Sstevel@tonic-gate #include <stdarg.h> 450Sstevel@tonic-gate #include <stdio.h> 460Sstevel@tonic-gate #include <stdlib.h> 470Sstevel@tonic-gate #include <string.h> 48407Sjwadams #include <sys/stat.h> 49407Sjwadams #include <sys/statvfs.h> 500Sstevel@tonic-gate #include <unistd.h> 510Sstevel@tonic-gate #include <zone.h> 52*6035Sstevep #include <libscf_priv.h> 530Sstevel@tonic-gate 540Sstevel@tonic-gate #include "configd.h" 550Sstevel@tonic-gate #include "repcache_protocol.h" 560Sstevel@tonic-gate 574520Snw141292 #include <sqlite.h> 584520Snw141292 #include <sqlite-misc.h> 590Sstevel@tonic-gate 600Sstevel@tonic-gate /* 610Sstevel@tonic-gate * This file has two purposes: 620Sstevel@tonic-gate * 630Sstevel@tonic-gate * 1. It contains the database schema, and the code for setting up our backend 640Sstevel@tonic-gate * databases, including installing said schema. 650Sstevel@tonic-gate * 660Sstevel@tonic-gate * 2. It provides a simplified interface to the SQL database library, and 670Sstevel@tonic-gate * synchronizes MT access to the database. 680Sstevel@tonic-gate */ 690Sstevel@tonic-gate 700Sstevel@tonic-gate typedef struct backend_spent { 710Sstevel@tonic-gate uint64_t bs_count; 720Sstevel@tonic-gate hrtime_t bs_time; 730Sstevel@tonic-gate hrtime_t bs_vtime; 740Sstevel@tonic-gate } backend_spent_t; 750Sstevel@tonic-gate 760Sstevel@tonic-gate typedef struct backend_totals { 770Sstevel@tonic-gate backend_spent_t bt_lock; /* waiting for lock */ 780Sstevel@tonic-gate backend_spent_t bt_exec; /* time spent executing SQL */ 790Sstevel@tonic-gate } backend_totals_t; 800Sstevel@tonic-gate 810Sstevel@tonic-gate typedef struct sqlite_backend { 820Sstevel@tonic-gate pthread_mutex_t be_lock; 830Sstevel@tonic-gate pthread_t be_thread; /* thread holding lock */ 840Sstevel@tonic-gate struct sqlite *be_db; 850Sstevel@tonic-gate const char *be_path; /* path to db */ 86407Sjwadams int be_readonly; /* readonly at start, and still is */ 870Sstevel@tonic-gate int be_writing; /* held for writing */ 880Sstevel@tonic-gate backend_type_t be_type; /* type of db */ 89407Sjwadams hrtime_t be_lastcheck; /* time of last read-only check */ 900Sstevel@tonic-gate backend_totals_t be_totals[2]; /* one for reading, one for writing */ 910Sstevel@tonic-gate } sqlite_backend_t; 920Sstevel@tonic-gate 930Sstevel@tonic-gate struct backend_tx { 940Sstevel@tonic-gate sqlite_backend_t *bt_be; 950Sstevel@tonic-gate int bt_readonly; 960Sstevel@tonic-gate int bt_type; 970Sstevel@tonic-gate int bt_full; /* SQLITE_FULL during tx */ 980Sstevel@tonic-gate }; 990Sstevel@tonic-gate 1000Sstevel@tonic-gate #define UPDATE_TOTALS_WR(sb, writing, field, ts, vts) { \ 1010Sstevel@tonic-gate backend_spent_t *__bsp = &(sb)->be_totals[!!(writing)].field; \ 1020Sstevel@tonic-gate __bsp->bs_count++; \ 1030Sstevel@tonic-gate __bsp->bs_time += (gethrtime() - ts); \ 1040Sstevel@tonic-gate __bsp->bs_vtime += (gethrvtime() - vts); \ 1050Sstevel@tonic-gate } 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate #define UPDATE_TOTALS(sb, field, ts, vts) \ 1080Sstevel@tonic-gate UPDATE_TOTALS_WR(sb, (sb)->be_writing, field, ts, vts) 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate struct backend_query { 1110Sstevel@tonic-gate char *bq_buf; 1120Sstevel@tonic-gate size_t bq_size; 1130Sstevel@tonic-gate }; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate struct backend_tbl_info { 1160Sstevel@tonic-gate const char *bti_name; 1170Sstevel@tonic-gate const char *bti_cols; 1180Sstevel@tonic-gate }; 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate struct backend_idx_info { 1210Sstevel@tonic-gate const char *bxi_tbl; 1220Sstevel@tonic-gate const char *bxi_idx; 1230Sstevel@tonic-gate const char *bxi_cols; 1240Sstevel@tonic-gate }; 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate static pthread_mutex_t backend_panic_lock = PTHREAD_MUTEX_INITIALIZER; 1270Sstevel@tonic-gate static pthread_cond_t backend_panic_cv = PTHREAD_COND_INITIALIZER; 1280Sstevel@tonic-gate pthread_t backend_panic_thread = 0; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate int backend_do_trace = 0; /* invoke tracing callback */ 1310Sstevel@tonic-gate int backend_print_trace = 0; /* tracing callback prints SQL */ 1320Sstevel@tonic-gate int backend_panic_abort = 0; /* abort when panicking */ 1330Sstevel@tonic-gate 134407Sjwadams /* interval between read-only checks while starting up */ 135407Sjwadams #define BACKEND_READONLY_CHECK_INTERVAL (2 * (hrtime_t)NANOSEC) 136407Sjwadams 1370Sstevel@tonic-gate /* 1380Sstevel@tonic-gate * Any change to the below schema should bump the version number 1390Sstevel@tonic-gate */ 1400Sstevel@tonic-gate #define BACKEND_SCHEMA_VERSION 5 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate static struct backend_tbl_info tbls_normal[] = { /* BACKEND_TYPE_NORMAL */ 1430Sstevel@tonic-gate /* 1440Sstevel@tonic-gate * service_tbl holds all services. svc_id is the identifier of the 1450Sstevel@tonic-gate * service. 1460Sstevel@tonic-gate */ 1470Sstevel@tonic-gate { 1480Sstevel@tonic-gate "service_tbl", 1490Sstevel@tonic-gate "svc_id INTEGER PRIMARY KEY," 1500Sstevel@tonic-gate "svc_name CHAR(256) NOT NULL" 1510Sstevel@tonic-gate }, 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate /* 1540Sstevel@tonic-gate * instance_tbl holds all of the instances. The parent service id 1550Sstevel@tonic-gate * is instance_svc. 1560Sstevel@tonic-gate */ 1570Sstevel@tonic-gate { 1580Sstevel@tonic-gate "instance_tbl", 1590Sstevel@tonic-gate "instance_id INTEGER PRIMARY KEY," 1600Sstevel@tonic-gate "instance_name CHAR(256) NOT NULL," 1610Sstevel@tonic-gate "instance_svc INTEGER NOT NULL" 1620Sstevel@tonic-gate }, 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate /* 1650Sstevel@tonic-gate * snapshot_lnk_tbl links (instance, snapshot name) with snapshots. 1660Sstevel@tonic-gate */ 1670Sstevel@tonic-gate { 1680Sstevel@tonic-gate "snapshot_lnk_tbl", 1690Sstevel@tonic-gate "lnk_id INTEGER PRIMARY KEY," 1700Sstevel@tonic-gate "lnk_inst_id INTEGER NOT NULL," 1710Sstevel@tonic-gate "lnk_snap_name CHAR(256) NOT NULL," 1720Sstevel@tonic-gate "lnk_snap_id INTEGER NOT NULL" 1730Sstevel@tonic-gate }, 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate /* 1760Sstevel@tonic-gate * snaplevel_tbl maps a snapshot id to a set of named, ordered 1770Sstevel@tonic-gate * snaplevels. 1780Sstevel@tonic-gate */ 1790Sstevel@tonic-gate { 1800Sstevel@tonic-gate "snaplevel_tbl", 1810Sstevel@tonic-gate "snap_id INTEGER NOT NULL," 1820Sstevel@tonic-gate "snap_level_num INTEGER NOT NULL," 1830Sstevel@tonic-gate "snap_level_id INTEGER NOT NULL," 1840Sstevel@tonic-gate "snap_level_service_id INTEGER NOT NULL," 1850Sstevel@tonic-gate "snap_level_service CHAR(256) NOT NULL," 1860Sstevel@tonic-gate "snap_level_instance_id INTEGER NULL," 1870Sstevel@tonic-gate "snap_level_instance CHAR(256) NULL" 1880Sstevel@tonic-gate }, 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate /* 1910Sstevel@tonic-gate * snaplevel_lnk_tbl links snaplevels to property groups. 1920Sstevel@tonic-gate * snaplvl_pg_* is identical to the original property group, 1930Sstevel@tonic-gate * and snaplvl_gen_id overrides the generation number. 1940Sstevel@tonic-gate * The service/instance ids are as in the snaplevel. 1950Sstevel@tonic-gate */ 1960Sstevel@tonic-gate { 1970Sstevel@tonic-gate "snaplevel_lnk_tbl", 1980Sstevel@tonic-gate "snaplvl_level_id INTEGER NOT NULL," 1990Sstevel@tonic-gate "snaplvl_pg_id INTEGER NOT NULL," 2000Sstevel@tonic-gate "snaplvl_pg_name CHAR(256) NOT NULL," 2010Sstevel@tonic-gate "snaplvl_pg_type CHAR(256) NOT NULL," 2020Sstevel@tonic-gate "snaplvl_pg_flags INTEGER NOT NULL," 2030Sstevel@tonic-gate "snaplvl_gen_id INTEGER NOT NULL" 2040Sstevel@tonic-gate }, 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate { NULL, NULL } 2070Sstevel@tonic-gate }; 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate static struct backend_idx_info idxs_normal[] = { /* BACKEND_TYPE_NORMAL */ 2100Sstevel@tonic-gate { "service_tbl", "name", "svc_name" }, 2110Sstevel@tonic-gate { "instance_tbl", "name", "instance_svc, instance_name" }, 2120Sstevel@tonic-gate { "snapshot_lnk_tbl", "name", "lnk_inst_id, lnk_snap_name" }, 2130Sstevel@tonic-gate { "snapshot_lnk_tbl", "snapid", "lnk_snap_id" }, 2140Sstevel@tonic-gate { "snaplevel_tbl", "id", "snap_id" }, 2150Sstevel@tonic-gate { "snaplevel_lnk_tbl", "id", "snaplvl_pg_id" }, 2160Sstevel@tonic-gate { "snaplevel_lnk_tbl", "level", "snaplvl_level_id" }, 2170Sstevel@tonic-gate { NULL, NULL, NULL } 2180Sstevel@tonic-gate }; 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate static struct backend_tbl_info tbls_np[] = { /* BACKEND_TYPE_NONPERSIST */ 2210Sstevel@tonic-gate { NULL, NULL } 2220Sstevel@tonic-gate }; 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate static struct backend_idx_info idxs_np[] = { /* BACKEND_TYPE_NONPERSIST */ 2250Sstevel@tonic-gate { NULL, NULL, NULL } 2260Sstevel@tonic-gate }; 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate static struct backend_tbl_info tbls_common[] = { /* all backend types */ 2290Sstevel@tonic-gate /* 2300Sstevel@tonic-gate * pg_tbl defines property groups. They are associated with a single 2310Sstevel@tonic-gate * service or instance. The pg_gen_id links them with the latest 2320Sstevel@tonic-gate * "edited" version of its properties. 2330Sstevel@tonic-gate */ 2340Sstevel@tonic-gate { 2350Sstevel@tonic-gate "pg_tbl", 2360Sstevel@tonic-gate "pg_id INTEGER PRIMARY KEY," 2370Sstevel@tonic-gate "pg_parent_id INTEGER NOT NULL," 2380Sstevel@tonic-gate "pg_name CHAR(256) NOT NULL," 2390Sstevel@tonic-gate "pg_type CHAR(256) NOT NULL," 2400Sstevel@tonic-gate "pg_flags INTEGER NOT NULL," 2410Sstevel@tonic-gate "pg_gen_id INTEGER NOT NULL" 2420Sstevel@tonic-gate }, 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate /* 2450Sstevel@tonic-gate * prop_lnk_tbl links a particular pg_id and gen_id to a set of 2460Sstevel@tonic-gate * (prop_name, prop_type, val_id) trios. 2470Sstevel@tonic-gate */ 2480Sstevel@tonic-gate { 2490Sstevel@tonic-gate "prop_lnk_tbl", 2500Sstevel@tonic-gate "lnk_prop_id INTEGER PRIMARY KEY," 2510Sstevel@tonic-gate "lnk_pg_id INTEGER NOT NULL," 2520Sstevel@tonic-gate "lnk_gen_id INTEGER NOT NULL," 2530Sstevel@tonic-gate "lnk_prop_name CHAR(256) NOT NULL," 2540Sstevel@tonic-gate "lnk_prop_type CHAR(2) NOT NULL," 2550Sstevel@tonic-gate "lnk_val_id INTEGER" 2560Sstevel@tonic-gate }, 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate /* 2590Sstevel@tonic-gate * value_tbl maps a value_id to a set of values. For any given 2600Sstevel@tonic-gate * value_id, value_type is constant. 2610Sstevel@tonic-gate */ 2620Sstevel@tonic-gate { 2630Sstevel@tonic-gate "value_tbl", 2640Sstevel@tonic-gate "value_id INTEGER NOT NULL," 2650Sstevel@tonic-gate "value_type CHAR(1) NOT NULL," 2660Sstevel@tonic-gate "value_value VARCHAR NOT NULL" 2670Sstevel@tonic-gate }, 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate /* 2700Sstevel@tonic-gate * id_tbl has one row per id space 2710Sstevel@tonic-gate */ 2720Sstevel@tonic-gate { 2730Sstevel@tonic-gate "id_tbl", 2740Sstevel@tonic-gate "id_name STRING NOT NULL," 2750Sstevel@tonic-gate "id_next INTEGER NOT NULL" 2760Sstevel@tonic-gate }, 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * schema_version has a single row, which contains 2800Sstevel@tonic-gate * BACKEND_SCHEMA_VERSION at the time of creation. 2810Sstevel@tonic-gate */ 2820Sstevel@tonic-gate { 2830Sstevel@tonic-gate "schema_version", 2840Sstevel@tonic-gate "schema_version INTEGER" 2850Sstevel@tonic-gate }, 2860Sstevel@tonic-gate { NULL, NULL } 2870Sstevel@tonic-gate }; 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate static struct backend_idx_info idxs_common[] = { /* all backend types */ 2900Sstevel@tonic-gate { "pg_tbl", "parent", "pg_parent_id" }, 2910Sstevel@tonic-gate { "pg_tbl", "name", "pg_parent_id, pg_name" }, 2920Sstevel@tonic-gate { "pg_tbl", "type", "pg_parent_id, pg_type" }, 2930Sstevel@tonic-gate { "prop_lnk_tbl", "base", "lnk_pg_id, lnk_gen_id" }, 2940Sstevel@tonic-gate { "prop_lnk_tbl", "val", "lnk_val_id" }, 2950Sstevel@tonic-gate { "value_tbl", "id", "value_id" }, 2960Sstevel@tonic-gate { "id_tbl", "id", "id_name" }, 2970Sstevel@tonic-gate { NULL, NULL, NULL } 2980Sstevel@tonic-gate }; 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate struct run_single_int_info { 3010Sstevel@tonic-gate uint32_t *rs_out; 3020Sstevel@tonic-gate int rs_result; 3030Sstevel@tonic-gate }; 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /*ARGSUSED*/ 3060Sstevel@tonic-gate static int 3070Sstevel@tonic-gate run_single_int_callback(void *arg, int columns, char **vals, char **names) 3080Sstevel@tonic-gate { 3090Sstevel@tonic-gate struct run_single_int_info *info = arg; 3100Sstevel@tonic-gate uint32_t val; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate char *endptr = vals[0]; 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate assert(info->rs_result != REP_PROTOCOL_SUCCESS); 3150Sstevel@tonic-gate assert(columns == 1); 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate if (vals[0] == NULL) 3180Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate errno = 0; 3210Sstevel@tonic-gate val = strtoul(vals[0], &endptr, 10); 3220Sstevel@tonic-gate if ((val == 0 && endptr == vals[0]) || *endptr != 0 || errno != 0) 3230Sstevel@tonic-gate backend_panic("malformed integer \"%20s\"", vals[0]); 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate *info->rs_out = val; 3260Sstevel@tonic-gate info->rs_result = REP_PROTOCOL_SUCCESS; 3270Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate /*ARGSUSED*/ 3310Sstevel@tonic-gate int 3320Sstevel@tonic-gate backend_fail_if_seen(void *arg, int columns, char **vals, char **names) 3330Sstevel@tonic-gate { 3340Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate 337407Sjwadams /* 338407Sjwadams * check to see if we can successfully start a transaction; if not, the 339407Sjwadams * filesystem is mounted read-only. 340407Sjwadams */ 3410Sstevel@tonic-gate static int 342407Sjwadams backend_is_readonly(struct sqlite *db, const char *path) 3430Sstevel@tonic-gate { 344407Sjwadams int r; 345407Sjwadams statvfs64_t stat; 346407Sjwadams 347407Sjwadams if (statvfs64(path, &stat) == 0 && (stat.f_flag & ST_RDONLY)) 348407Sjwadams return (SQLITE_READONLY); 349407Sjwadams 350407Sjwadams r = sqlite_exec(db, 3510Sstevel@tonic-gate "BEGIN TRANSACTION; " 3520Sstevel@tonic-gate "UPDATE schema_version SET schema_version = schema_version; ", 353407Sjwadams NULL, NULL, NULL); 3540Sstevel@tonic-gate (void) sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); 3550Sstevel@tonic-gate return (r); 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate static void 3590Sstevel@tonic-gate backend_trace_sql(void *arg, const char *sql) 3600Sstevel@tonic-gate { 3610Sstevel@tonic-gate sqlite_backend_t *be = arg; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate if (backend_print_trace) { 3640Sstevel@tonic-gate (void) fprintf(stderr, "%d: %s\n", be->be_type, sql); 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate static sqlite_backend_t be_info[BACKEND_TYPE_TOTAL]; 3690Sstevel@tonic-gate static sqlite_backend_t *bes[BACKEND_TYPE_TOTAL]; 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate #define BACKEND_PANIC_TIMEOUT (50 * MILLISEC) 3720Sstevel@tonic-gate /* 3730Sstevel@tonic-gate * backend_panic() -- some kind of database problem or corruption has been hit. 3740Sstevel@tonic-gate * We attempt to quiesce the other database users -- all of the backend sql 3750Sstevel@tonic-gate * entry points will call backend_panic(NULL) if a panic is in progress, as 3760Sstevel@tonic-gate * will any attempt to start a transaction. 3770Sstevel@tonic-gate * 3780Sstevel@tonic-gate * We give threads holding a backend lock 50ms (BACKEND_PANIC_TIMEOUT) to 3790Sstevel@tonic-gate * either drop the lock or call backend_panic(). If they don't respond in 3800Sstevel@tonic-gate * time, we'll just exit anyway. 3810Sstevel@tonic-gate */ 3820Sstevel@tonic-gate void 3830Sstevel@tonic-gate backend_panic(const char *format, ...) 3840Sstevel@tonic-gate { 3850Sstevel@tonic-gate int i; 3860Sstevel@tonic-gate va_list args; 3870Sstevel@tonic-gate int failed = 0; 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 3900Sstevel@tonic-gate if (backend_panic_thread != 0) { 3910Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 3920Sstevel@tonic-gate /* 3930Sstevel@tonic-gate * first, drop any backend locks we're holding, then 3940Sstevel@tonic-gate * sleep forever on the panic_cv. 3950Sstevel@tonic-gate */ 3960Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 3970Sstevel@tonic-gate if (bes[i] != NULL && 3980Sstevel@tonic-gate bes[i]->be_thread == pthread_self()) 3990Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 4000Sstevel@tonic-gate } 4010Sstevel@tonic-gate (void) pthread_mutex_lock(&backend_panic_lock); 4020Sstevel@tonic-gate for (;;) 4030Sstevel@tonic-gate (void) pthread_cond_wait(&backend_panic_cv, 4040Sstevel@tonic-gate &backend_panic_lock); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate backend_panic_thread = pthread_self(); 4070Sstevel@tonic-gate (void) pthread_mutex_unlock(&backend_panic_lock); 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4100Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread == pthread_self()) 4110Sstevel@tonic-gate (void) pthread_mutex_unlock(&bes[i]->be_lock); 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate va_start(args, format); 4150Sstevel@tonic-gate configd_vcritical(format, args); 4160Sstevel@tonic-gate va_end(args); 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate for (i = 0; i < BACKEND_TYPE_TOTAL; i++) { 4190Sstevel@tonic-gate timespec_t rel; 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate rel.tv_sec = 0; 4220Sstevel@tonic-gate rel.tv_nsec = BACKEND_PANIC_TIMEOUT; 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate if (bes[i] != NULL && bes[i]->be_thread != pthread_self()) { 4250Sstevel@tonic-gate if (pthread_mutex_reltimedlock_np(&bes[i]->be_lock, 4260Sstevel@tonic-gate &rel) != 0) 4270Sstevel@tonic-gate failed++; 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate } 4300Sstevel@tonic-gate if (failed) { 4310Sstevel@tonic-gate configd_critical("unable to quiesce database\n"); 4320Sstevel@tonic-gate } 4330Sstevel@tonic-gate 4340Sstevel@tonic-gate if (backend_panic_abort) 4350Sstevel@tonic-gate abort(); 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate exit(CONFIGD_EXIT_DATABASE_BAD); 4380Sstevel@tonic-gate } 4390Sstevel@tonic-gate 4400Sstevel@tonic-gate /* 4410Sstevel@tonic-gate * Returns 4420Sstevel@tonic-gate * _SUCCESS 4430Sstevel@tonic-gate * _DONE - callback aborted query 4440Sstevel@tonic-gate * _NO_RESOURCES - out of memory (_FULL & _TOOBIG?) 4450Sstevel@tonic-gate */ 4460Sstevel@tonic-gate static int 4470Sstevel@tonic-gate backend_error(sqlite_backend_t *be, int error, char *errmsg) 4480Sstevel@tonic-gate { 4490Sstevel@tonic-gate if (error == SQLITE_OK) 4500Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate switch (error) { 4530Sstevel@tonic-gate case SQLITE_ABORT: 4540Sstevel@tonic-gate free(errmsg); 4550Sstevel@tonic-gate return (REP_PROTOCOL_DONE); 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate case SQLITE_NOMEM: 4580Sstevel@tonic-gate case SQLITE_FULL: 4590Sstevel@tonic-gate case SQLITE_TOOBIG: 4600Sstevel@tonic-gate free(errmsg); 4610Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate default: 4640Sstevel@tonic-gate backend_panic("%s: db error: %s", be->be_path, errmsg); 4650Sstevel@tonic-gate /*NOTREACHED*/ 4660Sstevel@tonic-gate } 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate static void 4700Sstevel@tonic-gate backend_backup_cleanup(const char **out_arg, ssize_t out_sz) 4710Sstevel@tonic-gate { 4720Sstevel@tonic-gate char **out = (char **)out_arg; 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate while (out_sz-- > 0) 4750Sstevel@tonic-gate free(*out++); 4760Sstevel@tonic-gate free(out_arg); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * builds a inverse-time-sorted array of backup files. The path is a 4810Sstevel@tonic-gate * a single buffer, and the pointers look like: 4820Sstevel@tonic-gate * 4830Sstevel@tonic-gate * /this/is/a/full/path/to/repository-name-YYYYMMDDHHMMSS 4840Sstevel@tonic-gate * ^pathname ^ ^(pathname+pathlen) 4850Sstevel@tonic-gate * basename 4860Sstevel@tonic-gate * 4870Sstevel@tonic-gate * dirname will either be pathname, or ".". 4880Sstevel@tonic-gate * 4890Sstevel@tonic-gate * Returns the number of elements in the array, 0 if there are no previous 4900Sstevel@tonic-gate * backups, or -1 on error. 4910Sstevel@tonic-gate */ 4920Sstevel@tonic-gate static ssize_t 4930Sstevel@tonic-gate backend_backup_get_prev(char *pathname, size_t pathlen, const char ***out_arg) 4940Sstevel@tonic-gate { 4950Sstevel@tonic-gate char b_start, b_end; 4960Sstevel@tonic-gate DIR *dir; 4970Sstevel@tonic-gate char **out = NULL; 4980Sstevel@tonic-gate char *name, *p; 4990Sstevel@tonic-gate char *dirname, *basename; 5000Sstevel@tonic-gate char *pathend; 5010Sstevel@tonic-gate struct dirent *ent; 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate size_t count = 0; 5040Sstevel@tonic-gate size_t baselen; 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate /* 5070Sstevel@tonic-gate * year, month, day, hour, min, sec, plus an '_'. 5080Sstevel@tonic-gate */ 5090Sstevel@tonic-gate const size_t ndigits = 4 + 5*2 + 1; 5100Sstevel@tonic-gate const size_t baroffset = 4 + 2*2; 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate size_t idx; 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate pathend = pathname + pathlen; 5150Sstevel@tonic-gate b_end = *pathend; 5160Sstevel@tonic-gate *pathend = '\0'; 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate basename = strrchr(pathname, '/'); 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate if (basename != NULL) { 5210Sstevel@tonic-gate assert(pathend > pathname && basename < pathend); 5220Sstevel@tonic-gate basename++; 5230Sstevel@tonic-gate dirname = pathname; 5240Sstevel@tonic-gate } else { 5250Sstevel@tonic-gate basename = pathname; 5260Sstevel@tonic-gate dirname = "."; 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate baselen = strlen(basename); 5300Sstevel@tonic-gate 5310Sstevel@tonic-gate /* 5320Sstevel@tonic-gate * munge the string temporarily for the opendir(), then restore it. 5330Sstevel@tonic-gate */ 5340Sstevel@tonic-gate b_start = basename[0]; 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate basename[0] = '\0'; 5370Sstevel@tonic-gate dir = opendir(dirname); 5380Sstevel@tonic-gate basename[0] = b_start; /* restore path */ 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate if (dir == NULL) 5410Sstevel@tonic-gate goto fail; 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate while ((ent = readdir(dir)) != NULL) { 5450Sstevel@tonic-gate /* 5460Sstevel@tonic-gate * Must match: 5470Sstevel@tonic-gate * basename-YYYYMMDD_HHMMSS 5480Sstevel@tonic-gate * or we ignore it. 5490Sstevel@tonic-gate */ 5500Sstevel@tonic-gate if (strncmp(ent->d_name, basename, baselen) != 0) 5510Sstevel@tonic-gate continue; 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate name = ent->d_name; 5540Sstevel@tonic-gate if (name[baselen] != '-') 5550Sstevel@tonic-gate continue; 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate p = name + baselen + 1; 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate for (idx = 0; idx < ndigits; idx++) { 5600Sstevel@tonic-gate char c = p[idx]; 5610Sstevel@tonic-gate if (idx == baroffset && c != '_') 5620Sstevel@tonic-gate break; 5630Sstevel@tonic-gate if (idx != baroffset && (c < '0' || c > '9')) 5640Sstevel@tonic-gate break; 5650Sstevel@tonic-gate } 5660Sstevel@tonic-gate if (idx != ndigits || p[idx] != '\0') 5670Sstevel@tonic-gate continue; 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate /* 5700Sstevel@tonic-gate * We have a match. insertion-sort it into our list. 5710Sstevel@tonic-gate */ 5720Sstevel@tonic-gate name = strdup(name); 5730Sstevel@tonic-gate if (name == NULL) 5740Sstevel@tonic-gate goto fail_closedir; 5750Sstevel@tonic-gate p = strrchr(name, '-'); 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate for (idx = 0; idx < count; idx++) { 5780Sstevel@tonic-gate char *tmp = out[idx]; 5790Sstevel@tonic-gate char *tp = strrchr(tmp, '-'); 5800Sstevel@tonic-gate 5810Sstevel@tonic-gate int cmp = strcmp(p, tp); 5820Sstevel@tonic-gate if (cmp == 0) 5830Sstevel@tonic-gate cmp = strcmp(name, tmp); 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate if (cmp == 0) { 5860Sstevel@tonic-gate free(name); 5870Sstevel@tonic-gate name = NULL; 5880Sstevel@tonic-gate break; 5890Sstevel@tonic-gate } else if (cmp > 0) { 5900Sstevel@tonic-gate out[idx] = name; 5910Sstevel@tonic-gate name = tmp; 5920Sstevel@tonic-gate p = tp; 5930Sstevel@tonic-gate } 5940Sstevel@tonic-gate } 5950Sstevel@tonic-gate 5960Sstevel@tonic-gate if (idx == count) { 5970Sstevel@tonic-gate char **new_out = realloc(out, 5980Sstevel@tonic-gate (count + 1) * sizeof (*out)); 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate if (new_out == NULL) { 6010Sstevel@tonic-gate free(name); 6020Sstevel@tonic-gate goto fail_closedir; 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate out = new_out; 6060Sstevel@tonic-gate out[count++] = name; 6070Sstevel@tonic-gate } else { 6080Sstevel@tonic-gate assert(name == NULL); 6090Sstevel@tonic-gate } 6100Sstevel@tonic-gate } 6110Sstevel@tonic-gate (void) closedir(dir); 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate basename[baselen] = b_end; 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate *out_arg = (const char **)out; 6160Sstevel@tonic-gate return (count); 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate fail_closedir: 6190Sstevel@tonic-gate (void) closedir(dir); 6200Sstevel@tonic-gate fail: 6210Sstevel@tonic-gate basename[0] = b_start; 6220Sstevel@tonic-gate *pathend = b_end; 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate backend_backup_cleanup((const char **)out, count); 6250Sstevel@tonic-gate 6260Sstevel@tonic-gate *out_arg = NULL; 6270Sstevel@tonic-gate return (-1); 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate /* 6310Sstevel@tonic-gate * Copies the repository path into out, a buffer of out_len bytes, 6320Sstevel@tonic-gate * removes the ".db" (or whatever) extension, and, if name is non-NULL, 6330Sstevel@tonic-gate * appends "-name" to it. If name is non-NULL, it can fail with: 6340Sstevel@tonic-gate * 6350Sstevel@tonic-gate * _TRUNCATED will not fit in buffer. 6360Sstevel@tonic-gate * _BAD_REQUEST name is not a valid identifier 6370Sstevel@tonic-gate */ 6380Sstevel@tonic-gate static rep_protocol_responseid_t 6390Sstevel@tonic-gate backend_backup_base(sqlite_backend_t *be, const char *name, 6400Sstevel@tonic-gate char *out, size_t out_len) 6410Sstevel@tonic-gate { 6420Sstevel@tonic-gate char *p, *q; 6430Sstevel@tonic-gate size_t len; 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate /* 6460Sstevel@tonic-gate * for paths of the form /path/to/foo.db, we truncate at the final 6470Sstevel@tonic-gate * '.'. 6480Sstevel@tonic-gate */ 6490Sstevel@tonic-gate (void) strlcpy(out, be->be_path, out_len); 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate p = strrchr(out, '/'); 6520Sstevel@tonic-gate q = strrchr(out, '.'); 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate if (p != NULL && q != NULL && q > p) 6550Sstevel@tonic-gate *q = 0; 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate if (name != NULL) { 6580Sstevel@tonic-gate len = strlen(out); 6590Sstevel@tonic-gate assert(len < out_len); 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate out += len; 6620Sstevel@tonic-gate out_len -= len; 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate len = strlen(name); 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate /* 6670Sstevel@tonic-gate * verify that the name tag is entirely alphabetic, 6680Sstevel@tonic-gate * non-empty, and not too long. 6690Sstevel@tonic-gate */ 6700Sstevel@tonic-gate if (len == 0 || len >= REP_PROTOCOL_NAME_LEN || 6710Sstevel@tonic-gate uu_check_name(name, UU_NAME_DOMAIN) < 0) 6720Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BAD_REQUEST); 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate if (snprintf(out, out_len, "-%s", name) >= out_len) 6750Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_TRUNCATED); 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 6790Sstevel@tonic-gate } 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate /* 682407Sjwadams * See if a backup is needed. We do a backup unless both files are 683407Sjwadams * byte-for-byte identical. 684407Sjwadams */ 685407Sjwadams static int 686407Sjwadams backend_check_backup_needed(const char *rep_name, const char *backup_name) 687407Sjwadams { 688407Sjwadams int repfd = open(rep_name, O_RDONLY); 689407Sjwadams int fd = open(backup_name, O_RDONLY); 690407Sjwadams struct stat s_rep, s_backup; 691407Sjwadams int c1, c2; 692407Sjwadams 693407Sjwadams FILE *f_rep = NULL; 694407Sjwadams FILE *f_backup = NULL; 695407Sjwadams 696407Sjwadams if (repfd < 0 || fd < 0) 697407Sjwadams goto fail; 698407Sjwadams 699407Sjwadams if (fstat(repfd, &s_rep) < 0 || fstat(fd, &s_backup) < 0) 700407Sjwadams goto fail; 701407Sjwadams 702407Sjwadams /* 703407Sjwadams * if they are the same file, we need to do a backup to break the 704407Sjwadams * hard link or symlink involved. 705407Sjwadams */ 706407Sjwadams if (s_rep.st_ino == s_backup.st_ino && s_rep.st_dev == s_backup.st_dev) 707407Sjwadams goto fail; 708407Sjwadams 709407Sjwadams if (s_rep.st_size != s_backup.st_size) 710407Sjwadams goto fail; 711407Sjwadams 712407Sjwadams if ((f_rep = fdopen(repfd, "r")) == NULL || 713407Sjwadams (f_backup = fdopen(fd, "r")) == NULL) 714407Sjwadams goto fail; 715407Sjwadams 716407Sjwadams do { 717407Sjwadams c1 = getc(f_rep); 718407Sjwadams c2 = getc(f_backup); 719407Sjwadams if (c1 != c2) 720407Sjwadams goto fail; 721407Sjwadams } while (c1 != EOF); 722407Sjwadams 723407Sjwadams if (!ferror(f_rep) && !ferror(f_backup)) { 724407Sjwadams (void) fclose(f_rep); 725407Sjwadams (void) fclose(f_backup); 726407Sjwadams (void) close(repfd); 727407Sjwadams (void) close(fd); 728407Sjwadams return (0); 729407Sjwadams } 730407Sjwadams 731407Sjwadams fail: 732407Sjwadams if (f_rep != NULL) 733407Sjwadams (void) fclose(f_rep); 734407Sjwadams if (f_backup != NULL) 735407Sjwadams (void) fclose(f_backup); 736407Sjwadams if (repfd >= 0) 737407Sjwadams (void) close(repfd); 738407Sjwadams if (fd >= 0) 739407Sjwadams (void) close(fd); 740407Sjwadams return (1); 741407Sjwadams } 742407Sjwadams 743407Sjwadams /* 744*6035Sstevep * This interface is called to perform the actual copy 745*6035Sstevep * 746*6035Sstevep * Return: 747*6035Sstevep * _FAIL_UNKNOWN read/write fails 748*6035Sstevep * _FAIL_NO_RESOURCES out of memory 749*6035Sstevep * _SUCCESS copy succeeds 750*6035Sstevep */ 751*6035Sstevep static rep_protocol_responseid_t 752*6035Sstevep backend_do_copy(const char *src, int srcfd, const char *dst, 753*6035Sstevep int dstfd, size_t *sz) 754*6035Sstevep { 755*6035Sstevep char *buf; 756*6035Sstevep off_t nrd, nwr, n, r_off = 0, w_off = 0; 757*6035Sstevep 758*6035Sstevep if ((buf = malloc(8192)) == NULL) 759*6035Sstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 760*6035Sstevep 761*6035Sstevep while ((nrd = read(srcfd, buf, 8192)) != 0) { 762*6035Sstevep if (nrd < 0) { 763*6035Sstevep if (errno == EINTR) 764*6035Sstevep continue; 765*6035Sstevep 766*6035Sstevep configd_critical( 767*6035Sstevep "Backend copy failed: fails to read from %s " 768*6035Sstevep "at offset %d: %s\n", src, r_off, strerror(errno)); 769*6035Sstevep free(buf); 770*6035Sstevep return (REP_PROTOCOL_FAIL_UNKNOWN); 771*6035Sstevep } 772*6035Sstevep 773*6035Sstevep r_off += nrd; 774*6035Sstevep 775*6035Sstevep nwr = 0; 776*6035Sstevep do { 777*6035Sstevep if ((n = write(dstfd, &buf[nwr], nrd - nwr)) < 0) { 778*6035Sstevep if (errno == EINTR) 779*6035Sstevep continue; 780*6035Sstevep 781*6035Sstevep configd_critical( 782*6035Sstevep "Backend copy failed: fails to write to %s " 783*6035Sstevep "at offset %d: %s\n", dst, w_off, 784*6035Sstevep strerror(errno)); 785*6035Sstevep free(buf); 786*6035Sstevep return (REP_PROTOCOL_FAIL_UNKNOWN); 787*6035Sstevep } 788*6035Sstevep 789*6035Sstevep nwr += n; 790*6035Sstevep w_off += n; 791*6035Sstevep 792*6035Sstevep } while (nwr < nrd); 793*6035Sstevep } 794*6035Sstevep 795*6035Sstevep if (sz) 796*6035Sstevep *sz = w_off; 797*6035Sstevep 798*6035Sstevep free(buf); 799*6035Sstevep return (REP_PROTOCOL_SUCCESS); 800*6035Sstevep } 801*6035Sstevep 802*6035Sstevep /* 8030Sstevel@tonic-gate * Can return: 8040Sstevel@tonic-gate * _BAD_REQUEST name is not valid 8050Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 8060Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 8070Sstevel@tonic-gate * console) 8080Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 809*6035Sstevep * _NO_RESOURCES out of memory 8100Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 8110Sstevel@tonic-gate */ 8120Sstevel@tonic-gate static rep_protocol_responseid_t 8130Sstevel@tonic-gate backend_create_backup_locked(sqlite_backend_t *be, const char *name) 8140Sstevel@tonic-gate { 8150Sstevel@tonic-gate const char **old_list; 8160Sstevel@tonic-gate ssize_t old_sz; 8170Sstevel@tonic-gate ssize_t old_max = max_repository_backups; 8180Sstevel@tonic-gate ssize_t cur; 8190Sstevel@tonic-gate char *finalname; 820*6035Sstevep char *finalpath; 821*6035Sstevep char *tmppath; 8220Sstevel@tonic-gate int infd, outfd; 8230Sstevel@tonic-gate size_t len; 8240Sstevel@tonic-gate time_t now; 8250Sstevel@tonic-gate struct tm now_tm; 8260Sstevel@tonic-gate rep_protocol_responseid_t result; 8270Sstevel@tonic-gate 828*6035Sstevep if ((finalpath = malloc(PATH_MAX)) == NULL) 829*6035Sstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 830*6035Sstevep 831*6035Sstevep if ((tmppath = malloc(PATH_MAX)) == NULL) { 832*6035Sstevep free(finalpath); 833*6035Sstevep return (REP_PROTOCOL_FAIL_NO_RESOURCES); 834*6035Sstevep } 8350Sstevel@tonic-gate 836*6035Sstevep if (be->be_readonly) { 837*6035Sstevep result = REP_PROTOCOL_FAIL_BACKEND_READONLY; 838*6035Sstevep goto out; 839*6035Sstevep } 840*6035Sstevep 841*6035Sstevep result = backend_backup_base(be, name, finalpath, PATH_MAX); 8420Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 843*6035Sstevep goto out; 8440Sstevel@tonic-gate 845407Sjwadams if (!backend_check_backup_needed(be->be_path, finalpath)) { 846*6035Sstevep result = REP_PROTOCOL_SUCCESS; 847*6035Sstevep goto out; 848407Sjwadams } 849407Sjwadams 8500Sstevel@tonic-gate /* 8510Sstevel@tonic-gate * remember the original length, and the basename location 8520Sstevel@tonic-gate */ 8530Sstevel@tonic-gate len = strlen(finalpath); 8540Sstevel@tonic-gate finalname = strrchr(finalpath, '/'); 8550Sstevel@tonic-gate if (finalname != NULL) 8560Sstevel@tonic-gate finalname++; 8570Sstevel@tonic-gate else 8580Sstevel@tonic-gate finalname = finalpath; 8590Sstevel@tonic-gate 860*6035Sstevep (void) strlcpy(tmppath, finalpath, PATH_MAX); 861*6035Sstevep if (strlcat(tmppath, "-tmpXXXXXX", PATH_MAX) >= PATH_MAX) { 862*6035Sstevep result = REP_PROTOCOL_FAIL_TRUNCATED; 863*6035Sstevep goto out; 864*6035Sstevep } 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate now = time(NULL); 8670Sstevel@tonic-gate if (localtime_r(&now, &now_tm) == NULL) { 8680Sstevel@tonic-gate configd_critical( 8690Sstevel@tonic-gate "\"%s\" backup failed: localtime(3C) failed: %s\n", name, 8700Sstevel@tonic-gate be->be_path, strerror(errno)); 871*6035Sstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 872*6035Sstevep goto out; 8730Sstevel@tonic-gate } 8740Sstevel@tonic-gate 875*6035Sstevep if (strftime(finalpath + len, PATH_MAX - len, 876*6035Sstevep "-%Y""%m""%d""_""%H""%M""%S", &now_tm) >= PATH_MAX - len) { 877*6035Sstevep result = REP_PROTOCOL_FAIL_TRUNCATED; 878*6035Sstevep goto out; 8790Sstevel@tonic-gate } 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate infd = open(be->be_path, O_RDONLY); 8820Sstevel@tonic-gate if (infd < 0) { 8830Sstevel@tonic-gate configd_critical("\"%s\" backup failed: opening %s: %s\n", name, 8840Sstevel@tonic-gate be->be_path, strerror(errno)); 885*6035Sstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 886*6035Sstevep goto out; 8870Sstevel@tonic-gate } 8880Sstevel@tonic-gate 8890Sstevel@tonic-gate outfd = mkstemp(tmppath); 8900Sstevel@tonic-gate if (outfd < 0) { 8910Sstevel@tonic-gate configd_critical("\"%s\" backup failed: mkstemp(%s): %s\n", 8920Sstevel@tonic-gate name, tmppath, strerror(errno)); 8930Sstevel@tonic-gate (void) close(infd); 894*6035Sstevep result = REP_PROTOCOL_FAIL_UNKNOWN; 895*6035Sstevep goto out; 8960Sstevel@tonic-gate } 8970Sstevel@tonic-gate 898*6035Sstevep if ((result = backend_do_copy((const char *)be->be_path, infd, 899*6035Sstevep (const char *)tmppath, outfd, NULL)) != REP_PROTOCOL_SUCCESS) 9000Sstevel@tonic-gate goto fail; 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate /* 9030Sstevel@tonic-gate * grab the old list before doing our re-name. 9040Sstevel@tonic-gate */ 9050Sstevel@tonic-gate if (old_max > 0) 9060Sstevel@tonic-gate old_sz = backend_backup_get_prev(finalpath, len, &old_list); 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate if (rename(tmppath, finalpath) < 0) { 9090Sstevel@tonic-gate configd_critical( 9100Sstevel@tonic-gate "\"%s\" backup failed: rename(%s, %s): %s\n", 9110Sstevel@tonic-gate name, tmppath, finalpath, strerror(errno)); 9120Sstevel@tonic-gate result = REP_PROTOCOL_FAIL_UNKNOWN; 9130Sstevel@tonic-gate goto fail; 9140Sstevel@tonic-gate } 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate tmppath[len] = 0; /* strip -XXXXXX, for reference symlink */ 9170Sstevel@tonic-gate 9180Sstevel@tonic-gate (void) unlink(tmppath); 9190Sstevel@tonic-gate if (symlink(finalname, tmppath) < 0) { 9200Sstevel@tonic-gate configd_critical( 9210Sstevel@tonic-gate "\"%s\" backup completed, but updating " 9220Sstevel@tonic-gate "\"%s\" symlink to \"%s\" failed: %s\n", 9230Sstevel@tonic-gate name, tmppath, finalname, strerror(errno)); 9240Sstevel@tonic-gate } 9250Sstevel@tonic-gate 9260Sstevel@tonic-gate if (old_max > 0 && old_sz > 0) { 9270Sstevel@tonic-gate /* unlink all but the first (old_max - 1) files */ 9280Sstevel@tonic-gate for (cur = old_max - 1; cur < old_sz; cur++) { 9290Sstevel@tonic-gate (void) strlcpy(finalname, old_list[cur], 930*6035Sstevep PATH_MAX - (finalname - finalpath)); 9310Sstevel@tonic-gate if (unlink(finalpath) < 0) 9320Sstevel@tonic-gate configd_critical( 9330Sstevel@tonic-gate "\"%s\" backup completed, but removing old " 9340Sstevel@tonic-gate "file \"%s\" failed: %s\n", 9350Sstevel@tonic-gate name, finalpath, strerror(errno)); 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate 9380Sstevel@tonic-gate backend_backup_cleanup(old_list, old_sz); 9390Sstevel@tonic-gate } 9400Sstevel@tonic-gate 9410Sstevel@tonic-gate result = REP_PROTOCOL_SUCCESS; 9420Sstevel@tonic-gate 9430Sstevel@tonic-gate fail: 9440Sstevel@tonic-gate (void) close(infd); 9450Sstevel@tonic-gate (void) close(outfd); 9460Sstevel@tonic-gate if (result != REP_PROTOCOL_SUCCESS) 9470Sstevel@tonic-gate (void) unlink(tmppath); 9480Sstevel@tonic-gate 949*6035Sstevep out: 950*6035Sstevep free(finalpath); 951*6035Sstevep free(tmppath); 952*6035Sstevep 9530Sstevel@tonic-gate return (result); 9540Sstevel@tonic-gate } 9550Sstevel@tonic-gate 956407Sjwadams static int 957407Sjwadams backend_check_readonly(sqlite_backend_t *be, int writing, hrtime_t t) 958407Sjwadams { 959407Sjwadams char *errp; 960407Sjwadams struct sqlite *new; 961407Sjwadams int r; 962407Sjwadams 963407Sjwadams assert(be->be_readonly); 964407Sjwadams assert(be == bes[BACKEND_TYPE_NORMAL]); 965407Sjwadams 966407Sjwadams /* 967407Sjwadams * If we don't *need* to be writable, only check every once in a 968407Sjwadams * while. 969407Sjwadams */ 970407Sjwadams if (!writing) { 971407Sjwadams if ((uint64_t)(t - be->be_lastcheck) < 972407Sjwadams BACKEND_READONLY_CHECK_INTERVAL) 973407Sjwadams return (REP_PROTOCOL_SUCCESS); 974407Sjwadams be->be_lastcheck = t; 975407Sjwadams } 976407Sjwadams 977407Sjwadams new = sqlite_open(be->be_path, 0600, &errp); 978407Sjwadams if (new == NULL) { 979407Sjwadams backend_panic("reopening %s: %s\n", be->be_path, errp); 980407Sjwadams /*NOTREACHED*/ 981407Sjwadams } 982407Sjwadams r = backend_is_readonly(new, be->be_path); 983407Sjwadams 984407Sjwadams if (r != SQLITE_OK) { 985407Sjwadams sqlite_close(new); 986407Sjwadams if (writing) 987407Sjwadams return (REP_PROTOCOL_FAIL_BACKEND_READONLY); 988407Sjwadams return (REP_PROTOCOL_SUCCESS); 989407Sjwadams } 990407Sjwadams 991407Sjwadams /* 992407Sjwadams * We can write! Swap the db handles, mark ourself writable, 993407Sjwadams * and make a backup. 994407Sjwadams */ 995407Sjwadams sqlite_close(be->be_db); 996407Sjwadams be->be_db = new; 997407Sjwadams be->be_readonly = 0; 998407Sjwadams 999407Sjwadams if (backend_create_backup_locked(be, REPOSITORY_BOOT_BACKUP) != 1000407Sjwadams REP_PROTOCOL_SUCCESS) { 1001407Sjwadams configd_critical( 1002407Sjwadams "unable to create \"%s\" backup of \"%s\"\n", 1003407Sjwadams REPOSITORY_BOOT_BACKUP, be->be_path); 1004407Sjwadams } 1005407Sjwadams 1006407Sjwadams return (REP_PROTOCOL_SUCCESS); 1007407Sjwadams } 10080Sstevel@tonic-gate 10090Sstevel@tonic-gate /* 10100Sstevel@tonic-gate * If t is not BACKEND_TYPE_NORMAL, can fail with 10110Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 10120Sstevel@tonic-gate * 10130Sstevel@tonic-gate * If writing is nonzero, can also fail with 10140Sstevel@tonic-gate * _BACKEND_READONLY - backend is read-only 10150Sstevel@tonic-gate */ 10160Sstevel@tonic-gate static int 10170Sstevel@tonic-gate backend_lock(backend_type_t t, int writing, sqlite_backend_t **bep) 10180Sstevel@tonic-gate { 10190Sstevel@tonic-gate sqlite_backend_t *be = NULL; 10200Sstevel@tonic-gate hrtime_t ts, vts; 10210Sstevel@tonic-gate 10220Sstevel@tonic-gate *bep = NULL; 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || 10250Sstevel@tonic-gate t == BACKEND_TYPE_NONPERSIST); 10260Sstevel@tonic-gate 10270Sstevel@tonic-gate be = bes[t]; 10280Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) 10290Sstevel@tonic-gate assert(be != NULL); /* should always be there */ 10300Sstevel@tonic-gate 10310Sstevel@tonic-gate if (be == NULL) 10320Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_BACKEND_ACCESS); 10330Sstevel@tonic-gate 10340Sstevel@tonic-gate if (backend_panic_thread != 0) 10350Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 10360Sstevel@tonic-gate 10370Sstevel@tonic-gate ts = gethrtime(); 10380Sstevel@tonic-gate vts = gethrvtime(); 10390Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 10400Sstevel@tonic-gate UPDATE_TOTALS_WR(be, writing, bt_lock, ts, vts); 10410Sstevel@tonic-gate 10420Sstevel@tonic-gate if (backend_panic_thread != 0) { 10430Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10440Sstevel@tonic-gate backend_panic(NULL); /* don't proceed */ 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate be->be_thread = pthread_self(); 10470Sstevel@tonic-gate 1048407Sjwadams if (be->be_readonly) { 10490Sstevel@tonic-gate int r; 10500Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL); 10510Sstevel@tonic-gate 1052407Sjwadams r = backend_check_readonly(be, writing, ts); 1053407Sjwadams if (r != REP_PROTOCOL_SUCCESS) { 10540Sstevel@tonic-gate be->be_thread = 0; 10550Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 1056407Sjwadams return (r); 10570Sstevel@tonic-gate } 1058407Sjwadams } 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate if (backend_do_trace) 10610Sstevel@tonic-gate (void) sqlite_trace(be->be_db, backend_trace_sql, be); 10620Sstevel@tonic-gate else 10630Sstevel@tonic-gate (void) sqlite_trace(be->be_db, NULL, NULL); 10640Sstevel@tonic-gate 10650Sstevel@tonic-gate be->be_writing = writing; 10660Sstevel@tonic-gate *bep = be; 10670Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 10680Sstevel@tonic-gate } 10690Sstevel@tonic-gate 10700Sstevel@tonic-gate static void 10710Sstevel@tonic-gate backend_unlock(sqlite_backend_t *be) 10720Sstevel@tonic-gate { 10730Sstevel@tonic-gate be->be_writing = 0; 10740Sstevel@tonic-gate be->be_thread = 0; 10750Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10760Sstevel@tonic-gate } 10770Sstevel@tonic-gate 10780Sstevel@tonic-gate static void 10790Sstevel@tonic-gate backend_destroy(sqlite_backend_t *be) 10800Sstevel@tonic-gate { 10810Sstevel@tonic-gate if (be->be_db != NULL) { 10820Sstevel@tonic-gate sqlite_close(be->be_db); 10830Sstevel@tonic-gate be->be_db = NULL; 10840Sstevel@tonic-gate } 10850Sstevel@tonic-gate be->be_thread = 0; 10860Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10870Sstevel@tonic-gate (void) pthread_mutex_destroy(&be->be_lock); 10880Sstevel@tonic-gate } 10890Sstevel@tonic-gate 10900Sstevel@tonic-gate static void 10910Sstevel@tonic-gate backend_create_finish(backend_type_t backend_id, sqlite_backend_t *be) 10920Sstevel@tonic-gate { 10930Sstevel@tonic-gate assert(MUTEX_HELD(&be->be_lock)); 10940Sstevel@tonic-gate assert(be == &be_info[backend_id]); 10950Sstevel@tonic-gate 10960Sstevel@tonic-gate bes[backend_id] = be; 10970Sstevel@tonic-gate (void) pthread_mutex_unlock(&be->be_lock); 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate static int 11010Sstevel@tonic-gate backend_fd_write(int fd, const char *mess) 11020Sstevel@tonic-gate { 11030Sstevel@tonic-gate int len = strlen(mess); 11040Sstevel@tonic-gate int written; 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate while (len > 0) { 11070Sstevel@tonic-gate if ((written = write(fd, mess, len)) < 0) 11080Sstevel@tonic-gate return (-1); 11090Sstevel@tonic-gate mess += written; 11100Sstevel@tonic-gate len -= written; 11110Sstevel@tonic-gate } 11120Sstevel@tonic-gate return (0); 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate /* 11160Sstevel@tonic-gate * Can return: 11170Sstevel@tonic-gate * _BAD_REQUEST name is not valid 11180Sstevel@tonic-gate * _TRUNCATED name is too long for current repository path 11190Sstevel@tonic-gate * _UNKNOWN failed for unknown reason (details written to 11200Sstevel@tonic-gate * console) 11210Sstevel@tonic-gate * _BACKEND_READONLY backend is not writable 1122*6035Sstevep * _NO_RESOURCES out of memory 11230Sstevel@tonic-gate * _SUCCESS Backup completed successfully. 11240Sstevel@tonic-gate */ 11250Sstevel@tonic-gate rep_protocol_responseid_t 11260Sstevel@tonic-gate backend_create_backup(const char *name) 11270Sstevel@tonic-gate { 11280Sstevel@tonic-gate rep_protocol_responseid_t result; 11290Sstevel@tonic-gate sqlite_backend_t *be; 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate result = backend_lock(BACKEND_TYPE_NORMAL, 0, &be); 1132*6035Sstevep assert(result == REP_PROTOCOL_SUCCESS); 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate result = backend_create_backup_locked(be, name); 11350Sstevel@tonic-gate backend_unlock(be); 11360Sstevel@tonic-gate 11370Sstevel@tonic-gate return (result); 11380Sstevel@tonic-gate } 11390Sstevel@tonic-gate 1140*6035Sstevep /* 1141*6035Sstevep * Copy the repository. If the sw_back flag is not set, we are 1142*6035Sstevep * copying the repository from the default location under /etc/svc to 1143*6035Sstevep * the tmpfs /etc/svc/volatile location. If the flag is set, we are 1144*6035Sstevep * copying back to the /etc/svc location from the volatile location 1145*6035Sstevep * after manifest-import is completed. 1146*6035Sstevep * 1147*6035Sstevep * Can return: 1148*6035Sstevep * 1149*6035Sstevep * REP_PROTOCOL_SUCCESS successful copy and rename 1150*6035Sstevep * REP_PROTOCOL_FAIL_UNKNOWN file operation error 1151*6035Sstevep * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory 1152*6035Sstevep */ 1153*6035Sstevep static rep_protocol_responseid_t 1154*6035Sstevep backend_switch_copy(const char *src, const char *dst, int sw_back) 1155*6035Sstevep { 1156*6035Sstevep int srcfd, dstfd; 1157*6035Sstevep char *tmppath = malloc(PATH_MAX); 1158*6035Sstevep rep_protocol_responseid_t res = REP_PROTOCOL_SUCCESS; 1159*6035Sstevep struct stat s_buf; 1160*6035Sstevep size_t cpsz, sz; 1161*6035Sstevep 1162*6035Sstevep if (tmppath == NULL) { 1163*6035Sstevep res = REP_PROTOCOL_FAIL_NO_RESOURCES; 1164*6035Sstevep goto out; 1165*6035Sstevep } 1166*6035Sstevep 1167*6035Sstevep /* 1168*6035Sstevep * Create and open the related db files 1169*6035Sstevep */ 1170*6035Sstevep (void) strlcpy(tmppath, dst, PATH_MAX); 1171*6035Sstevep sz = strlcat(tmppath, "-XXXXXX", PATH_MAX); 1172*6035Sstevep assert(sz < PATH_MAX); 1173*6035Sstevep if (sz >= PATH_MAX) { 1174*6035Sstevep configd_critical( 1175*6035Sstevep "Backend copy failed: strlcat %s: overflow\n", tmppath); 1176*6035Sstevep abort(); 1177*6035Sstevep } 1178*6035Sstevep 1179*6035Sstevep if ((dstfd = mkstemp(tmppath)) < 0) { 1180*6035Sstevep configd_critical("Backend copy failed: mkstemp %s: %s\n", 1181*6035Sstevep tmppath, strerror(errno)); 1182*6035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1183*6035Sstevep goto out; 1184*6035Sstevep } 1185*6035Sstevep 1186*6035Sstevep if ((srcfd = open(src, O_RDONLY)) < 0) { 1187*6035Sstevep configd_critical("Backend copy failed: opening %s: %s\n", 1188*6035Sstevep src, strerror(errno)); 1189*6035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1190*6035Sstevep goto errexit; 1191*6035Sstevep } 1192*6035Sstevep 1193*6035Sstevep /* 1194*6035Sstevep * fstat the backend before copy for sanity check. 1195*6035Sstevep */ 1196*6035Sstevep if (fstat(srcfd, &s_buf) < 0) { 1197*6035Sstevep configd_critical("Backend copy failed: fstat %s: %s\n", 1198*6035Sstevep src, strerror(errno)); 1199*6035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1200*6035Sstevep goto errexit; 1201*6035Sstevep } 1202*6035Sstevep 1203*6035Sstevep if ((res = backend_do_copy(src, srcfd, dst, dstfd, &cpsz)) != 1204*6035Sstevep REP_PROTOCOL_SUCCESS) 1205*6035Sstevep goto errexit; 1206*6035Sstevep 1207*6035Sstevep if (cpsz != s_buf.st_size) { 1208*6035Sstevep configd_critical("Backend copy failed: incomplete copy\n"); 1209*6035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1210*6035Sstevep goto errexit; 1211*6035Sstevep } 1212*6035Sstevep 1213*6035Sstevep /* 1214*6035Sstevep * Rename tmppath to dst 1215*6035Sstevep */ 1216*6035Sstevep if (rename(tmppath, dst) < 0) { 1217*6035Sstevep configd_critical( 1218*6035Sstevep "Backend copy failed: rename %s to %s: %s\n", 1219*6035Sstevep tmppath, dst, strerror(errno)); 1220*6035Sstevep res = REP_PROTOCOL_FAIL_UNKNOWN; 1221*6035Sstevep } 1222*6035Sstevep 1223*6035Sstevep errexit: 1224*6035Sstevep if (res != REP_PROTOCOL_SUCCESS && unlink(tmppath) < 0) 1225*6035Sstevep configd_critical( 1226*6035Sstevep "Backend copy failed: remove %s: %s\n", 1227*6035Sstevep tmppath, strerror(errno)); 1228*6035Sstevep 1229*6035Sstevep (void) close(srcfd); 1230*6035Sstevep (void) close(dstfd); 1231*6035Sstevep 1232*6035Sstevep out: 1233*6035Sstevep free(tmppath); 1234*6035Sstevep if (sw_back) { 1235*6035Sstevep if (unlink(src) < 0) 1236*6035Sstevep configd_critical( 1237*6035Sstevep "Backend copy failed: remove %s: %s\n", 1238*6035Sstevep src, strerror(errno)); 1239*6035Sstevep } 1240*6035Sstevep 1241*6035Sstevep return (res); 1242*6035Sstevep } 1243*6035Sstevep 1244*6035Sstevep /* 1245*6035Sstevep * Perform sanity check on the repository. 1246*6035Sstevep * Return 0 if check succeeds or -1 if fails. 1247*6035Sstevep */ 1248*6035Sstevep static int 1249*6035Sstevep backend_switch_check(struct sqlite *be_db, char **errp) 1250*6035Sstevep { 1251*6035Sstevep struct run_single_int_info info; 1252*6035Sstevep uint32_t val = -1UL; 1253*6035Sstevep int r; 1254*6035Sstevep 1255*6035Sstevep info.rs_out = &val; 1256*6035Sstevep info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 1257*6035Sstevep 1258*6035Sstevep r = sqlite_exec(be_db, 1259*6035Sstevep "SELECT schema_version FROM schema_version;", 1260*6035Sstevep run_single_int_callback, &info, errp); 1261*6035Sstevep 1262*6035Sstevep if (r == SQLITE_OK && 1263*6035Sstevep info.rs_result != REP_PROTOCOL_FAIL_NOT_FOUND && 1264*6035Sstevep val == BACKEND_SCHEMA_VERSION) 1265*6035Sstevep return (0); 1266*6035Sstevep else 1267*6035Sstevep return (-1); 1268*6035Sstevep } 1269*6035Sstevep 1270*6035Sstevep /* 1271*6035Sstevep * Backend switch entry point. It is called to perform the backend copy and 1272*6035Sstevep * switch from src to dst. First, it blocks all other clients from accessing 1273*6035Sstevep * the repository by calling backend_lock to lock the repository. Upon 1274*6035Sstevep * successful lock, copying and switching of the repository are performed. 1275*6035Sstevep * 1276*6035Sstevep * Can return: 1277*6035Sstevep * REP_PROTOCOL_SUCCESS successful switch 1278*6035Sstevep * REP_PROTOCOL_FAIL_BACKEND_ACCESS backen access fails 1279*6035Sstevep * REP_PROTOCOL_FAIL_BACKEND_READONLY backend is not writable 1280*6035Sstevep * REP_PROTOCOL_FAIL_UNKNOWN file operation error 1281*6035Sstevep * REP_PROTOCOL_FAIL_NO_RESOURCES out of memory 1282*6035Sstevep */ 1283*6035Sstevep rep_protocol_responseid_t 1284*6035Sstevep backend_switch(int sw_back) 1285*6035Sstevep { 1286*6035Sstevep rep_protocol_responseid_t result; 1287*6035Sstevep sqlite_backend_t *be; 1288*6035Sstevep struct sqlite *new; 1289*6035Sstevep char *errp; 1290*6035Sstevep const char *dst; 1291*6035Sstevep 1292*6035Sstevep result = backend_lock(BACKEND_TYPE_NORMAL, 1, &be); 1293*6035Sstevep if (result != REP_PROTOCOL_SUCCESS) 1294*6035Sstevep return (result); 1295*6035Sstevep 1296*6035Sstevep if (sw_back) { 1297*6035Sstevep dst = REPOSITORY_DB; 1298*6035Sstevep } else { 1299*6035Sstevep dst = FAST_REPOSITORY_DB; 1300*6035Sstevep } 1301*6035Sstevep 1302*6035Sstevep /* 1303*6035Sstevep * Do the actual copy and rename 1304*6035Sstevep */ 1305*6035Sstevep result = backend_switch_copy(be->be_path, dst, sw_back); 1306*6035Sstevep if (result != REP_PROTOCOL_SUCCESS) { 1307*6035Sstevep goto errout; 1308*6035Sstevep } 1309*6035Sstevep 1310*6035Sstevep /* 1311*6035Sstevep * Do the backend sanity check and switch 1312*6035Sstevep */ 1313*6035Sstevep new = sqlite_open(dst, 0600, &errp); 1314*6035Sstevep if (new != NULL) { 1315*6035Sstevep /* 1316*6035Sstevep * Sanity check 1317*6035Sstevep */ 1318*6035Sstevep if (backend_switch_check(new, &errp) == 0) { 1319*6035Sstevep free((char *)be->be_path); 1320*6035Sstevep be->be_path = strdup(dst); 1321*6035Sstevep if (be->be_path == NULL) { 1322*6035Sstevep configd_critical( 1323*6035Sstevep "Backend switch failed: strdup %s: %s\n", 1324*6035Sstevep dst, strerror(errno)); 1325*6035Sstevep result = REP_PROTOCOL_FAIL_NO_RESOURCES; 1326*6035Sstevep sqlite_close(new); 1327*6035Sstevep } else { 1328*6035Sstevep sqlite_close(be->be_db); 1329*6035Sstevep be->be_db = new; 1330*6035Sstevep } 1331*6035Sstevep } else { 1332*6035Sstevep configd_critical( 1333*6035Sstevep "Backend switch failed: integrity check %s: %s\n", 1334*6035Sstevep dst, errp); 1335*6035Sstevep result = REP_PROTOCOL_FAIL_BACKEND_ACCESS; 1336*6035Sstevep } 1337*6035Sstevep } else { 1338*6035Sstevep configd_critical("Backend switch failed: sqlite_open %s: %s\n", 1339*6035Sstevep dst, errp); 1340*6035Sstevep result = REP_PROTOCOL_FAIL_BACKEND_ACCESS; 1341*6035Sstevep } 1342*6035Sstevep 1343*6035Sstevep errout: 1344*6035Sstevep backend_unlock(be); 1345*6035Sstevep return (result); 1346*6035Sstevep } 1347*6035Sstevep 1348*6035Sstevep /* 1349*6035Sstevep * This routine is called to attempt the recovery of 1350*6035Sstevep * the most recent valid repository if possible when configd 1351*6035Sstevep * is restarted for some reasons or when system crashes 1352*6035Sstevep * during the switch operation. The repository databases 1353*6035Sstevep * referenced here are indicators of successful switch 1354*6035Sstevep * operations. 1355*6035Sstevep */ 1356*6035Sstevep static void 1357*6035Sstevep backend_switch_recovery(void) 1358*6035Sstevep { 1359*6035Sstevep const char *fast_db = FAST_REPOSITORY_DB; 1360*6035Sstevep char *errp; 1361*6035Sstevep struct stat s_buf; 1362*6035Sstevep struct sqlite *be_db; 1363*6035Sstevep 1364*6035Sstevep 1365*6035Sstevep /* 1366*6035Sstevep * A good transient db containing most recent data can 1367*6035Sstevep * exist if system or svc.configd crashes during the 1368*6035Sstevep * switch operation. If that is the case, check its 1369*6035Sstevep * integrity and use it. 1370*6035Sstevep */ 1371*6035Sstevep if (stat(fast_db, &s_buf) < 0) { 1372*6035Sstevep return; 1373*6035Sstevep } 1374*6035Sstevep 1375*6035Sstevep /* 1376*6035Sstevep * Do sanity check on the db 1377*6035Sstevep */ 1378*6035Sstevep be_db = sqlite_open(fast_db, 0600, &errp); 1379*6035Sstevep 1380*6035Sstevep if (be_db != NULL) { 1381*6035Sstevep if (backend_switch_check(be_db, &errp) == 0) 1382*6035Sstevep (void) backend_switch_copy(fast_db, REPOSITORY_DB, 1); 1383*6035Sstevep } 1384*6035Sstevep 1385*6035Sstevep (void) unlink(fast_db); 1386*6035Sstevep } 1387*6035Sstevep 13880Sstevel@tonic-gate /*ARGSUSED*/ 13890Sstevel@tonic-gate static int 13900Sstevel@tonic-gate backend_integrity_callback(void *private, int narg, char **vals, char **cols) 13910Sstevel@tonic-gate { 13920Sstevel@tonic-gate char **out = private; 13930Sstevel@tonic-gate char *old = *out; 13940Sstevel@tonic-gate char *new; 13950Sstevel@tonic-gate const char *info; 13960Sstevel@tonic-gate size_t len; 13970Sstevel@tonic-gate int x; 13980Sstevel@tonic-gate 13990Sstevel@tonic-gate for (x = 0; x < narg; x++) { 14000Sstevel@tonic-gate if ((info = vals[x]) != NULL && 14010Sstevel@tonic-gate strcmp(info, "ok") != 0) { 14020Sstevel@tonic-gate len = (old == NULL)? 0 : strlen(old); 14030Sstevel@tonic-gate len += strlen(info) + 2; /* '\n' + '\0' */ 14040Sstevel@tonic-gate 14050Sstevel@tonic-gate new = realloc(old, len); 14060Sstevel@tonic-gate if (new == NULL) 14070Sstevel@tonic-gate return (BACKEND_CALLBACK_ABORT); 14080Sstevel@tonic-gate if (old == NULL) 14090Sstevel@tonic-gate new[0] = 0; 14100Sstevel@tonic-gate old = *out = new; 14110Sstevel@tonic-gate (void) strlcat(new, info, len); 14120Sstevel@tonic-gate (void) strlcat(new, "\n", len); 14130Sstevel@tonic-gate } 14140Sstevel@tonic-gate } 14150Sstevel@tonic-gate return (BACKEND_CALLBACK_CONTINUE); 14160Sstevel@tonic-gate } 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate #define BACKEND_CREATE_LOCKED -2 14190Sstevel@tonic-gate #define BACKEND_CREATE_FAIL -1 14200Sstevel@tonic-gate #define BACKEND_CREATE_SUCCESS 0 14210Sstevel@tonic-gate #define BACKEND_CREATE_READONLY 1 14220Sstevel@tonic-gate #define BACKEND_CREATE_NEED_INIT 2 14230Sstevel@tonic-gate static int 14240Sstevel@tonic-gate backend_create(backend_type_t backend_id, const char *db_file, 14250Sstevel@tonic-gate sqlite_backend_t **bep) 14260Sstevel@tonic-gate { 14270Sstevel@tonic-gate char *errp; 14280Sstevel@tonic-gate char *integrity_results = NULL; 14290Sstevel@tonic-gate sqlite_backend_t *be; 14300Sstevel@tonic-gate int r; 14310Sstevel@tonic-gate uint32_t val = -1UL; 14320Sstevel@tonic-gate struct run_single_int_info info; 14330Sstevel@tonic-gate int fd; 14340Sstevel@tonic-gate 14350Sstevel@tonic-gate assert(backend_id >= 0 && backend_id < BACKEND_TYPE_TOTAL); 14360Sstevel@tonic-gate 14370Sstevel@tonic-gate be = &be_info[backend_id]; 14380Sstevel@tonic-gate assert(be->be_db == NULL); 14390Sstevel@tonic-gate 14400Sstevel@tonic-gate (void) pthread_mutex_init(&be->be_lock, NULL); 14410Sstevel@tonic-gate (void) pthread_mutex_lock(&be->be_lock); 14420Sstevel@tonic-gate 14430Sstevel@tonic-gate be->be_type = backend_id; 14440Sstevel@tonic-gate be->be_path = strdup(db_file); 14450Sstevel@tonic-gate if (be->be_path == NULL) { 14460Sstevel@tonic-gate perror("malloc"); 14470Sstevel@tonic-gate goto fail; 14480Sstevel@tonic-gate } 14490Sstevel@tonic-gate 14500Sstevel@tonic-gate be->be_db = sqlite_open(be->be_path, 0600, &errp); 14510Sstevel@tonic-gate 14520Sstevel@tonic-gate if (be->be_db == NULL) { 14530Sstevel@tonic-gate if (strstr(errp, "out of memory") != NULL) { 14540Sstevel@tonic-gate configd_critical("%s: %s\n", db_file, errp); 14550Sstevel@tonic-gate free(errp); 14560Sstevel@tonic-gate 14570Sstevel@tonic-gate goto fail; 14580Sstevel@tonic-gate } 14590Sstevel@tonic-gate 14600Sstevel@tonic-gate /* report it as an integrity failure */ 14610Sstevel@tonic-gate integrity_results = errp; 14620Sstevel@tonic-gate errp = NULL; 14630Sstevel@tonic-gate goto integrity_fail; 14640Sstevel@tonic-gate } 14650Sstevel@tonic-gate 14660Sstevel@tonic-gate /* 14670Sstevel@tonic-gate * check if we are inited and of the correct schema version 14680Sstevel@tonic-gate * 14690Sstevel@tonic-gate * Eventually, we'll support schema upgrade here. 14700Sstevel@tonic-gate */ 14710Sstevel@tonic-gate info.rs_out = &val; 14720Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 14730Sstevel@tonic-gate 14740Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT schema_version FROM schema_version;", 14750Sstevel@tonic-gate run_single_int_callback, &info, &errp); 14760Sstevel@tonic-gate if (r == SQLITE_ERROR && 14770Sstevel@tonic-gate strcmp("no such table: schema_version", errp) == 0) { 14780Sstevel@tonic-gate free(errp); 14790Sstevel@tonic-gate /* 14800Sstevel@tonic-gate * Could be an empty repository, could be pre-schema_version 14810Sstevel@tonic-gate * schema. Check for id_tbl, which has always been there. 14820Sstevel@tonic-gate */ 14830Sstevel@tonic-gate r = sqlite_exec(be->be_db, "SELECT count() FROM id_tbl;", 14840Sstevel@tonic-gate NULL, NULL, &errp); 14850Sstevel@tonic-gate if (r == SQLITE_ERROR && 14860Sstevel@tonic-gate strcmp("no such table: id_tbl", errp) == 0) { 14870Sstevel@tonic-gate free(errp); 14880Sstevel@tonic-gate *bep = be; 14890Sstevel@tonic-gate return (BACKEND_CREATE_NEED_INIT); 14900Sstevel@tonic-gate } 14910Sstevel@tonic-gate 14920Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", db_file); 14930Sstevel@tonic-gate goto fail; 14940Sstevel@tonic-gate } 14950Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 14960Sstevel@tonic-gate free(errp); 14970Sstevel@tonic-gate *bep = NULL; 14980Sstevel@tonic-gate backend_destroy(be); 14990Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 15000Sstevel@tonic-gate } 15010Sstevel@tonic-gate if (r == SQLITE_OK) { 15020Sstevel@tonic-gate if (info.rs_result == REP_PROTOCOL_FAIL_NOT_FOUND || 15030Sstevel@tonic-gate val != BACKEND_SCHEMA_VERSION) { 15040Sstevel@tonic-gate configd_critical("%s: schema version mismatch\n", 15050Sstevel@tonic-gate db_file); 15060Sstevel@tonic-gate goto fail; 15070Sstevel@tonic-gate } 15080Sstevel@tonic-gate } 15090Sstevel@tonic-gate 15100Sstevel@tonic-gate /* 15110Sstevel@tonic-gate * pull in the whole database sequentially. 15120Sstevel@tonic-gate */ 15130Sstevel@tonic-gate if ((fd = open(db_file, O_RDONLY)) >= 0) { 15140Sstevel@tonic-gate size_t sz = 64 * 1024; 15150Sstevel@tonic-gate char *buffer = malloc(sz); 15160Sstevel@tonic-gate if (buffer != NULL) { 15170Sstevel@tonic-gate while (read(fd, buffer, sz) > 0) 15180Sstevel@tonic-gate ; 15190Sstevel@tonic-gate free(buffer); 15200Sstevel@tonic-gate } 15210Sstevel@tonic-gate (void) close(fd); 15220Sstevel@tonic-gate } 15230Sstevel@tonic-gate 15240Sstevel@tonic-gate /* 15250Sstevel@tonic-gate * run an integrity check 15260Sstevel@tonic-gate */ 15270Sstevel@tonic-gate r = sqlite_exec(be->be_db, "PRAGMA integrity_check;", 15280Sstevel@tonic-gate backend_integrity_callback, &integrity_results, &errp); 15290Sstevel@tonic-gate 15300Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 15310Sstevel@tonic-gate free(errp); 15320Sstevel@tonic-gate *bep = NULL; 15330Sstevel@tonic-gate backend_destroy(be); 15340Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 15350Sstevel@tonic-gate } 15360Sstevel@tonic-gate if (r == SQLITE_ABORT) { 15370Sstevel@tonic-gate free(errp); 15380Sstevel@tonic-gate errp = NULL; 15390Sstevel@tonic-gate integrity_results = "out of memory running integrity check\n"; 15400Sstevel@tonic-gate } else if (r != SQLITE_OK && integrity_results == NULL) { 15410Sstevel@tonic-gate integrity_results = errp; 15420Sstevel@tonic-gate errp = NULL; 15430Sstevel@tonic-gate } 15440Sstevel@tonic-gate 15450Sstevel@tonic-gate integrity_fail: 15460Sstevel@tonic-gate if (integrity_results != NULL) { 15470Sstevel@tonic-gate const char *fname = "/etc/svc/volatile/db_errors"; 15480Sstevel@tonic-gate if ((fd = open(fname, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { 15490Sstevel@tonic-gate fname = NULL; 15500Sstevel@tonic-gate } else { 15510Sstevel@tonic-gate if (backend_fd_write(fd, "\n\n") < 0 || 15520Sstevel@tonic-gate backend_fd_write(fd, db_file) < 0 || 15530Sstevel@tonic-gate backend_fd_write(fd, 15540Sstevel@tonic-gate ": PRAGMA integrity_check; failed. Results:\n") < 15550Sstevel@tonic-gate 0 || backend_fd_write(fd, integrity_results) < 0 || 15560Sstevel@tonic-gate backend_fd_write(fd, "\n\n") < 0) { 15570Sstevel@tonic-gate fname = NULL; 15580Sstevel@tonic-gate } 15590Sstevel@tonic-gate (void) close(fd); 15600Sstevel@tonic-gate } 15610Sstevel@tonic-gate 15620Sstevel@tonic-gate if (!is_main_repository || 15630Sstevel@tonic-gate backend_id == BACKEND_TYPE_NONPERSIST) { 15640Sstevel@tonic-gate if (fname != NULL) 15650Sstevel@tonic-gate configd_critical( 15660Sstevel@tonic-gate "%s: integrity check failed. Details in " 15670Sstevel@tonic-gate "%s\n", db_file, fname); 15680Sstevel@tonic-gate else 15690Sstevel@tonic-gate configd_critical( 15704931Spjung "%s: integrity check failed.\n", 15710Sstevel@tonic-gate db_file); 15720Sstevel@tonic-gate } else { 15730Sstevel@tonic-gate (void) fprintf(stderr, 15740Sstevel@tonic-gate "\n" 15750Sstevel@tonic-gate "svc.configd: smf(5) database integrity check of:\n" 15760Sstevel@tonic-gate "\n" 15770Sstevel@tonic-gate " %s\n" 15780Sstevel@tonic-gate "\n" 15790Sstevel@tonic-gate " failed. The database might be damaged or a media error might have\n" 15800Sstevel@tonic-gate " prevented it from being verified. Additional information useful to\n" 15810Sstevel@tonic-gate " your service provider%s%s\n" 15820Sstevel@tonic-gate "\n" 15830Sstevel@tonic-gate " The system will not be able to boot until you have restored a working\n" 15840Sstevel@tonic-gate " database. svc.startd(1M) will provide a sulogin(1M) prompt for recovery\n" 15850Sstevel@tonic-gate " purposes. The command:\n" 15860Sstevel@tonic-gate "\n" 15870Sstevel@tonic-gate " /lib/svc/bin/restore_repository\n" 15880Sstevel@tonic-gate "\n" 15890Sstevel@tonic-gate " can be run to restore a backup version of your repository. See\n" 15900Sstevel@tonic-gate " http://sun.com/msg/SMF-8000-MY for more information.\n" 15910Sstevel@tonic-gate "\n", 15924520Snw141292 db_file, 15934520Snw141292 (fname == NULL)? ":\n\n" : " is in:\n\n ", 15944520Snw141292 (fname == NULL)? integrity_results : fname); 15950Sstevel@tonic-gate } 15960Sstevel@tonic-gate free(errp); 15970Sstevel@tonic-gate goto fail; 15980Sstevel@tonic-gate } 15990Sstevel@tonic-gate 16000Sstevel@tonic-gate /* 16010Sstevel@tonic-gate * check if we are writable 16020Sstevel@tonic-gate */ 1603407Sjwadams r = backend_is_readonly(be->be_db, be->be_path); 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate if (r == SQLITE_BUSY || r == SQLITE_LOCKED) { 16060Sstevel@tonic-gate free(errp); 16070Sstevel@tonic-gate *bep = NULL; 16080Sstevel@tonic-gate backend_destroy(be); 16090Sstevel@tonic-gate return (BACKEND_CREATE_LOCKED); 16100Sstevel@tonic-gate } 16110Sstevel@tonic-gate if (r != SQLITE_OK && r != SQLITE_FULL) { 16120Sstevel@tonic-gate free(errp); 16130Sstevel@tonic-gate be->be_readonly = 1; 16140Sstevel@tonic-gate *bep = be; 16150Sstevel@tonic-gate return (BACKEND_CREATE_READONLY); 16160Sstevel@tonic-gate } 16170Sstevel@tonic-gate *bep = be; 16180Sstevel@tonic-gate return (BACKEND_CREATE_SUCCESS); 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate fail: 16210Sstevel@tonic-gate *bep = NULL; 16220Sstevel@tonic-gate backend_destroy(be); 16230Sstevel@tonic-gate return (BACKEND_CREATE_FAIL); 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate /* 16270Sstevel@tonic-gate * (arg & -arg) is, through the magic of twos-complement arithmetic, the 16280Sstevel@tonic-gate * lowest set bit in arg. 16290Sstevel@tonic-gate */ 16300Sstevel@tonic-gate static size_t 16310Sstevel@tonic-gate round_up_to_p2(size_t arg) 16320Sstevel@tonic-gate { 16330Sstevel@tonic-gate /* 16340Sstevel@tonic-gate * Don't allow a zero result. 16350Sstevel@tonic-gate */ 16360Sstevel@tonic-gate assert(arg > 0 && ((ssize_t)arg > 0)); 16370Sstevel@tonic-gate 16380Sstevel@tonic-gate while ((arg & (arg - 1)) != 0) 16390Sstevel@tonic-gate arg += (arg & -arg); 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate return (arg); 16420Sstevel@tonic-gate } 16430Sstevel@tonic-gate 16440Sstevel@tonic-gate /* 16450Sstevel@tonic-gate * Returns 16460Sstevel@tonic-gate * _NO_RESOURCES - out of memory 16470Sstevel@tonic-gate * _BACKEND_ACCESS - backend type t (other than _NORMAL) doesn't exist 16480Sstevel@tonic-gate * _DONE - callback aborted query 16490Sstevel@tonic-gate * _SUCCESS 16500Sstevel@tonic-gate */ 16510Sstevel@tonic-gate int 16520Sstevel@tonic-gate backend_run(backend_type_t t, backend_query_t *q, 16530Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 16540Sstevel@tonic-gate { 16550Sstevel@tonic-gate char *errmsg = NULL; 16560Sstevel@tonic-gate int ret; 16570Sstevel@tonic-gate sqlite_backend_t *be; 16580Sstevel@tonic-gate hrtime_t ts, vts; 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 16610Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate if ((ret = backend_lock(t, 0, &be)) != REP_PROTOCOL_SUCCESS) 16640Sstevel@tonic-gate return (ret); 16650Sstevel@tonic-gate 16660Sstevel@tonic-gate ts = gethrtime(); 16670Sstevel@tonic-gate vts = gethrvtime(); 16680Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 16690Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 16700Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 16710Sstevel@tonic-gate backend_unlock(be); 16720Sstevel@tonic-gate 16730Sstevel@tonic-gate return (ret); 16740Sstevel@tonic-gate } 16750Sstevel@tonic-gate 16760Sstevel@tonic-gate /* 16770Sstevel@tonic-gate * Starts a "read-only" transaction -- i.e., locks out writers as long 16780Sstevel@tonic-gate * as it is active. 16790Sstevel@tonic-gate * 16800Sstevel@tonic-gate * Fails with 16810Sstevel@tonic-gate * _NO_RESOURCES - out of memory 16820Sstevel@tonic-gate * 16830Sstevel@tonic-gate * If t is not _NORMAL, can also fail with 16840Sstevel@tonic-gate * _BACKEND_ACCESS - backend does not exist 16850Sstevel@tonic-gate * 16860Sstevel@tonic-gate * If writable is true, can also fail with 16870Sstevel@tonic-gate * _BACKEND_READONLY 16880Sstevel@tonic-gate */ 16890Sstevel@tonic-gate static int 16900Sstevel@tonic-gate backend_tx_begin_common(backend_type_t t, backend_tx_t **txp, int writable) 16910Sstevel@tonic-gate { 16920Sstevel@tonic-gate backend_tx_t *ret; 16930Sstevel@tonic-gate sqlite_backend_t *be; 16940Sstevel@tonic-gate int r; 16950Sstevel@tonic-gate 16960Sstevel@tonic-gate *txp = NULL; 16970Sstevel@tonic-gate 16980Sstevel@tonic-gate ret = uu_zalloc(sizeof (*ret)); 16990Sstevel@tonic-gate if (ret == NULL) 17000Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 17010Sstevel@tonic-gate 17020Sstevel@tonic-gate if ((r = backend_lock(t, writable, &be)) != REP_PROTOCOL_SUCCESS) { 17030Sstevel@tonic-gate uu_free(ret); 17040Sstevel@tonic-gate return (r); 17050Sstevel@tonic-gate } 17060Sstevel@tonic-gate 17070Sstevel@tonic-gate ret->bt_be = be; 17080Sstevel@tonic-gate ret->bt_readonly = !writable; 17090Sstevel@tonic-gate ret->bt_type = t; 17100Sstevel@tonic-gate ret->bt_full = 0; 17110Sstevel@tonic-gate 17120Sstevel@tonic-gate *txp = ret; 17130Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 17140Sstevel@tonic-gate } 17150Sstevel@tonic-gate 17160Sstevel@tonic-gate int 17170Sstevel@tonic-gate backend_tx_begin_ro(backend_type_t t, backend_tx_t **txp) 17180Sstevel@tonic-gate { 17190Sstevel@tonic-gate return (backend_tx_begin_common(t, txp, 0)); 17200Sstevel@tonic-gate } 17210Sstevel@tonic-gate 17220Sstevel@tonic-gate static void 17230Sstevel@tonic-gate backend_tx_end(backend_tx_t *tx) 17240Sstevel@tonic-gate { 17250Sstevel@tonic-gate sqlite_backend_t *be; 17260Sstevel@tonic-gate 17270Sstevel@tonic-gate be = tx->bt_be; 17280Sstevel@tonic-gate 17290Sstevel@tonic-gate if (tx->bt_full) { 17300Sstevel@tonic-gate struct sqlite *new; 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate /* 17330Sstevel@tonic-gate * sqlite tends to be sticky with SQLITE_FULL, so we try 17340Sstevel@tonic-gate * to get a fresh database handle if we got a FULL warning 17350Sstevel@tonic-gate * along the way. If that fails, no harm done. 17360Sstevel@tonic-gate */ 17370Sstevel@tonic-gate new = sqlite_open(be->be_path, 0600, NULL); 17380Sstevel@tonic-gate if (new != NULL) { 17390Sstevel@tonic-gate sqlite_close(be->be_db); 17400Sstevel@tonic-gate be->be_db = new; 17410Sstevel@tonic-gate } 17420Sstevel@tonic-gate } 17430Sstevel@tonic-gate backend_unlock(be); 17440Sstevel@tonic-gate tx->bt_be = NULL; 17450Sstevel@tonic-gate uu_free(tx); 17460Sstevel@tonic-gate } 17470Sstevel@tonic-gate 17480Sstevel@tonic-gate void 17490Sstevel@tonic-gate backend_tx_end_ro(backend_tx_t *tx) 17500Sstevel@tonic-gate { 17510Sstevel@tonic-gate assert(tx->bt_readonly); 17520Sstevel@tonic-gate backend_tx_end(tx); 17530Sstevel@tonic-gate } 17540Sstevel@tonic-gate 17550Sstevel@tonic-gate /* 17560Sstevel@tonic-gate * Fails with 17570Sstevel@tonic-gate * _NO_RESOURCES - out of memory 17580Sstevel@tonic-gate * _BACKEND_ACCESS 17590Sstevel@tonic-gate * _BACKEND_READONLY 17600Sstevel@tonic-gate */ 17610Sstevel@tonic-gate int 17620Sstevel@tonic-gate backend_tx_begin(backend_type_t t, backend_tx_t **txp) 17630Sstevel@tonic-gate { 17640Sstevel@tonic-gate int r; 17650Sstevel@tonic-gate char *errmsg; 17660Sstevel@tonic-gate hrtime_t ts, vts; 17670Sstevel@tonic-gate 17680Sstevel@tonic-gate r = backend_tx_begin_common(t, txp, 1); 17690Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) 17700Sstevel@tonic-gate return (r); 17710Sstevel@tonic-gate 17720Sstevel@tonic-gate ts = gethrtime(); 17730Sstevel@tonic-gate vts = gethrvtime(); 17740Sstevel@tonic-gate r = sqlite_exec((*txp)->bt_be->be_db, "BEGIN TRANSACTION", NULL, NULL, 17750Sstevel@tonic-gate &errmsg); 17760Sstevel@tonic-gate UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); 17770Sstevel@tonic-gate if (r == SQLITE_FULL) 17780Sstevel@tonic-gate (*txp)->bt_full = 1; 17790Sstevel@tonic-gate r = backend_error((*txp)->bt_be, r, errmsg); 17800Sstevel@tonic-gate 17810Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 17820Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 17830Sstevel@tonic-gate (void) sqlite_exec((*txp)->bt_be->be_db, 17840Sstevel@tonic-gate "ROLLBACK TRANSACTION", NULL, NULL, NULL); 17850Sstevel@tonic-gate backend_tx_end(*txp); 17860Sstevel@tonic-gate *txp = NULL; 17870Sstevel@tonic-gate return (r); 17880Sstevel@tonic-gate } 17890Sstevel@tonic-gate 17900Sstevel@tonic-gate (*txp)->bt_readonly = 0; 17910Sstevel@tonic-gate 17920Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 17930Sstevel@tonic-gate } 17940Sstevel@tonic-gate 17950Sstevel@tonic-gate void 17960Sstevel@tonic-gate backend_tx_rollback(backend_tx_t *tx) 17970Sstevel@tonic-gate { 17980Sstevel@tonic-gate int r; 17990Sstevel@tonic-gate char *errmsg; 18000Sstevel@tonic-gate sqlite_backend_t *be; 18010Sstevel@tonic-gate hrtime_t ts, vts; 18020Sstevel@tonic-gate 18030Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 18040Sstevel@tonic-gate be = tx->bt_be; 18050Sstevel@tonic-gate 18060Sstevel@tonic-gate ts = gethrtime(); 18070Sstevel@tonic-gate vts = gethrvtime(); 18080Sstevel@tonic-gate r = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 18090Sstevel@tonic-gate &errmsg); 18100Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 18110Sstevel@tonic-gate if (r == SQLITE_FULL) 18120Sstevel@tonic-gate tx->bt_full = 1; 18130Sstevel@tonic-gate (void) backend_error(be, r, errmsg); 18140Sstevel@tonic-gate 18150Sstevel@tonic-gate backend_tx_end(tx); 18160Sstevel@tonic-gate } 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate /* 18190Sstevel@tonic-gate * Fails with 18200Sstevel@tonic-gate * _NO_RESOURCES - out of memory 18210Sstevel@tonic-gate */ 18220Sstevel@tonic-gate int 18230Sstevel@tonic-gate backend_tx_commit(backend_tx_t *tx) 18240Sstevel@tonic-gate { 18250Sstevel@tonic-gate int r, r2; 18260Sstevel@tonic-gate char *errmsg; 18270Sstevel@tonic-gate sqlite_backend_t *be; 18280Sstevel@tonic-gate hrtime_t ts, vts; 18290Sstevel@tonic-gate 18300Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 18310Sstevel@tonic-gate be = tx->bt_be; 18320Sstevel@tonic-gate ts = gethrtime(); 18330Sstevel@tonic-gate vts = gethrvtime(); 18340Sstevel@tonic-gate r = sqlite_exec(be->be_db, "COMMIT TRANSACTION", NULL, NULL, 18350Sstevel@tonic-gate &errmsg); 18360Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 18370Sstevel@tonic-gate if (r == SQLITE_FULL) 18380Sstevel@tonic-gate tx->bt_full = 1; 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate r = backend_error(be, r, errmsg); 18410Sstevel@tonic-gate assert(r != REP_PROTOCOL_DONE); 18420Sstevel@tonic-gate 18430Sstevel@tonic-gate if (r != REP_PROTOCOL_SUCCESS) { 18440Sstevel@tonic-gate r2 = sqlite_exec(be->be_db, "ROLLBACK TRANSACTION", NULL, NULL, 18450Sstevel@tonic-gate &errmsg); 18460Sstevel@tonic-gate r2 = backend_error(be, r2, errmsg); 18470Sstevel@tonic-gate if (r2 != REP_PROTOCOL_SUCCESS) 18480Sstevel@tonic-gate backend_panic("cannot rollback failed commit"); 18490Sstevel@tonic-gate 18500Sstevel@tonic-gate backend_tx_end(tx); 18510Sstevel@tonic-gate return (r); 18520Sstevel@tonic-gate } 18530Sstevel@tonic-gate backend_tx_end(tx); 18540Sstevel@tonic-gate return (REP_PROTOCOL_SUCCESS); 18550Sstevel@tonic-gate } 18560Sstevel@tonic-gate 18570Sstevel@tonic-gate static const char * 18580Sstevel@tonic-gate id_space_to_name(enum id_space id) 18590Sstevel@tonic-gate { 18600Sstevel@tonic-gate switch (id) { 18610Sstevel@tonic-gate case BACKEND_ID_SERVICE_INSTANCE: 18620Sstevel@tonic-gate return ("SI"); 18630Sstevel@tonic-gate case BACKEND_ID_PROPERTYGRP: 18640Sstevel@tonic-gate return ("PG"); 18650Sstevel@tonic-gate case BACKEND_ID_GENERATION: 18660Sstevel@tonic-gate return ("GEN"); 18670Sstevel@tonic-gate case BACKEND_ID_PROPERTY: 18680Sstevel@tonic-gate return ("PROP"); 18690Sstevel@tonic-gate case BACKEND_ID_VALUE: 18700Sstevel@tonic-gate return ("VAL"); 18710Sstevel@tonic-gate case BACKEND_ID_SNAPNAME: 18720Sstevel@tonic-gate return ("SNAME"); 18730Sstevel@tonic-gate case BACKEND_ID_SNAPSHOT: 18740Sstevel@tonic-gate return ("SHOT"); 18750Sstevel@tonic-gate case BACKEND_ID_SNAPLEVEL: 18760Sstevel@tonic-gate return ("SLVL"); 18770Sstevel@tonic-gate default: 18780Sstevel@tonic-gate abort(); 18790Sstevel@tonic-gate /*NOTREACHED*/ 18800Sstevel@tonic-gate } 18810Sstevel@tonic-gate } 18820Sstevel@tonic-gate 18830Sstevel@tonic-gate /* 18840Sstevel@tonic-gate * Returns a new id or 0 if the id argument is invalid or the query fails. 18850Sstevel@tonic-gate */ 18860Sstevel@tonic-gate uint32_t 18870Sstevel@tonic-gate backend_new_id(backend_tx_t *tx, enum id_space id) 18880Sstevel@tonic-gate { 18890Sstevel@tonic-gate struct run_single_int_info info; 18900Sstevel@tonic-gate uint32_t new_id = 0; 18910Sstevel@tonic-gate const char *name = id_space_to_name(id); 18920Sstevel@tonic-gate char *errmsg; 18930Sstevel@tonic-gate int ret; 18940Sstevel@tonic-gate sqlite_backend_t *be; 18950Sstevel@tonic-gate hrtime_t ts, vts; 18960Sstevel@tonic-gate 18970Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 18980Sstevel@tonic-gate be = tx->bt_be; 18990Sstevel@tonic-gate 19000Sstevel@tonic-gate info.rs_out = &new_id; 19010Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 19020Sstevel@tonic-gate 19030Sstevel@tonic-gate ts = gethrtime(); 19040Sstevel@tonic-gate vts = gethrvtime(); 19050Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 19060Sstevel@tonic-gate "SELECT id_next FROM id_tbl WHERE (id_name = '%q');" 19070Sstevel@tonic-gate "UPDATE id_tbl SET id_next = id_next + 1 WHERE (id_name = '%q');", 19080Sstevel@tonic-gate run_single_int_callback, &info, &errmsg, name, name); 19090Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 19100Sstevel@tonic-gate if (ret == SQLITE_FULL) 19110Sstevel@tonic-gate tx->bt_full = 1; 19120Sstevel@tonic-gate 19130Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 19140Sstevel@tonic-gate 19150Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) { 19160Sstevel@tonic-gate return (0); 19170Sstevel@tonic-gate } 19180Sstevel@tonic-gate 19190Sstevel@tonic-gate return (new_id); 19200Sstevel@tonic-gate } 19210Sstevel@tonic-gate 19220Sstevel@tonic-gate /* 19230Sstevel@tonic-gate * Returns 19240Sstevel@tonic-gate * _NO_RESOURCES - out of memory 19250Sstevel@tonic-gate * _DONE - callback aborted query 19260Sstevel@tonic-gate * _SUCCESS 19270Sstevel@tonic-gate */ 19280Sstevel@tonic-gate int 19290Sstevel@tonic-gate backend_tx_run(backend_tx_t *tx, backend_query_t *q, 19300Sstevel@tonic-gate backend_run_callback_f *cb, void *data) 19310Sstevel@tonic-gate { 19320Sstevel@tonic-gate char *errmsg = NULL; 19330Sstevel@tonic-gate int ret; 19340Sstevel@tonic-gate sqlite_backend_t *be; 19350Sstevel@tonic-gate hrtime_t ts, vts; 19360Sstevel@tonic-gate 19370Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL); 19380Sstevel@tonic-gate be = tx->bt_be; 19390Sstevel@tonic-gate 19400Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 19410Sstevel@tonic-gate return (REP_PROTOCOL_FAIL_NO_RESOURCES); 19420Sstevel@tonic-gate 19430Sstevel@tonic-gate ts = gethrtime(); 19440Sstevel@tonic-gate vts = gethrvtime(); 19450Sstevel@tonic-gate ret = sqlite_exec(be->be_db, q->bq_buf, cb, data, &errmsg); 19460Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 19470Sstevel@tonic-gate if (ret == SQLITE_FULL) 19480Sstevel@tonic-gate tx->bt_full = 1; 19490Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 19500Sstevel@tonic-gate 19510Sstevel@tonic-gate return (ret); 19520Sstevel@tonic-gate } 19530Sstevel@tonic-gate 19540Sstevel@tonic-gate /* 19550Sstevel@tonic-gate * Returns 19560Sstevel@tonic-gate * _NO_RESOURCES - out of memory 19570Sstevel@tonic-gate * _NOT_FOUND - the query returned no results 19580Sstevel@tonic-gate * _SUCCESS - the query returned a single integer 19590Sstevel@tonic-gate */ 19600Sstevel@tonic-gate int 19610Sstevel@tonic-gate backend_tx_run_single_int(backend_tx_t *tx, backend_query_t *q, uint32_t *buf) 19620Sstevel@tonic-gate { 19630Sstevel@tonic-gate struct run_single_int_info info; 19640Sstevel@tonic-gate int ret; 19650Sstevel@tonic-gate 19660Sstevel@tonic-gate info.rs_out = buf; 19670Sstevel@tonic-gate info.rs_result = REP_PROTOCOL_FAIL_NOT_FOUND; 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate ret = backend_tx_run(tx, q, run_single_int_callback, &info); 19700Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 19710Sstevel@tonic-gate 19720Sstevel@tonic-gate if (ret != REP_PROTOCOL_SUCCESS) 19730Sstevel@tonic-gate return (ret); 19740Sstevel@tonic-gate 19750Sstevel@tonic-gate return (info.rs_result); 19760Sstevel@tonic-gate } 19770Sstevel@tonic-gate 19780Sstevel@tonic-gate /* 19790Sstevel@tonic-gate * Fails with 19800Sstevel@tonic-gate * _NO_RESOURCES - out of memory 19810Sstevel@tonic-gate */ 19820Sstevel@tonic-gate int 19830Sstevel@tonic-gate backend_tx_run_update(backend_tx_t *tx, const char *format, ...) 19840Sstevel@tonic-gate { 19850Sstevel@tonic-gate va_list a; 19860Sstevel@tonic-gate char *errmsg; 19870Sstevel@tonic-gate int ret; 19880Sstevel@tonic-gate sqlite_backend_t *be; 19890Sstevel@tonic-gate hrtime_t ts, vts; 19900Sstevel@tonic-gate 19910Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 19920Sstevel@tonic-gate be = tx->bt_be; 19930Sstevel@tonic-gate 19940Sstevel@tonic-gate va_start(a, format); 19950Sstevel@tonic-gate ts = gethrtime(); 19960Sstevel@tonic-gate vts = gethrvtime(); 19970Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 19980Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 19990Sstevel@tonic-gate if (ret == SQLITE_FULL) 20000Sstevel@tonic-gate tx->bt_full = 1; 20010Sstevel@tonic-gate va_end(a); 20020Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 20030Sstevel@tonic-gate assert(ret != REP_PROTOCOL_DONE); 20040Sstevel@tonic-gate 20050Sstevel@tonic-gate return (ret); 20060Sstevel@tonic-gate } 20070Sstevel@tonic-gate 20080Sstevel@tonic-gate /* 20090Sstevel@tonic-gate * returns REP_PROTOCOL_FAIL_NOT_FOUND if no changes occured 20100Sstevel@tonic-gate */ 20110Sstevel@tonic-gate int 20120Sstevel@tonic-gate backend_tx_run_update_changed(backend_tx_t *tx, const char *format, ...) 20130Sstevel@tonic-gate { 20140Sstevel@tonic-gate va_list a; 20150Sstevel@tonic-gate char *errmsg; 20160Sstevel@tonic-gate int ret; 20170Sstevel@tonic-gate sqlite_backend_t *be; 20180Sstevel@tonic-gate hrtime_t ts, vts; 20190Sstevel@tonic-gate 20200Sstevel@tonic-gate assert(tx != NULL && tx->bt_be != NULL && !tx->bt_readonly); 20210Sstevel@tonic-gate be = tx->bt_be; 20220Sstevel@tonic-gate 20230Sstevel@tonic-gate va_start(a, format); 20240Sstevel@tonic-gate ts = gethrtime(); 20250Sstevel@tonic-gate vts = gethrvtime(); 20260Sstevel@tonic-gate ret = sqlite_exec_vprintf(be->be_db, format, NULL, NULL, &errmsg, a); 20270Sstevel@tonic-gate UPDATE_TOTALS(be, bt_exec, ts, vts); 20280Sstevel@tonic-gate if (ret == SQLITE_FULL) 20290Sstevel@tonic-gate tx->bt_full = 1; 20300Sstevel@tonic-gate va_end(a); 20310Sstevel@tonic-gate 20320Sstevel@tonic-gate ret = backend_error(be, ret, errmsg); 20330Sstevel@tonic-gate 20340Sstevel@tonic-gate return (ret); 20350Sstevel@tonic-gate } 20360Sstevel@tonic-gate 20370Sstevel@tonic-gate #define BACKEND_ADD_SCHEMA(be, file, tbls, idxs) \ 20380Sstevel@tonic-gate (backend_add_schema((be), (file), \ 20390Sstevel@tonic-gate (tbls), sizeof (tbls) / sizeof (*(tbls)), \ 20400Sstevel@tonic-gate (idxs), sizeof (idxs) / sizeof (*(idxs)))) 20410Sstevel@tonic-gate 20420Sstevel@tonic-gate static int 20430Sstevel@tonic-gate backend_add_schema(sqlite_backend_t *be, const char *file, 20440Sstevel@tonic-gate struct backend_tbl_info *tbls, int tbl_count, 20450Sstevel@tonic-gate struct backend_idx_info *idxs, int idx_count) 20460Sstevel@tonic-gate { 20470Sstevel@tonic-gate int i; 20480Sstevel@tonic-gate char *errmsg; 20490Sstevel@tonic-gate int ret; 20500Sstevel@tonic-gate 20510Sstevel@tonic-gate /* 20520Sstevel@tonic-gate * Create the tables. 20530Sstevel@tonic-gate */ 20540Sstevel@tonic-gate for (i = 0; i < tbl_count; i++) { 20550Sstevel@tonic-gate if (tbls[i].bti_name == NULL) { 20560Sstevel@tonic-gate assert(i + 1 == tbl_count); 20570Sstevel@tonic-gate break; 20580Sstevel@tonic-gate } 20590Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 20600Sstevel@tonic-gate "CREATE TABLE %s (%s);\n", 20610Sstevel@tonic-gate NULL, NULL, &errmsg, tbls[i].bti_name, tbls[i].bti_cols); 20620Sstevel@tonic-gate 20630Sstevel@tonic-gate if (ret != SQLITE_OK) { 20640Sstevel@tonic-gate configd_critical( 20650Sstevel@tonic-gate "%s: %s table creation fails: %s\n", file, 20660Sstevel@tonic-gate tbls[i].bti_name, errmsg); 20670Sstevel@tonic-gate free(errmsg); 20680Sstevel@tonic-gate return (-1); 20690Sstevel@tonic-gate } 20700Sstevel@tonic-gate } 20710Sstevel@tonic-gate 20720Sstevel@tonic-gate /* 20730Sstevel@tonic-gate * Make indices on key tables and columns. 20740Sstevel@tonic-gate */ 20750Sstevel@tonic-gate for (i = 0; i < idx_count; i++) { 20760Sstevel@tonic-gate if (idxs[i].bxi_tbl == NULL) { 20770Sstevel@tonic-gate assert(i + 1 == idx_count); 20780Sstevel@tonic-gate break; 20790Sstevel@tonic-gate } 20800Sstevel@tonic-gate 20810Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 20820Sstevel@tonic-gate "CREATE INDEX %s_%s ON %s (%s);\n", 20830Sstevel@tonic-gate NULL, NULL, &errmsg, idxs[i].bxi_tbl, idxs[i].bxi_idx, 20840Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_cols); 20850Sstevel@tonic-gate 20860Sstevel@tonic-gate if (ret != SQLITE_OK) { 20870Sstevel@tonic-gate configd_critical( 20880Sstevel@tonic-gate "%s: %s_%s index creation fails: %s\n", file, 20890Sstevel@tonic-gate idxs[i].bxi_tbl, idxs[i].bxi_idx, errmsg); 20900Sstevel@tonic-gate free(errmsg); 20910Sstevel@tonic-gate return (-1); 20920Sstevel@tonic-gate } 20930Sstevel@tonic-gate } 20940Sstevel@tonic-gate return (0); 20950Sstevel@tonic-gate } 20960Sstevel@tonic-gate 20970Sstevel@tonic-gate static int 20980Sstevel@tonic-gate backend_init_schema(sqlite_backend_t *be, const char *db_file, backend_type_t t) 20990Sstevel@tonic-gate { 21000Sstevel@tonic-gate int i; 21010Sstevel@tonic-gate char *errmsg; 21020Sstevel@tonic-gate int ret; 21030Sstevel@tonic-gate 21040Sstevel@tonic-gate assert(t == BACKEND_TYPE_NORMAL || t == BACKEND_TYPE_NONPERSIST); 21050Sstevel@tonic-gate 21060Sstevel@tonic-gate if (t == BACKEND_TYPE_NORMAL) { 21070Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_normal, idxs_normal); 21080Sstevel@tonic-gate } else if (t == BACKEND_TYPE_NONPERSIST) { 21090Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_np, idxs_np); 21100Sstevel@tonic-gate } else { 21110Sstevel@tonic-gate abort(); /* can't happen */ 21120Sstevel@tonic-gate } 21130Sstevel@tonic-gate 21140Sstevel@tonic-gate if (ret < 0) { 21150Sstevel@tonic-gate return (ret); 21160Sstevel@tonic-gate } 21170Sstevel@tonic-gate 21180Sstevel@tonic-gate ret = BACKEND_ADD_SCHEMA(be, db_file, tbls_common, idxs_common); 21190Sstevel@tonic-gate if (ret < 0) { 21200Sstevel@tonic-gate return (ret); 21210Sstevel@tonic-gate } 21220Sstevel@tonic-gate 21230Sstevel@tonic-gate /* 21240Sstevel@tonic-gate * Add the schema version to the table 21250Sstevel@tonic-gate */ 21260Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 21270Sstevel@tonic-gate "INSERT INTO schema_version (schema_version) VALUES (%d)", 21280Sstevel@tonic-gate NULL, NULL, &errmsg, BACKEND_SCHEMA_VERSION); 21290Sstevel@tonic-gate if (ret != SQLITE_OK) { 21300Sstevel@tonic-gate configd_critical( 21310Sstevel@tonic-gate "setting schema version fails: %s\n", errmsg); 21320Sstevel@tonic-gate free(errmsg); 21330Sstevel@tonic-gate } 21340Sstevel@tonic-gate 21350Sstevel@tonic-gate /* 21360Sstevel@tonic-gate * Populate id_tbl with initial IDs. 21370Sstevel@tonic-gate */ 21380Sstevel@tonic-gate for (i = 0; i < BACKEND_ID_INVALID; i++) { 21390Sstevel@tonic-gate const char *name = id_space_to_name(i); 21400Sstevel@tonic-gate 21410Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 21420Sstevel@tonic-gate "INSERT INTO id_tbl (id_name, id_next) " 21430Sstevel@tonic-gate "VALUES ('%q', %d);", NULL, NULL, &errmsg, name, 1); 21440Sstevel@tonic-gate if (ret != SQLITE_OK) { 21450Sstevel@tonic-gate configd_critical( 21460Sstevel@tonic-gate "id insertion for %s fails: %s\n", name, errmsg); 21470Sstevel@tonic-gate free(errmsg); 21480Sstevel@tonic-gate return (-1); 21490Sstevel@tonic-gate } 21500Sstevel@tonic-gate } 21510Sstevel@tonic-gate /* 21520Sstevel@tonic-gate * Set the persistance of the database. The normal database is marked 21530Sstevel@tonic-gate * "synchronous", so that all writes are synchronized to stable storage 21540Sstevel@tonic-gate * before proceeding. 21550Sstevel@tonic-gate */ 21560Sstevel@tonic-gate ret = sqlite_exec_printf(be->be_db, 21570Sstevel@tonic-gate "PRAGMA default_synchronous = %s; PRAGMA synchronous = %s;", 21580Sstevel@tonic-gate NULL, NULL, &errmsg, 21590Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF", 21600Sstevel@tonic-gate (t == BACKEND_TYPE_NORMAL)? "ON" : "OFF"); 21610Sstevel@tonic-gate if (ret != SQLITE_OK) { 21620Sstevel@tonic-gate configd_critical("pragma setting fails: %s\n", errmsg); 21630Sstevel@tonic-gate free(errmsg); 21640Sstevel@tonic-gate return (-1); 21650Sstevel@tonic-gate } 21660Sstevel@tonic-gate 21670Sstevel@tonic-gate return (0); 21680Sstevel@tonic-gate } 21690Sstevel@tonic-gate 21700Sstevel@tonic-gate int 21710Sstevel@tonic-gate backend_init(const char *db_file, const char *npdb_file, int have_np) 21720Sstevel@tonic-gate { 21730Sstevel@tonic-gate sqlite_backend_t *be; 21740Sstevel@tonic-gate int r; 21750Sstevel@tonic-gate int writable_persist = 1; 21760Sstevel@tonic-gate 21770Sstevel@tonic-gate /* set up our temporary directory */ 21780Sstevel@tonic-gate sqlite_temp_directory = "/etc/svc/volatile"; 21790Sstevel@tonic-gate 21800Sstevel@tonic-gate if (strcmp(SQLITE_VERSION, sqlite_version) != 0) { 21810Sstevel@tonic-gate configd_critical("Mismatched link! (%s should be %s)\n", 21820Sstevel@tonic-gate sqlite_version, SQLITE_VERSION); 21830Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 21840Sstevel@tonic-gate } 2185*6035Sstevep 2186*6035Sstevep /* 2187*6035Sstevep * If the system crashed during a backend switch, there might 2188*6035Sstevep * be a leftover transient database which contains useful 2189*6035Sstevep * information which can be used for recovery. 2190*6035Sstevep */ 2191*6035Sstevep backend_switch_recovery(); 2192*6035Sstevep 21930Sstevel@tonic-gate if (db_file == NULL) 21940Sstevel@tonic-gate db_file = REPOSITORY_DB; 21955777Stw21770 if (strcmp(db_file, REPOSITORY_DB) != 0) { 21965777Stw21770 is_main_repository = 0; 21975777Stw21770 } 21980Sstevel@tonic-gate 21990Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NORMAL, db_file, &be); 22000Sstevel@tonic-gate switch (r) { 22010Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 22020Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 22030Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 22040Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 22050Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 22060Sstevel@tonic-gate break; /* success */ 22070Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 22080Sstevel@tonic-gate writable_persist = 0; 22090Sstevel@tonic-gate break; 22100Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 22110Sstevel@tonic-gate if (backend_init_schema(be, db_file, BACKEND_TYPE_NORMAL)) { 22120Sstevel@tonic-gate backend_destroy(be); 22130Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 22140Sstevel@tonic-gate } 22150Sstevel@tonic-gate break; 22160Sstevel@tonic-gate default: 22170Sstevel@tonic-gate abort(); 22180Sstevel@tonic-gate /*NOTREACHED*/ 22190Sstevel@tonic-gate } 22200Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NORMAL, be); 22210Sstevel@tonic-gate 22220Sstevel@tonic-gate if (have_np) { 22230Sstevel@tonic-gate if (npdb_file == NULL) 22240Sstevel@tonic-gate npdb_file = NONPERSIST_DB; 22250Sstevel@tonic-gate 22260Sstevel@tonic-gate r = backend_create(BACKEND_TYPE_NONPERSIST, npdb_file, &be); 22270Sstevel@tonic-gate switch (r) { 22280Sstevel@tonic-gate case BACKEND_CREATE_SUCCESS: 22290Sstevel@tonic-gate break; /* success */ 22300Sstevel@tonic-gate case BACKEND_CREATE_FAIL: 22310Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 22320Sstevel@tonic-gate case BACKEND_CREATE_LOCKED: 22330Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_LOCKED); 22340Sstevel@tonic-gate case BACKEND_CREATE_READONLY: 22350Sstevel@tonic-gate configd_critical("%s: unable to write\n", npdb_file); 22360Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 22370Sstevel@tonic-gate case BACKEND_CREATE_NEED_INIT: 22380Sstevel@tonic-gate if (backend_init_schema(be, db_file, 22390Sstevel@tonic-gate BACKEND_TYPE_NONPERSIST)) { 22400Sstevel@tonic-gate backend_destroy(be); 22410Sstevel@tonic-gate return (CONFIGD_EXIT_DATABASE_INIT_FAILED); 22420Sstevel@tonic-gate } 22430Sstevel@tonic-gate break; 22440Sstevel@tonic-gate default: 22450Sstevel@tonic-gate abort(); 22460Sstevel@tonic-gate /*NOTREACHED*/ 22470Sstevel@tonic-gate } 22480Sstevel@tonic-gate backend_create_finish(BACKEND_TYPE_NONPERSIST, be); 22490Sstevel@tonic-gate 22500Sstevel@tonic-gate /* 22510Sstevel@tonic-gate * If we started up with a writable filesystem, but the 22520Sstevel@tonic-gate * non-persistent database needed initialization, we 22530Sstevel@tonic-gate * are booting a non-global zone, so do a backup. 22540Sstevel@tonic-gate */ 22550Sstevel@tonic-gate if (r == BACKEND_CREATE_NEED_INIT && writable_persist && 22560Sstevel@tonic-gate backend_lock(BACKEND_TYPE_NORMAL, 0, &be) == 22570Sstevel@tonic-gate REP_PROTOCOL_SUCCESS) { 22580Sstevel@tonic-gate if (backend_create_backup_locked(be, 22590Sstevel@tonic-gate REPOSITORY_BOOT_BACKUP) != REP_PROTOCOL_SUCCESS) { 22600Sstevel@tonic-gate configd_critical( 22610Sstevel@tonic-gate "unable to create \"%s\" backup of " 22620Sstevel@tonic-gate "\"%s\"\n", REPOSITORY_BOOT_BACKUP, 22630Sstevel@tonic-gate be->be_path); 22640Sstevel@tonic-gate } 22650Sstevel@tonic-gate backend_unlock(be); 22660Sstevel@tonic-gate } 22670Sstevel@tonic-gate } 22680Sstevel@tonic-gate return (CONFIGD_EXIT_OKAY); 22690Sstevel@tonic-gate } 22700Sstevel@tonic-gate 22710Sstevel@tonic-gate /* 22720Sstevel@tonic-gate * quiesce all database activity prior to exiting 22730Sstevel@tonic-gate */ 22740Sstevel@tonic-gate void 22750Sstevel@tonic-gate backend_fini(void) 22760Sstevel@tonic-gate { 22770Sstevel@tonic-gate sqlite_backend_t *be_normal, *be_np; 22780Sstevel@tonic-gate 22790Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NORMAL, 1, &be_normal); 22800Sstevel@tonic-gate (void) backend_lock(BACKEND_TYPE_NONPERSIST, 1, &be_np); 22810Sstevel@tonic-gate } 22820Sstevel@tonic-gate 22830Sstevel@tonic-gate #define QUERY_BASE 128 22840Sstevel@tonic-gate backend_query_t * 22850Sstevel@tonic-gate backend_query_alloc(void) 22860Sstevel@tonic-gate { 22870Sstevel@tonic-gate backend_query_t *q; 22880Sstevel@tonic-gate q = calloc(1, sizeof (backend_query_t)); 22890Sstevel@tonic-gate if (q != NULL) { 22900Sstevel@tonic-gate q->bq_size = QUERY_BASE; 22910Sstevel@tonic-gate q->bq_buf = calloc(1, q->bq_size); 22920Sstevel@tonic-gate if (q->bq_buf == NULL) { 22930Sstevel@tonic-gate q->bq_size = 0; 22940Sstevel@tonic-gate } 22950Sstevel@tonic-gate 22960Sstevel@tonic-gate } 22970Sstevel@tonic-gate return (q); 22980Sstevel@tonic-gate } 22990Sstevel@tonic-gate 23000Sstevel@tonic-gate void 23010Sstevel@tonic-gate backend_query_append(backend_query_t *q, const char *value) 23020Sstevel@tonic-gate { 23030Sstevel@tonic-gate char *alloc; 23040Sstevel@tonic-gate int count; 23050Sstevel@tonic-gate size_t size, old_len; 23060Sstevel@tonic-gate 23070Sstevel@tonic-gate if (q == NULL) { 23080Sstevel@tonic-gate /* We'll discover the error when we try to run the query. */ 23090Sstevel@tonic-gate return; 23100Sstevel@tonic-gate } 23110Sstevel@tonic-gate 23120Sstevel@tonic-gate while (q->bq_buf != NULL) { 23130Sstevel@tonic-gate old_len = strlen(q->bq_buf); 23140Sstevel@tonic-gate size = q->bq_size; 23150Sstevel@tonic-gate count = strlcat(q->bq_buf, value, size); 23160Sstevel@tonic-gate 23170Sstevel@tonic-gate if (count < size) 23180Sstevel@tonic-gate break; /* success */ 23190Sstevel@tonic-gate 23200Sstevel@tonic-gate q->bq_buf[old_len] = 0; 23210Sstevel@tonic-gate size = round_up_to_p2(count + 1); 23220Sstevel@tonic-gate 23230Sstevel@tonic-gate assert(size > q->bq_size); 23240Sstevel@tonic-gate alloc = realloc(q->bq_buf, size); 23250Sstevel@tonic-gate if (alloc == NULL) { 23260Sstevel@tonic-gate free(q->bq_buf); 23270Sstevel@tonic-gate q->bq_buf = NULL; 23280Sstevel@tonic-gate break; /* can't grow */ 23290Sstevel@tonic-gate } 23300Sstevel@tonic-gate 23310Sstevel@tonic-gate q->bq_buf = alloc; 23320Sstevel@tonic-gate q->bq_size = size; 23330Sstevel@tonic-gate } 23340Sstevel@tonic-gate } 23350Sstevel@tonic-gate 23360Sstevel@tonic-gate void 23370Sstevel@tonic-gate backend_query_add(backend_query_t *q, const char *format, ...) 23380Sstevel@tonic-gate { 23390Sstevel@tonic-gate va_list args; 23400Sstevel@tonic-gate char *new; 23410Sstevel@tonic-gate 23420Sstevel@tonic-gate if (q == NULL || q->bq_buf == NULL) 23430Sstevel@tonic-gate return; 23440Sstevel@tonic-gate 23450Sstevel@tonic-gate va_start(args, format); 23460Sstevel@tonic-gate new = sqlite_vmprintf(format, args); 23470Sstevel@tonic-gate va_end(args); 23480Sstevel@tonic-gate 23490Sstevel@tonic-gate if (new == NULL) { 23500Sstevel@tonic-gate free(q->bq_buf); 23510Sstevel@tonic-gate q->bq_buf = NULL; 23520Sstevel@tonic-gate return; 23530Sstevel@tonic-gate } 23540Sstevel@tonic-gate 23550Sstevel@tonic-gate backend_query_append(q, new); 23560Sstevel@tonic-gate 23570Sstevel@tonic-gate free(new); 23580Sstevel@tonic-gate } 23590Sstevel@tonic-gate 23600Sstevel@tonic-gate void 23610Sstevel@tonic-gate backend_query_free(backend_query_t *q) 23620Sstevel@tonic-gate { 23630Sstevel@tonic-gate if (q != NULL) { 23640Sstevel@tonic-gate if (q->bq_buf != NULL) { 23650Sstevel@tonic-gate free(q->bq_buf); 23660Sstevel@tonic-gate } 23670Sstevel@tonic-gate free(q); 23680Sstevel@tonic-gate } 23690Sstevel@tonic-gate } 2370